# 股票投資分析期末報告
###### 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 %