trading/doc_02_strategy_backtest.md

25 KiB
Raw Permalink Blame History

量化策略开发与向量化回测详解

quant_strategy_backtest_demo.py 学习文档

目标读者:零基础量化入门者
配套文件quant_strategy_backtest_demo.py
前置知识:建议先阅读 doc_01_data_pipeline.md
系列位置:第 2 篇 — 策略开发与回测篇


目录

  1. 本篇概览
  2. 合成价格数据:几何布朗运动
  3. 技术指标详解
  4. 策略 A双均线金叉/死叉
  5. 策略 BRSI 均值回归
  6. 向量化回测引擎
  7. 绩效指标详解
  8. 滚动前向验证与参数优化
  9. 收益率分布分析
  10. 术语速查表

1. 本篇概览

这篇文档对应量化交易的第二个核心环节:"有了干净的数据,怎么用它来赚钱?"

整个流程可以归纳为:

干净的价格数据
      │
      ▼
计算技术指标均线、RSI、MACD、布林带
      │
      ▼
制定交易规则 → 生成交易信号(买/卖/空仓)
      │
      ▼
向量化回测引擎(模拟历史交易,扣除成本)
      │
      ▼
计算绩效指标(夏普、最大回撤、胜率…)
      │
      ▼
前向验证(防止过拟合)→ 判断策略是否值得继续研究

两种策略类型,代码各实现一种:

类型 代表指标 逻辑 适合市场环境
趋势跟随 (Trend Following) 移动均线、MACD 跟着趋势走,买涨跟涨 有明显趋势的牛市/熊市
均值回归 (Mean Reversion) RSI、布林带 逆势操作,跌多了买、涨多了卖 震荡盘整市场

2. 合成价格数据:几何布朗运动 (Geometric Brownian Motion)

2.1 为什么用模拟数据?

  • 真实数据需要付费数据源,这个 Demo 用合成数据让所有人都能直接运行
  • 合成数据有"已知"的统计特性(我们自己设定的 μ 和 σ),便于验证代码正确性
  • 通过调整参数可以模拟不同市场环境(牛市、熊市、震荡市)

2.2 GBM 公式直观理解

几何布朗运动 (GBM) 是描述股价随机游走的经典数学模型也是期权定价公式Black-Scholes的基础。

离散形式(代码实现的):

P_t = P_{t-1} \times e^{\left(\mu - \frac{\sigma^2}{2}\right) \cdot \Delta t + \sigma \cdot \sqrt{\Delta t} \cdot \varepsilon_t}

用大白话解释这个公式:

  • $P_t$:今天的价格
  • $P_{t-1}$:昨天的价格
  • $\mu$mu年化漂移率 (Annual Drift),相当于股票的"长期趋势倾斜度"。\mu = 0.10 表示每年平均上涨 10%
  • $\sigma$sigma年化波动率 (Annual Volatility),表示价格的"抖动剧烈程度"。\sigma = 0.25 表示年化波动 25%
  • $\Delta t = 1/252$:一个交易日是一年的 1/252
  • $\varepsilon_t \sim N(0,1)$:每天的随机冲击,从标准正态分布中随机抽取

直觉:每天的价格变化 = 确定性漂移(趋势)+ 随机波动(噪声)

# 代码中的实现
dt = 1.0 / 252
epsilon = np.random.randn(n_days)                          # 随机冲击
log_returns = (mu - 0.5 * sigma**2) * dt + sigma * np.sqrt(dt) * epsilon
prices = s0 * np.exp(np.cumsum(log_returns))               # 累积乘积 → 价格路径

3. 技术指标详解 (Technical Indicators)

技术分析 (Technical Analysis) 的核心假设:过去的价格和成交量模式包含了预测未来走势的信息。技术指标就是从原始价格数据中提炼这些模式的数学工具。

⚠️ 注意:技术分析在学术界存在争议,有效市场假说 (Efficient Market Hypothesis) 认为价格已经反映了所有信息,技术分析无法持续获得超额收益。但在实践中,许多量化基金仍然将技术指标作为信号的一部分。

3.1 SMA 简单移动平均线 (Simple Moving Average)

公式

\text{SMA}_n(t) = \frac{P_t + P_{t-1} + \cdots + P_{t-n+1}}{n}

直觉:取最近 n 天收盘价的算术平均数,形成一条平滑的"趋势线"。

