├── Black Scholes Merton Model.ipynb ├── Buffett's Business Investment Rules.ipynb ├── ESG ETF.ipynb ├── LDA_model.ipynb ├── LSTM 回測 ├── backtest_2618.ipynb ├── backtest_8215.ipynb ├── config.ini ├── initialize.py ├── lstm_2618.keras ├── lstm_8215.keras ├── main_2618.ipynb ├── main_8215.ipynb └── select_1.py ├── Pre-Earnings.ipynb ├── README.md ├── TEJAPIMedium實戰應用(13).ipynb ├── TEJAPI_MediumWeek13.ipynb ├── TEJAPI_MediumWeek14.ipynb ├── TEJAPI_MediumWeek15.ipynb ├── TEJAPI_MediumWeek5.ipynb ├── TEJAPI_MediumWeek6.ipynb ├── TEJAPI_Medium_吉姆史萊特.ipynb ├── TEJAPI_Medium_巴菲特價值投資策略.ipynb ├── TEJAPI_Medium_馬丁格爾策略.ipynb ├── TEJAPI_Medium實戰應用10.ipynb ├── TEJAPI_Medium實戰應用11.ipynb ├── TEJAPI_Medium實戰應用12.ipynb ├── TEJAPI_Medium實戰應用13.ipynb ├── TEJAPI_Medium實戰應用14.ipynb ├── TEJAPI_Medium實戰應用9.ipynb ├── TEJAPI_Python_ESG_PORT.ipynb ├── TEJAPI_Python_Monthly_return.ipynb ├── TEJAPI_Python_PCA.py ├── TEJAPI_Python_Seeking_Alpha.ipynb ├── TEJAPI_medium布林通道.ipynb ├── TQuant Lab 超級趨勢策略_永豐 Shioaji.ipynb ├── TW50.csv ├── TejApi_Python_GRU.ipynb ├── streamlit_with_tejapi.ipynb ├── 乖離率.ipynb ├── 價值因子ESG.ipynb ├── 動能因子.ipynb ├── 威廉指標.ipynb ├── 巴菲特企業投資法則選股策略.ipynb ├── 月營收.ipynb ├── 模組化回測系統.ipynb ├── 羊群指標.ipynb ├── 肯尼斯.費雪成長型策略.ipynb ├── 董監持股策略1-高持股董監.ipynb ├── 董監持股策略2-排名前30董監.ipynb ├── 董監持股策略3-兩月連增董監.ipynb ├── 處置股回測.ipynb ├── 資料科學【十四】LSTM .ipynb ├── 趨勢跟蹤.ipynb └── 高股息ETF.ipynb /Black Scholes Merton Model.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "ad87483c", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "# 載入所需套件\n", 11 | "import math \n", 12 | "import tejapi\n", 13 | "import pandas as pd \n", 14 | "import numpy as np \n", 15 | "import matplotlib.pyplot as plt \n", 16 | "from scipy.stats import norm\n", 17 | "plt.style.use('bmh')\n", 18 | "plt.rcParams['font.sans-serif']=['Microsoft YaHei']\n", 19 | "\n", 20 | "# 登入TEJ API\n", 21 | "api_key = 'YOUR_KEY'\n", 22 | "tejapi.ApiConfig.api_key = api_key\n", 23 | "tejapi.ApiConfig.ignoretz = True\n", 24 | "\n", 25 | "gte, lte = '2021-03-16', '2023-04-10'\n", 26 | "# 標的物價格\n", 27 | "stocks = tejapi.get('TWN/APRCD',\n", 28 | " paginate = True,\n", 29 | " coid = 'Y9999',\n", 30 | " mdate = {'gte':gte, 'lte':lte},\n", 31 | " chinese_column_name = True,\n", 32 | " opts = {\n", 33 | " 'columns':[ 'mdate','close_d']\n", 34 | " }\n", 35 | " )\n", 36 | "# 選擇權價格\n", 37 | "options = tejapi.get(\n", 38 | " 'TWN/AOPTION',\n", 39 | " paginate = True,\n", 40 | " coid = 'TXO202304C15500',\n", 41 | " mdate = {'gte':gte, 'lte':lte},\n", 42 | " chinese_column_name = True,\n", 43 | " opts = {\n", 44 | " 'columns':['mdate', 'coid','settle', 'kk', 'theoremp', 'acls', 'ex_price', 'td1y', 'avolt', 'rtime']\n", 45 | " }\n", 46 | ")\n", 47 | "# 重設日期為index\n", 48 | "stocks = stocks.set_index('年月日')\n", 49 | "options = options.set_index('日期')\n", 50 | "\n", 51 | "stocks['日報酬'] = np.log(stocks['收盤價(元)']) - np.log(stocks['收盤價(元)'].shift(1))\n", 52 | "stocks['移動報酬波動度'] = stocks['日報酬'].rolling(252).std()" 53 | ] 54 | }, 55 | { 56 | "cell_type": "code", 57 | "execution_count": null, 58 | "id": "3a01f887", 59 | "metadata": {}, 60 | "outputs": [], 61 | "source": [ 62 | "class BS_formula:\n", 63 | "\n", 64 | " def __init__(self, s0, k, r, sigma, T): \n", 65 | " self.s0 = s0 # 標的物價格\n", 66 | " self.k = k # 履約價格\n", 67 | " self.r = r # 無風險利率\n", 68 | " self.sigma = sigma # 歷史波動度\n", 69 | " self.T = T # 剩餘到期時間\n", 70 | " self.d1 = (np.log(s0/k)+(r+sigma**2/2)*T) / (sigma * np.sqrt(T))\n", 71 | " self.d2 = ((np.log(s0/k)+(r+sigma**2/2)*T) / (sigma * np.sqrt(T))) - sigma*np.sqrt(T)\n", 72 | " \n", 73 | " def BS_price(self): # 計算理論價格\n", 74 | " c = self.s0*norm.cdf(self.d1) - self.k*np.exp(-self.r*self.T)*norm.cdf(self.d2)\n", 75 | " p = self.k*np.exp(-self.r*self.T)*norm.cdf(-self.d2) - self.s0*norm.cdf(-self.d1)\n", 76 | " return c,p\n", 77 | " \n", 78 | " def BS_delta(self): # 計算 delta\n", 79 | " return norm.cdf(self.d1), norm.cdf(self.d1)-1\n", 80 | " \n", 81 | " def BS_gamma(self): # 計算 gamma\n", 82 | " return norm.pdf(self.d1)/(self.s0*self.sigma*np.sqrt(self.T)), norm.pdf(self.d1)/(self.s0*self.sigma*np.sqrt(self.T))\n", 83 | " \n", 84 | " def BS_vega(self): # 計算 vega\n", 85 | " return self.s0*np.sqrt(self.T)*norm.pdf(self.d1), self.s0*np.sqrt(self.T)*norm.pdf(self.d1)\n", 86 | " \n", 87 | " def BS_theta(self): # 計算 theta \n", 88 | " c_theta = -self.s0*norm.pdf(self.d1)*self.sigma / (2*np.sqrt(self.T)) - self.r*self.k*np.exp(-self.r*self.T)*norm.cdf(self.d2)\n", 89 | " p_theta = -self.s0*norm.pdf(self.d1)*self.sigma / (2*np.sqrt(self.T)) + self.r*self.k*np.exp(-self.r*self.T)*norm.cdf(-self.d2)\n", 90 | " return c_theta, p_theta\n", 91 | " \n", 92 | " def BS_rho(self): # 計算 rho \n", 93 | " return self.k*self.T*np.exp(-self.r*self.T)*norm.cdf(self.d2), -self.k*self.T*np.exp(-self.r*self.T)*norm.cdf(-self.d2)" 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": null, 99 | "id": "d8e88fc9", 100 | "metadata": {}, 101 | "outputs": [], 102 | "source": [ 103 | "s0 = np.linspace(200,800)\n", 104 | "k = 500\n", 105 | "r = 0.00\n", 106 | "sigma = 0.2\n", 107 | "T = 252/252\n", 108 | "\n", 109 | "mybs = BS_formula(s0, k, r, sigma, T)\n", 110 | "c, p = mybs.BS_price()\n", 111 | "\n", 112 | "fig = plt.figure(figsize = (12,8))\n", 113 | "plt.plot(s0, c, label = '買權')\n", 114 | "plt.plot(s0, p, label = '賣權')\n", 115 | "plt.axvline(x = 500, color = 'black', linestyle = '--')\n", 116 | "plt.xlabel('標的物價格', fontsize = 15)\n", 117 | "plt.ylabel('選擇權價格', fontsize = 15)\n", 118 | "plt.title('選擇權價格 VS. 標的物價格', fontsize = 20)\n", 119 | "plt.legend(fontsize = 14)\n", 120 | "plt.savefig('black scholes put call price.png')\n", 121 | "plt.show()" 122 | ] 123 | }, 124 | { 125 | "cell_type": "code", 126 | "execution_count": null, 127 | "id": "312569cf", 128 | "metadata": {}, 129 | "outputs": [], 130 | "source": [ 131 | "s0 = np.linspace(200,800)\n", 132 | "k = 500\n", 133 | "r = 0.00\n", 134 | "sigma = 0.2\n", 135 | "T = 252/252\n", 136 | "\n", 137 | "mybs = BS_formula(s0, k, r, sigma, T)\n", 138 | "c, p = mybs.BS_delta()\n", 139 | "\n", 140 | "fig = plt.figure(figsize = (12,8))\n", 141 | "plt.plot(s0, c, label = '買權')\n", 142 | "plt.plot(s0, p, label = '賣權')\n", 143 | "plt.axvline(x = 500, color = 'black', linestyle = '--')\n", 144 | "plt.axhline(y = 0, color = 'black', linestyle = '--')\n", 145 | "plt.xlabel('標的物價格', fontsize = 15)\n", 146 | "plt.ylabel('Delta值', fontsize = 15)\n", 147 | "plt.title('Delta值 VS. 標的物價格', fontsize = 20)\n", 148 | "plt.legend(fontsize = 14)\n", 149 | "plt.savefig('black scholes put call delta.png')\n", 150 | "plt.show()" 151 | ] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": null, 156 | "id": "78b6d3a6", 157 | "metadata": {}, 158 | "outputs": [], 159 | "source": [ 160 | "s0, s1, s2 = 400, 500, 600 \n", 161 | "k = 500\n", 162 | "r = 0.00\n", 163 | "sigma = 0.2\n", 164 | "T = np.linspace(1, 0.01)\n", 165 | "\n", 166 | "mybs0 = BS_formula(s0, k, r, sigma, T)\n", 167 | "c0, p0 = mybs0.BS_gamma()\n", 168 | "\n", 169 | "mybs1 = BS_formula(s1, k, r, sigma, T)\n", 170 | "c1, p1 = mybs1.BS_gamma()\n", 171 | "\n", 172 | "mybs2 = BS_formula(s2, k, r, sigma, T)\n", 173 | "c2, p2 = mybs2.BS_gamma()\n", 174 | "\n", 175 | "fig = plt.figure(figsize = (12,8))\n", 176 | "plt.plot(T, c0, label = '買權(價外)')\n", 177 | "plt.plot(T, c1, label = '買權(價平)')\n", 178 | "plt.plot(T, c2, label = '買權(價內)')\n", 179 | "plt.xlabel('剩餘時間', fontsize = 15)\n", 180 | "plt.ylabel('Gamma值', fontsize = 15)\n", 181 | "plt.title('Gamma值 VS. 剩餘時間', fontsize = 20)\n", 182 | "plt.legend(fontsize = 14)\n", 183 | "plt.axis([1.005, -0, -0.005, .045])\n", 184 | "plt.savefig('black scholes put call gamma2.png')\n", 185 | "plt.show()" 186 | ] 187 | }, 188 | { 189 | "cell_type": "code", 190 | "execution_count": null, 191 | "id": "1628e153", 192 | "metadata": {}, 193 | "outputs": [], 194 | "source": [ 195 | "r = 0.012\n", 196 | "s0 = stocks.loc['2023-04-10']['收盤價(元)']\n", 197 | "k = 15500\n", 198 | "sigma = stocks.loc['2023-04-10']['移動報酬波動度']*np.sqrt(252)\n", 199 | "T = 6/252\n", 200 | "\n", 201 | "mybs = BS_formula(s0, k, r, sigma, T)\n", 202 | "c, p = mybs.BS_price()\n", 203 | "c_delta, p_delta = mybs.BS_delta()\n", 204 | "c_gamma, p_gamma = mybs.BS_gamma()\n", 205 | "c_vega, p_vega = mybs.BS_vega()\n", 206 | "c_theta, p_theta = mybs.BS_theta()\n", 207 | "c_rho, p_rho = mybs.BS_rho()\n", 208 | "\n", 209 | "print('==2023-04-10履約價為525的台積電買權==')\n", 210 | "print('當前標的物價格為 %.3f, 年化波動度為 %.3f, 剩餘期間為 %.3f'%(s0, sigma, T*252))\n", 211 | "print('買權理論價格: %.4f, 賣權理論價格: %.4f' %(c,p))\n", 212 | "print('買權delta: %.4f, 賣權delta: %.4f' %(c_delta,p_delta))\n", 213 | "print('買權gamma: %.4f, 賣權gamma: %.4f' %(c_gamma,p_gamma))\n", 214 | "print('買權vega: %.4f, 賣權vega: %.4f' %(c_vega,p_vega))\n", 215 | "print('買權theta: %.4f, 賣權theta: %.4f' %(c_theta,p_theta))\n", 216 | "print('買權rho: %.4f, 賣權rho: %.4f' %(c_rho,p_rho))" 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": null, 222 | "id": "0b224856", 223 | "metadata": {}, 224 | "outputs": [], 225 | "source": [ 226 | "options.loc['2023-04-10'] # 實際買權價格" 227 | ] 228 | } 229 | ], 230 | "metadata": { 231 | "kernelspec": { 232 | "display_name": "Python 3 (ipykernel)", 233 | "language": "python", 234 | "name": "python3" 235 | }, 236 | "language_info": { 237 | "codemirror_mode": { 238 | "name": "ipython", 239 | "version": 3 240 | }, 241 | "file_extension": ".py", 242 | "mimetype": "text/x-python", 243 | "name": "python", 244 | "nbconvert_exporter": "python", 245 | "pygments_lexer": "ipython3", 246 | "version": "3.11.3" 247 | } 248 | }, 249 | "nbformat": 4, 250 | "nbformat_minor": 5 251 | } -------------------------------------------------------------------------------- /LSTM 回測/config.ini: -------------------------------------------------------------------------------- 1 | [API_KEYS] 2 | API_KEY=your key 3 | API_BASE=https://api.tej.com.tw -------------------------------------------------------------------------------- /LSTM 回測/initialize.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import tejapi 4 | import talib as ta 5 | from talib import abstract 6 | import numpy as np 7 | import pandas as pd 8 | import matplotlib.pyplot as plt 9 | import seaborn as sns 10 | import datetime 11 | from datetime import datetime 12 | from datetime import timedelta 13 | import configparser 14 | import re 15 | from sklearn.model_selection import GridSearchCV, RandomizedSearchCV 16 | from sklearn.preprocessing import StandardScaler 17 | 18 | class ML_stock: 19 | def __init__(self, config_file='config.ini') -> None: 20 | self.config_file = config_file 21 | self.api_keys = self.get_api_keys(self.config_file) 22 | self.ini() 23 | 24 | def get_api_keys(self, config_file): 25 | config = configparser.ConfigParser() 26 | config.read(config_file) 27 | api_keys = {} 28 | if 'API_KEYS' in config: 29 | for key, value in config['API_KEYS'].items(): 30 | api_keys[key] = value 31 | 32 | return api_keys 33 | 34 | def ini(self): 35 | os.environ['TEJAPI_KEY'] = self.api_keys.get('api_key') 36 | os.environ['TEJAPI_BASE'] = self.api_keys.get('api_base') 37 | 38 | def get_fundamental(self, start, end, tickers, column): 39 | import TejToolAPI 40 | from zipline.sources.TEJ_Api_Data import get_universe 41 | start_dt, end_dt = pd.Timestamp(start, tz='utc'), pd.Timestamp(end, tz='utc') 42 | df = TejToolAPI.get_history_data(start = start_dt, 43 | end = end_dt, 44 | ticker = tickers, 45 | columns = column, 46 | transfer_to_chinese = False) 47 | 48 | mask = df['Close'] > 10 49 | df = df[mask] 50 | return df 51 | 52 | def calculate_all_technical_indicators(self, df): 53 | df_all = df.copy() 54 | change = { 55 | 'Open': 'open', 56 | 'High': 'high', 57 | 'Low': 'low', 58 | 'Close': 'close', 59 | 'Volume_1000_Shares': 'volume' 60 | } 61 | df_all.rename(columns=change, inplace=True) 62 | df_all['MOM'] = abstract.MOM(df_all['close']) 63 | df_all['RSI'] = abstract.RSI(df_all['close']) 64 | 65 | return df_all 66 | 67 | def preprocessing(self, df): 68 | df_all = df.copy() 69 | data = df_all.groupby('coid').apply(self.calculate_all_technical_indicators) 70 | data.reset_index(drop=True, inplace=True) 71 | data['coid'] = pd.to_numeric(data['coid']) 72 | 73 | # deal with the NAs 74 | data = data[~(data['Return_Rate_on_Equity_A_percent_A'].isna())].reset_index(drop=True) 75 | aa = data[data.isnull().any(axis=1)] 76 | nan_col = aa.columns[aa.isnull().any()].tolist() 77 | for col in nan_col: 78 | aa[col] = aa.groupby('coid')[col].transform(lambda x: x.fillna(x.mean())) 79 | if data.isnull().any().any(): 80 | for col in nan_col: 81 | data[col] = data[col].fillna(0) 82 | data = data.drop(columns = ['Return_Rate_on_Equity_A_percent_A', 'Return_Rate_on_Equity_A_percent_TTM']) 83 | data = data.sort_values(by=['coid', 'mdate']).reset_index(drop=True) 84 | 85 | return data 86 | -------------------------------------------------------------------------------- /LSTM 回測/lstm_2618.keras: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tejtw/TEJAPI_Python_Medium_Application/0c47ee0b3a7cafee5a8e39b5843bb1c37f462121/LSTM 回測/lstm_2618.keras -------------------------------------------------------------------------------- /LSTM 回測/lstm_8215.keras: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tejtw/TEJAPI_Python_Medium_Application/0c47ee0b3a7cafee5a8e39b5843bb1c37f462121/LSTM 回測/lstm_8215.keras -------------------------------------------------------------------------------- /LSTM 回測/select_1.py: -------------------------------------------------------------------------------- 1 | column = [ 2 | 'Open', 3 | 'High', 4 | 'Low', 5 | 'Close', 6 | 'Volume_1000_Shares', 7 | 'Return_Rate_on_Equity_A_percent' 8 | ] 9 | 10 | sample = [ 11 | '8215', 12 | '2618' 13 | ] 14 | 15 | feature = [ 16 | 'low', 17 | 'volume', 18 | 'close', 19 | 'open', 20 | 'high', 21 | 'Return_Rate_on_Equity_A_percent_Q', 22 | 'MOM', 23 | 'RSI', 24 | ] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TEJAPI_Python_Medium_Application 2 | Medium 文章程式碼-Week6, 13, 14, 15 3 | 4 | ## 主題:Python_大師策略 5 | 程式內容說明
6 | Week6:透過Python實作班傑明葛拉漢的投資策略並進行績效回測
7 | Medium文章連結:https://medium.com/tej-api-金融資料分析/實戰應用-一-證券分析之開山始祖一班傑明-葛拉漢的投資心法-221645c773aa
8 | 9 | Week13:讓資料進入你的Line
10 | Medium文章連結:https://medium.com/tej-api-金融資料分析/實戰應用-二-當-tej-api遇上-line-199ed923115
11 | 12 | Week14:透過Python實作班傑明羅傑.金的投資策略並進行績效回測
13 | Medium文章連結:https://medium.com/tej-api-金融資料分析/實戰應用-三-羅傑-金-roger-e-king-的投資策略-f7cbc004bc4a
14 | 15 | 16 | Week15:透過Python實作三一投資管理公司價值型選股法則並進行績效回測
17 | Medium文章連結:https://medium.com/tej-api-金融資料分析/實戰應用-三-三一投資管理公司-trinity-investment-management-價值型選股法則-bf13b041a059
18 | 19 | 【實戰應用(五)】模組化回測系統
20 | Medium文章連結: https://medium.com/tej-api-金融資料分析/實戰應用-五-模組化回測系統-f7262d6acc6c
21 | 22 | 【實戰應用(六)】巴菲特價值投資策略
23 | Medium文章連結: https://medium.com/tej-api-金融資料分析/實戰應用-六-巴菲特價值投資策略-db9e958b0d4
24 | 25 | 【實戰應用(七)】吉姆史萊特投資策略
26 | Medium文章連結: https://medium.com/tej-api-金融資料分析/實戰應用-七-吉姆-史萊特-jim-slater-祖魯原則投資法-5de2c3c445c5
27 | 28 | 【實戰應用(八)】馬丁格爾策略
29 | Medium文章連結: https://medium.com/tej-api-金融資料分析/實戰應用-八-馬丁格爾策略-424283ddafca
30 | 31 | 【實戰應用(九)】Brinson Model 績效歸因
32 | Medium文章連結: https://medium.com/tej-api-金融資料分析/實戰應用-九-brinson-model-績效歸因-2cbb5070cda7 33 | 34 | 【實戰應用(十)】配對交易
35 | Medium文章連結: https://medium.com/tej-api-金融資料分析/實戰應用-十-配對交易-5eff5d9517bc 36 | 37 | 【實戰應用(十一)】春節期間台股績效表現
38 | Medium文章連結: https://medium.com/tej-api-金融資料分析/實戰應用-十一-春節期間台股績效表現-b4d3258f59de 39 | 40 | 【實戰應用(十二)】配對交易
41 | Medium文章連結: https://medium.com/tej-api-金融資料分析/實戰應用-十二-資金管理-69af8651494 42 | 43 | 【實戰應用(十三)】量價篩選
44 | Medium文章連結: https://medium.com/tej-api-金融資料分析/實戰應用-十三-量價篩選-d25e0e57ac6c 45 | 46 | ## 備註 47 | 資料庫使用: 48 | TEJ IFRS以合併為主簡表(累計)-全產業(TWN/AIM1A)
49 | TEJ 上市(櫃)股價報酬(日)-報酬率 (TWN/APRCD2)
50 | TEJ 上市(櫃)未調整股價(年) (TWN/APRCY) 51 | -------------------------------------------------------------------------------- /TEJAPI_MediumWeek13.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Week 13 Medium - Line Notify的應用" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 1, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "import tejapi \n", 17 | "import pandas as pd\n", 18 | "import numpy as np\n", 19 | "import datetime\n", 20 | "import matplotlib.pyplot as plt\n", 21 | "\n", 22 | "tejapi.ApiConfig.api_key = \"yourkey\"\n", 23 | "tejapi.ApiConfig.ignoretz = True" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": {}, 29 | "source": [ 30 | "### 資料撈取(TEJAPI)及整理" 31 | ] 32 | }, 33 | { 34 | "cell_type": "code", 35 | "execution_count": 2, 36 | "metadata": {}, 37 | "outputs": [], 38 | "source": [ 39 | "ticks = ['2330', '2303', '1101']\n", 40 | "\n", 41 | "DailyPrice= tejapi.get('TWN/EWPRCD',coid=ticks,\n", 42 | " opts={'columns':['coid', 'mdate', 'open_d', 'high_d','low_d', 'close_d']},\n", 43 | " mdate={'gte':'2020-05-01','lte':'2021-05-25'}, paginate=True, )\n", 44 | "DailyPrice= DailyPrice.set_index('mdate')" 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": 3, 50 | "metadata": {}, 51 | "outputs": [], 52 | "source": [ 53 | "## 周線;月線;日變化\n", 54 | "\n", 55 | "MovingAvg_5D = {}\n", 56 | "MovingAvg_20D = {}\n", 57 | "DailyRt = {}\n", 58 | "\n", 59 | "for ticker in ticks:\n", 60 | " MovingAvg_5D[ticker] = DailyPrice[DailyPrice['coid']==ticker]['close_d'].rolling(5).mean()\n", 61 | " MovingAvg_20D[ticker] = DailyPrice[DailyPrice['coid']==ticker]['close_d'].rolling(20).mean()\n", 62 | " DailyRt[ticker] = DailyPrice[DailyPrice['coid']==ticker]['close_d'].pct_change()*100" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": 4, 68 | "metadata": {}, 69 | "outputs": [ 70 | { 71 | "data": { 72 | "text/plain": [ 73 | "mdate\n", 74 | "2020-05-04 NaN\n", 75 | "2020-05-05 NaN\n", 76 | "2020-05-06 NaN\n", 77 | "2020-05-07 NaN\n", 78 | "2020-05-08 NaN\n", 79 | " ... \n", 80 | "2021-05-19 583.45\n", 81 | "2021-05-20 582.20\n", 82 | "2021-05-21 581.30\n", 83 | "2021-05-24 579.60\n", 84 | "2021-05-25 578.25\n", 85 | "Name: close_d, Length: 260, dtype: float64" 86 | ] 87 | }, 88 | "execution_count": 4, 89 | "metadata": {}, 90 | "output_type": "execute_result" 91 | } 92 | ], 93 | "source": [ 94 | "MovingAvg_20D['2330']" 95 | ] 96 | }, 97 | { 98 | "cell_type": "markdown", 99 | "metadata": {}, 100 | "source": [ 101 | "### 連接Line Notify" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": null, 107 | "metadata": {}, 108 | "outputs": [], 109 | "source": [ 110 | "token = \"your token\"\n", 111 | "\n", 112 | "def LineNotify(params, token):\n", 113 | " \n", 114 | " headers = {\n", 115 | " \"Authorization\": \"Bearer \" + token,\n", 116 | " \"Content-Type\": \"application/x-www-form-urlencoded\"\n", 117 | " }\n", 118 | " r = requests.post(\"https://notify-api.line.me/api/notify\", headers=headers, params=params)\n", 119 | " print(r.status_code)" 120 | ] 121 | }, 122 | { 123 | "cell_type": "code", 124 | "execution_count": null, 125 | "metadata": {}, 126 | "outputs": [], 127 | "source": [ 128 | "import requests\n", 129 | "params = []\n", 130 | "for ticker in ticks:\n", 131 | " \n", 132 | " if MovingAvg_20D[ticker][-1] > MovingAvg_20D[ticker][-2] and MovingAvg_5D[ticker][-1] > MovingAvg_5D[ticker][-2]:\n", 133 | "\n", 134 | " params.append('\\n' + ticker + ':\\n' \n", 135 | " + \"五日均線往上為: \" + str(round(MovingAvg_5D[ticker][-1],2)) + '\\n' \n", 136 | " + \"月均線往上為: \" + str(round(MovingAvg_20D[ticker][-1],2)) + '\\n' \n", 137 | " + \"日報酬率為: \" + str(round(DailyRt[ticker][-1],2)) + '%' + '\\n' \n", 138 | " + \"當前價格: \" + str(round(DailyPrice[DailyPrice['coid']==ticker]['close_d'][-1])) + '\\n')\n", 139 | " \n", 140 | " elif MovingAvg_20D[ticker][-1] > MovingAvg_20D[ticker][-2]:\n", 141 | "\n", 142 | " params.append('\\n' + ticker + ':\\n' \n", 143 | " + \"月均線往上為: \" + str(round(MovingAvg_20D[ticker][-1],2)) + '\\n' \n", 144 | " + \"日報酬率為: \" + str(round(DailyRt[ticker][-1],2)) + '%' + '\\n' \n", 145 | " + \"當前價格: \" + str(round(DailyPrice[DailyPrice['coid']==ticker]['close_d'][-1])) + '\\n')\n", 146 | " \n", 147 | " elif MovingAvg_5D[ticker][-1] > MovingAvg_5D[ticker][-2]:\n", 148 | "\n", 149 | " params.append('\\n' + ticker + ':\\n' \n", 150 | " + \"五日均線往上為: \" + str(round(MovingAvg_5D[ticker][-1],2)) + '\\n' \n", 151 | " + \"日報酬率為: \" + str(round(DailyRt[ticker][-1],2)) + '%' + '\\n' \n", 152 | " + \"當前價格: \" + str(round(DailyPrice[DailyPrice['coid']==ticker]['close_d'][-1])) + '\\n') \n", 153 | " \n", 154 | "params = {'message': params}\n", 155 | "LineNotify(params, token)" 156 | ] 157 | } 158 | ], 159 | "metadata": { 160 | "kernelspec": { 161 | "display_name": "Python 3", 162 | "language": "python", 163 | "name": "python3" 164 | }, 165 | "language_info": { 166 | "codemirror_mode": { 167 | "name": "ipython", 168 | "version": 3 169 | }, 170 | "file_extension": ".py", 171 | "mimetype": "text/x-python", 172 | "name": "python", 173 | "nbconvert_exporter": "python", 174 | "pygments_lexer": "ipython3", 175 | "version": "3.7.3" 176 | } 177 | }, 178 | "nbformat": 4, 179 | "nbformat_minor": 4 180 | } 181 | -------------------------------------------------------------------------------- /TEJAPI_Medium實戰應用14.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "9a901d0d", 6 | "metadata": {}, 7 | "source": [ 8 | "## Import Packages" 9 | ] 10 | }, 11 | { 12 | "cell_type": "code", 13 | "execution_count": 1, 14 | "id": "3fa9c2ba", 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "import numpy as np\n", 19 | "import pandas as pd\n", 20 | "\n", 21 | "import tejapi\n", 22 | "tejapi.ApiConfig.api_key = 'Your Key'\n", 23 | "tejapi.ApiConfig.ignoretz = True" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "id": "72ba3885", 29 | "metadata": {}, 30 | "source": [ 31 | "## Import Data\n", 32 | "### Risk-free Rate" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": 2, 38 | "id": "250ec263", 39 | "metadata": { 40 | "scrolled": false 41 | }, 42 | "outputs": [ 43 | { 44 | "data": { 45 | "text/plain": [ 46 | "['美國十年期公債殖利率',\n", 47 | " '美國五年期公債殖利率',\n", 48 | " '美國七年期公債殖利率',\n", 49 | " '美國六個月公債殖利率',\n", 50 | " '美國二年期公債殖利率',\n", 51 | " '美國一年期公債殖利率',\n", 52 | " '美國三年期公債殖利率',\n", 53 | " '日本十年公債殖利率-月底值',\n", 54 | " '日本十年公債殖利率-月均值',\n", 55 | " '德國公債殖利率-三年期以上',\n", 56 | " '德國公債殖利率-五年期以上',\n", 57 | " '德國公債殖利率-十年期',\n", 58 | " '加拿大十年以上公債殖利率',\n", 59 | " '加拿大一至三年公債殖利率',\n", 60 | " '加拿大三至五年公債殖利率',\n", 61 | " '加拿大五至十年公債殖利率',\n", 62 | " '英國二十年公債殖利率',\n", 63 | " '義大利十年公債殖利率',\n", 64 | " '義大利三年公債殖利率-月均',\n", 65 | " '義大利五年公債殖利率-月均',\n", 66 | " '南韓十年期公債殖利率',\n", 67 | " '韓國一年期公債殖利率',\n", 68 | " '新加坡十年期公債殖利率',\n", 69 | " '歐元區十年公債殖利率',\n", 70 | " '西班牙五年期公債殖利率',\n", 71 | " '西班牙十年期公債殖利率',\n", 72 | " '西班牙十五年期公債殖利率',\n", 73 | " '希臘三年期公債殖利率',\n", 74 | " '希臘五年期公債殖利率',\n", 75 | " '希臘七年期公債殖利率',\n", 76 | " '希臘十年期公債殖利率',\n", 77 | " '希臘十五年期公債殖利率',\n", 78 | " '希臘二十年期公債殖利率',\n", 79 | " '希臘三十年期公債殖利率',\n", 80 | " '台灣上櫃股票加權平均殖利率(D/P)',\n", 81 | " '台灣-OTC-殖利率-文化創意業',\n", 82 | " '台灣-OTC-殖利率-食品工業',\n", 83 | " '台灣-OTC-殖利率-塑膠工業',\n", 84 | " '台灣-OTC-殖利率-紡織纖維',\n", 85 | " '台灣-OTC-殖利率-電機機械',\n", 86 | " '台灣-OTC-殖利率-電器電纜',\n", 87 | " '台灣-OTC-殖利率-鋼鐵工業',\n", 88 | " '台灣-OTC-殖利率-半導體業',\n", 89 | " '台灣-OTC-殖利率-電腦及週邊',\n", 90 | " '台灣-OTC-殖利率-光電業',\n", 91 | " '台灣-OTC-殖利率-通信網路業',\n", 92 | " '台灣-OTC-殖利率-電子零組件業',\n", 93 | " '台灣-OTC-殖利率-電子通路業',\n", 94 | " '台灣-OTC-殖利率-資訊服務業',\n", 95 | " '台灣-OTC-殖利率-其它電子業',\n", 96 | " '台灣-OTC-殖利率-建材營造',\n", 97 | " '台灣-OTC-殖利率-航運業',\n", 98 | " '台灣-OTC-殖利率-觀光事業',\n", 99 | " '台灣-OTC-殖利率-化學工業',\n", 100 | " '台灣-OTC-殖利率-生技醫療',\n", 101 | " '台灣-OTC-殖利率-油電燃氣',\n", 102 | " '台灣-OTC-殖利率-金融業',\n", 103 | " '台灣-OTC-殖利率-貿易百貨業',\n", 104 | " '台灣-OTC-殖利率-其他類',\n", 105 | " 'OTC-橡膠工業殖利率',\n", 106 | " '台灣-殖利率-上市公司-大盤-月',\n", 107 | " '美國股票殖利率-年',\n", 108 | " '韓國股票殖利率-年',\n", 109 | " '新加坡股票殖利率-年',\n", 110 | " '香港股票殖利率-年',\n", 111 | " '上海股票殖利率-年',\n", 112 | " '日本股票殖利率-年',\n", 113 | " '英國股票殖利率-年',\n", 114 | " '深圳股票殖利率-年',\n", 115 | " '台灣-殖利率-上市公司-水泥窯製類-月',\n", 116 | " '台灣-殖利率-上市公司-食品工業類-月',\n", 117 | " '台灣-殖利率-上市公司-塑膠化工類-月',\n", 118 | " '台灣-殖利率-上市公司-紡織纖維類-月',\n", 119 | " '台灣-殖利率-上市公司-機電類-月',\n", 120 | " '台灣-殖利率-上市公司-造紙工業類-月',\n", 121 | " '台灣-殖利率-上市公司-建材營造類-月',\n", 122 | " '台灣-殖利率-上市公司-金融保險類-月',\n", 123 | " '台灣-殖利率-上市公司-未含金融保險類-月',\n", 124 | " '台灣-殖利率-上市公司-化學工業類-月',\n", 125 | " '台灣-殖利率-上市公司-生技醫療類-月',\n", 126 | " '台灣-殖利率-上市公司-油電燃氣類-月',\n", 127 | " '台灣-殖利率-上市公司-水泥工業類-月',\n", 128 | " '台灣-殖利率-上市公司-塑膠工業類-月',\n", 129 | " '台灣-殖利率-上市公司-電機機械類-月',\n", 130 | " '台灣-殖利率-上市公司-電器電纜類-月',\n", 131 | " '台灣-殖利率-上市公司-玻璃陶瓷類-月',\n", 132 | " '台灣-殖利率-上市公司-鋼鐵工業類-月',\n", 133 | " '台灣-殖利率-上市公司-橡膠工業類-月',\n", 134 | " '台灣-殖利率-上市公司-汽車工業類-月',\n", 135 | " '台灣-殖利率-上市公司-半導體類-月',\n", 136 | " '台灣-殖利率-上市公司-電腦及周邊設備類-月',\n", 137 | " '台灣-殖利率-上市公司-光電類-月',\n", 138 | " '台灣-殖利率-上市公司-通信網路類-月',\n", 139 | " '台灣-殖利率-上市公司-電子零組件類-月',\n", 140 | " '台灣-殖利率-上市公司-電子通路類-月',\n", 141 | " '台灣-殖利率-上市公司-資訊服務類-月',\n", 142 | " '台灣-殖利率-上市公司-其他電子類-月',\n", 143 | " '台灣-殖利率-上市公司-電子工業類-月',\n", 144 | " '台灣-殖利率-上市公司-化學生技醫療類-月',\n", 145 | " '台灣-殖利率-上市公司-未含電子類-月',\n", 146 | " '台灣-殖利率-上市公司-未含金融電子類-月',\n", 147 | " '台灣-殖利率-上市公司-航運業類-月',\n", 148 | " '台灣-殖利率-上市公司-觀光事業類-月',\n", 149 | " '台灣-殖利率-上市公司-貿易百貨類-月',\n", 150 | " '台灣-殖利率-上市公司-其他類-月']" 151 | ] 152 | }, 153 | "execution_count": 2, 154 | "metadata": {}, 155 | "output_type": "execute_result" 156 | } 157 | ], 158 | "source": [ 159 | "factor_macro = tejapi.get('GLOBAL/ABMAR',\n", 160 | " opts={'columns': ['coid','mdate', 'cname', 'freq']},\n", 161 | " chinese_column_name=True,\n", 162 | " paginate=True)\n", 163 | "select_1 = list(factor_macro['中文全稱'][i] for i in range(0,6214) if '殖利率' in factor_macro.iloc[i,2])\n", 164 | "select_1" 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": 3, 170 | "id": "751a5ed3", 171 | "metadata": {}, 172 | "outputs": [ 173 | { 174 | "data": { 175 | "text/html": [ 176 | "
\n", 177 | "\n", 190 | "\n", 191 | " \n", 192 | " \n", 193 | " \n", 194 | " \n", 195 | " \n", 196 | " \n", 197 | " \n", 198 | " \n", 199 | " \n", 200 | " \n", 201 | " \n", 202 | " \n", 203 | " \n", 204 | " \n", 205 | " \n", 206 | " \n", 207 | " \n", 208 | " \n", 209 | " \n", 210 | " \n", 211 | " \n", 212 | " \n", 213 | " \n", 214 | " \n", 215 | " \n", 216 | "
總經代碼目前狀態中文全稱頻率代碼
None
50CA152022-01-01美國十年期公債殖利率M
\n", 217 | "
" 218 | ], 219 | "text/plain": [ 220 | " 總經代碼 目前狀態 中文全稱 頻率代碼\n", 221 | "None \n", 222 | "50 CA15 2022-01-01 美國十年期公債殖利率 M" 223 | ] 224 | }, 225 | "execution_count": 3, 226 | "metadata": {}, 227 | "output_type": "execute_result" 228 | } 229 | ], 230 | "source": [ 231 | "factor_macro[factor_macro['中文全稱'] == '美國十年期公債殖利率']" 232 | ] 233 | }, 234 | { 235 | "cell_type": "code", 236 | "execution_count": 16, 237 | "id": "7a1db7c9", 238 | "metadata": { 239 | "scrolled": false 240 | }, 241 | "outputs": [ 242 | { 243 | "data": { 244 | "text/html": [ 245 | "
\n", 246 | "\n", 259 | "\n", 260 | " \n", 261 | " \n", 262 | " \n", 263 | " \n", 264 | " \n", 265 | " \n", 266 | " \n", 267 | " \n", 268 | " \n", 269 | " \n", 270 | " \n", 271 | " \n", 272 | " \n", 273 | " \n", 274 | " \n", 275 | " \n", 276 | " \n", 277 | " \n", 278 | " \n", 279 | " \n", 280 | " \n", 281 | " \n", 282 | " \n", 283 | " \n", 284 | " \n", 285 | " \n", 286 | " \n", 287 | " \n", 288 | " \n", 289 | " \n", 290 | " \n", 291 | " \n", 292 | " \n", 293 | " \n", 294 | " \n", 295 | " \n", 296 | " \n", 297 | " \n", 298 | " \n", 299 | "
年月yield_10yr
None
02020-01-011.76
12020-02-011.50
22020-03-010.87
32020-04-010.66
42020-05-010.67
\n", 300 | "
" 301 | ], 302 | "text/plain": [ 303 | " 年月 yield_10yr\n", 304 | "None \n", 305 | "0 2020-01-01 1.76\n", 306 | "1 2020-02-01 1.50\n", 307 | "2 2020-03-01 0.87\n", 308 | "3 2020-04-01 0.66\n", 309 | "4 2020-05-01 0.67" 310 | ] 311 | }, 312 | "execution_count": 16, 313 | "metadata": {}, 314 | "output_type": "execute_result" 315 | } 316 | ], 317 | "source": [ 318 | "yield_10yr = tejapi.get('GLOBAL/ANMAR',\n", 319 | " mdate={'gte': '2020-01-01', 'lte':'2022-03-01'},\n", 320 | " opts={'columns': ['mdate', 'val']},\n", 321 | " coid = 'CA15',\n", 322 | " chinese_column_name=True,\n", 323 | " paginate=True)\n", 324 | "yield_10yr = yield_10yr.rename(columns = {'數值':'yield_10yr'})\n", 325 | "yield_10yr.head()" 326 | ] 327 | }, 328 | { 329 | "cell_type": "markdown", 330 | "id": "86225daf", 331 | "metadata": {}, 332 | "source": [ 333 | "### 指數、基金:Y9997大盤 / 0055金融 / 0053電子 / 0056高股息" 334 | ] 335 | }, 336 | { 337 | "cell_type": "code", 338 | "execution_count": 10, 339 | "id": "7143b804", 340 | "metadata": {}, 341 | "outputs": [], 342 | "source": [ 343 | "index_list = ['Y9997', '0053', '0055', '0056']" 344 | ] 345 | }, 346 | { 347 | "cell_type": "code", 348 | "execution_count": 18, 349 | "id": "238daa74", 350 | "metadata": { 351 | "scrolled": false 352 | }, 353 | "outputs": [ 354 | { 355 | "data": { 356 | "text/html": [ 357 | "
\n", 358 | "\n", 371 | "\n", 372 | " \n", 373 | " \n", 374 | " \n", 375 | " \n", 376 | " \n", 377 | " \n", 378 | " \n", 379 | " \n", 380 | " \n", 381 | " \n", 382 | " \n", 383 | " \n", 384 | " \n", 385 | " \n", 386 | " \n", 387 | " \n", 388 | " \n", 389 | " \n", 390 | " \n", 391 | " \n", 392 | " \n", 393 | " \n", 394 | " \n", 395 | " \n", 396 | " \n", 397 | " \n", 398 | " \n", 399 | " \n", 400 | " \n", 401 | " \n", 402 | " \n", 403 | " \n", 404 | " \n", 405 | " \n", 406 | " \n", 407 | " \n", 408 | " \n", 409 | " \n", 410 | " \n", 411 | " \n", 412 | " \n", 413 | " \n", 414 | " \n", 415 | " \n", 416 | " \n", 417 | " \n", 418 | " \n", 419 | " \n", 420 | " \n", 421 | " \n", 422 | " \n", 423 | " \n", 424 | " \n", 425 | " \n", 426 | " \n", 427 | " \n", 428 | " \n", 429 | " \n", 430 | " \n", 431 | " \n", 432 | " \n", 433 | " \n", 434 | " \n", 435 | " \n", 436 | " \n", 437 | " \n", 438 | " \n", 439 | " \n", 440 | " \n", 441 | " \n", 442 | " \n", 443 | " \n", 444 | " \n", 445 | " \n", 446 | " \n", 447 | " \n", 448 | " \n", 449 | " \n", 450 | " \n", 451 | " \n", 452 | " \n", 453 | " \n", 454 | "
證券代碼年月報酬率%_月
None
000532020-01-01-4.1972
100532020-02-01-2.3761
200532020-03-01-13.2353
300532020-04-0113.9682
400532020-05-01-1.1027
............
103Y99972021-11-012.5960
104Y99972021-12-014.6791
105Y99972022-01-01-2.9810
106Y99972022-02-01-0.1182
107Y99972022-03-010.4297
\n", 455 | "

