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
91 changes: 81 additions & 10 deletions .github/workflows/ai_review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,106 @@ name: AI Monthly Review
"on":
issues:
types: [opened]
workflow_dispatch:
inputs:
issue_number:
description: "Issue number to review"
required: true

jobs:
ai-review:
if: contains(github.event.issue.labels.*.name, 'monthly-review')
if: contains(github.event.issue.labels.*.name, 'monthly-review') || inputs.issue_number != ''
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
issues: write

steps:
- name: Checkout
uses: actions/checkout@v5

- name: Load review issue context
id: issue_context
run: |
python3 - <<'PY'
import json
import os
import urllib.request

repo = os.environ["GITHUB_REPOSITORY"]
issue_number = os.environ["ISSUE_NUMBER"]
token = os.environ["GITHUB_TOKEN"]
api_url = f"https://api.github.com/repos/{repo}/issues/{issue_number}"
request = urllib.request.Request(
api_url,
headers={
"Accept": "application/vnd.github+json",
"Authorization": f"Bearer {token}",
"X-GitHub-Api-Version": "2022-11-28",
"User-Agent": "binanceplatform-ai-review",
},
)
with urllib.request.urlopen(request) as response:
issue = json.load(response)

with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output:
print("issue_title<<EOF", file=output)
print(issue["title"], file=output)
print("EOF", file=output)
print("issue_body<<EOF", file=output)
print(issue["body"], file=output)
print("EOF", file=output)
PY
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ inputs.issue_number || github.event.issue.number }}

- name: Run Claude Code Review
id: claude_review
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
model: claude-sonnet-4-6-20250514
github_token: ${{ secrets.GITHUB_TOKEN }}
use_bedrock: false
use_vertex: false
allowed_tools: |
Read
Glob
Grep
Bash(git log:*)
Bash(git diff:*)
custom_instructions: |
prompt: |
Do not use Bash or ask for additional approval.
Return only the final bilingual review in markdown.
The workflow will publish your final review as the issue comment.

You are reviewing the monthly execution report for BinancePlatform,
an automated crypto quantitative trading system for Binance Spot.
It runs hourly, executing a BTC DCA core strategy plus altcoin trend
rotation using symbols from an upstream CryptoLeaderRotation pool.

The Issue body contains the full monthly execution review data.
This is a downstream execution review, not a pure upstream pool review.
Upstream pool changes are only one input section from CryptoLeaderRotation.
Equity changes may include manual deposits, withdrawals, or other external
balance flows, so do not treat equity delta as pure strategy PnL unless
the report contains explicit cash-flow evidence.
Trade and earn sections in the report are based on recorded hourly
execution intents/actions, not a separate exchange fill reconciliation.
Do not assume zero trades are automatically anomalous: when deployable
capital is small, manual withdrawals reduced balance, or threshold/cooldown
gates are active, a no-trade month may be legitimate.

The issue title and body are provided below.
Analyze it and provide your review in BOTH English and Chinese.

## Issue Title
${{ steps.issue_context.outputs.issue_title }}

## Issue Body
${{ steps.issue_context.outputs.issue_body }}

## Review Scope

1. **Trade Execution Quality**
- Are buy/sell frequencies reasonable for the month?
- Any abnormally dense trading periods suggesting misbehavior?
- Is the BTC DCA pattern consistent with the expected valuation-based scaling?
- Treat a zero-trade month as context-dependent, not automatically a bug.

2. **Circuit Breaker Events**
- How many times was the circuit breaker triggered?
Expand All @@ -59,6 +118,7 @@ jobs:
- Overall portfolio performance for the month.
- BTC core vs trend sleeve performance separately.
- Any concerning drawdowns?
- Explicitly separate raw equity change from strategy-attributable PnL when cash-flow context is missing.

5. **Upstream Pool Change Impact**
- Did pool symbol rotations cause significant position churn?
Expand Down Expand Up @@ -115,3 +175,14 @@ jobs:
...
### 操作员待办事项
...

