From 47aadcb4e7b98dca5e0612df0b7d54e0ffda6227 Mon Sep 17 00:00:00 2001 From: agentson Date: Sat, 28 Feb 2026 14:38:53 +0900 Subject: [PATCH] fix: include exchange_code in latest BUY matching key (#323) --- src/db.py | 53 +++++++++++++++++++++++++++++++++++------------- src/main.py | 14 +++++++++++-- tests/test_db.py | 38 +++++++++++++++++++++++++++++++++- 3 files changed, 88 insertions(+), 17 deletions(-) diff --git a/src/db.py b/src/db.py index 4a0c9f0..00637c4 100644 --- a/src/db.py +++ b/src/db.py @@ -290,22 +290,47 @@ def _resolve_session_id(*, market: str, session_id: str | None) -> str: def get_latest_buy_trade( - conn: sqlite3.Connection, stock_code: str, market: str + conn: sqlite3.Connection, + stock_code: str, + market: str, + exchange_code: str | None = None, ) -> dict[str, Any] | None: """Fetch the most recent BUY trade for a stock and market.""" - cursor = conn.execute( - """ - SELECT decision_id, price, quantity - FROM trades - WHERE stock_code = ? - AND market = ? - AND action = 'BUY' - AND decision_id IS NOT NULL - ORDER BY timestamp DESC - LIMIT 1 - """, - (stock_code, market), - ) + if exchange_code: + cursor = conn.execute( + """ + SELECT decision_id, price, quantity + FROM trades + WHERE stock_code = ? + AND market = ? + AND action = 'BUY' + AND decision_id IS NOT NULL + AND ( + exchange_code = ? + OR exchange_code IS NULL + OR exchange_code = '' + ) + ORDER BY + CASE WHEN exchange_code = ? THEN 0 ELSE 1 END, + timestamp DESC + LIMIT 1 + """, + (stock_code, market, exchange_code, exchange_code), + ) + else: + cursor = conn.execute( + """ + SELECT decision_id, price, quantity + FROM trades + WHERE stock_code = ? + AND market = ? + AND action = 'BUY' + AND decision_id IS NOT NULL + ORDER BY timestamp DESC + LIMIT 1 + """, + (stock_code, market), + ) row = cursor.fetchone() if not row: return None diff --git a/src/main.py b/src/main.py index cc158a2..67c533a 100644 --- a/src/main.py +++ b/src/main.py @@ -1655,7 +1655,12 @@ async def trading_cycle( logger.warning("Telegram notification failed: %s", exc) if decision.action == "SELL" and order_succeeded: - buy_trade = get_latest_buy_trade(db_conn, stock_code, market.code) + buy_trade = get_latest_buy_trade( + db_conn, + stock_code, + market.code, + exchange_code=market.exchange_code, + ) if buy_trade and buy_trade.get("price") is not None: buy_price = float(buy_trade["price"]) buy_qty = int(buy_trade.get("quantity") or 1) @@ -2752,7 +2757,12 @@ async def run_daily_session( continue if decision.action == "SELL" and order_succeeded: - buy_trade = get_latest_buy_trade(db_conn, stock_code, market.code) + buy_trade = get_latest_buy_trade( + db_conn, + stock_code, + market.code, + exchange_code=market.exchange_code, + ) if buy_trade and buy_trade.get("price") is not None: buy_price = float(buy_trade["price"]) buy_qty = int(buy_trade.get("quantity") or 1) diff --git a/tests/test_db.py b/tests/test_db.py index bbd600e..9de5413 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -3,7 +3,7 @@ import tempfile import os -from src.db import get_open_position, init_db, log_trade +from src.db import get_latest_buy_trade, get_open_position, init_db, log_trade def test_get_open_position_returns_latest_buy() -> None: @@ -329,3 +329,39 @@ def test_log_trade_unknown_market_falls_back_to_unknown_session() -> None: row = conn.execute("SELECT session_id FROM trades ORDER BY id DESC LIMIT 1").fetchone() assert row is not None assert row[0] == "UNKNOWN" + + +def test_get_latest_buy_trade_prefers_exchange_code_match() -> None: + conn = init_db(":memory:") + log_trade( + conn=conn, + stock_code="AAPL", + action="BUY", + confidence=80, + rationale="legacy", + quantity=10, + price=120.0, + market="US_NASDAQ", + exchange_code="", + decision_id="legacy-buy", + ) + log_trade( + conn=conn, + stock_code="AAPL", + action="BUY", + confidence=85, + rationale="matched", + quantity=5, + price=125.0, + market="US_NASDAQ", + exchange_code="NASD", + decision_id="matched-buy", + ) + matched = get_latest_buy_trade( + conn, + stock_code="AAPL", + market="US_NASDAQ", + exchange_code="NASD", + ) + assert matched is not None + assert matched["decision_id"] == "matched-buy"