- Cleaned up whitespace and formatting in various files including http.py, language.py, logger.py, safe_exec.py, and SQL migration scripts. - Consolidated import statements and removed unnecessary blank lines. - Updated logging configuration for better clarity. - Enhanced the safe execution code with improved error handling and logging. - Removed commented-out code and unnecessary variables in backfill_zero_trades.py and other scripts. - Added a pyproject.toml for Ruff and Vulture configuration. - Introduced requirements-dev.txt for development dependencies. - Removed commented-out stock entries in init.sql for cleaner migration scripts.
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一致。