"""GLFT Market Maker on Polymarket (paper mode)."""
import math
import horizon as hz
from horizon.context import FeedData
def black_scholes_binary(price: float, strike: float, vol: float, tte: float) -> float:
"""Binary option fair value: Phi(d2)."""
if tte <= 0:
return 1.0 if price >= strike else 0.0
d2 = (math.log(price / strike) - 0.5 * vol**2 * tte) / (vol * math.sqrt(tte))
return 0.5 * (1 + math.erf(d2 / math.sqrt(2)))
def spread_toxicity(bid: float, ask: float) -> float:
"""Spread-based toxicity proxy: wider spread = more toxic flow."""
if bid <= 0 or ask <= 0:
return 0.5
mid = (bid + ask) / 2.0
if mid <= 0:
return 0.5
relative_spread = (ask - bid) / mid
return min(1.0, max(0.0, relative_spread * 100.0))
def glft_spread(fair: float, tox: float, inventory: float, gamma: float = 0.1) -> float:
"""GLFT adverse selection spread with inventory penalty."""
base = 0.02
inv_penalty = gamma * abs(inventory) * 0.001
tox_adj = tox * 0.04
return base + inv_penalty + tox_adj
# --- Pipeline functions ---
def fair_value(ctx: hz.Context) -> float:
btc_price = ctx.feeds.get("btc", FeedData()).price or 100_000
strike = 100_000
vol = 0.6
tte = 30 / 365 # 30 days to expiry
return black_scholes_binary(btc_price, strike, vol, tte)
def toxicity(ctx: hz.Context) -> float:
feed = ctx.feeds.get("btc", FeedData())
return spread_toxicity(feed.bid, feed.ask)
def quoter(ctx: hz.Context, fair: float, tox: float) -> list[hz.Quote]:
spread = glft_spread(fair, tox, ctx.inventory.net, gamma=0.1)
return hz.quotes(fair, spread, size=5)
if __name__ == "__main__":
hz.run(
name="btc_mm",
exchange=hz.Polymarket(private_key="0x_demo_key"),
markets=["will-btc-hit-100k-by-end-of-2025"],
feeds={"btc": hz.BinanceWS("btcusdt")},
pipeline=[fair_value, toxicity, quoter],
risk=hz.Risk(max_position=100, max_drawdown_pct=5),
interval=0.5,
mode="paper",
dashboard=False,
)