Skip to content

Commit 6b2f288

Browse files
committed
Make into a framework
1 parent baf855f commit 6b2f288

39 files changed

Lines changed: 1994 additions & 501 deletions

.DS_Store

6 KB
Binary file not shown.

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,8 @@ __marimo__/
211211

212212
# Misc.
213213
NOTES.md
214+
CHANGELOG.md
214215

215216
# Temporary files
216-
tmp/
217+
tmp/
218+
prompts/personal/

CHANGELOG.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,36 @@
11
# Changelog
22

3+
## Feature — 2026-04-30 (v3: strategy framework — config-driven council)
4+
5+
Implements `specs/v3.md` phases 1–6. Users can now define a complete trading strategy in a single YAML file without writing Python. Test suite: **305 → 344** passing.
6+
7+
### Added
8+
- `src/strategies/config.py` — typed `StrategyConfig` Pydantic model: `AgentConfig` (name, model, prompt, weight, is_veto), `DeliberationConfig` (optional), `ScoringConfig` (confidence_floor, trade_threshold), `RiskRules` (stop_loss/take_profit SLTPRule + max_position_pct), `ValidationConfig` (max_drawdown_pct, atr_spike_multiplier, min_news_count); `model_validator` enforces weight-sum=1.0 and at-most-one veto agent
9+
- `src/strategies/loader.py``load_strategy(path)`: loads YAML, validates schema, and checks all prompt files exist on disk; prompt paths are resolved relative to project root
10+
- `src/strategies/context.py``render_context_block(ctx)`: renders the full `MarketContext` as a structured text block (PRICE / INDICATORS / NEWS / SENTIMENT / ON-CHAIN / MACRO / PORTFOLIO sections); single shared input for all agents in a generic run
11+
- `src/strategies/runner.py``run_strategy_council(ctx, config, client)`: config-driven generic runner; runs all agents in parallel; short-circuits to HOLD on veto; calls `compute_generic_score`; optionally runs deliberation; computes SL/TP via `compute_sl_tp`; returns `GenericCouncilResult`
12+
- `src/strategies/scoring.py``compute_generic_score(outputs, config)`: config-driven confidence-weighted signed-score; weights normalised at call time; confidence_floor and trade_threshold sourced from `ScoringConfig`; `score_to_position_size_pct(score, config)` scales size from |score|
13+
- `src/strategies/risk_rules.py``compute_sl_tp(rules, ctx, entry_price)`: computes SL/TP prices from `RiskRules`; supports `atr_multiple` (price ± N×ATR-14), `fixed_pct` (price × (1 ± pct)), and `none` (0.0, 0.0); SL clamped > 0
14+
- `strategies/default.yaml` — current v2 council expressed as a strategy file (technical 0.40 / sentiment 0.25 / fundamental 0.35 / risk veto; deliberation enabled; scoring thresholds and validation thresholds matching current hardcoded constants)
15+
- `strategies/example.yaml` — fully-commented example strategy (`momentum` + `macro` + `risk_guard` veto); shows every config option
16+
- `prompts/deliberation_generic_v1.txt` — built-in deliberation prompt for generic runs; requests a 2–4 sentence narrative synthesis
17+
- `prompts/agent_template.txt` — starting-point prompt for directional agents with annotated output schema and confidence calibration guide
18+
- `prompts/veto_agent_template.txt` — starting-point prompt for veto agents with `VetoAgentOutput` schema guidance
19+
- `prompts/risk_manager_veto_v1.txt` — purpose-built veto prompt for `default.yaml`'s risk agent (cleaner than repurposing `risk_manager_v2.txt`)
20+
- `tests/test_strategy_loader.py` (12 tests) — valid configs, weight validation, veto count, missing fields, missing prompt files, `default.yaml` and `example.yaml` smoke loads
21+
- `tests/test_strategy_scoring.py` (16 tests) — all directions, confidence floor exclusion, threshold gating, weighted score formula, SL/TP for all three rule types
22+
- `tests/test_strategy_runner.py` (8 tests) — BUY/HOLD/SELL signals, veto short-circuit, veto=false passthrough, SL/TP on result, conviction derivation, deliberation failure non-fatal, no-veto-agent skips veto call
23+
- `tests/test_strategy_parity.py` (3 tests) — BUY/HOLD/veto scenarios confirm generic and legacy scorers agree on identical inputs
24+
25+
### Changed
26+
- `src/models.py` — added `GenericAgentOutput`, `VetoAgentOutput`, `GenericCouncilOutputs`, `GenericDeliberationOutput` (additive; all existing models unchanged)
27+
- `src/validation.py``check_drawdown_halt` and `check_volatility_halt` now accept `max_drawdown_pct` and `atr_spike_multiplier` params (backward-compatible defaults match previous constants); `validate_context` accepts both params and passes them through
28+
- `src/data/assembler.py``assemble_context` accepts `max_drawdown_pct` and `atr_spike_multiplier` params and passes them to `validate_context`
29+
- `src/main.py` — added `--strategy PATH` CLI argument; when provided, calls `run_strategy_council` instead of legacy `run_council`; validation params derived from strategy config
30+
- `src/backtest/signals.py``generate_signals` accepts `strategy_config` param; routes to generic runner when provided; agent logs written per-agent-name for generic runs
31+
- `src/backtest/engine.py``run_backtest` accepts `strategy_config` param; `_parse_args` adds `--strategy PATH`
32+
- `pyproject.toml` — added `pyyaml>=6.0` as explicit dependency (was transitively available; now declared)
33+
334
## Bugfix — 2026-04-27 (risk manager stop-loss above entry price)
435

