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 brief["holdings"][0]["ticker"] == "005930"
|
||||||
assert "portfolio_health" in brief
|
assert "portfolio_health" in brief
|
||||||
assert brief["holdings"][0]["action"] in ("add", "hold", "trim", "sell")
|
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