06 / 快速开始06 / QUICKSTART
先把一个样例日读对,再开始写策略。Read one sample day correctly before writing a strategy.
这是一条从 ZIP 到 Parquet、从字段关联到最小成交模拟的可复制路径,适用于 5分钟和15分钟市场。A copyable path from ZIP to Parquet, from schema joins to a minimal fill simulator for both 5-minute and 15-minute markets.
1. 解压并识别文件1. Extract and inventory the files
15m 样例是一个 ZIP。5m 样例分成 part1 和 part2,两个文件都下载后解压到同一个目录;分包只是传输层拆分。The 15m sample is one ZIP. The 5m sample is split into part 1 and part 2; download both and extract them into the same directory. The split is only for transport.
sample_15m/
├── ticks_20260810_00.parquet
├── depth_20260810_00.parquet
└── btc_15m_market_outcomes.parquet实际文件可能按小时继续增加。不要仅凭文件名推断覆盖范围,先列出文件大小、Parquet schema、时间范围和 event_slug 数量。More hourly files may be present in a full archive. Do not infer coverage from filenames alone; inventory file sizes, Parquet schemas, time ranges, and event_slug counts first.
2. 安装最小读取依赖2. Install the minimal reader
python -m pip install pandas pyarrowfrom pathlib import Path
import pandas as pd
root = Path("sample_15m")
ticks = pd.concat(
[pd.read_parquet(path) for path in sorted(root.glob("ticks_*.parquet"))],
ignore_index=True,
)
depth = pd.concat(
[pd.read_parquet(path) for path in sorted(root.glob("depth_*.parquet"))],
ignore_index=True,
)
outcomes = pd.read_parquet(root / "btc_15m_market_outcomes.parquet")
ticks = ticks.sort_values(["event_slug", "ts"])
depth = depth.sort_values(["event_slug", "ts", "outcome", "side", "level"])
data = ticks.merge(outcomes, on="event_slug", how="left")3. 用 event_slug 关联3. Join with event_slug
ticks、depth、market_outcomes 和 seen_markets 都通过 event_slug 关联。需要核对 token 时,再从 seen_markets 补充 up_asset_id 和 down_asset_id。Join ticks, depth, market_outcomes, and seen_markets with event_slug. When token-level verification is needed, add up_asset_id and down_asset_id from seen_markets.
event_slug → market context → quotes / depth → resolved outcome
5m 市场窗口通常是 300 秒,15m 通常是 900 秒。回测边界使用文件中的 window_start_ts、window_end_ts 和 interval_min。A 5m window is normally 300 seconds and a 15m window 900 seconds. Use window_start_ts, window_end_ts, and interval_min from the files for backtest boundaries.
4. 先做最小成交模拟4. Start with a minimal fill simulator
买入从对应 outcome 的 ASK level 1 开始逐档消耗,卖出从 BID level 1 开始逐档消耗。展示数量不是成交保证。A buy consumes the selected outcome's ASK levels from level 1 upward; a sell consumes BID levels from level 1 downward. Displayed size is not a fill guarantee.
def consume_book(snapshot, outcome, side, quantity):
levels = snapshot[
(snapshot["outcome"] == outcome) &
(snapshot["side"] == side)
].sort_values("level")
remaining = quantity
cost = 0.0
filled = 0.0
for row in levels.itertuples():
amount = min(remaining, row.size)
cost += amount * row.price
filled += amount
remaining -= amount
if remaining <= 0:
break
return {
"filled": filled,
"average_price": cost / filled if filled else None,
"unfilled": remaining,
}
# buy -> side="ASK"; sell -> side="BID"
# Add latency, fees, slippage, and a fill-ratio assumption in the real backtest.第一版回测至少显式设置延迟、部分成交比例、深度不足、手续费和滑点。不要用 mid 价代替真实买卖价,也不要把 winner_side 或 target 当成预测特征。The first backtest must state latency, partial-fill ratio, insufficient depth, fees, and slippage. Do not use mid as the buy/sell fill, and never use winner_side or target as a predictive feature.