news 2026/9/11 21:24:44

quantitative-trading 插件 backtesting-frameworks Skill 实战:构建抗偏差的量化回测系统

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
quantitative-trading 插件 backtesting-frameworks Skill 实战:构建抗偏差的量化回测系统

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) │ └─────────────────────────────────────────┘

三段职责边界:

  1. Training Set(训练集):只用于策略开发与参数优化。所有基于历史数据的"学习"都必须发生在这里。
  2. Validation Set(验证集):用于参数选择与模型调优,严禁偷看(No Peeking)。如果验证集被反复用于调参,它会退化为训练集的一部分,最终 Test Set 也会被污染。
  3. 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 None

Strategy用抽象方法把"信号生成"与"成交回报处理"分离;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

主循环的时序纪律值得反复强调,它同时规避了前视偏差与交易成本偏差:

  1. 先执行昨日挂单pending_orders):上一 bar 收盘生成的订单,在今日价格上成交;
  2. 再记录今日净值:用今日close计算权益,写入权益曲线;
  3. 最后生成明日新订单:策略基于截至当前的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_searchitertools.productparam_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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 21:23:28

ResNet18网络实战指南:结构拆解、PyTorch训练与避坑技巧

简介&#xff1a;ResNet18 是深度残差网络中结构精简且常用的 18 层 CNN 模型&#xff0c;由何恺明等人提出&#xff0c;适合希望在图像分类、特征提取等视觉任务中快速上手的初学者&#xff0c;以及需要在嵌入式或移动端部署轻量级网络的开发者。资源包仅 3 个文件&#xff0c…

作者头像 李华
网站建设 2026/9/11 21:21:21

浏览器缓存机制与性能优化实践

1. 浏览器缓存机制全景解析 浏览器缓存作为Web性能优化的核心手段&#xff0c;其运作机制涉及多个层次的协同配合。现代浏览器通常采用四级缓存体系&#xff1a;Service Worker缓存、HTTP缓存、内存缓存&#xff08;Memory Cache&#xff09;和磁盘缓存&#xff08;Disk Cache&…

作者头像 李华
网站建设 2026/9/11 21:21:06

1m³/h袋式过滤器设计解析与工程实践

1. 项目概述&#xff1a;1m/h袋式过滤器图纸解析在工业流体处理领域&#xff0c;袋式过滤器作为预处理设备的核心部件&#xff0c;其设计合理性直接影响整个系统的运行效率。今天要拆解的这套1立方米每小时处理量的袋式过滤器图纸&#xff0c;是典型的低压小型过滤系统设计方案…

作者头像 李华
网站建设 2026/9/11 21:19:31

STM32工程化避坑指南:HAL库、硬件耦合与量产可靠性

1. 这不是“学得久就变强”的故事&#xff0c;而是“学得久才看清陷阱”的真相STM32学得越久&#xff0c;越容易掉进这三个坑——这句话不是危言耸听&#xff0c;是我带过67个嵌入式毕设学生、亲手调试过213块不同型号开发板、在工厂产线跟过4个月量产烧录流程后&#xff0c;用…

作者头像 李华
网站建设 2026/9/11 21:19:13

串口通信11

串口定义&#xff1a;是一种应用十分广泛的通讯接口&#xff0c;成本低操作简单&#xff0c;可实现两个设备的互相通信。51单片机内部自带UART&#xff0c;可实现单片机的串口通信。硬件电路&#xff1a;电平标准&#xff1a;差分信号是指两根线之间的电平差&#xff0c;TTL与R…

作者头像 李华