模块技术选型 · 字段与设计要点
基础层
asyncio+asyncio.TaskGroup(Python 3.11+):每个 SystemInstance 作为独立 Task,TaskGroup 提供结构化并发,子任务异常不会静默吞掉signal.signal(SIGTERM/SIGINT)+asyncio.Event实现优雅退出:收到信号后置位 shutdown_event,各模块在循环中检查contextvars.ContextVar注入 system_id / symbol,配合structlog自动给每条日志打标签,多系统日志不混淆- 不用多进程/多线程:CCXT 异步版本(ccxt.async_support)+ websockets 已足够,单进程调试链路最短
| 字段 | 类型 | 说明 |
|---|---|---|
system_configs系统配置列表 | list[SystemConfig] | 各系统实例配置列表 |
shared_hubs共享上下文 | SharedContext | 包含 MarketDataHub、TrendResultCache、KeyLevelCache、SymbolStrategyRegistry、RiskController、RevengeController 的共享上下文 |
| 字段 | 类型 | 说明 |
|---|---|---|
runtime_status运行状态 | dict[system_id, SystemRuntimeStatus] | 每个系统的运行状态:RUNNING/STOPPED/ERROR、last_heartbeat、last_error |
shutdown_report关机报告 | ShutdownReport | 优雅退出时各系统的持仓快照、未完成订单清单 |
单个 system 抛异常不能拖垮全局。每个 SystemInstance 外层包 while not shutdown: try: await run() except: log + sleep(backoff) + continue,崩溃次数超阈值才真正下线
每个 system 必须周期性更新 last_heartbeat,Runner 主循环检测 >N 分钟没心跳的 system 强制重启。asyncio 死锁/await 卡死时只靠异常捕获救不回来
MarketDataHub → Cache → Registry → 各 SystemInstance。SystemInstance 启动时必须等 Hub 至少有一份完整 K 线快照,否则首轮分析会拿到空数据
- 状态用 dataclass +
aiofiles持久化到 JSON(risk_state_YYYYMMDD.json),进程重启不丢失当日累计亏损 - 日界切换用
pytz+ 用户配置的risk_timezone,跨日时归零并归档旧日文件 asyncio.Lock保护亏损累加,避免多个 system 同时平仓时竞态写入
| 字段 | 类型 | 说明 |
|---|---|---|
symbol交易对 | str | 交易对 |
system_id系统ID | str | 发起方系统 ID |
intended_position预期持仓 | PositionIntent | 含 entry/stop/qty,用于计算最大潜在亏损 |
current_account_state账户状态 | AccountState | 当前余额、已用保证金 |
| 字段 | 类型 | 说明 |
|---|---|---|
decision风控决策 | Literal["ALLOW","REJECT_SYMBOL_DAILY","REJECT_ACCOUNT_DAILY","REJECT_DRAWDOWN_LEVEL_N"] | 是否允许开仓 |
remaining_budget剩余可亏额度 | dict[str, Decimal] | 该 symbol 剩余可亏额度、账户剩余可亏额度 |
cooldown_until熔断解除时间 | datetime | None | 触发熔断后的解除时间 |
只看已实现亏损会让仓位叠加突破上限,必须把当前持仓的 stop 距离也算进"理论最大亏损"
例如 -3% 暂停 1 小时、-5% 暂停当日;恢复时检查是否仍处亏损区间,避免 ping-pong 反复解禁
每次累加完立即 flush,不能依赖进程退出钩子(kill -9 拿不到)
- 内存中维护
revenge_events: dict[event_id, RevengeEvent],event_id = hash(symbol + direction + price_bucket + time_bucket) - 价格重叠判定:
abs(level_a - level_b) / mid < 0.004(±0.4%) sortedcontainers.SortedList按时间戳索引,超 4 小时的事件自动剔除(懒删除)
| 字段 | 类型 | 说明 |
|---|---|---|
symbol交易对 | str | 交易对 |
direction做单方向 | Literal["LONG","SHORT"] | 做单方向 |
key_level关键位 | KeyLevel | 含 zone_low/zone_high 的价格区间 |
system_id系统ID | str | 发起方系统 ID |
proposed_qty请求仓位 | Decimal | 请求的仓位大小 |
| 字段 | 类型 | 说明 |
|---|---|---|
event_id复仇事件ID | str | 归属的复仇事件 ID |
allowed_qty允许仓位 | Decimal | 分配给本次请求的配额(可能被压缩) |
event_state事件状态 | RevengeEventState | 已用配额、参与系统列表、首次触发时间 |
用户关键位本身是矩形区间,必须用区间 IoU 或边界距离判定,否则"宽区间"会被误判为不重叠
首个系统拿主配额(如 60%),后续系统按剩余均分,避免单系统垄断复仇配额
新请求来时先查是否落入已有 event 的重叠区间,落入则合并而非新建,防止同一关键位被切成多个独立事件
共享数据层
ccxt.async_support:REST 拉历史 K 线做冷启动,WS 增量更新 last bar- 内存结构:
dict[(symbol, timeframe), pandas.DataFrame],index=close_time(UTC ms),列=[open, high, low, close, volume] asyncio.Lockper (symbol, tf) 防止读写冲突;订阅者用asyncio.Event通知新 bar 闭合- 持久化:每个 (symbol, tf) 落一个
parquet文件(pyarrow),重启时增量补齐到当前时间
| 字段 | 类型 | 说明 |
|---|---|---|
subscriptions订阅列表 | list[tuple[symbol, timeframe]] | 全局去重后的订阅列表 |
kline_windowK线窗口大小 | int | 每个 (symbol, tf) 维持的最大 bar 数(滑动窗口) |
| 字段/方法 | 类型 | 说明 |
|---|---|---|
get_klines(symbol, tf, n)获取K线 | DataFrame | 最近 n 根已闭合 K 线 |
on_bar_close(symbol, tf)K线闭合监听 | AsyncIterator[Bar] | 异步迭代器,每根新闭合 K 线推送一次 |
BarK线数据体 | dataclass | open_time, close_time, o, h, l, c, v, is_closed |
WS 推的是 tick 级更新,必须等 close_time 抵达才视为新 bar,否则下游拿到反复变动的"未完成 bar"导致信号抖动
所有 timeframe 用交易所返回的 close_time(UTC ms),不要本地时间戳。多 timeframe 联动时,下游必须用 close_time 而非序号对齐
WS 断开重连后必须用 REST 比对最后一根 bar 的 close_time,缺失的中间 bar 用 fetch_ohlcv 回补,否则 EMA/趋势线类指标会错位
- 内存
dict[cache_key, TrendResult]+cachetools.TTLCache或自实现按 source_close_time 淘汰 cache_key = (symbol, timeframe, method, params_hash),params_hash = hashlib.md5(json.dumps(params, sort_keys=True)).hexdigest()[:8]- 持久化:
msgpack落盘到cache/trend/{symbol}_{tf}_{method}.msgpack,重启复用,支持回测
| 字段 | 类型 | 说明 |
|---|---|---|
direction做单方向 | Literal["UP","DOWN","RANGE"] | 趋势方向 |
stage | Literal["initiation","extension","pullback","reversal"] | 趋势阶段 |
extreme_price极值价格 | Decimal | 当前趋势的极值 |
extreme_time极值时间 | int | 极值出现的 close_time(UTC ms) |
pullback_depth回调深度 | Decimal | None | 回调深度(占趋势幅度比例),仅 pullback 阶段有值 |
source_close_time源K线收盘时间 | int | 产生该结果的最新 K 线 close_time |
computed_at计算时间戳 | int | 计算时间戳 |
algo_version算法版本 | str | 算法版本号,用于 cache 失效判定 |
同一 timeframe 在 close_time 不变时结果必然一致,用 wall time TTL 会浪费算力或拿到过期值
浮点参数要 round 到固定精度(如 6 位)后再 hash,否则 1.0 vs 1.0000001 会击穿缓存
cache 文件要带版本号字段 algo_version,算法升级时旧 cache 自动失效但保留供 A/B 对比
- 与 TrendResultCache 同构,但 entry 含
source_close_times列表(关键位由多根 K 线推导) - 示例 key:
BTCUSDT_H4_swing_high_low_a3f2c1(symbol_tf_method_paramshash) - 失效策略:参与计算的任一源 K 线 close_time 变化即整条失效(关键位对源数据高度敏感)
| 字段 | 类型 | 说明 |
|---|---|---|
zone_low区间下边界 | Decimal | 区间下边界(不是单点,强制规范) |
zone_high区间上边界 | Decimal | 区间上边界 |
zone_type支撑/阻力类型 | Literal["support","resistance"] | 支撑或阻力 |
touch_count触碰次数 | int | 历史触碰次数 |
last_touch_time最近触碰时间 | int | 最近一次触碰的 close_time |
strength综合强度评分 | Decimal | 综合强度评分(触碰次数 × 时间衰减) |
origin_method来源算法 | str | 产生该关键位的算法名 |
is_historical是否历史强位 | bool | 是否为历史强位(超出当前价格一定距离) |
所有上下游必须用双值区间,禁止单点。PlanModule 计算止损时用 zone 边界 + buffer,不用 zone 中心
超过当前价格相当距离的历史强位(touch_count ≥ 3)仍要输出,标记 is_historical=True,用于图表显示和止盈目标
不同 method 产生的近邻关键位(区间重叠 >50%)要合并,避免 PlanModule 看到密集"假关键位"
- YAML 配置文件
registry.yaml,pydanticv2 解析校验 - 运行时数据结构:
dict[symbol, list[SystemBinding]],反查dict[system_id, list[symbol]] - 热更新:
watchfiles监听文件变化,diff 出新增/移除后通知 MultiSystemRunner
| 方法 | 返回 | 说明 |
|---|---|---|
get_systems_for_symbol(symbol)查交易对关联系统 | list[system_id] | 查询该交易对激活了哪些系统 |
get_symbols_for_system(system_id)查系统关联交易对 | list[str] | 查询该系统跟踪哪些交易对 |
get_all_subscriptions()获取全部去重订阅 | set[tuple[symbol, timeframe]] | 供 MarketDataHub 去重订阅 |
一个 symbol 可能同时被多个 system 跟踪(这正是 RevengeController 存在的前提),数据结构要同时支持 symbol→systems 和 system→symbols 查询
变更必须原子生效,建议 swap 引用而非原地修改,否则可能出现"某 symbol 暂时无系统跟踪导致持仓失监控"
系统接口层
pydantic.BaseModelv2:类型校验 + 字段约束 + JSON Schema 导出pydantic-settings支持 yaml/env 双源加载@field_validator校验 timeframe 合法性、symbols 格式(BTC/USDT:USDT永续合约写法)- method 配置用 discriminated union:
{type: "ema_combo", params: {...}},pydantic 按 type 路由到对应子 Schema
| 字段 | 类型 | 说明 |
|---|---|---|
exchange交易所 | Literal["binance","bybit",...] | 交易所 |
symbol交易对(实例参数) | str | 启动实例时传入,不在配置文件中定义。同一份配置可复用于不同交易对 |
trend_timeframe趋势分析周期 | str | 趋势级别,如 "4h" |
main_timeframe主交易周期 | str | 主级别,如 "15m" |
kline_windowK线窗口大小 | int | K 线窗口长度 |
trend_method趋势分析方法 | TrendMethodConfig | discriminated union:含 type + params |
keylevel_method关键位识别方法 | KeyLevelMethodConfig | discriminated union:含 type + params |
signal_method入场信号方法 | SignalMethodConfig | discriminated union:含 type + params |
tp_method止盈算法 | TpMethodConfig | discriminated union;可选:fixed_rr(固定盈亏比倍数)/ keylevel_tp(下一关键位近端,不存在时降级为 fixed_rr) |
sl_method止损算法 | SlMethodConfig | 可选:zone_atr(关键区反向边界外 stop_atr_buffer 倍 ATR) |
sizing_method仓位计算 | SizingMethodConfig | 可选:fixed_risk_pct(qty = balance × risk_pct / |entry − stop|) |
trade_direction做单方向 | Literal["TREND_FOLLOW","LONG_ONLY","SHORT_ONLY","COUNTER_TREND"] | TREND_FOLLOW:趋势方向一致时才开仓;LONG_ONLY / SHORT_ONLY:单方向;COUNTER_TREND:逆势 |
risk_pct风险敞口 | Decimal | 每笔最大亏损占账户余额的比例,如 0.01 = 1%;供 sizing_method 计算仓位用 |
stop_atr_bufferATR 缓冲倍数 | Decimal | 止损在关键区边界外额外留出的 ATR 倍数,默认 0.5 |
min_rr_ratio最小盈亏比 | Decimal | 用第一档 TP 计算;低于此值时 PlanModule 短路 |
auto_trade是否真实下单 | bool | False(默认):只推通知、记录计划;True:向交易所发送真实订单 |
session_end_time会话结束时间 | str | None | UTC 时间字符串,如 "23:00";到达后不再新开仓 |
discord_webhookDiscord 通知地址 | str | None | Notifier 推送信号、成交、平仓三类通知 |
不是所有交易所支持所有周期(Bybit 没有 2h),启动时调用 exchange.timeframes 校验,避免运行期才报错
SystemConfig 要能产生稳定的 config_hash,供 TrendResultCache/KeyLevelCache 作为 params_hash 来源,保证缓存 key 唯一
流水线模块(可插拔)
pandas+numpy做 K 线计算;EMA 用pandas.Series.ewm- 形态识别(Vegas 隧道、趋势线)用纯 numpy 向量化,不引入 ta-lib(C 依赖部署麻烦)
- 算法注册表:
TREND_METHODS: dict[str, Callable[[DataFrame, params], TrendResult]],可插拔 - 可选算法:裸K结构、EMA组(三均线/Vegas隧道)、趋势线、形态识别(高低点序列)
| 字段 | 类型 | 说明 |
|---|---|---|
klinesK线数据 | DataFrame | 来自 MarketDataHub 的趋势级别 K 线 |
params算法参数 | dict | 算法参数 |
cached_result缓存结果 | TrendResult | None | 来自 TrendResultCache,source_close_time 未变时直接复用 |
| 字段 | 类型 | 说明 |
|---|---|---|
should_short_circuit是否触发短路 | bool | direction=RANGE 时为 True,下游流水线停止执行 |
strength综合强度评分 | Decimal | None | 趋势强度(预留字段,当前版本不输出) |
返回 direction=RANGE 时 should_short_circuit=True,PlanModule 不应被迫处理"无方向"输入
pullback_depth 仅在 stage=pullback 时有意义,其他阶段为 None,避免下游误用非法值
当前版本 strength 不参与下游逻辑,但 dataclass 保留字段,避免后续加字段触发上下游重构
- 摆动高低点:
scipy.signal.argrelextrema或自实现 N 根左/右 fractal 判定 - 整数关口/均线:直接计算 EMA200 等并构造 zone,zone 宽度 =
k * ATR(N)自适应波动率 - 区间合并用排序 + 扫描线(关键位数量通常 <100,简单实现足够,无需区间树)
- 算法注册表:
KEYLEVEL_METHODS: dict[str, Callable[[DataFrame, TrendResult, params], KeyLevelResult]]
| 字段 | 类型 | 说明 |
|---|---|---|
klinesK线数据 | DataFrame | 主级别 K 线 |
trend_result趋势结果 | TrendResult | 用于过滤无效方向的关键位(上行趋势只保留 support) |
params算法参数 | dict | 算法参数 |
cached_result缓存结果 | KeyLevelResult | None | 来自 KeyLevelCache,source_close_times 未变时复用 |
| 字段 | 类型 | 说明 |
|---|---|---|
levels关键位列表 | list[KeyLevel] | KeyLevel 见缓存章节 |
age_bars新鲜度(距当前K线数) | int | 关键位距当前 K 线的 bar 数,衡量"新鲜度" |
should_short_circuit是否触发短路 | bool | levels 为空时为 True |
固定 % 或固定点数在不同币种上失真,用 zone_width = k * ATR(N) 让区间随波动率缩放,同样参数在 BTC 和 山寨币上都能合理运作
上行趋势中只保留 support zones(阻力位保留供止盈参考),避免给 SignalModule 喂大量逆势位导致信号噪声
- 形态识别(吞没、锤子、孕线)用纯 numpy 实现,每个 pattern 一个独立函数
detect_engulfing(df) -> bool[] - 算法注册表:
SIGNAL_METHODS: dict[str, Callable] - 信号评分用加权求和(不引入 ML,保持可解释性)
- 可选算法:金K/银K(蜡烛形态)、K 线结构形态、均线交叉信号
| 字段 | 类型 | 说明 |
|---|---|---|
klinesK线数据 | DataFrame | 主级别最近 N 根已闭合 K 线 |
trend_result趋势结果 | TrendResult | 用于方向过滤 |
key_levels关键位列表 | list[KeyLevel] | 候选关键位 |
params算法参数 | dict | 算法参数 |
| 字段 | 类型 | 说明 |
|---|---|---|
signal_type信号类型 | str | 信号类型,如 "engulfing_bullish" |
signal_bar_close_time信号K线收盘时间 | int | 信号 K 线的 close_time,用于去重 |
signal_bar_ohlc信号K线OHLC快照 | dict | 信号 K 线的 OHLC 快照 |
related_key_level关联关键位 | KeyLevel | 信号关联的关键位(必须绑定) |
direction做单方向 | Literal["LONG","SHORT"] | 做单方向 |
confidence信号置信度 | Decimal | 信号置信度评分(0–1) |
raw_features原始特征值 | dict | 原始特征值,供 ReviewLogger 存档 |
should_short_circuit是否触发短路 | bool | 无合格信号时为 True |
单纯"K 线形态"不构成信号,必须发生在某个 key_level 的 zone 内或边界,绑定后才进入 PlanModule
禁止"未闭合 K 线产生信号"导致重复触发,bar_close_time 必须等于本轮 close_time
同一 signal_bar_close_time + 同一 key_level 只能产生一次 signal,防止流水线被重复触发时重复下单
decimal.Decimal全程,避免浮点精度坑(交易所 price/qty 步长校验严格)- 用
ccxt.market(symbol)拿precision/limits,对 entry/stop/qty round 到合规精度 - 仓位 sizing:fixed-fractional,
risk_amount = balance * risk_pct,qty = risk_amount / abs(entry - stop) - 止盈算法可选:固定比例 TP(min_rr_ratio × stop_distance)或关键位 TP(下一个阻力 zone 边界)
| 字段 | 类型 | 说明 |
|---|---|---|
signal入场信号 | Signal | 来自 SignalModule |
trend_result趋势结果 | TrendResult | 用于止盈目标参考 |
key_levels关键位列表 | list[KeyLevel] | 用于关键位 TP 算法 |
account_state账户状态 | AccountState | 当前余额,用于仓位计算 |
params算法参数 | dict | min_rr_ratio, risk_pct, stop_buffer_atr_mult, tp_method |
| 字段 | 类型 | 说明 |
|---|---|---|
entry_price入场价 | Decimal | 入场价(已对齐精度) |
entry_type下单类型 | Literal["LIMIT","MARKET"] | 下单类型 |
stop_price止损价 | Decimal | 止损价(zone 外侧 + ATR buffer) |
take_profits止盈档位 | list[TakeProfit] | 止盈档位列表(含 price、qty_ratio) |
qty下单数量 | Decimal | 下单数量(已对齐步长) |
rr_ratio实际盈亏比 | Decimal | 盈亏比(以第一档 TP 计算) |
expected_loss预期最大亏损 | Decimal | 预期最大亏损金额 |
plan_id计划唯一ID | str | 唯一 ID,作为 OrderManager 幂等键 |
expires_at计划有效期 | int | 计划有效期(UTC ms),超时未成交则取消 |
should_short_circuit是否触发短路 | bool | rr_ratio < min_rr_ratio 时为 True |
信号关联关键位是区间,止损必须落在 zone 反向边界外 k * ATR,不能用 zone 中心,否则正常波动会被洗出
用最远 TP 会虚高 RR,导致大量低质量信号通过 min_rr_ratio 门槛,必须用第一档 TP 保守计算
qty 必须先按 amount_step round down,再校验 qty * price >= min_notional,否则下单会被交易所拒绝
ccxt.async_support的create_order/cancel_order/fetch_order- 订单状态机:
PENDING → SUBMITTED → FILLED / EXPIRED / CANCELED / REJECTED,用枚举 + 跳转表 - 本地订单簿用
aiosqlite(SQLite 是文件级,零运维,最适合订单状态机)或 JSON 行式追加 - WebSocket user data stream 接收 ORDER_TRADE_UPDATE,REST 轮询作为兜底
| 字段 | 类型 | 说明 |
|---|---|---|
plan交易计划 | TradePlan | 来自 PlanModule 的完整交易计划 |
idempotency_key幂等键 | str | 防止重复提单,通常用 plan_id |
| 字段 | 类型 | 说明 |
|---|---|---|
order_id本地订单ID | str | 本地唯一 ID |
exchange_order_id交易所订单ID | str | 交易所返回的订单 ID |
plan_id计划唯一ID | str | 关联的交易计划 |
status订单状态 | Literal["PENDING","SUBMITTED","FILLED","EXPIRED","CANCELED","REJECTED"] | 当前状态 |
filled_qty实际成交量 | Decimal | 实际成交量 |
avg_fill_price成交均价 | Decimal | 成交均价 |
event状态变更事件 | Literal["FILLED","EXPIRED","REJECTED","CANCELED"] | 推送给 PositionMonitor / ReviewLogger |
网络重试/进程崩溃重启时必须用 idempotency_key 查本地状态,已 SUBMITTED 的不再重发,否则极易双倒
只信 WS 会丢事件(断线期),只用 REST 延迟高,必须两者合一,事件去重用 exchange_order_id + update_time
LIMIT 单到达过期时间未成交要主动 cancel 并标记 EXPIRED,留着会在突变行情中突然成交导致计划失效
- WebSocket 持仓推送 + 价格推送双订阅(mark price stream)
- 移动止损用本地计算 + REST 修改止损单,不依赖交易所 trailing stop(各交易所参数不一致)
- 状态机:
OPEN → PARTIAL_TP → MOVED_STOP → CLOSED - 单独 asyncio Task,priority 高于分析流水线,确保毫秒级响应
| 字段 | 类型 | 说明 |
|---|---|---|
position持仓信息 | Position | 来自 OrderManager FILLED 事件 |
plan交易计划 | TradePlan | 原始计划(含 TP 列表、移损规则) |
live_price_stream实时价格流 | AsyncIterator[Price] | 实时价格流(mark price) |
| 字段 | 类型 | 说明 |
|---|---|---|
type事件类型 | Literal["TP_HIT","SL_HIT","STOP_MOVED","CLOSED"] | 事件类型 |
price触发价格 | Decimal | 触发价格 |
qty_closed本次平仓量 | Decimal | 本次平仓量 |
realized_pnl本次实现盈亏 | Decimal | 本次实现盈亏 |
closed_position完整平仓记录 | ClosedPosition | 平仓后完整记录,传给 ReviewLogger |
仅靠交易所 SL 在闪崩/API 故障时可能滑点严重;本地监控达到 SL 价格立即用市价平仓兜底
先创建新止损单成功,再撤旧单;反过来操作可能瞬间裸奔(无止损单保护)
判定 TP/SL 必须用与下单同源的价格(mark price vs last price 在合约里差异巨大),必须与 PlanModule 约定一致
- 每笔交易写一个 JSON 文件
reviews/{symbol}/{plan_id}.json,含完整生命周期 - 同时追加到
reviews/index.ndjson供后续批量分析(每行一条 summary) - 关联快照:触发信号时把 trend_result / key_levels / signal / klines_snapshot 一并存档,回测可复现
- Discord 通知:
aiohttpPOST webhook,含交易摘要 + matplotlib K 线图附件
| 字段 | 类型 | 说明 |
|---|---|---|
lifecycle交易生命周期 | TradeLifecycle | 汇总 plan + orders + position_events + close_result |
context_snapshot市场上下文快照 | ContextSnapshot | 触发时的市场上下文(趋势/关键位/信号/K线快照) |
| 字段 | 类型 | 说明 |
|---|---|---|
plan_id计划唯一ID | str | |
symbol交易对 | str | |
direction做单方向 | Literal["LONG","SHORT"] | |
entry / exit入场/出场价 | Decimal | 实际成交的入场/出场价 |
pnl / pnl_pct盈亏金额/百分比 | Decimal | 盈亏金额和百分比 |
duration持仓时长(秒) | int | 持仓时长(秒) |
max_favorable_excursion最大有利幅度 MFE | Decimal | 最大有利幅度(MFE) |
max_adverse_excursion最大不利幅度 MAE | Decimal | 最大不利幅度(MAE) |
exit_reason平仓原因 | Literal["TP","SL","MANUAL","EXPIRED"] | 平仓原因 |
review_file_path归档文件路径 | str | 完整 JSON 归档路径 |
最大有利/不利幅度是后续优化止损止盈的关键指标,必须在 PositionMonitor 阶段就持续记录,不能事后从 K 线还原(粒度已丢失)
context_snapshot 一旦写入不再改动,文件名带 schema_version,算法迭代后老 review 仍可读,不会因 Schema 变化报错
Discord 推送/文件写入失败要 try/except + 重试队列,归档失败不能影响下一笔交易的执行
跨模块统一约定
- 统一时间戳
所有*_time字段一律 UTC 毫秒整数,展示层再转时区。禁止混用 datetime 对象和时间戳 - 统一价格类型
所有价格用decimal.Decimal,序列化用 str;DataFrame 内部可用 float,但出口必须转 Decimal - 统一短路协议
所有流水线模块输出含should_short_circuit: bool,由宿主 MultiSystemRunner 统一处理,各模块不各自 raise - 统一 params_hash
所有 cache 模块共用compute_params_hash(params: dict) -> str工具函数,浮点参数先 round 到 6 位精度,保证可复现
与 trading_framework_v1 的差异
对比来源:trading_framework_v1.html(框架接口协议层)vs 本文档(Opus 4.7 技术选型层)。差异分为三类:框架文档有而本文缺失、本文有而框架文档未涉及、字段命名/语义不同。
| 字段 | 中文名 | 说明 | 补充建议 |
|---|---|---|---|
system_id系统唯一标识 |
系统唯一标识 | 框架文档要求每个系统实例有唯一 ID(如 "sanbuqu_v1"),用于日志标签和状态追踪 | 需补充 |
datafeed_lib / datafeed_symbol数据源配置 |
数据源配置 | 框架文档有 datafeed_lib、datafeed_exchange_id、datafeed_symbol、refresh_mode、refresh_delay_seconds 五个数据采集参数 |
需补充 |
trend_methods_aux / signal_methods_aux辅助方法列表 |
辅助方法列表 | 框架文档支持主方法 + 辅助方法列表,辅助方法用于信号置信度加权;本文只有单一 method 字段 | 需补充 |
trade_direction_policy顺势/逆势 |
做单方向策略 | 框架文档明确区分顺势/逆势交易,逆势需要更高信号质量阈值;本文未体现 | 需补充 |
entry_style左侧/右侧入场 |
入场时机风格 | 框架文档区分左侧(首次触碰)和右侧(二次确认)入场,SignalModule 输出 entry_timing.side 与之匹配 |
需补充 |
take_profit_strategy_default默认止盈策略 |
默认止盈策略 | 框架文档将止盈策略(KeyLevel / TrailingMA / Equidistant)提升到系统配置层,本文仅在 PlanModule params 中提及 | 可合并 |
risk_mode / risk_value / margin_mode / leverage仓位与杠杆配置 |
仓位与杠杆 | 框架文档将 risk_mode(fixed_amount / equity_percent)、leverage、margin_mode 纳入系统配置;本文 PlanModule 仅有 risk_pct 参数 | 需补充 |
protection_check_delay_seconds止损确认延迟 |
保护参数 | 框架文档有四个保护参数:止损确认延迟、WebSocket 故障超时、订单取消超时、取消价格缓冲;本文未覆盖 | 需补充 |
| 维度 | 框架文档 | 本文档(Opus) |
|---|---|---|
| 生命周期状态 | 有 status 字段:ACTIVE / HIDDEN(价格远离超 3×ATR) / EXPIRED(超出 kline_window),支持复活机制 |
无 status,用 is_historical 布尔值区分当前/历史位,语义更简化 |
| 形成原因 | 有 reasons 字段(列表),记录该关键位形成的多个理由,PlanModule 按 reasons 数量筛选置信度 |
无 reasons,用 touch_count + strength 替代,但缺少"理由去重计数"逻辑 |
| 多周期融合 | 有 timeframes 字段,记录哪些周期共同确认了该关键位(±0.4% 合并),多周期=更高置信 |
无 timeframes,仅有 origin_method,跨周期融合逻辑未体现 |
| 字段 | 框架文档语义 | 本文差异 |
|---|---|---|
signal_subtype信号子类型 |
细化信号类型(如 bullish_engulfing 的子类型),用于统计分析 | 本文无 subtype,仅有 signal_type |
pattern_prices形态K线OHLC |
结构化记录形态的 open/high/low/close,止损价直接从 pattern_prices.low/high 取,有明确的固定止损规则 | 本文用 signal_bar_ohlc(dict),止损规则由 PlanModule 自行计算,不强制绑定形态价格 |
entry_timing.side左侧/右侧标记 |
标记本次信号是首次触碰(左侧)还是二次确认(右侧),与 SystemConfig.entry_style 匹配才通过 | 本文无此字段,左侧/右侧概念未引入 |
框架文档:先按市价(pattern_prices.close)计算 RR,不足则降级为限价单(多头取 low + 50% 回撤,空头对称),两步都不足才短路。本文只提到 entry_type 字段,未定义降级逻辑。
框架文档:多头止损 = pattern_prices.low,空头止损 = pattern_prices.high,两步试价期间止损不变。本文使用 zone 外侧 + ATR buffer,是另一种止损方案,与框架文档不同。
框架文档:len(unique(reasons)) < threshold 则不生成计划(要求多个理由支撑)。本文依赖 confidence 评分,但无明确的 reasons 计数机制。
框架文档 → 本文档:min_risk_reward → min_rr_ratio | stop_loss_price → stop_price | t1_price → take_profits[0].price | position_size → qty
下单前 REST 查询当前交易所杠杆,与 SystemConfig.leverage 不符时先调用 SET LEVERAGE API,再下单,防止因账户状态遗留导致仓位暴露不符预期。
成交后 15 秒内 REST 确认止损单已存在,若缺失则重新挂单;重试失败触发 emergency_flatten(市价减仓平仓)。
WS 断开超过 websocket_failsafe_seconds 且 REST 也失败时,以 stop_loss_price 触发本地市价平仓,记录 close_reason = "connection failsafe"。
收到成交通知时,若 filled_price 已穿越 stop_loss_price,立即以实际成交价平仓,不等止损单回调,防止跳空滑点扩大。
框架文档要求在配置的每日结束时间按比例强平持仓,本文未提及该机制(PositionMonitor 仅处理 TP/SL/移损)。
| 内容 | 说明 |
|---|---|
| MFE / MAE 追踪 | 最大有利/不利幅度在持仓期间持续更新并归档,框架文档 ReviewLogger 仅提"复盘记录",无此字段 |
| plan_id / expires_at | 本文明确将 plan_id 作为幂等键贯穿 M4→M5A→M5C,框架文档未定义 plan 的唯一标识方案 |
| age_bars(KeyLevel) | 关键位"新鲜度"(距当前 K 线数),框架文档无此字段 |
| auto_trade(SystemConfig) | 框架文档隐含此概念但未命名字段;本文明确为 bool 开关 |
| discord_webhook(SystemConfig) | 框架文档通知渠道未纳入 SystemConfig Schema,本文直接放入配置 |
| MarketDataHub 断线补洞逻辑 | 本文明确用 REST 比对 close_time 回补缺失 bar,框架文档未详细描述 |
| cache 持久化方案(msgpack/parquet) | 本文明确了 TrendResultCache / MarketDataHub 的持久化格式;框架文档只定义接口不涉及实现 |