跳轉到

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_5ma5, ma_20ma20, ma_60ma60, ma_120ma120, ma_240ma240
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 映射 rsirsi_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 映射 macdmacd_dif, macd_signalmacd_dea, macd_histmacd_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 映射 kkdj_k, dkdj_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 映射 atratr_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 映射 adxadx_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_20avg_volume_20volume_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 映射 chopchop_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 映射 wt1wt1_value, wt2wt2_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": { ... }
}

2.1 meta

{
  "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

  1. bias == "bullish" AND confidence >= 0.70
  2. rsi_divergence != "bearish_warning"(RSI 背離否決)
  3. trend quality score >= 2/5:
  4. +1 if ma_alignment == "bullish" OR price_position == "above_all"
  5. +1 if cti_score > 0.6
  6. +1 if MACD histogram expanding (hist > 0 AND |hist| > |prev_hist|)
  7. +1 if volume_ratio >= 1.2
  8. +1 if has_high_pattern

Channel 2: momentum_bearish

  1. bias == "bearish" AND confidence >= 0.70
  2. rsi_divergence != "bullish_warning"
  3. trend quality score >= 2/5(方向反轉)

Channel 3: reversal_bullish

  1. confidence >= 0.40 AND price_position != "above_all"
  2. CTI 否決:cti_score NOT < -0.8(過度線性下跌不可反轉)
  3. reversal score >= 3/5:
  4. +1 if rsi <= 35 OR rsi_divergence == "bullish_warning"
  5. +1 if macd_histogram > prev_macd_histogram
  6. +1 if close > ma5 AND ma5 >= ma20
  7. +1 if volume_ratio >= 1.2 AND volume_status == "aggressive_buying"
  8. +1 if has_high_pattern AND has_bottom_pattern

Channel 4: reversal_bearish

  1. confidence >= 0.40 AND price_position != "below_all"
  2. CTI 否決:cti_score NOT > 0.8
  3. reversal score >= 3/5(方向反轉)

Channel 5: squeeze_bullish

  1. bb_status in ("contracting", "squeezing") AND adx < 20
  2. ma_alignment in ("bullish", "mixed") AND price_position != "below_all"
  3. 若 close > bb_upper 且 bandwidth_ratio >= 1.3 → "strong" breakout → 踢出 squeeze

Channel 6: squeeze_bearish

  1. 同 squeeze_bullish 但方向反轉
  2. 若 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.yamlstart_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.pycompute_bias_confidence_reasons_warnings() 使用獨立的 hardcoded 分數調整
  • 兩套邏輯各自獨立,signal channel 分類與 bias 計算用不同的權重
  • 需要:統一或明確區分兩套系統的角色

5.6 Pattern 可靠性未整合

  • patterns.detectedscore, 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