用 AKQuant 测试翻倍股(doubles)策略

来源: Trilium

─────────────────────────────────────── ● 请考虑如何使用akquant来测试我的http://192.168.123.104:3001/john/doubles.git ────────────────────────────────────────

🔐 Nous agent key refreshed after 401. Retrying request...

D:\hermes\mystocks\doubles\test_akquant.py 02m231s)

╭─ ⚕ Hermes ────────────────────────────────────────── 让我先回顾 doubles 项目的结构和策略逻辑: ╰───────────────────────────────────────── ┊ 🔍 preparing session_search… ┊ 🔍 recall "doubles 翻倍股 Ross Hook 策略" 0.7s ┊ 🔍 preparing session_search… ┊ 🔍 recall "doubled_stocks_ma_analysis MA斜率 五阶段" 0.1s ┊ ⚡ preparing mempalace_search…

╭─ ⚕ Hermes ────────────────────────────────────────── 基于我的记忆和文档内容,给你一个完整的方案:

用 AKQuant 测试翻倍股(doubles)策略

你的 doubles 项目核心逻辑

你的研究已经得出几条关键结论:

结论 说明
起涨信号:69% 翻倍股起涨于 MA20 < MA60(死叉) 短期均线下穿长期均线后,反而爆发反弹
MA60 全程正斜率 长期趋势向上是翻倍的前提条件
MA20 高斜率 +3.2%/天 见顶点信号,斜率过高则追高风险大
五阶段模型 起涨前→起涨日→起涨后10天→见顶前10天→见顶日

用 AKQuant 实现回测方案

AKQuant 可以直接把上述逻辑编码为策略,在一个 Framework 内完成 筛选 → 回测 → 绩效评估 的闭环。

第一步:准备数据

AKQuant 最适配的数据源是你的 ClickHouse day_kline 表(5207股、2024-01 至 2026-06)。用 python-clickhouse 或转换脚本把 ClickHouse 数据导出为 AKQuant 标准格式:

python import akshare as ak import pandas as pd from akquant import Strategy, run_backtest

