feat: show open grid lots
This commit is contained in:
77
src/grid_trading/domain/open_grid_lots.py
Normal file
77
src/grid_trading/domain/open_grid_lots.py
Normal file
@@ -0,0 +1,77 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import replace
|
||||
from datetime import date
|
||||
from decimal import Decimal
|
||||
|
||||
from grid_trading.domain.calculations import money, price
|
||||
from grid_trading.domain.models import OpenGridLot, Trade, TradeGroup, TradeSide
|
||||
|
||||
|
||||
def calculate_open_grid_lots(
|
||||
trades: list[Trade],
|
||||
*,
|
||||
spacing: Decimal,
|
||||
current_price: Decimal | None,
|
||||
as_of: date,
|
||||
) -> list[OpenGridLot]:
|
||||
if spacing <= 0 or spacing >= 1:
|
||||
raise ValueError("网格间距必须大于 0 且小于 100%")
|
||||
|
||||
lots: list[OpenGridLot] = []
|
||||
for trade in sorted(trades, key=lambda item: (item.trade_date, item.id or 0)):
|
||||
if trade.trade_date > as_of or trade.trade_group is not TradeGroup.GRID:
|
||||
continue
|
||||
if trade.side is TradeSide.BUY:
|
||||
buy_price = price(trade.price)
|
||||
suggested_sell_price = price(buy_price * (Decimal("1") + spacing))
|
||||
lots.append(
|
||||
OpenGridLot(
|
||||
buy_trade_id=trade.id,
|
||||
buy_date=trade.trade_date,
|
||||
buy_price=buy_price,
|
||||
remaining_quantity=trade.quantity,
|
||||
actual_investment=money(buy_price * Decimal(trade.quantity)),
|
||||
suggested_sell_price=suggested_sell_price,
|
||||
estimated_gross_profit=money(
|
||||
(suggested_sell_price - buy_price) * Decimal(trade.quantity)
|
||||
),
|
||||
current_price=current_price,
|
||||
status=_status(current_price, suggested_sell_price),
|
||||
)
|
||||
)
|
||||
else:
|
||||
lots = _match_sell_against_lots(lots, trade.quantity)
|
||||
return lots
|
||||
|
||||
|
||||
def _match_sell_against_lots(lots: list[OpenGridLot], quantity: int) -> list[OpenGridLot]:
|
||||
remaining_sell_quantity = quantity
|
||||
updated_lots: list[OpenGridLot] = []
|
||||
for lot in lots:
|
||||
if remaining_sell_quantity <= 0:
|
||||
updated_lots.append(lot)
|
||||
continue
|
||||
matched_quantity = min(lot.remaining_quantity, remaining_sell_quantity)
|
||||
remaining_sell_quantity -= matched_quantity
|
||||
remaining_quantity = lot.remaining_quantity - matched_quantity
|
||||
if remaining_quantity > 0:
|
||||
updated_lots.append(_with_remaining_quantity(lot, remaining_quantity))
|
||||
return updated_lots
|
||||
|
||||
|
||||
def _with_remaining_quantity(lot: OpenGridLot, quantity: int) -> OpenGridLot:
|
||||
return replace(
|
||||
lot,
|
||||
remaining_quantity=quantity,
|
||||
actual_investment=money(lot.buy_price * Decimal(quantity)),
|
||||
estimated_gross_profit=money((lot.suggested_sell_price - lot.buy_price) * Decimal(quantity)),
|
||||
)
|
||||
|
||||
|
||||
def _status(current_price: Decimal | None, suggested_sell_price: Decimal) -> str:
|
||||
if current_price is None:
|
||||
return "未刷新行情"
|
||||
if current_price >= suggested_sell_price:
|
||||
return "可卖"
|
||||
return "未到价"
|
||||
Reference in New Issue
Block a user