自动刷新改用 tail update 与 scrollToPosition 恢复视窗,避免 setData 后跳到最右;拆分 chart_tv 模块并扩展 analyze/recent API。同步威科夫分析、pipeline 增量构建及相关策略与配置。 Co-authored-by: Cursor <cursoragent@cursor.com>
114 lines
3.7 KiB
Python
114 lines
3.7 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
LPS V2 单独 Phase2(不改 Spring、不合并组合)
|
|
|
|
同一 WFO / Regime / 成本模型。
|
|
目标: net PF > 1.2;频率约 5-15/year。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import logging
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[3]
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
from user_data.Chan.scripts.wyckoff_phase2_compare import ( # noqa: E402
|
|
BRANCHES,
|
|
WFO,
|
|
install_offline_markets,
|
|
run_bt,
|
|
)
|
|
|
|
OUT = ROOT / "user_data/Chan/scripts/wyckoff_lps_v2_phase2_result.json"
|
|
COMPARE = ROOT / "user_data/Chan/scripts/wyckoff_phase2_compare_result.json"
|
|
|
|
|
|
def main() -> None:
|
|
logging.getLogger("freqtrade").setLevel(logging.ERROR)
|
|
install_offline_markets()
|
|
br = next(b for b in BRANCHES if b["name"] == "LPS_V2")
|
|
print(f"===== {br['name']} ({br['strategy']}) — LPS-only Phase2 =====", flush=True)
|
|
block = {"version": "LPS_V2", "wfo": {}, "regimes": {}, "cost_stress": {}, "target": br["target"]}
|
|
|
|
print("--- WFO ---", flush=True)
|
|
for wname, tr in WFO:
|
|
r = run_bt(br["strategy"], br["config"], tr)
|
|
block["wfo"][wname] = {"timerange": tr, **r}
|
|
print(
|
|
f" {wname:<8} profit={r['profit_pct']:>7.2f}% n={r['trades']:<3} "
|
|
f"dd={r['dd_pct']:.1f}% pf={r['pf']:.2f} wr={r['winrate']:.1f}%",
|
|
flush=True,
|
|
)
|
|
|
|
print("--- Regime ---", flush=True)
|
|
for mode in ["trend", "bull", "bear", "range", "all"]:
|
|
r = run_bt(br["strategy"], br["config"], "20230101-", regime=mode)
|
|
block["regimes"][mode] = r
|
|
print(
|
|
f" {mode:<6} profit={r['profit_pct']:>7.2f}% n={r['trades']:<3} "
|
|
f"pf={r['pf']:.2f} (loaded={r['regime_loaded']})",
|
|
flush=True,
|
|
)
|
|
|
|
print("--- Cost (net PF) ---", flush=True)
|
|
for label, fee, extra in [
|
|
("fee_5bps", 0.0005, 0.0),
|
|
("fee_5bps+slip_5bps", 0.0005, 0.0005),
|
|
("fee_10bps+slip_10bps", 0.0010, 0.0010),
|
|
]:
|
|
r = run_bt(br["strategy"], br["config"], "20230101-", fee=fee, extra_cost=extra)
|
|
block["cost_stress"][label] = r
|
|
flag = "OK" if r["pf"] >= br["target"]["pf"] else ("WEAK" if r["pf"] >= 1.0 else "FAIL")
|
|
print(
|
|
f" {label:<22} profit={r['profit_pct']:>7.2f}% n={r['trades']:<3} "
|
|
f"pf={r['pf']:.2f} [{flag}]",
|
|
flush=True,
|
|
)
|
|
|
|
full = block["wfo"]["full"]
|
|
mid = block["cost_stress"]["fee_5bps+slip_5bps"]
|
|
tpy = full["trades"] / 3.6
|
|
trend_pf = block["regimes"]["trend"]["pf"]
|
|
range_pf = block["regimes"]["range"]["pf"]
|
|
block["verdict"] = {
|
|
"full_pf": full["pf"],
|
|
"full_dd": full["dd_pct"],
|
|
"trades_per_year": tpy,
|
|
"net_mid_pf": mid["pf"],
|
|
"target_pf_ok": mid["pf"] >= br["target"]["pf"],
|
|
"target_dd_ok": full["dd_pct"] <= br["target"]["dd"],
|
|
"freq_ok": 5.0 <= tpy <= 15.0,
|
|
"regime_logic_ok": trend_pf >= range_pf, # 趋势应不差于横盘
|
|
"status": "PASS" if (mid["pf"] >= br["target"]["pf"] and full["dd_pct"] <= br["target"]["dd"]) else "FAIL",
|
|
"hypothesis": "4h native SOS → 1h LPS",
|
|
}
|
|
print("\n===== Verdict =====")
|
|
print(json.dumps(block["verdict"], ensure_ascii=False, indent=2))
|
|
|
|
OUT.write_text(json.dumps(block, indent=2, ensure_ascii=False))
|
|
# 合并进 compare 结果(保留 Spring,覆盖 LPS)
|
|
if COMPARE.exists():
|
|
prev = json.loads(COMPARE.read_text())
|
|
else:
|
|
prev = {"branches": {}}
|
|
prev.setdefault("branches", {})["LPS_V2"] = block
|
|
# 清理旧 LPS_V1 key 的活跃地位,保留作历史可手动看
|
|
spring = prev["branches"].get("Spring_V1", {}).get("verdict", {})
|
|
prev["system_status"] = {
|
|
"spring": "BASELINE FROZEN / PASS + Limited Evidence",
|
|
"lps": block["verdict"]["status"],
|
|
"spring_tpy": spring.get("trades_per_year"),
|
|
"lps_tpy": tpy,
|
|
"next": "若 LPS PASS → 组合层;否则 Spring-only",
|
|
}
|
|
COMPARE.write_text(json.dumps(prev, indent=2, ensure_ascii=False))
|
|
print(f"\nSaved {OUT}")
|
|
print("system_status:", json.dumps(prev["system_status"], ensure_ascii=False))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|