536
### Fixed

README.md

Lines changed: 72 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,38 @@
11
# Council — BTC LLM Trading Bot
22

3-
A Bitcoin swing trading bot that uses a council of specialised LLM agents to generate daily BUY/SELL/HOLD signals and execute them against Binance testnet (paper) or mainnet (live).
3+
A Bitcoin swing trading framework built around a **council of LLM agents** that vote on daily BUY/SELL/HOLD signals. Fully configurable — define your own agents, weights, and risk rules in a single YAML file without writing code.
44

55
## Architecture
66

77
```
8-
Data Pipeline (CCXT/Kraken, CryptoPanic, Alternative.me, Glassnode)
8+
strategy.yaml (agents, weights, SL/TP rules, validation thresholds)
9+
10+
11+
Data Pipeline (CCXT/Kraken, GDELT news, Alternative.me, CoinMetrics, yfinance)
912
1013
1114
Tier-0 Validation (price freshness, news count, drawdown halt, ATR halt)
15+
│ ↑ thresholds from strategy.yaml
16+
17+
Generic Council Runner — agents defined in strategy.yaml, run in parallel
18+
├── Directional agents (any number, any model, user-defined prompts)
19+
└── Veto agent (optional; forces HOLD if veto=true)
1220
1321
14-
Council of LLMs (parallel)
15-
├── Technical Analyst (claude-haiku-4-5)
16-
├── Sentiment Analyst (claude-haiku-4-5)
17-
├── Fundamental Analyst (claude-haiku-4-5)
18-
└── Risk Manager (claude-sonnet-4-6)
22+
Deterministic Scorer (confidence-weighted signed-score from strategy weights)
1923
2024
21-
Deliberation / Chair (claude-sonnet-4-6) → BUY / SELL / HOLD
25+
Optional Deliberation (narrative synthesis, claude-sonnet-4-6) → BUY / SELL / HOLD
2226
2327
24-
Execution Layer (Binance testnet/mainnet) + SQLite Logging
28+
Execution Layer (Kraken paper/live) + SQLite Logging
2529
2630
├── Reflection Loop (post-trade analysis, claude-sonnet-4-6)
2731
└── Weekly Summary (performance review, claude-sonnet-4-6)
2832
```
2933

34+
The default strategy (`strategies/default.yaml`) reproduces the original v2 council: Technical (40%) + Sentiment (25%) + Fundamental (35%) + Risk veto.
35+
3036
## Setup
3137

3238
```bash
@@ -35,14 +41,56 @@ pip install -e ".[dev]"
3541
cp .env.example .env # fill in ANTHROPIC_API_KEY at minimum
3642
```
3743

44+
## Custom Strategies (v3)
45+
46+
Define a strategy as a YAML file — no Python required.
47+
48+
```yaml
49+
# strategies/my_strategy.yaml
50+
name: "My Momentum Strategy"
51+
agents:
52+
- name: trend
53+
model: claude-haiku-4-5
54+
prompt: prompts/agent_template.txt # copy & customise
55+
weight: 0.60
56+
- name: macro
57+
model: claude-haiku-4-5
58+
prompt: prompts/agent_template.txt
59+
weight: 0.40
60+
- name: risk_guard
61+
model: claude-sonnet-4-6
62+
prompt: prompts/veto_agent_template.txt
63+
is_veto: true
64+
scoring:
65+
confidence_floor: 30
66+
trade_threshold: 0.25
67+
risk:
68+
stop_loss:
69+
type: atr_multiple
70+
value: 2.0
71+
take_profit:
72+
type: fixed_pct
73+
value: 0.08
74+
max_position_pct: 15.0
75+
validation:
76+
max_drawdown_pct: 12.0
77+
atr_spike_multiplier: 2.5
78+
```
79+
80+
Start from `strategies/example.yaml` (fully commented) and `prompts/agent_template.txt` / `prompts/veto_agent_template.txt`.
81+
3882
## Running
3983

