Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 83 additions & 2 deletions scripts/build_monthly_optimization_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,85 @@
RISK_ORDER = {"low": 0, "medium": 1, "high": 2}
SCHEMA_VERSION = "2026-04-02"
REPO_ORDER = ["CryptoLeaderRotation", "CryptoStrategies", "BinancePlatform"]
MANUAL_REVIEW_PREFIXES = (
"check ",
"review ",
"audit ",
"reconcile ",
"assess ",
"confirm ",
"determine ",
"verify ",
)


def _combined_action_text(action: dict[str, Any]) -> str:
return f"{action.get('title', '')} {action.get('summary', '')}".lower()


def _resolve_owner_repo(source_review: dict[str, Any], action: dict[str, Any]) -> str:
text = _combined_action_text(action)

if any(
marker in text
for marker in (
"monthly report",
"cash-flow",
"cash flow",
"withdrawal",
"deposit",
"realized pnl",
"unrealized pnl",
"open positions",
"no-trade",
"gating",
"free usdt",
"dca",
"rotation",
)
):
return "BinancePlatform"

if any(
marker in text
for marker in (
"shadow build",
"challenger",
"tie-break",
"tie break",
"equal scores",
"boundary",
"near-cutoff",
"near-cutoff",
"selection threshold",
)
):
return "CryptoLeaderRotation"

return action["owner_repo"]


def _resolve_auto_pr_safe(source_review: dict[str, Any], action: dict[str, Any]) -> bool:
title = str(action.get("title", "")).strip().lower()
summary = str(action.get("summary", "")).strip().lower()
if not bool(action.get("auto_pr_safe")):
return False

if title.startswith(MANUAL_REVIEW_PREFIXES):
return False
if any(
phrase in summary
for phrase in (
"assess whether",
"confirm whether",
"determine whether",
"verify minimum order size",
"review logs",
"pull binance transaction history",
)
):
return False
return True


def highest_risk(actions: list[dict[str, Any]]) -> str:
Expand All @@ -31,16 +110,18 @@ def sort_actions(actions: list[dict[str, Any]]) -> list[dict[str, Any]]:


def normalize_action(source_review: dict[str, Any], action: dict[str, Any]) -> dict[str, Any]:
owner_repo = _resolve_owner_repo(source_review, action)
auto_pr_safe = _resolve_auto_pr_safe(source_review, action)
return {
"source_repo": source_review["source_repo"],
"source_issue_number": source_review["source_issue"]["number"],
"source_issue_title": source_review["source_issue"]["title"],
"source_issue_url": source_review["source_issue"]["url"],
"source_review_kind": source_review["review_kind"],
"owner_repo": action["owner_repo"],
"owner_repo": owner_repo,
"title": action["title"],
"risk_level": action["risk_level"],
"auto_pr_safe": bool(action.get("auto_pr_safe")),
"auto_pr_safe": auto_pr_safe,
"experiment_only": bool(action.get("experiment_only")),
"summary": action["summary"],
}
Expand Down
10 changes: 7 additions & 3 deletions scripts/prepare_auto_optimization_pr.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,22 @@ def _is_completed_low_risk_task(action: dict[str, Any], repo_root: Path) -> bool
repo_name = repo_root.name

if repo_name == "CryptoLeaderRotation":
if "shadow/challenger build generation" in title:
if "shadow/challenger build generation" in title or "shadow build" in title:
workflow = _read_text(repo_root / ".github" / "workflows" / "monthly_publish.yml")
return "run_monthly_shadow_build.py" in workflow
if "deterministic tie-break behavior" in title:
if "tie-break" in title or "tie break" in title:
readme = _read_text(repo_root / "README.md")
runbook = _read_text(repo_root / "docs" / "operator_runbook.md")
return (
"Monthly ranking tie-break rule for `core_major` live exports:" in readme
and "deterministic tie-break" in runbook
)

