Signed-off-by: TIANHE <TIANHE@GMAIL.COM>
7.4 KiB
QuantDinger Python 策略開發指南
本指南將詳細介紹如何在 QuantDinger 平台中使用 Python 開發交易策略。QuantDinger 提供了靈活的執行環境,支持數據訪問、指標計算和信號生成。
1. 概覽
QuantDinger 中的策略基於 信號提供者 (Signal Provider) 模式運行。系統執行你的 Python 腳本,該腳本負責處理市場數據(DataFrame)並輸出交易信號。
執行流程如下:
- 輸入:系統將包含 OHLCV 數據的
df(Pandas DataFrame) 注入到你的腳本環境中。 - 處理:你使用 Python (
pandas,numpy) 計算指標並定義buy/sell邏輯。 - 輸出:你構造一個特定的
output字典,包含繪圖數據和信號。
2. 環境與數據
你的腳本運行在一個沙盒化的 Python 環境中。
2.1 預導入庫
以下庫默認可用(無需 import):
pd(pandas)np(numpy)
2.2 輸入數據 (df)
一個名為 df 的 Pandas DataFrame 變量會自動存在於全局作用域中。它包含所選代碼和時間週期的歷史市場數據。
列 (Columns):
time: 時間戳 (datetime 或 int,視上下文而定)open: 開盤價 (float)high: 最高價 (float)low: 最低價 (float)close: 收盤價 (float)volume: 成交量 (float)
示例:
# 獲取收盤價序列
closes = df['close']
# 計算簡單移動平均線 (SMA)
sma_20 = df['close'].rolling(20).mean()
3. 開發策略
一個標準的策略腳本包含三個部分:
- 指標計算:計算技術指標。
- 信號生成:定義買入和賣出信號邏輯。
- 輸出構建:格式化結果以供圖表展示和執行引擎使用。
3.1 指標計算
你可以使用標準的 Pandas 操作來計算指標。
# 示例:MACD 計算
short_window = 12
long_window = 26
signal_window = 9
ema12 = df['close'].ewm(span=short_window, adjust=False).mean()
ema26 = df['close'].ewm(span=long_window, adjust=False).mean()
macd = ema12 - ema26
signal_line = macd.ewm(span=signal_window, adjust=False).mean()
3.2 信號生成 (關鍵)
你 必須 在 df 中創建(或作為獨立變量)兩個布爾類型的 Series,分別命名為 buy 和 sell。
True表示觸發信號。False表示無信號。
重要:邊緣觸發 (Edge Triggering) 為了避免在連續的 K 線上重複發出信號(這可能導致重複下單,取決於後端配置),最佳實踐是使用 邊緣觸發 信號(即只在條件變真的那一刻發出信號)。
# 條件:收盤價上穿 SMA 20
condition_buy = (df['close'] > sma_20) & (df['close'].shift(1) <= sma_20.shift(1))
# 條件:收盤價下穿 SMA 20
condition_sell = (df['close'] < sma_20) & (df['close'].shift(1) >= sma_20.shift(1))
# 賦值給 df (回測必需)
df['buy'] = condition_buy.fillna(False)
df['sell'] = condition_sell.fillna(False)
關於信號類型的說明:
- QuantDinger 會根據你的策略配置(僅做多、僅做空或雙向)來標準化信號。
- 你的腳本只需輸出 "buy"(看漲意圖)或 "sell"(看跌意圖)。後端會處理開倉/平倉邏輯。
3.3 可視化標記
為了在圖表上展示,你通常希望將信號圖標放在 K 線的上方或下方。
# 將買入標記放在最低價下方 0.5% 處
buy_marks = [
df['low'].iloc[i] * 0.995 if df['buy'].iloc[i] else None
for i in range(len(df))
]
# 將賣出標記放在最高價上方 0.5% 處
sell_marks = [
df['high'].iloc[i] * 1.005 if df['sell'].iloc[i] else None
for i in range(len(df))
]
3.4 output 變量 (必須)
最後一步是將一個字典賦值給變量 output。這告訴前端如何繪圖,以及告訴後端信號在哪裡。
結構:
output = {
"name": "我的策略名稱",
"plots": [ ... ], # 要繪製的線條/指標列表
"signals": [ ... ] # 信號標記列表
}
Plots Schema (繪圖配置):
name: 圖例名稱 (例如 "SMA 20")data: 數值列表 (必須與df長度一致)。使用.tolist()轉換。color: 十六進制顏色字符串 (例如 "#ff0000")。overlay:True表示繪製在主圖(價格圖)上,False表示繪製在副圖(如 RSI/MACD)。
Signals Schema (信號配置):
type: 必須是 "buy" 或 "sell"。text: 圖標上顯示的文本 (例如 "B", "S")。data: 數值列表 (價格位置)。無信號處為None。color: 圖標顏色。
4. 完整示例:雙均線交叉 (Dual SMA)
以下是一個完整的、可複製的雙均線策略示例:當 SMA(10) 上穿 SMA(30) 時買入,下穿時賣出。
# 1. 指標計算
# -----------------------
# 計算短期和長期 SMA
sma_short = df['close'].rolling(10).mean()
sma_long = df['close'].rolling(30).mean()
# 2. 信號邏輯
# -----------------------
# 買入:短期 SMA 上穿 長期 SMA
raw_buy = (sma_short > sma_long) & (sma_short.shift(1) <= sma_long.shift(1))
# 賣出:短期 SMA 下穿 長期 SMA
raw_sell = (sma_short < sma_long) & (sma_short.shift(1) >= sma_long.shift(1))
# 清理 NaN 並確保布爾類型
buy = raw_buy.fillna(False)
sell = raw_sell.fillna(False)
# 賦值給 df 列 (後端執行的關鍵)
df['buy'] = buy
df['sell'] = sell
# 3. 可視化格式化
# -----------------------
# 計算標記位置
buy_marks = [
df['low'].iloc[i] * 0.995 if buy.iloc[i] else None
for i in range(len(df))
]
sell_marks = [
df['high'].iloc[i] * 1.005 if sell.iloc[i] else None
for i in range(len(df))
]
# 4. 最終輸出
# -----------------------
output = {
'name': '雙均線策略',
'plots': [
{
'name': 'SMA 10',
'data': sma_short.fillna(0).tolist(),
'color': '#1890ff',
'overlay': True
},
{
'name': 'SMA 30',
'data': sma_long.fillna(0).tolist(),
'color': '#faad14',
'overlay': True
}
],
'signals': [
{
'type': 'buy',
'text': '買',
'data': buy_marks,
'color': '#00E676'
},
{
'type': 'sell',
'text': '賣',
'data': sell_marks,
'color': '#FF5252'
}
]
}
5. 最佳實踐與故障排除
5.1 處理 NaN
滾動計算(如 rolling(14))會在數據開頭產生 NaN 值。
- 規則:生成信號前必須處理
NaN。 - 修復:使用
.fillna(0)或.fillna(False)。
5.2 未來函數 (Look-ahead Bias)
系統基於 K 線 收盤 時產生的信號執行交易。
- 回測引擎通常在 下一根 K 線的開盤價 執行訂單。
- 你的信號邏輯應依賴
close(當前已完成的 K 線) 或shift(1)(前一根 K 線)。切勿使用shift(-1)。
5.3 性能
避免在計算邏輯中遍歷 DataFrame 行 (for i in range(len(df)): ...)。這非常慢。
- 錯誤:使用循環計算 SMA。
- 正確:使用
df['close'].rolling(...)。 - 例外:構建
buy_marks/sell_marks列表通常需要列表推導式,這是可接受的(僅用於可視化輸出)。
5.4 調試
由於在某些執行模式下無法輕易看到 print() 輸出,如果策略加載失敗,請檢查後端日誌 (backend_api_python/logs/app.log)。
- 常見錯誤:
KeyError(列名錯誤)。 - 常見錯誤:
ValueError(數組長度不一致)。確保plots中的數據長度與df一致。