import pandas as pd
import numpy as np
# 假设已有 df:包含 close, signal
# signal: 1 多头,-1 空头,0 空仓
def rolling_backtest(df, window=252):
results = []
for start in range(0, len(df)-window):
end = start + window
sub = df.iloc[start:end]
# 简单收益:signal * 下一根收益率
sub['ret'] = sub['close'].pct_change()
sub['strategy_ret'] = sub['signal'].shift(1) * sub['ret']
cum_ret = (1 + sub['strategy_ret']).prod() - 1
max_dd = (sub['strategy_ret'].cumsum().cummax() - sub['strategy_ret'].cumsum()).max()
results.append([sub.index[0], sub.index[-1], cum_ret, max_dd])
return pd.DataFrame(results, columns=['start', 'end', 'cum_ret', 'max_dd'])
rolling_result = rolling_backtest(df)
rolling_result.head()
import numpy as np
import matplotlib.pyplot as plt
returns = df['strategy_ret'].dropna().values
def monte_carlo(returns, n=1000):
simulations = []
for _ in range(n):
shuffled = np.random.permutation(returns)
simulated = (1 + shuffled).prod() - 1
simulations.append(simulated)
return simulations
mc_results = monte_carlo(returns, 1000)
plt.hist(mc_results, bins=50)
plt.title("Monte Carlo Return Distribution")
plt.show()
rolling_result.to_csv("rolling_result.csv", index=False)
# 可视化每个窗口的收益
plt.plot(rolling_result['cum_ret'])
plt.title("Rolling Window Performance")
plt.show()
# 可视化最大回撤
plt.plot(rolling_result['max_dd'])
plt.title("Rolling MaxDD")
plt.show()
# %窗口正收益
positive_ratio = (rolling_result['cum_ret'] > 0).mean()
# median
median_yield = rolling_result['cum_ret'].median()
# IQR
iqr = rolling_result['cum_ret'].quantile(0.75) - rolling_result['cum_ret'].quantile(0.25)
stability_report = {
"positive_ratio": positive_ratio,
"median_yield": median_yield,
"iqr": iqr
}
stability_report
判定建议模板:
def evaluate_stability(stab):
if stab["positive_ratio"] > 0.6 and stab["iqr"] < 0.15:
return "策略整体稳定,适合进入实盘或继续优化。"
else:
return "策略稳定性偏弱,建议继续改进(过滤器、时段优化、特征)。"
evaluate_stability(stability_report)
def stress_test(df, fee=0.0004, slippage=0.0005):
df = df.copy()
df['ret'] = df['close'].pct_change()
# 模拟滑点 + 费用
df['strategy_ret_stress'] = df['signal'].shift(1) * df['ret'] - fee - slippage
result = (1 + df['strategy_ret_stress'].dropna()).prod() - 1
return result
for fee in [0.0004, 0.0008, 0.001]:
for slip in [0.0005, 0.001]:
print(f"fee={fee}, slippage={slip}, return={stress_test(df, fee, slip)}")
模拟缺失数据:
df_missing = df.copy()
df_missing.loc[df_missing.sample(100).index, 'close'] = np.nan
df_missing = df_missing.fillna(method="ffill")