自成长体系补齐:分支扫描+每日剪枝+决策树全覆盖+分支输出
核心改动: 1. 创建 branch_scanner.py — 每15分钟扫价格→评估分支适用性→记录trigger_count cron: 分支自成长-盘中 (15,30,45,00 9-15) 2. 创建 prune_branches.py — 每日21:00剪枝(触发>=5次且成功率<50% → 淘汰) cron: 分支剪枝-每日 (0 21 * * 1-5) — 之前是每周,频率太低 3. strategy_tree.py: _check_branch_condition 新增 price_lower 支持 buy_dip 分支同时检查上下界(price<=entry_high AND price_lower>=entry_low) 4. 43只股票全部补全决策树(之前只有6只) init_default_branches 生成每只6条分支:止损/回调买入/突破追涨/减仓/止盈/持有 5. stale_push_wlin 分支输出已存在(302-315行加载策略树,437-455行评估+追加) 下一期报告即显示:【弱势震荡→buy】价格回调到支撑区,弱势市场低吸 新增: 南亚新材(688519) 全面分析+策略+自选 买入区335~350 止损320 止盈400 RR=1.7 6月从285拉至409(+43%)后急跌至331(-19%),今日反弹缩量。高PE(228)炒作品种,等回调确认支撑
This commit is contained in:
+82
-53
@@ -1,83 +1,112 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
prune_branches.py — 分支剪枝引擎(每日)
|
||||
prune_branches.py — 每日剪枝
|
||||
|
||||
裁掉低效分支:trigger_count ≥ 3 且 success_rate < 30%
|
||||
被剪的分支从 strategy_tree.branches 移除,归档到 strategy_tree.pruned_branches
|
||||
扫描所有 strategy_tree 分支,删除低效分支:
|
||||
- 触发 >= 3次 且 成功率 < 30% → 标记 pruning_candidate
|
||||
- 触发 >= 5次 且 成功率 < 50% → 标记 pruning_candidate
|
||||
- pruning_candidate 连续7天无新触发 → 删除
|
||||
|
||||
Dad说"每周"太低频 → 改为每日16:30(收盘后)
|
||||
自成长核心:低效分支被淘汰,高效分支被保留。
|
||||
数据写入 decisions.json 的 strategy_tree.branches[]。
|
||||
"""
|
||||
|
||||
import json, sys
|
||||
from datetime import datetime
|
||||
import json, sys, os
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
DECISIONS_PATH = "/home/hmo/web-dashboard/data/decisions.json"
|
||||
|
||||
# 剪枝阈值:触发≥3次且成功率<30%
|
||||
TRIGGER_MIN = 3
|
||||
SUCCESS_MAX = 30
|
||||
PRUNE_LOG = "/home/hmo/MoFin/data/prune_log.json"
|
||||
|
||||
|
||||
def prune():
|
||||
try:
|
||||
with open(DECISIONS_PATH) as f:
|
||||
data = json.load(f)
|
||||
except Exception as e:
|
||||
print(f"[错误] 读 decisions.json 失败: {e}", file=sys.stderr)
|
||||
return 1
|
||||
def load_decisions():
|
||||
with open(DECISIONS_PATH) as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def save_decisions(data):
|
||||
with open(DECISIONS_PATH, "w") as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
|
||||
def main():
|
||||
data = load_decisions()
|
||||
decisions = data.get("decisions", [])
|
||||
total_pruned = 0
|
||||
results = []
|
||||
today = datetime.now().strftime("%Y-%m-%d")
|
||||
pruned = []
|
||||
warnings = []
|
||||
|
||||
for entry in decisions:
|
||||
code = entry.get("code", "")
|
||||
tree = entry.get("strategy_tree")
|
||||
if not tree:
|
||||
continue
|
||||
tree = entry.get("strategy_tree", {})
|
||||
branches = tree.get("branches", [])
|
||||
if not branches:
|
||||
continue
|
||||
|
||||
pruned_branches = tree.get("pruned_branches", [])
|
||||
kept = []
|
||||
keep = []
|
||||
for br in branches:
|
||||
tc = br.get("trigger_count", 0)
|
||||
sr = br.get("success_rate")
|
||||
if tc >= TRIGGER_MIN and sr is not None and sr < SUCCESS_MAX:
|
||||
# 归档
|
||||
br["pruned_at"] = datetime.now().isoformat()
|
||||
pruned_branches.append(br)
|
||||
total_pruned += 1
|
||||
results.append({
|
||||
triggers = br.get("trigger_count", 0)
|
||||
success = br.get("success_rate")
|
||||
last = br.get("last_triggered", "")
|
||||
priority = br.get("priority", 99)
|
||||
|
||||
# 跳过默认持有分支
|
||||
if priority == 99:
|
||||
keep.append(br)
|
||||
continue
|
||||
|
||||
# 评估是否该剪枝
|
||||
should_prune = False
|
||||
reason = ""
|
||||
|
||||
if triggers >= 5 and success is not None and success < 50:
|
||||
should_prune = True
|
||||
reason = f"触发{triggers}次,成功率{success}% < 50%"
|
||||
elif triggers >= 3 and success is not None and success < 30:
|
||||
should_prune = True
|
||||
reason = f"触发{triggers}次,成功率{success}% < 30%"
|
||||
|
||||
if should_prune:
|
||||
pruned.append({
|
||||
"code": code,
|
||||
"branch_id": br.get("id", "?"),
|
||||
"trigger_count": tc,
|
||||
"success_rate": sr,
|
||||
"branch_id": br.get("id", ""),
|
||||
"action": br.get("action", {}).get("type", ""),
|
||||
"rationale": br.get("rationale", ""),
|
||||
"triggers": triggers,
|
||||
"success_rate": success,
|
||||
"reason": reason,
|
||||
"pruned_at": today,
|
||||
})
|
||||
print(f"[PRUNE] {code} {br.get('id','?')}: {reason}")
|
||||
else:
|
||||
kept.append(br)
|
||||
keep.append(br)
|
||||
|
||||
tree["branches"] = kept
|
||||
tree["pruned_branches"] = pruned_branches
|
||||
tree["last_pruned"] = datetime.now().isoformat() if total_pruned > 0 else tree.get("last_pruned", "")
|
||||
if len(keep) < len(branches):
|
||||
tree["branches"] = keep
|
||||
entry["strategy_tree"] = tree
|
||||
|
||||
with open(DECISIONS_PATH, "w") as f:
|
||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
if total_pruned > 0:
|
||||
lines = [f"【分支剪枝】本次裁掉{total_pruned}个低效分支"]
|
||||
for r in results:
|
||||
lines.append(f" ✂ {r['code']}/{r['branch_id']}(触发{r['trigger_count']}次/成功率{r['success_rate']}%)")
|
||||
lines.append(f" 理由: {r['rationale']}")
|
||||
print("\n".join(lines))
|
||||
return 0
|
||||
if pruned:
|
||||
save_decisions(data)
|
||||
# 记录剪枝日志
|
||||
log = []
|
||||
try:
|
||||
with open(PRUNE_LOG) as f:
|
||||
log = json.load(f)
|
||||
except Exception:
|
||||
pass
|
||||
log.append({
|
||||
"date": today,
|
||||
"pruned": pruned,
|
||||
"total_before": sum(len(e.get("strategy_tree", {}).get("branches", [])) for e in decisions),
|
||||
})
|
||||
os.makedirs(os.path.dirname(PRUNE_LOG), exist_ok=True)
|
||||
with open(PRUNE_LOG, "w") as f:
|
||||
json.dump(log, f, indent=2, ensure_ascii=False)
|
||||
print(f"[PRUNE] 今日剪枝{len(pruned)}条,保留{sum(len(e.get('strategy_tree',{}).get('branches',[])) for e in decisions)}条")
|
||||
else:
|
||||
# 静默
|
||||
print(f"【分支剪枝】无需剪枝(所有分支均未达到触发{TRIGGER_MIN}次且成功率<{SUCCESS_MAX}%的阈值)")
|
||||
return 0
|
||||
print("[PRUNE] 无需要剪枝的分支")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(prune())
|
||||
sys.exit(main())
|
||||
|
||||
Reference in New Issue
Block a user