关键参数 n 的选择

窗口 n 代号 特性
5、10 天 短期 (Short-term) 贴近价格,反应快,但"噪声"多
20、30 天 中期 (Medium-term) 月线级别的趋势
60、120 天 长期 (Long-term) 季度级别的主要趋势,滞后但稳定
250 天 年线 (Annual) 区分牛熊市的重要参考线

代码

def sma(prices, window):
    return prices.rolling(window=window).mean()
    # rolling(window) = 创建一个滑动窗口
    # .mean() = 对窗口内的值取平均

前 n-1 天为什么没有值?
计算 20 日均线需要 20 天数据,第 1-19 天的均线值是 NaN(数据不够)。第 20 天才有第一个有效均线值。

3.2 EMA 指数移动平均线 (Exponential Moving Average)

公式

\text{EMA}_t = \alpha \cdot P_t + (1 - \alpha) \cdot \text{EMA}_{t-1}, \quad \alpha = \frac{2}{n+1}

与 SMA 的核心区别SMA 对窗口内所有日期权重相等EMA 给近期数据更高权重,越新的数据越重要。

形象比喻
SMA 像"民主投票"——每一天的发言权相等;
EMA 像"资历制"——越近的日期说话越算数。

结果EMA 比 SMA 对最近的价格变化反应更快,但也更"神经质"(更多小幅震动)。

def ema(prices, span):
    return prices.ewm(span=span, adjust=False).mean()
    # ewm = exponentially weighted moving (指数加权移动)
    # span 对应公式中的 nα = 2/(span+1)

3.3 RSI 相对强弱指数 (Relative Strength Index)

RSI 解决的问题:量化"最近一段时间,涨的力量和跌的力量哪个更强?"

计算步骤(以 14 天为例):

  1. 计算每天的涨跌:delta = close.diff()
  2. 上涨天的涨幅 (Gain)delta > 0 的部分
  3. 下跌天的跌幅 (Loss)delta < 0 的部分(取正值)
  4. 用指数平均分别平滑 Gain 和 Lossavg_gainavg_loss
  5. 计算相对强弱 RS = avg_gain / avg_loss
  6. 转换:RSI = 100 - 100 / (1 + RS)

结果范围RSI 永远在 0 到 100 之间。

RSI 值 解读
> 70 超买 (Overbought):涨势过强,可能即将回调
50~70 偏强,多头占优
50 中性 (Neutral):多空势均力敌
30~50 偏弱,空头占优
< 30 超卖 (Oversold):跌势过强,可能即将反弹

形象比喻RSI 就像一个"疲劳度量表"。跑得太快(涨太猛)= 超买,该休息了;跌得太惨 = 超卖,该反弹了。

def rsi(prices, window=14):
    delta    = prices.diff()
    gain     = delta.clip(lower=0)      # 只保留正值(涨的部分)
    loss     = -delta.clip(upper=0)     # 只保留负值并取正(跌的部分)
    avg_gain = gain.ewm(alpha=1.0/window, adjust=False).mean()
    avg_loss = loss.ewm(alpha=1.0/window, adjust=False).mean()
    rs       = avg_gain / avg_loss      # 相对强弱
    return 100 - (100 / (1 + rs))      # 映射到 0~100

3.4 MACD 指数平滑异同移动平均线

MACD 解决的问题:用两条不同速度的 EMA 的"差距"来衡量趋势动能的变化。

三条线

名称 计算 含义
MACD 线 (DIF) EMA(12) - EMA(26) 快线(短期)减慢线(长期),反映近期动能
信号线 (DEA) EMA(9) of MACD 线 MACD 线的 9 日平均,平滑后的趋势
柱状图 (Histogram) MACD 线 - 信号线 两线之差,放大了趋势变化

交叉信号

信号 含义
金叉 (Golden Cross)MACD 线上穿信号线 买入信号,趋势转强
死叉 (Death Cross)MACD 线下穿信号线 卖出信号,趋势转弱
柱状图由负转正 多头动能开始占优
柱状图由正转负 空头动能开始占优

形象比喻MACD 像赛跑中两个运动员的"领先差距"。快选手(短期均线)超过慢选手(长期均线)→ 金叉;被超过 → 死叉。

3.5 布林带 (Bollinger Bands)

