fix: MC年化硬编码^0.5虚高——改用实际区间年数(5年策略年化从21.8%修正到真实~8.9%)

This commit is contained in:
hmo
2026-07-30 15:38:59 +08:00
parent 6f4be02a90
commit 853e0cc942
+24 -3
View File
@@ -1123,10 +1123,18 @@ def portfolio_sim_full(trades, capital=1000000, runs=50):
mean_ret = sum(rets) / len(rets)
mean_dd = sum(dds) / len(dds)
std = (sum((x - mean_ret) ** 2 for x in rets) / len(rets)) ** 0.5
# 年化用实际区间年数(修复硬编码^0.5=2年的虚高问题)
_dts = sorted({t['entry_date'] for t in trades if t.get('entry_date')})
if _dts:
from datetime import datetime as _dt2
_span_days = (_dt2.strptime(_dts[-1], '%Y-%m-%d') - _dt2.strptime(_dts[0], '%Y-%m-%d')).days
_years = max(_span_days / 365.0, 0.5)
else:
_years = 2.0
return {
'capital_final': round(sum(finals) / len(finals), 0),
'total_return_pct': round(mean_ret, 1),
'cagr_pct': round((((1 + mean_ret / 100) ** 0.5) - 1) * 100, 1),
'cagr_pct': round((((1 + mean_ret / 100) ** (1 / _years)) - 1) * 100, 1),
'portfolio_max_dd_pct': round(mean_dd, 1),
'positions_taken': round(sum(taken) / len(taken)),
'positions_skipped': len(trades) - round(sum(taken) / len(taken)),
@@ -1154,9 +1162,22 @@ def portfolio_sim(trades, capital=1000000, max_positions=10, cost=True, random_s
if not trades:
return {}
# 交易日历(用大盘指数日期)
cal = sorted(_MKT_CTX.keys())
if not cal:
# 日历从 stock_daily 实际交易日生成(覆盖策略完整区间,不被全局_MKT_CTX的2年限制截断)
_dates = sorted({t['entry_date'] for t in trades if t.get('entry_date')})
if not _dates:
return {}
_min_d, _max_d = _dates[0], _dates[-1]
# 日历缓存(避免每策略x50次MC重复扫stock_daily
global _CAL_CACHE
if '_CAL_CACHE' not in globals() or _CAL_CACHE.get('range') != (_min_d, _max_d):
_c = sqlite3.connect(DB_PATH)
_CAL_CACHE = {'range': (_min_d, _max_d), 'cal': [r[0] for r in _c.execute(
"SELECT DISTINCT date FROM stock_daily WHERE date>=? AND date<=? ORDER BY date",
(_min_d, _max_d)).fetchall()]}
_c.close()
cal = _CAL_CACHE['cal']
if not cal:
cal = _dates
cal_idx = {d: i for i, d in enumerate(cal)}
def add_days(d, n):
i = cal_idx.get(d)