fix: separate trade price from realtime price

This commit is contained in:
王鹏
2026-07-09 10:45:27 +08:00
parent 5bbc4e607a
commit 15636fb910
9 changed files with 105 additions and 55 deletions

View File

@@ -37,7 +37,7 @@ def make_trade(
def test_buy_sell_grid_profit_and_breakeven():
today = date(2026, 7, 8)
instrument = Instrument(id=1, code="510300", name="沪深300ETF", manual_price=Decimal("9.50"))
instrument = Instrument(id=1, code="510300", name="沪深300ETF")
trades = [
make_trade(
trade_id=1,
@@ -68,7 +68,17 @@ def test_buy_sell_grid_profit_and_breakeven():
),
]
[summary] = calculate_positions([instrument], trades, as_of=today)
quotes = {
1: QuoteSnapshot(
symbol="sh510300",
code="510300",
name="沪深300ETF",
price=Decimal("9.50"),
source="tencent",
)
}
[summary] = calculate_positions([instrument], trades, as_of=today, quote_snapshots=quotes)
assert summary.total_quantity == 100
assert summary.base_quantity == 100
@@ -79,6 +89,8 @@ def test_buy_sell_grid_profit_and_breakeven():
assert summary.cost_price == Decimal("9.01")
assert summary.position_breakeven_price == Decimal("8.03")
assert summary.account_breakeven_price == Decimal("8.03")
assert summary.current_price == Decimal("9.50")
assert summary.price_source == "tencent"
assert summary.market_value == Decimal("950.00")
assert summary.floating_pnl == Decimal("49.00")
@@ -111,7 +123,7 @@ def test_t_plus_one_available_quantity_excludes_today_buys():
assert summary.available_quantity == 200
def test_quote_snapshot_overrides_manual_price_for_position_value():
def test_quote_snapshot_supplies_current_price_for_position_value():
today = date(2026, 7, 8)
instrument = Instrument(id=1, code="510300", name="沪深300ETF", manual_price=Decimal("3.90"))
trades = [
@@ -141,6 +153,28 @@ def test_quote_snapshot_overrides_manual_price_for_position_value():
assert summary.market_value == Decimal("4120.00")
def test_missing_quote_does_not_use_manual_or_last_trade_price_for_current_price():
today = date(2026, 7, 8)
instrument = Instrument(id=1, code="510300", name="沪深300ETF", manual_price=Decimal("3.90"))
trades = [
make_trade(
trade_id=1,
trade_date=today - timedelta(days=1),
side=TradeSide.BUY,
price="4.00",
quantity=1000,
trade_group=TradeGroup.BASE,
)
]
[summary] = calculate_positions([instrument], trades, as_of=today)
assert summary.current_price is None
assert summary.price_source == "missing"
assert summary.market_value is None
assert summary.floating_pnl is None
def test_sell_more_than_group_position_raises():
today = date(2026, 7, 8)
instrument = Instrument(id=1, code="159915", name="创业板ETF")

View File

@@ -64,10 +64,12 @@ def test_service_creates_default_account_and_computes_summary(tmp_path):
assert len(positions) == 1
assert positions[0].total_quantity == 500
assert positions[0].current_price is None
assert positions[0].price_source == "missing"
assert positions[0].grid_profit == Decimal("91.47")
assert summary.cash == Decimal("98138.97")
assert summary.market_value == Decimal("2000.00")
assert summary.total_assets == Decimal("100138.97")
assert summary.market_value == Decimal("0.00")
assert summary.total_assets == Decimal("98138.97")
def test_service_validates_lot_size_and_available_sell_quantity(tmp_path):

View File

@@ -35,6 +35,47 @@ def test_main_window_can_be_constructed_offscreen(tmp_path, monkeypatch):
app.processEvents()
def test_instrument_dialog_does_not_collect_manual_current_price(monkeypatch):
monkeypatch.setenv("QT_QPA_PLATFORM", "offscreen")
from PySide6.QtWidgets import QApplication, QLabel
from grid_trading.ui.dialogs import InstrumentDialog
app = QApplication.instance() or QApplication([])
dialog = InstrumentDialog()
labels = [label.text() for label in dialog.findChildren(QLabel)]
assert "手动价格" not in labels
assert not hasattr(dialog, "manual_price_edit")
assert dialog.to_instrument().manual_price is None
dialog.close()
app.processEvents()
def test_trade_dialog_labels_price_as_trade_price(tmp_path, monkeypatch):
monkeypatch.setenv("QT_QPA_PLATFORM", "offscreen")
from PySide6.QtWidgets import QApplication, QLabel
from grid_trading.services.trading_service import TradingService
from grid_trading.ui.dialogs import TradeDialog
app = QApplication.instance() or QApplication([])
service = TradingService(tmp_path / "grid.db")
service.ensure_defaults()
account = service.get_active_account()
dialog = TradeDialog(service, account.id, [Instrument(id=1, code="510300", name="沪深300ETF")])
labels = [label.text() for label in dialog.findChildren(QLabel)]
assert "成交价" in labels
dialog.close()
service.close()
app.processEvents()
def test_quote_refresh_does_not_block_main_window(tmp_path, monkeypatch):
monkeypatch.setenv("QT_QPA_PLATFORM", "offscreen")