trading/services/kevin_signal_bridge/risk_counters.py
Viktor Barzin a417cae77b
Some checks failed
ci/woodpecker/push/woodpecker Pipeline was canceled
feat(kevin_bridge): blocklist + daily risk counters
2026-05-24 01:01:54 +00:00

54 lines
1.8 KiB
Python

"""Daily Redis counters for trade-cap + alloc-cap + pause flag."""
from __future__ import annotations
from datetime import datetime, timezone
from decimal import Decimal
from typing import Any
class KevinRiskCounters:
_PAUSE_KEY = "trading:paused"
_TRADES_KEY = "kevin:daily_trades:{date}"
_ALLOC_KEY = "kevin:daily_alloc:{date}"
def __init__(self, redis: Any) -> None:
self.redis = redis
@staticmethod
def _today_utc() -> str:
return datetime.now(timezone.utc).strftime("%Y%m%d")
async def get_daily_trades(self) -> int:
v = await self.redis.get(self._TRADES_KEY.format(date=self._today_utc()))
if v is None:
return 0
return int(v)
async def increment_daily_trades(self) -> int:
key = self._TRADES_KEY.format(date=self._today_utc())
n = await self.redis.incr(key)
await self.redis.expire(key, 172800) # 48h
return int(n)
async def get_daily_alloc(self) -> Decimal:
v = await self.redis.get(self._ALLOC_KEY.format(date=self._today_utc()))
if v is None:
return Decimal("0")
s = v.decode() if isinstance(v, bytes) else str(v)
return Decimal(s)
async def add_daily_alloc(self, usd: Decimal) -> Decimal:
key = self._ALLOC_KEY.format(date=self._today_utc())
new = await self.redis.incrbyfloat(key, float(usd))
await self.redis.expire(key, 172800)
return Decimal(str(new))
async def is_trading_paused(self) -> bool:
return bool(await self.redis.get(self._PAUSE_KEY))
async def set_trading_paused(self, ttl_seconds: int | None = None) -> None:
if ttl_seconds:
await self.redis.set(self._PAUSE_KEY, "1", ex=ttl_seconds)
else:
await self.redis.set(self._PAUSE_KEY, "PERMANENT")