108 rows × 3 columns

\n", 456 | "
" 457 | ], 458 | "text/plain": [ 459 | " 證券代碼 年月 報酬率%_月\n", 460 | "None \n", 461 | "0 0053 2020-01-01 -4.1972\n", 462 | "1 0053 2020-02-01 -2.3761\n", 463 | "2 0053 2020-03-01 -13.2353\n", 464 | "3 0053 2020-04-01 13.9682\n", 465 | "4 0053 2020-05-01 -1.1027\n", 466 | "... ... ... ...\n", 467 | "103 Y9997 2021-11-01 2.5960\n", 468 | "104 Y9997 2021-12-01 4.6791\n", 469 | "105 Y9997 2022-01-01 -2.9810\n", 470 | "106 Y9997 2022-02-01 -0.1182\n", 471 | "107 Y9997 2022-03-01 0.4297\n", 472 | "\n", 473 | "[108 rows x 3 columns]" 474 | ] 475 | }, 476 | "execution_count": 18, 477 | "metadata": {}, 478 | "output_type": "execute_result" 479 | } 480 | ], 481 | "source": [ 482 | "index_data = tejapi.get('TWN/AAPRCM1',\n", 483 | " coid = index_list,\n", 484 | " mdate= {'gte': '2020-01-01', 'lte': '2022-03-01'},\n", 485 | " opts={'columns': ['coid', 'mdate', 'roi']},\n", 486 | " paginate = True,\n", 487 | " chinese_column_name = True)\n", 488 | "index_data" 489 | ] 490 | }, 491 | { 492 | "cell_type": "code", 493 | "execution_count": 19, 494 | "id": "1bb374a3", 495 | "metadata": {}, 496 | "outputs": [ 497 | { 498 | "data": { 499 | "text/html": [ 500 | "
\n", 501 | "\n", 514 | "\n", 515 | " \n", 516 | " \n", 517 | " \n", 518 | " \n", 519 | " \n", 520 | " \n", 521 | " \n", 522 | " \n", 523 | " \n", 524 | " \n", 525 | " \n", 526 | " \n", 527 | " \n", 528 | " \n", 529 | " \n", 530 | " \n", 531 | " \n", 532 | " \n", 533 | " \n", 534 | " \n", 535 | " \n", 536 | " \n", 537 | " \n", 538 | " \n", 539 | " \n", 540 | " \n", 541 | " \n", 542 | " \n", 543 | " \n", 544 | " \n", 545 | " \n", 546 | " \n", 547 | " \n", 548 | " \n", 549 | " \n", 550 | " \n", 551 | " \n", 552 | " \n", 553 | " \n", 554 | " \n", 555 | " \n", 556 | " \n", 557 | " \n", 558 | " \n", 559 | " \n", 560 | " \n", 561 | " \n", 562 | " \n", 563 | " \n", 564 | " \n", 565 | " \n", 566 | " \n", 567 | "
年月Y9997005300550056
02020-01-01-4.1847-4.1972-1.7367-3.6245
12020-02-01-1.7621-2.37610.0535-0.0714
22020-03-01-13.8174-13.2353-17.2913-12.2221
32020-04-0113.256213.968211.779812.1275
42020-05-01-0.4498-1.1027-0.11581.4565
\n", 568 | "
" 569 | ], 570 | "text/plain": [ 571 | " 年月 Y9997 0053 0055 0056\n", 572 | "0 2020-01-01 -4.1847 -4.1972 -1.7367 -3.6245\n", 573 | "1 2020-02-01 -1.7621 -2.3761 0.0535 -0.0714\n", 574 | "2 2020-03-01 -13.8174 -13.2353 -17.2913 -12.2221\n", 575 | "3 2020-04-01 13.2562 13.9682 11.7798 12.1275\n", 576 | "4 2020-05-01 -0.4498 -1.1027 -0.1158 1.4565" 577 | ] 578 | }, 579 | "execution_count": 19, 580 | "metadata": {}, 581 | "output_type": "execute_result" 582 | } 583 | ], 584 | "source": [ 585 | "index_data = index_data.set_index('年月')\n", 586 | "\n", 587 | "roi = {}\n", 588 | "\n", 589 | "for i in index_list:\n", 590 | " r = index_data[index_data['證券代碼'] == i]\n", 591 | " r = r['報酬率%_月']\n", 592 | " roi.setdefault(i, r) \n", 593 | "\n", 594 | "roi = pd.concat(roi, axis = 1).reset_index()\n", 595 | "roi.head()" 596 | ] 597 | }, 598 | { 599 | "cell_type": "code", 600 | "execution_count": 20, 601 | "id": "bf990a5f", 602 | "metadata": {}, 603 | "outputs": [ 604 | { 605 | "data": { 606 | "text/html": [ 607 | "
\n", 608 | "\n", 621 | "\n", 622 | " \n", 623 | " \n", 624 | " \n", 625 | " \n", 626 | " \n", 627 | " \n", 628 | " \n", 629 | " \n", 630 | " \n", 631 | " \n", 632 | " \n", 633 | " \n", 634 | " \n", 635 | " \n", 636 | " \n", 637 | " \n", 638 | " \n", 639 | " \n", 640 | " \n", 641 | " \n", 642 | " \n", 643 | " \n", 644 | " \n", 645 | " \n", 646 | " \n", 647 | " \n", 648 | " \n", 649 | " \n", 650 | " \n", 651 | " \n", 652 | " \n", 653 | " \n", 654 | " \n", 655 | " \n", 656 | " \n", 657 | " \n", 658 | " \n", 659 | " \n", 660 | " \n", 661 | " \n", 662 | " \n", 663 | " \n", 664 | " \n", 665 | " \n", 666 | " \n", 667 | " \n", 668 | " \n", 669 | " \n", 670 | " \n", 671 | " \n", 672 | " \n", 673 | " \n", 674 | " \n", 675 | " \n", 676 | " \n", 677 | " \n", 678 | " \n", 679 | " \n", 680 | "
年月台灣大盤電子股金融股高股息yield_10yr
02020-01-01-4.1847-4.1972-1.7367-3.62451.76
12020-02-01-1.7621-2.37610.0535-0.07141.50
22020-03-01-13.8174-13.2353-17.2913-12.22210.87
32020-04-0113.256213.968211.779812.12750.66
42020-05-01-0.4498-1.1027-0.11581.45650.67
\n", 681 | "
" 682 | ], 683 | "text/plain": [ 684 | " 年月 台灣大盤 電子股 金融股 高股息 yield_10yr\n", 685 | "0 2020-01-01 -4.1847 -4.1972 -1.7367 -3.6245 1.76\n", 686 | "1 2020-02-01 -1.7621 -2.3761 0.0535 -0.0714 1.50\n", 687 | "2 2020-03-01 -13.8174 -13.2353 -17.2913 -12.2221 0.87\n", 688 | "3 2020-04-01 13.2562 13.9682 11.7798 12.1275 0.66\n", 689 | "4 2020-05-01 -0.4498 -1.1027 -0.1158 1.4565 0.67" 690 | ] 691 | }, 692 | "execution_count": 20, 693 | "metadata": {}, 694 | "output_type": "execute_result" 695 | } 696 | ], 697 | "source": [ 698 | "final = pd.merge(roi, yield_10yr, how = 'inner', on = ['年月'])\n", 699 | "final = final.rename(columns = {'Y9997':'台灣大盤', '0053':'電子股', '0055':'金融股', '0056':'高股息'})\n", 700 | "final.head()" 701 | ] 702 | }, 703 | { 704 | "cell_type": "code", 705 | "execution_count": 21, 706 | "id": "570a03d5", 707 | "metadata": { 708 | "scrolled": false 709 | }, 710 | "outputs": [ 711 | { 712 | "name": "stdout", 713 | "output_type": "stream", 714 | "text": [ 715 | "台灣大盤\n", 716 | "\n", 717 | " OLS Regression Results \n", 718 | "==============================================================================\n", 719 | "Dep. Variable: 台灣大盤 R-squared: 0.074\n", 720 | "Model: OLS Adj. R-squared: 0.037\n", 721 | "Method: Least Squares F-statistic: 2.003\n", 722 | "Date: Tue, 03 May 2022 Prob (F-statistic): 0.169\n", 723 | "Time: 07:54:21 Log-Likelihood: -82.700\n", 724 | "No. Observations: 27 AIC: 169.4\n", 725 | "Df Residuals: 25 BIC: 172.0\n", 726 | "Df Model: 1 \n", 727 | "Covariance Type: nonrobust \n", 728 | "==============================================================================\n", 729 | " coef std err t P>|t| [0.025 0.975]\n", 730 | "------------------------------------------------------------------------------\n", 731 | "Intercept 5.9990 3.120 1.923 0.066 -0.427 12.425\n", 732 | "yield_10yr -3.3213 2.347 -1.415 0.169 -8.155 1.512\n", 733 | "==============================================================================\n", 734 | "Omnibus: 9.507 Durbin-Watson: 2.379\n", 735 | "Prob(Omnibus): 0.009 Jarque-Bera (JB): 8.683\n", 736 | "Skew: -0.914 Prob(JB): 0.0130\n", 737 | "Kurtosis: 5.093 Cond. No. 6.11\n", 738 | "==============================================================================\n", 739 | "\n", 740 | "Notes:\n", 741 | "[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.\n", 742 | "\n", 743 | "電子股\n", 744 | "\n", 745 | " OLS Regression Results \n", 746 | "==============================================================================\n", 747 | "Dep. Variable: 電子股 R-squared: 0.156\n", 748 | "Model: OLS Adj. R-squared: 0.122\n", 749 | "Method: Least Squares F-statistic: 4.605\n", 750 | "Date: Tue, 03 May 2022 Prob (F-statistic): 0.0418\n", 751 | "Time: 07:54:21 Log-Likelihood: -86.522\n", 752 | "No. Observations: 27 AIC: 177.0\n", 753 | "Df Residuals: 25 BIC: 179.6\n", 754 | "Df Model: 1 \n", 755 | "Covariance Type: nonrobust \n", 756 | "==============================================================================\n", 757 | " coef std err t P>|t| [0.025 0.975]\n", 758 | "------------------------------------------------------------------------------\n", 759 | "Intercept 9.3494 3.595 2.601 0.015 1.946 16.752\n", 760 | "yield_10yr -5.8026 2.704 -2.146 0.042 -11.371 -0.234\n", 761 | "==============================================================================\n", 762 | "Omnibus: 4.723 Durbin-Watson: 2.262\n", 763 | "Prob(Omnibus): 0.094 Jarque-Bera (JB): 3.446\n", 764 | "Skew: -0.392 Prob(JB): 0.179\n", 765 | "Kurtosis: 4.565 Cond. No. 6.11\n", 766 | "==============================================================================\n", 767 | "\n", 768 | "Notes:\n", 769 | "[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.\n", 770 | "\n", 771 | "金融股\n", 772 | "\n", 773 | " OLS Regression Results \n", 774 | "==============================================================================\n", 775 | "Dep. Variable: 金融股 R-squared: 0.034\n", 776 | "Model: OLS Adj. R-squared: -0.005\n", 777 | "Method: Least Squares F-statistic: 0.8809\n", 778 | "Date: Tue, 03 May 2022 Prob (F-statistic): 0.357\n", 779 | "Time: 07:54:21 Log-Likelihood: -82.687\n", 780 | "No. Observations: 27 AIC: 169.4\n", 781 | "Df Residuals: 25 BIC: 172.0\n", 782 | "Df Model: 1 \n", 783 | "Covariance Type: nonrobust \n", 784 | "==============================================================================\n", 785 | " coef std err t P>|t| [0.025 0.975]\n", 786 | "------------------------------------------------------------------------------\n", 787 | "Intercept -1.0606 3.119 -0.340 0.737 -7.483 5.362\n", 788 | "yield_10yr 2.2018 2.346 0.939 0.357 -2.630 7.033\n", 789 | "==============================================================================\n", 790 | "Omnibus: 14.834 Durbin-Watson: 2.406\n", 791 | "Prob(Omnibus): 0.001 Jarque-Bera (JB): 21.555\n", 792 | "Skew: -1.101 Prob(JB): 2.09e-05\n", 793 | "Kurtosis: 6.784 Cond. No. 6.11\n", 794 | "==============================================================================\n", 795 | "\n", 796 | "Notes:\n", 797 | "[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.\n", 798 | "\n", 799 | "高股息\n", 800 | "\n", 801 | " OLS Regression Results \n", 802 | "==============================================================================\n", 803 | "Dep. Variable: 高股息 R-squared: 0.010\n", 804 | "Model: OLS Adj. R-squared: -0.030\n", 805 | "Method: Least Squares F-statistic: 0.2496\n", 806 | "Date: Tue, 03 May 2022 Prob (F-statistic): 0.622\n", 807 | "Time: 07:54:21 Log-Likelihood: -79.146\n", 808 | "No. Observations: 27 AIC: 162.3\n", 809 | "Df Residuals: 25 BIC: 164.9\n", 810 | "Df Model: 1 \n", 811 | "Covariance Type: nonrobust \n", 812 | "==============================================================================\n", 813 | " coef std err t P>|t| [0.025 0.975]\n", 814 | "------------------------------------------------------------------------------\n", 815 | "Intercept 2.3649 2.735 0.865 0.395 -3.268 7.998\n", 816 | "yield_10yr -1.0279 2.058 -0.500 0.622 -5.265 3.210\n", 817 | "==============================================================================\n", 818 | "Omnibus: 5.134 Durbin-Watson: 2.240\n", 819 | "Prob(Omnibus): 0.077 Jarque-Bera (JB): 3.994\n", 820 | "Skew: -0.411 Prob(JB): 0.136\n", 821 | "Kurtosis: 4.695 Cond. No. 6.11\n", 822 | "==============================================================================\n", 823 | "\n", 824 | "Notes:\n", 825 | "[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.\n", 826 | "\n" 827 | ] 828 | } 829 | ], 830 | "source": [ 831 | "import statsmodels.formula.api as smf\n", 832 | "from statsmodels.stats.anova import anova_lm\n", 833 | "\n", 834 | "for i in list(final.columns)[1:5]:\n", 835 | " data = pd.DataFrame(final[[i, 'yield_10yr']])\n", 836 | " mdl = smf.ols(i + ' ~ yield_10yr', data=data).fit()\n", 837 | " print(i)\n", 838 | " print()\n", 839 | " print(mdl.summary())\n", 840 | " print()" 841 | ] 842 | }, 843 | { 844 | "cell_type": "code", 845 | "execution_count": null, 846 | "id": "fdd3554d", 847 | "metadata": {}, 848 | "outputs": [], 849 | "source": [] 850 | }, 851 | { 852 | "cell_type": "code", 853 | "execution_count": null, 854 | "id": "1cb78710", 855 | "metadata": {}, 856 | "outputs": [], 857 | "source": [] 858 | } 859 | ], 860 | "metadata": { 861 | "kernelspec": { 862 | "display_name": "Python 3", 863 | "language": "python", 864 | "name": "python3" 865 | }, 866 | "language_info": { 867 | "codemirror_mode": { 868 | "name": "ipython", 869 | "version": 3 870 | }, 871 | "file_extension": ".py", 872 | "mimetype": "text/x-python", 873 | "name": "python", 874 | "nbconvert_exporter": "python", 875 | "pygments_lexer": "ipython3", 876 | "version": "3.8.8" 877 | } 878 | }, 879 | "nbformat": 4, 880 | "nbformat_minor": 5 881 | } 882 | -------------------------------------------------------------------------------- /TEJAPI_Medium實戰應用9.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "4ede4248-fd65-4f30-9588-aca73e08c545", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "#%% 匯入功能\n", 11 | "# 功能模組\n", 12 | "import pandas as pd\n", 13 | "import numpy as np\n", 14 | "import plotly.graph_objects as go\n", 15 | "\n", 16 | "#TEJ API\n", 17 | "import tejapi\n", 18 | "tejapi.ApiConfig.api_key = \"The Key\"" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 2, 24 | "id": "c29290d1-8675-43f0-845e-2411d44cb306", 25 | "metadata": {}, 26 | "outputs": [ 27 | { 28 | "data": { 29 | "text/html": [ 30 | "
\n", 31 | "\n", 44 | "\n", 45 | " \n", 46 | " \n", 47 | " \n", 48 | " \n", 49 | " \n", 50 | " \n", 51 | " \n", 52 | " \n", 53 | " \n", 54 | " \n", 55 | " \n", 56 | " \n", 57 | " \n", 58 | " \n", 59 | " \n", 60 | " \n", 61 | " \n", 62 | " \n", 63 | " \n", 64 | " \n", 65 | " \n", 66 | " \n", 67 | " \n", 68 | " \n", 69 | " \n", 70 | " \n", 71 | " \n", 72 | " \n", 73 | " \n", 74 | " \n", 75 | " \n", 76 | " \n", 77 | " \n", 78 | " \n", 79 | " \n", 80 | " \n", 81 | " \n", 82 | " \n", 83 | " \n", 84 | " \n", 85 | " \n", 86 | " \n", 87 | " \n", 88 | " \n", 89 | " \n", 90 | " \n", 91 | " \n", 92 | " \n", 93 | " \n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | " \n", 114 | " \n", 115 | " \n", 116 | " \n", 117 | " \n", 118 | " \n", 119 | " \n", 120 | " \n", 121 | " \n", 122 | " \n", 123 | " \n", 124 | " \n", 125 | " \n", 126 | " \n", 127 | " \n", 128 | " \n", 129 | " \n", 130 | " \n", 131 | " \n", 132 | " \n", 133 | " \n", 134 | " \n", 135 | " \n", 136 | " \n", 137 | " \n", 138 | " \n", 139 | " \n", 140 | " \n", 141 | " \n", 142 | " \n", 143 | " \n", 144 | " \n", 145 | " \n", 146 | " \n", 147 | " \n", 148 | " \n", 149 | " \n", 150 | " \n", 151 | " \n", 152 | " \n", 153 | " \n", 154 | " \n", 155 | " \n", 156 | " \n", 157 | "
日期標的名稱權重證券碼
None
32021-09-01 00:00:00+00:002303 聯電6.41230320219
42021-09-01 00:00:00+00:002317 鴻海11.99231720219
52021-09-01 00:00:00+00:002324 仁寶1.05232420219
62021-09-01 00:00:00+00:002330 台積電30.24233020219
72021-09-01 00:00:00+00:002345 智邦1.73234520219
82021-09-01 00:00:00+00:002352 佳世達0.55235220219
92021-09-01 00:00:00+00:002356 英業達0.84235620219
102021-09-01 00:00:00+00:002360 致茂0.80236020219
112021-09-01 00:00:00+00:002379 瑞昱3.05237920219
122021-09-01 00:00:00+00:002382 廣達2.24238220219
\n", 158 | "
" 159 | ], 160 | "text/plain": [ 161 | " 日期 標的名稱 權重 證券碼 年 月\n", 162 | "None \n", 163 | "3 2021-09-01 00:00:00+00:00 2303 聯電 6.41 2303 2021 9\n", 164 | "4 2021-09-01 00:00:00+00:00 2317 鴻海 11.99 2317 2021 9\n", 165 | "5 2021-09-01 00:00:00+00:00 2324 仁寶 1.05 2324 2021 9\n", 166 | "6 2021-09-01 00:00:00+00:00 2330 台積電 30.24 2330 2021 9\n", 167 | "7 2021-09-01 00:00:00+00:00 2345 智邦 1.73 2345 2021 9\n", 168 | "8 2021-09-01 00:00:00+00:00 2352 佳世達 0.55 2352 2021 9\n", 169 | "9 2021-09-01 00:00:00+00:00 2356 英業達 0.84 2356 2021 9\n", 170 | "10 2021-09-01 00:00:00+00:00 2360 致茂 0.80 2360 2021 9\n", 171 | "11 2021-09-01 00:00:00+00:00 2379 瑞昱 3.05 2379 2021 9\n", 172 | "12 2021-09-01 00:00:00+00:00 2382 廣達 2.24 2382 2021 9" 173 | ] 174 | }, 175 | "execution_count": 2, 176 | "metadata": {}, 177 | "output_type": "execute_result" 178 | } 179 | ], 180 | "source": [ 181 | "#%% 匯入 TEJ資料\n", 182 | "etf = tejapi.get('TWN/AEHOLD',\n", 183 | " coid = '00881',\n", 184 | " mdate= {'gte': '2021-09-01','lte':'2021-11-30'},\n", 185 | " opts={'columns':['mdate', 'no','pct']},\n", 186 | " chinese_column_name=True,paginate=True)\n", 187 | "\n", 188 | "# 標竿指數:臺50指數\n", 189 | "benchmark = tejapi.get('TWN/AIDXS',\n", 190 | " coid = 'TWN50',\n", 191 | " mdate= {'gte': '2021-09-01','lte':'2021-11-30'},\n", 192 | " opts={'columns':['mdate','key3','mv_pct']},\n", 193 | " chinese_column_name=True,paginate=True)\n", 194 | "\n", 195 | "\n", 196 | "etf = etf[~etf['標的名稱'].isin(['申贖應付款','保證金','現金'])]\n", 197 | "etf['證券碼'] = etf['標的名稱'].str[0:4]\n", 198 | "etf['證券碼'] = np.where(etf['證券碼'] == 'TX 台','Y9999',etf['證券碼'])\n", 199 | "etf['年'] = etf['日期'].dt.year\n", 200 | "etf['月'] = etf['日期'].dt.month\n", 201 | "etf = etf.drop_duplicates(subset=['年','月','證券碼'], keep='first')\n", 202 | "\n", 203 | "benchmark['證券碼'] = benchmark['成份股'].str[0:4]\n", 204 | "benchmark['年'] = benchmark['年月日'].dt.year\n", 205 | "benchmark['月'] = benchmark['年月日'].dt.month\n", 206 | "benchmark = benchmark.drop_duplicates(subset=['年','月','證券碼'], keep='first')\n", 207 | "\n", 208 | "etf.head(10)" 209 | ] 210 | }, 211 | { 212 | "cell_type": "code", 213 | "execution_count": 3, 214 | "id": "31571ff9-edc9-4f20-b12c-3cf73d0fe14e", 215 | "metadata": {}, 216 | "outputs": [ 217 | { 218 | "data": { 219 | "text/html": [ 220 | "
\n", 221 | "\n", 234 | "\n", 235 | " \n", 236 | " \n", 237 | " \n", 238 | " \n", 239 | " \n", 240 | " \n", 241 | " \n", 242 | " \n", 243 | " \n", 244 | " \n", 245 | " \n", 246 | " \n", 247 | " \n", 248 | " \n", 249 | " \n", 250 | " \n", 251 | " \n", 252 | " \n", 253 | " \n", 254 | " \n", 255 | " \n", 256 | " \n", 257 | " \n", 258 | " \n", 259 | " \n", 260 | " \n", 261 | " \n", 262 | " \n", 263 | " \n", 264 | " \n", 265 | " \n", 266 | " \n", 267 | " \n", 268 | " \n", 269 | " \n", 270 | " \n", 271 | " \n", 272 | " \n", 273 | " \n", 274 | " \n", 275 | " \n", 276 | " \n", 277 | " \n", 278 | " \n", 279 | " \n", 280 | " \n", 281 | "
證券碼證券名稱TSE產業名
None
01101台泥水泥工業
11102亞泥水泥工業
21216統一食品工業
31301台塑塑膠工業
41303南亞塑膠工業
\n", 282 | "
" 283 | ], 284 | "text/plain": [ 285 | " 證券碼 證券名稱 TSE產業名\n", 286 | "None \n", 287 | "0 1101 台泥 水泥工業\n", 288 | "1 1102 亞泥 水泥工業\n", 289 | "2 1216 統一 食品工業\n", 290 | "3 1301 台塑 塑膠工業\n", 291 | "4 1303 南亞 塑膠工業" 292 | ] 293 | }, 294 | "execution_count": 3, 295 | "metadata": {}, 296 | "output_type": "execute_result" 297 | } 298 | ], 299 | "source": [ 300 | "# 獲得 etf與 benchmark的代碼\n", 301 | "coid_list = etf['證券碼'].unique().tolist()\n", 302 | "coid_list.append('Y9999')\n", 303 | "coid_list = coid_list + benchmark['證券碼'].unique().tolist()\n", 304 | "\n", 305 | "# 抓取公司的產業名稱\n", 306 | "code = tejapi.get(\"TWN/EWNPRCSTD\",\n", 307 | " coid = coid_list,\n", 308 | " paginate=True,\n", 309 | " opts={'columns':['coid', 'coid_name','tseindnm']},\n", 310 | " chinese_column_name=True)\n", 311 | "\n", 312 | "code.head(5)" 313 | ] 314 | }, 315 | { 316 | "cell_type": "code", 317 | "execution_count": 4, 318 | "id": "512f4f31-e17a-4ca3-9f3d-8fa7b19fa1e7", 319 | "metadata": {}, 320 | "outputs": [ 321 | { 322 | "data": { 323 | "text/html": [ 324 | "
\n", 325 | "\n", 338 | "\n", 339 | " \n", 340 | " \n", 341 | " \n", 342 | " \n", 343 | " \n", 344 | " \n", 345 | " \n", 346 | " \n", 347 | " \n", 348 | " \n", 349 | " \n", 350 | " \n", 351 | " \n", 352 | " \n", 353 | " \n", 354 | " \n", 355 | " \n", 356 | " \n", 357 | " \n", 358 | " \n", 359 | " \n", 360 | " \n", 361 | " \n", 362 | " \n", 363 | " \n", 364 | " \n", 365 | " \n", 366 | " \n", 367 | " \n", 368 | " \n", 369 | " \n", 370 | " \n", 371 | " \n", 372 | " \n", 373 | " \n", 374 | " \n", 375 | " \n", 376 | " \n", 377 | " \n", 378 | " \n", 379 | " \n", 380 | " \n", 381 | " \n", 382 | " \n", 383 | " \n", 384 | " \n", 385 | " \n", 386 | " \n", 387 | " \n", 388 | " \n", 389 | " \n", 390 | " \n", 391 | " \n", 392 | " \n", 393 | " \n", 394 | " \n", 395 | " \n", 396 | " \n", 397 | " \n", 398 | " \n", 399 | "
證券代碼年月報酬率%_月
None
011012021-09-01 00:00:00+00:005.263120219
111012021-10-01 00:00:00+00:00-5.3920202110
211012021-11-01 00:00:00+00:00-4.6630202111
311022021-09-01 00:00:00+00:001.558920219
411022021-10-01 00:00:00+00:00-2.8506202110
\n", 400 | "
" 401 | ], 402 | "text/plain": [ 403 | " 證券代碼 年月 報酬率%_月 年 月\n", 404 | "None \n", 405 | "0 1101 2021-09-01 00:00:00+00:00 5.2631 2021 9\n", 406 | "1 1101 2021-10-01 00:00:00+00:00 -5.3920 2021 10\n", 407 | "2 1101 2021-11-01 00:00:00+00:00 -4.6630 2021 11\n", 408 | "3 1102 2021-09-01 00:00:00+00:00 1.5589 2021 9\n", 409 | "4 1102 2021-10-01 00:00:00+00:00 -2.8506 2021 10" 410 | ] 411 | }, 412 | "execution_count": 4, 413 | "metadata": {}, 414 | "output_type": "execute_result" 415 | } 416 | ], 417 | "source": [ 418 | "# 股價\n", 419 | "price = tejapi.get('TWN/AAPRCM1',\n", 420 | " coid = coid_list,\n", 421 | " mdate= {'gte': '2021-09-01','lte':'2021-11-30'},\n", 422 | " opts={'columns':['coid', 'mdate','roi']},\n", 423 | " chinese_column_name=True,\n", 424 | " paginate=True)\n", 425 | "\n", 426 | "price['年'] = price['年月'].dt.year\n", 427 | "price['月'] = price['年月'].dt.month\n", 428 | "\n", 429 | "price.head(5)" 430 | ] 431 | }, 432 | { 433 | "cell_type": "code", 434 | "execution_count": 5, 435 | "id": "f585e1cd-f604-4054-807d-9636eb2122b8", 436 | "metadata": {}, 437 | "outputs": [], 438 | "source": [ 439 | "#%% 合併產業名稱 ETF\n", 440 | "\n", 441 | "# 合併產業名稱\n", 442 | "etf = pd.merge(etf ,code , how = 'left' , on = ['證券碼'])\n", 443 | "etf = pd.merge(etf ,price ,how = 'left' , left_on=['年','月','證券碼'], right_on=['年','月','證券代碼'])\n", 444 | "\n", 445 | "benchmark = pd.merge(benchmark ,code , how = 'left' , on = ['證券碼'])\n", 446 | "benchmark = pd.merge(benchmark ,price ,how = 'left' ,\n", 447 | " left_on=['年','月','證券碼'], right_on=['年','月','證券代碼'])\n", 448 | "\n", 449 | "# 處理產業不一致問題\n", 450 | "# 若 benchmark的產業種類沒有在 etf的產業種類中找到,則 benchmark中特殊的產業改成其他\n", 451 | "benchmark['TSE產業名'] = np.where(benchmark['TSE產業名'].isin(etf['TSE產業名'].unique().tolist()),\n", 452 | " benchmark['TSE產業名'],'其他')\n", 453 | "\n", 454 | "# 若 etf的產業種類沒有在 benchmark的產業種類中找到,則 etf中特殊的產業改成其他\n", 455 | "etf['TSE產業名'] = np.where(etf['TSE產業名'].isin(benchmark['TSE產業名'].unique().tolist()),\n", 456 | " etf['TSE產業名'],'其他')" 457 | ] 458 | }, 459 | { 460 | "cell_type": "code", 461 | "execution_count": 6, 462 | "id": "25000c35-4854-4e78-a076-0490af5880fc", 463 | "metadata": {}, 464 | "outputs": [ 465 | { 466 | "data": { 467 | "text/html": [ 468 | "
\n", 469 | "\n", 482 | "\n", 483 | " \n", 484 | " \n", 485 | " \n", 486 | " \n", 487 | " \n", 488 | " \n", 489 | " \n", 490 | " \n", 491 | " \n", 492 | " \n", 493 | " \n", 494 | " \n", 495 | " \n", 496 | " \n", 497 | " \n", 498 | " \n", 499 | " \n", 500 | " \n", 501 | " \n", 502 | " \n", 503 | " \n", 504 | " \n", 505 | " \n", 506 | " \n", 507 | " \n", 508 | " \n", 509 | " \n", 510 | " \n", 511 | " \n", 512 | " \n", 513 | " \n", 514 | " \n", 515 | " \n", 516 | " \n", 517 | " \n", 518 | " \n", 519 | " \n", 520 | " \n", 521 | " \n", 522 | " \n", 523 | " \n", 524 | " \n", 525 | " \n", 526 | " \n", 527 | " \n", 528 | " \n", 529 | " \n", 530 | " \n", 531 | " \n", 532 | " \n", 533 | " \n", 534 | " \n", 535 | " \n", 536 | " \n", 537 | " \n", 538 | " \n", 539 | " \n", 540 | " \n", 541 | " \n", 542 | " \n", 543 | " \n", 544 | " \n", 545 | " \n", 546 | " \n", 547 | " \n", 548 | " \n", 549 | " \n", 550 | " \n", 551 | " \n", 552 | " \n", 553 | " \n", 554 | " \n", 555 | " \n", 556 | " \n", 557 | " \n", 558 | " \n", 559 | " \n", 560 | " \n", 561 | " \n", 562 | " \n", 563 | " \n", 564 | " \n", 565 | " \n", 566 | " \n", 567 | " \n", 568 | " \n", 569 | " \n", 570 | " \n", 571 | " \n", 572 | " \n", 573 | " \n", 574 | " \n", 575 | " \n", 576 | " \n", 577 | " \n", 578 | " \n", 579 | " \n", 580 | " \n", 581 | " \n", 582 | " \n", 583 | "
年月日TSE產業名成份股證券代碼前日市值比重報酬率%_月產業權重實際當月報酬率產業當月報酬率實際產業當月報酬率ETF 當月報酬率
02021-09-01 00:00:00+00:00光電業2409 友達2409202190.0050840.28420.0139390.001445-11.511265-0.160456-3.615165
12021-09-01 00:00:00+00:00光電業3008 大立光3008202190.008855-18.28350.013939-0.161900-11.511265-0.160456-3.615165
22021-09-01 00:00:00+00:00其他1101 台泥1101202190.0084785.26310.2743390.044621-0.963335-0.264280-3.615165
32021-09-01 00:00:00+00:00其他1102 亞泥1102202190.0035971.55890.2743390.005607-0.963335-0.264280-3.615165
42021-09-01 00:00:00+00:00其他1216 統一1216202190.011469-1.73680.274339-0.019919-0.963335-0.264280-3.615165
\n", 584 | "
" 585 | ], 586 | "text/plain": [ 587 | " 年月日 TSE產業名 成份股 證券代碼 年 月 前日市值比重 \\\n", 588 | "0 2021-09-01 00:00:00+00:00 光電業 2409 友達 2409 2021 9 0.005084 \n", 589 | "1 2021-09-01 00:00:00+00:00 光電業 3008 大立光 3008 2021 9 0.008855 \n", 590 | "2 2021-09-01 00:00:00+00:00 其他 1101 台泥 1101 2021 9 0.008478 \n", 591 | "3 2021-09-01 00:00:00+00:00 其他 1102 亞泥 1102 2021 9 0.003597 \n", 592 | "4 2021-09-01 00:00:00+00:00 其他 1216 統一 1216 2021 9 0.011469 \n", 593 | "\n", 594 | " 報酬率%_月 產業權重 實際當月報酬率 產業當月報酬率 實際產業當月報酬率 ETF 當月報酬率 \n", 595 | "0 0.2842 0.013939 0.001445 -11.511265 -0.160456 -3.615165 \n", 596 | "1 -18.2835 0.013939 -0.161900 -11.511265 -0.160456 -3.615165 \n", 597 | "2 5.2631 0.274339 0.044621 -0.963335 -0.264280 -3.615165 \n", 598 | "3 1.5589 0.274339 0.005607 -0.963335 -0.264280 -3.615165 \n", 599 | "4 -1.7368 0.274339 -0.019919 -0.963335 -0.264280 -3.615165 " 600 | ] 601 | }, 602 | "execution_count": 6, 603 | "metadata": {}, 604 | "output_type": "execute_result" 605 | } 606 | ], 607 | "source": [ 608 | "#%% 計算產業與標竿指數的月報酬率,權重\n", 609 | "\n", 610 | "etf = etf.sort_values(by=['年','月','TSE產業名','證券代碼']).reset_index(drop=True) # 排序年月日\n", 611 | "etf['TSE產業名'] = np.where(etf['TSE產業名'].isna(),'其他' ,etf['TSE產業名'])\n", 612 | "etf['權重'] = etf['權重'] * 0.01\n", 613 | "\n", 614 | "\n", 615 | "etf['產業權重'] = etf.groupby(['TSE產業名','年','月'])['權重'].transform('sum')\n", 616 | "etf['實際當月報酬率'] = etf['權重'] * etf['報酬率%_月']\n", 617 | "etf['產業當月報酬率'] = etf.groupby(['TSE產業名','年','月'])['實際當月報酬率'].transform('sum') / etf['產業權重']\n", 618 | "\n", 619 | "etf['實際產業當月報酬率'] = etf['產業當月報酬率'] * etf['產業權重']\n", 620 | "etf['ETF 當月報酬率'] = etf.groupby(['年','月'])['實際當月報酬率'].transform('sum')\n", 621 | "etf = etf[['年','月','TSE產業名','標的名稱','權重','報酬率%_月','產業權重','產業當月報酬率']]\n", 622 | "\n", 623 | "\n", 624 | "benchmark = benchmark.sort_values(by=['年','月','TSE產業名','證券代碼']).reset_index(drop=True) # 排序年月日\n", 625 | "benchmark = benchmark[['年月日','TSE產業名','成份股','證券代碼','年','月','前日市值比重','報酬率%_月']]\n", 626 | "\n", 627 | "benchmark['前日市值比重'] = benchmark['前日市值比重'] * 0.01\n", 628 | "benchmark['產業權重'] = benchmark.groupby(['TSE產業名','年','月'])['前日市值比重'].transform('sum')\n", 629 | "benchmark['實際當月報酬率'] = benchmark['前日市值比重'] * benchmark['報酬率%_月']\n", 630 | "benchmark['產業當月報酬率'] = benchmark.groupby(['TSE產業名','年','月'])['實際當月報酬率'].transform('sum') \\\n", 631 | " / benchmark['產業權重']\n", 632 | "\n", 633 | "benchmark['實際產業當月報酬率'] = benchmark['產業當月報酬率'] * benchmark['產業權重']\n", 634 | "benchmark['ETF 當月報酬率'] = benchmark.groupby(['年','月'])['實際當月報酬率'].transform('sum')\n", 635 | "\n", 636 | "benchmark.head(5)" 637 | ] 638 | }, 639 | { 640 | "cell_type": "code", 641 | "execution_count": 7, 642 | "id": "75769555-aef8-4cd0-b275-4b424d3bf270", 643 | "metadata": {}, 644 | "outputs": [ 645 | { 646 | "data": { 647 | "text/html": [ 648 | "
\n", 649 | "\n", 662 | "\n", 663 | " \n", 664 | " \n", 665 | " \n", 666 | " \n", 667 | " \n", 668 | " \n", 669 | " \n", 670 | " \n", 671 | " \n", 672 | " \n", 673 | " \n", 674 | " \n", 675 | " \n", 676 | " \n", 677 | " \n", 678 | " \n", 679 | " \n", 680 | " \n", 681 | " \n", 682 | " \n", 683 | " \n", 684 | " \n", 685 | " \n", 686 | " \n", 687 | " \n", 688 | " \n", 689 | " \n", 690 | " \n", 691 | " \n", 692 | " \n", 693 | " \n", 694 | " \n", 695 | " \n", 696 | " \n", 697 | " \n", 698 | " \n", 699 | " \n", 700 | " \n", 701 | " \n", 702 | " \n", 703 | " \n", 704 | " \n", 705 | " \n", 706 | " \n", 707 | " \n", 708 | " \n", 709 | " \n", 710 | " \n", 711 | " \n", 712 | " \n", 713 | " \n", 714 | " \n", 715 | "
TSE產業名投組權重投組當月報酬率
020219光電業0.0238-18.283500
120219其他0.0272-2.156355
220219其他電子業0.1279-5.189256
320219半導體0.6297-3.043494
420219通信網路業0.0553-2.857103
\n", 716 | "
" 717 | ], 718 | "text/plain": [ 719 | " 年 月 TSE產業名 投組權重 投組當月報酬率\n", 720 | "0 2021 9 光電業 0.0238 -18.283500\n", 721 | "1 2021 9 其他 0.0272 -2.156355\n", 722 | "2 2021 9 其他電子業 0.1279 -5.189256\n", 723 | "3 2021 9 半導體 0.6297 -3.043494\n", 724 | "4 2021 9 通信網路業 0.0553 -2.857103" 725 | ] 726 | }, 727 | "execution_count": 7, 728 | "metadata": {}, 729 | "output_type": "execute_result" 730 | } 731 | ], 732 | "source": [ 733 | "#%% 合併 ETF與 Benchmark\n", 734 | "\n", 735 | "benchmark = benchmark.drop_duplicates(subset=['TSE產業名','年','月'], keep='first').reset_index(drop=True)\n", 736 | "benchmark = benchmark[['年','月','TSE產業名','產業權重','產業當月報酬率']].rename({'產業權重': '標竿權重',\n", 737 | " '產業當月報酬率':'標竿當月報酬率'}, axis=1)\n", 738 | "\n", 739 | "etf = etf.drop_duplicates(subset=['TSE產業名','年','月'], keep='first').reset_index(drop=True)\n", 740 | "etf = etf[['年','月','TSE產業名','產業權重','產業當月報酬率']].rename({'產業權重': '投組權重',\n", 741 | " '產業當月報酬率':'投組當月報酬率'}, axis=1)\n", 742 | "\n", 743 | "etf.head(5)" 744 | ] 745 | }, 746 | { 747 | "cell_type": "code", 748 | "execution_count": 8, 749 | "id": "ea4e90f8-74a4-4e0e-ac16-7f2a349b1259", 750 | "metadata": {}, 751 | "outputs": [ 752 | { 753 | "data": { 754 | "text/html": [ 755 | "
\n", 756 | "\n", 769 | "\n", 770 | " \n", 771 | " \n", 772 | " \n", 773 | " \n", 774 | " \n", 775 | " \n", 776 | " \n", 777 | " \n", 778 | " \n", 779 | " \n", 780 | " \n", 781 | " \n", 782 | " \n", 783 | " \n", 784 | " \n", 785 | " \n", 786 | " \n", 787 | " \n", 788 | " \n", 789 | " \n", 790 | " \n", 791 | " \n", 792 | " \n", 793 | " \n", 794 | " \n", 795 | " \n", 796 | " \n", 797 | " \n", 798 | " \n", 799 | " \n", 800 | " \n", 801 | " \n", 802 | " \n", 803 | " \n", 804 | " \n", 805 | " \n", 806 | " \n", 807 | " \n", 808 | " \n", 809 | " \n", 810 | " \n", 811 | " \n", 812 | " \n", 813 | " \n", 814 | " \n", 815 | " \n", 816 | " \n", 817 | " \n", 818 | " \n", 819 | " \n", 820 | " \n", 821 | " \n", 822 | " \n", 823 | " \n", 824 | " \n", 825 | " \n", 826 | " \n", 827 | " \n", 828 | " \n", 829 | " \n", 830 | " \n", 831 | " \n", 832 | " \n", 833 | " \n", 834 | " \n", 835 | " \n", 836 | " \n", 837 | " \n", 838 | " \n", 839 | " \n", 840 | " \n", 841 | " \n", 842 | " \n", 843 | " \n", 844 | " \n", 845 | " \n", 846 | " \n", 847 | " \n", 848 | " \n", 849 | " \n", 850 | " \n", 851 | " \n", 852 | " \n", 853 | " \n", 854 | " \n", 855 | " \n", 856 | " \n", 857 | " \n", 858 | " \n", 859 | " \n", 860 | " \n", 861 | " \n", 862 | " \n", 863 | " \n", 864 | " \n", 865 | " \n", 866 | " \n", 867 | " \n", 868 | " \n", 869 | " \n", 870 | " \n", 871 | " \n", 872 | " \n", 873 | " \n", 874 | " \n", 875 | " \n", 876 | " \n", 877 | " \n", 878 | " \n", 879 | " \n", 880 | " \n", 881 | " \n", 882 | " \n", 883 | " \n", 884 | "
投組權重投組當月報酬率標竿權重標竿當月報酬率配置效果選擇效果交互效果主動報酬
TSE產業名
半導體0.614.930.602.470.011.460.041.52
其他電子業0.12-2.910.04-3.27-0.390.020.03-0.34
電子零組件0.1111.800.037.500.460.120.340.93
電腦及週邊0.071.550.033.480.07-0.05-0.08-0.06
通信網路業0.055.010.020.65-0.030.100.120.19
光電業0.02-3.860.01-0.03-0.02-0.05-0.03-0.10
其他0.020.320.270.300.360.00-0.000.36
合計1.004.221.001.730.471.610.412.49
\n", 885 | "
" 886 | ], 887 | "text/plain": [ 888 | " 投組權重 投組當月報酬率 標竿權重 標竿當月報酬率 配置效果 選擇效果 交互效果 主動報酬\n", 889 | "TSE產業名 \n", 890 | "半導體 0.61 4.93 0.60 2.47 0.01 1.46 0.04 1.52\n", 891 | "其他電子業 0.12 -2.91 0.04 -3.27 -0.39 0.02 0.03 -0.34\n", 892 | "電子零組件 0.11 11.80 0.03 7.50 0.46 0.12 0.34 0.93\n", 893 | "電腦及週邊 0.07 1.55 0.03 3.48 0.07 -0.05 -0.08 -0.06\n", 894 | "通信網路業 0.05 5.01 0.02 0.65 -0.03 0.10 0.12 0.19\n", 895 | "光電業 0.02 -3.86 0.01 -0.03 -0.02 -0.05 -0.03 -0.10\n", 896 | "其他 0.02 0.32 0.27 0.30 0.36 0.00 -0.00 0.36\n", 897 | "合計 1.00 4.22 1.00 1.73 0.47 1.61 0.41 2.49" 898 | ] 899 | }, 900 | "execution_count": 8, 901 | "metadata": {}, 902 | "output_type": "execute_result" 903 | } 904 | ], 905 | "source": [ 906 | "#%% 績效歸因表\n", 907 | "\n", 908 | "table = pd.merge(etf ,benchmark ,how = 'left' , on=['年','月','TSE產業名'])\n", 909 | "table = table[table['月'] == 11]\n", 910 | "table = table.drop(['年','月'], axis=1)\n", 911 | "table = table.set_index(['TSE產業名'])\n", 912 | "table = table.sort_values(by=['投組權重'], ascending=False)\n", 913 | "table = table.fillna(0)\n", 914 | "\n", 915 | "\n", 916 | "table['配置效果'] = (table['投組權重'] - table['標竿權重']) * \\\n", 917 | " (table['標竿當月報酬率'] - sum(table[:7]['標竿權重'] * table[:7]['標竿當月報酬率']))\n", 918 | "\n", 919 | "table['選擇效果'] = table['標竿權重'] * (table['投組當月報酬率'] - table['標竿當月報酬率'])\n", 920 | "table['交互效果'] = (table['投組權重'] - table['標竿權重']) * (table['投組當月報酬率'] - table['標竿當月報酬率'])\n", 921 | "table['主動報酬'] = table['配置效果'] + table['選擇效果'] + table['交互效果']\n", 922 | "\n", 923 | "table.loc['合計',:] = table.sum(axis=0)\n", 924 | "table.loc['合計','投組當月報酬率'] = sum(table[:7]['投組權重'] * table[:7]['投組當月報酬率'])\n", 925 | "table.loc['合計','標竿當月報酬率'] = sum(table[:7]['標竿權重'] * table[:7]['標竿當月報酬率'])\n", 926 | "\n", 927 | "table = table.round(2)\n", 928 | "table" 929 | ] 930 | }, 931 | { 932 | "cell_type": "code", 933 | "execution_count": null, 934 | "id": "2ff9e6dc-8bf7-4f83-a403-bb171d6250d9", 935 | "metadata": {}, 936 | "outputs": [], 937 | "source": [ 938 | "#%% 製作三個月雷達圖\n", 939 | "\n", 940 | "fig = go.Figure()\n", 941 | "date = 10\n", 942 | "for date in etf['月'].unique():\n", 943 | " table = pd.merge(etf ,benchmark ,how = 'left' , on=['年','月','TSE產業名'])\n", 944 | " table = table[table['月'] == date]\n", 945 | " table = table.drop(['年','月'], axis=1)\n", 946 | " table = table.set_index(['TSE產業名'])\n", 947 | " table = table.sort_values(by=['投組權重'], ascending=False)\n", 948 | " table = table.fillna(0)\n", 949 | " \n", 950 | " table['配置效果'] = (table['投組權重'] - table['標竿權重']) * \\\n", 951 | " (table['標竿當月報酬率'] - sum(table[:7]['標竿權重'] * table[:7]['標竿當月報酬率']))\n", 952 | " \n", 953 | " table['選擇效果'] = table['標竿權重'] * (table['投組當月報酬率'] - table['標竿當月報酬率'])\n", 954 | " table['交互效果'] = (table['投組權重'] - table['標竿權重']) * (table['投組當月報酬率'] - table['標竿當月報酬率'])\n", 955 | " table['主動報酬'] = table['配置效果'] + table['選擇效果'] + table['交互效果']\n", 956 | " \n", 957 | " table.loc['合計',:] = table.sum(axis=0)\n", 958 | " table.loc['合計','投組當月報酬率'] = sum(table[:7]['投組權重'] * table[:7]['投組當月報酬率'])\n", 959 | " table.loc['合計','標竿當月報酬率'] = sum(table[:7]['標竿權重'] * table[:7]['標竿當月報酬率'])\n", 960 | " \n", 961 | " table = table.round(2)\n", 962 | " table.index = ['半導體', '其他電子業', '電子零組件', '電腦及週邊', '通信網路業', '光電業','其他','合計']\n", 963 | " \n", 964 | " fig.add_trace(go.Scatterpolar(r= table.drop(['合計']).loc[:,'主動報酬'],\n", 965 | " theta= table.drop(['合計']).index,\n", 966 | " fill='toself',\n", 967 | " name=str(date) + '月'))\n", 968 | "\n", 969 | "fig.show()" 970 | ] 971 | } 972 | ], 973 | "metadata": { 974 | "kernelspec": { 975 | "display_name": "Python 3", 976 | "language": "python", 977 | "name": "python3" 978 | }, 979 | "language_info": { 980 | "codemirror_mode": { 981 | "name": "ipython", 982 | "version": 3 983 | }, 984 | "file_extension": ".py", 985 | "mimetype": "text/x-python", 986 | "name": "python", 987 | "nbconvert_exporter": "python", 988 | "pygments_lexer": "ipython3", 989 | "version": "3.8.8" 990 | } 991 | }, 992 | "nbformat": 4, 993 | "nbformat_minor": 5 994 | } 995 | -------------------------------------------------------------------------------- /TEJAPI_Python_PCA.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # In[164]: 5 | 6 | 7 | #找出0050現存成分股 8 | import tejapi 9 | import pandas as pd 10 | 11 | tejapi.ApiConfig.api_key = "Your Key" 12 | tejapi.ApiConfig.ignoretz = True 13 | 14 | mdate = {'gte':'2000-01-01', 'lte':'2022-11-24'} 15 | data = tejapi.get('TWN/EWISAMPLE', 16 | idx_id = "IX0002", 17 | start_date = mdate, 18 | paginate=True) 19 | 20 | data1 = data[data["end_date"] < "2022-11-24"] 21 | diff_data = pd.concat([data,data1,data1]).drop_duplicates(keep=False) 22 | coid = list(diff_data["coid"]) 23 | print(len(coid)) 24 | diff_data 25 | 26 | 27 | # In[166]: 28 | 29 | 30 | #下載期間報酬率並合併 31 | for i in range(0,len(coid)): 32 | print(i) 33 | if i == 0: 34 | df = tejapi.get('TWN/EWPRCD2', 35 | coid = coid[i], 36 | mdate = {'gte':'2013-01-01', 'lte':'2022-11-24'}, 37 | paginate=True) 38 | df.set_index(df["mdate"],inplace=True) 39 | Df = pd.DataFrame({coid[i]:df["roia"]}) 40 | else: 41 | df = tejapi.get('TWN/EWPRCD2', 42 | coid = coid[i], 43 | mdate = {'gte':'2013-01-01', 'lte':'2022-11-24'}, 44 | paginate=True) 45 | df.set_index(df["mdate"],inplace=True) 46 | Df1 = pd.DataFrame({coid[i]:df["roia"]}) 47 | Df = pd.merge(Df,Df1[coid[i]],how='left', left_index=True, right_index=True) 48 | 49 | 50 | # In[169]: 51 | 52 | 53 | #剔除3711、5876、6415 54 | del Df["3711"] 55 | del Df["5876"] 56 | del Df["6415"] 57 | Df 58 | 59 | 60 | # In[170]: 61 | 62 | 63 | #畫圖套件 64 | import numpy as np 65 | import matplotlib.pyplot as plt 66 | import seaborn as sns 67 | from sklearn.preprocessing import StandardScaler 68 | 69 | 70 | # In[172]: 71 | 72 | 73 | #資料視覺化 74 | cor = Df.corr() 75 | plt.figure(figsize=(30,30)) 76 | plt.title("Correlation Matrix") 77 | sns.heatmap(cor, vmax=1,square=True,annot=True,cmap="cubehelix") 78 | 79 | 80 | # In[174]: 81 | 82 | 83 | #標準化 84 | scale = StandardScaler().fit(Df) 85 | rescale = pd.DataFrame(scale.fit_transform(Df),columns=Df.columns,index=Df.index) 86 | #標準化視覺化 87 | plt.figure(figsize=(20,5)) 88 | plt.title("2330_Return") 89 | rescale["2330"].plot() 90 | plt.grid=True 91 | plt.legend() 92 | plt.show() 93 | 94 | 95 | # In[300]: 96 | 97 | 98 | X_train = rescale.copy() 99 | 100 | 101 | # In[301]: 102 | 103 | 104 | #主成分分析 105 | #套件 106 | from sklearn.decomposition import PCA 107 | #主成分個數 108 | n_components = 10 109 | pca = PCA(n_components=n_components) 110 | Pc = pca.fit(X_train) 111 | 112 | 113 | # In[305]: 114 | 115 | 116 | #PCA解釋變數 117 | fig, axes = plt.subplots(ncols=2) 118 | Series1 = pd.Series(Pc.explained_variance_ratio_[:n_components ]).sort_values() 119 | Series2 = pd.Series(Pc.explained_variance_ratio_[:n_components ]).cumsum() 120 | 121 | Series1.plot.barh(title="Explained Variance",ax=axes[0]) 122 | Series2.plot(ylim=(0,1),ax=axes[1],title="Cumulative Explained Variance") 123 | print("變數累積解釋比例:") 124 | print(Series2[len(Series2)-1:len(Series2)].values[0]) 125 | print("各變數解釋比例:") 126 | print(Series1.sort_values(ascending=False)) 127 | 128 | 129 | # In[306]: 130 | 131 | 132 | #檢視投組權重 133 | n_components = 10 134 | weights = pd.DataFrame() 135 | for i in range(n_components): 136 | weights["weights_{}".format(i)] = pca.components_[i] / sum(pca.components_[i]) 137 | weights = weights.values.T 138 | weight_port = pd.DataFrame(weights,columns=Df.columns) 139 | weight_port.index = [f'Portfolio{i}' for i in range(weight_port.shape[0])] 140 | weight_port 141 | 142 | 143 | # In[308]: 144 | 145 | 146 | pca.components_[0] 147 | 148 | 149 | # In[232]: 150 | 151 | 152 | #權重合為1 153 | weight_port.sum(axis = 1) 154 | 155 | 156 | # In[234]: 157 | 158 | 159 | #畫權重圖top5 160 | weight_port[:5].T.plot.bar(subplots=True,layout = (int(5),1),figsize=(20,25), 161 | legend=False,sharey=True,ylim=(-2,2)) 162 | 163 | 164 | # In[309]: 165 | 166 | 167 | weight_port.iloc[0].T.sort_values(ascending=False).plot.bar(subplots=True,figsize=(20,5), 168 | legend=False,sharey=True,ylim=(-0.75,0.75)) 169 | 170 | 171 | # In[310]: 172 | 173 | 174 | #尋找最佳特徵投組(由Sharpe Ratio決定) 175 | def sharpe_ratio(ts_returns): 176 | ts_returns = ts_returns 177 | days = ts_returns.shape[0] 178 | n_years = days/252 179 | if ts_returns.cumsum()[-1] < 0: 180 | annualized_return = (np.power(1+abs(ts_returns.cumsum()[-1])*0.01,1/n_years)-1)*(-1) 181 | else: 182 | annualized_return = np.power(1+abs(ts_returns.cumsum()[-1])*0.01,1/n_years)-1 183 | annualized_vol = (ts_returns*0.01).std()*np.sqrt(252) 184 | annualized_sharpe = annualized_return / annualized_vol 185 | 186 | return annualized_return,annualized_vol,annualized_sharpe 187 | 188 | 189 | # In[311]: 190 | 191 | 192 | n_components = 10 193 | annualized_ret = np.array([0.]*n_components) 194 | sharpe_metric = np.array([0.]*n_components) 195 | annualized_vol = np.array([0.]*n_components) 196 | coids = X_train.columns.values 197 | n_coids = len(coids) 198 | 199 | 200 | # In[312]: 201 | 202 | 203 | #主成分分析 204 | pca = PCA(n_components=n_components) 205 | Pc = pca.fit(X_train) 206 | pcs = pca.components_ 207 | for i in range(n_components): 208 | pc_w = pcs[i] / sum(pcs[i]) 209 | eigen_port = pd.DataFrame(data={"weights":pc_w.squeeze()},index=coids) 210 | eigen_port.sort_values(by=["weights"],ascending=False,inplace=True) 211 | #權重與每天報酬內積,得出每日投資組合報酬 212 | eigen_port_returns = np.dot(X_train.loc[:,eigen_port.index],eigen_port["weights"]) 213 | eigen_port_returns = pd.Series(eigen_port_returns.squeeze(), 214 | index = X_train.index) 215 | 216 | ar,vol,sharpe = sharpe_ratio(eigen_port_returns) 217 | 218 | annualized_ret[i] = ar 219 | annualized_vol[i] = vol 220 | sharpe_metric[i] = sharpe 221 | 222 | sharpe_metric = np.nan_to_num(sharpe_metric) 223 | 224 | 225 | # In[313]: 226 | 227 | 228 | #Result of top 5 229 | N=5 230 | result = pd.DataFrame({"Annual Return":annualized_ret,"Vol":annualized_vol,"Sharpe":sharpe_metric}) 231 | result.dropna(inplace=True) 232 | #Sharpe Ratio of PCA portfolio 233 | ax = result[:N]["Sharpe"].plot(linewidth=3,xticks=range(0,N,1)) 234 | ax.set_ylabel("Sharpe") 235 | result.sort_values(by=["Sharpe"],ascending=False,inplace=True) 236 | print(result[:N]) 237 | 238 | 239 | # In[155]: 240 | 241 | 242 | X_train 243 | 244 | 245 | # In[156]: 246 | 247 | 248 | #0050 249 | import tejapi 250 | import pandas as pd 251 | 252 | tejapi.ApiConfig.api_key = "Your Key" 253 | tejapi.ApiConfig.ignoretz = True 254 | market = tejapi.get('TWN/EWPRCD2', 255 | coid = "0050", 256 | mdate = {'gte':'2013-12-12', 'lte':'2020-08-31'}, 257 | paginate=True) 258 | market.set_index(market["mdate"],inplace=True) 259 | market = pd.DataFrame({"0050":market["roia"]}) 260 | 261 | m_ar,m_vol,m_sharpe = sharpe_ratio(market["0050"]) 262 | print(m_ar,m_vol,m_sharpe) 263 | market["0050"].cumsum().plot(rot=45) 264 | 265 | 266 | # In[323]: 267 | 268 | 269 | #回測 270 | def Backtest(i,data): 271 | pca = PCA() 272 | Pc = pca.fit(data) 273 | pcs = pca.components_ 274 | pc_w = pcs[i] / sum(pcs[i]) 275 | eigen_port = pd.DataFrame(data={"weights":pc_w.squeeze()},index=coids) 276 | eigen_port.sort_values(by=["weights"],ascending=False,inplace=True) 277 | #權重與每天報酬取內積得出每日投資組合報酬 278 | eigen_port_returns = np.dot(data.loc[:,eigen_port.index],eigen_port["weights"]) 279 | eigen_port_returns = pd.Series(eigen_port_returns.squeeze(), 280 | index = data.index) 281 | 282 | ar,vol,sharpe = sharpe_ratio(eigen_port_returns) 283 | return eigen_port_returns,ar,vol,sharpe 284 | 285 | 286 | # In[322]: 287 | 288 | 289 | #畫出投資組合權重 290 | def Weight_plot(i): 291 | top_port = weight_port.iloc[[i]].T 292 | port_name = top_port.columns.values.tolist() 293 | top_port.sort_values(by=port_name,ascending=False,inplace=True) 294 | ax = top_port.plot(title = port_name[0],xticks=range(0,len(coids),1), 295 | figsize=(15,6), 296 | rot=45,linewidth=3) 297 | ax.set_ylabel("Portfolio Weight") 298 | 299 | 300 | # In[324]: 301 | 302 | 303 | #Backtest 304 | portfolio = 0 305 | train_returns,train_ar,train_vol,train_sharpe = Backtest(portfolio,X_train) 306 | ax = train_returns.cumsum().plot(rot=45) 307 | ax.set_ylabel("Accumulated Return(%)") 308 | Weight_plot(portfolio) 309 | 310 | 311 | # In[318]: 312 | 313 | 314 | #Test_Backtest 315 | portfolio = 2 316 | test_returns,test_ar,test_vol,test_sharpe = Backtest(portfolio,X_test) 317 | print(test_ar,test_vol,test_sharpe) 318 | ax = test_returns.cumsum().plot(rot=45) 319 | ax.set_ylabel("Accumulated Return(%)") 320 | Weight_plot(portfolio) 321 | 322 | -------------------------------------------------------------------------------- /TEJAPI_Python_Seeking_Alpha.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import pandas as pd\n", 10 | "import numpy as np\n", 11 | "import tejapi\n", 12 | "import statsmodels.api as sm\n", 13 | "import matplotlib.pyplot as plt\n", 14 | "import matplotlib.transforms as transforms\n", 15 | "plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # 解決MAC電腦 plot中文問題\n", 16 | "plt.rcParams['axes.unicode_minus'] = False\n", 17 | "tejapi.ApiConfig.api_key =\"Your Key\"\n", 18 | "tejapi.ApiConfig.ignoretz = True" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 3, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "data=tejapi.get('TWN/ANPRCSTD' ,chinese_column_name=True )\n", 28 | "select=data[\"上市別\"].unique()\n", 29 | "select=select[1:3]\n", 30 | "condition =(data[\"上市別\"].isin(select)) & ( data[\"證券種類名稱\"]==\"普通股\" )\n", 31 | "data=data[condition]\n", 32 | "twid=data[\"證券碼\"].to_list() #取得上市櫃股票證券碼" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": null, 38 | "metadata": {}, 39 | "outputs": [], 40 | "source": [ 41 | "df = pd.DataFrame()\n", 42 | "for i in twid: #資料筆數超過100萬筆,透過迴圈方式抓取\n", 43 | " df = pd.concat([df, tejapi.get('TWN/APRCD1', #從TEJ api撈取所需要的資料\n", 44 | " chinese_column_name = True,\n", 45 | " paginate = True,\n", 46 | " mdate = {'gt':'2013-12-31', 'lt':'2022-07-01'},\n", 47 | " coid=i,\n", 48 | " opts={'columns':['coid','mdate', 'close_adj' ,'roi' ,'mv', \"pbr_tej\"]})])" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": 4, 54 | "metadata": {}, 55 | "outputs": [], 56 | "source": [ 57 | "# df = pd.read_csv('alpha.csv', sep=',', encoding='big5')" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": 5, 63 | "metadata": {}, 64 | "outputs": [], 65 | "source": [ 66 | "# df = df.iloc[:,1:]" 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": 6, 72 | "metadata": {}, 73 | "outputs": [], 74 | "source": [ 75 | "# df['年月日'] = df['年月日'].map(lambda x: x[:10])" 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": 7, 81 | "metadata": {}, 82 | "outputs": [], 83 | "source": [ 84 | "# df['年月日'] = pd.to_datetime(df['年月日'])" 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": 8, 90 | "metadata": {}, 91 | "outputs": [], 92 | "source": [ 93 | "df['帳面市值比'] = 1/df['股價淨值比-TEJ']" 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": 10, 99 | "metadata": {}, 100 | "outputs": [], 101 | "source": [ 102 | "ME = df.groupby('年月日')['市值(百萬元)'].apply(lambda x: x.median())\n", 103 | "ME.name = '市值_中位數'\n", 104 | "df = df.merge(ME, on='年月日')\n", 105 | "df['市值matrix'] = np.where(df['市值(百萬元)']>df['市值_中位數'], 'B', 'S')" 106 | ] 107 | }, 108 | { 109 | "cell_type": "code", 110 | "execution_count": 13, 111 | "metadata": {}, 112 | "outputs": [], 113 | "source": [ 114 | "df1 = (df.groupby(['年月日','市值matrix'])['市值(百萬元)'].sum()).reset_index()\n", 115 | "df = df.merge(df1, on=['年月日','市值matrix'])\n", 116 | "df['weight'] = df['市值(百萬元)_x']/df['市值(百萬元)_y']\n", 117 | "df.groupby(['年月日','市值matrix'])['weight'].sum()" 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": 16, 123 | "metadata": {}, 124 | "outputs": [ 125 | { 126 | "data": { 127 | "text/plain": [ 128 | "年月日 市值matrix\n", 129 | "2014-01-02 B 1.0\n", 130 | " S 1.0\n", 131 | "2014-01-03 B 1.0\n", 132 | " S 1.0\n", 133 | "2014-01-06 B 1.0\n", 134 | " ... \n", 135 | "2022-07-27 S 1.0\n", 136 | "2022-07-28 B 1.0\n", 137 | " S 1.0\n", 138 | "2022-07-29 B 1.0\n", 139 | " S 1.0\n", 140 | "Name: weight, Length: 4196, dtype: float64" 141 | ] 142 | }, 143 | "execution_count": 16, 144 | "metadata": {}, 145 | "output_type": "execute_result" 146 | } 147 | ], 148 | "source": [ 149 | "df.groupby(['年月日','市值matrix'])['weight'].sum()" 150 | ] 151 | }, 152 | { 153 | "cell_type": "code", 154 | "execution_count": 37, 155 | "metadata": {}, 156 | "outputs": [], 157 | "source": [ 158 | "df['return1'] = df['報酬率%']* df['weight']\n", 159 | "SMB = df.groupby(['年月日','市值matrix'])['return1'].sum()\n", 160 | "SMB.reset_index(inplace=True)\n", 161 | "SMB.set_index('年月日',drop=True, inplace=True)\n", 162 | "SMB = SMB[SMB['市值matrix']=='S']['return1'] - SMB[SMB['市值matrix']=='B']['return1']\n", 163 | "SMB.name = 'SMB'\n", 164 | "SMB" 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": 42, 170 | "metadata": {}, 171 | "outputs": [ 172 | { 173 | "data": { 174 | "text/plain": [ 175 | "年月日\n", 176 | "2014-01-02 0.727428\n", 177 | "2014-01-03 1.214870\n", 178 | "2014-01-06 0.623517\n", 179 | "2014-01-07 0.642468\n", 180 | "2014-01-08 0.241418\n", 181 | " ... \n", 182 | "2022-07-25 0.322174\n", 183 | "2022-07-26 0.053480\n", 184 | "2022-07-27 -0.290234\n", 185 | "2022-07-28 -0.106124\n", 186 | "2022-07-29 0.065884\n", 187 | "Name: SMB, Length: 2098, dtype: float64" 188 | ] 189 | }, 190 | "execution_count": 42, 191 | "metadata": {}, 192 | "output_type": "execute_result" 193 | } 194 | ], 195 | "source": [ 196 | "SMB" 197 | ] 198 | }, 199 | { 200 | "cell_type": "code", 201 | "execution_count": 24, 202 | "metadata": {}, 203 | "outputs": [], 204 | "source": [ 205 | "a = df.groupby('年月日')['帳面市值比'].quantile(0.7)\n", 206 | "a.name = 'BM_0.7'\n", 207 | "b = df.groupby('年月日')['帳面市值比'].quantile(0.3)\n", 208 | "b.name = 'BM_0.3'\n", 209 | "df = df.merge(a, on='年月日')\n", 210 | "df = df.merge(b, on='年月日')\n", 211 | "df['BM_matrix'] = np.where(df['帳面市值比']>df['BM_0.7'], 'V', (np.where(df['帳面市值比']\n", 282 | "\n", 295 | "\n", 296 | " \n", 297 | " \n", 298 | " \n", 299 | " \n", 300 | " \n", 301 | " \n", 302 | " \n", 303 | " \n", 304 | " \n", 305 | " \n", 306 | " \n", 307 | " \n", 308 | " \n", 309 | " \n", 310 | " \n", 311 | " \n", 312 | " \n", 313 | " \n", 314 | " \n", 315 | " \n", 316 | " \n", 317 | " \n", 318 | " \n", 319 | " \n", 320 | " \n", 321 | " \n", 322 | " \n", 323 | " \n", 324 | " \n", 325 | " \n", 326 | " \n", 327 | " \n", 328 | " \n", 329 | " \n", 330 | " \n", 331 | " \n", 332 | " \n", 333 | " \n", 334 | " \n", 335 | " \n", 336 | " \n", 337 | " \n", 338 | " \n", 339 | " \n", 340 | " \n", 341 | " \n", 342 | " \n", 343 | " \n", 344 | " \n", 345 | " \n", 346 | " \n", 347 | " \n", 348 | " \n", 349 | " \n", 350 | " \n", 351 | " \n", 352 | " \n", 353 | " \n", 354 | " \n", 355 | " \n", 356 | " \n", 357 | " \n", 358 | " \n", 359 | " \n", 360 | " \n", 361 | " \n", 362 | " \n", 363 | " \n", 364 | " \n", 365 | "
SMBHML
年月日
2014-01-020.7274280.423522
2014-01-031.2148700.678727
2014-01-060.6235170.075185
2014-01-070.6424680.697764
2014-01-080.2414180.260657
.........
2022-07-250.3221740.767774
2022-07-260.0534800.787060
2022-07-27-0.290234-1.005587
2022-07-28-0.1061240.418247
2022-07-290.065884-0.549442
\n", 366 | "

2098 rows × 2 columns

\n", 367 | "" 368 | ], 369 | "text/plain": [ 370 | " SMB HML\n", 371 | "年月日 \n", 372 | "2014-01-02 0.727428 0.423522\n", 373 | "2014-01-03 1.214870 0.678727\n", 374 | "2014-01-06 0.623517 0.075185\n", 375 | "2014-01-07 0.642468 0.697764\n", 376 | "2014-01-08 0.241418 0.260657\n", 377 | "... ... ...\n", 378 | "2022-07-25 0.322174 0.767774\n", 379 | "2022-07-26 0.053480 0.787060\n", 380 | "2022-07-27 -0.290234 -1.005587\n", 381 | "2022-07-28 -0.106124 0.418247\n", 382 | "2022-07-29 0.065884 -0.549442\n", 383 | "\n", 384 | "[2098 rows x 2 columns]" 385 | ] 386 | }, 387 | "execution_count": 535, 388 | "metadata": {}, 389 | "output_type": "execute_result" 390 | } 391 | ], 392 | "source": [ 393 | "fama = pd.concat([SMB,HML], axis=1)\n", 394 | "fama" 395 | ] 396 | }, 397 | { 398 | "cell_type": "code", 399 | "execution_count": 79, 400 | "metadata": {}, 401 | "outputs": [], 402 | "source": [ 403 | "Y9999 = tejapi.get('TWN/APRCD1', #從TEJ api撈取所需要的資料\n", 404 | " chinese_column_name = True,\n", 405 | " paginate = True,\n", 406 | " mdate = {'gt':'2013-12-31', 'lt':'2022-07-01'},\n", 407 | " coid='Y9999',\n", 408 | " opts={'columns':['coid','mdate', 'roi']})" 409 | ] 410 | }, 411 | { 412 | "cell_type": "code", 413 | "execution_count": 81, 414 | "metadata": {}, 415 | "outputs": [], 416 | "source": [ 417 | "fama = fama.merge(Y9999[['年月日','報酬率%']], on='年月日')\n", 418 | "fama.rename(columns = {'報酬率%':'rm'}, inplace=True)\n", 419 | "fama.set_index('年月日',drop=True,inplace=True)\n", 420 | "fama" 421 | ] 422 | }, 423 | { 424 | "cell_type": "code", 425 | "execution_count": 157, 426 | "metadata": {}, 427 | "outputs": [], 428 | "source": [ 429 | "fama = fama.loc[:'2022-06-30']" 430 | ] 431 | }, 432 | { 433 | "cell_type": "code", 434 | "execution_count": 161, 435 | "metadata": {}, 436 | "outputs": [ 437 | { 438 | "data": { 439 | "text/html": [ 440 | "
\n", 441 | "\n", 454 | "\n", 455 | " \n", 456 | " \n", 457 | " \n", 458 | " \n", 459 | " \n", 460 | " \n", 461 | " \n", 462 | " \n", 463 | " \n", 464 | " \n", 465 | " \n", 466 | " \n", 467 | " \n", 468 | " \n", 469 | " \n", 470 | " \n", 471 | " \n", 472 | " \n", 473 | " \n", 474 | " \n", 475 | " \n", 476 | " \n", 477 | " \n", 478 | " \n", 479 | " \n", 480 | " \n", 481 | " \n", 482 | " \n", 483 | " \n", 484 | " \n", 485 | " \n", 486 | " \n", 487 | " \n", 488 | " \n", 489 | " \n", 490 | " \n", 491 | " \n", 492 | " \n", 493 | " \n", 494 | " \n", 495 | " \n", 496 | " \n", 497 | " \n", 498 | " \n", 499 | " \n", 500 | " \n", 501 | " \n", 502 | " \n", 503 | " \n", 504 | " \n", 505 | " \n", 506 | " \n", 507 | " \n", 508 | " \n", 509 | " \n", 510 | " \n", 511 | " \n", 512 | " \n", 513 | " \n", 514 | " \n", 515 | " \n", 516 | " \n", 517 | " \n", 518 | " \n", 519 | " \n", 520 | " \n", 521 | " \n", 522 | " \n", 523 | " \n", 524 | " \n", 525 | " \n", 526 | " \n", 527 | " \n", 528 | " \n", 529 | " \n", 530 | " \n", 531 | " \n", 532 | " \n", 533 | " \n", 534 | " \n", 535 | " \n", 536 | " \n", 537 | "
SMBHMLrm
年月日
2014-01-020.7274280.4235220.0120
2014-01-031.2148700.678727-0.7663
2014-01-060.6235170.075185-0.5444
2014-01-070.6424680.6977640.1446
2014-01-080.2414180.2606570.5135
............
2022-06-240.0674930.7379710.8360
2022-06-270.029549-0.9918831.5989
2022-06-28-0.0215010.649327-0.6952
2022-06-290.699726-0.251527-1.2940
2022-06-300.2833220.301092-2.7191
\n", 538 | "

2077 rows × 3 columns

\n", 539 | "
" 540 | ], 541 | "text/plain": [ 542 | " SMB HML rm\n", 543 | "年月日 \n", 544 | "2014-01-02 0.727428 0.423522 0.0120\n", 545 | "2014-01-03 1.214870 0.678727 -0.7663\n", 546 | "2014-01-06 0.623517 0.075185 -0.5444\n", 547 | "2014-01-07 0.642468 0.697764 0.1446\n", 548 | "2014-01-08 0.241418 0.260657 0.5135\n", 549 | "... ... ... ...\n", 550 | "2022-06-24 0.067493 0.737971 0.8360\n", 551 | "2022-06-27 0.029549 -0.991883 1.5989\n", 552 | "2022-06-28 -0.021501 0.649327 -0.6952\n", 553 | "2022-06-29 0.699726 -0.251527 -1.2940\n", 554 | "2022-06-30 0.283322 0.301092 -2.7191\n", 555 | "\n", 556 | "[2077 rows x 3 columns]" 557 | ] 558 | }, 559 | "execution_count": 161, 560 | "metadata": {}, 561 | "output_type": "execute_result" 562 | } 563 | ], 564 | "source": [ 565 | "fama" 566 | ] 567 | }, 568 | { 569 | "cell_type": "code", 570 | "execution_count": 160, 571 | "metadata": {}, 572 | "outputs": [ 573 | { 574 | "data": { 575 | "text/html": [ 576 | "
\n", 577 | "\n", 590 | "\n", 591 | " \n", 592 | " \n", 593 | " \n", 594 | " \n", 595 | " \n", 596 | " \n", 597 | " \n", 598 | " \n", 599 | " \n", 600 | " \n", 601 | " \n", 602 | " \n", 603 | " \n", 604 | " \n", 605 | " \n", 606 | " \n", 607 | " \n", 608 | " \n", 609 | " \n", 610 | " \n", 611 | " \n", 612 | " \n", 613 | " \n", 614 | " \n", 615 | " \n", 616 | " \n", 617 | " \n", 618 | " \n", 619 | " \n", 620 | " \n", 621 | " \n", 622 | " \n", 623 | " \n", 624 | " \n", 625 | " \n", 626 | " \n", 627 | " \n", 628 | " \n", 629 | " \n", 630 | " \n", 631 | " \n", 632 | " \n", 633 | " \n", 634 | " \n", 635 | " \n", 636 | " \n", 637 | " \n", 638 | " \n", 639 | " \n", 640 | " \n", 641 | " \n", 642 | " \n", 643 | " \n", 644 | " \n", 645 | " \n", 646 | " \n", 647 | " \n", 648 | " \n", 649 | " \n", 650 | " \n", 651 | " \n", 652 | " \n", 653 | " \n", 654 | " \n", 655 | " \n", 656 | " \n", 657 | " \n", 658 | " \n", 659 | " \n", 660 | "
證券代碼報酬率%
年月日
2014-01-021101-1.8378
2014-01-021102-1.2953
2014-01-0211040.1770
2014-01-0211101.7143
2014-01-0212030.0000
.........
2022-06-3084409.9836
2022-06-308446-0.2710
2022-06-308928-6.1002
2022-06-3099283.0488
2022-06-309950-9.1912
\n", 661 | "

3113725 rows × 2 columns

\n", 662 | "
" 663 | ], 664 | "text/plain": [ 665 | " 證券代碼 報酬率%\n", 666 | "年月日 \n", 667 | "2014-01-02 1101 -1.8378\n", 668 | "2014-01-02 1102 -1.2953\n", 669 | "2014-01-02 1104 0.1770\n", 670 | "2014-01-02 1110 1.7143\n", 671 | "2014-01-02 1203 0.0000\n", 672 | "... ... ...\n", 673 | "2022-06-30 8440 9.9836\n", 674 | "2022-06-30 8446 -0.2710\n", 675 | "2022-06-30 8928 -6.1002\n", 676 | "2022-06-30 9928 3.0488\n", 677 | "2022-06-30 9950 -9.1912\n", 678 | "\n", 679 | "[3113725 rows x 2 columns]" 680 | ] 681 | }, 682 | "execution_count": 160, 683 | "metadata": {}, 684 | "output_type": "execute_result" 685 | } 686 | ], 687 | "source": [ 688 | "stock = df[['證券代碼', '年月日','報酬率%']]\n", 689 | "stock.set_index('年月日', drop=True, inplace=True)\n", 690 | "stock = stock.loc[:'2022-06-30']\n", 691 | "stock" 692 | ] 693 | }, 694 | { 695 | "cell_type": "code", 696 | "execution_count": 220, 697 | "metadata": {}, 698 | "outputs": [], 699 | "source": [ 700 | "m = pd.date_range('2013-12-31', '2022-07-31', freq='6M').to_list()\n", 701 | "X = sm.add_constant(fama)\n", 702 | "stock_list = stock['證券代碼'].unique()\n" 703 | ] 704 | }, 705 | { 706 | "cell_type": "code", 707 | "execution_count": 302, 708 | "metadata": {}, 709 | "outputs": [], 710 | "source": [ 711 | "b = pd.DataFrame()\n", 712 | "for j in stock_list:\n", 713 | " a=[]\n", 714 | " for i in range(len(m)-1):\n", 715 | " try:\n", 716 | " Y = (stock[stock['證券代碼']== j]).loc[m[i]:m[i+1]]\n", 717 | " result = sm.OLS(Y['報酬率%'], X.loc[m[i]:m[i+1]]).fit()\n", 718 | " a.append(result.params[0])\n", 719 | " except:\n", 720 | " pass\n", 721 | " j = str(j)\n", 722 | " c = pd.DataFrame({'證券代碼':([j]*len(a)), 'alpha':a}, index=m[1:len(a)+1])\n", 723 | " b = pd.concat([b,c])\n", 724 | "b.index.name = '年月日'" 725 | ] 726 | }, 727 | { 728 | "cell_type": "code", 729 | "execution_count": 323, 730 | "metadata": {}, 731 | "outputs": [], 732 | "source": [ 733 | "alpha1 = b.groupby('年月日')['alpha'].apply(lambda x : x.quantile(0.8))\n", 734 | "alpha1.name = 'alpha0.8'\n", 735 | "alpha2 = b.groupby('年月日')['alpha'].apply(lambda x : x.quantile(0.2))\n", 736 | "alpha2.name = 'alpha0.2'\n", 737 | "b = b.merge(alpha1, on='年月日')\n", 738 | "b = b.merge(alpha2, on='年月日')\n", 739 | "long = (b.where(b['alpha'] > b['alpha0.8'])).dropna()\n", 740 | "short = (b.where(b['alpha'] < b['alpha0.2'])).dropna()" 741 | ] 742 | }, 743 | { 744 | "cell_type": "code", 745 | "execution_count": 532, 746 | "metadata": {}, 747 | "outputs": [ 748 | { 749 | "data": { 750 | "text/html": [ 751 | "
\n", 752 | "\n", 765 | "\n", 766 | " \n", 767 | " \n", 768 | " \n", 769 | " \n", 770 | " \n", 771 | " \n", 772 | " \n", 773 | " \n", 774 | " \n", 775 | " \n", 776 | " \n", 777 | " \n", 778 | " \n", 779 | " \n", 780 | " \n", 781 | " \n", 782 | " \n", 783 | " \n", 784 | " \n", 785 | " \n", 786 | " \n", 787 | " \n", 788 | " \n", 789 | " \n", 790 | " \n", 791 | " \n", 792 | " \n", 793 | " \n", 794 | " \n", 795 | " \n", 796 | " \n", 797 | " \n", 798 | " \n", 799 | " \n", 800 | " \n", 801 | " \n", 802 | " \n", 803 | " \n", 804 | " \n", 805 | " \n", 806 | " \n", 807 | " \n", 808 | " \n", 809 | " \n", 810 | " \n", 811 | " \n", 812 | " \n", 813 | " \n", 814 | " \n", 815 | " \n", 816 | " \n", 817 | " \n", 818 | " \n", 819 | " \n", 820 | " \n", 821 | " \n", 822 | " \n", 823 | " \n", 824 | " \n", 825 | " \n", 826 | " \n", 827 | " \n", 828 | " \n", 829 | " \n", 830 | " \n", 831 | " \n", 832 | " \n", 833 | " \n", 834 | " \n", 835 | " \n", 836 | " \n", 837 | " \n", 838 | " \n", 839 | " \n", 840 | " \n", 841 | " \n", 842 | " \n", 843 | " \n", 844 | " \n", 845 | " \n", 846 | " \n", 847 | " \n", 848 | " \n", 849 | " \n", 850 | " \n", 851 | " \n", 852 | " \n", 853 | " \n", 854 | " \n", 855 | " \n", 856 | " \n", 857 | " \n", 858 | " \n", 859 | " \n", 860 | " \n", 861 | "
證券代碼alphaalpha0.2alpha0.8
年月日
2014-06-3012100.216635-0.1422950.141690
2014-06-3015270.168521-0.1422950.141690
2014-06-3015820.237503-0.1422950.141690
2014-06-3015830.585237-0.1422950.141690
2014-06-3017110.192855-0.1422950.141690
...............
2022-06-3084090.490293-0.0876330.134834
2022-06-3084330.220098-0.0876330.134834
2022-06-3089280.279140-0.0876330.134834
2022-06-3089410.141910-0.0876330.134834
2022-06-3099500.295723-0.0876330.134834
\n", 862 | "

5067 rows × 4 columns

\n", 863 | "
" 864 | ], 865 | "text/plain": [ 866 | " 證券代碼 alpha alpha0.2 alpha0.8\n", 867 | "年月日 \n", 868 | "2014-06-30 1210 0.216635 -0.142295 0.141690\n", 869 | "2014-06-30 1527 0.168521 -0.142295 0.141690\n", 870 | "2014-06-30 1582 0.237503 -0.142295 0.141690\n", 871 | "2014-06-30 1583 0.585237 -0.142295 0.141690\n", 872 | "2014-06-30 1711 0.192855 -0.142295 0.141690\n", 873 | "... ... ... ... ...\n", 874 | "2022-06-30 8409 0.490293 -0.087633 0.134834\n", 875 | "2022-06-30 8433 0.220098 -0.087633 0.134834\n", 876 | "2022-06-30 8928 0.279140 -0.087633 0.134834\n", 877 | "2022-06-30 8941 0.141910 -0.087633 0.134834\n", 878 | "2022-06-30 9950 0.295723 -0.087633 0.134834\n", 879 | "\n", 880 | "[5067 rows x 4 columns]" 881 | ] 882 | }, 883 | "execution_count": 532, 884 | "metadata": {}, 885 | "output_type": "execute_result" 886 | } 887 | ], 888 | "source": [ 889 | "long" 890 | ] 891 | }, 892 | { 893 | "cell_type": "code", 894 | "execution_count": 359, 895 | "metadata": {}, 896 | "outputs": [], 897 | "source": [ 898 | "stock1 = df[['證券代碼','年月日','收盤價(元)']]\n", 899 | "stock1.set_index('年月日',drop=True, inplace=True)\n", 900 | "stock1 = stock1.loc[:\"2022-06-30\"]\n", 901 | "stock1['證券代碼'] = stock1['證券代碼'].astype('str')" 902 | ] 903 | }, 904 | { 905 | "cell_type": "code", 906 | "execution_count": 468, 907 | "metadata": {}, 908 | "outputs": [], 909 | "source": [ 910 | "ret = []\n", 911 | "for i in range(1, len(m)-1):\n", 912 | " qq = (stock1.loc[m[i]:m[i+1]])['證券代碼'].isin((long.loc[m[i]])['證券代碼'].tolist())\n", 913 | " a = ((stock1.loc[m[i]:m[i+1]])[qq]).groupby('證券代碼')['收盤價(元)'].tail(1).sum()\n", 914 | " b = ((stock1.loc[m[i]:m[i+1]])[qq]).groupby('證券代碼')['收盤價(元)'].head(1).sum()\n", 915 | " c = len((long.loc[m[i]])['證券代碼'].tolist())\n", 916 | " long_ret = ((a/b)-1)/c\n", 917 | " qq1 = (stock1.loc[m[i]:m[i+1]])['證券代碼'].isin((short.loc[m[i]])['證券代碼'].tolist())\n", 918 | " a1 = ((stock1.loc[m[i]:m[i+1]])[qq1]).groupby('證券代碼')['收盤價(元)'].tail(1).sum()\n", 919 | " b1 = ((stock1.loc[m[i]:m[i+1]])[qq1]).groupby('證券代碼')['收盤價(元)'].head(1).sum()\n", 920 | " c1 = len((short.loc[m[i]])['證券代碼'].tolist())\n", 921 | " short_ret = ((a1/b1)-1)/c1\n", 922 | " ret.append(long_ret - short_ret)" 923 | ] 924 | }, 925 | { 926 | "cell_type": "code", 927 | "execution_count": 524, 928 | "metadata": {}, 929 | "outputs": [], 930 | "source": [ 931 | "ret = pd.DataFrame({'ret':ret}, index=m[2:])" 932 | ] 933 | }, 934 | { 935 | "cell_type": "code", 936 | "execution_count": 479, 937 | "metadata": {}, 938 | "outputs": [], 939 | "source": [ 940 | "y9999 = tejapi.get('TWN/APRCD1', #從TEJ api撈取所需要的資料\n", 941 | " chinese_column_name = True,\n", 942 | " paginate = True,\n", 943 | " mdate = {'gt':'2013-12-31', 'lt':'2022-07-01'},\n", 944 | " coid='Y9999',\n", 945 | " opts={'columns':['coid','mdate', 'close_adj']})\n", 946 | "\n", 947 | "y9999.set_index('年月日' ,drop=True, inplace=True)\n", 948 | "\n", 949 | "a = []\n", 950 | "for i in range(1 , len(m)-1):\n", 951 | " b = (((y9999.loc[m[i]:m[i+1]]).tail(1)['收盤價(元)'].values / (y9999.loc[m[i]:m[i+1]]).head(1)['收盤價(元)'].values) -1)[0]\n", 952 | " a.append(b)\n", 953 | "\n", 954 | "ret['大盤'] = a\n", 955 | "ret[['ret', '大盤']].apply(lambda x :x*100)" 956 | ] 957 | }, 958 | { 959 | "cell_type": "code", 960 | "execution_count": null, 961 | "metadata": {}, 962 | "outputs": [], 963 | "source": [] 964 | } 965 | ], 966 | "metadata": { 967 | "kernelspec": { 968 | "display_name": "base", 969 | "language": "python", 970 | "name": "python3" 971 | }, 972 | "language_info": { 973 | "codemirror_mode": { 974 | "name": "ipython", 975 | "version": 3 976 | }, 977 | "file_extension": ".py", 978 | "mimetype": "text/x-python", 979 | "name": "python", 980 | "nbconvert_exporter": "python", 981 | "pygments_lexer": "ipython3", 982 | "version": "3.9.12 (main, Apr 4 2022, 05:22:27) [MSC v.1916 64 bit (AMD64)]" 983 | }, 984 | "orig_nbformat": 4, 985 | "vscode": { 986 | "interpreter": { 987 | "hash": "3e06028060a7864164bfee2227bae8dfd39c60041c455d9e6b5a8dd3aec9b09f" 988 | } 989 | } 990 | }, 991 | "nbformat": 4, 992 | "nbformat_minor": 2 993 | } 994 | -------------------------------------------------------------------------------- /TQuant Lab 超級趨勢策略_永豐 Shioaji.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "93cc65cb-d9ad-4ae2-944d-d9ce3e3b1a3d", 6 | "metadata": {}, 7 | "source": [ 8 | "# TQuant Lab 超級趨勢策略 - 永豐 Shioaji" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "id": "aea787ac-148e-4a3b-b65e-b2a0db85d898", 14 | "metadata": {}, 15 | "source": [ 16 | "## 超級趨勢策略簡介\n", 17 | "超級趨勢策略以超級趨勢指標以及 ADX 指標構成。超級趨勢指標擁有上軌和下軌,當股價突破上軌,代表價格突破壓力,即將形成上漲趨勢;反之,當股價跌破下軌,代表價格跌破支撐,即將形成下跌趨勢。另一方面,ADX 指標介於 0~100,數值越大代表趨勢越強,幫助我們確立上漲趨勢與下跌趨勢的形成。 \n", 18 | " \n", 19 | "超級趨勢策略的進出場規則如下:\n", 20 | " - Long Entry: 收盤價突破上軌,代表價格突破壓力,即將形成上漲趨勢,買入該股票。\n", 21 | " - Short Entry: 收盤價跌破下軌,加上 ADX > 50,代表價格跌破支撐,形成下跌趨勢,將持股賣出。\n", 22 | "\n", 23 | "詳細的策略說明與 TQuant Lab 的回測流程請參考:[TQuant Lab 超級趨勢策略,低買高賣賺取波段價差](https://www.tejwin.com/insight/tquant-lab-%E8%B6%85%E7%B4%9A%E8%B6%A8%E5%8B%A2%E7%AD%96%E7%95%A5/)" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "id": "c9b23d34-4ab4-4faa-9cd1-cc4f063fc336", 29 | "metadata": {}, 30 | "source": [ 31 | "## 利用 TQuant Lab 取得每日買賣標的" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": 1, 37 | "id": "b621e62e-7e39-4a66-a49a-255cbc8229aa", 38 | "metadata": {}, 39 | "outputs": [], 40 | "source": [ 41 | "import os\n", 42 | "import numpy as np\n", 43 | "import pandas as pd\n", 44 | "\n", 45 | "# tej_key\n", 46 | "tej_key = 'your key'\n", 47 | "api_base = 'https://api.tej.com.tw'\n", 48 | "\n", 49 | "os.environ['TEJAPI_KEY'] = tej_key \n", 50 | "os.environ['TEJAPI_BASE'] = api_base" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": 2, 56 | "id": "2cb7b194-2eaf-4fb0-9e3a-45ef8fb32981", 57 | "metadata": {}, 58 | "outputs": [ 59 | { 60 | "name": "stdout", 61 | "output_type": "stream", 62 | "text": [ 63 | "Currently used TEJ API key call quota 230/100000 (0.23%)\n", 64 | "Currently used TEJ API key data quota 787917/10000000 (7.88%)\n" 65 | ] 66 | } 67 | ], 68 | "source": [ 69 | "from zipline.utils.calendar_utils import get_calendar\n", 70 | "from zipline.sources.TEJ_Api_Data import get_universe\n", 71 | "\n", 72 | "cal = get_calendar('TEJ').all_sessions\n", 73 | "last_date = cal[-2] # 前一個交易日日期\n", 74 | "\n", 75 | "pool = get_universe(start = last_date, \n", 76 | " end = last_date,\n", 77 | " mkt_bd_e = 'TSE', # Listed stock in Taiwan\n", 78 | " stktp_e = 'Common Stock', \n", 79 | " main_ind_c = 'M2300 電子工業' # Electronics Industry\n", 80 | " )" 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": 3, 86 | "id": "d514b758-e8d5-413e-b81e-35c9e3678d98", 87 | "metadata": {}, 88 | "outputs": [ 89 | { 90 | "data": { 91 | "text/plain": [ 92 | "407" 93 | ] 94 | }, 95 | "execution_count": 3, 96 | "metadata": {}, 97 | "output_type": "execute_result" 98 | } 99 | ], 100 | "source": [ 101 | "len(pool)" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": 4, 107 | "id": "1a05c4bb-bb1a-424a-a953-293ad803830b", 108 | "metadata": {}, 109 | "outputs": [ 110 | { 111 | "name": "stdout", 112 | "output_type": "stream", 113 | "text": [ 114 | "Currently used TEJ API key call quota 260/100000 (0.26%)\n", 115 | "Currently used TEJ API key data quota 900607/10000000 (9.01%)\n" 116 | ] 117 | }, 118 | { 119 | "data": { 120 | "text/html": [ 121 | "
\n", 122 | "\n", 135 | "\n", 136 | " \n", 137 | " \n", 138 | " \n", 139 | " \n", 140 | " \n", 141 | " \n", 142 | " \n", 143 | " \n", 144 | " \n", 145 | " \n", 146 | " \n", 147 | " \n", 148 | " \n", 149 | " \n", 150 | " \n", 151 | " \n", 152 | " \n", 153 | " \n", 154 | " \n", 155 | " \n", 156 | " \n", 157 | " \n", 158 | " \n", 159 | " \n", 160 | " \n", 161 | " \n", 162 | " \n", 163 | " \n", 164 | " \n", 165 | " \n", 166 | " \n", 167 | " \n", 168 | " \n", 169 | " \n", 170 | " \n", 171 | " \n", 172 | " \n", 173 | " \n", 174 | " \n", 175 | " \n", 176 | " \n", 177 | " \n", 178 | " \n", 179 | " \n", 180 | " \n", 181 | " \n", 182 | " \n", 183 | " \n", 184 | " \n", 185 | " \n", 186 | " \n", 187 | " \n", 188 | " \n", 189 | " \n", 190 | " \n", 191 | " \n", 192 | " \n", 193 | " \n", 194 | " \n", 195 | " \n", 196 | " \n", 197 | " \n", 198 | " \n", 199 | " \n", 200 | " \n", 201 | " \n", 202 | " \n", 203 | " \n", 204 | " \n", 205 | " \n", 206 | " \n", 207 | " \n", 208 | " \n", 209 | " \n", 210 | " \n", 211 | " \n", 212 | "
coidmdateMarket_Cap_Dollars
014712024-10-251.962910e+09
115822024-10-251.514820e+10
220592024-10-251.195978e+11
323012024-10-252.441140e+11
423022024-10-253.201330e+09
............
40282152024-10-251.032572e+10
40382492024-10-259.068506e+09
40482612024-10-259.226725e+09
40582712024-10-256.603811e+09
40699122024-10-257.050700e+08
\n", 213 | "

407 rows × 3 columns

\n", 214 | "
" 215 | ], 216 | "text/plain": [ 217 | " coid mdate Market_Cap_Dollars\n", 218 | "0 1471 2024-10-25 1.962910e+09\n", 219 | "1 1582 2024-10-25 1.514820e+10\n", 220 | "2 2059 2024-10-25 1.195978e+11\n", 221 | "3 2301 2024-10-25 2.441140e+11\n", 222 | "4 2302 2024-10-25 3.201330e+09\n", 223 | ".. ... ... ...\n", 224 | "402 8215 2024-10-25 1.032572e+10\n", 225 | "403 8249 2024-10-25 9.068506e+09\n", 226 | "404 8261 2024-10-25 9.226725e+09\n", 227 | "405 8271 2024-10-25 6.603811e+09\n", 228 | "406 9912 2024-10-25 7.050700e+08\n", 229 | "\n", 230 | "[407 rows x 3 columns]" 231 | ] 232 | }, 233 | "execution_count": 4, 234 | "metadata": {}, 235 | "output_type": "execute_result" 236 | } 237 | ], 238 | "source": [ 239 | "import TejToolAPI\n", 240 | "\n", 241 | "mktcap_data = TejToolAPI.get_history_data(start = last_date,\n", 242 | " end = last_date,\n", 243 | " ticker = pool,\n", 244 | " columns = ['Market_Cap_Dollars']\n", 245 | " )\n", 246 | "\n", 247 | "mktcap_data" 248 | ] 249 | }, 250 | { 251 | "cell_type": "code", 252 | "execution_count": 5, 253 | "id": "943deaef-f891-4cb4-aa62-6f0f3281e2e6", 254 | "metadata": { 255 | "scrolled": true 256 | }, 257 | "outputs": [ 258 | { 259 | "data": { 260 | "text/plain": [ 261 | "['2330',\n", 262 | " '2317',\n", 263 | " '2454',\n", 264 | " '2382',\n", 265 | " '2308',\n", 266 | " '2412',\n", 267 | " '3711',\n", 268 | " '2303',\n", 269 | " '2357',\n", 270 | " '3045',\n", 271 | " '6669',\n", 272 | " '2345',\n", 273 | " '3231',\n", 274 | " '4904',\n", 275 | " '2327',\n", 276 | " '3008',\n", 277 | " '3034',\n", 278 | " '2395',\n", 279 | " '4938',\n", 280 | " '3017',\n", 281 | " '3037',\n", 282 | " '2379',\n", 283 | " '2301',\n", 284 | " '3653',\n", 285 | " '3533',\n", 286 | " '6409',\n", 287 | " '2376',\n", 288 | " '2360',\n", 289 | " '3443',\n", 290 | " '2356',\n", 291 | " '2474',\n", 292 | " '2449',\n", 293 | " '2324',\n", 294 | " '2377',\n", 295 | " '2383',\n", 296 | " '2408',\n", 297 | " '2409',\n", 298 | " '3481',\n", 299 | " '3702',\n", 300 | " '3036',\n", 301 | " '2353',\n", 302 | " '5269',\n", 303 | " '2385',\n", 304 | " '2347',\n", 305 | " '2059',\n", 306 | " '6526',\n", 307 | " '2354',\n", 308 | " '3044',\n", 309 | " '6239',\n", 310 | " '6176',\n", 311 | " '2368',\n", 312 | " '6789',\n", 313 | " '2344',\n", 314 | " '8046',\n", 315 | " '6770',\n", 316 | " '2313',\n", 317 | " '2352',\n", 318 | " '3005',\n", 319 | " '3035',\n", 320 | " '2388',\n", 321 | " '2404',\n", 322 | " '3023',\n", 323 | " '6285',\n", 324 | " '5434',\n", 325 | " '6805',\n", 326 | " '3706',\n", 327 | " '6139',\n", 328 | " '3189',\n", 329 | " '3406',\n", 330 | " '2492',\n", 331 | " '6412',\n", 332 | " '6531',\n", 333 | " '2337',\n", 334 | " '3532',\n", 335 | " '2458',\n", 336 | " '6515',\n", 337 | " '4915',\n", 338 | " '8070',\n", 339 | " '6414',\n", 340 | " '2451',\n", 341 | " '4919',\n", 342 | " '3042',\n", 343 | " '2312',\n", 344 | " '6257',\n", 345 | " '2498',\n", 346 | " '2363',\n", 347 | " '2362',\n", 348 | " '3413',\n", 349 | " '3583',\n", 350 | " '2393',\n", 351 | " '6442',\n", 352 | " '6214',\n", 353 | " '8112',\n", 354 | " '8210',\n", 355 | " '3376',\n", 356 | " '3714',\n", 357 | " '3596',\n", 358 | " '5388',\n", 359 | " '3030',\n", 360 | " '6691']" 361 | ] 362 | }, 363 | "execution_count": 5, 364 | "metadata": {}, 365 | "output_type": "execute_result" 366 | } 367 | ], 368 | "source": [ 369 | "tickers = mktcap_data.nlargest(100, 'Market_Cap_Dollars')['coid'].tolist()\n", 370 | "tickers" 371 | ] 372 | }, 373 | { 374 | "cell_type": "code", 375 | "execution_count": 6, 376 | "id": "e49f10b8-ffe4-42f5-aa69-fb6e04798a29", 377 | "metadata": {}, 378 | "outputs": [ 379 | { 380 | "name": "stdout", 381 | "output_type": "stream", 382 | "text": [ 383 | "Merging daily equity files:\n", 384 | "Currently used TEJ API key call quota 266/100000 (0.27%)\n", 385 | "Currently used TEJ API key data quota 914122/10000000 (9.14%)\n" 386 | ] 387 | }, 388 | { 389 | "name": "stderr", 390 | "output_type": "stream", 391 | "text": [ 392 | "[2024-10-28 03:45:19.943089] INFO: zipline.data.bundles.core: Ingesting tquant.\n", 393 | "[2024-10-28 03:45:22.401729] INFO: zipline.data.bundles.core: Ingest tquant successfully.\n" 394 | ] 395 | } 396 | ], 397 | "source": [ 398 | "from datetime import timedelta\n", 399 | "\n", 400 | "start = (last_date - timedelta(days=90)).strftime('%Y-%m-%d') # 取3個月(90天)的資料\n", 401 | "end = last_date.strftime('%Y-%m-%d')\n", 402 | "\n", 403 | "os.environ['mdate'] = start + ' ' + end\n", 404 | "os.environ['ticker'] = ' '.join(tickers)\n", 405 | "\n", 406 | "!zipline ingest -b tquant" 407 | ] 408 | }, 409 | { 410 | "cell_type": "code", 411 | "execution_count": 7, 412 | "id": "9a7f3390-046f-423d-a197-690964aec630", 413 | "metadata": {}, 414 | "outputs": [], 415 | "source": [ 416 | "from zipline.pipeline import CustomFactor\n", 417 | "from zipline.pipeline.data import TWEquityPricing\n", 418 | "\n", 419 | "class Supertrend(CustomFactor):\n", 420 | " inputs = [\n", 421 | " TWEquityPricing.high,\n", 422 | " TWEquityPricing.low,\n", 423 | " TWEquityPricing.close,\n", 424 | " ]\n", 425 | " window_length = 50 # Use a 50-day window length\n", 426 | " outputs = ['atr', 'basic_upperband', 'basic_lowerband', 'final_upperband', 'final_lowerband']\n", 427 | "\n", 428 | " def compute(self, today, assets, out, highs, lows, closes):\n", 429 | " # Calculate TR (True Range)\n", 430 | " high_low = highs[1:] - lows[1:]\n", 431 | " high_close = abs(highs[1:] - closes[:-1])\n", 432 | " low_close = abs(lows[1:] - closes[:-1])\n", 433 | " tr = np.maximum.reduce([high_low, high_close, low_close])\n", 434 | "\n", 435 | " # Calculate ATR (Average True Range)\n", 436 | " atr = np.mean(tr, axis=0)\n", 437 | " out.atr = atr\n", 438 | "\n", 439 | " # Calculate basic upperband & lowerband\n", 440 | " hl2 = (highs + lows) / 2\n", 441 | " basic_upperband = hl2 + 4 * atr\n", 442 | " basic_lowerband = hl2 - 4 * atr\n", 443 | "\n", 444 | " # Initialize final upperband & lowerband\n", 445 | " final_upperband = np.zeros_like(basic_upperband)\n", 446 | " final_lowerband = np.zeros_like(basic_lowerband)\n", 447 | "\n", 448 | " # Calculate fianl upperband & lowerband\n", 449 | " for i in range(1, len(closes)):\n", 450 | " final_upperband[i] = np.where(\n", 451 | " (basic_upperband[i] < final_upperband[i-1]) | (closes[i-1] > final_upperband[i-1]),\n", 452 | " basic_upperband[i],\n", 453 | " final_upperband[i-1]\n", 454 | " )\n", 455 | " \n", 456 | " final_lowerband[i] = np.where(\n", 457 | " (basic_lowerband[i] > final_lowerband[i-1]) | (closes[i-1] < final_lowerband[i-1]),\n", 458 | " basic_lowerband[i],\n", 459 | " final_lowerband[i-1]\n", 460 | " )\n", 461 | " \n", 462 | " out.basic_upperband = basic_upperband[-1]\n", 463 | " out.basic_lowerband = basic_lowerband[-1]\n", 464 | " out.final_upperband = final_upperband[-1]\n", 465 | " out.final_lowerband = final_lowerband[-1]" 466 | ] 467 | }, 468 | { 469 | "cell_type": "code", 470 | "execution_count": 8, 471 | "id": "ae6c4acd-4f5d-432d-b5c7-62c2e4ab1a22", 472 | "metadata": {}, 473 | "outputs": [], 474 | "source": [ 475 | "class ADX(CustomFactor):\n", 476 | " inputs = [TWEquityPricing.high, TWEquityPricing.low, TWEquityPricing.close]\n", 477 | " window_length = 14 # ADX通常使用14天作為默認週期\n", 478 | " outputs = ['adx']\n", 479 | " \n", 480 | " def compute(self, today, assets, out, highs, lows, closes):\n", 481 | " # Calculate TR (True Range)\n", 482 | " high_low = highs[1:] - lows[1:]\n", 483 | " high_close = abs(highs[1:] - closes[:-1])\n", 484 | " low_close = abs(lows[1:] - closes[:-1])\n", 485 | " tr = np.maximum.reduce([high_low, high_close, low_close])\n", 486 | "\n", 487 | " # Calculate ATR (Average True Range)\n", 488 | " atr = np.mean(tr, axis=0)\n", 489 | " \n", 490 | " # Calculate +DM, -DM (Directional Movement)\n", 491 | " up_move = highs[1:] - lows[:-1]\n", 492 | " down_move = lows[:-1] - lows[1:]\n", 493 | " plus_dm = np.where((up_move > down_move) & (up_move > 0), up_move, 0)\n", 494 | " minus_dm = np.where((down_move > up_move) & (down_move > 0), down_move, 0)\n", 495 | " \n", 496 | " # Calaulate rolling mean of +DM & -DM (smoothing)\n", 497 | " plus_dm_smooth = np.mean(plus_dm, axis=0)\n", 498 | " minus_dm_smooth = np.mean(minus_dm, axis=0)\n", 499 | " \n", 500 | " # Calculate +DI, -DI (Directional Indicator)\n", 501 | " di_plus = 100 * np.divide(plus_dm_smooth, atr)\n", 502 | " di_minus = 100 * np.divide(minus_dm_smooth, atr)\n", 503 | " \n", 504 | " # Calculate DX\n", 505 | " dx = 100 * np.divide(np.abs(di_plus - di_minus), np.abs(di_plus + di_minus))\n", 506 | " \n", 507 | " # Calculate ADX (rolling mean of DX)\n", 508 | " dx_series = pd.DataFrame(dx)\n", 509 | " adx = dx_series.rolling(window = 50, min_periods = 1).mean().values.flatten()\n", 510 | "\n", 511 | " out.adx = adx" 512 | ] 513 | }, 514 | { 515 | "cell_type": "code", 516 | "execution_count": 9, 517 | "id": "1049f7b3-7f5f-41b8-bdb0-0efac8a83631", 518 | "metadata": {}, 519 | "outputs": [], 520 | "source": [ 521 | "from zipline.pipeline import Pipeline\n", 522 | "from zipline.TQresearch.tej_pipeline import run_pipeline\n", 523 | "from zipline.pipeline.filters import StaticAssets\n", 524 | "\n", 525 | "def make_pipeline():\n", 526 | "\n", 527 | " close = TWEquityPricing.close.latest\n", 528 | " supertrend = Supertrend()\n", 529 | " adx = ADX()\n", 530 | " \n", 531 | " return Pipeline(\n", 532 | " columns={\n", 533 | " 'close': close,\n", 534 | " 'final_upperband': supertrend.final_upperband,\n", 535 | " 'final_lowerband': supertrend.final_lowerband,\n", 536 | " 'ADX': adx.adx\n", 537 | " }\n", 538 | " )" 539 | ] 540 | }, 541 | { 542 | "cell_type": "code", 543 | "execution_count": 10, 544 | "id": "5e8695ca-81f1-4741-a020-defb9d8715f3", 545 | "metadata": {}, 546 | "outputs": [ 547 | { 548 | "data": { 549 | "text/html": [ 550 | "
\n", 551 | "\n", 564 | "\n", 565 | " \n", 566 | " \n", 567 | " \n", 568 | " \n", 569 | " \n", 570 | " \n", 571 | " \n", 572 | " \n", 573 | " \n", 574 | " \n", 575 | " \n", 576 | " \n", 577 | " \n", 578 | " \n", 579 | " \n", 580 | " \n", 581 | " \n", 582 | " \n", 583 | " \n", 584 | " \n", 585 | " \n", 586 | " \n", 587 | " \n", 588 | " \n", 589 | " \n", 590 | " \n", 591 | " \n", 592 | "
closefinal_upperbandfinal_lowerbandADX
2024-10-25 00:00:00+00:00Equity(16 [2354])69.869.01428663.98571479.029377
Equity(73 [4919])94.189.44183778.70816379.987217
\n", 593 | "
" 594 | ], 595 | "text/plain": [ 596 | " close final_upperband \\\n", 597 | "2024-10-25 00:00:00+00:00 Equity(16 [2354]) 69.8 69.014286 \n", 598 | " Equity(73 [4919]) 94.1 89.441837 \n", 599 | "\n", 600 | " final_lowerband ADX \n", 601 | "2024-10-25 00:00:00+00:00 Equity(16 [2354]) 63.985714 79.029377 \n", 602 | " Equity(73 [4919]) 78.708163 79.987217 " 603 | ] 604 | }, 605 | "execution_count": 10, 606 | "metadata": {}, 607 | "output_type": "execute_result" 608 | } 609 | ], 610 | "source": [ 611 | "last_data = run_pipeline(make_pipeline(), last_date, last_date)\n", 612 | "buy_data = last_data[last_data['close'] > last_data['final_upperband']]\n", 613 | "buy_data" 614 | ] 615 | }, 616 | { 617 | "cell_type": "code", 618 | "execution_count": 11, 619 | "id": "854a55a6-7925-4ed6-8e9a-2f87c03df2da", 620 | "metadata": {}, 621 | "outputs": [ 622 | { 623 | "data": { 624 | "text/plain": [ 625 | "['2354', '4919']" 626 | ] 627 | }, 628 | "execution_count": 11, 629 | "metadata": {}, 630 | "output_type": "execute_result" 631 | } 632 | ], 633 | "source": [ 634 | "buy_list = []\n", 635 | "for i in range(len(buy_data)):\n", 636 | " stock = buy_data.index.get_level_values(1)[i].symbol\n", 637 | " buy_list.append(stock)\n", 638 | "\n", 639 | "buy_list" 640 | ] 641 | }, 642 | { 643 | "cell_type": "code", 644 | "execution_count": 12, 645 | "id": "19d00454-737e-4bfb-88fd-d73e5018275c", 646 | "metadata": {}, 647 | "outputs": [ 648 | { 649 | "data": { 650 | "text/html": [ 651 | "
\n", 652 | "\n", 665 | "\n", 666 | " \n", 667 | " \n", 668 | " \n", 669 | " \n", 670 | " \n", 671 | " \n", 672 | " \n", 673 | " \n", 674 | " \n", 675 | " \n", 676 | " \n", 677 | " \n", 678 | " \n", 679 | " \n", 680 | " \n", 681 | " \n", 682 | " \n", 683 | " \n", 684 | " \n", 685 | " \n", 686 | " \n", 687 | " \n", 688 | " \n", 689 | " \n", 690 | " \n", 691 | " \n", 692 | " \n", 693 | "
closefinal_upperbandfinal_lowerbandADX
2024-10-25 00:00:00+00:00Equity(42 [2498])45.8552.91020447.08979680.747982
Equity(93 [6770])19.7021.99387819.83112282.509597
\n", 694 | "
" 695 | ], 696 | "text/plain": [ 697 | " close final_upperband \\\n", 698 | "2024-10-25 00:00:00+00:00 Equity(42 [2498]) 45.85 52.910204 \n", 699 | " Equity(93 [6770]) 19.70 21.993878 \n", 700 | "\n", 701 | " final_lowerband ADX \n", 702 | "2024-10-25 00:00:00+00:00 Equity(42 [2498]) 47.089796 80.747982 \n", 703 | " Equity(93 [6770]) 19.831122 82.509597 " 704 | ] 705 | }, 706 | "execution_count": 12, 707 | "metadata": {}, 708 | "output_type": "execute_result" 709 | } 710 | ], 711 | "source": [ 712 | "sell_data = last_data[(last_data['close'] < last_data['final_lowerband']) & (last_data['ADX'] > 50)]\n", 713 | "sell_data" 714 | ] 715 | }, 716 | { 717 | "cell_type": "code", 718 | "execution_count": 13, 719 | "id": "8a9e0d06-8c05-4249-87f9-4810f9eaee66", 720 | "metadata": {}, 721 | "outputs": [ 722 | { 723 | "data": { 724 | "text/plain": [ 725 | "['2498', '6770']" 726 | ] 727 | }, 728 | "execution_count": 13, 729 | "metadata": {}, 730 | "output_type": "execute_result" 731 | } 732 | ], 733 | "source": [ 734 | "sell_list = []\n", 735 | "for i in range(len(sell_data)):\n", 736 | " stock = sell_data.index.get_level_values(1)[i].symbol\n", 737 | " sell_list.append(stock)\n", 738 | "\n", 739 | "sell_list" 740 | ] 741 | }, 742 | { 743 | "cell_type": "markdown", 744 | "id": "9cb976d3-120d-4bb0-9d26-6db63cdf3720", 745 | "metadata": {}, 746 | "source": [ 747 | "## 串接到永豐 API\n", 748 | "關於永豐 API 的詳細介紹可參考:[Shioaji 技術手冊](https://sinotrade.github.io/zh_TW/)" 749 | ] 750 | }, 751 | { 752 | "cell_type": "code", 753 | "execution_count": null, 754 | "id": "debd0485-df3f-4217-a68f-4b4f15822062", 755 | "metadata": {}, 756 | "outputs": [], 757 | "source": [ 758 | "import shioaji as sj\n", 759 | "\n", 760 | "api = sj.Shioaji(simulation=True) # 模擬模式\n", 761 | "api.login(\n", 762 | " api_key = 'your api_key', \n", 763 | " secret_key = 'your secret_key',\n", 764 | " contracts_cb=lambda security_type: print(f\"{repr(security_type)} fetch done.\") # 獲取商品檔 Callback\n", 765 | ")" 766 | ] 767 | }, 768 | { 769 | "cell_type": "markdown", 770 | "id": "266d425c-3c74-4614-b4a7-a1cecc064ddc", 771 | "metadata": {}, 772 | "source": [ 773 | "### 查看部位資訊\n", 774 | "透過永豐 API 的 `list_positions` 函式,我們可以看到當前持有的部位資訊,包含:股票代碼、持有數量、平均價格、目前股價、損益等。 \n", 775 | "\n", 776 | "p.s. 第一次執行 `list_positions` 函式時,因為我們尚未持有部位,因此會回傳**空的資料表**。" 777 | ] 778 | }, 779 | { 780 | "cell_type": "code", 781 | "execution_count": 15, 782 | "id": "54e25daf-1626-44f9-ad79-9aee443ec461", 783 | "metadata": {}, 784 | "outputs": [ 785 | { 786 | "data": { 787 | "text/html": [ 788 | "
\n", 789 | "\n", 802 | "\n", 803 | " \n", 804 | " \n", 805 | " \n", 806 | " \n", 807 | " \n", 808 | " \n", 809 | " \n", 810 | "
\n", 811 | "
" 812 | ], 813 | "text/plain": [ 814 | "Empty DataFrame\n", 815 | "Columns: []\n", 816 | "Index: []" 817 | ] 818 | }, 819 | "execution_count": 15, 820 | "metadata": {}, 821 | "output_type": "execute_result" 822 | } 823 | ], 824 | "source": [ 825 | "positions = api.list_positions(api.stock_account)\n", 826 | "position_data = pd.DataFrame(s.__dict__ for s in positions)\n", 827 | "position_data" 828 | ] 829 | }, 830 | { 831 | "cell_type": "markdown", 832 | "id": "d9ee5695-d1cc-45a6-8c5b-9d590fa81d47", 833 | "metadata": {}, 834 | "source": [ 835 | "### 取得當前持有標的清單 \n", 836 | "將部位資訊中的 *code* 欄位取出,即可獲得當前持有的股票清單 *hold_list*。" 837 | ] 838 | }, 839 | { 840 | "cell_type": "code", 841 | "execution_count": 16, 842 | "id": "bde4829b-b7a5-482a-a87b-a50ff8ab4f30", 843 | "metadata": {}, 844 | "outputs": [ 845 | { 846 | "data": { 847 | "text/plain": [ 848 | "[]" 849 | ] 850 | }, 851 | "execution_count": 16, 852 | "metadata": {}, 853 | "output_type": "execute_result" 854 | } 855 | ], 856 | "source": [ 857 | "if position_data.empty:\n", 858 | " hold_list = []\n", 859 | "else:\n", 860 | " hold_list = position_data['code'].to_list()\n", 861 | " \n", 862 | "hold_list" 863 | ] 864 | }, 865 | { 866 | "cell_type": "markdown", 867 | "id": "f080e563-6a73-4e56-9dfe-488441f9ae40", 868 | "metadata": {}, 869 | "source": [ 870 | "### 設定委託回報函式\n", 871 | "我們設定委託回報函式 `order_cb`,並將其傳入 `set_order_callback`,在下單完成後將會回傳委託資訊與成交回報訊息。" 872 | ] 873 | }, 874 | { 875 | "cell_type": "code", 876 | "execution_count": 17, 877 | "id": "94e95db1-303b-4d61-beb1-197fae70abd0", 878 | "metadata": {}, 879 | "outputs": [], 880 | "source": [ 881 | "def order_cb(stat, msg):\n", 882 | " print('my_order_callback')\n", 883 | " print(stat, msg)\n", 884 | "\n", 885 | "api.set_order_callback(order_cb)" 886 | ] 887 | }, 888 | { 889 | "cell_type": "markdown", 890 | "id": "8c039536-4cd1-41f3-951f-01098f466dff", 891 | "metadata": {}, 892 | "source": [ 893 | "### 下單買賣\n", 894 | "`place_order` 為永豐 API 的下單函式,下單時我們須提供商品資訊 *contract* 及下單資訊 *order*。*contract* 包含股票代碼,*order* 則包含買進或賣出、價格、張數、限市價單、掛單類型 ( ROD, IOC, FOK )、整股或零股等設定。\n", 895 | "\n", 896 | "本文的買賣條件如下:\n", 897 | " - 若 *buy_list* 的標的不在 *hold_list* 中,則用市價單買入一張股票\n", 898 | " - 若 *sell_list* 的標的在 *hold_list* 中,則用市價單賣出一張股票\n", 899 | "\n", 900 | "p.s. price_type 用 MKP (範圍市價) 可能會有以下的 error,且無法順利成交 \n", 901 | "```python\n", 902 | "2024-09-24 09:52:51.492 | ERROR | shioaji.shioaji:place_order:419 - {'status': {'status_code': 500}, 'response': {'detail': 'Internal Server Error'}}\n", 903 | "```" 904 | ] 905 | }, 906 | { 907 | "cell_type": "code", 908 | "execution_count": 18, 909 | "id": "e3a60547-e7c3-40bf-940e-06a1c0a2e1f1", 910 | "metadata": {}, 911 | "outputs": [ 912 | { 913 | "name": "stdout", 914 | "output_type": "stream", 915 | "text": [ 916 | "processing 2354\n", 917 | "2354 not in hold_list\n", 918 | "Ready to buy!\n", 919 | "====================================================================================================\n", 920 | "processing 4919\n", 921 | "4919 not in hold_list\n", 922 | "Ready to buy!\n", 923 | "my_order_callback\n", 924 | "OrderState.StockOrder {'operation': {'op_type': 'New', 'op_code': '00', 'op_msg': ''}, 'order': {'id': '000401', 'seqno': '000401', 'ordno': '000106', 'account': {'account_type': 'S', 'person_id': '', 'broker_id': '9A95', 'account_id': '3369822', 'signed': True}, 'action': 'Buy', 'price': 0, 'quantity': 1, 'order_cond': 'Cash', 'order_lot': 'Common', 'custom_field': '', 'order_type': 'IOC', 'price_type': 'MKT'}, 'status': {'id': '000401', 'exchange_ts': 1730087221.832041, 'order_quantity': 1, 'modified_price': 0, 'cancel_quantity': 0, 'web_id': '137'}, 'contract': {'security_type': 'STK', 'exchange': 'TSE', 'code': '2354'}}\n", 925 | "my_order_callback\n", 926 | "OrderState.StockOrder {'operation': {'op_type': 'New', 'op_code': '00', 'op_msg': ''}, 'order': {'id': '000402', 'seqno': '000402', 'ordno': '0000EE', 'account': {'account_type': 'S', 'person_id': '', 'broker_id': '9A95', 'account_id': '3369822', 'signed': True}, 'action': 'Buy', 'price': 0, 'quantity': 1, 'order_cond': 'Cash', 'order_lot': 'Common', 'custom_field': '', 'order_type': 'IOC', 'price_type': 'MKT'}, 'status': {'id': '000402', 'exchange_ts': 1730087221.86957, 'order_quantity': 1, 'modified_price': 0, 'cancel_quantity': 0, 'web_id': '137'}, 'contract': {'security_type': 'STK', 'exchange': 'TSE', 'code': '4919'}}\n", 927 | "====================================================================================================\n", 928 | "my_order_callback\n", 929 | "OrderState.StockDeal {'trade_id': '000402', 'seqno': '000402', 'ordno': '0000EE', 'exchange_seq': '000001', 'broker_id': '9A95', 'account_id': '3369822', 'action': 'Buy', 'code': '4919', 'order_cond': 'Cash', 'order_lot': 'Common', 'price': 95.4, 'quantity': 1, 'web_id': '137', 'custom_field': '', 'ts': 1730087222.687482}\n", 930 | "my_order_callback\n", 931 | "OrderState.StockDeal {'trade_id': '000401', 'seqno': '000401', 'ordno': '000106', 'exchange_seq': '000001', 'broker_id': '9A95', 'account_id': '3369822', 'action': 'Buy', 'code': '2354', 'order_cond': 'Cash', 'order_lot': 'Common', 'price': 72.9, 'quantity': 1, 'web_id': '137', 'custom_field': '', 'ts': 1730087229.40197}\n" 932 | ] 933 | } 934 | ], 935 | "source": [ 936 | "for i in buy_list:\n", 937 | " print('processing %s' % i)\n", 938 | " \n", 939 | " if i not in hold_list:\n", 940 | " print('%s not in hold_list' % i)\n", 941 | " print('Ready to buy!')\n", 942 | " \n", 943 | " contract = api.Contracts.Stocks.TSE[i]\n", 944 | "\n", 945 | " order = api.Order(\n", 946 | " action = 'Buy',\n", 947 | " price = 0, # MKT, MKP will not use price parameter\n", 948 | " quantity = 1,\n", 949 | " price_type = 'MKT', # MKT or MKP\n", 950 | " order_type = 'IOC', # ROD, IOC, FOK\n", 951 | " order_lot = 'Common', # Common:整股, Fixing:定盤, Odd:盤後零股, IntradayOdd:盤中零股\n", 952 | " account = api.stock_account\n", 953 | " )\n", 954 | "\n", 955 | " trade = api.place_order(contract, order)\n", 956 | " trade\n", 957 | "\n", 958 | " else:\n", 959 | " print('%s in hold_list' % i)\n", 960 | "\n", 961 | " print('=' * 100)" 962 | ] 963 | }, 964 | { 965 | "cell_type": "code", 966 | "execution_count": 19, 967 | "id": "37fa5021-2a45-46c6-9957-14a0bb70d960", 968 | "metadata": {}, 969 | "outputs": [ 970 | { 971 | "name": "stdout", 972 | "output_type": "stream", 973 | "text": [ 974 | "processing 2498\n", 975 | "2498 not in hold_list\n", 976 | "====================================================================================================\n", 977 | "processing 6770\n", 978 | "6770 not in hold_list\n", 979 | "====================================================================================================\n" 980 | ] 981 | } 982 | ], 983 | "source": [ 984 | "for i in sell_list:\n", 985 | " print('processing %s' % i)\n", 986 | " \n", 987 | " if i in hold_list:\n", 988 | " print('%s in hold_list' % i)\n", 989 | " print('Ready to sell!')\n", 990 | " \n", 991 | " contract = api.Contracts.Stocks.TSE[i]\n", 992 | "\n", 993 | " order = api.Order(\n", 994 | " action = 'Sell',\n", 995 | " price = 0, # MKT, MKP will not use price parameter\n", 996 | " quantity = 1,\n", 997 | " price_type = 'MKT', # MKT or MKP\n", 998 | " order_type = 'IOC', # ROD, IOC, FOK\n", 999 | " order_lot = 'Common', # Common:整股, Fixing:定盤, Odd:盤後零股, IntradayOdd:盤中零股\n", 1000 | " account = api.stock_account\n", 1001 | " )\n", 1002 | "\n", 1003 | " trade = api.place_order(contract, order)\n", 1004 | " trade\n", 1005 | "\n", 1006 | " else:\n", 1007 | " print('%s not in hold_list' % i)\n", 1008 | "\n", 1009 | " print('=' * 100)" 1010 | ] 1011 | }, 1012 | { 1013 | "cell_type": "code", 1014 | "execution_count": 20, 1015 | "id": "71d58839-3b74-4f4a-8b12-666bde3d234c", 1016 | "metadata": {}, 1017 | "outputs": [ 1018 | { 1019 | "data": { 1020 | "text/html": [ 1021 | "
\n", 1022 | "\n", 1035 | "\n", 1036 | " \n", 1037 | " \n", 1038 | " \n", 1039 | " \n", 1040 | " \n", 1041 | " \n", 1042 | " \n", 1043 | " \n", 1044 | " \n", 1045 | " \n", 1046 | " \n", 1047 | " \n", 1048 | " \n", 1049 | " \n", 1050 | " \n", 1051 | " \n", 1052 | " \n", 1053 | " \n", 1054 | " \n", 1055 | " \n", 1056 | " \n", 1057 | " \n", 1058 | " \n", 1059 | " \n", 1060 | " \n", 1061 | " \n", 1062 | " \n", 1063 | " \n", 1064 | " \n", 1065 | " \n", 1066 | " \n", 1067 | " \n", 1068 | " \n", 1069 | " \n", 1070 | " \n", 1071 | " \n", 1072 | " \n", 1073 | " \n", 1074 | " \n", 1075 | " \n", 1076 | " \n", 1077 | " \n", 1078 | " \n", 1079 | " \n", 1080 | " \n", 1081 | " \n", 1082 | " \n", 1083 | " \n", 1084 | " \n", 1085 | " \n", 1086 | " \n", 1087 | " \n", 1088 | "
idcodedirectionquantitypricelast_pricepnlyd_quantitycondmargin_purchase_amountcollateralshort_sale_margininterest
002354Action.Buy172.972.9-292.00StockOrderCond.Cash0000
114919Action.Buy195.495.4-381.00StockOrderCond.Cash0000
\n", 1089 | "
" 1090 | ], 1091 | "text/plain": [ 1092 | " id code direction quantity price last_price pnl yd_quantity \\\n", 1093 | "0 0 2354 Action.Buy 1 72.9 72.9 -292.0 0 \n", 1094 | "1 1 4919 Action.Buy 1 95.4 95.4 -381.0 0 \n", 1095 | "\n", 1096 | " cond margin_purchase_amount collateral short_sale_margin \\\n", 1097 | "0 StockOrderCond.Cash 0 0 0 \n", 1098 | "1 StockOrderCond.Cash 0 0 0 \n", 1099 | "\n", 1100 | " interest \n", 1101 | "0 0 \n", 1102 | "1 0 " 1103 | ] 1104 | }, 1105 | "execution_count": 20, 1106 | "metadata": {}, 1107 | "output_type": "execute_result" 1108 | } 1109 | ], 1110 | "source": [ 1111 | "positions = api.list_positions(api.stock_account)\n", 1112 | "position_data = pd.DataFrame(s.__dict__ for s in positions)\n", 1113 | "position_data" 1114 | ] 1115 | }, 1116 | { 1117 | "cell_type": "markdown", 1118 | "id": "c2016606-1e59-403e-b865-e2989447edcf", 1119 | "metadata": {}, 1120 | "source": [ 1121 | "### 查看已實現損益\n", 1122 | "使用 `list_profit_loss` 函式,搭配我們欲查詢的日期區間,即可查看股票代碼、價格、數量、損益、損益比、交易日期等已實現損益資訊。" 1123 | ] 1124 | }, 1125 | { 1126 | "cell_type": "code", 1127 | "execution_count": 21, 1128 | "id": "6e258cb1-8f0f-4031-8ea5-61011c6f71ec", 1129 | "metadata": {}, 1130 | "outputs": [ 1131 | { 1132 | "data": { 1133 | "text/html": [ 1134 | "
\n", 1135 | "\n", 1148 | "\n", 1149 | " \n", 1150 | " \n", 1151 | " \n", 1152 | " \n", 1153 | " \n", 1154 | " \n", 1155 | " \n", 1156 | " \n", 1157 | " \n", 1158 | " \n", 1159 | " \n", 1160 | " \n", 1161 | " \n", 1162 | " \n", 1163 | " \n", 1164 | " \n", 1165 | " \n", 1166 | " \n", 1167 | " \n", 1168 | " \n", 1169 | " \n", 1170 | " \n", 1171 | " \n", 1172 | " \n", 1173 | " \n", 1174 | " \n", 1175 | " \n", 1176 | " \n", 1177 | " \n", 1178 | " \n", 1179 | " \n", 1180 | " \n", 1181 | " \n", 1182 | " \n", 1183 | " \n", 1184 | " \n", 1185 | " \n", 1186 | " \n", 1187 | " \n", 1188 | " \n", 1189 | " \n", 1190 | " \n", 1191 | " \n", 1192 | " \n", 1193 | " \n", 1194 | " \n", 1195 | " \n", 1196 | " \n", 1197 | " \n", 1198 | " \n", 1199 | " \n", 1200 | " \n", 1201 | " \n", 1202 | " \n", 1203 | " \n", 1204 | " \n", 1205 | " \n", 1206 | " \n", 1207 | " \n", 1208 | " \n", 1209 | " \n", 1210 | " \n", 1211 | " \n", 1212 | " \n", 1213 | " \n", 1214 | " \n", 1215 | " \n", 1216 | " \n", 1217 | " \n", 1218 | " \n", 1219 | " \n", 1220 | " \n", 1221 | " \n", 1222 | " \n", 1223 | " \n", 1224 | " \n", 1225 | " \n", 1226 | " \n", 1227 | " \n", 1228 | " \n", 1229 | " \n", 1230 | " \n", 1231 | " \n", 1232 | " \n", 1233 | " \n", 1234 | " \n", 1235 | " \n", 1236 | " \n", 1237 | " \n", 1238 | " \n", 1239 | " \n", 1240 | " \n", 1241 | " \n", 1242 | " \n", 1243 | " \n", 1244 | " \n", 1245 | " \n", 1246 | " \n", 1247 | " \n", 1248 | " \n", 1249 | " \n", 1250 | " \n", 1251 | " \n", 1252 | " \n", 1253 | " \n", 1254 | " \n", 1255 | " \n", 1256 | " \n", 1257 | " \n", 1258 | " \n", 1259 | " \n", 1260 | " \n", 1261 | " \n", 1262 | " \n", 1263 | " \n", 1264 | " \n", 1265 | " \n", 1266 | " \n", 1267 | " \n", 1268 | " \n", 1269 | " \n", 1270 | " \n", 1271 | " \n", 1272 | " \n", 1273 | " \n", 1274 | " \n", 1275 | " \n", 1276 | " \n", 1277 | " \n", 1278 | " \n", 1279 | " \n", 1280 | " \n", 1281 | " \n", 1282 | " \n", 1283 | "
idcodequantitypnldatedseqpricepr_ratiocondseqno
0023301112818.0202410280001581060.0011.96379StockOrderCond.Cash00055D
1124541109777.0202410280000271320.009.11017StockOrderCond.Cash0000AB
2228901-1244.02024102800003023.35-5.07745StockOrderCond.Cash0000D0
3361521-2915.02024102800002815.85-15.58730StockOrderCond.Cash0000B2
442424118687.02024102800002C80.6030.33620StockOrderCond.Cash0000B0
5569161397.02024102800002D25.851.56548StockOrderCond.Cash0000B3
6624881-5294.02024102800002647.90-9.98896StockOrderCond.Cash0000B1
7767921-2067.02024102800019D66.60-3.02237StockOrderCond.Cash00066C
8824661-1540.02024102800018D34.90-4.24325StockOrderCond.Cash00066B
\n", 1284 | "
" 1285 | ], 1286 | "text/plain": [ 1287 | " id code quantity pnl date dseq price pr_ratio \\\n", 1288 | "0 0 2330 1 112818.0 20241028 000158 1060.00 11.96379 \n", 1289 | "1 1 2454 1 109777.0 20241028 000027 1320.00 9.11017 \n", 1290 | "2 2 2890 1 -1244.0 20241028 000030 23.35 -5.07745 \n", 1291 | "3 3 6152 1 -2915.0 20241028 000028 15.85 -15.58730 \n", 1292 | "4 4 2424 1 18687.0 20241028 00002C 80.60 30.33620 \n", 1293 | "5 5 6916 1 397.0 20241028 00002D 25.85 1.56548 \n", 1294 | "6 6 2488 1 -5294.0 20241028 000026 47.90 -9.98896 \n", 1295 | "7 7 6792 1 -2067.0 20241028 00019D 66.60 -3.02237 \n", 1296 | "8 8 2466 1 -1540.0 20241028 00018D 34.90 -4.24325 \n", 1297 | "\n", 1298 | " cond seqno \n", 1299 | "0 StockOrderCond.Cash 00055D \n", 1300 | "1 StockOrderCond.Cash 0000AB \n", 1301 | "2 StockOrderCond.Cash 0000D0 \n", 1302 | "3 StockOrderCond.Cash 0000B2 \n", 1303 | "4 StockOrderCond.Cash 0000B0 \n", 1304 | "5 StockOrderCond.Cash 0000B3 \n", 1305 | "6 StockOrderCond.Cash 0000B1 \n", 1306 | "7 StockOrderCond.Cash 00066C \n", 1307 | "8 StockOrderCond.Cash 00066B " 1308 | ] 1309 | }, 1310 | "execution_count": 21, 1311 | "metadata": {}, 1312 | "output_type": "execute_result" 1313 | } 1314 | ], 1315 | "source": [ 1316 | "profitloss = api.list_profit_loss(api.stock_account,'2024-10-28','2024-10-28')\n", 1317 | "profitloss_data = pd.DataFrame(pnl.__dict__ for pnl in profitloss)\n", 1318 | "profitloss_data" 1319 | ] 1320 | }, 1321 | { 1322 | "cell_type": "markdown", 1323 | "id": "2d749d21-d7ab-4451-9c6d-7ebdea305772", 1324 | "metadata": {}, 1325 | "source": [ 1326 | "### 登出永豐 API\n", 1327 | "因為永豐 API 有使用流量的限制,因此建議使用完 API 服務後順手登出 API 環境。" 1328 | ] 1329 | }, 1330 | { 1331 | "cell_type": "code", 1332 | "execution_count": 22, 1333 | "id": "ec5874dd-8d59-4e7d-8667-328a5b33fed8", 1334 | "metadata": {}, 1335 | "outputs": [ 1336 | { 1337 | "data": { 1338 | "text/plain": [ 1339 | "True" 1340 | ] 1341 | }, 1342 | "execution_count": 22, 1343 | "metadata": {}, 1344 | "output_type": "execute_result" 1345 | } 1346 | ], 1347 | "source": [ 1348 | "api.logout()" 1349 | ] 1350 | }, 1351 | { 1352 | "cell_type": "code", 1353 | "execution_count": null, 1354 | "id": "94cae78a-d68f-49df-a2bd-9a6d8386d2fd", 1355 | "metadata": {}, 1356 | "outputs": [], 1357 | "source": [] 1358 | } 1359 | ], 1360 | "metadata": { 1361 | "kernelspec": { 1362 | "display_name": "Python 3 (ipykernel)", 1363 | "language": "python", 1364 | "name": "python3" 1365 | }, 1366 | "language_info": { 1367 | "codemirror_mode": { 1368 | "name": "ipython", 1369 | "version": 3 1370 | }, 1371 | "file_extension": ".py", 1372 | "mimetype": "text/x-python", 1373 | "name": "python", 1374 | "nbconvert_exporter": "python", 1375 | "pygments_lexer": "ipython3", 1376 | "version": "3.11.5" 1377 | } 1378 | }, 1379 | "nbformat": 4, 1380 | "nbformat_minor": 5 1381 | } 1382 | -------------------------------------------------------------------------------- /TW50.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tejtw/TEJAPI_Python_Medium_Application/0c47ee0b3a7cafee5a8e39b5843bb1c37f462121/TW50.csv -------------------------------------------------------------------------------- /streamlit_with_tejapi.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "e08ed1c7", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "# STREAMLIT套件\n", 11 | "import streamlit as st\n", 12 | "# 可以互動的PLOT套件\n", 13 | "import plotly.graph_objects as go\n", 14 | "# 設置日期格式的套件\n", 15 | "import datetime\n", 16 | "from datetime import datetime as dt\n", 17 | "from datetime import timedelta \n", 18 | "import tejapi\n", 19 | "\n", 20 | "# 登入TEJ API\n", 21 | "tejapi.ApiConfig.api_key = \"your_key\"\n", 22 | "#把時間取消保留日期 (無視)\n", 23 | "tejapi.ApiConfig.ignoretz = True\n", 24 | "\n", 25 | "st.set_page_config(page_title='量化投資', page_icon=':bar_chart:', layout='wide')\n", 26 | "\n", 27 | "with st.sidebar:\n", 28 | " \n", 29 | " st.title('TEJAPI股票學習')\n", 30 | " col1, col2 = st.columns(2)\n", 31 | " with col1:\n", 32 | " # 將股票起使日期設置為變數d1\n", 33 | " d1 = st.date_input(\n", 34 | " \"股票起始日期\",\n", 35 | " # 並將預設資料設定為2022年的1/1\n", 36 | " datetime.date(2022, 1, 1))\n", 37 | " \n", 38 | " with col2:\n", 39 | " # 將股票起使日期設置為變數d2\n", 40 | " d2= st.date_input(\n", 41 | " \"股票結束日期\",\n", 42 | " datetime.date(2023, 2, 3))\n", 43 | " \n", 44 | " #輸入股價\n", 45 | " # 使用date套件的date獲取今天的日期資料\n", 46 | " current_date = dt.now().date()\n", 47 | " # 使用date套件的timedelta獲取昨天的日期資料\n", 48 | " previous_date = current_date - timedelta(days=1)\n", 49 | " data = tejapi.get('TWN/APIPRCD',\n", 50 | " mdate=previous_date,\n", 51 | " opts={'columns':['coid']},\n", 52 | " paginate=True)\n", 53 | " coids = data['coid'].tolist()\n", 54 | " stock_code = st.selectbox('選擇股票代碼', data)\n", 55 | " st.write('你選擇股票是: ', stock_code)\n", 56 | "\n", 57 | " \n", 58 | " # 預設選取 checkbox,並將結果儲存到變數 is_checked 中\n", 59 | " EMA1_checked = st.checkbox(\"發散指標(EMA1)(短線)\", value=True,key=\"EMA1\")\n", 60 | "\n", 61 | " # Add a slider to the sidebar:\n", 62 | " slider1 = st.sidebar.slider(\n", 63 | " '設置EMA1參數',\n", 64 | " 1, 31, 7\n", 65 | " )\n", 66 | "\n", 67 | " EMA2_checked = st.checkbox(\"發散指標(EMA2)(長線)\", value=True,key='EMA2')\n", 68 | " \n", 69 | " # Add a slider to the sidebar:\n", 70 | " slider2 = st.sidebar.slider(\n", 71 | " '設置EMA2參數',\n", 72 | " 1, 31, 21\n", 73 | " )\n", 74 | "\n", 75 | " stock_id = {stock_code}\n", 76 | " gte, lte = {d1}, {d2}\n", 77 | " tejdata= tejapi.get('TWN/APIPRCD',\n", 78 | " paginate = True,\n", 79 | " coid = stock_id,\n", 80 | " mdate = {'gte':gte, 'lte':lte},\n", 81 | " chinese_column_name=True\n", 82 | " )\n", 83 | " df = tejdata\n", 84 | " df.reset_index(drop=True, inplace=True)\n", 85 | " \n", 86 | " \n", 87 | "st.title('🌐STREAMLIT股票資料EMA應用')\n", 88 | "st.write(\"\")\n", 89 | "\n", 90 | "fig = go.Figure()\n", 91 | "# 根據 slider 的值來計算 EMA,並加入到圖表中\n", 92 | "df['EMA1'] = df['收盤價'].ewm(span=slider1, adjust=False).mean()\n", 93 | "df['EMA2'] = df['收盤價'].ewm(span=slider2, adjust=False).mean()\n", 94 | "\n", 95 | "\n", 96 | "# 如果 checkbox 被選取,則畫出 EMA1 的線\n", 97 | "if EMA1_checked:\n", 98 | " fig.add_trace(go.Scatter(x=df['資料日'], y=df['EMA1'],\n", 99 | " mode='lines',\n", 100 | " name=f'EMA{slider1}'))\n", 101 | "\n", 102 | "if EMA2_checked:\n", 103 | " fig.add_trace(go.Scatter(x=df['資料日'], y=df['EMA2'],\n", 104 | " mode='lines',\n", 105 | " name=f'EMA{slider2}')) \n", 106 | "\n", 107 | "fig.add_trace(go.Scatter(x=df['資料日'], y=df['收盤價'],\n", 108 | " mode='lines',\n", 109 | " name='收盤價'))\n", 110 | "\n", 111 | "\n", 112 | "fig.update_layout(\n", 113 | " title=f\"{stock_code} 股票價格走勢圖\",\n", 114 | " xaxis_title=\"股票時間\",\n", 115 | " yaxis_title=\"股票價格\",\n", 116 | " legend_title=\"指標\",\n", 117 | " autosize=True\n", 118 | ")\n", 119 | "st.plotly_chart(fig, use_container_width=True)\n", 120 | "\n", 121 | "\n", 122 | "with st.expander(\"🌐點此查看股票資料\"):\n", 123 | " \n", 124 | " st.dataframe(df, height=500)\n", 125 | " @st.cache_data\n", 126 | " def convert_df(df):\n", 127 | " # IMPORTANT: Cache the conversion to prevent computation on every rerun\n", 128 | " return df.to_csv().encode(\"utf-8\")\n", 129 | " csv = convert_df(df)\n", 130 | "\n", 131 | " st.download_button(\n", 132 | " label=\"點此下載資料範例\",\n", 133 | " data=csv,\n", 134 | " file_name=f\"{stock_code}股價資料.csv\",\n", 135 | " mime=\"text/csv\",\n", 136 | " )\n", 137 | "#輸入以下程式碼至終端機執行\n", 138 | "#streamlit run app.py" 139 | ] 140 | } 141 | ], 142 | "metadata": { 143 | "kernelspec": { 144 | "display_name": "Python 3 (ipykernel)", 145 | "language": "python", 146 | "name": "python3" 147 | }, 148 | "language_info": { 149 | "codemirror_mode": { 150 | "name": "ipython", 151 | "version": 3 152 | }, 153 | "file_extension": ".py", 154 | "mimetype": "text/x-python", 155 | "name": "python", 156 | "nbconvert_exporter": "python", 157 | "pygments_lexer": "ipython3", 158 | "version": "3.8.13" 159 | } 160 | }, 161 | "nbformat": 4, 162 | "nbformat_minor": 5 163 | } 164 | --------------------------------------------------------------------------------