├── 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 | " None | \n",
201 | " | \n",
202 | " | \n",
203 | " | \n",
204 | " | \n",
205 | "
\n",
206 | " \n",
207 | " \n",
208 | " \n",
209 | " 50 | \n",
210 | " CA15 | \n",
211 | " 2022-01-01 | \n",
212 | " 美國十年期公債殖利率 | \n",
213 | " M | \n",
214 | "
\n",
215 | " \n",
216 | "
\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 | " yield_10yr | \n",
265 | "
\n",
266 | " \n",
267 | " None | \n",
268 | " | \n",
269 | " | \n",
270 | "
\n",
271 | " \n",
272 | " \n",
273 | " \n",
274 | " 0 | \n",
275 | " 2020-01-01 | \n",
276 | " 1.76 | \n",
277 | "
\n",
278 | " \n",
279 | " 1 | \n",
280 | " 2020-02-01 | \n",
281 | " 1.50 | \n",
282 | "
\n",
283 | " \n",
284 | " 2 | \n",
285 | " 2020-03-01 | \n",
286 | " 0.87 | \n",
287 | "
\n",
288 | " \n",
289 | " 3 | \n",
290 | " 2020-04-01 | \n",
291 | " 0.66 | \n",
292 | "
\n",
293 | " \n",
294 | " 4 | \n",
295 | " 2020-05-01 | \n",
296 | " 0.67 | \n",
297 | "
\n",
298 | " \n",
299 | "
\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 | " None | \n",
381 | " | \n",
382 | " | \n",
383 | " | \n",
384 | "
\n",
385 | " \n",
386 | " \n",
387 | " \n",
388 | " 0 | \n",
389 | " 0053 | \n",
390 | " 2020-01-01 | \n",
391 | " -4.1972 | \n",
392 | "
\n",
393 | " \n",
394 | " 1 | \n",
395 | " 0053 | \n",
396 | " 2020-02-01 | \n",
397 | " -2.3761 | \n",
398 | "
\n",
399 | " \n",
400 | " 2 | \n",
401 | " 0053 | \n",
402 | " 2020-03-01 | \n",
403 | " -13.2353 | \n",
404 | "
\n",
405 | " \n",
406 | " 3 | \n",
407 | " 0053 | \n",
408 | " 2020-04-01 | \n",
409 | " 13.9682 | \n",
410 | "
\n",
411 | " \n",
412 | " 4 | \n",
413 | " 0053 | \n",
414 | " 2020-05-01 | \n",
415 | " -1.1027 | \n",
416 | "
\n",
417 | " \n",
418 | " ... | \n",
419 | " ... | \n",
420 | " ... | \n",
421 | " ... | \n",
422 | "
\n",
423 | " \n",
424 | " 103 | \n",
425 | " Y9997 | \n",
426 | " 2021-11-01 | \n",
427 | " 2.5960 | \n",
428 | "
\n",
429 | " \n",
430 | " 104 | \n",
431 | " Y9997 | \n",
432 | " 2021-12-01 | \n",
433 | " 4.6791 | \n",
434 | "
\n",
435 | " \n",
436 | " 105 | \n",
437 | " Y9997 | \n",
438 | " 2022-01-01 | \n",
439 | " -2.9810 | \n",
440 | "
\n",
441 | " \n",
442 | " 106 | \n",
443 | " Y9997 | \n",
444 | " 2022-02-01 | \n",
445 | " -0.1182 | \n",
446 | "
\n",
447 | " \n",
448 | " 107 | \n",
449 | " Y9997 | \n",
450 | " 2022-03-01 | \n",
451 | " 0.4297 | \n",
452 | "
\n",
453 | " \n",
454 | "
\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 | " Y9997 | \n",
520 | " 0053 | \n",
521 | " 0055 | \n",
522 | " 0056 | \n",
523 | "
\n",
524 | " \n",
525 | " \n",
526 | " \n",
527 | " 0 | \n",
528 | " 2020-01-01 | \n",
529 | " -4.1847 | \n",
530 | " -4.1972 | \n",
531 | " -1.7367 | \n",
532 | " -3.6245 | \n",
533 | "
\n",
534 | " \n",
535 | " 1 | \n",
536 | " 2020-02-01 | \n",
537 | " -1.7621 | \n",
538 | " -2.3761 | \n",
539 | " 0.0535 | \n",
540 | " -0.0714 | \n",
541 | "
\n",
542 | " \n",
543 | " 2 | \n",
544 | " 2020-03-01 | \n",
545 | " -13.8174 | \n",
546 | " -13.2353 | \n",
547 | " -17.2913 | \n",
548 | " -12.2221 | \n",
549 | "
\n",
550 | " \n",
551 | " 3 | \n",
552 | " 2020-04-01 | \n",
553 | " 13.2562 | \n",
554 | " 13.9682 | \n",
555 | " 11.7798 | \n",
556 | " 12.1275 | \n",
557 | "
\n",
558 | " \n",
559 | " 4 | \n",
560 | " 2020-05-01 | \n",
561 | " -0.4498 | \n",
562 | " -1.1027 | \n",
563 | " -0.1158 | \n",
564 | " 1.4565 | \n",
565 | "
\n",
566 | " \n",
567 | "
\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 | " yield_10yr | \n",
631 | "
\n",
632 | " \n",
633 | " \n",
634 | " \n",
635 | " 0 | \n",
636 | " 2020-01-01 | \n",
637 | " -4.1847 | \n",
638 | " -4.1972 | \n",
639 | " -1.7367 | \n",
640 | " -3.6245 | \n",
641 | " 1.76 | \n",
642 | "
\n",
643 | " \n",
644 | " 1 | \n",
645 | " 2020-02-01 | \n",
646 | " -1.7621 | \n",
647 | " -2.3761 | \n",
648 | " 0.0535 | \n",
649 | " -0.0714 | \n",
650 | " 1.50 | \n",
651 | "
\n",
652 | " \n",
653 | " 2 | \n",
654 | " 2020-03-01 | \n",
655 | " -13.8174 | \n",
656 | " -13.2353 | \n",
657 | " -17.2913 | \n",
658 | " -12.2221 | \n",
659 | " 0.87 | \n",
660 | "
\n",
661 | " \n",
662 | " 3 | \n",
663 | " 2020-04-01 | \n",
664 | " 13.2562 | \n",
665 | " 13.9682 | \n",
666 | " 11.7798 | \n",
667 | " 12.1275 | \n",
668 | " 0.66 | \n",
669 | "
\n",
670 | " \n",
671 | " 4 | \n",
672 | " 2020-05-01 | \n",
673 | " -0.4498 | \n",
674 | " -1.1027 | \n",
675 | " -0.1158 | \n",
676 | " 1.4565 | \n",
677 | " 0.67 | \n",
678 | "
\n",
679 | " \n",
680 | "
\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 | " None | \n",
57 | " | \n",
58 | " | \n",
59 | " | \n",
60 | " | \n",
61 | " | \n",
62 | " | \n",
63 | "
\n",
64 | " \n",
65 | " \n",
66 | " \n",
67 | " 3 | \n",
68 | " 2021-09-01 00:00:00+00:00 | \n",
69 | " 2303 聯電 | \n",
70 | " 6.41 | \n",
71 | " 2303 | \n",
72 | " 2021 | \n",
73 | " 9 | \n",
74 | "
\n",
75 | " \n",
76 | " 4 | \n",
77 | " 2021-09-01 00:00:00+00:00 | \n",
78 | " 2317 鴻海 | \n",
79 | " 11.99 | \n",
80 | " 2317 | \n",
81 | " 2021 | \n",
82 | " 9 | \n",
83 | "
\n",
84 | " \n",
85 | " 5 | \n",
86 | " 2021-09-01 00:00:00+00:00 | \n",
87 | " 2324 仁寶 | \n",
88 | " 1.05 | \n",
89 | " 2324 | \n",
90 | " 2021 | \n",
91 | " 9 | \n",
92 | "
\n",
93 | " \n",
94 | " 6 | \n",
95 | " 2021-09-01 00:00:00+00:00 | \n",
96 | " 2330 台積電 | \n",
97 | " 30.24 | \n",
98 | " 2330 | \n",
99 | " 2021 | \n",
100 | " 9 | \n",
101 | "
\n",
102 | " \n",
103 | " 7 | \n",
104 | " 2021-09-01 00:00:00+00:00 | \n",
105 | " 2345 智邦 | \n",
106 | " 1.73 | \n",
107 | " 2345 | \n",
108 | " 2021 | \n",
109 | " 9 | \n",
110 | "
\n",
111 | " \n",
112 | " 8 | \n",
113 | " 2021-09-01 00:00:00+00:00 | \n",
114 | " 2352 佳世達 | \n",
115 | " 0.55 | \n",
116 | " 2352 | \n",
117 | " 2021 | \n",
118 | " 9 | \n",
119 | "
\n",
120 | " \n",
121 | " 9 | \n",
122 | " 2021-09-01 00:00:00+00:00 | \n",
123 | " 2356 英業達 | \n",
124 | " 0.84 | \n",
125 | " 2356 | \n",
126 | " 2021 | \n",
127 | " 9 | \n",
128 | "
\n",
129 | " \n",
130 | " 10 | \n",
131 | " 2021-09-01 00:00:00+00:00 | \n",
132 | " 2360 致茂 | \n",
133 | " 0.80 | \n",
134 | " 2360 | \n",
135 | " 2021 | \n",
136 | " 9 | \n",
137 | "
\n",
138 | " \n",
139 | " 11 | \n",
140 | " 2021-09-01 00:00:00+00:00 | \n",
141 | " 2379 瑞昱 | \n",
142 | " 3.05 | \n",
143 | " 2379 | \n",
144 | " 2021 | \n",
145 | " 9 | \n",
146 | "
\n",
147 | " \n",
148 | " 12 | \n",
149 | " 2021-09-01 00:00:00+00:00 | \n",
150 | " 2382 廣達 | \n",
151 | " 2.24 | \n",
152 | " 2382 | \n",
153 | " 2021 | \n",
154 | " 9 | \n",
155 | "
\n",
156 | " \n",
157 | "
\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 | " TSE產業名 | \n",
241 | "
\n",
242 | " \n",
243 | " None | \n",
244 | " | \n",
245 | " | \n",
246 | " | \n",
247 | "
\n",
248 | " \n",
249 | " \n",
250 | " \n",
251 | " 0 | \n",
252 | " 1101 | \n",
253 | " 台泥 | \n",
254 | " 水泥工業 | \n",
255 | "
\n",
256 | " \n",
257 | " 1 | \n",
258 | " 1102 | \n",
259 | " 亞泥 | \n",
260 | " 水泥工業 | \n",
261 | "
\n",
262 | " \n",
263 | " 2 | \n",
264 | " 1216 | \n",
265 | " 統一 | \n",
266 | " 食品工業 | \n",
267 | "
\n",
268 | " \n",
269 | " 3 | \n",
270 | " 1301 | \n",
271 | " 台塑 | \n",
272 | " 塑膠工業 | \n",
273 | "
\n",
274 | " \n",
275 | " 4 | \n",
276 | " 1303 | \n",
277 | " 南亞 | \n",
278 | " 塑膠工業 | \n",
279 | "
\n",
280 | " \n",
281 | "
\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 | " None | \n",
350 | " | \n",
351 | " | \n",
352 | " | \n",
353 | " | \n",
354 | " | \n",
355 | "
\n",
356 | " \n",
357 | " \n",
358 | " \n",
359 | " 0 | \n",
360 | " 1101 | \n",
361 | " 2021-09-01 00:00:00+00:00 | \n",
362 | " 5.2631 | \n",
363 | " 2021 | \n",
364 | " 9 | \n",
365 | "
\n",
366 | " \n",
367 | " 1 | \n",
368 | " 1101 | \n",
369 | " 2021-10-01 00:00:00+00:00 | \n",
370 | " -5.3920 | \n",
371 | " 2021 | \n",
372 | " 10 | \n",
373 | "
\n",
374 | " \n",
375 | " 2 | \n",
376 | " 1101 | \n",
377 | " 2021-11-01 00:00:00+00:00 | \n",
378 | " -4.6630 | \n",
379 | " 2021 | \n",
380 | " 11 | \n",
381 | "
\n",
382 | " \n",
383 | " 3 | \n",
384 | " 1102 | \n",
385 | " 2021-09-01 00:00:00+00:00 | \n",
386 | " 1.5589 | \n",
387 | " 2021 | \n",
388 | " 9 | \n",
389 | "
\n",
390 | " \n",
391 | " 4 | \n",
392 | " 1102 | \n",
393 | " 2021-10-01 00:00:00+00:00 | \n",
394 | " -2.8506 | \n",
395 | " 2021 | \n",
396 | " 10 | \n",
397 | "
\n",
398 | " \n",
399 | "
\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 | " TSE產業名 | \n",
488 | " 成份股 | \n",
489 | " 證券代碼 | \n",
490 | " 年 | \n",
491 | " 月 | \n",
492 | " 前日市值比重 | \n",
493 | " 報酬率%_月 | \n",
494 | " 產業權重 | \n",
495 | " 實際當月報酬率 | \n",
496 | " 產業當月報酬率 | \n",
497 | " 實際產業當月報酬率 | \n",
498 | " ETF 當月報酬率 | \n",
499 | "
\n",
500 | " \n",
501 | " \n",
502 | " \n",
503 | " 0 | \n",
504 | " 2021-09-01 00:00:00+00:00 | \n",
505 | " 光電業 | \n",
506 | " 2409 友達 | \n",
507 | " 2409 | \n",
508 | " 2021 | \n",
509 | " 9 | \n",
510 | " 0.005084 | \n",
511 | " 0.2842 | \n",
512 | " 0.013939 | \n",
513 | " 0.001445 | \n",
514 | " -11.511265 | \n",
515 | " -0.160456 | \n",
516 | " -3.615165 | \n",
517 | "
\n",
518 | " \n",
519 | " 1 | \n",
520 | " 2021-09-01 00:00:00+00:00 | \n",
521 | " 光電業 | \n",
522 | " 3008 大立光 | \n",
523 | " 3008 | \n",
524 | " 2021 | \n",
525 | " 9 | \n",
526 | " 0.008855 | \n",
527 | " -18.2835 | \n",
528 | " 0.013939 | \n",
529 | " -0.161900 | \n",
530 | " -11.511265 | \n",
531 | " -0.160456 | \n",
532 | " -3.615165 | \n",
533 | "
\n",
534 | " \n",
535 | " 2 | \n",
536 | " 2021-09-01 00:00:00+00:00 | \n",
537 | " 其他 | \n",
538 | " 1101 台泥 | \n",
539 | " 1101 | \n",
540 | " 2021 | \n",
541 | " 9 | \n",
542 | " 0.008478 | \n",
543 | " 5.2631 | \n",
544 | " 0.274339 | \n",
545 | " 0.044621 | \n",
546 | " -0.963335 | \n",
547 | " -0.264280 | \n",
548 | " -3.615165 | \n",
549 | "
\n",
550 | " \n",
551 | " 3 | \n",
552 | " 2021-09-01 00:00:00+00:00 | \n",
553 | " 其他 | \n",
554 | " 1102 亞泥 | \n",
555 | " 1102 | \n",
556 | " 2021 | \n",
557 | " 9 | \n",
558 | " 0.003597 | \n",
559 | " 1.5589 | \n",
560 | " 0.274339 | \n",
561 | " 0.005607 | \n",
562 | " -0.963335 | \n",
563 | " -0.264280 | \n",
564 | " -3.615165 | \n",
565 | "
\n",
566 | " \n",
567 | " 4 | \n",
568 | " 2021-09-01 00:00:00+00:00 | \n",
569 | " 其他 | \n",
570 | " 1216 統一 | \n",
571 | " 1216 | \n",
572 | " 2021 | \n",
573 | " 9 | \n",
574 | " 0.011469 | \n",
575 | " -1.7368 | \n",
576 | " 0.274339 | \n",
577 | " -0.019919 | \n",
578 | " -0.963335 | \n",
579 | " -0.264280 | \n",
580 | " -3.615165 | \n",
581 | "
\n",
582 | " \n",
583 | "
\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 | " TSE產業名 | \n",
669 | " 投組權重 | \n",
670 | " 投組當月報酬率 | \n",
671 | "
\n",
672 | " \n",
673 | " \n",
674 | " \n",
675 | " 0 | \n",
676 | " 2021 | \n",
677 | " 9 | \n",
678 | " 光電業 | \n",
679 | " 0.0238 | \n",
680 | " -18.283500 | \n",
681 | "
\n",
682 | " \n",
683 | " 1 | \n",
684 | " 2021 | \n",
685 | " 9 | \n",
686 | " 其他 | \n",
687 | " 0.0272 | \n",
688 | " -2.156355 | \n",
689 | "
\n",
690 | " \n",
691 | " 2 | \n",
692 | " 2021 | \n",
693 | " 9 | \n",
694 | " 其他電子業 | \n",
695 | " 0.1279 | \n",
696 | " -5.189256 | \n",
697 | "
\n",
698 | " \n",
699 | " 3 | \n",
700 | " 2021 | \n",
701 | " 9 | \n",
702 | " 半導體 | \n",
703 | " 0.6297 | \n",
704 | " -3.043494 | \n",
705 | "
\n",
706 | " \n",
707 | " 4 | \n",
708 | " 2021 | \n",
709 | " 9 | \n",
710 | " 通信網路業 | \n",
711 | " 0.0553 | \n",
712 | " -2.857103 | \n",
713 | "
\n",
714 | " \n",
715 | "
\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 | " TSE產業名 | \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 | " 0.61 | \n",
798 | " 4.93 | \n",
799 | " 0.60 | \n",
800 | " 2.47 | \n",
801 | " 0.01 | \n",
802 | " 1.46 | \n",
803 | " 0.04 | \n",
804 | " 1.52 | \n",
805 | "
\n",
806 | " \n",
807 | " 其他電子業 | \n",
808 | " 0.12 | \n",
809 | " -2.91 | \n",
810 | " 0.04 | \n",
811 | " -3.27 | \n",
812 | " -0.39 | \n",
813 | " 0.02 | \n",
814 | " 0.03 | \n",
815 | " -0.34 | \n",
816 | "
\n",
817 | " \n",
818 | " 電子零組件 | \n",
819 | " 0.11 | \n",
820 | " 11.80 | \n",
821 | " 0.03 | \n",
822 | " 7.50 | \n",
823 | " 0.46 | \n",
824 | " 0.12 | \n",
825 | " 0.34 | \n",
826 | " 0.93 | \n",
827 | "
\n",
828 | " \n",
829 | " 電腦及週邊 | \n",
830 | " 0.07 | \n",
831 | " 1.55 | \n",
832 | " 0.03 | \n",
833 | " 3.48 | \n",
834 | " 0.07 | \n",
835 | " -0.05 | \n",
836 | " -0.08 | \n",
837 | " -0.06 | \n",
838 | "
\n",
839 | " \n",
840 | " 通信網路業 | \n",
841 | " 0.05 | \n",
842 | " 5.01 | \n",
843 | " 0.02 | \n",
844 | " 0.65 | \n",
845 | " -0.03 | \n",
846 | " 0.10 | \n",
847 | " 0.12 | \n",
848 | " 0.19 | \n",
849 | "
\n",
850 | " \n",
851 | " 光電業 | \n",
852 | " 0.02 | \n",
853 | " -3.86 | \n",
854 | " 0.01 | \n",
855 | " -0.03 | \n",
856 | " -0.02 | \n",
857 | " -0.05 | \n",
858 | " -0.03 | \n",
859 | " -0.10 | \n",
860 | "
\n",
861 | " \n",
862 | " 其他 | \n",
863 | " 0.02 | \n",
864 | " 0.32 | \n",
865 | " 0.27 | \n",
866 | " 0.30 | \n",
867 | " 0.36 | \n",
868 | " 0.00 | \n",
869 | " -0.00 | \n",
870 | " 0.36 | \n",
871 | "
\n",
872 | " \n",
873 | " 合計 | \n",
874 | " 1.00 | \n",
875 | " 4.22 | \n",
876 | " 1.00 | \n",
877 | " 1.73 | \n",
878 | " 0.47 | \n",
879 | " 1.61 | \n",
880 | " 0.41 | \n",
881 | " 2.49 | \n",
882 | "
\n",
883 | " \n",
884 | "
\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 | " SMB | \n",
300 | " HML | \n",
301 | "
\n",
302 | " \n",
303 | " 年月日 | \n",
304 | " | \n",
305 | " | \n",
306 | "
\n",
307 | " \n",
308 | " \n",
309 | " \n",
310 | " 2014-01-02 | \n",
311 | " 0.727428 | \n",
312 | " 0.423522 | \n",
313 | "
\n",
314 | " \n",
315 | " 2014-01-03 | \n",
316 | " 1.214870 | \n",
317 | " 0.678727 | \n",
318 | "
\n",
319 | " \n",
320 | " 2014-01-06 | \n",
321 | " 0.623517 | \n",
322 | " 0.075185 | \n",
323 | "
\n",
324 | " \n",
325 | " 2014-01-07 | \n",
326 | " 0.642468 | \n",
327 | " 0.697764 | \n",
328 | "
\n",
329 | " \n",
330 | " 2014-01-08 | \n",
331 | " 0.241418 | \n",
332 | " 0.260657 | \n",
333 | "
\n",
334 | " \n",
335 | " ... | \n",
336 | " ... | \n",
337 | " ... | \n",
338 | "
\n",
339 | " \n",
340 | " 2022-07-25 | \n",
341 | " 0.322174 | \n",
342 | " 0.767774 | \n",
343 | "
\n",
344 | " \n",
345 | " 2022-07-26 | \n",
346 | " 0.053480 | \n",
347 | " 0.787060 | \n",
348 | "
\n",
349 | " \n",
350 | " 2022-07-27 | \n",
351 | " -0.290234 | \n",
352 | " -1.005587 | \n",
353 | "
\n",
354 | " \n",
355 | " 2022-07-28 | \n",
356 | " -0.106124 | \n",
357 | " 0.418247 | \n",
358 | "
\n",
359 | " \n",
360 | " 2022-07-29 | \n",
361 | " 0.065884 | \n",
362 | " -0.549442 | \n",
363 | "
\n",
364 | " \n",
365 | "
\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 | " SMB | \n",
459 | " HML | \n",
460 | " rm | \n",
461 | "
\n",
462 | " \n",
463 | " 年月日 | \n",
464 | " | \n",
465 | " | \n",
466 | " | \n",
467 | "
\n",
468 | " \n",
469 | " \n",
470 | " \n",
471 | " 2014-01-02 | \n",
472 | " 0.727428 | \n",
473 | " 0.423522 | \n",
474 | " 0.0120 | \n",
475 | "
\n",
476 | " \n",
477 | " 2014-01-03 | \n",
478 | " 1.214870 | \n",
479 | " 0.678727 | \n",
480 | " -0.7663 | \n",
481 | "
\n",
482 | " \n",
483 | " 2014-01-06 | \n",
484 | " 0.623517 | \n",
485 | " 0.075185 | \n",
486 | " -0.5444 | \n",
487 | "
\n",
488 | " \n",
489 | " 2014-01-07 | \n",
490 | " 0.642468 | \n",
491 | " 0.697764 | \n",
492 | " 0.1446 | \n",
493 | "
\n",
494 | " \n",
495 | " 2014-01-08 | \n",
496 | " 0.241418 | \n",
497 | " 0.260657 | \n",
498 | " 0.5135 | \n",
499 | "
\n",
500 | " \n",
501 | " ... | \n",
502 | " ... | \n",
503 | " ... | \n",
504 | " ... | \n",
505 | "
\n",
506 | " \n",
507 | " 2022-06-24 | \n",
508 | " 0.067493 | \n",
509 | " 0.737971 | \n",
510 | " 0.8360 | \n",
511 | "
\n",
512 | " \n",
513 | " 2022-06-27 | \n",
514 | " 0.029549 | \n",
515 | " -0.991883 | \n",
516 | " 1.5989 | \n",
517 | "
\n",
518 | " \n",
519 | " 2022-06-28 | \n",
520 | " -0.021501 | \n",
521 | " 0.649327 | \n",
522 | " -0.6952 | \n",
523 | "
\n",
524 | " \n",
525 | " 2022-06-29 | \n",
526 | " 0.699726 | \n",
527 | " -0.251527 | \n",
528 | " -1.2940 | \n",
529 | "
\n",
530 | " \n",
531 | " 2022-06-30 | \n",
532 | " 0.283322 | \n",
533 | " 0.301092 | \n",
534 | " -2.7191 | \n",
535 | "
\n",
536 | " \n",
537 | "
\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 | " 2014-01-02 | \n",
606 | " 1101 | \n",
607 | " -1.8378 | \n",
608 | "
\n",
609 | " \n",
610 | " 2014-01-02 | \n",
611 | " 1102 | \n",
612 | " -1.2953 | \n",
613 | "
\n",
614 | " \n",
615 | " 2014-01-02 | \n",
616 | " 1104 | \n",
617 | " 0.1770 | \n",
618 | "
\n",
619 | " \n",
620 | " 2014-01-02 | \n",
621 | " 1110 | \n",
622 | " 1.7143 | \n",
623 | "
\n",
624 | " \n",
625 | " 2014-01-02 | \n",
626 | " 1203 | \n",
627 | " 0.0000 | \n",
628 | "
\n",
629 | " \n",
630 | " ... | \n",
631 | " ... | \n",
632 | " ... | \n",
633 | "
\n",
634 | " \n",
635 | " 2022-06-30 | \n",
636 | " 8440 | \n",
637 | " 9.9836 | \n",
638 | "
\n",
639 | " \n",
640 | " 2022-06-30 | \n",
641 | " 8446 | \n",
642 | " -0.2710 | \n",
643 | "
\n",
644 | " \n",
645 | " 2022-06-30 | \n",
646 | " 8928 | \n",
647 | " -6.1002 | \n",
648 | "
\n",
649 | " \n",
650 | " 2022-06-30 | \n",
651 | " 9928 | \n",
652 | " 3.0488 | \n",
653 | "
\n",
654 | " \n",
655 | " 2022-06-30 | \n",
656 | " 9950 | \n",
657 | " -9.1912 | \n",
658 | "
\n",
659 | " \n",
660 | "
\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 | " alpha | \n",
771 | " alpha0.2 | \n",
772 | " alpha0.8 | \n",
773 | "
\n",
774 | " \n",
775 | " 年月日 | \n",
776 | " | \n",
777 | " | \n",
778 | " | \n",
779 | " | \n",
780 | "
\n",
781 | " \n",
782 | " \n",
783 | " \n",
784 | " 2014-06-30 | \n",
785 | " 1210 | \n",
786 | " 0.216635 | \n",
787 | " -0.142295 | \n",
788 | " 0.141690 | \n",
789 | "
\n",
790 | " \n",
791 | " 2014-06-30 | \n",
792 | " 1527 | \n",
793 | " 0.168521 | \n",
794 | " -0.142295 | \n",
795 | " 0.141690 | \n",
796 | "
\n",
797 | " \n",
798 | " 2014-06-30 | \n",
799 | " 1582 | \n",
800 | " 0.237503 | \n",
801 | " -0.142295 | \n",
802 | " 0.141690 | \n",
803 | "
\n",
804 | " \n",
805 | " 2014-06-30 | \n",
806 | " 1583 | \n",
807 | " 0.585237 | \n",
808 | " -0.142295 | \n",
809 | " 0.141690 | \n",
810 | "
\n",
811 | " \n",
812 | " 2014-06-30 | \n",
813 | " 1711 | \n",
814 | " 0.192855 | \n",
815 | " -0.142295 | \n",
816 | " 0.141690 | \n",
817 | "
\n",
818 | " \n",
819 | " ... | \n",
820 | " ... | \n",
821 | " ... | \n",
822 | " ... | \n",
823 | " ... | \n",
824 | "
\n",
825 | " \n",
826 | " 2022-06-30 | \n",
827 | " 8409 | \n",
828 | " 0.490293 | \n",
829 | " -0.087633 | \n",
830 | " 0.134834 | \n",
831 | "
\n",
832 | " \n",
833 | " 2022-06-30 | \n",
834 | " 8433 | \n",
835 | " 0.220098 | \n",
836 | " -0.087633 | \n",
837 | " 0.134834 | \n",
838 | "
\n",
839 | " \n",
840 | " 2022-06-30 | \n",
841 | " 8928 | \n",
842 | " 0.279140 | \n",
843 | " -0.087633 | \n",
844 | " 0.134834 | \n",
845 | "
\n",
846 | " \n",
847 | " 2022-06-30 | \n",
848 | " 8941 | \n",
849 | " 0.141910 | \n",
850 | " -0.087633 | \n",
851 | " 0.134834 | \n",
852 | "
\n",
853 | " \n",
854 | " 2022-06-30 | \n",
855 | " 9950 | \n",
856 | " 0.295723 | \n",
857 | " -0.087633 | \n",
858 | " 0.134834 | \n",
859 | "
\n",
860 | " \n",
861 | "
\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 | " coid | \n",
140 | " mdate | \n",
141 | " Market_Cap_Dollars | \n",
142 | "
\n",
143 | " \n",
144 | " \n",
145 | " \n",
146 | " 0 | \n",
147 | " 1471 | \n",
148 | " 2024-10-25 | \n",
149 | " 1.962910e+09 | \n",
150 | "
\n",
151 | " \n",
152 | " 1 | \n",
153 | " 1582 | \n",
154 | " 2024-10-25 | \n",
155 | " 1.514820e+10 | \n",
156 | "
\n",
157 | " \n",
158 | " 2 | \n",
159 | " 2059 | \n",
160 | " 2024-10-25 | \n",
161 | " 1.195978e+11 | \n",
162 | "
\n",
163 | " \n",
164 | " 3 | \n",
165 | " 2301 | \n",
166 | " 2024-10-25 | \n",
167 | " 2.441140e+11 | \n",
168 | "
\n",
169 | " \n",
170 | " 4 | \n",
171 | " 2302 | \n",
172 | " 2024-10-25 | \n",
173 | " 3.201330e+09 | \n",
174 | "
\n",
175 | " \n",
176 | " ... | \n",
177 | " ... | \n",
178 | " ... | \n",
179 | " ... | \n",
180 | "
\n",
181 | " \n",
182 | " 402 | \n",
183 | " 8215 | \n",
184 | " 2024-10-25 | \n",
185 | " 1.032572e+10 | \n",
186 | "
\n",
187 | " \n",
188 | " 403 | \n",
189 | " 8249 | \n",
190 | " 2024-10-25 | \n",
191 | " 9.068506e+09 | \n",
192 | "
\n",
193 | " \n",
194 | " 404 | \n",
195 | " 8261 | \n",
196 | " 2024-10-25 | \n",
197 | " 9.226725e+09 | \n",
198 | "
\n",
199 | " \n",
200 | " 405 | \n",
201 | " 8271 | \n",
202 | " 2024-10-25 | \n",
203 | " 6.603811e+09 | \n",
204 | "
\n",
205 | " \n",
206 | " 406 | \n",
207 | " 9912 | \n",
208 | " 2024-10-25 | \n",
209 | " 7.050700e+08 | \n",
210 | "
\n",
211 | " \n",
212 | "
\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 | " close | \n",
570 | " final_upperband | \n",
571 | " final_lowerband | \n",
572 | " ADX | \n",
573 | "
\n",
574 | " \n",
575 | " \n",
576 | " \n",
577 | " 2024-10-25 00:00:00+00:00 | \n",
578 | " Equity(16 [2354]) | \n",
579 | " 69.8 | \n",
580 | " 69.014286 | \n",
581 | " 63.985714 | \n",
582 | " 79.029377 | \n",
583 | "
\n",
584 | " \n",
585 | " Equity(73 [4919]) | \n",
586 | " 94.1 | \n",
587 | " 89.441837 | \n",
588 | " 78.708163 | \n",
589 | " 79.987217 | \n",
590 | "
\n",
591 | " \n",
592 | "
\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 | " close | \n",
671 | " final_upperband | \n",
672 | " final_lowerband | \n",
673 | " ADX | \n",
674 | "
\n",
675 | " \n",
676 | " \n",
677 | " \n",
678 | " 2024-10-25 00:00:00+00:00 | \n",
679 | " Equity(42 [2498]) | \n",
680 | " 45.85 | \n",
681 | " 52.910204 | \n",
682 | " 47.089796 | \n",
683 | " 80.747982 | \n",
684 | "
\n",
685 | " \n",
686 | " Equity(93 [6770]) | \n",
687 | " 19.70 | \n",
688 | " 21.993878 | \n",
689 | " 19.831122 | \n",
690 | " 82.509597 | \n",
691 | "
\n",
692 | " \n",
693 | "
\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 | " id | \n",
1040 | " code | \n",
1041 | " direction | \n",
1042 | " quantity | \n",
1043 | " price | \n",
1044 | " last_price | \n",
1045 | " pnl | \n",
1046 | " yd_quantity | \n",
1047 | " cond | \n",
1048 | " margin_purchase_amount | \n",
1049 | " collateral | \n",
1050 | " short_sale_margin | \n",
1051 | " interest | \n",
1052 | "
\n",
1053 | " \n",
1054 | " \n",
1055 | " \n",
1056 | " 0 | \n",
1057 | " 0 | \n",
1058 | " 2354 | \n",
1059 | " Action.Buy | \n",
1060 | " 1 | \n",
1061 | " 72.9 | \n",
1062 | " 72.9 | \n",
1063 | " -292.0 | \n",
1064 | " 0 | \n",
1065 | " StockOrderCond.Cash | \n",
1066 | " 0 | \n",
1067 | " 0 | \n",
1068 | " 0 | \n",
1069 | " 0 | \n",
1070 | "
\n",
1071 | " \n",
1072 | " 1 | \n",
1073 | " 1 | \n",
1074 | " 4919 | \n",
1075 | " Action.Buy | \n",
1076 | " 1 | \n",
1077 | " 95.4 | \n",
1078 | " 95.4 | \n",
1079 | " -381.0 | \n",
1080 | " 0 | \n",
1081 | " StockOrderCond.Cash | \n",
1082 | " 0 | \n",
1083 | " 0 | \n",
1084 | " 0 | \n",
1085 | " 0 | \n",
1086 | "
\n",
1087 | " \n",
1088 | "
\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 | " id | \n",
1153 | " code | \n",
1154 | " quantity | \n",
1155 | " pnl | \n",
1156 | " date | \n",
1157 | " dseq | \n",
1158 | " price | \n",
1159 | " pr_ratio | \n",
1160 | " cond | \n",
1161 | " seqno | \n",
1162 | "
\n",
1163 | " \n",
1164 | " \n",
1165 | " \n",
1166 | " 0 | \n",
1167 | " 0 | \n",
1168 | " 2330 | \n",
1169 | " 1 | \n",
1170 | " 112818.0 | \n",
1171 | " 20241028 | \n",
1172 | " 000158 | \n",
1173 | " 1060.00 | \n",
1174 | " 11.96379 | \n",
1175 | " StockOrderCond.Cash | \n",
1176 | " 00055D | \n",
1177 | "
\n",
1178 | " \n",
1179 | " 1 | \n",
1180 | " 1 | \n",
1181 | " 2454 | \n",
1182 | " 1 | \n",
1183 | " 109777.0 | \n",
1184 | " 20241028 | \n",
1185 | " 000027 | \n",
1186 | " 1320.00 | \n",
1187 | " 9.11017 | \n",
1188 | " StockOrderCond.Cash | \n",
1189 | " 0000AB | \n",
1190 | "
\n",
1191 | " \n",
1192 | " 2 | \n",
1193 | " 2 | \n",
1194 | " 2890 | \n",
1195 | " 1 | \n",
1196 | " -1244.0 | \n",
1197 | " 20241028 | \n",
1198 | " 000030 | \n",
1199 | " 23.35 | \n",
1200 | " -5.07745 | \n",
1201 | " StockOrderCond.Cash | \n",
1202 | " 0000D0 | \n",
1203 | "
\n",
1204 | " \n",
1205 | " 3 | \n",
1206 | " 3 | \n",
1207 | " 6152 | \n",
1208 | " 1 | \n",
1209 | " -2915.0 | \n",
1210 | " 20241028 | \n",
1211 | " 000028 | \n",
1212 | " 15.85 | \n",
1213 | " -15.58730 | \n",
1214 | " StockOrderCond.Cash | \n",
1215 | " 0000B2 | \n",
1216 | "
\n",
1217 | " \n",
1218 | " 4 | \n",
1219 | " 4 | \n",
1220 | " 2424 | \n",
1221 | " 1 | \n",
1222 | " 18687.0 | \n",
1223 | " 20241028 | \n",
1224 | " 00002C | \n",
1225 | " 80.60 | \n",
1226 | " 30.33620 | \n",
1227 | " StockOrderCond.Cash | \n",
1228 | " 0000B0 | \n",
1229 | "
\n",
1230 | " \n",
1231 | " 5 | \n",
1232 | " 5 | \n",
1233 | " 6916 | \n",
1234 | " 1 | \n",
1235 | " 397.0 | \n",
1236 | " 20241028 | \n",
1237 | " 00002D | \n",
1238 | " 25.85 | \n",
1239 | " 1.56548 | \n",
1240 | " StockOrderCond.Cash | \n",
1241 | " 0000B3 | \n",
1242 | "
\n",
1243 | " \n",
1244 | " 6 | \n",
1245 | " 6 | \n",
1246 | " 2488 | \n",
1247 | " 1 | \n",
1248 | " -5294.0 | \n",
1249 | " 20241028 | \n",
1250 | " 000026 | \n",
1251 | " 47.90 | \n",
1252 | " -9.98896 | \n",
1253 | " StockOrderCond.Cash | \n",
1254 | " 0000B1 | \n",
1255 | "
\n",
1256 | " \n",
1257 | " 7 | \n",
1258 | " 7 | \n",
1259 | " 6792 | \n",
1260 | " 1 | \n",
1261 | " -2067.0 | \n",
1262 | " 20241028 | \n",
1263 | " 00019D | \n",
1264 | " 66.60 | \n",
1265 | " -3.02237 | \n",
1266 | " StockOrderCond.Cash | \n",
1267 | " 00066C | \n",
1268 | "
\n",
1269 | " \n",
1270 | " 8 | \n",
1271 | " 8 | \n",
1272 | " 2466 | \n",
1273 | " 1 | \n",
1274 | " -1540.0 | \n",
1275 | " 20241028 | \n",
1276 | " 00018D | \n",
1277 | " 34.90 | \n",
1278 | " -4.24325 | \n",
1279 | " StockOrderCond.Cash | \n",
1280 | " 00066B | \n",
1281 | "
\n",
1282 | " \n",
1283 | "
\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 |
--------------------------------------------------------------------------------