01
引言
不少喜歡量化的讀者向我反饋,雖然已經(jīng)掌握了Python的編程基礎,但仍不知道如何切入到股票量化分析上,一是對如何獲取股票數(shù)據(jù)還不太熟悉;二是拿到股票數(shù)據(jù)后不知道怎么做量化回測。實際上公眾號分享了很多這方面的文章,可以作為參考模板的包括:《【手把手教你】Python獲取交易數(shù)據(jù)》、《【手把手教你】搭建自己的量化分析數(shù)據(jù)庫》、《【手把手教你】Python面向對象編程入門及股票數(shù)據(jù)管理應用實例》;《手把手教你用Python搭建自己的量化回測框架【均值回歸策略】》、《【手把手教你】用Python量化海龜交易法則》、《A股存在月份效應嗎?構建月度擇時策略【附Python源碼】》、《北向資金能預示大盤漲跌?【附Python源碼】》,專業(yè)量化回測框架可以參考backtrader的系列文章(如【手把手教你】入門量化回測最強神器backtrader(一))。目前公眾號文章主要是以tusahre來獲取數(shù)據(jù),實際上Python可用的獲取數(shù)據(jù)api還是很多的,如開源的有akshare、baostock、pandas_datareader(國內外數(shù)據(jù)豐富,但外網(wǎng)訪問常連接不上)、alpha_vantage(國外)、quandl(國外)、yfinance(原雅虎財經(jīng),外網(wǎng)訪問常連接不上);非開源的有WindPy(Wind付費插件)、恒有數(shù)hs_udata(恒生云)、聚寬JQData(可試用)等等。關于這些庫或數(shù)據(jù)api的詳細介紹大家可以直接百度進入官網(wǎng)了解,此處不做進一步展開,本文主要介紹如何使用開源數(shù)據(jù)包獲取數(shù)據(jù)并進行量化回測,實現(xiàn)最簡單的代碼輸出專業(yè)的分析圖表。
02
數(shù)據(jù)獲取
下面分別對tusahre、tushare pro、akshare和baostock四個當前較流行的開源數(shù)據(jù)包構建統(tǒng)一參數(shù)的數(shù)據(jù)獲取函數(shù),比較程序編寫的復雜程度和獲取數(shù)據(jù)所需時間。
先導入pandas、matplotlib等常用包。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
#正常顯示畫圖時出現(xiàn)的中文和負號
from pylab import mpl
mpl.rcParams['font.sans-serif']=['SimHei']
mpl.rcParams['axes.unicode_minus']=False
#不顯示警告信息
import warnings
warnings.filterwarnings('ignore')
構建統(tǒng)一參數(shù)的數(shù)據(jù)獲取函數(shù),復權、起始和結束時間設置為默認參數(shù),注意tushare pro需要注冊獲取token(
https://tushare.pro/register?reg=218422)才能使用,某些數(shù)據(jù)權限受積分限制,其舊版非常接口簡潔好用但不再維護。akshare相當于連接全網(wǎng)開放數(shù)據(jù)源api的數(shù)據(jù)網(wǎng)站(如新浪財經(jīng)、同花順、東方財富等),某些函數(shù)由于包更新不可用。basostock無需注冊且免費,但函數(shù)調用稍顯麻煩。
#使用tushare舊版獲取數(shù)據(jù)
import tushare as ts
def get_from_tushare(code,adj='hfq',start='2010-01-01',end='2021-11-05'):
df=ts.get_k_data(code,autype=adj,start=start,end=end)
df.index=pd.to_datetime(df.date)
#原數(shù)據(jù)已默認按日期進行了排序
return df
#使用tushare pro獲取數(shù)據(jù)
import tushare as ts
token='輸入你自己的token'
pro=ts.pro_api(token)
ts.set_token(token)
def get_from_tushare_pro(code,adj='hfq',start='2010-01-01',end='2021-11-05'):
#code:輸入數(shù)字字符串,如‘300002’
#start和end輸入'年-月-日'需轉為'年月日'格式
if code.startswith('6'):
code=code+'.SH'
else:
code=code+'.SZ'
start=''.join(start.split('-'))
end=''.join(end.split('-'))
df=ts.pro_bar(ts_code=code,adj=adj,start_date=start,end_date=end)
#原數(shù)據(jù)是倒序的,所以將時間設置為索引,根據(jù)索引重新排序
df.index=pd.to_datetime(df.trade_date)
df=df.sort_index()
return df
#使用akshare獲取數(shù)據(jù),其數(shù)據(jù)源來自新浪,與tushare舊版本相似
import akshare as ak
def get_from_akshare(code,adj='hfq',start='2010-01-01',end='2021-11-05'):
if code.startswith('6'):
code='sh'+code
else:
code='sz'+code
start=''.join(start.split('-'))
end=''.join(end.split('-'))
df = ak.stock_zh_a_daily(symbol=code, start_date=start, end_date=end, adjust=adj)
return df
#使用baostock獲取數(shù)據(jù)
import baostock as bs
def get_from_baostock(code,adj='hfq',start='2010-01-01',end='2021-11-05'):
if code.startswith('6'):
code='sh.'+code
else:
code='sz.'+code
#轉換復權為數(shù)字
if adj=='hfq':
adj='1'
elif adj=='qfq':
adj='2'
else:
adj='3'
#必須登陸和登出系統(tǒng)
bs.login() #登陸系統(tǒng)
rs = bs.query_history_k_data_plus(code,
fields="date,code,open,high,low,close,volume",
start_date=start, end_date=end,
frequency="d", adjustflag=adj)
#adjustflag:復權類型,默認不復權:3;1:后復權;2:前復權
data_list = []
while (rs.error_code == '0') & rs.next():
data_list.Append(rs.get_row_data())
#將數(shù)據(jù)轉為dataframe格式
df = pd.DataFrame(data_list, columns=rs.fields)
df.index=pd.to_datetime(df.date)
bs.logout() #登出系統(tǒng)
return df
下面分別使用這四個api接口獲取數(shù)據(jù)并比較耗用時間,結果顯示,tushare pro和akshare耗用時間均較短,獲取‘300002’于2010.1-2021.11期間數(shù)據(jù)只需0.6-0.8秒,tushare舊版接口代碼最簡潔,但需1.05秒,而baostock則需1.6,當然結果會受網(wǎng)絡狀態(tài)影響。
func={'tushare':get_from_tushare,
'tushare pro':get_from_tushare_pro,
'akshare':get_from_akshare,
'baostock':get_from_baostock}
#以個股神州泰岳為例
code='300002'
from time import time
data=pd.DataFrame()
for name,f in func.items():
t0=time()
data[name]=f(code).close
t1=time()
print(f'{name}耗時:{t1-t0:.04f}秒')
輸出結果:
tushare耗時:1.0488秒
tushare pro耗時:0.6780秒
akshare耗時:0.7795秒
login success!
logout success!
baostock耗時:1.6439秒
比較四個api獲取的“300002”后復權價格數(shù)據(jù),結果顯示四個接口得到的數(shù)據(jù)存在一些差別,其中tushare和baostock數(shù)據(jù)接近,二者與akshare差別較大,可能是計算方式存在差異。由于采用后復權,隨著時間拉長,分紅次數(shù)增加,差異越來越大。
data.tail()
(data['tushare pro']-data['akshare']).plot(figsize=(12,5),c='r');

03
策略回測
下面以tushare舊版接口為例,獲取數(shù)據(jù)并基于技術指標進行量化回測。圖中顯示,“300002”的股價在2010.1.4-2021.11.5期間波動非常大,累計收益率-14.5%,年化收益率-1.4%,年化波動率53.8%,最大回撤高達89%,日VaR為-6.7%。換句話說,如果從2010年1月4日開始持有該股票,至2021年11月5日將虧損14.5%,但如果是在其最高點買入最大虧損為89%;當然,如果你是在2012年12月買入,持有到2015年6月21日,股價差不多增長了10倍?,F(xiàn)實的情況往往是,大量股民像韭菜一樣在股價不斷波動的過程中被收割。
df=get_from_tushare('300002')
df.close.plot(figsize=(12,6));
plt.title('神州泰岳股價走勢n2010-2021',size=15)

import pyfolio as pf
pf.create_simple_tear_sheet((df.close.pct_change()).fillna(0).tz_localize('UTC'))


下面以趨勢指標ADX結合均線和macD指標構建交易策略進行量化回測。ADX是一種常用的趨勢衡量指標,指標值越大代表趨勢越強,但指標本身無法告訴你趨勢的發(fā)展方向,與均線和MACD指標配合運用,可以確認市場是否存在趨勢,并衡量趨勢的強度。下面以13、55、89日均線(斐波那契數(shù)列),MACD(12,26,9)和ADX(閾值設置為前值和25)指標為例,得到下列回測結果。
#技術分析包import talib as ta
def adx_strategy(df,ma1=13,ma2=55,ma3=89,adx=25):
#計算MACD和ADX指標
df['EMA1'] = ta.EMA(df.close,ma1)
df['EMA2'] = ta.EMA(df.close,ma2)
df['EMA3'] = ta.EMA(df.close,ma3)
df['MACD'],df['MACDSignal'],df['MACDHist'] = ta.MACD(df.close,12,26,9)
df['ADX'] = ta.ADX(df.high,df.low,df.close,14)
#設計買賣信號:21日均線大于42日均線且42日均線大于63日均線;ADX大于前值小于25;MACD大于前值
df['Buy_Sig'] =(df['EMA1']>df['EMA2'])&(df['EMA2']>df['EMA3'])&(df['ADX']<=adx)
&(df['ADX']>df['ADX'].shift(1))&(df['MACDHist']>df['MACDHist'].shift(1))
df.loc[df.Buy_Sig,'Buy_Trade'] = 1
df.loc[df.Buy_Trade.shift(1)==1,'Buy_Trade'] = " "
#避免最后三天內出現(xiàn)交易
df.Buy_Trade.iloc[-3:] = " "
df.loc[df.Buy_Trade==1,'Buy_Price'] = df.close
df.Buy_Price = df.Buy_Price.ffill()
df['Buy_Daily_Return']= (df.close - df.Buy_Price)/df.Buy_Price
df.loc[df.Buy_Trade.shift(3)==1,'Sell_Trade'] = -1
df.loc[df.Sell_Trade==-1,'Buy_Total_Return'] = df.Buy_Daily_Return
df.loc[(df.Sell_Trade==-1)&(df.Buy_Daily_Return==0),'Buy_Total_Return'] =
(df.Buy_Price - df.Buy_Price.shift(1))/df.Buy_Price.shift(1)
df.loc[(df.Sell_Trade==-1)&(df.Buy_Trade.shift(1)==1),'Buy_Total_Return'] =
(df.close-df.Buy_Price.shift(2))/df.Buy_Price.shift(2)
#返回策略的日收益率
return df.Buy_Total_Return.fillna(0)
回測結果顯示,使用該交易策略可獲得11.3%的年化收益率,累計收益率221.5%,最大回撤-24.2%,夏普比率0.63。使用策略后各項指標得到有效改善。
import pyfolio as pf
pf.create_simple_tear_sheet(adx_strategy(df).tz_localize('UTC'))


04
結語
對于tushare、tushare pro、akshare、baostock開源數(shù)據(jù)api,本文構建了統(tǒng)一參數(shù)的數(shù)據(jù)獲取函數(shù),獲取個股后復權數(shù)據(jù)并以技術指標ADX結合均線和MACD構建交易策略進行量化回測。本文旨在為大家利用開源數(shù)據(jù)進行量化分析提供一個思路和模板,各位讀者可以在此基礎上結合自身對市場的理解進行深入拓展。值得注意的是,文中提及的交易策略僅供學習參考,切勿直接拿來做真實交易。實際上,文中對‘300002’單個標的進行回測得到結果不代表該交易策略就有效,可能正好適合該股票和該段時間走勢,任何基于技術指標的交易策略均具有一定的局限性,具體問題具體分析。