Files
ramseshk b59dcc3629 Initial project scaffold: five quant strategies for Hyperliquid Testnet
Set up the directory structure and wrote placeholder logic for:

- Order Book Imbalance: trades on L2 bid/ask skew
- Iceberg/TWAP detection: follows whale accumulation patterns
- Funding rate arbitrage: delta-neutral carry on perp funding
- Pairs trading: BTC/ETH spread mean reversion
- Avellaneda-Stoikov market making: optimal bid/ask quoting

Also added shared risk manager, portfolio tracker, and a plain-language strategy walkthrough in docs/.
2026-08-03 11:12:20 +00:00

88 lines
2.8 KiB
Python

"""
Avellaneda-Stoikov Market Making strategy.
A mathematical model for optimal market making based on
stochastic optimal control. Computes optimal bid/ask quotes
considering current inventory, risk aversion, volatility,
and time horizon.
Key formulas:
Reservation price: r = s - q * gamma * sigma^2 * tau
Optimal spread: delta = gamma * sigma^2 * tau + (2/gamma) * ln(1 + gamma/k)
where:
s = mid price, q = inventory, gamma = risk aversion
sigma = volatility, tau = remaining time, k = order intensity
"""
import math
from nautilus_trader.trading.strategy import Strategy
from nautilus_trader.config import StrategyConfig
from datetime import datetime, timezone
class AvellanedaStoikovConfig(StrategyConfig, frozen=True):
instrument_id: str
gamma: float = 0.1
sigma: float = 0.02
T: float = 1.0
k: float = 1.5
min_spread: float = 0.0001
max_inventory: float = 0.01
class AvellanedaStoikov(Strategy):
"""
A-S optimal market making.
Instead of predicting direction, this strategy provides
liquidity by continuously quoting bid/ask prices at an
optimal distance from the mid price. The spread widens
as inventory builds up (to discourage further accumulation)
and tightens as the time horizon approaches.
"""
def __init__(self, config: AvellanedaStoikovConfig) -> None:
super().__init__(config)
self.config = config
self.start_time: datetime | None = None
def on_start(self) -> None:
self.start_time = self.clock.utc_now()
self.subscribe_quote_ticks(self.config.instrument_id)
self.log.info(
f"A-S MM on {self.config.instrument_id} "
f"(gamma={self.config.gamma})"
)
def on_quote_tick(self, tick) -> None:
self.cancel_all_orders(self.config.instrument_id)
elapsed = (self.clock.utc_now() - self.start_time).total_seconds() / 3600
tau = max(self.config.T - elapsed, 0.01)
q = float(self.portfolio.net_position(self.config.instrument_id))
if abs(q) >= self.config.max_inventory:
return
g = self.config.gamma
s = self.config.sigma
k = self.config.k
mid = (tick.bid + tick.ask) / 2
reservation = mid - q * g * s**2 * tau
spread = g * s**2 * tau + (2 / g) * math.log(1 + g / k)
spread = max(spread, self.config.min_spread)
self.submit_order(self.order_factory.limit(
instrument_id=self.config.instrument_id,
order_side="BUY",
quantity=self.config.max_inventory / 10,
price=reservation - spread / 2,
))
self.submit_order(self.order_factory.limit(
instrument_id=self.config.instrument_id,
order_side="SELL",
quantity=self.config.max_inventory / 10,
price=reservation + spread / 2,
))