Python 量化

Data sources, backtesting skeleton, and the traps that make backtests lie.

Advanced
Key PointsA backtest without survivorship-bias handling, fees and slippage is fiction — fix those three before tuning anything.

Get Data

For A-share data, akshare (free) or tushare (token) are common. Pull daily bars into a DataFrame first.

Python
import akshare as ak

df = ak.stock_zh_a_hist(symbol="600519", period="daily",
                        start_date="20240101", end_date="20241231", adjust="qfq")
print(df.columns.tolist())
print(df.tail(2)[["日期", "收盘"]])
Output
['日期', '股票代码', '开盘', '收盘', '最高', '最低', '成交量', '成交额', '振幅', '涨跌幅', '涨跌额', '换手率']
      日期      收盘
239  2024-12-30  1524.00
240  2024-12-31  1521.00

A Backtest Skeleton

A minimal backtest is signal → position → returns. Compute returns first, tune later.

Python
import pandas as pd

close = df["收盘"].astype(float)
ret = close.pct_change().fillna(0)

signal = (close > close.rolling(20).mean()).astype(int)   # 20 日均线上穿
pos = signal.shift(1).fillna(0)                            # 次日开盘执行,避免未来函数

strat = pos * ret - 0.0003 * pos.diff().abs().fillna(0)    # 扣双边手续费
nav = (1 + strat).cumprod()

print(round(nav.iloc[-1], 3))
print(round(strat.mean() / strat.std() * 252 ** 0.5, 2))   # 年化夏普
Output
1.183
0.87

Traps

TrapEffectFix
Look-ahead biasUses tomorrow's data todayShift signals by one bar
Survivorship biasOnly delisted-free stocks testedInclude delisted universe
No fees/slippageReturns inflated 2-5% a yearSubtract commission and slippage
OverfittingParameters fit noiseOut-of-sample and walk-forward
Any backtest above 30% annualised is a code bug until proven otherwise. Validate out-of-sample before paper trading.

Friends & Partners