方法1:直接通过 AKShare 取 A 股日线 df = ak.stock_zh_a_hist( symbol="000001.SH", # 或 "sz000001" period="daily", start_date="20240301", end_date="20260612", adjust="qfq", # 前复权 — 必须! )

df = df.rename(columns={ "日期": "date", "开盘": "open", "最高": "high", "最低": "low", "收盘": "close", "成交量": "volume", }) df['date'] = pd.to_datetime(df['date']) df['symbol'] = "000001"

第二步:编码双均线策略(你的核心逻辑)

python import numpy as np from akquant import Strategy, run_backtest

class RossHookStrategy(Strategy): """基于 MA20/MA60 死叉起涨信号的翻倍股筛选策略

   入场条件(基于你的271只翻倍股分析结论):
   - MA20 < MA60(死叉,69%翻倍股起涨于此)
   - MA60 斜率 > 0(长期趋势向上,翻倍前提)
   - MA20 斜率 < 0(短期回调正在加速)

   出场条件:
   - MA20 斜率 > +3.2%/天(见顶信号)
   - 固定止损(如 -8%)
   - 固定止盈(如 +100%)
   """

   def init(self, ma_short=20, ma_long=60,
                stop_loss=-0.08, take_profit=2.0,
                ma_slope_peak=0.032):
       self.ma_short = ma_short
       self.ma_long = ma_long
       self.stop_loss = stop_loss
       self.take_profit = take_profit
       self.ma_slope_peak = ma_slope_peak
       self.warmup_period = ma_long + 5  # 预热期保证均线可用

   def on_bar(self, bar):
       hist = self.get_history(count=self.ma_long + 5, field="close")
       if len(hist) < self.ma_long + 2:
           return

       closes = hist
       ma20_now = closes[-20:].mean() if len(closes) >= 20 else None
       ma60_now = closes[-60:].mean() if len(closes) >= 60 else None
       ma20_prev = closes[-21:-1].mean() if len(closes) >= 21 else None
       ma60_prev = closes[-61:-1].mean() if len(closes) >= 61 else None

       if not all([ma20_now, ma60_now, ma20_prev, ma60_prev]):
           return

       position = self.get_position(bar.symbol)

       # 计算 MA20 斜率(最近5根K线变化率)
       ma20_old = closes[-25:-21].mean() if len(closes) >= 25 else ma20_prev
       ma20_slope = (ma20_now - ma20_old) / ma20_old

       # === 入场逻辑:死叉 + MA60上行 + MA20下行 ===
       if position == 0:
           if ma20_now < ma60_now and ma60_prev <= ma60_now and ma20_slope < 0:
               self.buy(bar.symbol, 100)
               self.log(f"[{bar.timestamp_iso}] 死叉买入 {bar.symbol} @ {bar.close:.2f} "
                        f"MA20={ma20_now:.2f} MA60={ma60_now:.2f} slope={ma20_slope*100:.3f}%")

       # === 出场逻辑:MA20斜率过高 / 止损 / 止盈 ===
       elif position > 0:
           entry_price = self.get_instrument_field(bar.symbol, "entry_price") or bar.close / (1 + self.stop_loss)
           pnl_pct = (bar.close - entry_price) / entry_price

           # 斜率过高 = 见顶信号
           if ma20_slope > self.ma_slope_peak:
               self.sell(bar.symbol, 100)
               self.log(f"[{bar.timestamp_iso}] 斜率见顶卖出 {bar.symbol} @ {bar.close:.2f} "
                        f"slope={ma20_slope100:.3f}% pnl={pnl_pct100:.1f}%")
           # 止损
           elif pnl_pct < self.stop_loss:
               self.sell(bar.symbol, 100)
               self.log(f"[{bar.timestamp_iso}] 止损卖出 {bar.symbol} @ {bar.close:.2f} "
                        f"pnl={pnl_pct*100:.1f}%")
           # 止盈(翻倍股)
           elif pnl_pct >= self.take_profit:
               self.sell(bar.symbol, 100)
               self.log(f"[{bar.timestamp_iso}] 止盈卖出 {bar.symbol} @ {bar.close:.2f} "
                        f"pnl={pnl_pct*100:.1f}%")

第三步:批量回测271只翻倍股

python 用你的 CSV 获取271只翻倍股代码 import pandas as pd candidates = pd.read_csv("D:/hermes/mystocks/doubles/doubled_stocks_list.csv", dtype=str)['代码'].tolist()

results = [] for code in candidates: try: # 整 AKShare 取该股前复权日线(2024-01 往后足够覆盖回测期) df = ak.stock_zh_a_hist( symbol=code, period="daily", start_date="20240101", end_date="20260612", adjust="qfq", ) if len(df) < 120: # 少于120天的跳过 continue df = df.rename(columns={"日期":"date","开盘":"open","最高":"high", "最低":"low","收盘":"close","成交量":"volume"}) df['date'] = pd.to_datetime(df['date']) df['symbol'] = code

       result = run_backtest(
           strategy=RossHookStrategy,
           data=df,
           symbols=code,
           initial_cash=100000,
           commission_rate=0.0003,
           slippage=0.001,
           show_progress=False,
       )
       m = result.metrics_df
       results.append({
           "code": code,
           "total_return": m.loc["total_return_pct", "Value"],
           "max_drawdown": m.loc["max_drawdown_pct", "Value"],
           "sharpe": m.loc["sharpe_ratio", "Value"],
           "trades": m.loc["trade_count", "Value"],
       })
   except Exception as e:
       print(f"  {code} 失败: {e}")

report = pd.DataFrame(results).sort_values("total_return", ascending=False) print(report) report.to_csv("D:/hermes/mystocks/doubles/akquant_backtest_results.csv", index=False)

第四步:参数扫描(优化入场/出场阈值)

python from akquant import run_grid_search

param_grid = { "ma_short": [10, 20], "ma_long": [40, 60], "stop_loss": [-0.05, -0.08, -0.10], "take_profit": [1.5, 2.0, 3.0], "ma_slope_peak": [0.02, 0.032, 0.05], }

results_grid = run_grid_search( strategy=RossHookStrategy, param_grid=param_grid, data=ddaily_df, # 可以先用一只股做快速参数扫描 initial_cash=100000, sort_by="sharpe_ratio", max_workers=4, )

print(results_grid.head(10))

对比你的原始数据 vs AKQuant 回测结果

你的原始数据库: - 271 只确认翻倍股(ClickHouse 筛选结果) - 265 只 MA 斜率分析(ELTDX K线 + 五阶段计算)

AKQuant 回测的价值在于: 1. 验证你的策略逻辑的真实收益 — 不用事后确认翻倍,看信号发出时是否真的买到了 2. 评估入场/出场规则的改进空间 — 正斜率、平斜率、反斜率各方案对收益的影响 3. 参数敏感性分析 — 20天 vs 30天均线?斜率阈值 3.2% 是否最优? 4. 对比不同止损/止盈策略

实际执行路径

我建议分三步:

  1. 先跑一只股的快速验证:用 000001(平安银行)或 605319(无锡振华,你的A级候选),验证 RossHookStrategy 能正常产生信号和结果
  2. 批量回测265只:用你有 K 线数据的 265 只股跑策略,对比原始翻倍股的实际表现
  3. 参数优化:在最有潜力的子集上跑 grid_search

需要我现在帮你开始第1步——在 000001 上跑一次 RossHookStrategy 快速验证吗? ────────────────────────────────────────── ● 同意