btc_oracle.py
python
| 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)}") |