布林带解决的问题:建立价格的"正常波动区间",判断当前价格是"正常"还是"过度偏离"。

三条线

\text{中轨 (Middle Band)} = \text{SMA}(20)
\text{上轨 (Upper Band)} = \text{SMA}(20) + 2\sigma_{20}
\text{下轨 (Lower Band)} = \text{SMA}(20) - 2\sigma_{20}

其中 \sigma_{20} 是 20 日滚动标准差 (Rolling Standard Deviation)。

含义:在正态分布假设下,价格有 95% 的概率在上下轨之间运动。

价格位置 信号
触碰下轨 超卖区域,考虑买入
触碰上轨 超买区域,考虑卖出
带宽收窄 (Band Squeeze) 波动率降低,预示即将有大行情
带宽突然扩大 大行情开始,追方向

4. 策略 A双均线金叉/死叉 (MA Crossover)

4.1 策略逻辑

这是最经典的趋势跟随策略,逻辑非常直观:

短期均线20日代表"近期走势"
长期均线60日代表"长期趋势基准"

金叉Golden Cross:
  短期均线 从下方穿过 长期均线 → 近期走势开始强于长期 → 买入信号 🟢

死叉Death Cross:
  短期均线 从上方穿过 长期均线 → 近期走势开始弱于长期 → 卖出平仓信号 🔴

4.2 为什么用"均线上穿"而不是"价格本身"

直接看价格太噪声(每天都有小涨小跌),用均线平滑后只关注真正的趋势转折。这是"降噪 (Noise Filtering)"的思想。

4.3 信号生成代码详解

# 计算两条均线
ma_short = sma(price, 20)    # 短期均线(快线)
ma_long  = sma(price, 60)    # 长期均线(慢线)

# 原始信号:短线在长线上方为 1看多否则为 0
raw_signal = (ma_short > ma_long).astype(int)

# ⭐ 关键步骤:信号向后移动一天,避免前视偏差
# 今天收盘后判断方向,明天开盘才能交易
# 如果不 shift(1),等于"今天收盘价知道后立刻成交"——这在现实中做不到
ma_signal = raw_signal.shift(1).fillna(0)

为什么一定要 shift(1)

假设均线在今天收盘后16:00产生了金叉信号。现实中你看到这个信号后最早也只能在明天的开盘9:30才能下单。如果不 shift相当于"今天收盘价一出来就瞬间买入",这是不可能做到的,这叫未来函数 (Future Function)前视偏差 (Lookahead Bias)

4.4 策略的适用条件与局限

适合

  • 有明显趋势的市场(牛市单边上涨,熊市单边下跌)
  • 日线、周线级别的中长线交易

不适合

  • 震荡市(横盘整理):频繁金叉死叉,反复"买高卖低",交易成本侵蚀利润
  • 高频交易:滞后性太大

5. 策略 BRSI 均值回归 (RSI Mean Reversion)

5.1 策略逻辑

这是逆势策略 (Contrarian Strategy),核心思想:

"市场短期会过度反应,极端走势终将回归正常。"
"Markets overreact in the short term, and extreme moves will eventually revert."

RSI < 30超卖市场跌过头了 → 做多(买入),等反弹 🟢
RSI > 70超买市场涨过头了 → 做空(卖出),等回落 🔴

出场规则Exit Rules
  多仓持有中RSI 回升到 50 以上 → 平仓多头(涨回去了,获利了结)
  空仓持有中RSI 回落到 50 以下 → 平仓空头(跌回去了,获利了结)

5.2 均值回归的理论基础

均值回归 (Mean Reversion) 是统计学中的一个概念:当一个量偏离其长期均值过远时,有向均值靠拢的倾向。

在金融市场中表现为:

  • 市场恐慌性下跌后,往往会有技术性反弹
  • 过度追涨后,往往会有获利回吐
  • 短期波动会均值回归,但长期趋势可能持续(这就是趋势策略存在的理由)

5.3 为什么 RSI 策略在本 Demo 中表现差?

看本 Demo 的回测结果RSI 策略的夏普比率为负数,而 Buy & Hold 为正。

原因很简单:我们的模拟数据用的是 GBM设定了 mu = 0.10(年化 10% 的正漂移)。这意味着数据有明显的上涨趋势。均值回归策略在趋势市中会频繁做空RSI 超买),而市场偏偏持续上涨,造成亏损。

