import datetime as dt import pandas as pd from app.screener.engine import ScreenContext from app.screener.nodes.vcp_lite import VcpLite from app.screener._test_fixtures import make_master, make_prices, make_flow def _ctx(master, prices, flow): return ScreenContext(master=master, prices=prices, flow=flow, kospi=pd.Series(dtype=float, name="kospi"), asof=dt.date(2026, 5, 12)) def test_contracting_stock_scores_higher_than_expanding(): asof = dt.date(2026, 5, 12) master = make_master(["CON", "EXP"]) prices = make_prices(["CON", "EXP"], days=260, asof=asof) # CON: 최근 40일 변동성 축소 (high/low 좁힘) mask_recent_con = (prices["ticker"] == "CON") & ( prices["date"] >= (asof - dt.timedelta(days=40)).isoformat() ) prices.loc[mask_recent_con, "high"] = (prices.loc[mask_recent_con, "close"] * 1.003).astype(int) prices.loc[mask_recent_con, "low"] = (prices.loc[mask_recent_con, "close"] * 0.997).astype(int) # EXP: 최근 40일 변동성 확대 mask_recent_exp = (prices["ticker"] == "EXP") & ( prices["date"] >= (asof - dt.timedelta(days=40)).isoformat() ) prices.loc[mask_recent_exp, "high"] = (prices.loc[mask_recent_exp, "close"] * 1.05).astype(int) prices.loc[mask_recent_exp, "low"] = (prices.loc[mask_recent_exp, "close"] * 0.95).astype(int) flow = make_flow(["CON", "EXP"], days=260, asof=asof) out = VcpLite().compute(_ctx(master, prices, flow), VcpLite.default_params) assert out["CON"] > out["EXP"]