from datetime import date, timedelta from decimal import Decimal from grid_trading.domain.models import Trade, TradeGroup, TradeSide from grid_trading.domain.open_grid_lots import calculate_open_grid_lots def make_trade( *, trade_id: int, trade_date: date, side: TradeSide, price: str, quantity: int, trade_group: TradeGroup = TradeGroup.GRID, ) -> Trade: return Trade( id=trade_id, account_id=1, instrument_id=1, trade_date=trade_date, side=side, price=Decimal(price), quantity=quantity, trade_group=trade_group, ) def test_calculate_open_grid_lots_fifo_matches_grid_sells_against_grid_buys(): today = date(2026, 7, 9) trades = [ make_trade( trade_id=1, trade_date=today - timedelta(days=3), side=TradeSide.BUY, price="10.00", quantity=300, ), make_trade( trade_id=2, trade_date=today - timedelta(days=2), side=TradeSide.BUY, price="9.50", quantity=200, ), make_trade( trade_id=3, trade_date=today - timedelta(days=1), side=TradeSide.SELL, price="10.30", quantity=350, ), make_trade( trade_id=4, trade_date=today, side=TradeSide.BUY, price="8.00", quantity=100, trade_group=TradeGroup.BASE, ), ] lots = calculate_open_grid_lots( trades, spacing=Decimal("0.03"), current_price=Decimal("9.80"), as_of=today, ) assert len(lots) == 1 [lot] = lots assert lot.buy_trade_id == 2 assert lot.buy_date == today - timedelta(days=2) assert lot.buy_price == Decimal("9.50") assert lot.remaining_quantity == 150 assert lot.actual_investment == Decimal("1425.00") assert lot.suggested_sell_price == Decimal("9.79") assert lot.estimated_gross_profit == Decimal("43.50") assert lot.current_price == Decimal("9.80") assert lot.status == "可卖" def test_calculate_open_grid_lots_reports_not_reached_and_missing_quote_statuses(): today = date(2026, 7, 9) trades = [ make_trade(trade_id=1, trade_date=today, side=TradeSide.BUY, price="10.00", quantity=100), ] [not_reached] = calculate_open_grid_lots( trades, spacing=Decimal("0.03"), current_price=Decimal("10.20"), as_of=today, ) [missing_quote] = calculate_open_grid_lots( trades, spacing=Decimal("0.03"), current_price=None, as_of=today, ) assert not_reached.suggested_sell_price == Decimal("10.30") assert not_reached.status == "未到价" assert missing_quote.current_price is None assert missing_quote.status == "未刷新行情"