BACKTEST_CONTEXT.md
Cortex Investment Agent -- Backtest Context Reference
Auto-generated scan, read-only. Last updated: 2026-02-25
1. 技術指標完整清單
系統有兩層指標計算:
- Pipeline 層 (
data_pipeline/indicators.py):寫入 technical_indicators 表,共 47 欄位
- Snapshot 層 (
scripts/daily_analysis_snapshot.py):讀取 Pipeline 結果 + OHLC 原始資料,計算衍生欄位後寫入 daily_technical_snapshot 表(180+ 欄位)
1.1 MA (Simple Moving Average)
| 項目 |
值 |
| 計算方式 |
close.rolling(window=N).mean() (pandas) |
| 週期 |
5, 10, 20, 50, 60, 100, 120, 200, 240 |
| Pipeline 輸出欄位 |
ma_5, ma_10, ma_20, ma_50, ma_60, ma_100, ma_120, ma_200, ma_240 |
| Snapshot 映射 |
ma_5 → ma5, ma_20 → ma20, ma_60 → ma60, ma_120 → ma120, ma_240 → ma240 |
| Snapshot 衍生欄位 |
ma_alignment ("bullish"/"bearish"/"mixed"), price_position ("above_all"/"below_all"/"mixed"), ma60_slope ("positive"/"negative"/"flat") |
MA period taxonomy contract
每個 ma_<N> 欄位都是真正的 N 日 SMA — 沒有 alias。TW 市場慣用的 60/120/240 (季線/半年線/年線) 與 US 慣用的 50/100/200 並存。
ma_50 is true 50-day MA.
ma_60 is true 60-day MA (TW 季線).
ma_100 is true 100-day MA.
ma_120 is true 120-day MA (TW 半年線).
ma_200 is true 200-day MA.
ma_240 is true 240-day MA (TW 年線).
Pre-2026-04 bug history: snapshot 的 ma60 欄位實際存的是 50 日 MA (因為 writer aliased iv["ma_50"])。已於 migration 041 修正並 backfill 全部歷史資料。ma_10 仍只存在 technical_indicators,未載入 snapshot。
1.2 EMA (Exponential Moving Average)
| 項目 |
值 |
| 計算方式 |
close.ewm(span=N, adjust=False).mean() (pandas) |
| 週期 |
5, 10, 20, 50, 100, 200 |
| Pipeline 輸出欄位 |
ema_5, ema_10, ema_20, ema_50, ema_100, ema_200 |
| Snapshot 映射 |
無(EMA 未載入 daily_technical_snapshot) |
1.3 RSI (Relative Strength Index)
| 項目 |
值 |
| 計算方式 |
Pipeline: ta.rsi(close, length=14) (pandas_ta);Snapshot: 手動 gain/loss rolling(14) |
| 週期 |
14 |
| Pipeline 輸出欄位 |
rsi |
| Snapshot 映射 |
rsi → rsi_value |
| Snapshot 衍生欄位 |
rsi_status ("overbought"/"oversold"/"neutral"), rsi_divergence ("bullish_warning"/"bearish_warning"/"none"), rsi_slope_3 (3-day diff) |
1.4 MACD (Moving Average Convergence Divergence)
| 項目 |
值 |
| 計算方式 |
Pipeline: ta.macd(close, fast=12, slow=26, signal=9) (pandas_ta);Snapshot: 手動 EMA12-EMA26 |
| 參數 |
fast=12, slow=26, signal=9 |
| Pipeline 輸出欄位 |
macd, macd_signal, macd_hist |
| Snapshot 映射 |
macd → macd_dif, macd_signal → macd_dea, macd_hist → macd_histogram |
| Snapshot 衍生欄位 |
macd_cross_signal ("golden_cross"/"death_cross"/"none"), macd_divergence ("bullish"/"bearish"/"none"), macd_hist_slope (3-day diff), prev_macd_histogram |
1.5 KD (Stochastic Oscillator)
| 項目 |
值 |
| 計算方式 |
Pipeline: ta.stoch(high, low, close, k=14, d=3, smooth_k=3);Snapshot: 手動 rolling(9) + ewm |
| 參數 |
k_period=14, d_period=3, smooth=3(Pipeline); k_period=9, d_period=3(Snapshot) |
| Pipeline 輸出欄位 |
k, d |
| Snapshot 映射 |
k → kdj_k, d → kdj_d |
| Snapshot 衍生欄位 |
kdj_j = 3k - 2d, kdj_signal ("high_passivization"/"low_passivization"/"neutral") |
1.6 BB (Bollinger Bands)
| 項目 |
值 |
| 計算方式 |
Pipeline: ta.bbands(close, length=20, std=2);Snapshot: 手動 rolling(20).mean() ± 2*std |
| 參數 |
period=20, std=2 |
| Pipeline 輸出欄位 |
bb_upper, bb_middle, bb_lower |
| Snapshot 映射 |
直接 1:1 |
| Snapshot 衍生欄位 |
bb_percent_b = (close-lower)/(upper-lower), bb_bandwidth = (upper-lower)/middle, bb_status ("expanding"/"contracting"/"squeezing"/"normal"), prev_bb_bandwidth |
1.7 ATR (Average True Range)
| 項目 |
值 |
| 計算方式 |
Pipeline: ta.atr(high, low, close, length=14);Snapshot: 手動 TR rolling(14).mean() |
| 週期 |
14 |
| Pipeline 輸出欄位 |
atr |
| Snapshot 映射 |
atr → atr_value |
| Snapshot 衍生欄位 |
atr_status ("high_volatility"/"normal"), atr_stop_distance (= atr * 2.0) |
1.8 ADX (Average Directional Index)
| 項目 |
值 |
| 計算方式 |
Pipeline: ta.adx(high, low, close, length=14) 取 ADX_14;Snapshot: 手動 DI → DX → rolling(14) |
| 週期 |
14 |
| Pipeline 輸出欄位 |
adx |
| Snapshot 映射 |
adx → adx_value |
| Snapshot 衍生欄位 |
adx_status ("trending"/"non_trending"), adx_slope (3-day diff), adx_yesterday, adx_rising_days (連續上升天數), plus_di, minus_di |
1.9 OBV (On-Balance Volume)
| 項目 |
值 |
| 計算方式 |
Pipeline: ta.obv(close, volume);Snapshot: 手動 sign(diff)*volume cumsum |
| Pipeline 輸出欄位 |
obv |
| Snapshot 映射 |
OBV 原始值不寫入 snapshot |
| Snapshot 衍生欄位 |
obv_trend ("new_high"/"new_low"/"flat"), obv_slope ("positive"/"negative"/"flat") |
1.10 Williams %R
| 項目 |
值 |
| 計算方式 |
ta.willr(high, low, close, length=14) (pandas_ta) |
| 週期 |
14 |
| Pipeline 輸出欄位 |
willr |
| Snapshot 映射 |
無(未載入 daily_technical_snapshot) |
1.11 Volume MA
| 項目 |
值 |
| 計算方式 |
volume.rolling(window=N).mean() (pandas) |
| 週期 |
5, 20 |
| Pipeline 輸出欄位 |
volume_ma_5, volume_ma_20 |
| Snapshot 映射 |
volume_ma_20 → avg_volume_20(volume_ma_5 未載入) |
| Snapshot 衍生欄位 |
volume_ratio = current_volume / avg_volume_20, volume_status ("aggressive_buying"/"aggressive_selling"/"normal"), volume_divergence ("PRICE_UP_VOL_LOW"/"PRICE_DOWN_VOL_HIGH"/None) |
1.12 VWAP (Volume Weighted Average Price)
| 項目 |
值 |
| 計算方式 |
手動:(typical_price * volume).cumsum() / volume.cumsum() |
| Pipeline 輸出欄位 |
vwap |
| Snapshot 映射 |
直接 1:1 → vwap |
1.13 Ichimoku Cloud
| 項目 |
值 |
| 計算方式 |
ta.ichimoku(high, low, close, tenkan=9, kijun=26, senkou=52) (pandas_ta) |
| 參數 |
tenkan=9, kijun=26, senkou=52 |
| Pipeline 輸出欄位 |
ichimoku_tenkan, ichimoku_kijun |
| Snapshot 映射 |
無(未載入 daily_technical_snapshot) |
1.14 CCI (Commodity Channel Index)
| 項目 |
值 |
| 計算方式 |
Pipeline: ta.cci(high, low, close, length=20);Agent: 手動 (TP-SMA(TP))/(0.015*MAD) |
| 週期 |
20 |
| Pipeline 輸出欄位 |
cci |
| Snapshot 映射 |
無 |
1.15 MFI (Money Flow Index)
| 項目 |
值 |
| 計算方式 |
ta.mfi(high, low, close, volume, length=14) (pandas_ta) |
| 週期 |
14 |
| Pipeline 輸出欄位 |
mfi |
| Snapshot 映射 |
無 |
1.16 CHOP (Choppiness Index) -- Pipeline Only
| 項目 |
值 |
| 計算方式 |
ta.chop(high, low, close, length=14) (pandas_ta) |
| 週期 |
14 |
| Pipeline 輸出欄位 |
chop |
| Snapshot 映射 |
chop → chop_value |
| Snapshot 衍生欄位 |
chop_status ("extreme_squeeze" if >70 / "choppy" if >61.8 / "trending" if <38.2 / else "neutral"), chop_slope (3-day diff) |
1.17 WaveTrend (WT1, WT2) -- Pipeline Only
| 項目 |
值 |
| 計算方式 |
手動:HLC3 → EMA(n1=10) → deviation → CI → wt1 = EMA(CI, n2=21), wt2 = SMA(wt1, signal=4) |
| 參數 |
n1=10, n2=21, signal_period=4 |
| Pipeline 輸出欄位 |
wt1, wt2 |
| Snapshot 映射 |
wt1 → wt1_value, wt2 → wt2_value |
| Snapshot 衍生欄位 |
wt_signal ("deep_bullish_cross"/"bullish_cross"/"deep_bearish_cross"/"bearish_cross"/"none"), wt_status ("extreme_overbought"/"extreme_oversold"/"overbought"/"oversold"/"neutral"), wt_divergence ("bearish_warning"/"bullish_warning") |
1.18 Aroon -- Pipeline Only
| 項目 |
值 |
| 計算方式 |
ta.aroon(high, low, length=25) (pandas_ta) |
| 週期 |
25 |
| Pipeline 輸出欄位 |
aroon_up, aroon_down, aroon_osc |
| Snapshot 映射 |
→ aroon_up_value, aroon_down_value, aroon_osc_value |
| Snapshot 衍生欄位 |
aroon_signal ("strong_uptrend"/"strong_downtrend"/"neutral") |
1.19 KC (Keltner Channel) -- Pipeline Only
| 項目 |
值 |
| 計算方式 |
ta.kc(high, low, close, length=20, scalar=1.5) (pandas_ta) |
| 參數 |
period=20, multiplier=1.5 |
| Pipeline 輸出欄位 |
kc_upper, kc_middle, kc_lower |
| Snapshot 映射 |
直接 1:1 |
| Snapshot 衍生欄位 (TTM Squeeze) |
squeeze_on (bool: bb_upper < kc_upper AND bb_lower > kc_lower), squeeze_off (bool: 昨日 squeeze_on 今日 off), squeeze_duration (連續 squeeze 天數) |
1.20 Supertrend -- Pipeline Only
| 項目 |
值 |
| 計算方式 |
ta.supertrend(high, low, close, length=10, multiplier=3.0) (pandas_ta) |
| 參數 |
period=10, multiplier=3.0 |
| Pipeline 輸出欄位 |
supertrend (數值), supertrend_direction ("bullish"/"bearish") |
| Snapshot 映射 |
→ supertrend_value, supertrend_direction |
| Snapshot 衍生欄位 |
distance_to_supertrend_pct = (close-supertrend)/close*100, supertrend_flip_recent (bool: 5日內方向翻轉), supertrend_flip_type ("bullish"/"bearish") |
1.21 Donchian Channel -- Pipeline Only
| 項目 |
值 |
| 計算方式 |
ta.donchian(high, low, lower_length=N, upper_length=N) (pandas_ta),跑兩次 (20, 55) |
| 參數 |
short=20, long=55 |
| Pipeline 輸出欄位 |
donchian_high_20, donchian_low_20, donchian_high_55, donchian_low_55 |
| Snapshot 映射 |
直接 1:1 |
| Snapshot 衍生欄位 |
breakout_20d ("upward"/"downward"), breakout_55d ("upward"/"downward") |
1.22 Snapshot-Only 衍生指標(不在 Pipeline 中)
以下指標僅在 daily_analysis_snapshot.py 中從 OHLC 原始資料計算,不存在於 technical_indicators 表:
CTI (Correlation Trend Indicator)
- 計算:
np.corrcoef(close.tail(20), np.arange(20))[0,1],clipped [-1, 1]
- 欄位:
cti_score, cti_interpretation ("strong_trend"/"weak_trend"/"range"), cti_linearity ("high"/"medium"/"low"), cti_delta (placeholder,目前未實作)
Vortex Indicator (VI)
- 計算:手動
vm_plus/vm_minus rolling(14) / TR rolling(14)
- 欄位:
vi_plus, vi_minus, vi_signal ("bullish_cross"/"bearish_cross"/"bullish"/"bearish"), vi_plus_prev, vi_minus_prev
DMI Derived
- 計算:從
plus_di, minus_di 衍生
- 欄位:
di_spread = plus_di - minus_di, di_spread_slope ("widening"/"narrowing"/"stable"), adx_rising_days
Relative Strength vs Market
- 計算:
stock_close / benchmark_close (benchmark: 0050.TW), 20d/60d percentage change
- 欄位:
rs_eval_ready (bool), rs_vs_market_20d, rs_vs_market_60d, rs_slope ("improving"/"deteriorating"/"stable")
Slope Features (3-day delta)
rsi_slope_3:RSI[-1] - RSI[-4]
macd_hist_slope:histogram[-1] - histogram[-4]
adx_slope:ADX[-1] - ADX[-4]
chop_slope:CHOP[-1] - CHOP[-4]
2. JSON Schema 結構
build_agent_payload() 輸出 JSON 的完整欄位樹。此 payload 是 LLM 分析的輸入。
頂層結構
{
"meta": { ... },
"market_summary": { ... },
"indicator_settings": { ... },
"technical_indicators": { ... },
"signals": { ... },
"risk": { ... }
}
{
"symbol": "string (e.g. 2330.TW)",
"trading_date": "ISO 8601 date string",
"interval": "string (e.g. 1d)",
"bar_state": "string",
"data_quality": "string (complete/partial)",
"missing_fields": ["string", ...],
"data_source": "string",
"notes": ["string", ...]
}
2.2 market_summary
{
"current_price": float,
"change_percent": float,
"market_phase": "string",
"price_bar": {
"open": float,
"high": float,
"low": float,
"close": float,
"prev_close": float,
"vwap": float,
"is_adjusted": bool,
"adjustment_type": "string"
}
}
2.3 indicator_settings
{
"moving_averages": { "type": "SMA/EMA", "periods": [5,10,20,50,100,200], "source": "close" },
"macd": { "fast": 12, "slow": 26, "signal": 9, "source": "close" },
"rsi": { "period": 14, "source": "close" },
"kdj": { "k_period": 14, "d_period": 3, "j_period": 3, "source": "close" },
"bollinger_bands": { "period": 20, "stddev": 2, "source": "close" },
"atr": { "period": 14 },
"adx": { "period": 14 },
"cti": { "range": [-1.0, 1.0], "comment": "rolling corr(close, index)" },
"choppiness": { "period": 14 },
"wavetrend": { "n1": 10, "n2": 21, "signal_period": 4 },
"aroon": { "period": 25 },
"keltner": { "period": 20, "multiplier": 1.5 },
"supertrend": { "period": 10, "multiplier": 3.0 },
"donchian": { "short": 20, "long": 55 },
"relative_strength": { "benchmark": "0050.TW" }
}
2.4 technical_indicators
{
"trend": {
"moving_averages": {
"ma5": float, "ma20": float, "ma60": float,
"alignment": "bullish|bearish|mixed",
"price_position": "above_all|below_all|mixed",
"ma60_slope": "positive|negative|flat"
},
"cti": {
"score": float (-1~1),
"interpretation": "strong_trend|weak_trend|range",
"linearity": "high|medium|low"
},
"adx": { "value": float, "status": "trending|non_trending" },
"vortex": {
"vi_plus": float, "vi_minus": float,
"signal": "bullish_cross|bearish_cross|bullish|bearish"
},
"choppiness": { "value": float, "status": "extreme_squeeze|choppy|trending|neutral" },
"aroon": {
"up": float, "down": float, "oscillator": float,
"signal": "strong_uptrend|strong_downtrend|neutral"
},
"supertrend": {
"value": float, "direction": "bullish|bearish",
"distance_pct": float, "flip_recent": bool, "flip_type": "bullish|bearish|null"
},
"relative_strength": {
"eval_ready": bool, "rs_20d": float, "rs_60d": float,
"slope": "improving|deteriorating|stable"
}
},
"momentum": {
"macd": {
"dif": float, "dea": float, "histogram": float,
"cross_signal": "golden_cross|death_cross|none",
"divergence": "bullish|bearish|none"
},
"rsi": {
"value": float, "status": "overbought|oversold|neutral",
"divergence": "bullish_warning|bearish_warning|none"
},
"kdj": {
"k": float, "d": float, "j": float,
"signal": "high_passivization|low_passivization|neutral"
},
"wavetrend": {
"wt1": float, "wt2": float,
"signal": "deep_bullish_cross|bullish_cross|deep_bearish_cross|bearish_cross|none",
"status": "extreme_overbought|extreme_oversold|overbought|oversold|neutral",
"divergence": "bearish_warning|bullish_warning|null"
},
"di_derived": {
"spread": float, "spread_slope": "widening|narrowing|stable",
"adx_rising_days": int
}
},
"volatility": {
"bollinger_bands": {
"upper": float, "middle": float, "lower": float,
"percent_b": float, "bandwidth": float,
"status": "expanding|contracting|squeezing|normal"
},
"atr": { "value": float, "status": "high_volatility|normal" },
"keltner": { "upper": float, "middle": float, "lower": float },
"squeeze": { "on": bool, "off": bool, "duration": int },
"donchian": {
"high_20": float, "low_20": float,
"high_55": float, "low_55": float,
"breakout_20d": "upward|downward|null",
"breakout_55d": "upward|downward|null"
}
},
"volume": {
"current_volume": int, "average_volume_20": int,
"volume_ratio": float, "status": "aggressive_buying|aggressive_selling|normal",
"obv": { "trend": "new_high|new_low|flat", "slope": "positive|negative|flat" }
},
"slopes": {
"rsi_slope_3": float, "macd_hist_slope": float,
"adx_slope": float, "cti_delta": float|null, "chop_slope": float
},
"patterns": {
"detected": [
{
"name": "string", "score": float, "reliability": "string",
"trigger_price": float, "invalidation_price": float,
"detected_at": "string", "notes": "string"
}
],
"support_resistance": {
"major_support": float, "major_resistance": float,
"low_20d_min": float, "low_60d_min": float,
"distance_to_support_20d_pct": float, "distance_to_support_60d_pct": float,
"distance_to_support_20d_price": float, "distance_to_support_60d_price": float,
"historical_highs": {
"all_time_high": {
"price": float, "date": "string", "prev_price": float,
"touched_intraday": bool, "is_new_intraday": bool, "is_new_close": bool
},
"high_52w": {
"price": float, "date": "string", "prev_price": float,
"touched_intraday": bool, "is_new_intraday": bool, "is_new_close": bool
},
"swing_high_20d": { "price": float, "date": "string" },
"swing_high_60d": { "price": float, "date": "string" }
},
"notes": []
},
"breakout_context": {
"is_new_all_time_high": bool, "is_new_52w_high": bool,
"close_above_prev_ath": bool, "close_above_prev_52w_high": bool,
"breakout_type": "string|null",
"ath_eval_ready": bool, "high_52w_eval_ready": bool,
"distance_to_all_time_high_pct": float,
"distance_to_52w_high_pct": float,
"price_discovery_mode": bool
}
}
}
2.5 signals
{
"bias": "bullish|bearish|neutral",
"confidence": float (0.0000 ~ 1.0000),
"reasons": ["翻譯後的 ReasonCode 字串", ...],
"warnings": ["翻譯後的 WarningCode 字串", ...],
"volume_divergence": "PRICE_UP_VOL_LOW|PRICE_DOWN_VOL_HIGH|null",
"market_structure_events": ["MarketEvent value string", ...]
}
2.6 risk
{
"atr_value": float,
"atr_stop_distance": float,
"structure_stop": float,
"invalidation_level": float,
"rr_targets": [
{ "name": "to_major_resistance", "target_price": float }
],
"position_sizing_hint": {
"comment": "position size = risk_per_trade / stop_distance"
}
}
atr_stop_distance = ATR * 2.0
structure_stop = major_support (20-day close min)
invalidation_level = structure_stop,若 null 則 close - atr_stop_distance
rr_targets 最多一筆 (to_major_resistance),若 major_resistance 為 null 則空陣列
3. 訊號產生邏輯
3.1 Bias 計算 — 純 Rule-Based
compute_bias_confidence_reasons_warnings() in daily_analysis_snapshot.py
起始分數:score = 0.5
14 個評分類別及其精確調整值
| 類別 |
條件 |
分數調整 |
ReasonCode / WarningCode |
| A. Trend Structure |
ma_alignment == "bullish" |
+0.20 |
MA_BULLISH_STACK |
|
ma_alignment == "bearish" |
-0.20 |
MA_BEARISH_STACK |
|
close > ma60 |
±0 |
PRICE_ABOVE_MA60 |
|
close <= ma60 |
±0 |
PRICE_BELOW_MA60 |
|
MA entangled (|ma5-ma20|/close < 0.003) |
±0 |
MA_ENTANGLED |
| B. Trend Strength |
adx >= 25 |
+0.10 |
ADX_STRONG_TREND |
|
adx > adx_yesterday AND adx >= 20 |
+0.04 |
ADX_ACCELERATING |
|
plus_di > minus_di |
±0 |
DI_BULLISH |
|
plus_di <= minus_di |
±0 |
DI_BEARISH |
| C. Momentum |
vi_signal == "bullish_cross" |
+0.08 |
VORTEX_GOLDEN_CROSS |
|
vi_signal == "bearish_cross" |
-0.08 |
VORTEX_DEAD_CROSS |
|
vi_signal == "bullish" |
+0.04 |
(無) |
|
vi_signal == "bearish" |
-0.04 |
(無) |
|
vortex diff widening (bullish) |
+0.04 |
VORTEX_DIFF_WIDENING |
|
macd_hist > 0 |
+0.08 |
MACD_HIST_POSITIVE |
|
macd_hist < 0 |
-0.08 |
MACD_HIST_NEGATIVE |
|
|hist| > |prev_hist| |
+0.03 |
MACD_HIST_EXPANDING |
| D. Volume |
vol_ratio >= 1.2 + aggressive_buying |
+0.08 |
VOLUME_EXPANSION_BUY |
|
vol_ratio >= 1.2 + aggressive_selling |
-0.08 |
VOLUME_EXPANSION_SELL |
|
bullish MA + pullback + low volume |
±0 |
LOW_VOLUME_PULLBACK |
| E. Pullback |
close > high_20 |
+0.05 |
BREAKOUT_ABOVE_RECENT_HIGH |
| E2. Breakout Highs |
is_new_ath_close |
+0.10 |
BREAKOUT_ALL_TIME_HIGH |
|
is_new_52w_high_close (elif) |
+0.08 |
BREAKOUT_52W_HIGH |
| F. Squeeze/CTI |
cti_score >= 0.6 (且 < 0.8) |
+0.10 |
(無) |
|
cti_score <= -0.6 (且 > -0.8) |
-0.10 |
(無) |
|
|cti_score| >= 0.8 |
±0 |
⚠️ CTI_EXTREME_LINEARITY |
| Warnings |
rsi >= 70 (且 score > 0.5) |
-0.05 |
⚠️ RSI_OVERBOUGHT |
|
rsi <= 30 |
±0 |
⚠️ RSI_OVERSOLD |
|
rsi_divergence == "bearish_warning" |
-0.08 |
⚠️ DIVERGENCE_BEARISH |
|
rsi_divergence == "bullish_warning" |
+0.06 |
⚠️ DIVERGENCE_BULLISH |
| G. Choppiness |
chop_status in (extreme_squeeze, choppy) |
score += (0.5-score)*0.30 |
⚠️ CHOP_CHOPPY |
|
chop_status == "trending" |
+0.05 |
CHOP_TRENDING |
| H. WaveTrend |
deep_bullish_cross |
+0.10 |
WT_DEEP_BULLISH_CROSS |
|
deep_bearish_cross |
-0.10 |
WT_DEEP_BEARISH_CROSS |
|
bullish_cross |
+0.06 |
WT_BULLISH_CROSS |
|
bearish_cross |
-0.06 |
WT_BEARISH_CROSS |
|
extreme_overbought |
-0.05 |
⚠️ WT_EXTREME |
|
overbought |
-0.03 |
⚠️ WT_OVERBOUGHT |
|
wt_divergence == "bearish_warning" |
-0.06 |
⚠️ WT_DIVERGENCE_BEARISH |
|
wt_divergence == "bullish_warning" |
+0.04 |
⚠️ WT_DIVERGENCE_BULLISH |
| I. Aroon |
strong_uptrend AND score >= 0.5 |
+0.04 |
AROON_FRESH_UPTREND |
|
strong_downtrend AND score <= 0.5 |
-0.04 |
AROON_FRESH_DOWNTREND |
|
aroon_up < 50 AND adx > 25 |
±0 |
⚠️ AROON_STALE_TREND |
| J. TTM Squeeze |
squeeze_off |
+0.08 |
SQUEEZE_RELEASE |
|
squeeze_duration > 10 |
±0 |
⚠️ SQUEEZE_PROLONGED |
| K. DMI Derived |
di_spread_slope == "widening" AND adx > 20 |
+0.04 |
DI_SPREAD_WIDENING |
|
adx_rising_days >= 3 |
+0.03 |
ADX_SUSTAINED_RISE |
| L. Supertrend |
direction == "bullish" |
+0.03 |
SUPERTREND_BULLISH |
|
direction == "bearish" |
±0 |
⚠️ SUPERTREND_BEARISH |
|
flip == "bullish" |
+0.06 |
SUPERTREND_FLIP_BULLISH |
|
flip == "bearish" |
-0.06 |
SUPERTREND_FLIP_BEARISH |
|
|dist_supertrend| < 1.0 |
±0 |
⚠️ NEAR_SUPERTREND |
| M. Donchian |
breakout_20d == "upward" |
+0.05 |
DONCHIAN_BREAKOUT_20D |
|
breakout_20d == "downward" |
-0.05 |
(無) |
|
breakout_55d == "upward" |
+0.08 |
DONCHIAN_BREAKOUT_55D |
|
breakout_55d == "downward" |
-0.08 |
(無) |
| N. RS vs Market |
rs_slope == "rising" AND rs_20d > 2.0 |
+0.04 |
RS_OUTPERFORM |
|
rs_slope == "falling" AND rs_20d < -2.0 |
-0.04 |
RS_UNDERPERFORM + ⚠️ RS_LAGGING |
最終處理
score = max(0.0, min(1.0, score)) # clamp to [0, 1]
if score >= 0.6 → bias = "bullish"
if score <= 0.4 → bias = "bearish"
else → bias = "neutral"
confidence = round(score, 4) # confidence 就是 score 本身
3.2 Confidence 計算
Confidence 就是累積分數本身,沒有獨立的 confidence 公式或 sub-dimension breakdown。
confidence = round(score, 4) 其中 score 是從 0.5 開始經過 14 個類別累加/累減後 clamp 到 [0, 1] 的值。
3.3 Warnings 產生
Warnings 在評分過程中同步產生,由 WarningCode enum 標記。額外的 warnings 包括:
| 條件 |
WarningCode |
| close / major_resistance >= 0.98 |
NEAR_RESISTANCE |
| close / major_support <= 1.02 |
NEAR_SUPPORT |
| distance_to_ath_pct 在 -2.0% ~ 0 之間 |
NEAR_ALL_TIME_HIGH |
| avg_volume_20 < 500,000 OR avg_amount_20 < 10M OR flat_days_20 > 10 |
LOW_LIQUIDITY |
Warning 去重規則:若 NEAR_ALL_TIME_HIGH 存在且 major_resistance 與 prev_ath_price 差距 < 0.5%,則移除 NEAR_RESISTANCE。
3.4 Signal Channel 分類
classify_channel() in api/routes/signals.py -- 嚴格 cascade(first match wins):
共用品質門檻 _passes_common_quality()
bias, confidence, data_quality, macd_histogram, cti_score, volume_ratio 皆非 None
data_quality == "complete"
Channel 1: momentum_bullish
- bias == "bullish" AND confidence >= 0.70
- rsi_divergence != "bearish_warning"(RSI 背離否決)
- trend quality score >= 2/5:
- +1 if ma_alignment == "bullish" OR price_position == "above_all"
- +1 if cti_score > 0.6
- +1 if MACD histogram expanding (hist > 0 AND |hist| > |prev_hist|)
- +1 if volume_ratio >= 1.2
- +1 if has_high_pattern
Channel 2: momentum_bearish
- bias == "bearish" AND confidence >= 0.70
- rsi_divergence != "bullish_warning"
- trend quality score >= 2/5(方向反轉)
Channel 3: reversal_bullish
- confidence >= 0.40 AND price_position != "above_all"
- CTI 否決:cti_score NOT < -0.8(過度線性下跌不可反轉)
- reversal score >= 3/5:
- +1 if rsi <= 35 OR rsi_divergence == "bullish_warning"
- +1 if macd_histogram > prev_macd_histogram
- +1 if close > ma5 AND ma5 >= ma20
- +1 if volume_ratio >= 1.2 AND volume_status == "aggressive_buying"
- +1 if has_high_pattern AND has_bottom_pattern
Channel 4: reversal_bearish
- confidence >= 0.40 AND price_position != "below_all"
- CTI 否決:cti_score NOT > 0.8
- reversal score >= 3/5(方向反轉)
Channel 5: squeeze_bullish
- bb_status in ("contracting", "squeezing") AND adx < 20
- ma_alignment in ("bullish", "mixed") AND price_position != "below_all"
- 若 close > bb_upper 且 bandwidth_ratio >= 1.3 → "strong" breakout → 踢出 squeeze
Channel 6: squeeze_bearish
- 同 squeeze_bullish 但方向反轉
- 若 close < bb_lower 且 bandwidth_ratio >= 1.3 → 踢出 squeeze
Channel 7: neutral
3.5 Entry/Exit 訊號
系統目前沒有明確的 entry/exit 訊號。只有方向性的 bias + confidence + signal channel。
沒有以下機制:
- 明確的 entry_price / exit_price
- Stop-loss / take-profit 具體建議(risk block 提供 invalidation_level 和 rr_targets 但非交易指令)
- 持倉期間建議
4. 資料來源與覆蓋範圍
4.1 股票池
重要更正 (2026-02-25 DB 實查):系統實際爬取的是台灣全市場(TWSE + TPEx),遠超 markets.yaml 定義的 20 支精選股。
4.1.1 設定層 vs 實際 DB
| 層級 |
來源 |
台股數量 |
美股數量 |
說明 |
markets.yaml 設定 |
手動清單 |
20 (.TW) |
7 |
原始精選清單 |
init.sql 種子 |
SQL 初始化 |
25 (.TW) |
7 |
含額外 2379, 2327, 2301, 2395, 2207 |
taiwan_stocks.py 同步 |
TWSE/TPEx API |
全市場 |
— |
動態同步上市櫃全股票 |
stocks 表實際 |
DB |
2,019 (.TW) + 897 (.TWO) |
7 |
共 2,923 支 |
4.1.2 各表實際資料量(DB 查詢 2026-02-25)
| 表 |
台股 .TW |
櫃買 .TWO |
美股 |
合計 tickers |
合計 rows |
stocks (master) |
2,019 |
897 |
7 |
2,923 |
2,923 |
stock_ohlc |
1,229 |
897 |
7 |
2,133 |
2,397,970 |
daily_technical_snapshot |
1,228 |
897 |
0 |
2,125 |
382,385 |
technical_indicators |
6 |
0 |
7 |
13 |
3,900 |
daily_chip_snapshot |
1,228 |
897 |
0 |
2,125 |
769,250 |
daily_financial_snapshot |
1,228 |
897 |
0 |
2,125 |
216,649 |
4.1.3 關鍵發現
- 美股無 snapshot:7 支美股有 OHLC 資料但
daily_technical_snapshot 為 0 筆。回測僅能對台股進行
technical_indicators 表僅 13 支:此表僅覆蓋 6 支台股 (2317, 2330, 2412, 2454, 2881, 2882) + 7 支美股。絕大多數股票的指標由 daily_technical_snapshot 在 snapshot 流程中直接從 OHLC 計算,不經過此表
- 台股包含 ETF:1,228 支 .TW 中約 136 支為 ETF(代號 00xxx),1,950 支為一般股票,20 支為其他(存託憑證等),19 支為 TWO 歸類問題
- TWO (櫃買) 已覆蓋:897 支櫃買市場股票已完整爬取
4.1.4 markets.yaml 精選 20 支台股
以下為系統原始設定的核心觀察清單(仍用於 RS benchmark):
| Ticker |
名稱 |
Sector |
| 2330.TW |
台積電 |
半導體 |
| 2317.TW |
鴻海 |
電子代工 |
| 2454.TW |
聯發科 |
IC 設計 |
| 2412.TW |
中華電 |
電信 |
| 2308.TW |
台達電 |
電子零組件 |
| 2881.TW |
富邦金 |
金融 |
| 2882.TW |
國泰金 |
金融 |
| 2891.TW |
中信金 |
金融 |
| 2303.TW |
聯電 |
半導體 |
| 3711.TW |
日月光投控 |
封測 |
| 1301.TW |
台塑 |
塑化 |
| 1303.TW |
南亞 |
塑化 |
| 2002.TW |
中鋼 |
鋼鐵 |
| 2886.TW |
兆豐金 |
金融 |
| 2884.TW |
玉山金 |
金融 |
| 2892.TW |
第一金 |
金融 |
| 5880.TW |
合庫金 |
金融 |
| 3008.TW |
大立光 |
光學 |
| 2357.TW |
華碩 |
品牌 PC |
| 2382.TW |
廣達 |
伺服器/AI |
4.1.5 美股 7 支 (Magnificent 7)
AAPL, MSFT, GOOGL, AMZN, NVDA, META, TSLA(僅有 OHLC,無 snapshot/chip/financial)
4.2 資料來源
| 項目 |
值 |
| OHLCV 來源 |
taiwan_stocks.py 同步全市場(FinMind 為主要來源,yfinance 備援) |
| 台股清單同步 |
taiwan_stocks.py 動態從 TWSE/TPEx API 拉取上市櫃完整名單 |
| 頻率 |
日線 (1d) |
| 更新排程 |
cron 0 6 * * 1-5 (Asia/Taipei 工作日 06:00) |
| Rate Limit |
60 requests/minute |
| 重試 |
max_attempts=3, exponential backoff factor=2 |
4.3 資料欄位與日期範圍
OHLCV 表 (stock_ohlc):ticker, timestamp, open, high, low, close, volume, adjusted_close
| 資料集 |
最早日期 |
最新日期 |
說明 |
stock_ohlc (TW/TWO) |
2021-02-04 |
2026-02-24 |
⚠️ 非 2020-01-01,markets.yaml 的 start_date: 2020-01-01 為設定值,實際 DB 從 2021-02 起 |
stock_ohlc (US) |
2021-02-04 |
2026-02-24 |
~1,269 交易日 |
daily_technical_snapshot |
2023-11-08 |
2026-02-24 |
549 個交易日 |
| snapshot 多數台股起始 |
2025-08-14 |
2026-02-24 |
1,738 支台股僅 ~126 天 snapshot |
| snapshot 早期批次 |
2024-02-02 |
2026-02-24 |
~316 支有 492 天(~2 年) |
daily_chip_snapshot |
2024-08-27 |
2026-02-24 |
|
daily_financial_snapshot |
2025-09-18 |
2026-02-24 |
僅 ~5 個月,非常新 |
technical_indicators |
2024-11-11 |
2026-01-30 |
僅 13 支股票 |
⚠️ 回測可用資料深度警告
| 問題 |
影響 |
| OHLC 起始為 2021-02-04 而非 2020-01-01 |
無法回測 COVID crash (2020-01~03),BACKTEST_SPEC 的 covid_crash 子區間不可用 |
| 多數 snapshot 僅從 2025-08-14 起 |
若回測依賴 snapshot 欄位(bias, confidence, channel),多數股票僅有 ~6 個月資料 |
| 316 支有 ~2 年 snapshot |
這批是回測可用的核心股票池 |
| Financial snapshot 僅 ~5 個月 |
Strategy E(三維度投票)的資料極淺 |
| 美股無 snapshot |
Strategy A~F 全部無法對美股執行 |
4.4 前日資料處理
- 前日指標值:從
indicators_df.iloc[-2] 取得(不是從 DB 載入前日 snapshot)
- 前日收盤:通過
load_prev_close() SQL 查詢
- cti_delta:placeholder,目前未實作
- 無 prev_day_snapshot 概念:不存在載入前一日完整 snapshot 的邏輯
4.5 分析維度覆蓋狀態
| 維度 |
狀態 |
DB 覆蓋 |
說明 |
| Technical |
ready |
2,125 tickers / 382K rows |
完整實作,180+ 欄位 |
| Chip (籌碼) |
ready |
2,125 tickers / 769K rows |
Engine A (法人流向 0.65) + Engine B (融資壓力 0.35) |
| Financial (財務) |
ready |
2,125 tickers / 217K rows |
Engine A (營收) + B (獲利) + C (評價),但僅 5 個月資料 |
| Fundamental (基本面) |
not_ready |
0 |
placeholder,所有欄位標記 status="placeholder" |
5. 目前缺少什麼(Backtesting Gaps)
5.1 無可量化的 Entry/Exit 規則
- 系統只產生 bias (bullish/bearish/neutral) + confidence (0~1)
- 沒有明確定義 "何時進場" 和 "何時出場" 的門檻
- Signal channel 提供分類但非交易觸發條件
- 需要:定義 confidence 門檻(如 >= 0.7 做多)、持倉退出條件(如 bias 翻轉或 confidence 低於門檻)
5.2 Confidence 無法分解
- Confidence 是一個累積分數,沒有 sub-dimension breakdown
- 無法分析 "哪個指標類別貢獻了多少信心"
- 需要:記錄各類別的分數貢獻(如 trend_score, momentum_score, volume_score)
5.3 無 Benchmark / 基準收益
- RS vs Market 用 0050.TW 但僅用於相對強弱判斷
- 沒有 buy-and-hold 基準用於比較策略表現
- 需要:記錄 benchmark 每日收益供 alpha 計算
5.4 無持倉追蹤
- 系統為每日獨立 snapshot,沒有 "目前持有" 的概念
- 無法計算連續持倉的盈虧
- 需要:持倉狀態機 (idle → long → exit) 和對應的 PnL 追蹤
5.5 Signal Scoring Weights 與 Snapshot Bias 斷裂
signal_scoring.py 定義了精細的權重系統 (momentum, reversal, squeeze weights)
- 但
daily_analysis_snapshot.py 的 compute_bias_confidence_reasons_warnings() 使用獨立的 hardcoded 分數調整
- 兩套邏輯各自獨立,signal channel 分類與 bias 計算用不同的權重
- 需要:統一或明確區分兩套系統的角色
5.6 Pattern 可靠性未整合
patterns.detected 有 score, reliability 欄位
- 但 patterns 未參與 bias/confidence 計算
- 需要:決定 pattern 是否應影響信號,以及如何影響
5.7 多維度整合缺失
- Technical, Chip, Financial 三個維度各自產生 bias/confidence
- 沒有跨維度的綜合信號(如:技術面看多 + 籌碼面看多 → 強化信號)
- 需要:定義多維度融合公式或投票機制
5.8 歷史回測基礎設施
已有部分基礎(signal_backtest.py):
- Horizons: 1, 3, 5, 20 trading days
- Deadzone: ±0.2%
- Cooldown: 5 days per ticker
- Squeeze volatility threshold: 5%
但缺少:
- 完整的回測引擎(entry/exit 模擬)
- 滑點和手續費模型
- 資金管理規則
- 績效指標計算 (Sharpe, max drawdown, win rate)
附錄:完整 Enum 值
SignalChannel (7)
momentum_bullish, momentum_bearish, reversal_bullish, reversal_bearish, squeeze_bullish, squeeze_bearish, neutral
Bias (3)
bullish, bearish, neutral
RiskFlag (11)
rsi_overbought, rsi_oversold, macd_divergence, rsi_divergence_bearish, rsi_divergence_bullish, bb_expanding, breakout_watch, volume_divergence, margin_buildup_risk, distribution_signs, foreign_distribution
QualityFlag (3)
complete, partial, low_liquidity
ReasonCode (48)
MA_BULLISH_STACK, MA_BEARISH_STACK, PRICE_ABOVE_MA60, PRICE_BELOW_MA60, MA_ENTANGLED, ADX_STRONG_TREND, ADX_ACCELERATING, DI_BULLISH, DI_BEARISH, VORTEX_GOLDEN_CROSS, VORTEX_DEAD_CROSS, VORTEX_DIFF_WIDENING, MACD_HIST_POSITIVE, MACD_HIST_NEGATIVE, MACD_HIST_EXPANDING, VOLUME_EXPANSION_BUY, VOLUME_EXPANSION_SELL, LOW_VOLUME_PULLBACK, PULLBACK_HOLD_MA5, BREAKOUT_ABOVE_RECENT_HIGH, SQUEEZE_ACTIVE, SQUEEZE_NARROW_BB, FOREIGN_NET_BUY, FOREIGN_NET_SELL, INST_CONSENSUS_BUY, INST_CONSENSUS_SELL, INSTITUTIONAL_STREAK_BUY, INSTITUTIONAL_STREAK_SELL, FLOW_WITH_VOLUME, SHORT_COVERING, SHORT_BUILDUP, MARGIN_CLEARING, BREAKOUT_52W_HIGH, BREAKOUT_ALL_TIME_HIGH, CHOP_TRENDING, WT_BULLISH_CROSS, WT_BEARISH_CROSS, WT_DEEP_BULLISH_CROSS, WT_DEEP_BEARISH_CROSS, AROON_FRESH_UPTREND, AROON_FRESH_DOWNTREND, SQUEEZE_RELEASE, DI_SPREAD_WIDENING, ADX_SUSTAINED_RISE, SUPERTREND_BULLISH, SUPERTREND_FLIP_BULLISH, SUPERTREND_FLIP_BEARISH, DONCHIAN_BREAKOUT_20D, DONCHIAN_BREAKOUT_55D, RS_OUTPERFORM, RS_UNDERPERFORM
WarningCode (27)
RSI_OVERBOUGHT, RSI_OVERSOLD, DIVERGENCE_BEARISH, DIVERGENCE_BULLISH, CTI_EXTREME_LINEARITY, CLIMAX_UNCONFIRMED, NEAR_RESISTANCE, NEAR_SUPPORT, VOLUME_DIVERGENCE, DISTRIBUTION_VOLUME, BREAKOUT_WATCH, BREAKOUT_CONFIRMED, DATA_PARTIAL, LOW_LIQUIDITY, NEAR_ALL_TIME_HIGH, CHOP_CHOPPY, WT_OVERBOUGHT, WT_OVERSOLD, WT_EXTREME, WT_DIVERGENCE_BEARISH, WT_DIVERGENCE_BULLISH, AROON_STALE_TREND, SQUEEZE_PROLONGED, SUPERTREND_BEARISH, NEAR_SUPERTREND, RS_LAGGING, MARGIN_INCREASING_FAST, FOREIGN_SELLING_STREAK, DATA_PARTIAL_CHIP, LOW_FLOAT_VOLUME, USING_STALE_MARGIN_DATA
MarketEvent (11)
TOUCHED_ALL_TIME_HIGH, NEW_ALL_TIME_HIGH, ATH_INTRADAY_ONLY, TOUCHED_52W_HIGH, NEW_52W_HIGH, HIGH_52W_INTRADAY_ONLY, LIMIT_UP_CLOSE, LIMIT_DOWN_CLOSE, GAP_UP, GAP_DOWN, NEAR_ALL_TIME_HIGH
ChipSignalType (9)
neutral, institutional_accumulation, short_squeeze, foreign_entry, cost_defense, institutional_distribution, retail_trap, margin_call_risk, fake_breakout