结论:没有"万能"策略。策略的有效性依赖市场环境:

  • 趋势市 → 趋势策略赢
  • 震荡市 → 均值回归策略赢
  • 如何判断当前市场状态?这正是更高级的量化研究课题(策略择时、机制识别)

6. 向量化回测引擎 (Vectorized Backtest Engine)

6.1 什么是向量化?

向量化 (Vectorization) 指用 numpy/pandas 的数组运算一次性计算所有日期的结果,而不是用 Python for 循环逐日计算。

# 非向量化(慢)
results = []
for i in range(len(prices)):
    r = signal[i] * daily_return[i]  # 逐日计算
    results.append(r)

# 向量化pandas 内部是 C 语言实现)
strategy_returns = signal * daily_returns  # 整个序列一次运算

速度差异:对于 1000 天的数据,向量化通常比 for 循环快 100-1000 倍。

6.2 回测核心逻辑

# 1. 每日收益率
daily_ret = prices.pct_change().fillna(0)

# 2. 策略每日收益率(持仓方向 × 市场收益率)
#    signal = 1 时,策略收益 = 市场收益(同向)
#    signal = 0 时,策略收益 = 0空仓不参与
#    signal = -1 时,策略收益 = -市场收益(反向做空)
strat_ret_gross = signal * daily_ret

# 3. 扣除交易成本
position_change = signal.diff().abs()    # 仓位变化量(换手)
cost = position_change * cost_per_trade  # 每次换手扣除成本

# 4. 净收益率
strat_ret_net = strat_ret_gross - cost

# 5. 净值曲线(每日净收益率连乘)
equity = initial_capital * (1 + strat_ret_net).cumprod()

6.3 交易成本的重要性

成本模型 (Cost Model)

成本项 说明 典型值A 股)
佣金 (Commission) 券商按交易金额收取 万分之三0.03%)单边
印花税 (Stamp Duty) A 股卖出时收取 万分之五0.05%),仅卖出
滑点 (Slippage) 信号价格与实际成交价的差 视流动性而定,通常 1-5 bp

一个典型的坑:回测不计成本,结果漂亮,一上实盘就亏钱。
高频交易尤其明显:哪怕每次 0.1% 的成本,一天交易 10 次就是 1% 的消耗。

6.4 净值曲线 (Equity Curve) 的含义

初始资金 100万元
      │ 每天乘以 (1 + 当天净收益率)
      ▼
...第 n 天的净值 = 100万 × (1 + r_1) × (1 + r_2) ×× (1 + r_n)

净值曲线是策略"假设从第一天以 100 万元开始运行"的账户价值随时间变化曲线


7. 绩效指标详解 (Performance Metrics)

不能只看"总收益率"——一个策略可能靠运气在某段时间暴赚,但实际上风险极高。需要多维度评估。

7.1 总收益率 & 年化收益率 (CAGR)

总收益率 (Total Return)

\text{Total Return} = \frac{V_{end} - V_{start}}{V_{start}}

年化复合增长率 (CAGR, Compound Annual Growth Rate)

\text{CAGR} = \left(\frac{V_{end}}{V_{start}}\right)^{1/\text{years}} - 1

将总收益率折算成"每年平均增长多少",消除回测时间长短的影响,便于不同策略横向比较。

例子5 年总收益 100%CAGR = $(2)^{1/5} - 1 \approx 14.9%$(不是 100%/5 = 20%,因为有复利效应)

7.2 夏普比率 (Sharpe Ratio)

\text{Sharpe} = \frac{\text{年化收益率} - r_f}{\text{年化波动率}} = \frac{\bar{r} - r_f}{\sigma} \times \sqrt{252}
  • $r_f$:无风险利率 (Risk-Free Rate),通常用国债利率,代码中简化为 0
  • 含义:每承担 1 单位波动率,获得多少超额收益
夏普比率 评级
< 0 不如存钱(还有风险)
0 ~ 1 可接受,但不优秀
1 ~ 2 良好,值得关注
> 2 优秀(或者有数据问题,需检查)
> 3 通常意味着回测存在 bug 或严重过拟合

局限:夏普比率假设收益率服从正态分布。如果有"小涨长期 + 偶尔大跌"的特征(如卖期权策略),夏普比率会虚高。

