# -*- coding: utf-8 -*-
"""品質 + 動能 + 低波 三因子複合 — finlab 台股真實回測(可重現)。

對應文章:https://finlab.finance/blog/ai-agent-quant-trading-tools
回測區間:2018-01-01 ~ 2026-06-09(全站 canonical 快照日)
基準:0050 含息(etl:adj_close 還原價買進持有)
成本:finlab sim() 台股預設(手續費 0.1425%、賣出證交稅 0.3%),未另設滑價

執行:
  cd ~/Documents/finlab && UV_ENV_FILE=.env uv run --with finlab python strategy.py

本程式僅供量化研究與教學,過去績效不代表未來表現,不構成任何投資建議。
"""
import finlab
from finlab import data
from finlab.backtest import sim

finlab.login()  # 第一次執行會引導你完成登入,不需手動貼 token

# 1) 載入資料
close = data.get("price:收盤價")
volume = data.get("price:成交股數")
roe = data.get("fundamental_features:ROE稅後").index_str_to_date()
roe = roe.reindex(close.index, method="ffill")  # 季財報對齊公布時點後補到日頻

# 2) 流動性過濾:近 20 日平均成交金額 > 1,000 萬
amount = (close * volume).rolling(20).mean()
liquid = (amount > 10_000_000) & close.notna()

# 3) 三個因子
quality = roe                                    # 品質:股東權益報酬率,越高越好
momentum = close.pct_change(120)                 # 動能:近 120 個交易日報酬,越高越好
volatility = close.pct_change().rolling(60).std()  # 波動:近 60 日日報酬標準差,越低越好

# 4) 池內百分位排名後相加(低波取反向排名)
rank_quality = quality.where(liquid).rank(axis=1, pct=True)
rank_momentum = momentum.where(liquid).rank(axis=1, pct=True)
rank_low_vol = volatility.where(liquid).rank(axis=1, pct=True, ascending=False)
score = rank_quality + rank_momentum + rank_low_vol

# 5) 取總分前 25 名,依分數平方加權(讓分數高的股票拿到較大權重),每月再平衡
top25 = score.rank(axis=1, ascending=False) <= 25
weight = (score[top25].fillna(0)) ** 2
weight = weight.div(weight.sum(axis=1), axis=0)

report = sim(weight, resample="M", name="品質動能低波三因子")
report.display()
