Basic strategy temp
給你更多的策略模板,幫助你有更多靈感!
安裝套件¶
In [1]:
Copied!
!pip install finlab > log.txt
!pip install ta-lib-bin > log.txt
!pip install finlab > log.txt
!pip install ta-lib-bin > log.txt
股價創新高動能¶
In [12]:
Copied!
from finlab.backtest import sim
close = data.get("price:收盤價")
position = (close == close.rolling(250).max())
report = sim(position, resample="M", name="創年新高策略",upload=False)
report.display()
from finlab.backtest import sim
close = data.get("price:收盤價")
position = (close == close.rolling(250).max())
report = sim(position, resample="M", name="創年新高策略",upload=False)
report.display()
00632R 0.125 00664R 0.125 00674R 0.125 00676R 0.125 00682U 0.125 00683L 0.125 00686R 0.125 1218 0.125 Name: 2022-07-31 00:00:00, dtype: float64
Timestamp('2022-07-31 00:00:00')
In [7]:
Copied!
from finlab import data
from finlab.backtest import sim
pe = data.get('price_earning_ratio:本益比')
rev = data.get('monthly_revenue:當月營收')
rev_ma3 = rev.average(3)
rev_ma12 = rev.average(12)
營業利益成長率 = data.get('fundamental_features:營業利益成長率')
peg = (pe/營業利益成長率)
cond1 = rev_ma3/rev_ma12 > 1.1
cond2 = rev/rev.shift(1) > 0.9
cond_all = cond1 & cond2
result = peg*(cond_all)
# 月營收截止日換股
position = result[result>0].is_smallest(10).reindex(rev.index_str_to_date().index, method='ffill')
report = sim(position=position, resample='D', name="策略教學範例:peg_rev", stop_loss=0.1, upload=False)
report.display()
from finlab import data
from finlab.backtest import sim
pe = data.get('price_earning_ratio:本益比')
rev = data.get('monthly_revenue:當月營收')
rev_ma3 = rev.average(3)
rev_ma12 = rev.average(12)
營業利益成長率 = data.get('fundamental_features:營業利益成長率')
peg = (pe/營業利益成長率)
cond1 = rev_ma3/rev_ma12 > 1.1
cond2 = rev/rev.shift(1) > 0.9
cond_all = cond1 & cond2
result = peg*(cond_all)
# 月營收截止日換股
position = result[result>0].is_smallest(10).reindex(rev.index_str_to_date().index, method='ffill')
report = sim(position=position, resample='D', name="策略教學範例:peg_rev", stop_loss=0.1, upload=False)
report.display()
1457 0.1 1906 0.1 2537 0.1 2603 0.1 3221 0.1 3706 0.1 4971 0.1 5309 0.1 6640 0.1 9906 0.1 Name: 2022-06-10 00:00:00, dtype: float64
Timestamp('2022-06-10 00:00:00')
技術指標選股¶
高 RSI 技術指標策略
上述策略會選出很多檔標的,要是我們只想要選出 20 檔要怎麼寫呢?這個策略我們可以將 RSI 最大的 20 檔股票納入組合,並且持有一週,來試試看效果如何。我們可以用 data.indicator 來計算所有股票的 RSI,也可以用 rsi.is_largest 來計算此股票的 RSI 是否是最大的 20 檔。
In [9]:
Copied!
from finlab import data
from finlab.backtest import sim
# 選出 RSI 最大的 20 檔股票
rsi = data.indicator('RSI', timeperiod=20)
position = rsi.is_largest(20)
# 回測,每週(W)調整一次
report = sim(position, resample='W', name="高RSI策略", upload=False)
report.display()
from finlab import data
from finlab.backtest import sim
# 選出 RSI 最大的 20 檔股票
rsi = data.indicator('RSI', timeperiod=20)
position = rsi.is_largest(20)
# 回測,每週(W)調整一次
report = sim(position, resample='W', name="高RSI策略", upload=False)
report.display()
00632R 0.05 00643K 0.05 00664R 0.05 00674R 0.05 00676R 0.05 00686R 0.05 020004 0.05 020015 0.05 02001R 0.05 2364 0.05 2936 0.05 2939 0.05 3205 0.05 5102 0.05 5904 0.05 6655 0.05 6796 0.05 8234 0.05 8427 0.05 9188 0.05 Name: 2022-07-10 00:00:00, dtype: float64
Timestamp('2022-07-10 00:00:00')
In [6]:
Copied!
from finlab import data
from finlab.backtest import sim
股本 = data.get('financial_statement:股本')
price = data.get('price:收盤價')
市值 = 股本 * price / 10 * 1000
df1 = data.get('financial_statement:投資活動之淨現金流入_流出')
df2 = data.get('financial_statement:營業活動之淨現金流入_流出')
自由現金流 = (df1 + df2).rolling(4).mean()
稅後淨利 = data.get('fundamental_features:經常稅後淨利')
權益總計 = data.get('financial_statement:股東權益總額')
股東權益報酬率 = 稅後淨利/ 權益總計
營業利益成長率 = data.get('fundamental_features:營業利益成長率')
當月營收 = data.get('monthly_revenue:當月營收')* 1000
當季營收 = 當月營收.rolling(4).sum()
市值營收比 = 市值 / 當季營收
condition1 = (市值 < 1e10)
condition2 = 自由現金流 > 0
condition3 = 股東權益報酬率 > 0
condition4 = 營業利益成長率 > 0
condition5 = 市值營收比 < 5
position = condition1 & condition2 & condition3 & condition4 & condition5
report = sim(position, resample='M', name="優等生策略", upload=False)
report.display()
from finlab import data
from finlab.backtest import sim
股本 = data.get('financial_statement:股本')
price = data.get('price:收盤價')
市值 = 股本 * price / 10 * 1000
df1 = data.get('financial_statement:投資活動之淨現金流入_流出')
df2 = data.get('financial_statement:營業活動之淨現金流入_流出')
自由現金流 = (df1 + df2).rolling(4).mean()
稅後淨利 = data.get('fundamental_features:經常稅後淨利')
權益總計 = data.get('financial_statement:股東權益總額')
股東權益報酬率 = 稅後淨利/ 權益總計
營業利益成長率 = data.get('fundamental_features:營業利益成長率')
當月營收 = data.get('monthly_revenue:當月營收')* 1000
當季營收 = 當月營收.rolling(4).sum()
市值營收比 = 市值 / 當季營收
condition1 = (市值 < 1e10)
condition2 = 自由現金流 > 0
condition3 = 股東權益報酬率 > 0
condition4 = 營業利益成長率 > 0
condition5 = 市值營收比 < 5
position = condition1 & condition2 & condition3 & condition4 & condition5
report = sim(position, resample='M', name="優等生策略", upload=False)
report.display()
INFO:finlab.data:financial_statement:股本 -- Daily data usage: 647.7 / 5000 MB INFO:finlab.data:financial_statement:投資活動之淨現金流入_流出 -- Daily data usage: 649.0 / 5000 MB INFO:finlab.data:financial_statement:營業活動之淨現金流入_流出 -- Daily data usage: 650.4 / 5000 MB INFO:finlab.data:fundamental_features:經常稅後淨利 -- Daily data usage: 651.7 / 5000 MB INFO:finlab.data:financial_statement:股東權益總額 -- Daily data usage: 653.0 / 5000 MB
stock_id 1108 0.005814 1203 0.005814 1217 0.005814 1258 0.005814 1336 0.005814 ... 8916 0.005814 8923 0.005814 8928 0.005814 8941 0.005814 9927 0.005814 Name: 2022-07-31 00:00:00, Length: 172, dtype: float64
Timestamp('2022-07-31 00:00:00')
多空並行回測¶
若策略想同時做多和做空,可參考以下範例,範例是做多 2330 一半的部位,做空 1101 一半的部位。
In [8]:
Copied!
from finlab import data
from finlab import backtest
close = data.get('price:收盤價')
position = close < 0
position['2330'] = 0.5
position['1101'] = -0.5
report = backtest.sim(position, upload=False)
report.display()
from finlab import data
from finlab import backtest
close = data.get('price:收盤價')
position = close < 0
position['2330'] = 0.5
position['1101'] = -0.5
report = backtest.sim(position, upload=False)
report.display()
1101 -0.5 2330 0.5 Name: 2022-07-06 00:00:00, dtype: float64
Timestamp('2022-07-06 00:00:00')
In [11]:
Copied!
from finlab import data
from finlab.backtest import sim
import pandas as pd
# 限定回測產業範圍
with data.universe(category='建材營造'):
# 資料引入
close = data.get('price:收盤價')
contract_debt = data.get('financial_statement:合約負債_流動')
contract_debt_gr = (contract_debt/(contract_debt.shift())-1)
equity = data.get('financial_statement:股本')
# 計算合約負債佔股本的比率,比率越高代表未來潛在貢獻每股營收越高
ce_ratio = (contract_debt / equity)
# 選股條件
cond1 = ce_ratio > 0.5
cond2 = (contract_debt_gr > 0.05) & (contract_debt_gr<0.5)
cond_all = cond1 & cond2
result = ce_ratio * (cond_all)
position = result[result > 0].is_largest(5)
# position_limit 限定單檔標的最多持有30%部位,避免標的數過少時產生重壓的非系統性風險
report = sim(position,resample='M',position_limit=0.3,stop_loss=0.1,fee_ratio=1.425/1000*0.3,name="營建股合約負債策略",upload=False)
report.display()
from finlab import data
from finlab.backtest import sim
import pandas as pd
# 限定回測產業範圍
with data.universe(category='建材營造'):
# 資料引入
close = data.get('price:收盤價')
contract_debt = data.get('financial_statement:合約負債_流動')
contract_debt_gr = (contract_debt/(contract_debt.shift())-1)
equity = data.get('financial_statement:股本')
# 計算合約負債佔股本的比率,比率越高代表未來潛在貢獻每股營收越高
ce_ratio = (contract_debt / equity)
# 選股條件
cond1 = ce_ratio > 0.5
cond2 = (contract_debt_gr > 0.05) & (contract_debt_gr<0.5)
cond_all = cond1 & cond2
result = ce_ratio * (cond_all)
position = result[result > 0].is_largest(5)
# position_limit 限定單檔標的最多持有30%部位,避免標的數過少時產生重壓的非系統性風險
report = sim(position,resample='M',position_limit=0.3,stop_loss=0.1,fee_ratio=1.425/1000*0.3,name="營建股合約負債策略",upload=False)
report.display()
stock_id 2540 0.2 2548 0.2 3056 0.2 5213 0.2 6186 0.2 Name: 2022-05-31 00:00:00, dtype: float64
Timestamp('2022-05-31 00:00:00')
乖離率和財報濾網¶
In [13]:
Copied!
from finlab import data
from finlab.backtest import sim
# 下載 ROE 跟收盤價
roe = data.get("fundamental_features:ROE稅後")
close = data.get("price:收盤價")
position = ((close / close.shift(60)).is_largest(30) & (roe > 0))
# 回測,每月(M)調整一次
report = sim(position, resample="M", upload=False)
report.display()
from finlab import data
from finlab.backtest import sim
# 下載 ROE 跟收盤價
roe = data.get("fundamental_features:ROE稅後")
close = data.get("price:收盤價")
position = ((close / close.shift(60)).is_largest(30) & (roe > 0))
# 回測,每月(M)調整一次
report = sim(position, resample="M", upload=False)
report.display()
INFO:finlab.data:fundamental_features:ROE稅後 -- Daily data usage: 655.7 / 5000 MB
1319 0.055556 1522 0.055556 1524 0.055556 2305 0.055556 2364 0.055556 2482 0.055556 2736 0.055556 2929 0.055556 2939 0.055556 4543 0.055556 5258 0.055556 6113 0.055556 6199 0.055556 6472 0.055556 6655 0.055556 6715 0.055556 8097 0.055556 9802 0.055556 Name: 2022-07-31 00:00:00, dtype: float64
Timestamp('2022-07-31 00:00:00')
加入大盤指標濾網¶
大盤指標轉空時,出清持股
In [51]:
Copied!
from finlab import data
from finlab.backtest import sim
import numpy as np
# 市場多空排列家數
def ls_order_position(short=5,mid=10,long=30):
close = data.get("price:收盤價")
short_ma = close.average(short)
mid_ma = close.average(mid)
long_ma = close.average(long)
long_order = (short_ma>=mid_ma) & (mid_ma>=long_ma)
long_order = long_order.sum(1)
short_order = (short_ma<mid_ma) & (mid_ma<long_ma)
short_order = short_order.sum(1)
entry = long_order > short_order
cond = ~close.isna()
position = cond & entry
return position
close = data.get("price:收盤價")
sma20 = close.average(20)
rev = data.get('monthly_revenue:當月營收')
cond1 = (close > close.shift(20)) & (close > close.shift(60))
cond2 = (rev.average(3) > rev.average(12)).sustain(3)
buy = cond1 & cond2
sell = sma20 < sma20.shift()
position = pd.DataFrame(np.nan, index=buy.index, columns=buy.columns)
position[buy] = 1
position[sell] = 0
position = position.ffill().fillna(0).astype(bool)
position = position & ls_order_position()
report = sim(position, resample="D", name="ls_order_position",upload=False)
report.display()
from finlab import data
from finlab.backtest import sim
import numpy as np
# 市場多空排列家數
def ls_order_position(short=5,mid=10,long=30):
close = data.get("price:收盤價")
short_ma = close.average(short)
mid_ma = close.average(mid)
long_ma = close.average(long)
long_order = (short_ma>=mid_ma) & (mid_ma>=long_ma)
long_order = long_order.sum(1)
short_order = (short_ma short_order
cond = ~close.isna()
position = cond & entry
return position
close = data.get("price:收盤價")
sma20 = close.average(20)
rev = data.get('monthly_revenue:當月營收')
cond1 = (close > close.shift(20)) & (close > close.shift(60))
cond2 = (rev.average(3) > rev.average(12)).sustain(3)
buy = cond1 & cond2
sell = sma20 < sma20.shift()
position = pd.DataFrame(np.nan, index=buy.index, columns=buy.columns)
position[buy] = 1
position[sell] = 0
position = position.ffill().fillna(0).astype(bool)
position = position & ls_order_position()
report = sim(position, resample="D", name="ls_order_position",upload=False)
report.display()
Series([], Name: 2022-07-06 00:00:00, dtype: float64)
Timestamp('2022-07-06 00:00:00')
營收動能 & 過濾全額交割股¶
買進訊號隔日若為全額交割股,則不交易。 全額交割股清單每日0815更新。
In [16]:
Copied!
from finlab import data
from finlab.backtest import sim
import pandas as pd
rev = data.get("monthly_revenue:當月營收")
rev_rf = data.get("monthly_revenue:去年同月增減(%)")
vol = data.get("price:成交股數")/1000
# 過濾全額交割股
full_cash_delivery_stock_filter = data.get("etl:full_cash_delivery_stock_filter")
rev_recent_3 = rev.rolling(3).sum()
vol_avg = vol.average(10)
cond1 = (rev_recent_3/rev_recent_3.rolling(24, min_periods=12).max())==1
cond2 = vol_avg > 300
cond_all = cond1 & cond2 & full_cash_delivery_stock_filter
result = rev_rf*(cond_all)
position = result[result>0].is_largest(10).reindex(rev.index_str_to_date().index, method="ffill")
report = sim(position=position,stop_loss=0.3,position_limit=0.1, upload=False)
report.display()
from finlab import data
from finlab.backtest import sim
import pandas as pd
rev = data.get("monthly_revenue:當月營收")
rev_rf = data.get("monthly_revenue:去年同月增減(%)")
vol = data.get("price:成交股數")/1000
# 過濾全額交割股
full_cash_delivery_stock_filter = data.get("etl:full_cash_delivery_stock_filter")
rev_recent_3 = rev.rolling(3).sum()
vol_avg = vol.average(10)
cond1 = (rev_recent_3/rev_recent_3.rolling(24, min_periods=12).max())==1
cond2 = vol_avg > 300
cond_all = cond1 & cond2 & full_cash_delivery_stock_filter
result = rev_rf*(cond_all)
position = result[result>0].is_largest(10).reindex(rev.index_str_to_date().index, method="ffill")
report = sim(position=position,stop_loss=0.3,position_limit=0.1, upload=False)
report.display()
INFO:finlab.data:etl:full_cash_delivery_stock_filter -- Daily data usage: 659.8 / 5000 MB
1906 0.1 2530 0.1 2607 0.1 4946 0.1 6111 0.1 6241 0.1 6259 0.1 6446 0.1 6715 0.1 8097 0.1 Name: 2022-06-10 00:00:00, dtype: float64
Timestamp('2022-06-10 00:00:00')
In [53]:
Copied!
from finlab import data
from finlab.backtest import sim
營業現金流 = data.get("financial_statement:營業活動之淨現金流入_流出").deadline()
投資現金流 = data.get("financial_statement:投資活動之淨現金流入_流出").deadline()
融資現金流 = data.get("financial_statement:籌資活動之淨現金流入_流出").deadline()
roe = data.get("fundamental_features:ROE稅後").deadline()
close = data.get("price:收盤價")
position = (營業現金流 > 0) & (投資現金流 > 0) & (融資現金流 < 0) & (roe > 5) & (close > 10)
# 過濾 KY股
sc = data.get("security_categories")
position_col = position.columns
ky_filter = position_col[~position_col.isin(list(sc[sc["name"].str.contains("KY")]["stock_id"]))]
position = position[ky_filter]
report = sim(position,resample="M",name="現金流正數", upload=False)
report.display()
from finlab import data
from finlab.backtest import sim
營業現金流 = data.get("financial_statement:營業活動之淨現金流入_流出").deadline()
投資現金流 = data.get("financial_statement:投資活動之淨現金流入_流出").deadline()
融資現金流 = data.get("financial_statement:籌資活動之淨現金流入_流出").deadline()
roe = data.get("fundamental_features:ROE稅後").deadline()
close = data.get("price:收盤價")
position = (營業現金流 > 0) & (投資現金流 > 0) & (融資現金流 < 0) & (roe > 5) & (close > 10)
# 過濾 KY股
sc = data.get("security_categories")
position_col = position.columns
ky_filter = position_col[~position_col.isin(list(sc[sc["name"].str.contains("KY")]["stock_id"]))]
position = position[ky_filter]
report = sim(position,resample="M",name="現金流正數", upload=False)
report.display()
stock_id 1539 0.043478 2303 0.043478 2504 0.043478 2636 0.043478 3093 0.043478 3131 0.043478 3169 0.043478 3557 0.043478 4506 0.043478 4945 0.043478 5534 0.043478 6195 0.043478 6278 0.043478 6281 0.043478 6294 0.043478 6506 0.043478 6542 0.043478 6683 0.043478 6706 0.043478 6747 0.043478 6761 0.043478 8084 0.043478 8421 0.043478 Name: 2022-07-31 00:00:00, dtype: float64
Timestamp('2022-07-31 00:00:00')