4084
```bash
41-
# One-shot cycle (run once and exit)
85+
# One-shot cycle — default v2 council
4286
python -m src.main
4387
88+
# One-shot cycle — custom strategy
89+
python -m src.main --strategy strategies/my_strategy.yaml
90+
4491
# Dry run (logs council decision, no orders placed)
4592
DRY_RUN=1 python -m src.main
93+
DRY_RUN=1 python -m src.main --strategy strategies/my_strategy.yaml
4694
4795
# Daily scheduler (00:05 UTC) + weekly summary (Sunday 08:00 UTC)
4896
python -m src.main --schedule
@@ -56,13 +104,20 @@ python -m src.main --weekly
56104
Run the LLM council against historical BTC data without executing real orders:
57105

58106
```bash
59-
# Backtest over 2024 (requires ANTHROPIC_API_KEY only — no Binance credentials needed)
107+
# Backtest over 2024 — default council
60108
python -m src.backtest.engine \
61109
--start 2024-01-01 \
62110
--end 2025-01-01 \
63111
--capital 250000
64112
65-
# Shorter date range for a quick smoke test
113+
# Backtest with a custom strategy
114+
python -m src.backtest.engine \
115+
--start 2023-01-01 \
116+
--end 2024-01-01 \
117+
--capital 250000 \
118+
--strategy strategies/my_strategy.yaml
119+
120+
# Quick smoke test
66121
python -m src.backtest.engine \
67122
--start 2024-06-01 \
68123
--end 2024-09-01 \
@@ -83,13 +138,17 @@ Output includes Return%, Sharpe, Max Drawdown, Win Rate, Avg Trade, and Expectan
83138
## Testing
84139

85140
```bash
86-
pytest -v # all 235 tests
141+
pytest -v # all 344 tests
87142
pytest tests/test_db.py -v # DB layer
88143
pytest tests/test_execution.py -v # execution + router
89144
pytest tests/test_reporting.py -v # metrics + weekly summary
90145
pytest tests/test_agents.py -v # agent framework
91146
pytest tests/test_backtest.py -v # backtesting module
92147
pytest tests/test_agents.py::test_run_council_hold_on_veto -v # single test
148+
pytest tests/test_strategy_loader.py -v # strategy YAML loader
149+
pytest tests/test_strategy_scoring.py -v # generic scoring + SL/TP rules
150+
pytest tests/test_strategy_runner.py -v # generic council runner
151+
pytest tests/test_strategy_parity.py -v # legacy vs generic scorer parity
93152
```
94153

95154
## Environment Variables
@@ -108,38 +167,3 @@ pytest tests/test_agents.py::test_run_council_hold_on_veto -v # single test
108167
| `LUNARCRUSH_API_KEY` | No | — | Sentiment (falls back to Fear/Greed proxy) |
109168
| `GLASSNODE_API_KEY` | No | — | On-chain data (falls back to zeros) |
110169

