Persist demo account in database

Этот коммит содержится в:
Виктор
2026-05-08 03:33:20 +09:00
родитель 5645b6cf93
Коммит 871a81e6b9
+61 -30
Просмотреть файл
@@ -1,8 +1,13 @@
from datetime import datetime, timezone from datetime import datetime
from pydantic import BaseModel, Field from pydantic import BaseModel, ConfigDict, Field
from sqlalchemy.orm import Session
from app.models import DemoAccountState, DemoTradeRecord
class DemoTrade(BaseModel): class DemoTrade(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int id: int
market: str market: str
side: str side: str
@@ -10,7 +15,7 @@ class DemoTrade(BaseModel):
amount: float amount: float
quote_amount: float quote_amount: float
reason: str reason: str
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) created_at: datetime
class DemoAccount(BaseModel): class DemoAccount(BaseModel):
@@ -25,24 +30,46 @@ class DemoAccountService:
def __init__(self, initial_balance: float, quote_asset: str) -> None: def __init__(self, initial_balance: float, quote_asset: str) -> None:
self.initial_balance = initial_balance self.initial_balance = initial_balance
self.quote_asset = quote_asset self.quote_asset = quote_asset
self.account = DemoAccount(
quote_asset=quote_asset, def _get_or_create_state(self, db: Session) -> DemoAccountState:
balance=initial_balance, state = db.get(DemoAccountState, 1)
equity=initial_balance, if state is None:
state = DemoAccountState(
id=1,
quote_asset=self.quote_asset,
balance=self.initial_balance,
equity=self.initial_balance,
realized_pnl=0.0,
)
db.add(state)
db.commit()
db.refresh(state)
return state
def snapshot(self, db: Session) -> DemoAccount:
state = self._get_or_create_state(db)
trades = db.query(DemoTradeRecord).order_by(DemoTradeRecord.id.desc()).limit(100).all()
return DemoAccount(
quote_asset=state.quote_asset,
balance=state.balance,
equity=state.equity,
realized_pnl=state.realized_pnl,
trades=[DemoTrade.model_validate(trade) for trade in reversed(trades)],
) )
def snapshot(self) -> DemoAccount: def reset(self, db: Session) -> DemoAccount:
return self.account db.query(DemoTradeRecord).delete()
state = self._get_or_create_state(db)
state.quote_asset = self.quote_asset
state.balance = self.initial_balance
state.equity = self.initial_balance
state.realized_pnl = 0.0
db.add(state)
db.commit()
db.refresh(state)
return self.snapshot(db)
def reset(self) -> DemoAccount: def add_demo_trade(self, db: Session, market: str, side: str, price: float, amount: float, reason: str) -> DemoTrade:
self.account = DemoAccount(
quote_asset=self.quote_asset,
balance=self.initial_balance,
equity=self.initial_balance,
)
return self.account
def add_demo_trade(self, market: str, side: str, price: float, amount: float, reason: str) -> DemoTrade:
if price <= 0 or amount <= 0: if price <= 0 or amount <= 0:
raise ValueError('price and amount must be positive') raise ValueError('price and amount must be positive')
@@ -51,17 +78,18 @@ class DemoAccountService:
if side_normalized not in {'buy', 'sell'}: if side_normalized not in {'buy', 'sell'}:
raise ValueError('side must be buy or sell') raise ValueError('side must be buy or sell')
if side_normalized == 'buy': state = self._get_or_create_state(db)
if quote_amount > self.account.balance:
raise ValueError('not enough demo balance')
self.account.balance -= quote_amount
else:
self.account.balance += quote_amount
self.account.realized_pnl += quote_amount
self.account.equity = self.account.balance if side_normalized == 'buy':
trade = DemoTrade( if quote_amount > state.balance:
id=len(self.account.trades) + 1, raise ValueError('not enough demo balance')
state.balance -= quote_amount
else:
state.balance += quote_amount
state.realized_pnl += quote_amount
state.equity = state.balance
trade = DemoTradeRecord(
market=market.upper(), market=market.upper(),
side=side_normalized, side=side_normalized,
price=price, price=price,
@@ -69,5 +97,8 @@ class DemoAccountService:
quote_amount=quote_amount, quote_amount=quote_amount,
reason=reason, reason=reason,
) )
self.account.trades.append(trade) db.add(state)
return trade db.add(trade)
db.commit()
db.refresh(trade)
return DemoTrade.model_validate(trade)