Proper Avellaneda-Stoikov: reservation price + optimal spread model
This commit is contained in:
+54
-11
@@ -344,6 +344,11 @@ async def main():
|
||||
STRATEGIES[strat]["fee_paid"] += abs(fee)
|
||||
if closed_pnl > 0:
|
||||
STRATEGIES[strat]["wins"] += 1
|
||||
# Track position for AS model
|
||||
if side == "B":
|
||||
STRATEGIES[strat]["position"] = STRATEGIES[strat].get("position", 0.0) + sz
|
||||
else:
|
||||
STRATEGIES[strat]["position"] = STRATEGIES[strat].get("position", 0.0) - sz
|
||||
STRATEGIES[strat]["pnl_pct"] = STRATEGIES[strat]["pnl"] / STRATEGIES[strat]["allocation"] * 100
|
||||
strategy_equity[strat].append({"t": time.time(), "v": STRATEGIES[strat]["allocation"] + STRATEGIES[strat]["pnl"]})
|
||||
if len(strategy_equity[strat]) > 1000:
|
||||
@@ -420,20 +425,58 @@ async def main():
|
||||
if has_position:
|
||||
continue # Don't replace existing orders
|
||||
|
||||
# Avellaneda-Stoikov: DUAL-SIDED (always active)
|
||||
# Avellaneda-Stoikov: proper optimal control (reservation price + spread)
|
||||
if name == "Avellaneda-Stoikov":
|
||||
cid_bid = ClientOrderId(str(UUID4()))
|
||||
cid_ask = ClientOrderId(str(UUID4()))
|
||||
try:
|
||||
client.submit_order(instrument_id=perp.id, client_order_id=cid_bid, order_side=OrderSide.BUY, order_type=OrderType.LIMIT, quantity=Quantity.from_str(str(cfg["size"])), price=Price.from_str(str(int(bid))), time_in_force=TimeInForce.GTC, post_only=True)
|
||||
client.submit_order(instrument_id=perp.id, client_order_id=cid_ask, order_side=OrderSide.SELL, order_type=OrderType.LIMIT, quantity=Quantity.from_str(str(cfg["size"])), price=Price.from_str(str(int(ask))), time_in_force=TimeInForce.GTC, post_only=True)
|
||||
if tick % 60 == 0:
|
||||
log.info(f"[Avel] DUAL: BID {cfg['size']} @ ${int(bid):,} | ASK {cfg['size']} @ ${int(ask):,}")
|
||||
active_cloids[name] = str(cid_bid)
|
||||
active_cloids_times[name] = tick
|
||||
active_cloids_px[name] = bid
|
||||
from strategies.as_quoter import ASQuoter
|
||||
if "_as_quoter" not in dir():
|
||||
globals()["_as_quoter"] = ASQuoter(
|
||||
gamma=0.1, k=1.5, tau=1.0,
|
||||
min_spread=0.0001, max_inventory=cfg["size"] * 5,
|
||||
)
|
||||
q = ASQuoter
|
||||
asq = globals()["_as_quoter"]
|
||||
asq.observe(mid)
|
||||
|
||||
# Get A-S inventory from position tracking
|
||||
as_inv = STRATEGIES[name].get("position", 0.0)
|
||||
elapsed = (tick * 1.0) % (asq.tau * 3600) / 3600.0 # 1-hour virtual sessions
|
||||
|
||||
result = asq.quotes(mid, as_inv, elapsed)
|
||||
if result is None:
|
||||
continue # Circuit breaker active — skip this tick
|
||||
|
||||
r_price = result["reservation"]
|
||||
as_bid = int(result["bid"])
|
||||
as_ask = int(result["ask"])
|
||||
# Clamp: never cross the market
|
||||
as_bid = min(as_bid, int(bid))
|
||||
as_ask = max(as_ask, int(ask))
|
||||
|
||||
cid_bid = ClientOrderId(str(UUID4()))
|
||||
cid_ask = ClientOrderId(str(UUID4()))
|
||||
try:
|
||||
client.submit_order(instrument_id=perp.id, client_order_id=cid_bid, order_side=OrderSide.BUY, order_type=OrderType.LIMIT, quantity=Quantity.from_str(str(cfg["size"])), price=Price.from_str(str(as_bid)), time_in_force=TimeInForce.GTC, post_only=True)
|
||||
client.submit_order(instrument_id=perp.id, client_order_id=cid_ask, order_side=OrderSide.SELL, order_type=OrderType.LIMIT, quantity=Quantity.from_str(str(cfg["size"])), price=Price.from_str(str(as_ask)), time_in_force=TimeInForce.GTC, post_only=True)
|
||||
if tick % 60 == 0:
|
||||
log.info(f"[AS] r={r_price:.1f} σ={asq.sigma*100:.2f}% BID {cfg['size']} @ ${as_bid:,} | ASK {cfg['size']} @ ${as_ask:,} (spread ${as_ask - as_bid:,})")
|
||||
active_cloids[name] = str(cid_bid)
|
||||
active_cloids_times[name] = tick
|
||||
active_cloids_px[name] = as_bid
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
# Fallback: best bid/ask if module unavailable
|
||||
cid_bid = ClientOrderId(str(UUID4()))
|
||||
cid_ask = ClientOrderId(str(UUID4()))
|
||||
try:
|
||||
client.submit_order(instrument_id=perp.id, client_order_id=cid_bid, order_side=OrderSide.BUY, order_type=OrderType.LIMIT, quantity=Quantity.from_str(str(cfg["size"])), price=Price.from_str(str(int(bid))), time_in_force=TimeInForce.GTC, post_only=True)
|
||||
client.submit_order(instrument_id=perp.id, client_order_id=cid_ask, order_side=OrderSide.SELL, order_type=OrderType.LIMIT, quantity=Quantity.from_str(str(cfg["size"])), price=Price.from_str(str(int(ask))), time_in_force=TimeInForce.GTC, post_only=True)
|
||||
active_cloids[name] = str(cid_bid)
|
||||
active_cloids_times[name] = tick
|
||||
active_cloids_px[name] = bid
|
||||
except Exception:
|
||||
pass
|
||||
continue
|
||||
|
||||
# For signal-driven strategies: use aggressive offset
|
||||
|
||||
Reference in New Issue
Block a user