
我用長橋 API 給 QQQ 0DTE 策略做回測,差點被數據騙了

做量化交易的人都聽過一句話:策略好不好,回測説了算。
但沒人告訴你的是——回測本身就會坑你。數據拿錯了、信號過濾太嚴了、參數看起來漂亮但實盤一塌糊塗……這些都是真實發生在我身上的事。
這篇文章記錄我用長橋 API 對 QQQ 0DTE 衰竭反轉策略做回測時,踩過的每一個坑。如果你也在用長橋做美股策略回測,希望這些經驗能幫你少走彎路。
坑 1:yfinance 不靠譜,長橋 API 才是正道
一開始我用 yfinance 下載歷史數據,想着免費就行。結果:
- 頻繁被限流(429 Too Many Requests)
- 1 分鐘數據只能拿最近 30 天
- 數據質量參差不齊,偶有缺失
換了長橋 API 之後,通過 history_candlesticks_by_date() 可以按天拉取 1 分鐘 K 線,每天約 241 根(Basic 級別,僅正式盤),Premium 級別含盤前盤後約 960 根。

from longport.openapi import Config, QuoteContext, Period, AdjustType, TradeSessions from datetime import date, timedelta ctx = QuoteContext(Config.from_apikey_env()) # 按天下载,精确控制范围 candles = ctx.history_candlesticks_by_date( symbol="QQQ.US", period=Period.Min_1, adjust_type=AdjustType.ForwardAdjust, start=date(2026, 4, 14), end=date(2026, 4, 15), # 注意:end 不包含这一天 trade_sessions=TradeSessions.All ) print(f"获取到 {len(candles)} 根 K 线")
⚡ 踩坑要點
1. start 和 end 必須是 date 對象,不能是字符串
# ❌ 报错:'str' object cannot be cast as 'date' candles = ctx.history_candlesticks_by_date(..., start='2026-04-14', end='2026-04-15') # ✅ 正确 from datetime import date candles = ctx.history_candlesticks_by_date(..., start=date(2026,4,14), end=date(2026,4,15))
2. 單次最多返回 1000 根 K 線
一天的 1 分鐘 K 線(含盤前盤後)剛好接近 1000 根的限制。所以按天循環下載是正確姿勢,別想一口氣拉一個月的數據:
import time from datetime import date, timedelta all_candles = [] current = date(2025, 7, 1) end_date = date(2026, 4, 18) while current <= end_date: try: candles = ctx.history_candlesticks_by_date( symbol="QQQ.US", period=Period.Min_1, adjust_type=AdjustType.ForwardAdjust, start=current, end=current + timedelta(days=1), trade_sessions=TradeSessions.All ) all_candles.extend(candles) print(f" {current}: {len(candles)}根") except Exception as e: print(f" {current}: {e}") current += timedelta(days=1) time.sleep(0.2) # 别太快,防限流
3. timestamp 可能是 datetime 對象
長橋返回的 Candlestick.timestamp 在不同 SDK 版本下可能是 datetime 或 Unix timestamp。直接用 fromtimestamp() 可能炸:
# ✅ 防御性写法 ts = candle.timestamp if isinstance(ts, (int, float)): ts = datetime.fromtimestamp(ts) # 如果已经是 datetime,直接用
坑 2:5 分鐘數據在開盤 1 小時窗口內直接啞火——0 筆交易
第一輪迴測,我用 5 分鐘 K 線跑了 60 天的數據結果 0 筆交易。
但我當時沒當回事,覺得是數據量不夠。直到後來用完整的 v6 全過濾策略(雙向突破 +ITM 期權 +Black-Scholes 定價)在 5 分鐘和 1 分鐘數據上做了一次正式對比,結果讓我徹底服了:

