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

對應文章:https://finlab.finance/blog/openclaw-lobster-finlab-stock-screening
回測區間:2018-01-01 ~ 2026-07-09(資料快照日)
基準:0050 含息(etl:adj_close 還原價買進持有)
成本:finlab sim() 台股預設(手續費 0.1425%、賣出證交稅 0.3%),未另設滑價

重點:只挑「殖利率最高」的股票在這段期間反而虧損(殖利率高往往是股價先跌出來的);
加上「公司賺錢(ROE)」和「股價有走勢(動能)」後結果翻正;
再加第四個條件「股價平穩(低波動)」,最大回撤與波動就比 0050 淺,
日夏普與月索提諾也高於 0050。

執行:
  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 會自動引導登入
finlab.login()

# 1) 載入資料:收盤價、成交股數、殖利率、ROE
close = data.get("price:收盤價")
volume = data.get("price:成交股數")
dividend_yield = data.get("price_earning_ratio:殖利率(%)").reindex(close.index, method="ffill")
roe = data.get("fundamental_features:ROE稅後").index_str_to_date()
roe = roe.reindex(close.index, method="ffill")

# 2) 流動性過濾 + 殖利率門檻(殖利率 > 3% 才納入候選)
amount = (close * volume).rolling(20).mean()
candidates = (amount > 10_000_000) & close.notna() & (dividend_yield > 3)

# 3) 四個因子各自在候選池內做百分位排名(0~1,越高越好)
#    低波動用 120 個交易日的日報酬標準差,波動越低排名越前
volatility = close.pct_change().rolling(120).std()
rank_yield = dividend_yield.where(candidates).rank(axis=1, pct=True)
rank_quality = roe.where(candidates).rank(axis=1, pct=True)
rank_momentum = close.pct_change(120).where(candidates).rank(axis=1, pct=True)
rank_low_vol = volatility.where(candidates).rank(axis=1, pct=True, ascending=False)
score = rank_yield + rank_quality + rank_momentum + rank_low_vol

# 4) 取總分前 15 名,依分數加權,每月再平衡
top15 = score.rank(axis=1, ascending=False) <= 15
weight = score[top15].fillna(0)
weight = weight.div(weight.sum(axis=1), axis=0)

report = sim(weight, resample="M", name="高股息品質動能低波動")
report.display()
