"""NautilusTrader Polymarket execution layer for HK weather prediction markets. Key characteristics per Polymarket + Nautilus docs: - BinaryOption instruments (outcome tokens, pay 0 or 1 at resolution) - pUSD collateral, 6 decimals, no leverage - Tick sizes: 0.001 to 0.1 (dictates price + size precision) - Market orders: FAK (IOC) or FOK only; GTC/GTD for resting limits - Market BUY must use quote_quantity=True (pUSD notional) - SELL quantities truncated to 2 decimal places - Weather category: 0.05% taker fee + 25% maker rebate - Recommended: 30s disconnection timeout + 30s post-stop delay """ import os from dataclasses import dataclass from typing import Optional from dotenv import load_dotenv load_dotenv() from nautilus_trader.adapters.polymarket.common.constants import POLYMARKET_VENUE from nautilus_trader.adapters.polymarket.config import PolymarketDataClientConfig, PolymarketExecClientConfig from nautilus_trader.common.config import InstrumentProviderConfig from nautilus_trader.config import TradingNodeConfig @dataclass class WeatherMarketConfig: """Configuration for the weather prediction market strategy.""" # Credentials private_key: str = "" funder: str = "" api_key: str = "" api_secret: str = "" passphrase: str = "" signature_type: int = 0 # 0=EOA, 3=DepositWallet # Strategy bankroll_pusd: float = 1000.0 min_edge_bps: int = 200 max_position_per_market_pusd: float = 500.0 kelly_fraction: float = 0.25 forecast_interval_mins: int = 360 # 6h # Market discovery search_tags: tuple = ("weather", "temperature", "hong kong", "typhoon", "precipitation", "climate") min_liquidity_usdc: float = 100.0 # Risk disconnect_timeout_secs: int = 30 post_stop_delay_secs: int = 30 @classmethod def from_env(cls) -> "WeatherMarketConfig": return cls( private_key=os.getenv("POLYMARKET_PK", ""), funder=os.getenv("POLYMARKET_FUNDER", ""), api_key=os.getenv("POLYMARKET_API_KEY", ""), api_secret=os.getenv("POLYMARKET_API_SECRET", ""), passphrase=os.getenv("POLYMARKET_PASSPHRASE", ""), signature_type=int(os.getenv("POLYMARKET_SIGNATURE_TYPE", "0")), bankroll_pusd=float(os.getenv("BANKROLL_PUSD", "1000.0")), min_edge_bps=int(os.getenv("MIN_EDGE_BPS", "200")), kelly_fraction=float(os.getenv("KELLY_FRACTION", "0.25")), ) def _env_or_none(key: str) -> str | None: """Get env var or None if not set.""" val = os.getenv(key, "") return val if val else None def build_data_client_config(cfg: WeatherMarketConfig) -> PolymarketDataClientConfig: """Build Polymarket market data client configuration.""" return PolymarketDataClientConfig( venue=POLYMARKET_VENUE, private_key=cfg.private_key or _env_or_none("POLYMARKET_PK"), funder=cfg.funder or _env_or_none("POLYMARKET_FUNDER"), api_key=cfg.api_key or _env_or_none("POLYMARKET_API_KEY"), api_secret=cfg.api_secret or _env_or_none("POLYMARKET_API_SECRET"), passphrase=cfg.passphrase or _env_or_none("POLYMARKET_PASSPHRASE"), signature_type=cfg.signature_type, instrument_provider=InstrumentProviderConfig(load_ids=[]), update_instrument_interval_mins=30, ) def build_exec_client_config(cfg: WeatherMarketConfig) -> PolymarketExecClientConfig: """Build Polymarket execution client configuration.""" return PolymarketExecClientConfig( venue=POLYMARKET_VENUE, private_key=cfg.private_key or _env_or_none("POLYMARKET_PK"), funder=cfg.funder or _env_or_none("POLYMARKET_FUNDER"), api_key=cfg.api_key or _env_or_none("POLYMARKET_API_KEY"), api_secret=cfg.api_secret or _env_or_none("POLYMARKET_API_SECRET"), passphrase=cfg.passphrase or _env_or_none("POLYMARKET_PASSPHRASE"), signature_type=cfg.signature_type, max_retries=3, retry_delay=1.0, instrument_provider=InstrumentProviderConfig(load_ids=[]), ) def build_node_config(cfg: WeatherMarketConfig) -> TradingNodeConfig: """Build TradingNode configuration for Polymarket + weather strategy.""" return TradingNodeConfig( timeout_disconnection=cfg.disconnect_timeout_secs, timeout_post_stop=cfg.post_stop_delay_secs, timeout_reconciliation=30.0, )