feat: use realtime quotes in summaries
This commit is contained in:
@@ -14,6 +14,7 @@ from grid_trading.domain.models import (
|
||||
FeeRules,
|
||||
Instrument,
|
||||
PositionSummary,
|
||||
QuoteSnapshot,
|
||||
Trade,
|
||||
TradeGroup,
|
||||
TradeSide,
|
||||
@@ -65,8 +66,10 @@ def calculate_positions(
|
||||
*,
|
||||
as_of: date,
|
||||
instrument_cashflows: Mapping[int, Decimal] | None = None,
|
||||
quote_snapshots: Mapping[int, QuoteSnapshot] | None = None,
|
||||
) -> list[PositionSummary]:
|
||||
cashflows = instrument_cashflows or {}
|
||||
quotes = quote_snapshots or {}
|
||||
instrument_by_id = {instrument.id: instrument for instrument in instruments if instrument.id is not None}
|
||||
states: dict[int, dict[TradeGroup, _GroupState]] = defaultdict(
|
||||
lambda: {group: _GroupState() for group in TradeGroup}
|
||||
@@ -120,7 +123,11 @@ def calculate_positions(
|
||||
remaining_cost = money(sum((state.cost for state in group_states.values()), Decimal("0")))
|
||||
realized_pnl = money(sum((state.realized_pnl for state in group_states.values()), Decimal("0")))
|
||||
grid_profit = money(group_states[TradeGroup.GRID].realized_pnl)
|
||||
current_price, price_source = _resolve_current_price(instrument, last_trade_price.get(instrument.id))
|
||||
current_price, price_source = _resolve_current_price(
|
||||
instrument,
|
||||
last_trade_price.get(instrument.id),
|
||||
quotes.get(instrument.id),
|
||||
)
|
||||
market_value = money(current_price * Decimal(total_quantity)) if current_price is not None else None
|
||||
floating_pnl = money(market_value - remaining_cost) if market_value is not None else None
|
||||
available_quantity = max(0, total_quantity - today_buys[instrument.id])
|
||||
@@ -203,7 +210,10 @@ def _validate_trade(trade: Trade) -> None:
|
||||
def _resolve_current_price(
|
||||
instrument: Instrument,
|
||||
fallback_trade_price: Decimal | None,
|
||||
quote_snapshot: QuoteSnapshot | None,
|
||||
) -> tuple[Decimal | None, str]:
|
||||
if quote_snapshot is not None:
|
||||
return quote_snapshot.price, quote_snapshot.source
|
||||
if instrument.manual_price is not None:
|
||||
return instrument.manual_price, "manual"
|
||||
if fallback_trade_price is not None:
|
||||
|
||||
@@ -19,18 +19,22 @@ from grid_trading.domain.models import (
|
||||
FeeRules,
|
||||
Instrument,
|
||||
PositionSummary,
|
||||
QuoteSnapshot,
|
||||
StrategyOverride,
|
||||
StrategyTemplate,
|
||||
Trade,
|
||||
TradeSide,
|
||||
)
|
||||
from grid_trading.market.tencent import TencentQuoteProvider
|
||||
from grid_trading.storage.repositories import Repository
|
||||
|
||||
|
||||
class TradingService:
|
||||
def __init__(self, db_path: str | Path):
|
||||
def __init__(self, db_path: str | Path, *, quote_provider=None):
|
||||
self.repository = Repository(db_path)
|
||||
self.repository.initialize()
|
||||
self.quote_provider = quote_provider or TencentQuoteProvider()
|
||||
self._quote_cache: dict[str, QuoteSnapshot] = {}
|
||||
|
||||
def close(self) -> None:
|
||||
self.repository.close()
|
||||
@@ -88,6 +92,12 @@ class TradingService:
|
||||
def list_instruments(self) -> list[Instrument]:
|
||||
return self.repository.list_instruments()
|
||||
|
||||
def refresh_quotes(self) -> dict[str, QuoteSnapshot]:
|
||||
instruments = self.repository.list_instruments()
|
||||
quotes = self.quote_provider.fetch_quotes(instruments)
|
||||
self._quote_cache = quotes
|
||||
return quotes
|
||||
|
||||
def get_default_strategy_template(self) -> StrategyTemplate:
|
||||
self.ensure_defaults()
|
||||
template = self.repository.get_default_strategy_template()
|
||||
@@ -168,6 +178,7 @@ class TradingService:
|
||||
trades,
|
||||
as_of=as_of_date,
|
||||
instrument_cashflows=self._instrument_cashflows(account.id),
|
||||
quote_snapshots=self._quote_snapshots_by_instrument_id(instruments),
|
||||
)
|
||||
|
||||
def get_account_summary(self, *, as_of: date | None = None) -> AccountSummary:
|
||||
@@ -247,3 +258,16 @@ class TradingService:
|
||||
if entry.instrument_id is not None:
|
||||
totals[entry.instrument_id] += entry.amount
|
||||
return dict(totals)
|
||||
|
||||
def _quote_snapshots_by_instrument_id(
|
||||
self,
|
||||
instruments: list[Instrument],
|
||||
) -> dict[int, QuoteSnapshot]:
|
||||
snapshots: dict[int, QuoteSnapshot] = {}
|
||||
for instrument in instruments:
|
||||
if instrument.id is None:
|
||||
continue
|
||||
quote = self._quote_cache.get(instrument.code)
|
||||
if quote is not None:
|
||||
snapshots[instrument.id] = quote
|
||||
return snapshots
|
||||
|
||||
Reference in New Issue
Block a user