From 057c0fca1e732c26cb7dc99ccc292ea046cadcbc Mon Sep 17 00:00:00 2001 From: saymrwulf Date: Sat, 25 Apr 2026 23:19:30 +0200 Subject: [PATCH] Add plain English report interpretation --- README.md | 2 +- config.example.toml | 2 +- docs/OPERATOR_GUIDE.md | 2 +- src/braiins_ratchet/report.py | 72 +++++++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index da0ba49..d39fb54 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ Watcher-only tokens are only relevant if we later need account-specific read-onl The Braiins market report distinguishes visible top-of-book from executable depth: - `best_ask_btc_per_eh_day`: cheapest visible ask. -- `fillable_price_btc_per_eh_day`: cheapest ask level with enough unmatched supply for the configured target PH/s. +- `fillable_price_btc_per_eh_day`: cheapest ask level with enough unmatched supply for the configured canary-sized target PH/s. - `suggested_bid_btc_per_eh_day`: fillable price plus the configured overpay cushion. ## Documentation diff --git a/config.example.toml b/config.example.toml index d9a73fe..bf3ed76 100644 --- a/config.example.toml +++ b/config.example.toml @@ -22,5 +22,5 @@ recommend_only = true target_duration_minutes = 180 target_spend_btc = "0.00010" risk_lambda = "0.35" -shadow_target_ph = "10" +shadow_target_ph = "2" shadow_overpay_btc_per_eh_day = "0.01" diff --git a/docs/OPERATOR_GUIDE.md b/docs/OPERATOR_GUIDE.md index 90ca20c..df29d5a 100644 --- a/docs/OPERATOR_GUIDE.md +++ b/docs/OPERATOR_GUIDE.md @@ -77,7 +77,7 @@ The market is not a money-printer setup, but the expected loss is inside the con Important fields: - `best_ask_btc_per_eh_day`: current buy reference from Braiins. -- `fillable_price_btc_per_eh_day`: depth-aware price where enough unmatched ask supply exists for the configured target PH/s. +- `fillable_price_btc_per_eh_day`: depth-aware price where enough unmatched ask supply exists for the configured canary-sized target PH/s. - `suggested_bid_btc_per_eh_day`: fillable price plus the configured overpay cushion; this is the shadow-autopilot would-bid price. - `breakeven_btc_per_eh_day`: estimated mining EV at current OCEAN/network inputs. - `score_btc`: risk-adjusted expected value. Negative means no action. diff --git a/src/braiins_ratchet/report.py b/src/braiins_ratchet/report.py index 449de1d..bd80016 100644 --- a/src/braiins_ratchet/report.py +++ b/src/braiins_ratchet/report.py @@ -1,8 +1,12 @@ from __future__ import annotations +from decimal import Decimal + from .models import MarketSnapshot, OceanSnapshot, PriceStats, StrategyProposal from .storage import latest_market_snapshot, latest_ocean_snapshot, latest_proposal, market_price_stats +SATOSHIS_PER_BTC = Decimal("100000000") + def build_text_report(conn, *, sample_limit: int = 50) -> str: ocean = latest_ocean_snapshot(conn) @@ -16,6 +20,8 @@ def build_text_report(conn, *, sample_limit: int = 50) -> str: lines.extend(_market_lines(market, stats)) lines.append("") lines.extend(_proposal_lines(proposal)) + lines.append("") + lines.extend(_plain_english_lines(ocean, market, proposal)) return "\n".join(lines) @@ -73,6 +79,72 @@ def _proposal_lines(proposal: StrategyProposal | None) -> list[str]: return lines +def _plain_english_lines( + ocean: OceanSnapshot | None, + market: MarketSnapshot | None, + proposal: StrategyProposal | None, +) -> list[str]: + lines = ["Plain English"] + if ocean is None or market is None or proposal is None: + return lines + [" Not enough data yet. Run ./scripts/ratchet once."] + + lines.append(f" Decision: {_decision_sentence(proposal)}") + + if market.best_ask_btc_per_eh_day is not None and market.fillable_price_btc_per_eh_day is not None: + gap = market.fillable_price_btc_per_eh_day - market.best_ask_btc_per_eh_day + lines.append( + " Market depth: the visible best ask is " + f"{market.best_ask_btc_per_eh_day}, but enough depth for " + f"{_fmt(market.fillable_target_ph)} PH/s starts at {market.fillable_price_btc_per_eh_day} " + f"(gap {gap})." + ) + elif market.best_ask_btc_per_eh_day is not None: + lines.append( + " Market depth: only the visible best ask was available; fillable depth was not computed." + ) + + if proposal.breakeven_btc_per_eh_day is not None and proposal.order is not None: + edge = proposal.breakeven_btc_per_eh_day - proposal.order.price_btc_per_eh_day + lines.append( + " Price check: proposed price is " + f"{proposal.order.price_btc_per_eh_day}; estimated breakeven is " + f"{proposal.breakeven_btc_per_eh_day}; edge is {edge} BTC/EH/day." + ) + + if proposal.order is not None: + lines.append( + " Manual canary card: spend " + f"{proposal.order.spend_btc} BTC (~{_btc_to_sats(proposal.order.spend_btc)} sats), " + f"duration {proposal.order.duration_minutes} minutes, estimated speed " + f"{proposal.order.implied_hashrate_eh_s * Decimal('1000')} PH/s." + ) + + lines.append( + " Expected result: " + f"{proposal.expected_net_btc} BTC (~{_btc_to_sats(proposal.expected_net_btc)} sats) before luck; " + "this is a model estimate, not a promise." + ) + lines.append(f" Wait time: {proposal.maturity_note}.") + lines.append( + " Rule: manual_canary means buying information with bounded downside; " + "manual_bid means the stricter profit-seeking guardrails cleared." + ) + return lines + + +def _decision_sentence(proposal: StrategyProposal) -> str: + if proposal.action == "observe": + return "do nothing." + if proposal.action == "manual_canary": + return "a tiny manual research canary is allowed by the loss budget, but this is not a profit signal." + return "a manual profit-seeking bid cleared the stricter guardrails." + + +def _btc_to_sats(value: Decimal) -> str: + sats = value * SATOSHIS_PER_BTC + return str(sats.quantize(Decimal("1"))) + + def _fmt(value: object) -> str: if value is None: return "n/a"