feat: 温区级综合/普适计算——温区内信号月份分散度+温区总月份占比,温区行综合用温区portfolio算,universality显示温区级月份/温区总月份;说明列fallback
This commit is contained in:
@@ -574,7 +574,65 @@ def api_research_strategies():
|
||||
_c.close()
|
||||
except Exception:
|
||||
pass
|
||||
# 2026-08-15 证伪标记(数据驱动):strategy_research.deprecated
|
||||
# 温区级普适前先建立日期→温区映射(A股 market_regime)
|
||||
_regime_map = {}
|
||||
try:
|
||||
import sqlite3 as _sq5
|
||||
_c5 = _sq5.connect(str(DATA_DIR / "mofin.db"), timeout=10)
|
||||
for _r in _c5.execute("SELECT date, regime FROM market_regime WHERE market='a'"):
|
||||
_regime_map[_r[0]] = _r[1]
|
||||
_c5.close()
|
||||
except Exception:
|
||||
pass
|
||||
# 2026-08-15 温区级普适:策略在适应温区内发出信号的月份分散度 + 温区总月份占比
|
||||
# 温区总月份数(A股: choppy35.4%/trend_down30.3%/trend_up34.3%;按市场 regime 时长统计)
|
||||
try:
|
||||
import sqlite3 as _sq3
|
||||
_c3 = _sq3.connect(str(DATA_DIR / "mofin.db"), timeout=10)
|
||||
_total_months = _c3.execute(
|
||||
"SELECT COUNT(DISTINCT substr(date,1,7)) FROM market_regime WHERE market='a'").fetchone()[0]
|
||||
_regime_months = {r[0]: r[1] for r in _c3.execute(
|
||||
"SELECT regime, COUNT(DISTINCT substr(date,1,7)) FROM market_regime WHERE market='a' GROUP BY regime").fetchall()}
|
||||
_c3.close()
|
||||
except Exception:
|
||||
_total_months = 0
|
||||
_regime_months = {}
|
||||
_regime_universality = {} # (version, regime) -> {'months': N, 'score': 分散度}
|
||||
try:
|
||||
import sqlite3 as _sq4, json as _json4, collections as _coll
|
||||
_c4 = _sq4.connect(str(DATA_DIR / "mofin.db"), timeout=10)
|
||||
for _r in _c4.execute(
|
||||
"SELECT version, results_json FROM strategy_research WHERE results_json IS NOT NULL"):
|
||||
try:
|
||||
_res = _json4.loads(_r[1])
|
||||
_trades = _res.get("trades", [])
|
||||
if not _trades:
|
||||
continue
|
||||
# 按温区过滤该策略 trades(regime_winrates 已有温区归类)
|
||||
for _rg in ("trend_up", "choppy", "trend_down"):
|
||||
_rg_months = _coll.Counter()
|
||||
for _t in _trades:
|
||||
_ed = _t.get("entry_date", "")
|
||||
if _ed and _regime_map.get(_ed) == _rg:
|
||||
_rg_months[_ed[:7]] += 1
|
||||
if _rg_months:
|
||||
_n = len(_rg_months)
|
||||
_tot = sum(_rg_months.values())
|
||||
_score = 0.0
|
||||
if _n > 1:
|
||||
for _c in _rg_months.values():
|
||||
_p = _c / _tot
|
||||
_score -= _p * (0.0 if _p == 0 else __import__('math').log(_p))
|
||||
_score = _score / __import__('math').log(_n) * 100
|
||||
_regime_universality.setdefault(_r[0], {})[_rg] = {
|
||||
"months": _n, "score": round(_score, 0),
|
||||
"regime_total_months": _regime_months.get(_rg, 0),
|
||||
}
|
||||
except Exception:
|
||||
continue
|
||||
_c4.close()
|
||||
except Exception:
|
||||
_regime_universality = {}
|
||||
_deprecated = {}
|
||||
try:
|
||||
import sqlite3 as _sq2
|
||||
|
||||
+5
-3
@@ -2442,8 +2442,9 @@ function renderStrategyTable(strategies) {
|
||||
const holdShow = rw && rw.avg_hold_days != null ? rw.avg_hold_days : (isOverall ? st.avg_hold_days : null);
|
||||
const sharpeShow = rw && rw.sharpe_ratio != null ? rw.sharpe_ratio : (isOverall ? st.sharpe_ratio : null);
|
||||
const pfShow = rw && rw.profit_factor != null ? rw.profit_factor : (isOverall ? st.profit_factor : null);
|
||||
// 组合级列:整体行用整体组合;温区行用温区级组合(rw.portfolio)+ 温区级 composite + 整体普适近似
|
||||
// 组合级列:整体行用整体组合;温区行用温区级组合(rw.portfolio)+ 温区级 composite + 温区级 universality
|
||||
const rwPf = rw && rw.portfolio ? rw.portfolio : null;
|
||||
const rwUniv = rw && rw.universality ? rw.universality : null;
|
||||
// 温区级 composite(用温区 portfolio 的 cagr/total_return + 温区胜率/夏普/盈亏比/回撤,对齐整体综合分逻辑)
|
||||
let rgComposite = null;
|
||||
if (rwPf) {
|
||||
@@ -2455,7 +2456,8 @@ function renderStrategyTable(strategies) {
|
||||
rgComposite = Math.round((rgRet + rgWr + rgSh + rgPfc + rgDd) * (s._confidence || 1));
|
||||
}
|
||||
const compositeShow = isOverall ? s._composite : rgComposite;
|
||||
const universalityShow = isOverall ? (st.universality || {}).score : (st.universality || {}).score;
|
||||
// 温区级 universality(温区内信号月份分散度 + 温区总月份占比)
|
||||
const universalityShow = isOverall ? (st.universality || {}).score : (rwUniv ? rwUniv.score : null);
|
||||
const retShow = isOverall ? (st.portfolio_full||{}).total_return_pct : (rwPf ? rwPf.total_return_pct : null);
|
||||
const capShow = isOverall ? (st.portfolio_full||pf).capital_final : (rwPf ? rwPf.capital_final : null);
|
||||
const cagrShow = isOverall ? (st.portfolio_full||pf).cagr_pct : (rwPf ? rwPf.cagr_pct : null);
|
||||
@@ -2474,7 +2476,7 @@ function renderStrategyTable(strategies) {
|
||||
'<td class="text-center px-2 py-1.5 font-mono text-cyan-300">' + (st.sizing_slots ? st.sizing_slots + '仓' : '10仓') + (st.conviction_model ? '<button onclick="event.stopPropagation();showConviction(\'' + s.version + '\')" class="text-amber-400 hover:text-amber-300" title="查看信念分级仓位规则">⚡</button>' : '') + '</td>' +
|
||||
'<td class="text-center px-2 py-1.5">' + ((s.description && s.description.title) ? '<button onclick="event.stopPropagation();showDesc(\'' + s.version + '\')" class="text-blue-400 hover:text-blue-300" title="查看策略说明">📖</button>' : '<span class="text-slate-700">—</span>') + '</td>' +
|
||||
cell('composite', compositeShow, v => v + (s._confidence != null && s._confidence < 1 ? '<span class="text-slate-500 text-[10px]">×' + s._confidence.toFixed(2) + '</span>' : ''), 'font-bold text-amber-300') +
|
||||
cell('universality_score', universalityShow, v => v + '<span class="text-slate-500">/' + (st.universality || {}).months + '月</span>') +
|
||||
cell('universality_score', universalityShow, v => v + '<span class="text-slate-500">/' + (isOverall ? (st.universality || {}).months : (rwUniv ? rwUniv.months + '/' + (rwUniv.regime_total_months || '?') : '?')) + '月</span>') +
|
||||
cell('full_return_pct', retShow, v => v + '%', 'font-bold text-amber-300') +
|
||||
cell('capital_final', capShow, v => '¥' + (v/10000).toFixed(0) + '万') +
|
||||
cell('cagr_pct', cagrShow, v => v + '%', hl(cagrShow, best.cagr_pct)) +
|
||||
|
||||
Reference in New Issue
Block a user