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
70 changes: 70 additions & 0 deletions .github/workflows/thesis-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: Thesis Check

on:
schedule:
- cron: "0 2 1 * *"
workflow_dispatch:

permissions:
contents: read
issues: write

jobs:
thesis:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.11"

- name: Validate thesis ledger
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_REPOSITORY: ${{ github.repository }}
run: |
set -euo pipefail
python - <<'PY'
import json, os, pathlib, subprocess, sys
path = pathlib.Path("docs/thesis-ledger.json")
if not path.exists():
print("::error::docs/thesis-ledger.json missing")
sys.exit(1)
data = json.loads(path.read_text())
strategies = data.get("strategies") or {}
if not strategies:
print("::error::no strategies in thesis ledger")
sys.exit(1)
failures = []
warnings = []
for name, row in strategies.items():
fals = row.get("falsification_conditions") or []
if not row.get("thesis"):
failures.append(f"{name}: missing thesis")
if len(fals) < 2:
failures.append(f"{name}: need >=2 falsification_conditions")
hit = row.get("hit_rate")
if hit is not None and float(hit) < 0.5:
warnings.append(f"{name}: hit_rate {hit} < 0.5")
if str(row.get("verdict", "")).lower() in {"falsified", "failed", "reject"}:
failures.append(f"{name}: verdict={row.get('verdict')}")
print(f"[thesis-check] strategies={len(strategies)} failures={len(failures)} warnings={len(warnings)}")
for item in failures:
print(f"::error::{item}")
for item in warnings:
print(f"::warning::{item}")
repo = os.environ.get("GITHUB_REPOSITORY", "")
token = os.environ.get("GH_TOKEN", "")
if failures and repo and token:
title = f"[thesis-check] falsification/validation failures ({len(failures)})"
body = "\n".join(f"- {x}" for x in failures)
# create issue (best-effort)
subprocess.run([
"gh", "issue", "create", "--title", title, "--body", body + "\n\ncc @Pigbibi"
], check=False)
sys.exit(1 if failures else 0)
PY
28 changes: 28 additions & 0 deletions docs/thesis-ledger.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"strategies": {
"crypto_live_pool_rotation": {
"thesis": "加密实时池轮动可在趋势市捕获相对强度并借助 ATR 止损控制尾部",
"falsification_conditions": [
"滚动12个月相对 BTC 超额 < 0",
"ATR 止损后仍出现 >30% 单笔回撤"
],
"created": "2025-09-01",
"last_verified": "2026-07-08",
"verdict": "active",
"hit_rate": null,
"status_note": "runtime_enabled"
},
"crypto_btc_dca": {
"thesis": "BTC 智能定投在波动分层下优于固定金额 DCA",
"falsification_conditions": [
"相对等额定投 12个月超额 < 0",
"高波动加仓规则连续失效 3 个月"
],
"created": "2025-09-01",
"last_verified": "2026-07-08",
"verdict": "active",
"hit_rate": null,
"status_note": "shadow_candidate"
}
}
}
4 changes: 2 additions & 2 deletions src/crypto_strategies/entrypoints/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ def evaluate_crypto_trend_rotation(ctx: StrategyContext) -> StrategyDecision:
risk_flags=risk_flags,
diagnostics=diagnostics,
)
return apply_risk_gate(decision, max_single_weight=0.50)
return apply_risk_gate(decision, max_single_weight=0.30)


crypto_trend_rotation_entrypoint = CallableStrategyEntrypoint(
Expand Down Expand Up @@ -615,7 +615,7 @@ def evaluate_crypto_equity_combo(ctx: StrategyContext) -> StrategyDecision:
risk_flags=risk_flags,
diagnostics=diagnostics,
)
return apply_risk_gate(decision)
return apply_risk_gate(decision, max_single_weight=0.30)


crypto_equity_combo_entrypoint = CallableStrategyEntrypoint(
Expand Down
Loading