What this adds
Three files to fix the daily Orinias_Hyper1 regime classifier — generated by a scheduled Claude Code session that could not push directly (egress proxy blocks crypto APIs + git push; GitHub MCP is read-only).
Apply with:
# 1. copy the files below into place
# 2. trigger a first run
gh workflow run regime-classifier.yml
File 1 — .github/workflows/regime-classifier.yml
name: BTC Regime Classifier
on:
schedule:
- cron: '5 0 * * *'
workflow_dispatch:
permissions:
contents: write
jobs:
classify:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Fetch BTC data and classify regime
id: classify
run: |
python3 << 'PYEOF'
import json, urllib.request, os, sys
url = 'https://api.binance.com/api/v3/klines?symbol=BTCUSDT&interval=1d&limit=201'
try:
with urllib.request.urlopen(url, timeout=15) as r:
raw = json.load(r)
except Exception as e:
print(f'Binance failed: {e}', file=sys.stderr)
yurl = 'https://query1.finance.yahoo.com/v8/finance/chart/BTC-USD?interval=1d&range=400d'
with urllib.request.urlopen(yurl, timeout=15) as r:
ydata = json.load(r)
rr = ydata['chart']['result'][0]
ts = rr['timestamp']; q = rr['indicators']['quote'][0]
raw2 = list(zip(ts, q['open'], q['high'], q['low'], q['close'], q['volume']))
bars = [{'t':r[0]*1000,'o':r[1] or 0,'h':r[2] or 0,'l':r[3] or 0,'c':r[4] or 0,'v':r[5] or 0} for r in raw2 if r[4]]
else:
bars = [{'t':b[0],'o':float(b[1]),'h':float(b[2]),'l':float(b[3]),'c':float(b[4]),'v':float(b[5])} for b in raw[:-1]]
bars = bars[-200:]
closes = [b['c'] for b in bars]; highs = [b['h'] for b in bars]; price = closes[-1]
sma50 = sum(closes[-50:]) / 50
sma200 = sum(closes[-200:]) / 200 if len(closes) >= 200 else sum(closes) / len(closes)
sma50_5d = sum(closes[-55:-5]) / 50 if len(closes) >= 55 else sma50
slope = (sma50 - sma50_5d) / sma50_5d
trs = []
for i in range(1, len(bars)):
c, p = bars[i], bars[i-1]
trs.append(max(c['h']-c['l'], abs(c['h']-p['c']), abs(c['l']-p['c'])))
atr14 = sum(trs[-14:]) / 14; atr_pct = atr14 / price
high200 = max(highs[-200:]); drawdown = (high200 - price) / high200
if price > sma50 * 1.30 and price > sma200 * 1.60:
regime = 'euphoria'
elif drawdown > 0.20 and atr_pct > 0.06:
regime = 'capitulation'
elif drawdown > 0.10 and price < sma50 and price > sma200 * 0.92:
regime = 'correction'
elif price > sma50 and price > sma200 and slope > 0:
regime = 'bull_trend'
else:
regime = 'range_bound'
out = os.environ.get('GITHUB_OUTPUT', '/dev/stdout')
with open(out, 'a') as f:
f.write(f'regime={regime}\nprice={price:.2f}\nsma50={sma50:.2f}\nsma200={sma200:.2f}\n')
f.write(f'slope={slope*100:.3f}\natr14={atr14:.2f}\natr_pct={atr_pct*100:.2f}\n')
f.write(f'drawdown={drawdown*100:.1f}\ncycle_high={high200:.2f}\n')
print(f'REGIME={regime} price={price:.2f} sma50={sma50:.2f} sma200={sma200:.2f}')
print(f'drawdown={drawdown*100:.1f}% atr={atr_pct*100:.2f}% slope={slope*100:.3f}%')
PYEOF
- name: Read previous regime
id: prev
run: |
if [ -f agents/regime.json ]; then
prev=$(python3 -c "import json; d=json.load(open('agents/regime.json')); print(d.get('regime','unknown'))")
else
prev="unknown"
fi
echo "regime=$prev" >> "$GITHUB_OUTPUT"
- name: Write regime.json
run: |
python3 << 'PYEOF'
import json, os
from datetime import datetime, timezone
data = {
"regime": os.environ['REGIME'],
"previous": os.environ['PREV'],
"set_at": datetime.now(timezone.utc).isoformat(),
"method": "github-action",
"metrics": {
"price": float(os.environ['PRICE']),
"sma50": float(os.environ['SMA50']),
"sma200": float(os.environ['SMA200']),
"sma50_slope": float(os.environ['SLOPE']),
"atr14": float(os.environ['ATR14']),
"atr_pct": float(os.environ['ATR_PCT']),
"drawdown_pct": float(os.environ['DRAWDOWN']),
"cycle_high": float(os.environ['CYCLE_HIGH']),
}
}
os.makedirs('agents', exist_ok=True)
with open('agents/regime.json', 'w') as f:
json.dump(data, f, indent=2); f.write('\n')
print(json.dumps(data, indent=2))
PYEOF
env:
REGIME: ${{ steps.classify.outputs.regime }}
PREV: ${{ steps.prev.outputs.regime }}
PRICE: ${{ steps.classify.outputs.price }}
SMA50: ${{ steps.classify.outputs.sma50 }}
SMA200: ${{ steps.classify.outputs.sma200 }}
SLOPE: ${{ steps.classify.outputs.slope }}
ATR14: ${{ steps.classify.outputs.atr14 }}
ATR_PCT: ${{ steps.classify.outputs.atr_pct }}
DRAWDOWN: ${{ steps.classify.outputs.drawdown }}
CYCLE_HIGH: ${{ steps.classify.outputs.cycle_high }}
- name: Commit if changed
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add agents/regime.json
if git diff --staged --quiet; then
echo "No change — skipping commit"
else
NEW="${{ steps.classify.outputs.regime }}"
OLD="${{ steps.prev.outputs.regime }}"
[ "$NEW" != "$OLD" ] && MSG="chore(regime): CHANGE $OLD -> $NEW [auto]" || MSG="chore(regime): update metrics ($NEW) [auto]"
git commit -m "$MSG" && git push
fi
File 2 — agents/regime.json
{
"regime": "correction",
"previous": "unknown",
"set_at": "2026-07-03T00:00:00.000Z",
"method": "bootstrap",
"metrics": {
"price": 61792,
"sma50": 67000,
"sma200": 61180,
"sma50_slope": -0.8,
"atr14": 2274,
"atr_pct": 3.68,
"drawdown_pct": 36.9,
"cycle_high": 97860,
"note": "Bootstrapped from WebSearch 2026-07-03; replace with first GitHub Action run"
}
}
File 3 — agents/set_regime.js
#!/usr/bin/env node
import { readFileSync, writeFileSync, existsSync } from 'fs';
import { resolve, dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const REGIME_PATH = resolve(__dirname, 'regime.json');
const VALID_REGIMES = ['bull_trend', 'range_bound', 'correction', 'capitulation', 'euphoria'];
const SIGNAL_GATES = {
bull_trend: { A:'off', B:'on', C:'off', D:'on', 'E-long':'on', 'E-short':'off' },
range_bound: { A:'on', B:'on', C:'on', D:'on', 'E-long':'on', 'E-short':'on' },
correction: { A:'on', B:'on', C:'on', D:'on', 'E-long':'off', 'E-short':'on' },
capitulation: { A:'off', B:'off', C:'off', D:'on', 'E-long':'off', 'E-short':'off' },
euphoria: { A:'on', B:'off', C:'on', D:'off', 'E-long':'off', 'E-short':'on' },
};
function readRegime() {
if (!existsSync(REGIME_PATH)) return { regime: 'unknown', set_at: null };
return JSON.parse(readFileSync(REGIME_PATH, 'utf8'));
}
const args = process.argv.slice(2);
const regimeArg = args.find(a => a.startsWith('--regime='));
if (!regimeArg) {
const d = readRegime();
const gates = SIGNAL_GATES[d.regime] ?? {};
console.log(`\nCurrent regime : ${d.regime}`);
console.log(`Set at : ${d.set_at ?? 'never'}`);
console.log(`Method : ${d.method ?? 'unknown'}`);
if (d.metrics) {
const m = d.metrics;
console.log(`\nMetrics:`);
console.log(` Price : $${m.price?.toFixed(2) ?? 'n/a'}`);
console.log(` SMA50 : $${m.sma50?.toFixed(2) ?? 'n/a'}`);
console.log(` SMA200 : $${m.sma200?.toFixed(2) ?? 'n/a'}`);
console.log(` Drawdown : ${m.drawdown_pct?.toFixed(1) ?? 'n/a'}%`);
console.log(` Cycle high : $${m.cycle_high?.toFixed(2) ?? 'n/a'}`);
console.log(` ATR% : ${m.atr_pct?.toFixed(2) ?? 'n/a'}%`);
}
const gateStr = Object.entries(gates).map(([k,v]) => `${k}:${v}`).join(' ');
if (gateStr) console.log(`\nSignal gate : ${gateStr}`);
console.log();
process.exit(0);
}
const newRegime = regimeArg.split('=')[1];
if (!VALID_REGIMES.includes(newRegime)) {
console.error(`Error: invalid regime "${newRegime}"`);
console.error(`Valid: ${VALID_REGIMES.join(', ')}`);
process.exit(1);
}
const prev = readRegime();
const data = { regime: newRegime, previous: prev.regime, set_at: new Date().toISOString(), method: 'manual', metrics: prev.metrics ?? null };
writeFileSync(REGIME_PATH, JSON.stringify(data, null, 2) + '\n');
console.log(`Regime updated: ${prev.regime} → ${newRegime}`);
package.json — add one line to scripts
"agent:regime": "node agents/set_regime.js"
What this adds
Three files to fix the daily Orinias_Hyper1 regime classifier — generated by a scheduled Claude Code session that could not push directly (egress proxy blocks crypto APIs + git push; GitHub MCP is read-only).
Apply with:
File 1 —
.github/workflows/regime-classifier.ymlFile 2 —
agents/regime.json{ "regime": "correction", "previous": "unknown", "set_at": "2026-07-03T00:00:00.000Z", "method": "bootstrap", "metrics": { "price": 61792, "sma50": 67000, "sma200": 61180, "sma50_slope": -0.8, "atr14": 2274, "atr_pct": 3.68, "drawdown_pct": 36.9, "cycle_high": 97860, "note": "Bootstrapped from WebSearch 2026-07-03; replace with first GitHub Action run" } }File 3 —
agents/set_regime.jspackage.json— add one line toscripts