- name: Post AI review issue comment
if: steps.claude_review.outcome == 'success'
run: |
python3 scripts/post_monthly_ai_review_comment.py \
--repo "${GITHUB_REPOSITORY}" \
--issue-number "${{ inputs.issue_number || github.event.issue.number }}" \
--execution-file "${{ steps.claude_review.outputs.execution_file }}" \
--run-url "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
20 changes: 18 additions & 2 deletions .github/workflows/monthly_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ jobs:
report:
runs-on: ubuntu-latest
permissions:
actions: write
contents: read
issues: write

Expand Down Expand Up @@ -56,15 +57,30 @@ jobs:
run: |
python scripts/run_monthly_report_bundle.py \
--month "${{ steps.month.outputs.month }}" \
--hourly-dir "hourly/${{ steps.month.outputs.month }}" \
--output-dir data/output

- name: Create monthly review issue
if: steps.fetch.outputs.has_data == 'true'
id: issue
run: |
set -euo pipefail
MONTH="${{ steps.month.outputs.month }}"
gh issue create \
gh label create monthly-review --description "Automated monthly execution AI review" --color "0E8A16" 2>/dev/null || true
ISSUE_URL=$(gh issue create \
--title "Monthly Execution Review: ${MONTH}" \
--label "monthly-review" \
--body-file data/output/ai_review_input.md
--body-file data/output/ai_review_input.md)
echo "issue_url=${ISSUE_URL}" >> "$GITHUB_OUTPUT"
echo "issue_number=${ISSUE_URL##*/}" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Trigger AI monthly review
if: steps.fetch.outputs.has_data == 'true'
run: |
gh workflow run ai_review.yml \
--ref "${GITHUB_REF_NAME}" \
-f issue_number="${{ steps.issue.outputs.issue_number }}"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
138 changes: 138 additions & 0 deletions scripts/post_monthly_ai_review_comment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
from __future__ import annotations

import argparse
import json
import os
import sys
import urllib.error
import urllib.request
from pathlib import Path
from typing import Any


COMMENT_MARKER = "<!-- monthly-ai-review -->"
DEFAULT_API_URL = "https://api.github.com"


def extract_latest_assistant_text(execution_log: list[dict[str, Any]]) -> str:
for turn in reversed(execution_log):
if turn.get("type") != "assistant":
continue

content_items = turn.get("message", {}).get("content", [])
text_parts = [
item.get("text", "").strip()
for item in content_items
if item.get("type") == "text" and item.get("text", "").strip()
]
if text_parts:
return "\n\n".join(text_parts).strip()

raise ValueError("No assistant review text found in Claude execution log")


def build_comment_body(review_text: str, run_url: str | None = None) -> str:
body = f"{COMMENT_MARKER}\n## Claude Monthly Review\n\n{review_text.strip()}"
if run_url:
body += f"\n\n---\n_Generated by AI Monthly Review workflow: {run_url}_"
return body


def github_request(
method: str,
url: str,
token: str,
payload: dict[str, Any] | None = None,
) -> Any:
data = None
headers = {
"Accept": "application/vnd.github+json",
"Authorization": f"Bearer {token}",
"X-GitHub-Api-Version": "2022-11-28",
"User-Agent": "binanceplatform-monthly-ai-review",
}
if payload is not None:
data = json.dumps(payload).encode("utf-8")
headers["Content-Type"] = "application/json"

request = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(request) as response:
charset = response.headers.get_content_charset("utf-8")
raw = response.read().decode(charset)
return json.loads(raw) if raw else None


def upsert_issue_comment(
*,
api_url: str,
repo: str,
issue_number: int,
token: str,
body: str,
) -> None:
comments_url = f"{api_url}/repos/{repo}/issues/{issue_number}/comments"
comments = github_request("GET", comments_url, token)
existing = next(
(
comment
for comment in comments
if COMMENT_MARKER in comment.get("body", "")
),
None,
)

if existing:
github_request(
"PATCH",
f"{api_url}/repos/{repo}/issues/comments/{existing['id']}",
token,
{"body": body},
)
print(f"Updated issue comment {existing['id']}")
return

github_request("POST", comments_url, token, {"body": body})
print(f"Created issue comment for issue #{issue_number}")


