"""Constants for RL BTC v4.""" from __future__ import annotations from dataclasses import dataclass from pathlib import Path import pandas as pd REPO_ROOT = Path(__file__).resolve().parents[1] DEFAULT_DATA_PATH = ( REPO_ROOT / "binance-experiment" / "research_datasets" / "btc_updown_5m_augmented_v1.parquet" ) STARTING_CASH = 50.0 DRAWDOWN_LIMIT = 0.40 DEFAULT_HISTORY_LENGTH = 30 # number of past observations to include in state DEFAULT_EPISODE_SPAN_DAYS = 30 DEFAULT_EPISODE_STRIDE_DAYS = 15 # overlapping episodes for more data DEFAULT_REBALANCE_TOLERANCE = 0.05 # ── Fee schedule (Crypto / BTC prediction markets) ──────────────────────── # Taker fee in basis points (0.072 = 7.2 bps). # Maker fee = 0 (not modelled — all orders assumed market/taker). # Fee formula: fee_usdc = shares × fee_rate × price × (1 - price) # Quadratic in price — highest near 0.50 (max uncertainty), zero at 0/1. # Maker rebate (20%) is not applied since we model taker orders. TAKER_FEE_RATE = 0.072 # ── Action space ────────────────────────────────────────────────────────────── @dataclass(frozen=True) class ActionSpec: id: int name: str kind: str # "hold" | "target" | "reduce" target_side: str | None # "YES" | "NO" | None target_fraction: float # target exposure as fraction of equity ACTIONS: tuple[ActionSpec, ...] = ( ActionSpec(0, "HOLD", "hold", None, 0.00), ActionSpec(1, "FLAT", "target", None, 0.00), ActionSpec(2, "YES_10", "target", "YES", 0.10), ActionSpec(3, "YES_25", "target", "YES", 0.25), ActionSpec(4, "YES_50", "target", "YES", 0.50), ActionSpec(5, "NO_10", "target", "NO", 0.10), ActionSpec(6, "NO_25", "target", "NO", 0.25), ActionSpec(7, "NO_50", "target", "NO", 0.50), ) ACTION_INDEX_BY_NAME: dict[str, int] = {s.name: s.id for s in ACTIONS} N_ACTIONS: int = len(ACTIONS) # ── Feature columns ─────────────────────────────────────────────────────────── MARKET_FEATURE_COLUMNS: list[str] = [ "obs_pos", "seconds_since_open", "seconds_to_close", "market_progress", "yes_bid_validated", "yes_ask_validated", "yes_mid_validated", "yes_spread_validated", "no_bid_validated", "no_ask_validated", "no_mid_validated", "no_spread_validated", "microprice_bias", "abs_yes_mid_distance_from_even", "price_return_from_open", "mark_return_from_open_filled", "index_return_from_open_filled", "abs_mark_return_from_open_filled", "rolling_vol_15m", "rolling_vol_60m", "taker_buy_ratio", "buy_sell_imbalance", "rolling_volume_15m", "rolling_num_trades_15m", "volume", "num_trades", "return_over_vol_15m", "return_over_vol_60m", "signed_move_x_time_remaining", "imbalance_x_vol", "funding_rate", "funding_rate_prev", "oi_delta_5m", "oi_delta_15m", "oi_delta_60m", "long_short_ratio", "hour_sin", "hour_cos", ] PORTFOLIO_FEATURE_COLUMNS: list[str] = [ "cash_fraction", "equity_fraction", "drawdown_fraction", "position_side", "position_fraction", "position_shares", "avg_entry_price", "unrealized_pnl_fraction", "steps_held_fraction", ] # Normalisation stats will be fitted on training data