Files
ramseshk 533939d178 Add NautilusTrader Polymarket execution layer
- Full NautilusTrader integration using BinaryOption instruments
- Polymarket CLOB data client (L2 order book, WebSocket deltas)
- Polymarket CLOB execution client (limit orders, market orders, batch ops)
- PolymarketWeatherStrategy with auto market discovery, order book subscription,
  weather model signal generation, Kelly sizing, and order placement
- Proper Polymarket precision: tick sizes, GTC/GTD limit orders, FAK/IOC market orders
- Weather category fee model (0.05% taker, 25% maker rebate)
- Paper trading mode (real market data, simulated execution)
- Live trading mode with PK/funder/env credential support
- 30s disconnection timeout + 30s post-stop delay per Polymarket docs

Run: python -m execution.runner --paper
2026-08-10 17:29:23 +08:00

161 lines
5.5 KiB
Python

#!/usr/bin/env python3
"""
NautilusTrader Live Runner for HK Weather Prediction Market Strategy.
Usage:
# Paper trading (real market data, simulated execution)
python -m execution.runner --paper
# Live trading with real Polymarket CLOB
python -m execution.runner --live
"""
import argparse
import asyncio
import signal
import sys
from datetime import datetime
sys.path.insert(0, "/home/satoshi/hk-weather-mkt")
from dotenv import load_dotenv
load_dotenv()
from nautilus_trader.adapters.polymarket.factories import (
PolymarketLiveDataClientFactory,
PolymarketLiveExecClientFactory,
)
from nautilus_trader.config import (
TradingNodeConfig,
ImportableStrategyConfig,
LiveDataEngineConfig,
LiveExecEngineConfig,
)
from nautilus_trader.live.node import TradingNode
from nautilus_trader.model.identifiers import TraderId
from execution import WeatherMarketConfig, build_data_client_config, build_exec_client_config, build_node_config
from execution.strategy import PolymarketWeatherStrategyConfig
class WeatherMarketRunner:
def __init__(self, cfg: WeatherMarketConfig, live: bool = False):
self.cfg = cfg
self.live = live
self.node: TradingNode | None = None
self._running = False
async def run(self):
mode = "LIVE" if self.live else "PAPER"
print("=" * 60)
print(f" HK Weather Prediction Market — {mode} TRADING")
print(f" Bankroll: ${self.cfg.bankroll_pusd:.2f} pUSD")
print(f" Min edge: {self.cfg.min_edge_bps} bps")
print(f" Kelly fraction: {self.cfg.kelly_fraction}")
if self.live:
print(f" Funder: {self.cfg.funder or '(env)'}")
print(" ⚠ REAL FUNDS WILL BE USED ⚠")
print("=" * 60)
if self.live and not self.cfg.private_key:
print("\nERROR: POLYMARKET_PK not set. Cannot trade live.")
print("Use --paper for paper trading.\n")
return
data_config = build_data_client_config(self.cfg)
exec_config = build_exec_client_config(self.cfg) if self.live else data_config
node_config = build_node_config(self.cfg)
strategy_config = ImportableStrategyConfig(
strategy_path="execution.strategy:PolymarketWeatherStrategy",
config_path="execution.strategy:PolymarketWeatherStrategyConfig",
config=PolymarketWeatherStrategyConfig(
bankroll_pusd=self.cfg.bankroll_pusd,
min_edge_bps=self.cfg.min_edge_bps,
max_position_per_market_pusd=self.cfg.max_position_per_market_pusd,
kelly_fraction=self.cfg.kelly_fraction,
forecast_interval_mins=self.cfg.forecast_interval_mins,
search_tags=self.cfg.search_tags,
min_liquidity_usdc=self.cfg.min_liquidity_usdc,
),
)
self.node = TradingNode(
config=TradingNodeConfig(
trader_id=TraderId("HKWEATHER-001"),
data_clients={
"POLYMARKET": (PolymarketLiveDataClientFactory, data_config),
},
exec_clients={
"POLYMARKET": (PolymarketLiveExecClientFactory, exec_config),
},
strategies=[strategy_config],
timeout_disconnection=node_config.timeout_disconnection,
timeout_post_stop=node_config.timeout_post_stop,
timeout_reconciliation=30.0,
)
)
self._setup_signals()
self._running = True
try:
print(f"\nStarting {mode.lower()} trading node...")
print("Press Ctrl+C to stop\n")
await self.node.start()
while self._running:
await asyncio.sleep(1)
except asyncio.CancelledError:
pass
finally:
await self._cleanup()
def _setup_signals(self):
loop = asyncio.get_event_loop()
def shutdown(sig, frame):
print(f"\nReceived signal {sig}, shutting down...")
self._running = False
if self.node:
asyncio.create_task(self._stop_node())
for sig in (signal.SIGINT, signal.SIGTERM):
try:
loop.add_signal_handler(sig, lambda s=sig: shutdown(s, None))
except NotImplementedError:
signal.signal(sig, lambda s, f: shutdown(s, f))
async def _stop_node(self):
try:
if self.node:
await self.node.stop()
except Exception as e:
print(f"Error during shutdown: {e}")
async def _cleanup(self):
self._running = False
print(f"\n[{datetime.now():%H:%M:%S}] Runner stopped.")
def main():
parser = argparse.ArgumentParser(description="HK Weather Prediction Market — NautilusTrader Runner")
parser.add_argument("--paper", action="store_true", default=True, help="Paper trading mode [default]")
parser.add_argument("--live", action="store_true", help="Live trading on Polymarket CLOB")
parser.add_argument("--bankroll", type=float, default=None)
parser.add_argument("--edge", type=int, default=None)
parser.add_argument("--kelly", type=float, default=None)
args = parser.parse_args()
cfg = WeatherMarketConfig.from_env()
if args.bankroll: cfg.bankroll_pusd = args.bankroll
if args.edge: cfg.min_edge_bps = args.edge
if args.kelly: cfg.kelly_fraction = args.kelly
runner = WeatherMarketRunner(cfg, live=args.live)
asyncio.run(runner.run())
if __name__ == "__main__":
main()