test(stock): Phase 4 회귀 (momentum_loss·멱등·non-KRX 경로)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -295,3 +295,93 @@ def test_compute_and_store_and_brief(monkeypatch):
|
||||
assert brief["holdings"][0]["ticker"] == "005930"
|
||||
assert "portfolio_health" in brief
|
||||
assert brief["holdings"][0]["action"] in ("add", "hold", "trim", "sell")
|
||||
|
||||
|
||||
def test_compute_momentum_loss_flag(monkeypatch):
|
||||
"""직전 시그널 tech_score HIGH → 오늘 LOW → momentum_loss=True."""
|
||||
import os, tempfile
|
||||
from app import db
|
||||
|
||||
monkeypatch.setattr(db, "DB_PATH", os.path.join(tempfile.mkdtemp(), "stock.db"))
|
||||
db.init_db()
|
||||
|
||||
yesterday = (dt.date.today() - dt.timedelta(days=1)).isoformat()
|
||||
today = dt.date.today()
|
||||
today_iso = today.isoformat()
|
||||
|
||||
# 어제 시그널 삽입: tech_score=90 (HIGH)
|
||||
db.upsert_holdings_signal(
|
||||
date=yesterday, ticker="005930", name="삼성전자",
|
||||
action="hold", tech_score=90.0, exit_flags={}, issues=[],
|
||||
close=1000, pnl_rate=0.0, reasons="x",
|
||||
)
|
||||
|
||||
monkeypatch.setattr(hi, "get_holdings", lambda: [
|
||||
{"ticker": "005930", "name": "삼성전자", "quantity": 10, "avg_price": 1000,
|
||||
"current_price": 1000, "pnl_rate": 0.0, "is_krx": True}
|
||||
])
|
||||
ctx = _toy_ctx(("005930",))
|
||||
monkeypatch.setattr(hi, "_load_ctx", lambda asof: ctx)
|
||||
monkeypatch.setattr(hi, "_news_sentiment_map", lambda date: {})
|
||||
monkeypatch.setattr(hi.db, "get_all_broker_cash", lambda: [])
|
||||
# tech_score=30 → 낮음 (momentum_low=35 미만, 또한 90-30=60 > momentum_drop=15)
|
||||
monkeypatch.setattr(hi, "technical_posture", lambda ctx, tickers: {"005930": 30.0})
|
||||
|
||||
res = hi.compute_and_store(asof=today, use_llm=False)
|
||||
assert res["stored"] == 1
|
||||
|
||||
signals = db.get_holdings_signals(today_iso)
|
||||
assert len(signals) == 1
|
||||
assert signals[0]["exit_flags"]["momentum_loss"] is True
|
||||
|
||||
|
||||
def test_compute_idempotent(monkeypatch):
|
||||
"""동일 입력으로 compute_and_store 두 번 실행 → upsert로 1건만 저장."""
|
||||
import os, tempfile
|
||||
from app import db
|
||||
|
||||
monkeypatch.setattr(db, "DB_PATH", os.path.join(tempfile.mkdtemp(), "stock.db"))
|
||||
db.init_db()
|
||||
|
||||
monkeypatch.setattr(hi, "get_holdings", lambda: [
|
||||
{"ticker": "005930", "name": "삼성전자", "quantity": 10, "avg_price": 1000,
|
||||
"current_price": 1100, "pnl_rate": 10.0, "is_krx": True}
|
||||
])
|
||||
ctx = _toy_ctx(("005930",))
|
||||
monkeypatch.setattr(hi, "_load_ctx", lambda asof: ctx)
|
||||
monkeypatch.setattr(hi, "_news_sentiment_map", lambda date: {})
|
||||
monkeypatch.setattr(hi.db, "get_all_broker_cash", lambda: [])
|
||||
|
||||
hi.compute_and_store(asof=ctx.asof, use_llm=False)
|
||||
hi.compute_and_store(asof=ctx.asof, use_llm=False)
|
||||
|
||||
signals = db.get_holdings_signals(ctx.asof.isoformat())
|
||||
assert len(signals) == 1, f"upsert 실패: {len(signals)}건 저장됨"
|
||||
|
||||
|
||||
def test_compute_non_krx_holding(monkeypatch):
|
||||
"""is_krx=False 종목은 tech_score=None·action='hold'로 저장된다."""
|
||||
import os, tempfile
|
||||
from app import db
|
||||
|
||||
monkeypatch.setattr(db, "DB_PATH", os.path.join(tempfile.mkdtemp(), "stock.db"))
|
||||
db.init_db()
|
||||
|
||||
monkeypatch.setattr(hi, "get_holdings", lambda: [
|
||||
{"ticker": "AAPL", "name": "Apple", "quantity": 5, "avg_price": 200,
|
||||
"current_price": 220, "pnl_rate": 10.0, "is_krx": False}
|
||||
])
|
||||
ctx = _toy_ctx(()) # ticker 없는 빈 ctx
|
||||
monkeypatch.setattr(hi, "_load_ctx", lambda asof: ctx)
|
||||
monkeypatch.setattr(hi, "_news_sentiment_map", lambda date: {})
|
||||
monkeypatch.setattr(hi.db, "get_all_broker_cash", lambda: [])
|
||||
|
||||
res = hi.compute_and_store(asof=ctx.asof, use_llm=False)
|
||||
assert res["stored"] == 1
|
||||
|
||||
signals = db.get_holdings_signals(ctx.asof.isoformat())
|
||||
assert len(signals) == 1
|
||||
sig = signals[0]
|
||||
assert sig["ticker"] == "AAPL"
|
||||
assert sig["tech_score"] is None
|
||||
assert sig["action"] == "hold"
|
||||
|
||||
Reference in New Issue
Block a user