if repo_name == "BinancePlatform" and "zero-trade diagnostics" in title:
if repo_name == "BinancePlatform" and (
"zero-trade diagnostics" in title
or "diagnostic reporting for no-trade months" in title
or "no-trade months" in title
):
monthly_report = _read_text(repo_root / "scripts" / "run_monthly_report_bundle.py")
return (
"No explicit gating or no-trade reasons were recorded this month." in monthly_report
Expand Down
48 changes: 48 additions & 0 deletions tests/test_build_monthly_optimization_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,54 @@ def test_build_plan_groups_actions_by_owner_repo(self) -> None:
self.assertEqual(len(plan["safe_auto_pr_candidates"]), 2)
self.assertEqual(len(plan["experiment_candidates"]), 1)

def test_build_plan_reassigns_reporting_tasks_and_downgrades_manual_checks(self) -> None:
upstream_review = {
"source_repo": "QuantStrategyLab/CryptoLeaderRotation",
"review_kind": "upstream_selector",
"source_issue": {"number": 11, "title": "Monthly Report Review: 2026-04-01", "url": "https://github.com/a/b/issues/11"},
"risk_level": "low",
"production_recommendation": "keep_production_as_is",
"summary": "Upstream is stable.",
"recommended_actions": [],
}
downstream_review = {
"source_repo": "QuantStrategyLab/BinancePlatform",
"review_kind": "execution_runtime",
"source_issue": {"number": 9, "title": "Monthly Execution Review: 2026-03", "url": "https://github.com/a/b/issues/9"},
"risk_level": "medium",
"production_recommendation": "needs_attention",
"summary": "Execution needs follow-up.",
"recommended_actions": [
{
"owner_repo": "CryptoStrategies",
"title": "Add monthly report cash-flow attribution",
"risk_level": "low",
"auto_pr_safe": True,
"experiment_only": False,
"summary": "Extend the monthly report to show deposits, withdrawals, realized PnL, and unrealized PnL separately.",
},
{
"owner_repo": "BinancePlatform",
"title": "Check DCA and rotation eligibility gates against current free USDT",
"risk_level": "low",
"auto_pr_safe": True,
"experiment_only": False,
"summary": "Verify minimum order size, reserve floor, and available balance thresholds.",
},
],
}

plan = build_plan(upstream_review, downstream_review)

bp_actions = plan["repo_action_summary"]["BinancePlatform"]["actions"]
self.assertEqual([action["title"] for action in bp_actions], [
"Check DCA and rotation eligibility gates against current free USDT",
"Add monthly report cash-flow attribution",
])
self.assertEqual(bp_actions[0]["auto_pr_safe"], False)
self.assertEqual(bp_actions[1]["auto_pr_safe"], True)
self.assertEqual(len(plan["safe_auto_pr_candidates"]), 1)

def test_render_summary_markdown_mentions_source_reviews_and_repos(self) -> None:
plan = {
"highest_review_risk": "medium",
Expand Down
6 changes: 3 additions & 3 deletions tests/test_prepare_auto_optimization_pr.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ def setUp(self) -> None:
- [ ] `low` Restore monthly shadow/challenger build generation before review [auto-pr-safe]
- Summary: Ensure `official_baseline` and `challenger_topk_60` are produced each month.
- Source: [QuantStrategyLab/CryptoLeaderRotation #11](https://github.com/QuantStrategyLab/CryptoLeaderRotation/issues/11)
- [ ] `low` Document deterministic tie-break behavior for equal-score boundary cases [auto-pr-safe]
- Summary: Add explicit documentation or report wording describing how equal scores are ordered and selected.
- [ ] `low` Document and verify tie-breaking for equal scores [auto-pr-safe]
- Summary: Confirm the secondary sort used for equal scores is deterministic, stable, and documented.
- Source: [QuantStrategyLab/CryptoLeaderRotation #11](https://github.com/QuantStrategyLab/CryptoLeaderRotation/issues/11)
- [ ] `low` Add a boundary tracker [auto-pr-safe, experiment-only]
- Summary: Track near-cutoff symbols monthly.
Expand All @@ -49,7 +49,7 @@ def test_build_payload_skips_completed_clr_tasks_and_excludes_experiments(self)
[action["title"] for action in payload["skipped_actions"]],
[
"Restore monthly shadow/challenger build generation before review",
"Document deterministic tie-break behavior for equal-score boundary cases",
"Document and verify tie-breaking for equal scores",
],
)

Expand Down