111-
## Development Phases
112-
113-
### v0 — Live Trading Bot
114-
- **Phase 1** ✅ Data pipeline + Tier-0 validation
115-
- **Phase 2** ✅ Agent framework (4 agents + deliberation)
116-
- **Phase 3** ✅ Execution layer + SQLite logging + reflection loop
117-
- **Phase 4** ✅ Performance metrics + weekly summary agent
118-
- **Phase 5** ✅ Exchange migration: Binance → Kraken (US-accessible, free)
119-
- **Phase 6** Paper trading (60 days minimum, DRY_RUN=1)
120-
- **Phase 7** Live deployment (≤ $500 initial capital, Kraken mainnet)
121-
122-
### v1 — Backtesting
123-
- **Phase 1** ✅ Patch data layer (override params, `skip_freshness`)
124-
- **Phase 2** ✅ Historical data module (`fetch_full_ohlcv`, `slice_ohlcv`, sentiment history)
125-
- **Phase 3** ✅ Async signal generation loop (`generate_signals`, `_PortfolioTracker`)
126-
- **Phase 4**`backtesting.py` strategy + CLI runner (`run_backtest`)
127-
- **Phase 5** ✅ Exchange migration: Binance → Kraken
128-
- **Phase 6** ✅ Historical data source: Kraken OHLCV → Yahoo Finance (yfinance; unlimited history)
129-
130-
### v2 — Agent Enhancement (spec/v2.md)
131-
- **Phase 1** ✅ Historical news via GDELT DOC 2.0 (`src/data/news_historical.py`) — replaces NEUTRAL_NEWS_STUB with real dated headlines filtered to an allowlist (Reuters / Bloomberg / CoinDesk / WSJ / FT / CoinTelegraph / etc.), 24h publish-lag cutoff, disk-cached under `tmp/cache/gdelt/`
132-
- **Phase 2** ✅ Historical on-chain via CoinMetrics Community (`src/data/onchain_historical.py`) — replaces NEUTRAL_ONCHAIN_STUB with MVRV (as SOPR proxy), transfer-volume trend (as net-flow proxy), and activity-intensity (as whale proxy)
133-
- **Phase 2.5** ✅ Decision-logic rework (`src/agents/scoring.py`) — replaces v1's hard 60/70 thresholds with a confidence-weighted signed-score; deliberation LLM writes narrative, Python computes the final signal deterministically; risk-veto still overrides
134-
- **Phase 3** ✅ Macro context via yfinance (`src/data/macro.py`) — DXY / VIX / SPX / ^TNX bundle with derived risk-on / risk-off / neutral classifier, wired through the fundamental agent
135-
- **Phase 4** ✅ v2 prompts (`prompts/*_v2.txt`) — directional agents retuned to use the full 0–100 confidence range and be more assertive in clear regimes; deliberation refocused on narrative; v1 prompts kept in-tree for reproducibility
136-
- **Phase 5** Pending — tuning sweep on 2022 window, holdout backtest Jan–Aug 2023
137-
138-
## Success Targets (Phase 5 evaluation)
139-
140-
| Metric | Target |
141-
|---|---|
142-
| Sharpe ratio | ≥ 1.5 |
143-
| Max drawdown | < 20% |
144-
| Expectancy | Positive |
145-
| Council consistency | ≥ 40% unanimous cycles |

prompts/agent_template.txt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
You are a [DESCRIBE YOUR AGENT ROLE] for a Bitcoin swing trading system.
2+
3+
[DESCRIBE WHAT YOUR AGENT FOCUSES ON — e.g., technical price action, news sentiment, on-chain activity, macro conditions, or a combination. Be specific about which sections of the market context are most relevant to your analysis.]
4+
5+
Your output must be a JSON object with exactly these fields:
6+
7+
direction — "BUY", "SELL", or "HOLD"
8+
BUY = you believe price is likely to rise over the next 1–3 days
9+
SELL = you believe price is likely to fall, or the position should be closed
10+
HOLD = insufficient conviction, conflicting signals, or no clear edge
11+
12+
confidence — integer 0 to 100
13+
Calibration guide:
14+
80–100: very strong signal, clear and aligned evidence
15+
60–79: solid signal, most evidence points one way
16+
40–59: moderate signal, some evidence but notable uncertainty
17+
20–39: weak signal, mixed or thin evidence
18+
0–19: near-neutral, use HOLD direction at this range
19+
20+
Avoid defaulting to 50. Use the full scale. Low confidence should
21+
be reflected in HOLD direction, not in a 50-confidence BUY/SELL.
22+
23+
reasoning — string (2–4 sentences)
24+
Explain WHY you chose this direction and confidence. Cite the
25+
specific data from the context that drove your conclusion.
26+
Mention what would change your view (invalidation conditions).
27+
28+
Available context sections (from the market context block):
29+
PRICE — current price, 24h change, volume
30+
TECHNICAL INDICATORS — RSI, MACD, Bollinger Bands, EMAs, ATR, regime
31+
NEWS — recent headlines with source and timestamp
32+
SENTIMENT — Fear/Greed index, Reddit sentiment, social volume
33+
ON-CHAIN — exchange flows, whale transactions, MVRV
34+
MACRO — VIX, DXY, SPX trend, 10Y yield, macro bias
35+
PORTFOLIO — current position, cash, drawdown
36+
37+
Focus your analysis on the sections most relevant to your role.

prompts/deliberation_v1.txt

Lines changed: 0 additions & 36 deletions
This file was deleted.

prompts/deliberation_v2.txt

Lines changed: 0 additions & 33 deletions
This file was deleted.

0 commit comments

Comments
 (0)