def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Extract Claude review text from execution output and upsert an issue comment.",
)
parser.add_argument("--repo", required=True, help="owner/repo")
parser.add_argument("--issue-number", required=True, type=int)
parser.add_argument("--execution-file", required=True, type=Path)
parser.add_argument("--api-url", default=DEFAULT_API_URL)
parser.add_argument("--run-url", default="")
return parser.parse_args()


def main() -> int:
args = parse_args()
token = os.environ.get("GITHUB_TOKEN")
if not token:
print("GITHUB_TOKEN is required", file=sys.stderr)
return 1

execution_log = json.loads(args.execution_file.read_text(encoding="utf-8"))
review_text = extract_latest_assistant_text(execution_log)
body = build_comment_body(review_text, args.run_url or None)

try:
upsert_issue_comment(
api_url=args.api_url.rstrip("/"),
repo=args.repo,
issue_number=args.issue_number,
token=token,
body=body,
)
except urllib.error.HTTPError as exc:
detail = exc.read().decode("utf-8", errors="replace")
print(f"GitHub API request failed: {exc.code} {detail}", file=sys.stderr)
return 1

return 0


if __name__ == "__main__":
raise SystemExit(main())
18 changes: 16 additions & 2 deletions scripts/run_monthly_report_bundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,16 @@ def format_review_markdown(bundle: dict[str, Any]) -> str:
lines.append(f"_Generated: {generated}_")
lines.append("")

# Scope / interpretation notes
lines.append("## Report Scope")
lines.append("")
lines.append("- This is BinancePlatform's downstream monthly execution review, not a pure upstream pool publication.")
lines.append("- It summarizes runtime health, recorded trade intents, earn buffer operations, circuit breaker activity, degraded mode, and upstream pool changes.")
lines.append("- Upstream pool changes are included as execution context from CryptoLeaderRotation, but they are only one input section of this report.")
lines.append("- Equity deltas in this report are raw month-start vs month-end snapshots and may include manual deposits, withdrawals, or other external balance flows.")
lines.append("- Trade and earn sections reflect execution intents/actions recorded in hourly reports, not a separate exchange fill reconciliation ledger.")
lines.append("")

# Run statistics
lines.append("## Run Statistics")
lines.append("")
Expand All @@ -255,10 +265,14 @@ def format_review_markdown(bundle: dict[str, Any]) -> str:
lines.append(f"| PnL (USDT) | {pnl['pnl_usdt']} |")
lines.append(f"| PnL (%) | {pnl['pnl_pct']} |")
lines.append("")
lines.append("> Note: Equity deltas may include external balance flows and should not be interpreted as pure strategy PnL without separate cash-flow reconciliation.")
lines.append("")

# Trade summary
lines.append("## Trade Summary")
lines.append("")
lines.append("> Note: Trade counts below are based on recorded strategy intents in hourly execution reports, not exchange fill reconciliation.")
lines.append("")
lines.append("### BTC Core (DCA)")
lines.append("")
lines.append("| Metric | Value |")
Expand Down Expand Up @@ -348,11 +362,11 @@ def format_review_markdown(bundle: dict[str, Any]) -> str:
# Review questions
lines.append("## Review Questions")
lines.append("")
lines.append("1. Is the PnL trend consistent with market conditions this month?")
lines.append("1. Does the equity trend look explainable once possible external deposits/withdrawals are considered?")
lines.append("2. Were any circuit breaker events justified, or do thresholds need adjusting?")
lines.append("3. Did upstream pool changes have a noticeable impact on performance?")
lines.append("4. Are the failed runs isolated incidents or part of a pattern?")
lines.append("5. Should BTC DCA cadence or sizing be adjusted based on this month's data?")
lines.append("5. Do the recorded trade intents suggest BTC DCA cadence or trend sizing should be adjusted?")
lines.append("6. Were earn buffer subscribe/redeem operations executed at appropriate times?")
lines.append("")

Expand Down
4 changes: 4 additions & 0 deletions tests/test_monthly_report_bundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,7 @@ def test_format_review_markdown(self):

self.assertIn("Monthly Execution Review", md)
self.assertIn("2026-03", md)
self.assertIn("downstream monthly execution review", md)
self.assertIn("not a pure upstream pool publication", md)
self.assertIn("external balance flows", md)
self.assertIn("recorded strategy intents", md)
Loading
Loading