refactor: simplify opportunity actions to entry/watch/avoid with confidence
- Remove dead scoring code (_score_candidate, _action_for, etc.) and align action decisions directly with score_opportunity_signal metrics. - Reduce action surface from trigger/setup/chase/skip to entry/watch/avoid. - Add confidence field (0..100) mapped from edge_score. - Update evaluate/optimize ground-truth mapping and tests. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -156,11 +156,11 @@ def _path_stats(entry: float, future_rows: list[list[Any]], take_profit: float,
|
||||
|
||||
|
||||
def _is_correct(action: str, trigger_path: dict[str, Any], setup_path: dict[str, Any]) -> bool:
|
||||
if action == "trigger":
|
||||
if action == "entry":
|
||||
return str(trigger_path["event"]) == "target"
|
||||
if action == "setup":
|
||||
if action == "watch":
|
||||
return str(setup_path["event"]) == "target"
|
||||
if action in {"skip", "chase"}:
|
||||
if action == "avoid":
|
||||
return str(setup_path["event"]) != "target"
|
||||
return False
|
||||
|
||||
@@ -274,7 +274,7 @@ def evaluate_opportunity_dataset(
|
||||
metrics["opportunity_score"] = round(opportunity_score, 4)
|
||||
metrics["position_weight"] = 0.0
|
||||
metrics["research_score"] = 0.0
|
||||
action, reasons = _action_for_opportunity(score, metrics, thresholds)
|
||||
action, reasons, _confidence = _action_for_opportunity(score, metrics, thresholds)
|
||||
candidates.append(
|
||||
{
|
||||
"symbol": symbol,
|
||||
@@ -304,7 +304,7 @@ def evaluate_opportunity_dataset(
|
||||
"forward_return": _round_float(trigger_path["final_return"]),
|
||||
"max_upside": _round_float(trigger_path["max_upside"]),
|
||||
"max_drawdown": _round_float(trigger_path["max_drawdown"]),
|
||||
"trade_return": _round_float(trigger_path["exit_return"]) if candidate["action"] == "trigger" else 0.0,
|
||||
"trade_return": _round_float(trigger_path["exit_return"]) if candidate["action"] == "entry" else 0.0,
|
||||
"trigger_event": trigger_path["event"],
|
||||
"setup_event": setup_path["event"],
|
||||
"metrics": candidate["metrics"],
|
||||
@@ -321,16 +321,16 @@ def evaluate_opportunity_dataset(
|
||||
bucket["count"] += 1
|
||||
bucket["correct"] += 1 if judgment["correct"] else 0
|
||||
bucket["forward_returns"].append(judgment["forward_return"])
|
||||
if action == "trigger":
|
||||
if action == "entry":
|
||||
bucket["trade_returns"].append(judgment["trade_return"])
|
||||
if action == "trigger":
|
||||
if action == "entry":
|
||||
trigger_returns.append(judgment["trade_return"])
|
||||
|
||||
by_action_result = {action: _finalize_bucket(bucket) for action, bucket in sorted(by_action.items())}
|
||||
incorrect_examples = [item for item in judgments if not item["correct"]][:max_examples]
|
||||
examples = judgments[:max_examples]
|
||||
trigger_count = by_action_result.get("trigger", {}).get("count", 0)
|
||||
trigger_correct = by_action_result.get("trigger", {}).get("correct", 0)
|
||||
trigger_count = by_action_result.get("entry", {}).get("count", 0)
|
||||
trigger_correct = by_action_result.get("entry", {}).get("correct", 0)
|
||||
return {
|
||||
"summary": {
|
||||
**_finalize_bucket(overall),
|
||||
@@ -377,7 +377,7 @@ def _objective(result: dict[str, Any]) -> float:
|
||||
trigger_coverage = min(trigger_rate / 0.08, 1.0)
|
||||
return round(
|
||||
0.45 * _as_float(summary.get("accuracy"))
|
||||
+ 0.20 * _as_float(by_action.get("setup", {}).get("accuracy"))
|
||||
+ 0.20 * _as_float(by_action.get("watch", {}).get("accuracy"))
|
||||
+ 0.25 * _as_float(trade.get("win_rate"))
|
||||
+ 6.0 * bounded_trade_return
|
||||
+ 0.05 * trigger_coverage,
|
||||
|
||||
Reference in New Issue
Block a user