| 5 分鐘 K 線 | 1 分鐘 K 線 | |
|---|---|---|
| K 線總數 | 40,583 根 | 202,866 根 |
| 交易日數 | 536 天 | 536 天 |
| 策略窗口 | 09:35-10:50(開盤 1 小時) | 09:35-10:50(開盤 1 小時) |
| 總交易筆數 | 0 筆 | 451 筆 |
| 勝率 | — | 78.5% |
| 總得分 | — | +2139.92% |
| 每年 | 0 筆 | 198 筆 |
| 最大回撤 | — | 25.19% |
5 分鐘數據在開盤 1 小時內,一筆交易都沒觸發。
為什麼?因為我的策略窗口只有開盤 1 小時(09:35-10:50),5 分鐘 K 線在這個窗口裏只有約 15 根。再加上全過濾(SMA20 趨勢 + 量能 + 動量 +K 線實體),15 根 5 分鐘 K 線根本不夠過濾條件判斷的——指標還沒算出來,窗口就關了。
而 1 分鐘 K 線在同一窗口內有約 75 根,信號充足,經過 6 層過濾後仍保留 451 筆。
教訓:策略的時間尺度和數據的顆粒度必須匹配。 開盤 1 小時的快速行情,5 分鐘顆粒度完全跟不上。這不是參數問題,是數據粒度的物理限制。
坑 3:24746 次突破信號只剩 454 筆——6 層過濾漏斗每一層都在"殺人"
切換到 1 分鐘數據後,信心滿滿跑回測。這次不是 0 筆了,但我想搞清楚:過濾條件到底砍掉了多少信號?
寫了個診斷腳本,逐層統計每一層過濾通過的次數:

突破信号触发 → 24746 次 ✅ 信号源充足
↓ 时间窗口过滤(只做 09:35-10:50)
时间窗口通过 → 3535 次 (14.3%) ⚠️ 85% 被砍
↓ 跳空过滤(gap < 0.20%)
跳空过滤通过 → 3464 次 (98.0%) ✅ 跳空不是问题
↓ SMA20 趋势过滤(做多价格>SMA20,做空<SMA20)
SMA20 通过 → 3450 次 (99.6%) ✅ 趋势几乎不影响
↓ 量能过滤(成交量 ≥ 20 均量 × 1.2)
量能通过 → 1205 次 (34.9%) ⚠️ 65% 被砍!
↓ 动量确认(最近 2 根 K 线同向)
动量通过 → 616 次 (51.1%) ⚠️ 又砍一半
↓ K 线实体确认(实体 ≥ 0.03%)
K 线实体通过 → 454 次 (73.7%)
↓ 最终入场
最终信号 → 454 次 (100%) ✅ 全部入场
漏斗分析揭示了三個關鍵真相:
1. 時間窗口是第一大瓶頸(保留 14.3%)。 開盤 1 小時雖然信號質量高,但直接砍掉了 85% 的突破信號。這是有意為之——全天的突破信號太多噪音,開盤時段的信號最有效。
2. 量能過濾是第二大瓶頸(保留 34.9%)。 要求成交量達到 20 日均量的 1.2 倍,直接砍掉了 65% 的信號。這意味着大部分突破發生在縮量狀態下,放量突破才是真突破。
3. SMA20 趨勢過濾幾乎沒用(保留 99.6%)。 原以為"做多必須價格在 SMA20 之上"會砍掉很多假信號,實際上 99.6% 的突破信號本身就已經滿足這個條件。趨勢是結果不是原因——突破本身就隱含了趨勢。
診斷代碼
如果你也遇到類似問題,可以用這個方法定位瓶頸:
# 逐层统计过滤漏斗 —— 直接告诉你哪里卡住了
stages = {
'突破信号': 0, '时间窗口': 0, '跳空过滤': 0,
'SMA20': 0, '量能': 0, '动量': 0, 'K 线实体': 0, '最终入场': 0,
}
for i in range(n):
# 第一层:突破信号
if not (prev_close > upper or prev_close < lower):
continue
stages['突破信号'] += 1
# 第二层:时间窗口
if not (9*60+35 <= hour_min <= 10*60+50):
continue
stages['时间窗口'] += 1
# 第三层:跳空
if gap > 0.0020:
continue
stages['跳空过滤'] += 1
# 第四层:SMA20
if sig == 'call' and close < sma20:
continue
if sig == 'put' and close > sma20:
continue
stages['SMA20'] += 1
# 第五层:量能
if volume < sma_vol * 1.2:
continue
stages['量能'] += 1
# 第六层:动量(2 根同向)
if not (连续 2 根同向 K 线):
continue
stages['动量'] += 1
# 第七层:K 线实体
if prev_body < 0.0003:
continue
stages['K 线实体'] += 1
stages['最终入场'] += 1
for stage, count in stages.items():
print(f" {stage:10s} → {count:5d} 次")
這個漏斗圖比任何優化算法都管用。 它直接告訴你哪層過濾太鬆(浪費計算)、哪層太緊(漏掉機會)、哪層純屬擺設。
坑 4:調參數治標不治本
發現問題後,我嘗試調整參數:

