gabriel / muse public
btc_oracle.py python
135 lines 4.8 KB
f7645c07 feat(store): self-describing HEAD format with typed read/write API (#163) Gabriel Cardona <cgcardona@gmail.com> 3d ago
1 """muse bitcoin oracle — versioned price and fee oracle history.
2
3 Shows the latest oracle price and fee data along with historical trends
4 from the versioned oracle snapshots.
5
6 Usage::
7
8 muse bitcoin oracle
9 muse bitcoin oracle --rows 20
10 muse bitcoin oracle --commit HEAD~5
11 muse bitcoin oracle --json
12
13 Output::
14
15 Oracle data — working tree (last 10 ticks)
16
17 Latest price: $62,000.00 / BTC (source: coinbase · block 850,000)
18 Latest fees: 1blk: 30 | 6blk: 15 | 144blk: 3 sat/vbyte
19
20 Price history:
21 Block 850,000 $62,000.00 coinbase
22 Block 849,856 $61,800.00 coinbase
23 Block 849,712 $62,100.00 coinbase
24 ...
25
26 Fee history:
27 Block 850,000 1blk:30 | 6blk:15 | 144blk:3
28 ...
29 """
30
31 from __future__ import annotations
32
33 import json
34 import logging
35
36 import typer
37
38 from muse.core.errors import ExitCode
39 from muse.core.repo import require_repo
40 from muse.core.store import read_current_branch, resolve_commit_ref
41 from muse.plugins.bitcoin._loader import (
42 load_fees,
43 load_fees_from_workdir,
44 load_prices,
45 load_prices_from_workdir,
46 read_repo_id,
47 )
48 from muse.plugins.bitcoin._query import fee_surface_str, latest_fee_estimate, latest_price
49
50 logger = logging.getLogger(__name__)
51 app = typer.Typer()
52
53
54 @app.callback(invoke_without_command=True)
55 def oracle(
56 ctx: typer.Context,
57 ref: str | None = typer.Option(None, "--commit", "-c", metavar="REF",
58 help="Read from a historical commit instead of the working tree."),
59 rows: int = typer.Option(10, "--rows", "-n", metavar="N",
60 help="Number of historical ticks to display."),
61 as_json: bool = typer.Option(False, "--json", help="Emit results as JSON."),
62 ) -> None:
63 """Show versioned price and fee oracle data with historical trend.
64
65 Oracle data (BTC/USD prices and fee estimates) is a first-class dimension
66 of the Bitcoin domain state. Every agent decision that depends on price
67 or fees is made with a specific oracle snapshot in scope — version-
68 controlled alongside the UTXO set and strategy.
69
70 This command makes the oracle history auditable: you can see the exact
71 price feed that was in effect at each historical commit.
72 """
73 root = require_repo()
74 commit_label = "working tree"
75
76 if ref is not None:
77 repo_id = read_repo_id(root)
78 branch = read_current_branch(root)
79 commit = resolve_commit_ref(root, repo_id, branch, ref)
80 if commit is None:
81 typer.echo(f"❌ Commit '{ref}' not found.", err=True)
82 raise typer.Exit(code=ExitCode.USER_ERROR)
83 prices = load_prices(root, commit.commit_id)
84 fees = load_fees(root, commit.commit_id)
85 commit_label = commit.commit_id[:8]
86 else:
87 prices = load_prices_from_workdir(root)
88 fees = load_fees_from_workdir(root)
89
90 latest_p = latest_price(prices)
91 latest_f = latest_fee_estimate(fees)
92
93 price_hist = sorted(prices, key=lambda p: p["timestamp"], reverse=True)[:rows]
94 fee_hist = sorted(fees, key=lambda f: f["timestamp"], reverse=True)[:rows]
95
96 if as_json:
97 typer.echo(json.dumps({
98 "commit": commit_label,
99 "latest_price_usd": latest_p,
100 "latest_fee_surface": {
101 "1_block": latest_f["target_1_block_sat_vbyte"] if latest_f else None,
102 "6_block": latest_f["target_6_block_sat_vbyte"] if latest_f else None,
103 "144_block": latest_f["target_144_block_sat_vbyte"] if latest_f else None,
104 } if latest_f else None,
105 "price_history": [dict(p) for p in price_hist],
106 "fee_history": [dict(f) for f in fee_hist],
107 }, indent=2))
108 return
109
110 typer.echo(f"\nOracle data — {commit_label} (last {rows} ticks)\n")
111
112 if latest_p:
113 src = price_hist[0]["source"] if price_hist else "unknown"
114 bh = price_hist[0]["block_height"] if price_hist else None
115 bh_str = f"block {bh:,}" if bh else "unanchored"
116 typer.echo(f" Latest price: ${latest_p:,.2f} / BTC (source: {src} · {bh_str})")
117 else:
118 typer.echo(" Latest price: (no oracle data)")
119
120 if latest_f:
121 typer.echo(f" Latest fees: {fee_surface_str(latest_f)}")
122 else:
123 typer.echo(" Latest fees: (no oracle data)")
124
125 if price_hist:
126 typer.echo(f"\n Price history ({len(price_hist)} ticks):")
127 for p in price_hist:
128 bh_str = f"block {p['block_height']:>8,}" if p["block_height"] else "unanchored "
129 typer.echo(f" {bh_str} ${p['price_usd']:>12,.2f} {p['source']}")
130
131 if fee_hist:
132 typer.echo(f"\n Fee history ({len(fee_hist)} ticks):")
133 for f in fee_hist:
134 bh_str = f"block {f['block_height']:>8,}" if f["block_height"] else "unanchored "
135 typer.echo(f" {bh_str} {fee_surface_str(f)}")