feat: integrate 398-400-401 for main merge REQ-OPS-001 TASK-OPS-003 TEST-ACC-009 #410

Merged
jihoson merged 13 commits from feature/398-400-401 into main 2026-03-04 09:56:01 +09:00
2 changed files with 58 additions and 0 deletions
Showing only changes of commit f245f55a32 - Show all commits

View File

@@ -2084,6 +2084,15 @@ async def trading_cycle(
quantity=quantity,
price=order_price,
)
if result.get("rt_cd", "0") != "0":
order_succeeded = False
msg1 = result.get("msg1") or ""
logger.warning(
"KR order not accepted for %s: rt_cd=%s msg=%s",
stock_code,
result.get("rt_cd"),
msg1,
)
else:
# For overseas orders, always use limit orders (지정가):
# - KIS market orders (ORD_DVSN=01) calculate quantity based on upper limit
@@ -3293,6 +3302,15 @@ async def run_daily_session(
quantity=quantity,
price=order_price,
)
if result.get("rt_cd", "0") != "0":
order_succeeded = False
daily_msg1 = result.get("msg1") or ""
logger.warning(
"KR order not accepted for %s: rt_cd=%s msg=%s",
stock_code,
result.get("rt_cd"),
daily_msg1,
)
else:
# KIS VTS only accepts limit orders; use 0.5% premium for BUY
if decision.action == "BUY":

View File

@@ -913,6 +913,46 @@ class TestTradingCycleTelegramIntegration:
# Verify notification was attempted
mock_telegram.notify_trade_execution.assert_called_once()
@pytest.mark.asyncio
async def test_kr_rejected_order_does_not_notify_or_log_trade(
self,
mock_broker: MagicMock,
mock_overseas_broker: MagicMock,
mock_scenario_engine: MagicMock,
mock_playbook: DayPlaybook,
mock_risk: MagicMock,
mock_db: MagicMock,
mock_decision_logger: MagicMock,
mock_context_store: MagicMock,
mock_criticality_assessor: MagicMock,
mock_telegram: MagicMock,
mock_market: MagicMock,
) -> None:
"""KR orders rejected by KIS should not trigger success side effects."""
mock_broker.send_order = AsyncMock(
return_value={"rt_cd": "1", "msg1": "장운영시간이 아닙니다."}
)
with patch("src.main.log_trade") as mock_log_trade:
await trading_cycle(
broker=mock_broker,
overseas_broker=mock_overseas_broker,
scenario_engine=mock_scenario_engine,
playbook=mock_playbook,
risk=mock_risk,
db_conn=mock_db,
decision_logger=mock_decision_logger,
context_store=mock_context_store,
criticality_assessor=mock_criticality_assessor,
telegram=mock_telegram,
market=mock_market,
stock_code="005930",
scan_candidates={},
)
mock_telegram.notify_trade_execution.assert_not_called()
mock_log_trade.assert_not_called()
@pytest.mark.asyncio
async def test_fat_finger_notification_sent(
self,