? ? 本人之前寫過若干“給程序員加財商”的系列文,目的是通過股票案例講述Python知識點,讓大家在學習Python的同時還能掌握相關的股票知識,所謂一舉兩得。
? ? 在之前的系列文里,大家能看到K線,均線,成交量的案例,在本文里,大家能看到通過RSI案例講述Python郵件編程的知識點,在后繼系列文里,大家還能看到MACD,BIAS,KDJ等指標相關案例。
1? RSI指標的原理和算法描述
? ? 相對強弱指標(RSI)是通過比較某個時段內單股價格的漲跌幅度來判斷多空雙方的強弱程度,以此來預測未來走勢。從數值上看,它體現出某股的買賣力量,所以投資者能據此預測未來價格的走勢,在實踐中,通常與移動平均線配合使用,以提高分析的準確性。
? ? RSI指標的計算公式如下所示。
? ? 第一步,RS(相對強度)=N日內收盤價漲數和的均值÷N日內收盤價跌數和的均值
? ? 第二步,RSI(相對強弱指標)=100-100÷(1+RS)
? ? 請注意,這里“均值“的計算方法可以是簡單移動平均(SMA),也可以是加權移動平均(WMA)指數移動平均(EMA)。本書采用的是比較簡單的簡單移動平均算法,有些軟件采用的是后兩種平均算法。采用不同的平均算法會導致RSI的值不同,但趨勢不會改變,對交易的指導意義也不會變。
? ? 以6日RSI指標為例,從當日算起向前推算6個交易日,獲取到包括本日在內的7個收盤價,用每一日的收盤價減去上一交易日的收盤價,以此方式得到6個數值,這些數值中有正有負。隨后再按如下四個步驟計算RSI指標。
? ? 第一步,up=6個數字中正數之和的平均值。
? ? 第二步,down=先取6個數字中負數之和的絕對值,再對絕對值取平均值。
? ? 第三步,RS=up除以down,RS表示相對強度
? ? 第四步,RSI(相對強弱指標)=100-100÷(1+RS)
? ? 如果再對第四步得出的結果進行數學變換,能進一步約去RS因素,得到如下的結論:RSI=100x(up) ÷(up+down),也就是說,RSI等于100乘以up除以(up和down的和)。
? ? 從本質上來看,RSI反映了某階段內(比如6個交易日內)由價格上漲引發的波動占總波動的百分比率,百分比越大,說明這個時間段內股票越強勢,反之如果百分比越小,則說明股票弱勢程度強。
? ? 從上述公式中我們能看到,RSI的值介于0到100之間,目前比較常見的基準周期為6日\12日和24日。把每個交易日的RSI值在坐標圖上的點連成曲線,即能繪制成RSI指標線,也就是說,目前滬深股市中RSI指標線是由三根曲線構成,如下圖所示。
2? 把用Matplotlib繪制的RSI指標圖存為圖片
? ? 在如下的DrawRSI.py案例中,我們將根據上述算法繪制600584(長電科技)從2018年9月到2019年5月間的的6日、12日和24日的RSI指標。
? ? 本例的數據來自csv文件,而該文件的數據來自網絡股票接口,相關內容大家可以閱讀之前博文。在本案例中,還會把由matplotlib生成的圖形存為png格式,以方便之后用郵件的形式發送。? ??
1 #!/usr/bin/env python 2 #coding=utf-8 3 import pandas as pd 4 import matplotlib.pyplot as plt 5 #計算RSI的方法,入參periodList傳入周期列表 6 def calRSI(df,periodList): 7 #計算和上一個交易日收盤價的差值 8 df['diff'] = df["Close"]-df["Close"].shift(1) 9 df['diff'].fillna(0, inplace = True) 10 df['up'] = df['diff'] 11 #過濾掉小于0的值 12 df['up'][df['up']] = 0 13 df['down'] = df['diff'] 14 #過濾掉大于0的值 15 df['down'][df['down']] = 0 16 #通過for循環,依次計算periodList中不同周期的RSI等值 17 for period in periodList: 18 df['upAvg'+str(period)] = df['up'].rolling(period).sum()/period 19 df['upAvg'+str(period)].fillna(0, inplace = True) 20 df['downAvg'+str(period)] = abs(df['down'].rolling(period).sum()/period) 21 df['downAvg'+str(period)].fillna(0, inplace = True) 22 df['RSI'+str(period)] = 100 - 100/((df['upAvg'+str(period)]/df['downAvg'+str(period)]+1)) 23 return df
? ? 在第5行里,我們定義了用于計算RSI值的calRSI方法,該方法第一個參數是包含日期收盤價等信息的dataframe類型的df對象,第二個參數是周期列表。
? ? 在第8行里,我們把本交易日和上個交易日收盤價的差價存入了'diff'列,這里是用shift(1)來獲取df里上一行(即上個交易日)的收盤價。由于第一行的diff值是NaN,所以需要用第9行的fillna方法把NaN值更新成0。
? ? 在第11行里,在df對象里創建了up列,該列的值暫時和diff值相同,有正有負,但馬上就通過第12行的df['up'][df['up']<0] = 0代碼,把up列中的負值設置成0,這樣一來,up列里就只包含了“N日內收盤價的漲數”。在第13行和第15行里,用同樣的方法,在df對象中創建了down列,并在其中存入了“N日內收盤價的跌數”。
? ? 隨后是通過第17行的for循環,遍歷存儲在periodList中的周期對象,其實通過下面第26行的代碼,我們能看到計算RSI的周期分別是6天、12天和24天。針對每個周期,先是在第18行,算出了這個周期內收盤價漲數和的均值,并把這個均值存入df對象中的'upAvg'+str(period)列中,比如當前周期是6,那么該漲數的均值是存入df[‘upAvg6‘]列。在第20行,則算出該周期內的收盤價跌數的均值,并存入'downAvg'+str(period)列中。最后在第22行,算出本周期內的RSI值,并放入df對象中的'RSI'+str(period)里。?
24 filename='D:\\stockData\ch10\\6005842018-09-012019-05-31.csv'
25 df = pd.read_csv(filename,encoding='gbk'
)
26 list = [6,12,24] #周期列表
27 #調用方法計算RSI
28 stockDataFrame =
calRSI(df,list) 29 #print(stockDataFrame) 30 #開始繪圖 31
plt.figure() 32 stockDataFrame['RSI6'].plot(color="blue",label='RSI6'
) 33 stockDataFrame['RSI12'].plot(color="green",label='RSI12'
) 34 stockDataFrame['RSI24'].plot(color="purple",label='RSI24'
) 35 plt.legend(loc='best') #繪制圖例 36 #設置x軸坐標標簽和旋轉角度 37 major_index=stockDataFrame.index[stockDataFrame.index==
0] 38 major_xtics=stockDataFrame['Date'][stockDataFrame.index==
0] 39
plt.xticks(major_index,major_xtics) 40 plt.setp(plt.gca().get_xticklabels(), rotation=30
) 41 #帶網格線,且設置了網格樣式 42 plt.grid(linestyle='-.'
) 43 plt.title("RSI效果圖"
) 44 plt.rcParams['font.sans-serif']=['SimHei'
] 45 plt.savefig('D:\\stockData\ch10\\6005842018-09-012019-05-31.png'
) 46 plt.show()
? ? ?在第25行里,我們從指定csv文件里得到了包含日期收盤價等信息的數據,并在第26行指定了三個計算周期。在第28行里,我們調用了calRSI方法計算了三個周期的RSI值,并存入stockDataFrame對象,當前第29行的輸出語句是注釋掉的,在打開后,大家能看到計算后的結果值,其中包含upAvg6、downAvg6和RSI6等列。
? ? 在得到RSI數據后,從第31行開始繪圖,其中比較重要的步驟是通過第32行到第34行的代碼,用plot方法繪制三根曲線,隨后通過第35行的legend方法設置圖例,通過第37行和第38行的代碼設置x軸刻度的文字以及旋轉效果,通過第42行的代碼設置網格樣式,通過第43的代碼設置標題。
? ? 在第46行通過show方法繪圖前,我們通過第45行的代碼,用savefig方法把圖形保存到了指定目錄,請注意這句話需要放在show方法前,否則保存的圖片就會是空的。
? ? 運行上述代碼,能看到如下圖所示的RSI效果圖。需要說明的是,由于本例在計算收盤價漲數和均值和收盤價跌數和均值時,用的是簡單移動平均算法,所以繪制出來的圖形可能和一些軟件里的不一致,但趨勢相同。而且,在指定目錄里,能看到png圖片。
3? 整合K線后用郵件發送
? ??在DrawKwithRSI.py代碼里,我們將完成如下三個工作,第一,計算6日、12日和24日的RSI值。第二,繪制K線加均線加RSI指標圖,并把結果保存為png格式圖片。第三,發送郵件,并把png圖片以富文本的格式展示在郵件正文中。
1 # !/usr/bin/env python 2 # coding=utf-8 3 import pandas as pd 4 import matplotlib.pyplot as plt 5 from mpl_finance import candlestick2_ochl 6 from matplotlib.ticker import MultipleLocator 7 import smtplib 8 from email.mime.text import MIMEText 9 from email.mime.image import MIMEImage 10 from email.mime.multipart import MIMEMultipart 11 # 計算RSI的方法,入參periodList傳入周期列表 12 def calRSI(df,periodList): 13 和DrawRSI.py案例中的一致? ??
? ? 從第3行到第10行,我們引入了相關的庫文件,第12行定義的calRSI方法和之前案例中的完全一致,所以就不再給出代碼。? ??
14 filename='D:\\stockData\ch10\\6005842018-09-012019-05-31.csv' 15 df = pd.read_csv(filename,encoding='gbk') 16 list = [6,12,24] #周期列表 17 #調用方法計算RSI 18 stockDataFrame = calRSI(df,list) 19 figure = plt.figure() 20 #創建子圖 21 (axPrice, axRSI) = figure.subplots(2, sharex=True) 22 #調用方法,在第axPrice子圖里繪制K線圖 23 candlestick2_ochl(ax = axPrice, opens=df["Open"].values, closes=df["Close"].values, highs=df["High"].values, lows=df["Low"].values,width=0.75, colorup='red', colordown='green') 24 axPrice.set_title("K線圖和均線圖")#設置子圖標題 25 stockDataFrame['Close'].rolling(window=3).mean().plot(ax=axPrice,color="red",label='3天均線') 26 stockDataFrame['Close'].rolling(window=5).mean().plot(ax=axPrice,color="blue",label='5天均線') 27 stockDataFrame['Close'].rolling(window=10).mean().plot(ax=axPrice,color="green",label='10天均線') 28 axPrice.legend(loc='best') #繪制圖例 29 axPrice.set_ylabel("價格(單位:元)") 30 axPrice.grid(linestyle='-.') #帶網格線 31 #在axRSI子圖里繪制RSI圖形 32 stockDataFrame['RSI6'].plot(ax=axRSI,color="blue",label='RSI6') 33 stockDataFrame['RSI12'].plot(ax=axRSI,color="green",label='RSI12') 34 stockDataFrame['RSI24'].plot(ax=axRSI,color="purple",label='RSI24') 35 plt.legend(loc='best') #繪制圖例 36 plt.rcParams['font.sans-serif']=['SimHei'] 37 axRSI.set_title("RSI圖")#設置子圖的標題 38 axRSI.grid(linestyle='-.') #帶網格線 39 #設置x軸坐標標簽和旋轉角度 40 major_index=stockDataFrame.index[stockDataFrame.index%7==0] 41 major_xtics=stockDataFrame['Date'][stockDataFrame.index%7==0] 42 plt.xticks(major_index,major_xtics) 43 plt.setp(plt.gca().get_xticklabels(), rotation=30) 44 plt.savefig('D:\\stockData\ch10\\600584RSI.png')
? ? 在第18行里,我們通過調用calRSI方法得到了三個周期的RSI數據。在第21行里,設置了axPrice和axRSI這兩個子圖共享x軸標簽,在第23行里繪制了K線圖,在第25行到第27行里,繪制了3日、5日和10日的均線,在第32行到第34行里,繪制了6日、12日和24日的三根RSI指標圖。在第44行里通過savefig方法,把包含K線、均線和RSI指標線的圖形存在指定目錄中。?? ??
45 #發送郵件 46 def sendMail(username,pwd,from_addr,to_addr,msg): 47 和之前sendMailWithPicAttachment.py案例中的一致 48 def buildMail(HTMLContent,subject,showFrom,showTo,attachfolder,attachFileName): 49 message = MIMEMultipart() 50 body = MIMEText(HTMLContent, 'html', 'utf-8') 51 message.attach(body) 52 message['Subject'] = subject 53 message['From'] = showFrom 54 message['To'] = showTo 55 imageFile = MIMEImage(open(attachfolder+attachFileName, 'rb').read()) 56 imageFile.add_header('Content-ID', attachFileName) 57 imageFile['Content-Disposition'] = 'attachment;filename="'+attachFileName+'"' 58 message.attach(imageFile) 59 return message
第46行定義的sendMail方法和之前案例中的完全一致,所以就不給出詳細的代碼。本案例與之前不同的是,在第48行里,專門定義了buildMail方法,用來組裝郵件對象,郵件的諸多要素由該方法的參數所定義。
? ? 具體而言,在第49行里定義郵件類型是MIMEMultipart,也就是說帶附件的郵件,在第50行和第51行里,根據參數HTMLContent構建了郵件的正文,在第52行到第54行里,設置了郵件的相關屬性值,從第55行到第57行,根據入參構建了MIMEImage類型的圖片類附件,在第58行里,通過attach方法把附件并入郵件正文。? ??
60 subject='RSI效果圖' 61 attachfolder='D:\\stockData\\ch10\\' 62 attachFileName='600584RSI.png' 63 HTMLContent = ''\ 64 ''\ 65 '' 66 message = buildMail(HTMLContent,subject,'hsm_computer@163.com','hsm_computer@163.com',attachfolder,attachFileName) 67 sendMail('hsm_computer','xxx','hsm_computer@163.com','hsm_computer@163.com',message.as_string()) 68 #最后再繪制 69 plt.show()
在第60行到第66行,我們設置了郵件的相關屬性值,并在第66行里,通過調用buildMail方法創建了郵件對象message,在第第67行里,通過調用sendMail方法發送郵件,最后在第69行通過show方法繪制了圖形。請大家注意本案例中的三個細節。
? ? 第一,?????????? 第64行cid的值需要和第56行的Content-ID值一致,否則圖片只能以附件的形式發送,無法在郵件正文里以富文本的格式展示。
? ? 第二,?????????? 我們是先構建和發送郵件,再通過第69行的代碼繪制圖形,如果次序顛倒先繪制圖形后發送郵件的話,show方法被調用后程序會阻塞在這個位置,代碼無法繼續執行。要等到手動關閉掉由show方法彈出的窗口后,才會觸發sendMail方法發送郵件。
? ? 第三,?????????? 在本案例的第48行,我們專門封裝了用于構建郵件對象的buildMail方法,在其中通過參數動態地構建郵件,這樣如果要發送其它郵件,則可以調用該方法,從而能提升代碼的重用性。
? ? 運行上述代碼,我們能在彈出的窗口里看到K線、均線和RSI指標圖整合后的效果圖,而且能在郵件的正文里看到相同的圖。
4??RSI指標對買賣點的指導意義
? ??一般來說,我們把6日、12日和24日的RSI指標稱為為短期、中期和長期指標。和KDJ指標一樣,RSI指標也有超買和超賣區。
? ? 具體而言,當RSI值在50到70間波動時,表示當前屬于強勢狀態,如繼續上升,超過80時,則到超買區,極可能在短期內轉升為跌。反之RSI值在20到50之間時,說明當前市場處于相對弱勢,如下降到20以下,則進入超賣區,股價可能出現反彈。
? ? 在講述RSI交易策略前,我們先來講述下在實際操作中總結出來的RSI指標的缺陷。
? ? 第一,周期較短(比如6日)的RSI指標比較靈敏,但快速震蕩的次數較多,可靠性相對差些,而周期較長(比如24日)的RSI指標可靠性強,但靈敏度不夠,經常會“滯后”的情況。
? ? 第二,當數值在40到60間波動時,往往參考價值不大,具體而言,當數值向上突破50臨界點時,表示股價已轉強,反之向下跌破50時則表示轉弱,不過在實踐過程中,經常會出現RSI跌破50后股價卻不下跌,而突破50后股價不漲。
? ? 綜合RSI算法、相關理論以及缺陷,我們來講述下實際操作中常用的基于該指標的賣策略。
? ? 第一,RSI短期指標(6日)在20以下超賣區與中長期RSI(12日或24日)發生黃金交叉,即6日線上穿12日或24日線,則說明即將發生反彈行情,如果其它技術或政策等方面沒太大問題,可以適當買進。
? ? 第二,反之,RSI短期指標(6日)在80以上超買區與中長期RSI(12日或24日)發生死亡交叉,即6日線下穿12日或24日線,則說明可能會出現高位反轉的情況,如果沒有其它利好消息,可以考慮賣出。
5?計算賣點,以郵件的形式發送
? ? 根據上文描述,這里采用的基于RSI的買點策略是,RSI6日線在20以下與中長期RSI(12日或24日)發生黃金交叉。在如下的calRSIBuyPoints.py案例中,我們據此策略計算600584(長電科技)從2018年9月到2019年5月間的賣點,而且通過郵件發送買點日期。? ? ?
1 #!/usr/bin/env python 2 #coding=utf-8 3 import pandas as pd 4 import smtplib 5 from email.mime.text import MIMEText 6 from email.mime.image import MIMEImage 7 from email.mime.multipart import MIMEMultipart 8 #計算RSI的方法,入參periodList傳入周期列表 9 def calRSI(df,periodList): 10 和DrawRSI.py案例中的一致,省略相關代碼 11 filename='D:\\stockData\ch10\\6005842018-09-012019-05-31.csv' 12 df = pd.read_csv(filename,encoding='gbk') 13 list = [6,12,24] #周期列表 14 #調用方法計算RSI 15 stockDataFrame = calRSI(df,list)
? ? 在上述代碼的第15行里,我們通過調用calRSI方法計算RSI指標值,這部分和10.3.2部分的calRSIBuyPoints.py相關代碼非常相似,所以就不再重復說明。? ?
16 cnt=0 17 sellDate='' 18 while cnt<=len(stockDataFrame)-1: 19 if(cnt>=30):#前幾天有誤差,從第30天算起 20 try: 21 #規則1,這天RSI6高于80 22 if stockDataFrame.iloc[cnt]['RSI6']<80: 23 #規則2.1,當天RSI6下穿RSI12 24 if stockDataFrame.iloc[cnt]['RSI6']stockDataFrame.iloc[cnt-1]['RSI12']: 25 sellDate = sellDate+stockDataFrame.iloc[cnt]['Date'] + ',' 26 #規則2.2,當天RSI6下穿RSI24 27 if stockDataFrame.iloc[cnt]['RSI6'] stockDataFrame.iloc[cnt-1]['RSI24']: 28 if sellDate.index(stockDataFrame.iloc[cnt]['Date']) == -1: 29 sellDate = sellDate+stockDataFrame.iloc[cnt]['Date'] + ',' 30 except: 31 pass 32 cnt=cnt+1 33 print(sellDate)
? ? 在第18行到第32行的while循環里,我們計算了基于RSI的賣點,在第22行的語句里,我們制定了第一個規則:RSI6數值大于80,在第23行和第27行,我們在規則一的基礎上制定了兩個并行的規則,通過上述代碼,我們會在sellDate對象里存放RSI6大于80并且RSI6下穿RSI12(或RSI24)的交易日,這些交易日即為賣點。? ??
34 def sendMail(username,pwd,from_addr,to_addr,msg): 35 和之前calRSIBuyPoints.py案例中的完全一致 36 def buildMail(HTMLContent,subject,showFrom,showTo,attachfolder,attachFileName): 37 和之前calRSIBuyPoints.py案例中的完全一致 38 subject='RSI賣點分析' 39 attachfolder='D:\\stockData\\ch10\\' 40 attachFileName='600584RSI.png' 41 HTMLContent = ''\ 42 '賣點日期' + sellDate + \ 43 ''\ 44 '' 45 message = buildMail(HTMLContent,subject,'hsm_computer@163.com','hsm_computer@163.com',attachfolder,attachFileName) 46 sendMail('hsm_computer','xxx','hsm_computer@163.com','hsm_computer@163.com',message.as_string())
第34行和第36行的兩個用于發送郵件和構建構建的方法和之前案例的完全一致,所以就不再額外說明。
? ?在第38行我們定義的郵件標題是“RSI賣點分析”,在第41行定義的描述正文的HTMLContent對象里,我們存放的也是“賣點日期”,最終是通過第46行調用sendMail方法發送郵件。
? ? 運行上述代碼,我們能看到如下圖所示的郵件,其中包括了賣點日期和指標圖。這里通過計算得出的賣點日期比較多,經分析,這些日期之后,股價多有下跌情況。
?
6 總結和版權說明
? ? 本文是給程序員加財商系列,之前的系列文如下:
? ??以預測股票漲跌案例入門基于SVM的機器學習 ? ?
? ?? 用python的matplotlib和numpy庫繪制股票K線均線和成交量的整合效果(含量化驗證交易策略代碼) ? ?
? ? 有不少網友轉載和想要轉載我的博文,本人感到十分榮幸,這也是本人不斷寫博文的動力。關于本文的版權有如下統一的說明,抱歉就不逐一回復了。
? ? 1 本文可轉載,無需告知,轉載時請用 鏈接 的方式,給出原文出處,別簡單地通過文本方式給出,同時寫明原作者是hsm_computer。
? ?? 2 在轉載時,請原文轉載 ,謝絕洗稿。否則本人保留追究法律責任的權利。
?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
