BTC at $76,099. Down 40.8% from the $125,835 ATH. RSI neutral at 54. Sitting 8% below the 200-day EMA.
Most retail traders and even most bots handle this kind of sideways chop with rules-based buy-the-dip logic. But rules miss context — when exchange reserves are at 7-year lows and $2.5B in ETF money just flowed in during a correction, that is structurally different from "BTC dropped 8% in a week, time to buy."
So I built a multi-agent BTC research system that does the reasoning explicitly. Three specialized agents, each with a domain, weighted and fused into a single daily signal.
Master Controller (zo.ask)
│
┌───────────────────┼───────────────────┐
▼ ▼ ▼
Technical Agent On-Chain Agent Macro Agent
(0.35) (0.40) (0.25)
│ │ │
└───────────────────┼───────────────────┘
▼
Signal Generator
▼
Daily Report
Technical Agent — RSI, MACD, Bollinger Bands, support/resistance, price vs 200 EMA, distance from ATH. Reads the chart and gives a directional score.
On-Chain Agent — MVRV, realized price floor, exchange reserves (7-year low = bullish), ETF flows, accumulation trend score. Reads the blockchain fundamentals.
Macro Agent — DXY correlation, BTC-S&P500 correlation, Fed rate expectations, S&P500 at record highs (risk-on backdrop), geopolitical risk (Iran war premium), institutional adoption. Reads the world outside crypto.
Each agent outputs a score
(0-1) and a confidence
(0-1). The signal generator multiplies scores by weights (0.35 / 0.40 / 0.25) and sums them. Today's output:
Total Score: 0.662
Confidence: 0.684
Signal Type: BUY
Action: Take partial position (25-50% of capital)
Here is the on-chain scoring (simplified):
def score_mvrv(mvrv):
if mvrv < 1.5: return +0.20, "UNDERVALUED"
if mvrv < 2.5: return +0.10, "FAIR_VALUE"
if mvrv < 3.5: return -0.15, "OVERVALUED"
return -0.25, "EXTREME_OVERVALUED"
def score_exchange_reserves(pct):
if pct < 12: return +0.15, "LOW_RESERVES_BULLISH" # 7-year low
if pct < 15: return +0.05, "NORMAL"
return -0.10, "HIGH_RESERVES_BEARISH"
The technical agent is similar — RSI 54 = neutral, below 200 EMA = -0.10, near resistance = -0.15. Each indicator adds or subtracts from the total weight.
| Agent | Score | Weight | Contribution |
|---|---|---|---|
| Technical | 0.44 | 0.35 | 0.154 |
| On-Chain | 0.80 | 0.40 | 0.320 |
| Macro | 0.75 | 0.25 | 0.188 |
On-chain at 0.8 because: MVRV 1.5 (fair value but historically a buy zone), realized price floor $50K (we're well above), exchange reserves at 11.9% (7-year low = holders aren't selling), $2.5B in Q1 ETF inflows (institutions bought the correction).
Macro at 0.75 because: S&P500 and Nasdaq at record highs (risk-on backdrop that should lift BTC), BTC lagging those highs (bullish catch-up), MSTR bought 34,164 BTC at $74,395 ($2.54B position), Trump Strategic Bitcoin Reserve at 328,372 BTC (~$24.5B).
Technical is the laggard at 0.44 because: RSI neutral, below 200 EMA ($82,919), and the price is near the $75,000 resistance. The chart says "wait for confirmation" while the fundamentals say "the dip is over."
That's the whole point of the multi-agent design: when fundamentals say BUY but technicals say WAIT, the system outputs a calibrated partial-position signal instead of a binary yes/no.
STRONG_BUY: score >= 0.75 → full position (50-100%)
BUY: score >= 0.60 → partial position (25-50%)
NEUTRAL: score >= 0.45 → hold / no new entries
SELL: score >= 0.30 → reduce position 25-50%
STRONG_SELL: score < 0.30 → exit or short
cd btc-research
python3 scripts/run_analysis.py
Outputs to signals/daily_signals.json
and reports/daily_report_YYYY-MM-DD.md
. Each agent is independently runnable:
python3 agents/technical_agent.py # chart signals
python3 agents/onchain_agent.py # Glassnode-style metrics
python3 agents/macro_agent.py # DXY, Fed, equities
The whole thing runs as a scheduled agent on Zo Computer at 06:00 UTC daily, writes a markdown report, and could email/Telegram it to the user. Every component is plain Python — no LLM calls, no external APIs, fully transparent scoring.
The agents currently use a static dataset (current price, MVRV, ETF flows, etc.) hand-curated from research. The next iteration is to wire real data: CoinGecko or Glassnode for price/on-chain, FRED for macro. The agent code is structured for it — analyze()
just needs to swap the hardcoded metrics dict for an API call.
The signal generator is also naive about disagreement. When technicals say NEUTRAL but on-chain says BULLISH, the weighted sum works but doesn't tell you "technicals are the drag, watch for the breakout." A v2 could add per-agent variance detection and flag low-confidence signals explicitly.
/home/workspace/btc-research/skills/btc_system/SKILL.md
btc-research/AUTOMATION.md
signals/daily_signals.json
MIT license. The signal is research, not advice — always do your own due diligence. Crypto is risky. The point of the system is to make the reasoning auditable, not to replace your judgment.
If you have ideas for additional agents (sentiment, derivatives funding rates, options skew, miner flows), PRs welcome. The agent interface is simple — return a dict with signal
, score
, confidence
, and a summary
string — and the signal generator picks them up automatically.