Files
polymarket_arbitrage/tests/test_risk_gate.py
T
gavindiaz 9259325d8a
deploy GitHub Pages / deploy (push) Has been cancelled
tests / test (push) Has been cancelled
first commit
2026-07-22 18:53:44 +08:00

100 lines
3.0 KiB
Python

"""Live executor risk gate tests — no py-clob-client required."""
from __future__ import annotations
from datetime import UTC, datetime
from decimal import Decimal as D
from pathlib import Path
import pytest
from arbitrage.db import db_conn
from arbitrage.engine.live_executor import RiskDenied, RiskLimits, risk_gate
from arbitrage.models import Event, Opportunity, OpportunityLeg, Outcome
def _opportunity(cost_per_share: D, size: D) -> Opportunity:
ev = Event(
id="e",
slug="e",
title="t",
is_neg_risk=True,
end_date=None,
outcomes=(
Outcome(token_id="A", name="A", outcome_index=0),
Outcome(token_id="B", name="B", outcome_index=1),
),
)
return Opportunity.from_legs(
detected_at=datetime.now(UTC),
event=ev,
legs=(
OpportunityLeg(
token_id="A",
outcome_name="A",
outcome_index=0,
vwap_price=cost_per_share / D(2),
size=size,
levels_consumed=1,
),
OpportunityLeg(
token_id="B",
outcome_name="B",
outcome_index=1,
vwap_price=cost_per_share / D(2),
size=size,
levels_consumed=1,
),
),
fees_per_share=D("0"),
gas_per_basket_usd=D("0.10"),
max_baskets=size,
)
def _limits(**overrides) -> RiskLimits:
base = dict(
max_basket_usd=D("200"),
max_open_baskets=3,
max_open_baskets_per_event=1,
daily_loss_stop_usd=D("100"),
kill_switch_file=Path("/nope/does-not-exist"),
)
base.update(overrides)
return RiskLimits(**base)
@pytest.mark.asyncio
async def test_risk_gate_accepts_within_limits(db) -> None:
opp = _opportunity(D("0.90"), D("100")) # cost = $90
await risk_gate(opp, _limits())
@pytest.mark.asyncio
async def test_risk_gate_rejects_oversized_basket(db) -> None:
opp = _opportunity(D("0.90"), D("1000")) # cost = $900
with pytest.raises(RiskDenied, match="basket cost"):
await risk_gate(opp, _limits(max_basket_usd=D("500")))
@pytest.mark.asyncio
async def test_risk_gate_rejects_when_kill_switch_present(db, tmp_path) -> None:
kill = tmp_path / "KILL"
kill.touch()
opp = _opportunity(D("0.90"), D("100"))
with pytest.raises(RiskDenied, match="kill switch"):
await risk_gate(opp, _limits(kill_switch_file=kill))
@pytest.mark.asyncio
async def test_risk_gate_rejects_daily_loss_stop(db) -> None:
async with db_conn() as conn:
today = datetime.now(UTC).date().isoformat()
await conn.execute(
"INSERT INTO daily_pnl (date, live_pnl_usd) VALUES (?, ?)",
(today, "-150.00"),
)
await conn.commit()
opp = _opportunity(D("0.90"), D("100"))
with pytest.raises(RiskDenied, match="daily loss stop"):
await risk_gate(opp, _limits(daily_loss_stop_usd=D("100")))