fix: PR review — DB reload, market-local date, market-scoped scan_candidates
Some checks failed
CI / test (pull_request) Has been cancelled
Some checks failed
CI / test (pull_request) Has been cancelled
Address PR #110 review findings: 1. High — Realtime mode now loads playbook from DB before calling Gemini, preventing duplicate API calls on process restart (4/day budget). 2. Medium — Pass market-local date (via market.timezone) to generate_playbook() and _empty_playbook() instead of date.today(). 3. Medium — scan_candidates restructured from {stock_code: candidate} to {market_code: {stock_code: candidate}} to prevent KR/US symbol collision. New test: test_scan_candidates_market_scoped verifies cross-market isolation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -792,7 +792,7 @@ class TestScenarioEngineIntegration:
|
||||
telegram=mock_telegram,
|
||||
market=mock_market,
|
||||
stock_code="005930",
|
||||
scan_candidates={"005930": candidate},
|
||||
scan_candidates={"KR": {"005930": candidate}},
|
||||
)
|
||||
|
||||
# Verify evaluate was called
|
||||
@@ -810,6 +810,48 @@ class TestScenarioEngineIntegration:
|
||||
assert "portfolio_pnl_pct" in portfolio_data
|
||||
assert "total_cash" in portfolio_data
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_scan_candidates_market_scoped(
|
||||
self, mock_broker: MagicMock, mock_market: MagicMock, mock_telegram: MagicMock,
|
||||
) -> None:
|
||||
"""Test scan_candidates uses market-scoped lookup, ignoring other markets."""
|
||||
from src.analysis.smart_scanner import ScanCandidate
|
||||
|
||||
engine = MagicMock(spec=ScenarioEngine)
|
||||
engine.evaluate = MagicMock(return_value=_make_hold_match())
|
||||
|
||||
# Candidate stored under US market — should NOT be found for KR market
|
||||
us_candidate = ScanCandidate(
|
||||
stock_code="005930", name="Overlap", price=100,
|
||||
volume=500000, volume_ratio=5.0, rsi=15.0,
|
||||
signal="oversold", score=90.0,
|
||||
)
|
||||
|
||||
with patch("src.main.log_trade"):
|
||||
await trading_cycle(
|
||||
broker=mock_broker,
|
||||
overseas_broker=MagicMock(),
|
||||
scenario_engine=engine,
|
||||
playbook=_make_playbook(),
|
||||
risk=MagicMock(),
|
||||
db_conn=MagicMock(),
|
||||
decision_logger=MagicMock(),
|
||||
context_store=MagicMock(get_latest_timeframe=MagicMock(return_value=None)),
|
||||
criticality_assessor=MagicMock(
|
||||
assess_market_conditions=MagicMock(return_value=MagicMock(value="NORMAL")),
|
||||
get_timeout=MagicMock(return_value=5.0),
|
||||
),
|
||||
telegram=mock_telegram,
|
||||
market=mock_market, # KR market
|
||||
stock_code="005930",
|
||||
scan_candidates={"US": {"005930": us_candidate}}, # Wrong market
|
||||
)
|
||||
|
||||
# Should NOT have rsi/volume_ratio because candidate is under US, not KR
|
||||
market_data = engine.evaluate.call_args[0][2]
|
||||
assert "rsi" not in market_data
|
||||
assert "volume_ratio" not in market_data
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_scenario_engine_called_without_scanner_data(
|
||||
self, mock_broker: MagicMock, mock_market: MagicMock, mock_telegram: MagicMock,
|
||||
|
||||
Reference in New Issue
Block a user