7.3 索提诺比率 (Sortino Ratio)

\text{Sortino} = \frac{\text{CAGR} - r_f}{\text{下行波动率 (Downside Std)}}

与夏普的区别:夏普把所有波动(涨和跌)都视为"风险";索提诺只把亏损日的波动视为风险(正向波动是好事,不应被惩罚)。

对于有正偏态(不对称上涨)的策略,索提诺比夏普更公平。

7.4 最大回撤 (Maximum Drawdown)

\text{Drawdown}(t) = \frac{V_t - \max_{s \leq t} V_s}{\max_{s \leq t} V_s}
\text{Max Drawdown} = \min_t \text{Drawdown}(t)

含义:从账户价值的历史最高点,最大跌了多少百分比。

为什么这是最重要的风险指标之一?

  • 假设你的策略总收益 200%,但中间经历了 -70% 的最大回撤
  • 你有没有能力扛过 -70% 而不放弃?(心理和资金层面)
  • 如果你在最大回撤开始前一年进场,可能等了很多年才回本

经验规则:最大回撤超过 30% 的策略,很少有人能真实执行。

7.5 卡玛比率 (Calmar Ratio)

\text{Calmar} = \frac{\text{CAGR}}{|\text{Max Drawdown}|}

每承担 1 单位的最大回撤,获得多少年化收益。适合用于比较不同策略的"收益/最大损失"性价比。

7.6 胜率 & 盈亏比 (Win Rate & Profit Factor)

日胜率 (Daily Win Rate):有收益的交易日 / 总交易日

盈亏比 (Profit Factor)

\text{Profit Factor} = \frac{\text{总盈利金额}}{\text{总亏损金额(绝对值)}}

Profit Factor > 1 意味着策略是盈利的(赚的比亏的多)

一个重要的直觉:胜率和盈亏比是跷跷板关系。

策略类型 典型胜率 典型盈亏比
趋势跟随(如本 Demo 的 MA 策略) 30%-40%(多次小亏) > 2偶尔大赚
均值回归(高频做市) 60%-80%(频繁小赚) < 1.5(偶尔大亏)

8. 滚动前向验证与参数优化 (Walk-Forward Validation)

8.1 过拟合的危害

过拟合 (Overfitting) 是量化回测最大的陷阱:

在相同的历史数据上测试足够多的参数组合,总能找到一组让历史表现完美的参数。
但这些参数可能只是"碰巧"匹配了历史噪声,在未来数据上会失效。

例子:如果你测试了 1000 种均线参数组合,在同一段历史数据上,必然有几组看起来"完美",纯属随机巧合。这叫数据窥探偏差 (Data Snooping Bias)p-hacking

8.2 前向验证的正确做法

完整历史数据1500天
├─ 训练集 / In-Sample (80%1200天)
│    用于:网格搜索最优参数
│    报告:训练集夏普(仅供参考)
│
└─ 测试集 / Out-of-Sample (20%300天)
     用于:用找到的"最优参数"在这段数据上跑一遍
     报告:测试集夏普(这才是真正的绩效评估)
     原则:这 300 天的数据在优化参数时绝对不能碰过!

夏普衰减 (Sharpe Decay)

通常,训练集夏普 > 测试集夏普。这是正常的,问题在于衰减多少:

  • 训练集 2.0,测试集 1.5 → 轻微过拟合,可以接受
  • 训练集 3.0,测试集 -0.5 → 严重过拟合,该策略不可用
for sw in [5, 10, 15, 20, 30]:        # 短窗口候选值
    for lw in [30, 40, 50, 60, 80, 100]:  # 长窗口候选值
        if sw >= lw:
            continue                   # 逻辑约束:短窗口必须 < 长窗口
        # 在训练集上运行回测,记录夏普
        ...
# 选出夏普最高的参数组合
# 再在测试集上验证

更严格的验证方法(代码未实现,但应了解):

滚动前向验证 (Rolling Walk-Forward)

[Train1] → [Test1]
     [Train2] → [Test2]
          [Train3] → [Test3]
                 ...
汇报所有 Test 期的拼接结果

9. 收益率分布分析 (Return Distribution Analysis)

9.1 为什么要分析收益率分布?

夏普比率的计算假设收益率服从正态分布。如果分布严重偏离正态,夏普比率就不可靠。

9.2 偏度 (Skewness)

