quantitative-trading 插件 backtesting-frameworks Skill 实战:构建抗偏差的量化回测系统
【免费下载链接】agentsMulti-harness agentic plugin marketplace for Claude Code, Codex, Cursor, OpenCode, GitHub Copilot, and Google Antigravity项目地址: https://gitcode.com/GitHub_Trending/agents24/agents
导读
本文讲解 GitHub 推荐项目精选 agents24 仓库中quantitative-trading插件内backtesting-frameworksAgent Skill 的核心内容:如何构建稳健、可上生产环境的量化交易策略回测系统。你将掌握回测五大偏差(前视偏差、幸存者偏差、过拟合、选择偏差、交易成本偏差)的识别与规避方法,理解 Train/Validation/Test 三段式回测结构与 Walk-Forward 前向分析,并通过事件驱动回测器、向量化回测器、Walk-Forward 优化器与 Monte Carlo 分析四个可运行 Python 模式,搭建一套能输出可靠策略绩效估计的回测基础设施。
该 Skill 在本仓库中定位为quantitative-trading插件的领域技能之一,与 quant-analyst.md(策略开发与回测)及 risk-metrics-calculation(VaR/CVaR/回撤指标)技能互补:本技能解决"如何正确地回测",风险指标技能解决"如何度量回测结果的风险"。其核心定义见 SKILL.md,全部可运行代码模式集中在 references/details.md。
一、何时使用该 Skill
SKILL.md 的 frontmatter 声明了明确的激活条件(description 中的 "Use when" 语义):
- 开发交易策略回测(Developing trading strategy backtests)
- 构建回测基础设施(Building backtesting infrastructure)
- 验证策略绩效(Validating strategy performance)
- 规避常见回测偏差(Avoiding common backtesting biases)
- 实现 Walk-Forward 前向分析(Implementing walk-forward analysis)
- 比较候选策略(Comparing strategy alternatives)
这与 quant-analyst.md 中 "Robust backtesting with transaction costs and slippage"(含交易成本与滑点的稳健回测)、"Out-of-sample testing to avoid overfitting"(样本外测试避免过拟合)两条方法论一一对应,Skill 是 Agent 在执行回测任务时加载的领域知识包。
二、核心概念:五大回测偏差
SKILL.md 用一张表系统归纳了回测中最常见的五种偏差及其缓解手段,这是本技能的理论基石:
| Bias 偏差 | Description 描述 | Mitigation 缓解手段 |
|---|---|---|
| Look-ahead 前视偏差 | 使用了未来信息 | Point-in-time data 时点数据 |
| Survivorship 幸存者偏差 | 只在幸存标的上测试 | Use delisted securities 纳入退市标的 |
| Overfitting 过拟合 | 对历史曲线拟合过头 | Out-of-sample testing 样本外测试 |
| Selection 选择偏差 | 挑选有利策略(cherry-picking) | Pre-registration 预先注册 |
| Transaction 交易成本偏差 | 忽略交易成本 | Realistic cost models 真实成本模型 |
关键解读:
- 前视偏差是最隐蔽也最常见的错误,典型来源包括:用当日收盘价计算信号却在当日开盘成交、信号未做 shift(1) 延迟一 bar、财务数据使用事后重述版本。在 details.md 的向量化回测器中,专门用一行
signals = signal_func(prices).shift(1).fillna(0)并通过注释 "shifted to avoid look-ahead" 明确示范了规避手段。 - 幸存者偏差要求数据集中包含已退市标的,否则回测收益会被系统性高估。
- 选择偏差要求策略在数据探索之前预先注册,避免"先看数据、再编故事"。
- 交易成本偏差要求回测引擎内置佣金与滑点模型,这正是 quant-analyst.md 强调的 "Include realistic assumptions about market microstructure"(包含现实的市场微观结构假设)。
三、正确的回测结构:Training / Validation / Test 三段式
SKILL.md 给出了标准的回测数据划分结构:
Historical Data │ ▼ ┌─────────────────────────────────────────┐ │ Training Set │ │ (Strategy Development & Optimization) │ └─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ Validation Set │ │ (Parameter Selection, No Peeking) │ └─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ Test Set │ │ (Final Performance Evaluation) │ └─────────────────────────────────────────┘三段职责边界:
- Training Set(训练集):只用于策略开发与参数优化。所有基于历史数据的"学习"都必须发生在这里。
- Validation Set(验证集):用于参数选择与模型调优,严禁偷看(No Peeking)。如果验证集被反复用于调参,它会退化为训练集的一部分,最终 Test Set 也会被污染。
- Test Set(测试集):仅在最终阶段运行一次,用于最终绩效评估。SKILL.md 的 Don'ts 明确警告"Don't optimize on full history - Reserve test set"(不要在全部历史上优化,必须保留测试集)。
四、Walk-Forward Analysis 前向分析
仅做一次 train/test 划分不足以评估策略的时间稳定性,SKILL.md 推荐使用滚动窗口的前向分析:
Window 1: [Train──────][Test] Window 2: [Train──────][Test] Window 3: [Train──────][Test] Window 4: [Train──────][Test] ─────▶ Time核心思想:在每个窗口内,仅在 Train 段上优化参数,再把最优参数应用到紧随其后的 Test 段评估,然后窗口向前滚动重复。这与"在全部历史上优化一次"截然不同,能暴露参数随市场状态漂移导致的过拟合问题。SKILL.md 的 Do's 中 "Use walk-forward - Not just train/test" 正是指这一点。
五、实现模式一:事件驱动回测器(Event-Driven Backtester)
details.md 的 Pattern 1 提供了完整的、可直接运行的事件驱动回测器,适合策略依赖成交回报回调、订单类型多样(市价/限价/止损)的场景。其核心抽象如下:
5.1 订单与成交的数据结构
from abc import ABC, abstractmethod from dataclasses import dataclass, field from datetime import datetime from decimal import Decimal from enum import Enum from typing import Dict, List, Optional import pandas as pd import numpy as np class OrderSide(Enum): BUY = "buy" SELL = "sell" class OrderType(Enum): MARKET = "market" LIMIT = "limit" STOP = "stop" @dataclass class Order: symbol: str side: OrderSide quantity: Decimal order_type: OrderType limit_price: Optional[Decimal] = None stop_price: Optional[Decimal] = None timestamp: Optional[datetime] = None @dataclass class Fill: order: Order fill_price: Decimal fill_quantity: Decimal commission: Decimal slippage: Decimal timestamp: datetime实现细节值得注意:
- 全部金额字段使用
decimal.Decimal而非 float,避免资金计算中的浮点误差累积——这正是生产级回测与快速原型的关键差异之一。 Order将交易方向(side)、订单类型(order_type)、限价/止损价分离建模,为后续接入限价单与止损单的撮合逻辑预留了扩展点。
5.2 持仓与组合账本
@dataclass class Position: symbol: str quantity: Decimal = Decimal("0") avg_cost: Decimal = Decimal("0") realized_pnl: Decimal = Decimal("0") def update(self, fill: Fill) -> None: if fill.order.side == OrderSide.BUY: new_quantity = self.quantity + fill.fill_quantity if new_quantity != 0: self.avg_cost = ( (self.quantity * self.avg_cost + fill.fill_quantity * fill.fill_price) / new_quantity ) self.quantity = new_quantity else: self.realized_pnl += fill.fill_quantity * (fill.fill_price - self.avg_cost) self.quantity -= fill.fill_quantity @dataclass class Portfolio: cash: Decimal positions: Dict[str, Position] = field(default_factory=dict) def get_position(self, symbol: str) -> Position: if symbol not in self.positions: self.positions[symbol] = Position(symbol=symbol) return self.positions[symbol] def process_fill(self, fill: Fill) -> None: position = self.get_position(fill.order.symbol) position.update(fill) if fill.order.side == OrderSide.BUY: self.cash -= fill.fill_price * fill.fill_quantity + fill.commission else: self.cash += fill.fill_price * fill.fill_quantity - fill.commission def get_equity(self, prices: Dict[str, Decimal]) -> Decimal: equity = self.cash for symbol, position in self.positions.items(): if position.quantity != 0 and symbol in prices: equity += position.quantity * prices[symbol] return equity要点:
- 买入时用加权平均成本法更新
avg_cost,卖出时按fill_price - avg_cost累积已实现盈亏,符合期货/股票多头仓位的标准记账方式。 process_fill中现金变化显式扣除commission(佣金),把交易成本真实写入账本,而非事后粗略折算。get_equity用持仓数量 × 当前价格估算组合净值,供逐 bar 绘制权益曲线。
5.3 策略与执行模型的抽象
class Strategy(ABC): @abstractmethod def on_bar(self, timestamp: datetime, data: pd.DataFrame) -> List[Order]: pass @abstractmethod def on_fill(self, fill: Fill) -> None: pass class ExecutionModel(ABC): @abstractmethod def execute(self, order: Order, bar: pd.Series) -> Optional[Fill]: pass class SimpleExecutionModel(ExecutionModel): def __init__(self, slippage_bps: float = 10, commission_per_share: float = 0.01): self.slippage_bps = slippage_bps self.commission_per_share = commission_per_share def execute(self, order: Order, bar: pd.Series) -> Optional[Fill]: if order.order_type == OrderType.MARKET: base_price = Decimal(str(bar["open"])) # Apply slippage slippage_mult = 1 + (self.slippage_bps / 10000) if order.side == OrderSide.BUY: fill_price = base_price * Decimal(str(slippage_mult)) else: fill_price = base_price / Decimal(str(slippage_mult)) commission = order.quantity * Decimal(str(self.commission_per_share)) slippage = abs(fill_price - base_price) * order.quantity return Fill( order=order, fill_price=fill_price, fill_quantity=order.quantity, commission=commission, slippage=slippage, timestamp=bar.name ) return NoneStrategy用抽象方法把"信号生成"与"成交回报处理"分离;ExecutionModel把"订单如何变成成交"独立出来。SimpleExecutionModel演示了:
- 滑点(slippage):默认 10 bps(基点),买入在基准价上浮、卖出在基准价下浮,即
slippage_mult = 1 + slippage_bps/10000; - 佣金(commission):默认每股
0.01,按成交数量线性计算; - 以 bar 的开盘价(
bar["open"])作为成交基准价,模拟"下一根 bar 开盘执行"的时序,天然规避收盘价信号与开盘价成交的前视偏差。
5.4 主循环:Backtester
class Backtester: def __init__( self, strategy: Strategy, execution_model: ExecutionModel, initial_capital: Decimal = Decimal("100000") ): self.strategy = strategy self.execution_model = execution_model self.portfolio = Portfolio(cash=initial_capital) self.equity_curve: List[tuple] = [] self.trades: List[Fill] = [] def run(self, data: pd.DataFrame) -> pd.DataFrame: """Run backtest on OHLCV data with DatetimeIndex.""" pending_orders: List[Order] = [] for timestamp, bar in data.iterrows(): # Execute pending orders at today's prices for order in pending_orders: fill = self.execution_model.execute(order, bar) if fill: self.portfolio.process_fill(fill) self.strategy.on_fill(fill) self.trades.append(fill) pending_orders.clear() # Get current prices for equity calculation prices = {data.index.name or "default": Decimal(str(bar["close"]))} equity = self.portfolio.get_equity(prices) self.equity_curve.append((timestamp, float(equity))) # Generate new orders for next bar new_orders = self.strategy.on_bar(timestamp, data.loc[:timestamp]) pending_orders.extend(new_orders) return self._create_results() def _create_results(self) -> pd.DataFrame: equity_df = pd.DataFrame(self.equity_curve, columns=["timestamp", "equity"]) equity_df.set_index("timestamp", inplace=True) equity_df["returns"] = equity_df["equity"].pct_change() return equity_df主循环的时序纪律值得反复强调,它同时规避了前视偏差与交易成本偏差:
- 先执行昨日挂单(
pending_orders):上一 bar 收盘生成的订单,在今日价格上成交; - 再记录今日净值:用今日
close计算权益,写入权益曲线; - 最后生成明日新订单:策略基于截至当前的
data.loc[:timestamp]生成订单,加入pending_orders,留待下一根 bar 执行。
输出为标准 DataFrame:以时间戳为索引的equity(权益曲线)与returns(由pct_change()计算的收益率序列),可直接喂给后文 5.7 的指标计算函数。
六、实现模式二:向量化回测器(Vectorized Backtester)
当策略逻辑简单、只需要验证想法时,逐 bar 事件循环性能不足。Pattern 2 提供了基于 pandas 数组运算的向量化回测器,适合动量、均线交叉这类信号型策略的大规模快速扫描:
import pandas as pd import numpy as np from typing import Callable, Dict, Any class VectorizedBacktester: """Fast vectorized backtester for simple strategies.""" def __init__( self, initial_capital: float = 100000, commission: float = 0.001, # 0.1% slippage: float = 0.0005 # 0.05% ): self.initial_capital = initial_capital self.commission = commission self.slippage = slippage def run( self, prices: pd.DataFrame, signal_func: Callable[[pd.DataFrame], pd.Series] ) -> Dict[str, Any]: """ Run backtest with signal function. Args: prices: DataFrame with 'close' column signal_func: Function that returns position signals (-1, 0, 1) Returns: Dictionary with results """ # Generate signals (shifted to avoid look-ahead) signals = signal_func(prices).shift(1).fillna(0) # Calculate returns returns = prices["close"].pct_change() # Calculate strategy returns with costs position_changes = signals.diff().abs() trading_costs = position_changes * (self.commission + self.slippage) strategy_returns = signals * returns - trading_costs # Build equity curve equity = (1 + strategy_returns).cumprod() * self.initial_capital # Calculate metrics results = { "equity": equity, "returns": strategy_returns, "signals": signals, "metrics": self._calculate_metrics(strategy_returns, equity) } return results def _calculate_metrics( self, returns: pd.Series, equity: pd.Series ) -> Dict[str, float]: """Calculate performance metrics.""" total_return = (equity.iloc[-1] / self.initial_capital) - 1 annual_return = (1 + total_return) ** (252 / len(returns)) - 1 annual_vol = returns.std() * np.sqrt(252) sharpe = annual_return / annual_vol if annual_vol > 0 else 0 # Drawdown rolling_max = equity.cummax() drawdown = (equity - rolling_max) / rolling_max max_drawdown = drawdown.min() # Win rate winning_days = (returns > 0).sum() total_days = (returns != 0).sum() win_rate = winning_days / total_days if total_days > 0 else 0 return { "total_return": total_return, "annual_return": annual_return, "annual_volatility": annual_vol, "sharpe_ratio": sharpe, "max_drawdown": max_drawdown, "win_rate": win_rate, "num_trades": int((returns != 0).sum()) }几个实现要点:
- 前视偏差的天然防御:
signals = signal_func(prices).shift(1).fillna(0)——信号整体后移一根 bar,确保第 t 天的持仓决策只使用第 t-1 天及之前的信息;signals.diff().abs()计算换仓位置变化,仅对发生调仓的 bar 计收交易成本。 - 成本建模:
trading_costs = position_changes * (commission + slippage),把 0.1% 佣金与 0.05% 滑点合并为单次换仓成本,strategy_returns = signals * returns - trading_costs显式扣除。 - 年化约定:按 252 个交易日年化(
np.sqrt(252)),这与 risk-metrics-calculation 中ann_factor = 252的约定一致,便于两个技能产出的指标直接互相引用。 - 使用示例:
momentum_signal演示了 20 日均线动量信号(收盘价高于 SMA 持多头,否则空仓),调用方式为backtester.run(price_data, lambda p: momentum_signal(p, 50))。
七、实现模式三:Walk-Forward Optimization 前向优化
Pattern 3 把第 4 节的 Walk-Forward 方法论落成代码,支持anchored(锚定/扩展窗口)与rolling(滚动窗口)两种模式,并通过网格搜索在每段训练窗口内选择最优参数:
from typing import Callable, Dict, List, Tuple, Any import pandas as pd import numpy as np from itertools import product class WalkForwardOptimizer: """Walk-forward analysis with anchored or rolling windows.""" def __init__( self, train_period: int, test_period: int, anchored: bool = False, n_splits: int = None ): """ Args: train_period: Number of bars in training window test_period: Number of bars in test window anchored: If True, training always starts from beginning n_splits: Number of train/test splits (auto-calculated if None) """ self.train_period = train_period self.test_period = test_period self.anchored = anchored self.n_splits = n_splits def generate_splits( self, data: pd.DataFrame ) -> List[Tuple[pd.DataFrame, pd.DataFrame]]: """Generate train/test splits.""" splits = [] n = len(data) if self.n_splits: step = (n - self.train_period) // self.n_splits else: step = self.test_period start = 0 while start + self.train_period + self.test_period <= n: if self.anchored: train_start = 0 else: train_start = start train_end = start + self.train_period test_end = min(train_end + self.test_period, n) train_data = data.iloc[train_start:train_end] test_data = data.iloc[train_end:test_end] splits.append((train_data, test_data)) start += step return splits def optimize( self, data: pd.DataFrame, strategy_func: Callable, param_grid: Dict[str, List], metric: str = "sharpe_ratio" ) -> Dict[str, Any]: """ Run walk-forward optimization. Args: data: Full dataset strategy_func: Function(data, **params) -> results dict param_grid: Parameter combinations to test metric: Metric to optimize Returns: Combined results from all test periods """ splits = self.generate_splits(data) all_results = [] optimal_params_history = [] for i, (train_data, test_data) in enumerate(splits): # Optimize on training data best_params, best_metric = self._grid_search( train_data, strategy_func, param_grid, metric ) optimal_params_history.append(best_params) # Test with optimal params test_results = strategy_func(test_data, **best_params) test_results["split"] = i test_results["params"] = best_params all_results.append(test_results) print(f"Split {i+1}/{len(splits)}: " f"Best {metric}={best_metric:.4f}, params={best_params}") return { "split_results": all_results, "param_history": optimal_params_history, "combined_equity": self._combine_equity_curves(all_results) } def _grid_search( self, data: pd.DataFrame, strategy_func: Callable, param_grid: Dict[str, List], metric: str ) -> Tuple[Dict, float]: """Grid search for best parameters.""" best_params = None best_metric = -np.inf # Generate all parameter combinations param_names = list(param_grid.keys()) param_values = list(param_grid.values()) for values in product(*param_values): params = dict(zip(param_names, values)) results = strategy_func(data, **params) if results["metrics"][metric] > best_metric: best_metric = results["metrics"][metric] best_params = params return best_params, best_metric def _combine_equity_curves( self, results: List[Dict] ) -> pd.Series: """Combine equity curves from all test periods.""" combined = pd.concat([r["equity"] for r in results]) return combined设计解读:
- 切分逻辑:
generate_splits默认以test_period为步长滚动;指定n_splits时自动计算步长。anchored=True时训练起点始终为 0(扩展窗口),否则训练窗口随起点滚动(滚动窗口)。 - 优化闭环:每个 split 内,
_grid_search用itertools.product对param_grid做笛卡尔积网格搜索,在训练段上按目标指标(默认sharpe_ratio)选出最优参数,再把该参数应用到紧随其后的测试段——训练与测试严格分离,杜绝在测试段上"偷看调参"。 - 产出:
split_results(各段测试结果)、param_history(参数漂移历史,可观察最优参数是否随市场环境剧烈变化——若剧烈变化,往往意味着过拟合)、combined_equity(拼接全部测试段的权益曲线,用于评估整体样本外表现)。
八、实现模式四:Monte Carlo 分析
Pattern 4 通过自助重采样(bootstrap with replacement)量化策略的不确定性。SKILL.md 的 Do's 中 "Monte Carlo analysis - Understand uncertainty" 即是本模式的入口:
import numpy as np import pandas as pd from typing import Dict, List class MonteCarloAnalyzer: """Monte Carlo simulation for strategy robustness.""" def __init__(self, n_simulations: int = 1000, confidence: float = 0.95): self.n_simulations = n_simulations self.confidence = confidence def bootstrap_returns( self, returns: pd.Series, n_periods: int = None ) -> np.ndarray: """ Bootstrap simulation by resampling returns. Args: returns: Historical returns series n_periods: Length of each simulation (default: same as input) Returns: Array of shape (n_simulations, n_periods) """ if n_periods is None: n_periods = len(returns) simulations = np.zeros((self.n_simulations, n_periods)) for i in range(self.n_simulations): # Resample with replacement simulated_returns = np.random.choice( returns.values, size=n_periods, replace=True ) simulations[i] = simulated_returns return simulations def analyze_drawdowns( self, returns: pd.Series ) -> Dict[str, float]: """Analyze drawdown distribution via simulation.""" simulations = self.bootstrap_returns(returns) max_drawdowns = [] for sim_returns in simulations: equity = (1 + sim_returns).cumprod() rolling_max = np.maximum.accumulate(equity) drawdowns = (equity - rolling_max) / rolling_max max_drawdowns.append(drawdowns.min()) max_drawdowns = np.array(max_drawdowns) return { "expected_max_dd": np.mean(max_drawdowns), "median_max_dd": np.median(max_drawdowns), f"worst_{int(self.confidence*100)}pct": np.percentile( max_drawdowns, (1 - self.confidence) * 100 ), "worst_case": max_drawdowns.min() } def probability_of_loss( self, returns: pd.Series, holding_periods: List[int] = [21, 63, 126, 252] ) -> Dict[int, float]: """Calculate probability of loss over various holding periods.""" results = {} for period in holding_periods: if period > len(returns): continue simulations = self.bootstrap_returns(returns, period) total_returns = (1 + simulations).prod(axis=1) - 1 prob_loss = (total_returns < 0).mean() results[period] = prob_loss return results def confidence_interval( self, returns: pd.Series, periods: int = 252 ) -> Dict[str, float]: """Calculate confidence interval for future returns.""" simulations = self.bootstrap_returns(returns, periods) total_returns = (1 + simulations).prod(axis=1) - 1 lower = (1 - self.confidence) / 2 upper = 1 - lower return { "expected": total_returns.mean(), "lower_bound": np.percentile(total_returns, lower * 100), "upper_bound": np.percentile(total_returns, upper * 100), "std": total_returns.std() }三个核心分析维度:
analyze_drawdowns:对历史收益率做 1000 次有放回重采样,每次模拟一条权益曲线并计算最大回撤,输出期望最大回撤、中位数最大回撤、95% 分位最差回撤与最坏情形,回答"这个策略的最大回撤有多稳定"。probability_of_loss:默认考察 21/63/126/252(约 1/3/6/12 个月)持有期下亏损的概率,用于资金管理与持有期决策。confidence_interval:对 252 个周期(一年)的累计收益给出置信区间(默认 95%),lower_bound/upper_bound直接量化策略收益的不确定性范围。
九、性能指标体系
details.md 末尾提供了统一的绩效指标计算函数,可直接对接事件驱动回测器与向量化回测器产出的returns序列:
def calculate_metrics(returns: pd.Series, rf_rate: float = 0.02) -> Dict[str, float]: """Calculate comprehensive performance metrics.""" # Annualization factor (assuming daily returns) ann_factor = 252 # Basic metrics total_return = (1 + returns).prod() - 1 annual_return = (1 + total_return) ** (ann_factor / len(returns)) - 1 annual_vol = returns.std() * np.sqrt(ann_factor) # Risk-adjusted returns sharpe = (annual_return - rf_rate) / annual_vol if annual_vol > 0 else 0 # Sortino (downside deviation) downside_returns = returns[returns < 0] downside_vol = downside_returns.std() * np.sqrt(ann_factor) sortino = (annual_return - rf_rate) / downside_vol if downside_vol > 0 else 0 # Calmar ratio equity = (1 + returns).cumprod() rolling_max = equity.cummax() drawdowns = (equity - rolling_max) / rolling_max max_drawdown = drawdowns.min() calmar = annual_return / abs(max_drawdown) if max_drawdown != 0 else 0 # Win rate and profit factor wins = returns[returns > 0] losses = returns[returns < 0] win_rate = len(wins) / len(returns[returns != 0]) if len(returns[returns != 0]) > 0 else 0 profit_factor = wins.sum() / abs(losses.sum()) if losses.sum() != 0 else np.inf return { "total_return": total_return, "annual_return": annual_return, "annual_volatility": annual_vol, "sharpe_ratio": sharpe, "sortino_ratio": sortino, "calmar_ratio": calmar, "max_drawdown": max_drawdown, "win_rate": win_rate, "profit_factor": profit_factor, "num_trades": int((returns != 0).sum()) }各指标含义与计算口径:
| 指标 | 含义 | 计算口径 |
|---|---|---|
total_return | 累计总收益 | (1+returns).prod()-1 |
annual_return | 年化收益 | 按 252 交易日几何年化 |
annual_volatility | 年化波动率 | 日收益标准差 × √252 |
sharpe_ratio | 夏普比率 | (年化收益 - 无风险利率) / 年化波动率,默认rf_rate=0.02 |
sortino_ratio | 索提诺比率 | 分母换为下行波动(仅亏损日标准差) |
calmar_ratio | 卡玛比率 | 年化收益 / 最大回撤绝对值 |
max_drawdown | 最大回撤 | 权益曲线峰值到谷底的最大跌幅 |
win_rate | 胜率 | 盈利周期数 / 非零周期数 |
profit_factor | 盈亏比 | 总盈利 / 总亏损绝对值,无亏损时为inf |
num_trades | 交易次数 | 非零收益周期计数 |
风险调整后收益指标(Sharpe/Sortino/Calmar)与回撤指标正是 risk-manager.md 中 "Risk-adjusted performance metrics"、"Maximum drawdown analysis" 所依赖的度量基础;若需要更细化的 VaR、CVaR、下行偏差等风险指标,可进一步引用同插件下的 risk-metrics-calculation Skill。
十、Best Practices:Do's 与 Don'ts
SKILL.md 末尾以清单形式总结了可操作的最佳实践,这是本技能最浓缩的实战守则:
应该做(Do's)
- Use point-in-time data—— 使用时点数据,规避前视偏差
- Include transaction costs—— 计入交易成本,获得现实估计
- Test out-of-sample—— 始终预留样本外数据
- Use walk-forward—— 使用前向分析,而非仅一次 train/test
- Monte Carlo analysis—— 用蒙特卡洛理解不确定性
不该做(Don'ts)
- Don't overfit—— 限制参数数量,避免对历史曲线过度拟合
- Don't ignore survivorship—— 数据必须包含退市标的
- Don't use adjusted data carelessly—— 谨慎使用复权数据,理解复权方式对信号的影响
- Don't optimize on full history—— 不在全历史上优化,必须保留测试集
- Don't ignore capacity—— 不要忽略资金容量,市场冲击(market impact)会影响真实收益
十一、在本仓库中的使用方式
本 Skill 遵循 Agent Skills 规范,采用"元数据 → 指令 → 资源"的三层渐进式披露(progressive disclosure)结构:SKILL.md的 frontmatter 承载名称与激活条件(始终加载),正文为导航级核心概念与最佳实践(激活后加载),references/details.md存放完整代码模式(按需读取)。
安装方式有两种:
# 方式一:通过插件安装(加载 quantitative-trading 插件的 agents + skills + commands) /plugin install quantitative-trading # 方式二:仅安装单个 Skill 到任意 Agent(使用 Agent Skills 安装器) gh skill install wshobson/agents backtesting-frameworks # GitHub CLI 2.90+ npx skills add wshobson/agents --skill backtesting-frameworks # vercel-labs/skills在实际使用中,Agent 的典型协作链路为:quant-analystAgent 负责设计策略与搭建回测(调用本 Skill 的事件驱动/向量化模式),risk-managerAgent 负责用 Monte Carlo 与风险指标评估回测结果的稳健性,两者共享本 Skill 产出的权益曲线与收益序列。该插件在 docs/plugins.md 中被归类为 Finance 类目("Algorithmic trading and risk management"),共提供 backtesting-frameworks 与 risk-metrics-calculation 两个技能,形成"先正确回测、再度量风险"的完整闭环。
结语
backtesting-frameworks Skill 的价值不在于提供某一个回测框架,而在于把回测中最容易被忽视、也最致命的偏差问题系统化、工程化:前视偏差靠时序纪律与shift(1)防御,幸存者偏差靠数据完整性,过拟合靠样本外与前向分析,交易成本靠显式成本模型,选择偏差靠预先注册。配合事件驱动与向量化两套回测引擎、Walk-Forward 优化器、Monte Carlo 分析器以及统一的绩效指标函数,你可以直接在本仓库提供的代码模式之上搭建属于自己的、可上生产环境的量化回测基础设施。
【免费下载链接】agentsMulti-harness agentic plugin marketplace for Claude Code, Codex, Cursor, OpenCode, GitHub Copilot, and Google Antigravity项目地址: https://gitcode.com/GitHub_Trending/agents24/agents
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考