# 股票投資分析期末報告 ###### tags: `Final Report` `Stock Investment` :::info [TOC] ::: ## KD指標策略 ### 第一種買賣訊號 >當K值大於D值,顯示目前是向上漲升的趨勢,因此在圖形上K線向上突破D線時,即為買進訊號。 >當D值大於K值,顯示目前是向下跌落,因此在圖形上K 線向下跌破D線,此即為賣出訊號。 >K線與D線的交叉,須在80以上,20以下(一說70、30;視市場投機程度而彈性擴大範圍),訊號才正確。 ### 第二種買賣訊號 >當K值大於80,D值大於70時,表示當日收盤價處於偏高之價格區域,即為超買狀態,應該出場。 當K值小於20,D值小於30時,表示當日收盤價處於偏低之價格區域,即為超賣狀態,可以進場。 ### 第三種買賣訊號 >當D值超過85以上時,意味市場為嚴重之超買,其為賣出訊號。 >當D值跌至15以下時,意味市場為嚴重之超賣,其為買入訊號。 ```python= import numpy as np import pandas as pd import mplfinance as mpf import talib from datetime import datetime data = pd.read_csv('3563.csv') data.iloc[:, 0] = pd.to_datetime(data.iloc[:, 0], format = "%Y/%m/%d") dataclose= [float(line) for line in data['Close']] #求出 KD值 K,D = talib.STOCH(data['High'].values,data['Low'].values,data['Close'].values) #記錄總交易細節 trade = [['Data', 'Buy("+")/Sell("-")', 'Price', 'Pay("-")/Gain("+")', 'Net Income']] # 暫存單筆交易用 information = [] #持有股票張數 stock = 0 #現階段獲利 get = 0 for i in range(1, len(K)): if((( (K[i - 1] < D[i - 1] and K[i] > D[i] and K[i] < 20)or D[i] < 15 or (D[i] < 30 and K[i] < 20)) ) and stock == 0): stock = 1 get -= round(stock * dataclose[i] * 1000 * 1.001425) information.append( str(datetime.date(data['Date'][i]).year) + '/' +str(datetime.date(data['Date'][i]).month) + '/' + str(datetime.date(data['Date'][i]).day) ) information.append('+1') information.append(dataclose[i]) information.append('-' + str(round(stock * dataclose[i] * 1000 * 1.001425))) information.append(get) #存入總交易目錄 trade.append(information) #清空單筆交易細項 information = [] elif((((K[i - 1] > D[i - 1] and K[i] < D[i] and K[i] > 80) or D[i] > 85 or (D[i] > 70 and K[i] > 80))) and stock == 1): get += round(stock * dataclose[i] * 1000 * 0.995575) information.append(str(datetime.date(data['Date'][i]).year) + '/' + str(datetime.date(data['Date'][i]).month) + '/' + str(datetime.date(data['Date'][i]).day) ) information.append('-1') information.append(dataclose[i]) information.append('+' + str(round(stock * dataclose[i] * 1000 * 0.995575))) information.append(get) #存入總交易目錄 trade.append(information) #售出清空持股 stock = 0 #清空單筆交易細項 information = [] #如果到最後都沒賣出,最後一天強制賣出 if(stock == 1): get += round(stock * dataclose[i] * 1000 * 0.995575) information.append(str(datetime.date(data['Date'][len(K) - 1]).year) + '/' + str(datetime.date(data['Date'][len(K) - 1]).month) + '/' + str(datetime.date(data['Date'][len(K) - 1]).day) ) information.append('-1') information.append(dataclose[len(K) - 1]) information.append('+' + str(round(stock * dataclose[len(K) - 1] * 1000 * 0.995575))) information.append(get) #存入總交易目錄 trade.append(information) #售出清空持股 stock = 0 #清空單筆交易細項 information = [] # 支出 cost = 0 # 獲利 gain = 0 for i in range(1, len(trade)): if(float(trade[i][3]) < 0): cost += abs(int(trade[i][3])) elif(float(trade[i][3]) > 0): gain += int(trade[i][3]) print("Total Cost:", cost, "元") print("Total Gain:", gain, "元") print("Net Income:", gain - cost, "元") print("Rate of Retrun:", round(100 * (gain - cost) / cost, 2), "%") trade = pd.DataFrame(trade) trade ``` #### 測試範例: 牧德科技股份有限公司 ![](https://i.imgur.com/5D3MqUG.png) ## MACD 策略 當 MACD > SIGNAL LINE,則於該時間點買入 反之,若 MACD < SIGNAL LINE,則於該時間點賣出 > MACD 計算方法: 平滑異同移動平均線指標 = 快線(12日之指數移動平均EMA) - 慢線(26日之指數移動平均EMA) > SIGNAL LINE 計算方法: 9日之指數移動平均EMA :::warning 個人所使用之 MACD 及 SIGNAL LINE 為外國較常使用之定義,相較國內通行版本略有不同(主要為名詞定義差異),在此僅作為參考。 ::: ```python= import pandas as pd import numpy as np from datetime import datetime import matplotlib.pyplot as plt plt.style.use('fivethirtyeight') df = pd.read_csv('2327 國巨.csv') df = df.set_index(pd.DatetimeIndex(df['Date'].values)) # .mean() = function for column average # .ewm = exponential weight function ShortEMA = df.Close.ewm(span=12, adjust=False).mean() # Fast Moving Average LongEMA = df.Close.ewm(span=26, adjust=False).mean() # Slow Moving Average MACD = ShortEMA - LongEMA signal = MACD.ewm(span=9, adjust=False).mean() df['MACD'] = MACD df['Signal Line'] = signal flag = 0 def InvestStrategy(signal): BuyStock = [] SellStock = [] global flag # If MACD > Signal Line, Buy The Stock. If Not, Sell The Stock for i in range(0, len(signal)): if signal['MACD'][i] > signal['Signal Line'][i]: if flag == 0: BuyStock.append(signal['Close'][i]) SellStock.append(np.nan) flag = 1 else: BuyStock.append(np.nan) SellStock.append(np.nan) elif signal['MACD'][i] < signal['Signal Line'][i]: if flag == 1: SellStock.append(signal['Close'][i]) BuyStock.append(np.nan) flag = 0 else: BuyStock.append(np.nan) SellStock.append(np.nan) else: # Handling nan values BuyStock.append(np.nan) SellStock.append(np.nan) return (BuyStock, SellStock) temp = InvestStrategy(df) df['BuyStockPrice'] = temp[0] df['SellStockPrice'] = temp[1] TotalCost = np.sum(df['BuyStockPrice']) TotalEarn = np.sum(df['SellStockPrice']) if flag == 1: TotalEarn = TotalEarn + df['Close'].tail(1) NetValue = TotalEarn - TotalCost ReturnRate = round(100 * (TotalEarn - TotalCost) / TotalCost, 2) # Visualize The Strategy plt.figure(figsize=(12.2,4.5)) #width = 12.2in, height = 4.5 plt.plot(df.index, MACD, label='2327 STOCK', color = 'red') plt.plot(df.index, signal, label='SIGNAL LINE', color='blue') plt.xticks(rotation=45) plt.legend(loc='upper left') plt.show() title = 'Close Price History & Buy / Sell Signals ' my_stocks = df plt.figure(figsize=(12.2,4.5)) plt.scatter(my_stocks.index, my_stocks['BuyStockPrice'], color = 'green', label='Buy Signal', marker = '^', alpha = 1) plt.scatter(my_stocks.index, my_stocks['SellStockPrice'], color = 'red', label='Sell Signal', marker = 'v', alpha = 1) plt.plot(my_stocks['Close'], label='Close Price', alpha = 0.35) plt.xticks(rotation=45) plt.title(title) plt.ylabel('Close Price NTD ($)',fontsize=18) plt.legend(loc='upper left') plt.show() print("Total Cost:", TotalCost, "NTD") print("Total Earn:", TotalEarn, "NTD") print("Net Value:", NetValue, "NTD") print("Rate Of Return:", ReturnRate, "%") ``` ### 以「2327 國巨」作為此策略之參考資料 __MACD & SIGNAL LINE 指標__ ![](https://i.imgur.com/z1QyHoG.png) __收盤價及買賣點圖:__ ![](https://i.imgur.com/oicYZ0y.png) __評估投資效益:__ ![](https://i.imgur.com/lccsEZs.png) ## 雙RSI策略 ### 策略 > RSI長度調整 > * RSI=n日漲幅平均值÷(n日漲幅平均值+n日跌幅平均值)×100% * RSI長度:快線(5日)、慢線(14日) > 買賣訊號 * RSI快線向**上**穿破RSI慢線時**買進** * RSI快線向**下**穿破RSI慢線時**賣出** > 限制 * 設置多單(RSI<30)、多空區(RSI>70) * 設置最高買入價格、最低賣出價格 (以第一次進出場為基準) > 注意 * 最後一張一定要賣掉!!! ```python= import numpy as np import pandas as pd import mplfinance as mpf import matplotlib.pyplot as plt from datetime import datetime def rsi(close,n): change = close - close.shift(1) #上漲幅度,nan以0填滿 change_p = pd.Series(index = change.index,data = change[change>0]) change_p = change_p.fillna(0) #下跌幅度,nan以0填滿,取正值所以加-號 change_n = pd.Series(index = change.index,data = -change[change<0]) change_n = change_n.fillna(0) #平均漲幅 up_mean_n = [] #平均跌幅 down_mean_n = [] #第一天沒有漲跌幅,計算平均漲跌幅 for i in range(n+1,len(change)+1): up_mean_n.append(np.mean(change_p.values[i-n:i])) down_mean_n.append(np.mean(change_n.values[i-n:i])) #計算RSI rsi_n = [0]*n for i in range(len(up_mean_n)): rsi_n.append(100 * up_mean_n[i] / (up_mean_n[i] + down_mean_n[i])) rsi_n_series = pd.Series(index = close.index, data = rsi_n) return rsi_n_series #整理資料 dataname = "1234_2020.csv" SData = pd.read_csv(dataname, index_col = 'Date') SData.index = pd.DatetimeIndex(SData.index) #收盤價 close = SData.Close #呼叫函數計算RSI rsi_fast = rsi(close,5) rsi_slow = rsi(close,14) ''' 策略:RSI_fast向上穿破RSI_slow買進,RSI_fast向下穿破RSI_slow賣出 ''' #訊號 signal = [] #庫存 stock = 0 #成本、獲益 cost = 0 benifit = 0 #超買超賣區標準 b_std = 30 s_std = 70 #買更低,賣更貴 b_floor = 500 s_ceil = 0 #偵測RSI訊號 for i in range(len(close)): #buy if stock == 0: if (rsi_fast[i-1] < rsi_slow[i-1]) and (rsi_fast[i] > rsi_slow[i]) and (rsi_slow[i-1] < b_std) and (close[i] < b_floor): stock += 1 signal.append(1) cost = cost + close[i] * (1+0.001425) * 1000 b_floor = close[i] else: signal.append(0) #sell elif stock == 1: if (rsi_fast[i-1] > rsi_slow[i-1]) and (rsi_fast[i] < rsi_slow[i]) and (rsi_slow[i-1] > s_std) and (close[i] > s_ceil): stock -= 1 signal.append(-1) benifit = benifit + close[i] * (1-0.004425) * 1000 s_ceil = close[i] else: signal.append(0) #last trade if stock == 1: stock -= 1 signal[-1] = -1 benifit = benifit + close[-1] * (1-0.004425) *1000 #整理 rsi_sig = pd.Series(index = SData.index, data = signal) #output return_final = (benifit - cost) / cost print("Total Cost:",round(cost,2)) print("Total Benifit:",round(benifit,2)) print("Total Return:",round(return_final*100,2),"%") #plot from matplotlib import gridspec fig = plt.figure(figsize=(30,20)) gs = gridspec.GridSpec(2, 1, height_ratios=[2, 1]) ax0 = plt.subplot(gs[0]) ax0.plot(rsi_fast) ax0.plot(rsi_slow) ax0.legend(["RSI_fast"]+["RSI_slow"]) ax0.axhline(y=b_std, color='red') ax0.axhline(y=s_std, color='green') ax1 = plt.subplot(gs[1], sharex = ax0) ax1.plot(rsi_sig) plt.legend() plt.show() ``` ### 實際操作 * 以黑松(1234)為例 ![](https://i.imgur.com/CrW8NjB.png) Total Cost: 28690.83 Total Benifit: 34148.22 Total Return: 19.02 % ## ==實測最佳績效== ### ==3665 貿聯控股== ![](https://i.imgur.com/v6RzQZr.png) ![](https://i.imgur.com/tVGfMRx.png) ![](https://i.imgur.com/dytOOlt.png) ### ==4171 瑞基== ![](https://i.imgur.com/shDkMoN.png) ![](https://i.imgur.com/xiNShrY.png) ![](https://i.imgur.com/qLv2EH3.png) ### ==4743 合一== ![](https://i.imgur.com/IUDUzaH.png) ![](https://i.imgur.com/XEj61LD.png) ![](https://i.imgur.com/lzS8IA8.png) ## 總績效數據: ### KD 指標: > 1325 >![](https://i.imgur.com/jaSVJbN.png) > 1565 >![](https://i.imgur.com/8SqYy1T.png) > 2327 >![](https://i.imgur.com/hYW1Ivy.png) > 2408 >![](https://i.imgur.com/dxQLUh3.png) > 3008 >![](https://i.imgur.com/a9a3Vgj.png) > 3131 >![](https://i.imgur.com/AXbtvON.png) > 3563 >![](https://i.imgur.com/Gq8P2i8.png) > 3665 >![](https://i.imgur.com/IDX3HEp.png) > 4171 >![](https://i.imgur.com/uwGZqVB.png) > 4721 >![](https://i.imgur.com/CZV59LP.png) > 4743 >![](https://i.imgur.com/S5tzUIW.png) > 5536 >![](https://i.imgur.com/fIQRrkh.png) > 5904 >![](https://i.imgur.com/RcpDK3D.png) > 6452 >![](https://i.imgur.com/9Zahhoq.png) > 9958 >![](https://i.imgur.com/YUZ29sd.png) ### MACD 指標: > 1325 ![](https://i.imgur.com/oSQGf6w.png) > 1565 ![](https://i.imgur.com/8BkH1z2.png) > 2327 ![](https://i.imgur.com/XSRSeMC.png) > 2408 ![](https://i.imgur.com/aHoTfYT.png) > 3008 ![](https://i.imgur.com/SHhaT5U.png) > 3131 ![](https://i.imgur.com/K2jj9UO.png) > 3563 ![](https://i.imgur.com/s67Wnl9.png) > 3665 ![](https://i.imgur.com/tukIAMH.png) > 4171 ![](https://i.imgur.com/ufIWFgw.png) > 4721 ![](https://i.imgur.com/Ek0Q7A2.png) > 4743 ![](https://i.imgur.com/DAEe4uS.png) > 5536 ![](https://i.imgur.com/iS5LbJy.png) > 5904 ![](https://i.imgur.com/y9AXPNy.png) > 6452 ![](https://i.imgur.com/xSKPQRq.png) > 9958 ![](https://i.imgur.com/JofgXKz.png) ### RSI 指標: > 1325 Total Cost: 0 Total Benifit: 0 Total Return: 0 % > 1565 Total Cost: 0 Total Benifit: 0 Total Return: 0 % > 2327 Total Cost: 415591.38 Total Benifit: 386283.1 Total Return: -7.05 % > 2408 Total Cost: 81115.42 Total Benifit: 54159.28 Total Return: -33.23 % > 3008 Total Cost: 0 Total Benifit: 0 Total Return: 0 % > 3131 Total Cost: 646920.55 Total Benifit: 638163.58 Total Return: -1.35 % > 3563 Total Cost: 162731.56 Total Benifit: 176216.78 Total Return: 8.29 % > 3665 Total Cost: 0 Total Benifit: 0 Total Return: 0 % > 4171 Total Cost: 0 Total Benifit: 0 Total Return: 0 % > 4721 Total Cost: 217309.22 Total Benifit: 173230.05 Total Return: -20.28 % > 4743 Total Cost: 0 Total Benifit: 0 Total Return: 0 % > 5536 Total Cost: 318453.15 Total Benifit: 359900.36 Total Return: 13.02 % > 5904 Total Cost: 142202.35 Total Benifit: 182190.22 Total Return: 28.12 % > 6452 Total Cost: 269383.32 Total Benifit: 357411.42 Total Return: 32.68 % > 9958 Total Cost: 49069.82 Total Benifit: 65907.06 Total Return: 34.31 %