\text{Skewness} = \frac{\frac{1}{n}\sum(r_i - \bar{r})^3}{\sigma^3}
偏度值 含义
接近 0 左右对称(正态分布)
> 0 右偏(右尾长),有"意外大赚"的可能性
< 0 左偏(左尾长),有"意外大亏"的可能性 ⚠️

金融数据的现实:大多数股票收益率分布是负偏态(崩盘比暴涨更极端)。

9.3 峰度 (Kurtosis / Excess Kurtosis)

  • 正态分布的峰度 = 0代码中 pandas 计算的是超额峰度)
  • 金融收益率通常峰度 > 0厚尾Fat Tails

含义极端事件±3σ 以外)发生的频率比正态分布预测的更高。这意味着基于正态分布计算的风险模型(如 VaR会低估实际风险。

9.4 月度收益热力图 (Monthly Return Heatmap)

热力图直观展示每个月的盈亏情况:

  • 🟢 绿色 → 盈利月份
  • 🔴 红色 → 亏损月份
  • 颜色越深 → 盈亏越大

用途:快速识别策略的"季节性"——某些策略在特定月份/季节表现特别好或特别差,这可能是利用了真实的市场规律,也可能是偶然的样本特征。


10. 术语速查表

中文 English 简要说明
技术分析 Technical Analysis 用历史价格/成交量预测未来走势
趋势跟随 Trend Following 顺势而为,买入上涨资产
均值回归 Mean Reversion 逆势操作,买入超跌资产
技术指标 Technical Indicator 从原始数据提炼交易信号的数学公式
简单移动平均线 SMA (Simple Moving Average) n 日收盘价的算术平均
指数移动平均线 EMA (Exponential Moving Average) 近期数据权重更高的加权平均
相对强弱指数 RSI (Relative Strength Index) 衡量涨跌动能的 0-100 震荡指标
超买 Overbought RSI > 70涨势过强
超卖 Oversold RSI < 30跌势过强
指数平滑异同移动平均 MACD 两条 EMA 之差,衡量趋势动能
布林带 Bollinger Bands 均线 ± 2 倍标准差的价格通道
带宽 Band Width 上轨与下轨之差,衡量波动率
金叉 Golden Cross 短线上穿长线,看涨信号
死叉 Death Cross 短线下穿长线,看跌信号
信号生成 Signal Generation 根据指标产生买卖指令的逻辑
前视偏差 Lookahead Bias 回测中使用了"当时无法知道"的未来数据
向量化回测 Vectorized Backtest 用数组运算一次性计算所有时间步
净值曲线 Equity Curve 账户价值随时间变化的曲线
无风险利率 Risk-Free Rate 国债等无风险资产的收益率
夏普比率 Sharpe Ratio 每单位总风险对应的超额收益
索提诺比率 Sortino Ratio 每单位下行风险对应的超额收益
最大回撤 Maximum Drawdown (MDD) 净值从峰值跌落的最大幅度
卡玛比率 Calmar Ratio CAGR 除以最大回撤
年化复合增长率 CAGR 考虑复利后的年均增长率
胜率 Win Rate 盈利次数 / 总次数
盈亏比 Profit Factor 总盈利 / 总亏损
过拟合 Overfitting 策略过度适配历史数据,泛化能力差
数据窥探偏差 Data Snooping Bias 在同一数据上测试太多参数导致的虚假优化
训练集 In-Sample (IS) 用于优化参数的历史数据段
测试集 Out-of-Sample (OOS) 用于验证的未见过的历史数据段
滚动前向验证 Walk-Forward Validation 用过去优化参数,在紧接着的未来上验证
网格搜索 Grid Search 枚举所有参数组合的暴力搜索法
正态分布 Normal Distribution 均值两侧对称的钟形分布
厚尾 Fat Tails (Leptokurtosis) 极端事件频率高于正态分布预测
偏度 Skewness 分布的左右不对称程度
峰度 Kurtosis 分布尾部的厚度(与正态分布对比)
几何布朗运动 GBM (Geometric Brownian Motion) 描述股价随机游走的经典数学模型
年化波动率 Annualized Volatility 日波动率 × √252
滑点 Slippage 信号价格与实际成交价的差
佣金 Commission 券商收取的交易手续费

上一篇:数据管道
下一篇:事件驱动回测引擎