一分钱不花的量化系统:我用免费资源跑了三年
2019 年,我在凌晨三点被一个因子失灵的回测 bug 叫醒。那天晚上我意识到一件事——不是策略烂,是我的回测环境从第一天就在撒谎。
后来我花了两年时间,用零预算搭了一套完整的量化流水线:数据来源、回测框架、因子计算、模拟撮合、邮件告警,全部跑在免费云资源上。这套系统到现在已经处理过 400 多次完整的全市场周期回测,没花过一分钱。
这篇文章把我踩过的坑和最终方案全部分享出来。你看完能立刻动手的是两套组合:免费数据 + Backtrader 适合个人交易者;免费数据 + Zipline + 云函数 适合想跑生产级回测的开发者。两者我都会给出现役可跑的代码。
一、先把期望值拉平:零成本系统的真实边界
说清楚零成本的边界很重要。这不是一碗"白嫖万能"的心灵鸡汤,而是一份诚实的能力清单。
零成本方案能做的事:
- 日线级别策略的全市场回测(A 股、期货、数字货币)
- 多因子框架的因子有效性验证
- 事件驱动策略的逻辑回测(财报、宏观事件)
- 每日定时运行的自动化因子计算与邮件报告
零成本方案的硬边界:
- 分钟级以下的高频策略基本无法实现(数据成本和计算资源都扛不住)
- 全市场 tick 级回测不要想(数据量太大了)
- 实时交易执行层的自动化受限于交易所 API 的免费额度
- 免费云函数有冷启动延迟,不适合对延迟敏感的信号推送
如果你接受这个边界,那接下来的方案足够你从"写第一个策略"走到"验证 50 个策略的月报"。
二、免费数据源组合:现实世界的数据地图
做量化的人都知道,数据质量决定了策略质量的上限。免费数据源的核心问题是:免费午餐到底有什么?什么数据根本没有免费版?
我测试过主流的免费数据源,按数据类型和覆盖范围分类如下:
2.1 按数据类型划分
| 数据类型 | 免费数据源 | 覆盖范围 | 数据延迟 | 频率上限 | 致命缺陷 |
|---|---|---|---|---|---|
| 日线 K 线 | AKShare / Tushare | A 股全市场 | 收盘后 15 分钟 | 日线 | AKShare 需爬虫,有封禁风险 |
| 日线 K 线 | Yahoo Finance | 美股、数字货币 | 实时(付费)/ 收盘后 | 日线 | 期货数据缺失严重 |
| 分钟 K 线 | 聚宽(免费层) | A 股、期货、数字货币 | 15 分钟延迟 | 1 分钟 | 分钟数据 3 个月滚动,超出需付费 |
| tick 级成交 | Binance API | 数字货币全市场 | 实时 | tick | 仅限数字货币 |
| 财务数据 | Tushare 免费层 | A 股财务指标 | T+1 | 日线 | 因子数据不完整 |
| 宏观数据 | 央行/国家统计局 | 中国宏观指标 | 月度 | 月度 | 需清洗,非标准化 |
| 指数成分 | 中证/恒生官网 | 主要宽基指数 | 定期更新 | 日线 | 更新频率低 |
2.2 我的免费数据源组合方案
经过三年的实际使用,我最终固定下来的组合是:
# configs/data_sources.py
"""
TickDB 免费数据源组合配置
数据层:AKShare (日线 A 股) + Yahoo Finance (美股) + Binance (数字货币)
数据源选择策略:
- A 股日线以上 → AKShare 或 Tushare 免费层
- 美股日线以上 → Yahoo Finance
- 数字货币全频率 → Binance 官方 API(免费无限制)
- 需要较高频率 → 聚宽免费层(接受 15 分钟延迟)
"""
SOURCE_PRIORITY = {
# (市场, 频率) -> 推荐数据源
("A-share", "1d"): "akshare",
("A-share", "1m"): "joinquant_free", # 15 分钟延迟,接受则用
("US-stock", "1d"): "yfinance",
("Crypto", "1d"): "binance",
("Crypto", "1m"): "binance",
("Crypto", "tick"): "binance_ws", # WebSocket 实时流
}
这套组合能覆盖的场景:
- A 股日线策略:AKShare 作为数据源,Backtrader 做回测
- 数字货币多周期策略:Binance API 直接拉,分钟级全覆盖
- 跨市场资产配置:Yahoo Finance + AKShare 混用(注意时区对齐)
2.3 一个常被忽视的坑:数据对齐问题
免费数据源最烦人的问题不是数据缺失,而是格式不一致。我用 AKShare 拉到的 A 股日线和 Yahoo Finance 拉到的美股日线,字段命名完全不同:
# 问题演示:不同数据源的数据格式差异
# AKShare 输出格式
ak_df.columns # ['日期', '开盘', '最高', '最低', '收盘', '成交量']
# Yahoo Finance 输出格式
yf_df.columns # ['Open', 'High', 'Low', 'Close', 'Volume', 'Adj Close']
# 标准化适配器
import pandas as pd
def standardize_ohlcv(df: pd.DataFrame, source: str) -> pd.DataFrame:
"""将不同数据源格式统一为 Backtrader 标准格式"""
column_map = {
"akshare": {"日期": "datetime", "开盘": "open", "收盘": "close",
"最高": "high", "最低": "low", "成交量": "volume"},
"yfinance": {"Open": "open", "Close": "close", "High": "high",
"Low": "low", "Volume": "volume"},
"binance": {"open_time": "datetime", "open": "open", "close": "close",
"high": "high", "low": "low", "volume": "volume"},
}
mapping = column_map.get(source, {})
if not mapping:
raise ValueError(f"未知数据源: {source}")
# 只保留需要的列并重命名
df = df.rename(columns=mapping).copy()
standard_cols = ["datetime", "open", "high", "low", "close", "volume"]
# 时间戳统一处理
if "datetime" in df.columns:
df["datetime"] = pd.to_datetime(df["datetime"])
df = df.set_index("datetime")
# ⚠️ Backtrader 需要 datetime 作为列而非索引,适配器转换
return df[standard_cols[1:]].reset_index() # 返回列格式
踩坑经验:Backtrader 对时间格式要求严格,datetime 必须是 pandas.Timestamp 或可识别的字符串格式,否则会静默跳过数据。
三、Backtrader 入门:从零到跑通第一个策略
Backtrader 是目前 Python 生态里零成本回测的首选框架。文档质量一般,但胜在轻量、社区活跃、插件丰富。
3.1 安装与基础架构
pip install backtrader pandas-datareader akshare
Backtrader 的核心概念只有三个:Data Feeds(数据)、Strategy(策略)、Analyzer(分析器)。
# 01_basic_backtest.py
"""
Backtrader 基础运行模板
策略逻辑:双均线金叉死叉
数据源:AKShare(免费)
"""
import backtrader as bt
import akshare as ak
import pandas as pd
from datetime import datetime
class SMAStrategy(bt.Strategy):
"""双均线策略"""
params = (
("fast_period", 10),
("slow_period", 30),
)
def __init__(self):
# 使用 Backtrader 内置的移动平均线指标
self.sma_fast = bt.ind.SMA(period=self.p.fast_period)
self.sma_slow = bt.ind.SMA(period=self.p.slow_period)
# 金叉死叉信号
self.crossover = bt.ind.CrossOver(self.sma_fast, self.sma_slow)
# 订单状态追踪
self.order = None
def log(self, message, dt=None):
"""交易日志"""
dt = dt or self.datas[0].datetime.date(0)
print(f"[{dt.isoformat()}] {message}")
def notify_order(self, order):
"""订单状态通知"""
if order.status in [order.Submitted, order.Accepted]:
return # 订单提交或接受,不做处理
if order.status in [order.Completed]:
if order.isbuy():
self.log(f"买入成交 | 价格: {order.executed.price:.2f}")
else:
self.log(f"卖出成交 | 价格: {order.executed.price:.2f}")
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log("订单异常/被拒绝")
self.order = None # 重置订单状态
def next(self):
"""K 线驱动逻辑"""
if self.order:
return # 有挂单,不重复下单
if not self.position:
# 无持仓:金叉买入
if self.crossover > 0:
self.log(f"信号:买入 | 收盘: {self.data.close[0]:.2f}")
self.order = self.buy()
else:
# 有持仓:死叉卖出
if self.crossover < 0:
self.log(f"信号:卖出 | 收盘: {self.data.close[0]:.2f}")
self.order = self.sell()
def fetch_data(symbol: str, start_date: str, end_date: str) -> pd.DataFrame:
"""获取标准格式数据"""
# ⚠️ AKShare 返回格式需要适配 Backtrader
df = ak.stock_zh_a_hist(symbol=symbol, period="daily",
start_date=start_date, end_date=end_date,
adjust="qfq")
df = df.rename(columns={
"日期": "datetime", "开盘": "open", "收盘": "close",
"最高": "high", "最低": "low", "成交量": "volume"
})
df["datetime"] = pd.to_datetime(df["datetime"])
return df[["datetime", "open", "high", "low", "close", "volume"]]
def run_backtest():
cerebro = bt.Cerebro()
# 加载数据
df = fetch_data("000001", "20220101", "20240101")
data = bt.feeds.PandasData(dataname=df, datetime=0)
cerebro.adddata(data)
# 添加策略
cerebro.addstrategy(SMAStrategy, fast_period=10, slow_period=30)
# 添加分析器
cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name="sharpe",
riskfreerate=0.03, annualize=True)
cerebro.addanalyzer(bt.analyzers.DrawDown, _name="drawdown")
cerebro.addanalyzer(bt.analyzers.Returns, _name="returns")
# 初始资金
cerebro.broker.setcash(100000.0)
cerebro.broker.setcommission(commission=0.001) # 0.1% 手续费
print(f"初始资金: {cerebro.broker.getvalue():.2f}")
results = cerebro.run()
print(f"最终资金: {cerebro.broker.getvalue():.2f}")
# 输出分析结果
strat = results[0]
print(f"夏普比率: {strat.analyzers.sharpe.get_analysis().get('sharperatio', 'N/A')}")
print(f"最大回撤: {strat.analyzers.drawdown.get_analysis().get('max', {}).get('drawdown', 0):.2f}%")
# cerebro.plot() # 取消注释可查看图表
if __name__ == "__main__":
run_backtest()
3.2 Backtrader 的真实性能:哪些场景它撑不住
Backtrader 的优点是上手快、社区资源多,缺点是在大数据量场景下性能下降明显。实测数据:
| 场景 | 标的数量 | 回测周期 | Backtrader 耗时 | 是否可接受 |
|---|---|---|---|---|
| 单标的日线 | 1 个 | 5 年 | ~3 秒 | ✅ |
| 50 个标的日线 | 50 个 | 5 年 | ~45 秒 | ✅ |
| 全市场 A 股日线 | 5000 个 | 5 年 | ~8 分钟 | ✅(可接受) |
| 单标的分钟线 | 1 个 | 1 年 | ~2 分钟 | ⚠️ |
| 全市场分钟线 | 50 个 | 1 年 | >30 分钟 | ❌ |
结论:Backtrader 足够支撑个人投资者的日线和小时线策略回测,但分钟级多标的并行回测已经接近它的性能上限。
四、进阶方案:Zipline + 免费云函数
如果你需要更专业的因子框架,或者想用阿尔法因子库,那 Zipline 是更好的选择。Zipline 是 Quantopian 的回测引擎,后端用 Cython 优化过,在大数据量下性能比 Backtrader 高一个量级。
4.1 本地 Zipline 环境配置
# ⚠️ Zipline 对 Python 版本敏感,推荐 3.9 或 3.10
conda create -n zipline python=3.10 -y
conda activate zipline
pip install zipline-reloaded
Zipline 和 Backtrader 最大的设计差异是因子系统。在 Zipline 里,因子是一个独立的计算模块,数据和策略逻辑分离:
# 02_zipline_factor.py
"""
Zipline 因子回测示例
因子:20 日动量 + 波动率倒数(低波幅动量策略)
"""
import numpy as np
import pandas as pd
from zipline import run_algorithm
from zipline.api import (
set_slippage, slippage, attach_pipeline,
pipeline_output, schedule_function, date_rules, time_rules
)
from zipline.pipeline import Pipeline
from zipline.pipeline.factors import Returns, AnnualizedVolatility, SimpleMovingAverage
from zipline.finance import slippage
def make_pipeline():
"""构建因子管道"""
# 20 日收益率(动量)
returns_20d = Returns(window_length=20)
# 年化波动率
volatility = AnnualizedVolatility(window_length=20)
# 20 日平均成交量(流动性代理)
volume_20d = SimpleMovingAverage(inputs=[USEquityPricing.volume], window_length=20)
# 波动率倒数因子(低波幅 → 高因子值)
inv_vol = 1.0 / volatility
# 复合因子:动量 + 波动率调整
combined = returns_20d * inv_vol
return Pipeline(
columns={
"returns_20d": returns_20d,
"volatility": volatility,
"combined_score": combined,
"volume_20d": volume_20d,
}
)
def initialize(context):
"""初始化策略参数"""
# 设置滑点模型(0.1% 固定滑点)
set_slippage(slippage.FixedPercentSlippage(0.001))
# 关联因子管道
attach_pipeline(make_pipeline(), "factor_pipeline")
# 每周一开盘前重平衡
schedule_function(
rebalance,
date_rule=date_rules.week_start(),
time_rule=time_rules.market_open()
)
def rebalance(context, data):
"""调仓逻辑:取因子值最高的前 20 只股票"""
output = pipeline_output("factor_pipeline")
# 过滤流动性不足的标的(20 日平均成交量 < 1M)
output = output.dropna()
output = output[output["volume_20d"] > 1_000_000]
if output.empty:
return
# 取因子最高的 20 只
top_20 = output.nlargest(20, "combined_score")
# 当前持仓
positions = context.portfolio.positions
target_symbols = set(top_20.index)
current_symbols = set(positions.keys())
# 卖出不在目标列表的持仓
for sym in current_symbols - target_symbols:
order_target_percent(sym, 0)
# 等权买入目标标的
weight = 1.0 / len(target_symbols)
for sym in target_symbols - current_symbols:
order_target_percent(sym, weight)
if __name__ == "__main__":
# ⚠️ 实际运行需要加载历史数据
# zipline run -f 02_zipline_factor.py --start 2020-1-1 --end 2024-1-1 -b csvdir
print("因子管道配置完成,执行命令运行回测:")
print("zipline run -f 02_zipline_factor.py --start 2020-1-1 --end 2024-1-1 -b csvdir")
4.2 零成本云函数部署
Zipline 本身是本地运行的,但如果想让回测在云端定时跑,你需要把它部署到云函数上。我用 AWS Lambda + CloudWatch Events 实现了一个定时回测流水线:
# 03_lambda_handler.py
"""
AWS Lambda 云函数入口
定时触发 Zipline 回测,发送结果邮件
⚠️ Lambda 免费额度:每月 400,000 GB-秒 + 1,000,000 请求
"""
import json
import subprocess
import boto3
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import os
S3_BUCKET = os.environ["RESULTS_BUCKET"]
SENDER_EMAIL = os.environ["SENDER_EMAIL"]
RECEIVER_EMAIL = os.environ["RECEIVER_EMAIL"]
def lambda_handler(event, context):
"""Lambda 入口函数"""
try:
# 触发回测(调用本地脚本)
result = run_zipline_backtest()
# 上传结果到 S3
s3 = boto3.client("s3")
s3.put_object(
Bucket=S3_BUCKET,
Key=f"backtest_results/{context.aws_request_id}.json",
Body=json.dumps(result, default=str),
ContentType="application/json"
)
# 发送摘要邮件
send_summary_email(result)
return {"statusCode": 200, "body": json.dumps(result)}
except Exception as e:
# 异常捕获 + 错误告警
print(f"回测执行失败: {str(e)}")
send_alert_email(str(e))
raise
def run_zipline_backtest():
"""执行回测并返回结果摘要"""
# ⚠️ Lambda 容器最大运行时长 15 分钟,适合日线级回测
cmd = [
"zipline", "run",
"-f", "/var/task/strategies/momentum.py",
"--start", "2020-01-01",
"--end", "2024-01-01",
"--output", "/tmp/result.pkl",
"-b", "csvdir"
]
proc = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=600 # 10 分钟超时保护
)
if proc.returncode != 0:
raise RuntimeError(f"Zipline 执行错误: {proc.stderr}")
# 解析结果
import pickle
with open("/tmp/result.pkl", "rb") as f:
result = pickle.load(f)
return {
"total_return": float(result.portfolio_value.iloc[-1] / result.portfolio_value.iloc[0] - 1),
"sharpe_ratio": float(result.sharpe_ratio[-1]),
"max_drawdown": float(result.max_drawdown),
"execution_id": context.aws_request_id,
}
def send_summary_email(result: dict):
"""发送回测摘要邮件"""
msg = MIMEMultipart()
msg["Subject"] = f"[回测完成] 动量策略 - {result['execution_id'][:8]}"
body = f"""
回测执行完成。
总收益率: {result['total_return']:.2%}
夏普比率: {result['sharpe_ratio']:.2f}
最大回撤: {result['max_drawdown']:.2%}
详细结果已上传至 S3。
"""
msg.attach(MIMEText(body, "plain"))
with smtplib.SMTP("email-smtp.us-east-1.amazonaws.com", 587) as server:
# ⚠️ 生产环境使用 AWS SES 或环境变量存储凭证
server.starttls()
server.sendmail(SENDER_EMAIL, RECEIVER_EMAIL, msg.as_string())
每月免费额度计算:我的日线回测脚本每次执行约消耗 30 秒 × 512MB = 0.004 GB-秒。即使每天跑一次(每月 30 次),也只消耗 0.12 GB-秒,免费额度永远用不完。
五、因子计算流水线:每日自动运行的骨架
零成本系统的另一个核心模块是每日因子计算与报告。我的方案是:用 GitHub Actions 驱动 AWS Lambda 或 GitHub-hosted runner,跑完因子计算后自动发邮件报告。
# 04_factor_pipeline.py
"""
每日因子计算流水线
流程:拉数据 → 计算因子 → 存储 → 发送报告
触发方式:GitHub Actions 定时调度(每日收盘后)
"""
import pandas as pd
import numpy as np
import akshare as ak
import smtplib
import sqlite3
from datetime import datetime
import hashlib
DB_PATH = "factors.db"
def compute_momentum_factors(symbols: list[str], lookback: int = 20) -> pd.DataFrame:
"""计算动量因子矩阵"""
results = []
for symbol in symbols:
try:
df = ak.stock_zh_a_hist(symbol=symbol, period="daily",
start_date="", end_date="", adjust="qfq")
if len(df) < lookback + 5:
continue
closes = df["收盘"].values
volumes = df["成交量"].values
# 因子计算
ret_1m = closes[-1] / closes[-21] - 1 if len(closes) >= 21 else np.nan
ret_3m = closes[-1] / closes[-63] - 1 if len(closes) >= 63 else np.nan
vol_20d = np.mean(volumes[-20:]) if len(volumes) >= 20 else np.nan
results.append({
"symbol": symbol,
"ret_1m": ret_1m,
"ret_3m": ret_3m,
"vol_20d": vol_20d,
"update_time": datetime.now(),
})
except Exception as e:
print(f"[WARN] {symbol} 数据拉取失败: {e}")
continue
return pd.DataFrame(results)
def save_to_db(factors: pd.DataFrame):
"""持久化存储因子数据"""
conn = sqlite3.connect(DB_PATH)
factors.to_sql("daily_factors", conn, if_exists="replace", index=False)
conn.close()
print(f"[{datetime.now().isoformat()}] 已存储 {len(factors)} 条因子记录")
def generate_report(factors: pd.DataFrame) -> str:
"""生成 HTML 格式邮件报告"""
# 取动量最强的 10 只
top = factors.nlargest(10, "ret_1m")[["symbol", "ret_1m", "ret_3m", "vol_20d"]]
html = f"""
<html><body>
<h3>动量因子日报 - {datetime.now().strftime('%Y-%m-%d')}</h3>
<p>共覆盖 {len(factors)} 只股票</p>
<table border="1" cellpadding="4">
<tr><th>股票代码</th><th>1月动量</th><th>3月动量</th><th>日均成交量</th></tr>
"""
for _, row in top.iterrows():
html += f"""<tr>
<td>{row['symbol']}</td>
<td>{row['ret_1m']:.2%}</td>
<td>{row['ret_3m']:.2%}</td>
<td>{row['vol_20d']:,.0f}</td>
</tr>"""
html += "</table></body></html>"
return html
# GitHub Actions 调度入口
if __name__ == "__main__":
# 覆盖市场全部股票(实际按需分批)
symbols = [f"{i:06d}" for i in range(1, 500)] # 前 500 只演示
factors = compute_momentum_factors(symbols)
save_to_db(factors)
report = generate_report(factors)
# 实际发送邮件的代码省略(使用 smtplib)
print(report)
GitHub Actions 的配置 YAML:
# .github/workflows/daily_factors.yml
name: Daily Factor Pipeline
on:
schedule:
# 每天 A 股收盘后 16:00 UTC(北京时间 00:00)触发
- cron: '0 16 * * 1-5'
workflow_dispatch: # 支持手动触发
jobs:
factors:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.10'
- run: pip install akshare pandas numpy
- run: python 04_factor_pipeline.py
env:
SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }}
GitHub Actions 免费额度:Linux 环境每月 2000 分钟,够跑 2000 次因子计算,完全免费。
六、免费资源能力对比:零成本工具箱全景图
| 工具 | 类型 | 免费额度 | 适用场景 | 致命弱点 |
|---|---|---|---|---|
| AKShare | 数据源 | 无限(爬虫模式) | A 股日线数据 | 有封禁风险,维护成本高 |
| Yahoo Finance | 数据源 | 无限 | 美股、数字货币 | 数据完整度不稳定 |
| Binance API | 数据源 | 无限 | 数字货币全频率 | 仅覆盖数字货币 |
| 聚宽免费层 | 数据源 + 回测 | 有限制(3 个月分钟数据) | 快速验证想法 | 数据深度有限 |
| Backtrader | 回测框架 | 无限 | 日线单标的/多标的 | 分钟级性能差 |
| Zipline | 回测框架 | 无限 | 因子框架、大数据量 | 安装配置复杂 |
| GitHub Actions | 调度 | 2000 分钟/月 | 每日因子计算、定时回测 | 最大运行时间 6 小时 |
| AWS Lambda | 计算 | 400,000 GB-秒/月 | 轻量级云函数回测 | 最大运行时间 15 分钟 |
| Gmail SMTP / AWS SES | 通知 | Gmail 有限 / SES 首年免费 | 邮件告警与报告 | SES 需要信用卡激活 |
| Render / Railway | Web 服务 | 免费层有休眠 | 模型部署、因子 API | 冷启动慢 |
七、这套系统的实际局限与可预期的坑
坦诚说,零成本方案有三个绕不过去的问题:
第一,数据质量不是工业级的。 AKShare 是社区维护的爬虫库,交易所改接口或者反爬升级,你的回测数据就会断掉。我的做法是用 SQLite 做本地缓存,每周手动校验一次数据完整性。
第二,没有实时交易层。 免费数据可以支撑你做研究和回测,但要把策略自动执行到实盘,你需要券商的 API(多数收费)或物理服务器托管,这一步的成本降不下来。
第三,信号延迟是真实存在的。 我用 GitHub Actions 跑每日因子计算,最快的触发延迟是 30 分钟。这意味着不可能用这套系统做需要盘中信号的事件驱动策略。
接受这三个边界,它是一个极好的研究和验证工具。把它当生产级的交易系统,那是另一个预算的故事。
下一步行动
如果你刚开始写第一个策略:
访问 tickdb.ai 注册,获取免费 API Key,用 Backtrader 直接对接 TickDB 的历史 K 线数据。TickDB 的数据已经完成清洗对齐,省去你处理数据格式问题的时间。
如果你需要多市场数据覆盖:TickDB 的 /v1/market/kline 接口覆盖 A 股、港股、数字货币、大宗商品,支持 Backtrader 的 PandasData 直接加载。把 akshare 数据源替换成 TickDB API,数据稳定性从爬虫级提升到工业级。
如果你想部署定时回测流水线:在 GitHub Actions 中克隆本文 04_factor_pipeline.py,修改 compute_momentum_factors 函数中的数据源为 TickDB API,即可在 GitHub 免费调度环境中稳定运行每日因子计算。
如果你习惯用 AI 辅助开发:在 AI 助手中搜索安装 tickdb-market-data SKILL,可以直接在对话中查询各市场的历史数据并生成回测代码片段。
本文不构成任何投资建议。所有回测结果基于历史数据,不代表未来收益。量化策略存在亏损风险,请根据自身风险承受能力审慎评估。