| big_mult | fail_thresh | 交易筆數 | 結果 |
|---|---|---|---|
| 2.5 | 2 | 0 | 原始參數,全滅 |
| 2.0 | 2 | 0 | 放寬了,還是沒用 |
| 1.5 | 2 | 1 | 終於有 1 筆了 |
| 1.5 | 1 | 1 | 降低衰竭閾值,還是一筆 |
| 1.2 | 2 | 1 | 極端放寬,依然只有 1 筆 |
結論:參數調整在當前市場環境下效果有限。
這不是參數的問題,是市場狀態的問題。最近 QQQ 處於低波動的趨勢行情,衰竭反轉信號本身就少。策略需要的是高波動、頻繁反轉的市場環境才能發揮。
這給我的啓發是:回測不能只看數字好看不好看,還要看回測數據覆蓋了什麼樣的市場狀態。
- 只回測牛市?策略可能只會做多
- 只回測低波動?策略可能一單都不觸發
- 必須覆蓋牛、熊、震盪至少三種行情
坑 5:時區差點讓我多做了一筆假交易
長橋返回的 K 線時間戳是 HKT(UTC+8),不是 UTC,也不是美股東部時間(ET)。
我一開始沒注意,直接拿 HKT 時間去判斷"美東 9:30 開盤",結果時間全部偏移了 13 個小時(夏令時 12 小時)。這意味着:

- 美東 9:30 開盤 = 北京時間 21:30
- 如果代碼裏寫
if hour == 9,實際對應的是北京時間 9 點——根本不在交易時段內
正確的處理方式:
from datetime import datetime import pytz # 长桥返回的是 UTC 时间 utc_time = candle.timestamp # datetime with tzinfo=UTC # 转换为美东时间 et = pytz.timezone('America/New_York') et_time = utc_time.astimezone(et) # 判断是否在交易时段 if et_time.hour == 9 and et_time.minute >= 30: # 正式开盘 pass
WSL 環境的額外坑:pip3 可能指向系統 Python,而你在虛擬環境裏。安裝 pytz 要用:
# ❌ pip3 install pytz → 可能装到系统 python 去了 # ✅ /usr/bin/python3 -m pip install pytz --break-system-packages
坑 6:舊數據和新數據合併時格式不統一
我的回測數據來自兩個時期:
- 舊數據:CSV 格式,時間列無時區信息
- 新數據:從長橋 API 獲取,帶 UTC 時區
直接 pd.concat() 會報錯或者時間對不上。正確做法:

import pandas as pd # 强制统一为 UTC old['Datetime'] = pd.to_datetime(old['Datetime'], utc=True) new['Datetime'] = pd.to_datetime(new['Datetime'], utc=True) # 可选:统一转为美东时间(去掉时区信息,方便按小时筛选)old['Datetime'] = old['Datetime'].dt.tz_convert('America/New_York').dt.tz_localize(None) new['Datetime'] = new['Datetime'].dt.tz_convert('America/New_York').dt.tz_localize(None) # 合并去重 all_data = pd.concat([old, new]).drop_duplicates(subset='Datetime').sort_values('Datetime')
總結:回測中我學到的 6 件事
數據源要可靠。yfinance 免費但不穩定,長橋 API 按天下載 1 分鐘 K 線是更好的選擇,注意 start/end 必須是 date 對象,單次最多 1000 根。
數據粒度要匹配策略。0DTE 做分鐘級交易,必須用 1 分鐘數據,5 分鐘會漏掉大部分信號。
回測時一定要做信號漏斗分析。逐層統計每個過濾條件通過的次數,快速定位瓶頸在哪。
參數調優有上限。如果市場狀態不支持策略邏輯,調什麼參數都沒用。要看回測數據是否覆蓋了不同市場環境。
時區處理是重災區。長橋返回 UTC 時間,做美股策略需要轉成 ET。WSL 下 pip 版本可能串,注意用對 Python。
數據合併前先統一格式。舊 CSV 無時區 + API 數據有時區,直接拼會出 bug。先統一為同一時區再去重合並。
以上是我在 QQQ 0DTE 策略回測過程中的真實踩坑經歷。如果你也在用長橋做量化交易,歡迎交流。
本文僅供技術交流,不構成投資建議。
@LongbridgeAI $納指 100 ETF - Invesco(QQQ.US)
本文版權歸屬原作者/機構所有。
當前內容僅代表作者觀點,與本平台立場無關。內容僅供投資者參考,亦不構成任何投資建議。如對本平台提供的內容服務有任何疑問或建議,請聯絡我們。



