Add plain English report interpretation

This commit is contained in:
saymrwulf 2026-04-25 23:19:30 +02:00
parent 5691bbb299
commit 057c0fca1e
4 changed files with 75 additions and 3 deletions

View file

@ -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

View file

@ -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"

View file

@ -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.

View file

@ -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"