[verified] Signal column now derives from theme engine (combined 60/40 + quality), not tourism_result

- /api/v1/factors signal no longer from tourism-only signals; derived from RealDashboard combined score: LONG>=0.15, SHORT<=-0.15, else NEUTRAL. One source of truth.
- Verified: LONG 25 / NEUTRAL 16 / SHORT 8; BANPU LONG (3 themes), KTB LONG (banks), CPN LONG (3 themes), JMART SHORT (nonbank) — reflects all 13 themes + per-symbol quality, not flat tourism.
- reason_codes + combined_score on each factor row.
This commit is contained in:
Kunthawat Greethong
2026-08-26 15:26:34 +07:00
parent d850955c44
commit 375682d2dc

View File

@@ -455,8 +455,33 @@ def create_app(config: dict[str, Any] | None = None) -> Flask:
from app import siamchart_factors
factor_view = siamchart_factors.build_factor_view()
current = app.extensions["tourism_result"]
signal_by_symbol = {s["symbol"]: s for s in current["signals"]}
# signal = derived from the NEW theme-engine combined score (60/40 with
# per-symbol quality), NOT the old tourism_result. One source of truth.
from app import daily_cache
from app.dashboard import RealDashboard
cache = app.extensions.setdefault("daily_cache", daily_cache.DailyCache())
current = app.extensions.get("tourism_result") or {}
signal_by_symbol = {}
try:
dash = RealDashboard(current.get("signals", []), cache).build()
for row in dash.get("board", []):
comb = row.get("combined")
if comb is None:
signal_by_symbol[row["symbol"]] = {"side": None, "score": None}
continue
if comb >= 0.15:
side, score = "LONG", round(min(abs(comb) * 3.0, 1.0) * 0.9 + 0.1, 3)
elif comb <= -0.15:
side, score = "SHORT", round(min(abs(comb) * 3.0, 1.0) * 0.9 + 0.1, 3)
else:
side, score = "NEUTRAL", round(abs(comb) / 0.15, 3)
signal_by_symbol[row["symbol"]] = {
"side": side, "score": score, "confidence": "medium",
"combined_score": comb,
}
except Exception:
# no dashboard -> fall back to neutral for all
pass
merged = []
for factor in factor_view.get("factors", []):
@@ -467,8 +492,9 @@ def create_app(config: dict[str, Any] | None = None) -> Flask:
"signal_side": sig.get("side"),
"signal_score": sig.get("score"),
"signal_confidence": sig.get("confidence"),
"signal_target_weight": sig.get("target_weight"),
"reason_codes": sig.get("reason_codes", []),
"combined_score": sig.get("combined_score"),
"signal_target_weight": None,
"reason_codes": ["combined 60/40 + firm quality"] if sig.get("side") in ("LONG", "SHORT") else [],
}
)
# Put symbols that carry a non-neutral signal first, then by signal score.