# -*- coding: utf-8 -*-
"""
Python 股票選股與回測教學:把「選股條件」寫成程式並回測(finlab 套件,真實台股資料)
作者:FinLab 量化研究團隊 | 資料日期:2026-08 | 視窗:2018-01 ~ 2026-08,月頻換股

示範策略(反面教材):月營收年增 > 20%、ROE > 8%、收盤價站上 60 日均線,
通過者依月營收年增率取前 30 檔等權。2018-01 ~ 2026-08 年化 17.7%、日夏普 0.70、
最大回撤 -47.8%,輸給同期買進持有 0050(含息)的年化 24.8%、日夏普 1.18、最大回撤 -34.0%。
完整五組對照(含 2020 舊版 10 日均線篩選)見同資料夾 metrics.json。

執行方式:
  先完成 https://finlab.finance/setup 的 AI 輔助設定流程,再執行本檔。
  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:成交股數')

# 2) 財務資料:index_str_to_date() 把季報 / 月營收對齊到「實際公告期限」,避免用到還沒公布的數字(前視偏差)
rev_yoy = data.get('monthly_revenue:去年同月增減(%)').index_str_to_date().reindex(close.index, method='ffill')
roe = data.get('fundamental_features:ROE稅後').index_str_to_date().reindex(close.index, method='ffill')

# 3) 股票池:60 日均成交金額 > 5,000 萬、股價 > 10 元(買得到、賣得掉)
amount = (close * volume).rolling(60).mean()
pool = (amount > 50_000_000) & (close > 10)

# 4) 選股條件:成長(月營收年增)+ 品質(ROE)+ 趨勢確認(站上 60 日均線)
cond = pool & (rev_yoy > 20) & (roe > 8) & (close > close.rolling(60).mean())

# 想加本益比上限,就多一行條件(文章實測:上限越嚴、持股越少、績效越差)
# pe = data.get('price_earning_ratio:本益比').reindex(close.index, method='ffill')
# cond = cond & (pe > 0) & (pe < 15)

# 5) 通過條件的股票依月營收年增排序,取前 30 檔等權
score = rev_yoy.where(cond)
position = score.rank(axis=1, ascending=False) <= 30

# 6) 回測:月頻換股,sim() 內扣手續費 0.1425% 與賣出證交稅 0.3%
report = sim(position, resample='M', name='營收年增 + ROE + 趨勢')
print(report.get_metrics()['ratio'])
# report.display()  # 互動式報告

# 2020 舊版寫法(對照):10 日均線在全市場前 20% 的股票等權
# ma10 = close.rolling(10).mean().where(pool)
# old_position = ma10 > ma10.quantile(0.8, axis=1)
# old_report = sim(old_position, resample='M', name='舊版:10 日均線前 20%')
