mirror of
https://github.com/saymrwulf/BraiinsRatchet.git
synced 2026-05-14 20:37:52 +00:00
Add depth-aware shadow bid pricing
This commit is contained in:
parent
6096434ef4
commit
5691bbb299
15 changed files with 227 additions and 19 deletions
|
|
@ -61,6 +61,12 @@ Watcher-only tokens are only relevant if we later need account-specific read-onl
|
|||
- `manual_canary`: a tiny manually executed research experiment is within the configured loss budget.
|
||||
- `manual_bid`: a manually executed bid clears profit-seeking discount and risk guardrails.
|
||||
|
||||
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.
|
||||
- `suggested_bid_btc_per_eh_day`: fillable price plus the configured overpay cushion.
|
||||
|
||||
## Documentation
|
||||
|
||||
- `PROGRAM.md`: research charter and ratchet rules.
|
||||
|
|
|
|||
|
|
@ -22,3 +22,5 @@ recommend_only = true
|
|||
target_duration_minutes = 180
|
||||
target_spend_btc = "0.00010"
|
||||
risk_lambda = "0.35"
|
||||
shadow_target_ph = "10"
|
||||
shadow_overpay_btc_per_eh_day = "0.01"
|
||||
|
|
|
|||
|
|
@ -77,6 +77,8 @@ 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.
|
||||
- `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.
|
||||
- `proposed_spend_btc`: canary spend, currently tiny by design.
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ The result is a recommendation only. `observe` means no action is recommended. `
|
|||
|
||||
The report's sampled price min/avg/max uses public Braiins snapshots only. Manual imports are still stored and can drive evaluation, but they do not pollute live market summary statistics.
|
||||
|
||||
The strategy price is depth-aware when orderbook depth is available: it prefers `suggested_bid_btc_per_eh_day` over raw best ask. This prevents top-of-book slivers or already-matched asks from looking cheaper than actually executable depth.
|
||||
|
||||
## Repeated Sampling
|
||||
|
||||
For short monitor sessions:
|
||||
|
|
|
|||
|
|
@ -36,14 +36,24 @@ class BraiinsPublicClient:
|
|||
with urlopen(request, timeout=15) as response:
|
||||
return json.loads(response.read().decode("utf-8"))
|
||||
|
||||
def fetch_market_snapshot(self) -> MarketSnapshot:
|
||||
def fetch_market_snapshot(
|
||||
self,
|
||||
*,
|
||||
target_ph: Decimal = Decimal("10"),
|
||||
overpay_btc_per_eh_day: Decimal = Decimal("0.01"),
|
||||
) -> MarketSnapshot:
|
||||
stats = self.get_json("/spot/stats")
|
||||
orderbook = self.get_json("/orderbook")
|
||||
if not isinstance(stats, dict):
|
||||
raise BraiinsSafetyError("/spot/stats did not return an object")
|
||||
if not isinstance(orderbook, dict):
|
||||
raise BraiinsSafetyError("/orderbook did not return an object")
|
||||
return market_snapshot_from_public_api(stats, orderbook)
|
||||
return market_snapshot_from_public_api(
|
||||
stats,
|
||||
orderbook,
|
||||
target_ph=target_ph,
|
||||
overpay_btc_per_eh_day=overpay_btc_per_eh_day,
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
|
@ -90,6 +100,13 @@ def market_snapshot_from_json_file(path: str) -> MarketSnapshot:
|
|||
),
|
||||
best_bid_btc_per_eh_day=_optional_decimal(raw.get("best_bid_btc_per_eh_day")),
|
||||
best_ask_btc_per_eh_day=_optional_decimal(raw.get("best_ask_btc_per_eh_day")),
|
||||
fillable_price_btc_per_eh_day=_optional_decimal(raw.get("fillable_price_btc_per_eh_day")),
|
||||
fillable_target_ph=_optional_decimal(raw.get("fillable_target_ph")),
|
||||
fillable_available_ph=_optional_decimal(raw.get("fillable_available_ph")),
|
||||
suggested_bid_btc_per_eh_day=_optional_decimal(raw.get("suggested_bid_btc_per_eh_day")),
|
||||
suggested_overpay_btc_per_eh_day=_optional_decimal(
|
||||
raw.get("suggested_overpay_btc_per_eh_day")
|
||||
),
|
||||
last_price_btc_per_eh_day=_optional_decimal(raw.get("last_price_btc_per_eh_day")),
|
||||
total_hashrate_eh_s=_optional_decimal(raw.get("total_hashrate_eh_s")),
|
||||
available_hashrate_eh_s=(
|
||||
|
|
@ -106,9 +123,12 @@ def market_snapshot_from_public_api(
|
|||
stats: dict[str, Any],
|
||||
orderbook: dict[str, Any],
|
||||
timestamp_utc: str | None = None,
|
||||
target_ph: Decimal = Decimal("10"),
|
||||
overpay_btc_per_eh_day: Decimal = Decimal("0.01"),
|
||||
) -> MarketSnapshot:
|
||||
best_bid = _best_price_from_orders(orderbook.get("bids"), prefer="max")
|
||||
best_ask = _best_price_from_orders(orderbook.get("asks"), prefer="min")
|
||||
depth = fillable_ask_for_target(orderbook.get("asks"), target_ph)
|
||||
last_price = _sat_to_btc(stats.get("last_avg_price_sat"))
|
||||
total_hashrate = _ph_to_eh(stats.get("hash_rate_available_10m_ph"))
|
||||
matched_hashrate = _ph_to_eh(stats.get("hash_rate_matched_10m_ph"))
|
||||
|
|
@ -117,13 +137,23 @@ def market_snapshot_from_public_api(
|
|||
if total_hashrate is not None and matched_hashrate is not None:
|
||||
available_hashrate = max(Decimal("0"), total_hashrate - matched_hashrate)
|
||||
|
||||
best_price = best_ask or last_price or best_bid
|
||||
suggested_bid = (
|
||||
depth.price_btc_per_eh_day + overpay_btc_per_eh_day
|
||||
if depth.price_btc_per_eh_day is not None
|
||||
else None
|
||||
)
|
||||
best_price = suggested_bid or best_ask or last_price or best_bid
|
||||
|
||||
return MarketSnapshot(
|
||||
timestamp_utc=timestamp_utc or datetime.now(UTC).isoformat(timespec="seconds"),
|
||||
best_price_btc_per_eh_day=best_price,
|
||||
best_bid_btc_per_eh_day=best_bid,
|
||||
best_ask_btc_per_eh_day=best_ask,
|
||||
fillable_price_btc_per_eh_day=depth.price_btc_per_eh_day,
|
||||
fillable_target_ph=target_ph,
|
||||
fillable_available_ph=depth.available_ph,
|
||||
suggested_bid_btc_per_eh_day=suggested_bid,
|
||||
suggested_overpay_btc_per_eh_day=overpay_btc_per_eh_day,
|
||||
last_price_btc_per_eh_day=last_price,
|
||||
total_hashrate_eh_s=total_hashrate,
|
||||
available_hashrate_eh_s=available_hashrate,
|
||||
|
|
@ -132,6 +162,59 @@ def market_snapshot_from_public_api(
|
|||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class FillableDepth:
|
||||
price_btc_per_eh_day: Decimal | None
|
||||
available_ph: Decimal
|
||||
|
||||
|
||||
def fillable_ask_for_target(asks: object, target_ph: Decimal) -> FillableDepth:
|
||||
if not isinstance(asks, list) or target_ph <= 0:
|
||||
return FillableDepth(price_btc_per_eh_day=None, available_ph=Decimal("0"))
|
||||
|
||||
levels: list[tuple[Decimal, Decimal]] = []
|
||||
for ask in asks:
|
||||
if not isinstance(ask, dict):
|
||||
continue
|
||||
price = _sat_to_btc(ask.get("price_sat"))
|
||||
available = _available_ask_ph(ask)
|
||||
if price is not None and available > 0:
|
||||
levels.append((price, available))
|
||||
|
||||
levels.sort(key=lambda item: item[0])
|
||||
cumulative = Decimal("0")
|
||||
for price, available in levels:
|
||||
cumulative += available
|
||||
if cumulative >= target_ph:
|
||||
return FillableDepth(price_btc_per_eh_day=price, available_ph=cumulative)
|
||||
|
||||
return FillableDepth(price_btc_per_eh_day=None, available_ph=cumulative)
|
||||
|
||||
|
||||
def _available_ask_ph(ask: dict[str, Any]) -> Decimal:
|
||||
for key in ("hash_rate_available_ph", "available_hashrate_ph", "available_ph"):
|
||||
value = _optional_decimal(ask.get(key))
|
||||
if value is not None:
|
||||
return max(Decimal("0"), value)
|
||||
|
||||
limit = _optional_decimal(
|
||||
ask.get("hash_rate_limit_ph")
|
||||
or ask.get("limit_ph")
|
||||
or ask.get("hashRateAvailable")
|
||||
or ask.get("amount")
|
||||
)
|
||||
used = _optional_decimal(
|
||||
ask.get("hash_rate_matched_ph")
|
||||
or ask.get("used_ph")
|
||||
or ask.get("hashRateMatched")
|
||||
or ask.get("total")
|
||||
)
|
||||
if limit is not None:
|
||||
return max(Decimal("0"), limit - (used or Decimal("0")))
|
||||
|
||||
return Decimal("0")
|
||||
|
||||
|
||||
def _best_price_from_orders(orders: object, prefer: str) -> Decimal | None:
|
||||
if not isinstance(orders, list):
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -50,8 +50,12 @@ def cmd_import_market(args: argparse.Namespace) -> int:
|
|||
|
||||
|
||||
def cmd_collect_braiins_public(args: argparse.Namespace) -> int:
|
||||
config = load_config(Path(args.config) if args.config else None)
|
||||
client = BraiinsPublicClient(api_base=args.base_url.rstrip("/"))
|
||||
snapshot = client.fetch_market_snapshot()
|
||||
snapshot = client.fetch_market_snapshot(
|
||||
target_ph=config.strategy.shadow_target_ph,
|
||||
overpay_btc_per_eh_day=config.strategy.shadow_overpay_btc_per_eh_day,
|
||||
)
|
||||
with connect() as conn:
|
||||
init_db(conn)
|
||||
save_market_snapshot(conn, snapshot)
|
||||
|
|
@ -139,6 +143,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|||
"collect-braiins-public",
|
||||
help="collect one unauthenticated Braiins public market snapshot",
|
||||
)
|
||||
braiins.add_argument("--config")
|
||||
braiins.add_argument("--base-url", default="https://hashpower.braiins.com/webapi")
|
||||
braiins.set_defaults(func=cmd_collect_braiins_public)
|
||||
|
||||
|
|
|
|||
|
|
@ -51,6 +51,8 @@ class StrategyConfig:
|
|||
target_duration_minutes: int
|
||||
target_spend_btc: Decimal
|
||||
risk_lambda: Decimal
|
||||
shadow_target_ph: Decimal
|
||||
shadow_overpay_btc_per_eh_day: Decimal
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
|
@ -98,5 +100,9 @@ def load_config(path: Path | None = None) -> AppConfig:
|
|||
target_duration_minutes=int(strategy.get("target_duration_minutes", 180)),
|
||||
target_spend_btc=_decimal(strategy.get("target_spend_btc"), "0.0001"),
|
||||
risk_lambda=_decimal(strategy.get("risk_lambda"), "0.25"),
|
||||
shadow_target_ph=_decimal(strategy.get("shadow_target_ph"), "10"),
|
||||
shadow_overpay_btc_per_eh_day=_decimal(
|
||||
strategy.get("shadow_overpay_btc_per_eh_day"), "0.01"
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@ class MarketSnapshot:
|
|||
best_price_btc_per_eh_day: Decimal | None
|
||||
best_bid_btc_per_eh_day: Decimal | None = None
|
||||
best_ask_btc_per_eh_day: Decimal | None = None
|
||||
fillable_price_btc_per_eh_day: Decimal | None = None
|
||||
fillable_target_ph: Decimal | None = None
|
||||
fillable_available_ph: Decimal | None = None
|
||||
suggested_bid_btc_per_eh_day: Decimal | None = None
|
||||
suggested_overpay_btc_per_eh_day: Decimal | None = None
|
||||
last_price_btc_per_eh_day: Decimal | None = None
|
||||
total_hashrate_eh_s: Decimal | None = None
|
||||
available_hashrate_eh_s: Decimal | None = None
|
||||
|
|
|
|||
|
|
@ -48,7 +48,12 @@ def run_cycle(
|
|||
save_ocean_snapshot(conn, ocean)
|
||||
|
||||
if collect_braiins:
|
||||
fetcher = market_fetcher or BraiinsPublicClient().fetch_market_snapshot
|
||||
fetcher = market_fetcher or (
|
||||
lambda: BraiinsPublicClient().fetch_market_snapshot(
|
||||
target_ph=config.strategy.shadow_target_ph,
|
||||
overpay_btc_per_eh_day=config.strategy.shadow_overpay_btc_per_eh_day,
|
||||
)
|
||||
)
|
||||
market = fetcher()
|
||||
save_market_snapshot(conn, market)
|
||||
|
||||
|
|
|
|||
|
|
@ -39,10 +39,13 @@ def _market_lines(market: MarketSnapshot | None, stats: PriceStats) -> list[str]
|
|||
f" status: {market.status or 'unknown'}",
|
||||
f" best_bid_btc_per_eh_day: {_fmt(market.best_bid_btc_per_eh_day)}",
|
||||
f" best_ask_btc_per_eh_day: {_fmt(market.best_ask_btc_per_eh_day)}",
|
||||
f" fillable_target_ph: {_fmt(market.fillable_target_ph)}",
|
||||
f" fillable_price_btc_per_eh_day: {_fmt(market.fillable_price_btc_per_eh_day)}",
|
||||
f" suggested_bid_btc_per_eh_day: {_fmt(market.suggested_bid_btc_per_eh_day)}",
|
||||
f" last_price_btc_per_eh_day: {_fmt(market.last_price_btc_per_eh_day)}",
|
||||
f" available_hashrate_eh_s: {_fmt(market.available_hashrate_eh_s)}",
|
||||
f" sampled_price_count: {stats.count}",
|
||||
f" sampled_price_min_avg_max: {_fmt(stats.min_price)} / {_fmt(stats.avg_price)} / {_fmt(stats.max_price)}",
|
||||
f" sampled_strategy_price_count: {stats.count}",
|
||||
f" sampled_strategy_price_min_avg_max: {_fmt(stats.min_price)} / {_fmt(stats.avg_price)} / {_fmt(stats.max_price)}",
|
||||
]
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,11 @@ def init_db(conn: sqlite3.Connection) -> None:
|
|||
best_price_btc_per_eh_day TEXT,
|
||||
best_bid_btc_per_eh_day TEXT,
|
||||
best_ask_btc_per_eh_day TEXT,
|
||||
fillable_price_btc_per_eh_day TEXT,
|
||||
fillable_target_ph TEXT,
|
||||
fillable_available_ph TEXT,
|
||||
suggested_bid_btc_per_eh_day TEXT,
|
||||
suggested_overpay_btc_per_eh_day TEXT,
|
||||
last_price_btc_per_eh_day TEXT,
|
||||
total_hashrate_eh_s TEXT,
|
||||
available_hashrate_eh_s TEXT,
|
||||
|
|
@ -72,6 +77,11 @@ def _ensure_market_columns(conn: sqlite3.Connection) -> None:
|
|||
desired = {
|
||||
"best_bid_btc_per_eh_day": "TEXT",
|
||||
"best_ask_btc_per_eh_day": "TEXT",
|
||||
"fillable_price_btc_per_eh_day": "TEXT",
|
||||
"fillable_target_ph": "TEXT",
|
||||
"fillable_available_ph": "TEXT",
|
||||
"suggested_bid_btc_per_eh_day": "TEXT",
|
||||
"suggested_overpay_btc_per_eh_day": "TEXT",
|
||||
"last_price_btc_per_eh_day": "TEXT",
|
||||
"total_hashrate_eh_s": "TEXT",
|
||||
"status": "TEXT",
|
||||
|
|
@ -107,10 +117,12 @@ def save_market_snapshot(conn: sqlite3.Connection, snapshot: MarketSnapshot) ->
|
|||
"""
|
||||
INSERT INTO market_snapshots (
|
||||
timestamp_utc, best_price_btc_per_eh_day, best_bid_btc_per_eh_day,
|
||||
best_ask_btc_per_eh_day, last_price_btc_per_eh_day,
|
||||
best_ask_btc_per_eh_day, fillable_price_btc_per_eh_day,
|
||||
fillable_target_ph, fillable_available_ph, suggested_bid_btc_per_eh_day,
|
||||
suggested_overpay_btc_per_eh_day, last_price_btc_per_eh_day,
|
||||
total_hashrate_eh_s, available_hashrate_eh_s, status, source
|
||||
)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
snapshot.timestamp_utc,
|
||||
|
|
@ -123,6 +135,21 @@ def save_market_snapshot(conn: sqlite3.Connection, snapshot: MarketSnapshot) ->
|
|||
str(snapshot.best_ask_btc_per_eh_day)
|
||||
if snapshot.best_ask_btc_per_eh_day is not None
|
||||
else None,
|
||||
str(snapshot.fillable_price_btc_per_eh_day)
|
||||
if snapshot.fillable_price_btc_per_eh_day is not None
|
||||
else None,
|
||||
str(snapshot.fillable_target_ph)
|
||||
if snapshot.fillable_target_ph is not None
|
||||
else None,
|
||||
str(snapshot.fillable_available_ph)
|
||||
if snapshot.fillable_available_ph is not None
|
||||
else None,
|
||||
str(snapshot.suggested_bid_btc_per_eh_day)
|
||||
if snapshot.suggested_bid_btc_per_eh_day is not None
|
||||
else None,
|
||||
str(snapshot.suggested_overpay_btc_per_eh_day)
|
||||
if snapshot.suggested_overpay_btc_per_eh_day is not None
|
||||
else None,
|
||||
str(snapshot.last_price_btc_per_eh_day)
|
||||
if snapshot.last_price_btc_per_eh_day is not None
|
||||
else None,
|
||||
|
|
@ -167,7 +194,9 @@ def latest_market_snapshot(conn: sqlite3.Connection) -> MarketSnapshot | None:
|
|||
row = conn.execute(
|
||||
"""
|
||||
SELECT timestamp_utc, best_price_btc_per_eh_day, best_bid_btc_per_eh_day,
|
||||
best_ask_btc_per_eh_day, last_price_btc_per_eh_day,
|
||||
best_ask_btc_per_eh_day, fillable_price_btc_per_eh_day,
|
||||
fillable_target_ph, fillable_available_ph, suggested_bid_btc_per_eh_day,
|
||||
suggested_overpay_btc_per_eh_day, last_price_btc_per_eh_day,
|
||||
total_hashrate_eh_s, available_hashrate_eh_s, status, source
|
||||
FROM market_snapshots
|
||||
ORDER BY id DESC
|
||||
|
|
@ -183,11 +212,16 @@ def latest_market_snapshot(conn: sqlite3.Connection) -> MarketSnapshot | None:
|
|||
best_price_btc_per_eh_day=Decimal(row[1]) if row[1] else None,
|
||||
best_bid_btc_per_eh_day=Decimal(row[2]) if row[2] else None,
|
||||
best_ask_btc_per_eh_day=Decimal(row[3]) if row[3] else None,
|
||||
last_price_btc_per_eh_day=Decimal(row[4]) if row[4] else None,
|
||||
total_hashrate_eh_s=Decimal(row[5]) if row[5] else None,
|
||||
available_hashrate_eh_s=Decimal(row[6]) if row[6] else None,
|
||||
status=row[7],
|
||||
source=row[8],
|
||||
fillable_price_btc_per_eh_day=Decimal(row[4]) if row[4] else None,
|
||||
fillable_target_ph=Decimal(row[5]) if row[5] else None,
|
||||
fillable_available_ph=Decimal(row[6]) if row[6] else None,
|
||||
suggested_bid_btc_per_eh_day=Decimal(row[7]) if row[7] else None,
|
||||
suggested_overpay_btc_per_eh_day=Decimal(row[8]) if row[8] else None,
|
||||
last_price_btc_per_eh_day=Decimal(row[9]) if row[9] else None,
|
||||
total_hashrate_eh_s=Decimal(row[10]) if row[10] else None,
|
||||
available_hashrate_eh_s=Decimal(row[11]) if row[11] else None,
|
||||
status=row[12],
|
||||
source=row[13],
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
from decimal import Decimal
|
||||
import unittest
|
||||
|
||||
from braiins_ratchet.braiins import market_snapshot_from_public_api
|
||||
from braiins_ratchet.braiins import fillable_ask_for_target, market_snapshot_from_public_api
|
||||
|
||||
|
||||
class BraiinsPublicParserTests(unittest.TestCase):
|
||||
def test_public_snapshot_uses_best_ask_as_buy_reference(self) -> None:
|
||||
def test_public_snapshot_uses_depth_aware_suggested_bid_as_buy_reference(self) -> None:
|
||||
stats = {
|
||||
"status": "SPOT_INSTRUMENT_STATUS_ACTIVE",
|
||||
"last_avg_price_sat": 31000000,
|
||||
|
|
@ -14,18 +14,27 @@ class BraiinsPublicParserTests(unittest.TestCase):
|
|||
}
|
||||
orderbook = {
|
||||
"bids": [{"price_sat": 29000000}, {"price_sat": 28000000}],
|
||||
"asks": [{"price_sat": 30000000}, {"price_sat": 32000000}],
|
||||
"asks": [
|
||||
{"price_sat": 30000000, "hash_rate_available_ph": 4},
|
||||
{"price_sat": 32000000, "hash_rate_available_ph": 10},
|
||||
],
|
||||
}
|
||||
|
||||
snapshot = market_snapshot_from_public_api(
|
||||
stats,
|
||||
orderbook,
|
||||
timestamp_utc="2026-04-25T12:00:00+00:00",
|
||||
target_ph=Decimal("10"),
|
||||
overpay_btc_per_eh_day=Decimal("0.01"),
|
||||
)
|
||||
|
||||
self.assertEqual(snapshot.best_price_btc_per_eh_day, Decimal("0.3"))
|
||||
self.assertEqual(snapshot.best_price_btc_per_eh_day, Decimal("0.33"))
|
||||
self.assertEqual(snapshot.best_bid_btc_per_eh_day, Decimal("0.29"))
|
||||
self.assertEqual(snapshot.best_ask_btc_per_eh_day, Decimal("0.3"))
|
||||
self.assertEqual(snapshot.fillable_price_btc_per_eh_day, Decimal("0.32"))
|
||||
self.assertEqual(snapshot.fillable_target_ph, Decimal("10"))
|
||||
self.assertEqual(snapshot.fillable_available_ph, Decimal("14"))
|
||||
self.assertEqual(snapshot.suggested_bid_btc_per_eh_day, Decimal("0.33"))
|
||||
self.assertEqual(snapshot.last_price_btc_per_eh_day, Decimal("0.31"))
|
||||
self.assertEqual(snapshot.total_hashrate_eh_s, Decimal("0.25"))
|
||||
self.assertEqual(snapshot.available_hashrate_eh_s, Decimal("0.21"))
|
||||
|
|
@ -42,6 +51,38 @@ class BraiinsPublicParserTests(unittest.TestCase):
|
|||
self.assertEqual(snapshot.best_bid_btc_per_eh_day, Decimal("0.29"))
|
||||
self.assertIsNone(snapshot.best_ask_btc_per_eh_day)
|
||||
|
||||
def test_fillable_ask_uses_limit_minus_used_when_available_absent(self) -> None:
|
||||
depth = fillable_ask_for_target(
|
||||
[
|
||||
{"price_sat": 30000000, "hash_rate_limit_ph": 5, "hash_rate_matched_ph": 3},
|
||||
{"price_sat": 31000000, "hash_rate_limit_ph": 8, "hash_rate_matched_ph": 1},
|
||||
],
|
||||
Decimal("6"),
|
||||
)
|
||||
|
||||
self.assertEqual(depth.price_btc_per_eh_day, Decimal("0.31"))
|
||||
self.assertEqual(depth.available_ph, Decimal("9"))
|
||||
|
||||
def test_fillable_ask_supports_public_camel_case_orderbook(self) -> None:
|
||||
depth = fillable_ask_for_target(
|
||||
[
|
||||
{
|
||||
"price_sat": 46090000,
|
||||
"hashRateAvailable": 106,
|
||||
"hashRateMatched": 106,
|
||||
},
|
||||
{
|
||||
"price_sat": 47486000,
|
||||
"hashRateAvailable": 576,
|
||||
"hashRateMatched": 182,
|
||||
},
|
||||
],
|
||||
Decimal("10"),
|
||||
)
|
||||
|
||||
self.assertEqual(depth.price_btc_per_eh_day, Decimal("0.47486"))
|
||||
self.assertEqual(depth.available_ph, Decimal("394"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
|||
|
|
@ -57,6 +57,8 @@ def _config() -> AppConfig:
|
|||
target_duration_minutes=180,
|
||||
target_spend_btc=Decimal("0.00010"),
|
||||
risk_lambda=Decimal("0.35"),
|
||||
shadow_target_ph=Decimal("10"),
|
||||
shadow_overpay_btc_per_eh_day=Decimal("0.01"),
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@ class StorageTests(unittest.TestCase):
|
|||
best_price_btc_per_eh_day=Decimal("0.30"),
|
||||
best_bid_btc_per_eh_day=Decimal("0.29"),
|
||||
best_ask_btc_per_eh_day=Decimal("0.30"),
|
||||
fillable_price_btc_per_eh_day=Decimal("0.32"),
|
||||
fillable_target_ph=Decimal("10"),
|
||||
fillable_available_ph=Decimal("14"),
|
||||
suggested_bid_btc_per_eh_day=Decimal("0.33"),
|
||||
suggested_overpay_btc_per_eh_day=Decimal("0.01"),
|
||||
last_price_btc_per_eh_day=Decimal("0.31"),
|
||||
total_hashrate_eh_s=Decimal("0.25"),
|
||||
available_hashrate_eh_s=Decimal("0.21"),
|
||||
|
|
@ -31,6 +36,11 @@ class StorageTests(unittest.TestCase):
|
|||
self.assertEqual(snapshot.best_price_btc_per_eh_day, Decimal("0.30"))
|
||||
self.assertEqual(snapshot.best_bid_btc_per_eh_day, Decimal("0.29"))
|
||||
self.assertEqual(snapshot.best_ask_btc_per_eh_day, Decimal("0.30"))
|
||||
self.assertEqual(snapshot.fillable_price_btc_per_eh_day, Decimal("0.32"))
|
||||
self.assertEqual(snapshot.fillable_target_ph, Decimal("10"))
|
||||
self.assertEqual(snapshot.fillable_available_ph, Decimal("14"))
|
||||
self.assertEqual(snapshot.suggested_bid_btc_per_eh_day, Decimal("0.33"))
|
||||
self.assertEqual(snapshot.suggested_overpay_btc_per_eh_day, Decimal("0.01"))
|
||||
self.assertEqual(snapshot.last_price_btc_per_eh_day, Decimal("0.31"))
|
||||
self.assertEqual(snapshot.total_hashrate_eh_s, Decimal("0.25"))
|
||||
self.assertEqual(snapshot.available_hashrate_eh_s, Decimal("0.21"))
|
||||
|
|
|
|||
|
|
@ -70,6 +70,8 @@ def _config() -> AppConfig:
|
|||
target_duration_minutes=180,
|
||||
target_spend_btc=Decimal("0.00010"),
|
||||
risk_lambda=Decimal("0.35"),
|
||||
shadow_target_ph=Decimal("10"),
|
||||
shadow_overpay_btc_per_eh_day=Decimal("0.01"),
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue