研报复现系列(六)【国泰君安】基于CCK模型的股票市场羊群效应研究
前言
我們是國內(nèi)普通高校的在校學(xué)生,同時(shí)也是量化投資的初學(xué)者。我們的學(xué)校不是清北復(fù)交,也沒有金融工程實(shí)驗(yàn)室,同時(shí)地處三線小城,因此我們在校期間較難獲得量化實(shí)習(xí)機(jī)會,但我們期待與業(yè)界進(jìn)行溝通、交流。
蔡金航同學(xué)是我們其中的一員。其在尋找暑期量化實(shí)習(xí)時(shí),收到了幾家私募和券商金工組的筆試邀請,筆試內(nèi)容皆為在給定時(shí)間內(nèi)復(fù)現(xiàn)出一篇金工研報(bào)。蔡同學(xué)受到啟發(fā),發(fā)覺復(fù)現(xiàn)金工研報(bào)是我們學(xué)習(xí)量化策略、鍛煉程序設(shè)計(jì)能力同時(shí)也是與業(yè)界交流的很好的途徑。
在蔡同學(xué)的建議下,我們開啟研報(bào)復(fù)現(xiàn)系列的創(chuàng)作,記錄我們的學(xué)習(xí)過程,并將我們的創(chuàng)作內(nèi)容分享出來,與讀者們一起交流、學(xué)習(xí)、進(jìn)步。
我們的水平有限,創(chuàng)作的內(nèi)容難免會有錯(cuò)誤或不嚴(yán)謹(jǐn)?shù)膬?nèi)容,我們歡迎讀者的批評指正。
如果您對我們的內(nèi)容感興趣,請聯(lián)系我們:cai_jinhang@foxmail.com
本文作者:
徐鵬宇 山東大學(xué)(威海)數(shù)學(xué)與統(tǒng)計(jì)學(xué)院 pengyu_xu0607@163.com
1.研報(bào)概述
研究目的
參考國泰君安證券研報(bào)《20181128-基于CCK模型的股票市場羊群效應(yīng)研究》,對市場上存在的個(gè)別股票的漲跌引起相關(guān)股票收益率聯(lián)動(dòng)的現(xiàn)象進(jìn)行分析探究。根據(jù)研報(bào)構(gòu)建CCK模型,并進(jìn)行改良,尋找更多聯(lián)動(dòng)信號,并正確分析市場趨勢。
研究思路
1、計(jì)算市場回報(bào)率并進(jìn)行改良
2、確定羊群效應(yīng)顯著性水平的衡量標(biāo)準(zhǔn)
3、選取研報(bào)中若干數(shù)據(jù)進(jìn)行回測
2.研究環(huán)境
JointQuant
Tushare
因?yàn)閿?shù)據(jù)接口不同,兩大平臺在獲取一些數(shù)據(jù)各有優(yōu)勢。本人第一次進(jìn)行研報(bào)復(fù)現(xiàn),混用兩種數(shù)據(jù)接口,擇優(yōu)使用
from numpy.core.fromnumeric import product from numpy.core.numeric import NaN from jqdatasdk import * import jqdatasdk as jq import tushare as ts import pandas as pd import numpy as np import matplotlib.pyplot as plt import matplotlib.patches as mpatches from datetime import * from pandas.io.pytables import performance_doc import statsmodels.api as sm from sklearn.preprocessing import scale import warnings warnings.filterwarnings('ignore') plt.rcParams['font.sans-serif'] = ['SimHei'] # 用來正常顯示中文標(biāo)簽 plt.rcParams['axes.unicode_minus'] = False # 用來正常顯示負(fù)號3.研報(bào)復(fù)現(xiàn)
3.1數(shù)據(jù)獲取
本文數(shù)據(jù)來源于聚寬JoinQuant以及Tushare。用到的接口名和功能如下表所示。具體參數(shù)請查閱官方API文檔。
| get_price | 獲取數(shù)據(jù)、是否停牌、漲跌停價(jià) |
| get_trade_days | 獲取一段時(shí)間內(nèi)的交易日列表 |
| get_extras | 查詢特定股票是否是ST股 |
| get_index_stocks | 獲取指數(shù)成分股 |
| get_all_securities | 獲取某日所有股票 |
| Tushare接口 | 功能 |
| daily | 獲取股票單日數(shù)據(jù) |
| trade_cal | 獲取交易日列表 |
3.2構(gòu)建市場回報(bào)率
對于接下來要使用的A股綜合市場回報(bào)率,研報(bào)中使用的為國泰安數(shù)據(jù)庫,我們考慮用免費(fèi)獲得的數(shù)據(jù)進(jìn)行代替。
流通市值加權(quán)平均
首先,我們想到的是使用流通市值加權(quán)平均的方法計(jì)算A股市場總收益率:
上證A股指數(shù)收益率上證A股總流通市值占比
+
深證A股指數(shù)收益率深證A股總流通市值占比
總流通市值為當(dāng)日指數(shù)成分股流通市值之和
def get_CriValue(exchange_code, start_date, end_date, count=3000):'''獲取流通市值參數(shù):exchange_code 指數(shù)代碼,start_date 開始日期end_date 結(jié)束日期count 最多返回值個(gè)數(shù),是聚寬規(guī)定的一次獲取最多個(gè)數(shù)返回值:dataframe類型,每日流通市值'''CriValue = jq.finance.run_query(query(finance.STK_EXCHANGE_TRADE_INFO.circulating_market_cap,finance.STK_EXCHANGE_TRADE_INFO.date).filter(jq.finance.STK_EXCHANGE_TRADE_INFO.exchange_code==exchange_code,jq.finance.STK_EXCHANGE_TRADE_INFO.date<=end_date,jq.finance.STK_EXCHANGE_TRADE_INFO.date>=start_date).limit(count))CriValue.set_index('date', inplace=True)return CriValue # 計(jì)算2017年一年的Rm SH_CriValue = get_CriValue('322001', '2017-01-01', '2017-12-31') SZ_CriValue = get_CriValue('322004', '2017-01-01', '2017-12-31') ###運(yùn)用流通市值加權(quán)平均法計(jì)算Rm### sz = pd.DataFrame(SZ_CriValue.circulating_market_cap) sh = pd.DataFrame(SH_CriValue.circulating_market_cap) real_market = pd.concat([sz,sh],axis=1) real_market.columns = ['深證','上證'] real_market.index = SH_CriValue.index real_market['all'] = real_market['深證'] + real_market['上證'] # print(real_market.tail()) SH_market_return = jq.get_price('000001.XSHG',start_date='2017-01-01',end_date='2017-12-31',fields='close').pct_change(1).fillna(0) SZ_market_return = jq.get_price('399106.XSHE',start_date='2017-01-01',end_date='2017-12-31',fields='close').pct_change(1).fillna(0) real_market['R深證'],real_market['R上證'] = SZ_market_return,SH_market_return Rm = (real_market['深證']/real_market['all'] * real_market['R深證'])+(real_market['上證']/real_market['all'] * real_market['R上證'])由于該方法構(gòu)建的市場收益率占用內(nèi)存過高,需要很多次查詢,新手賬號暫且沒有這么多積分,所以尋求更簡單的方法。
簡化的綜合市場回報(bào)率
去掉加權(quán)平均,計(jì)算公式=(上A指數(shù)收益率+深A(yù)指數(shù)收益率)/2
def get_Rm_simple_cont(start_date, end_date):'''功能:計(jì)算滬深A(yù)股收益率均值作為市場均值連續(xù)時(shí)間參數(shù):start_date, end_date返回值:list類型,A股收益率'''SH_market_return = jq.get_price('000002.XSHG',start_date=start_date,end_date=end_date,fields=['close', 'pre_close']).fillna(0)SH_market_return = (SH_market_return['close']-SH_market_return['pre_close']) / SH_market_return['pre_close']SZ_market_return = jq.get_price('399107.XSHE',start_date=start_date,end_date=end_date,fields=['close', 'pre_close']).fillna(0)SZ_market_return = (SZ_market_return['close']-SZ_market_return['pre_close']) / SZ_market_return['pre_close']Rm_simple_cont = (SH_market_return+SZ_market_return)/2return list(Rm_simple_cont)該方法構(gòu)建的綜合市場回報(bào)率與國泰君安的數(shù)據(jù)庫基本一致,因此下文使用該方法。此外,我們給這個(gè)函數(shù)稍作變化:
增加了一個(gè)新的參數(shù)time_interval,適用于計(jì)算若干天內(nèi)市場回報(bào)率的指數(shù)平均。
增加了參數(shù)code,計(jì)算其他指數(shù)的收益率
def get_Rm_simple2(start_date,end_date,time_interval=None,code = None):'''功能:計(jì)算滬深A(yù)股收益率均值作為市場均值,也可以計(jì)算其他指數(shù)的收益率參數(shù):start_date,end_date,time_interval默認(rèn)為None,計(jì)算每日收益率,當(dāng)為int型數(shù)字時(shí),計(jì)算前time_interval天的指數(shù)平均收益率返回值:list類型,A股收益率'''if not code:return0 = jq.get_price(['000002.XSHG','399107.XSHE'],start_date=start_date,end_date=end_date,fields=['close', 'pre_close'],).fillna(0) SH_market_return = return0.loc[return0['code']=='000002.XSHG'].reset_index(drop=True) SZ_market_return = return0.loc[return0['code']=='399107.XSHE'].reset_index(drop=True) if time_interval: return1 = jq.get_price(['000002.XSHG','399107.XSHE'],count = time_interval,end_date=start_date,fields=['close', 'pre_close'], ).fillna(0) SH_market_return1 = return1.loc[return1['code']=='000002.XSHG'].reset_index(drop=True) SH_market_return1.drop([len(SH_market_return1)-1],inplace=True) SH_market_return = SH_market_return1.append(SH_market_return).reset_index(drop = True) SZ_market_return1 = return1.loc[return1['code']=='399107.XSHE'].reset_index(drop=True) SZ_market_return1.drop([len(SZ_market_return1)-1],inplace=True) SZ_market_return = SZ_market_return1.append(SZ_market_return).reset_index(drop = True) SH_market_return = (SH_market_return['close']-SH_market_return['pre_close']) / SH_market_return['pre_close'] SZ_market_return = (SZ_market_return['close']-SZ_market_return['pre_close']) / SZ_market_return['pre_close'] Rm_simple = list((SH_market_return+SZ_market_return)/2) else: return0 = jq.get_price(code,start_date=start_date,end_date=end_date,fields=['close', 'pre_close'], ).fillna(0).reset_index(drop=True) if time_interval: return1 = jq.get_price(code,count = time_interval,end_date=start_date,fields=['close', 'pre_close'], ).fillna(0) return1 = return1.reset_index(drop=True) return1.drop([len(return1)-1],inplace=True) return0 = return1.append(return0).reset_index(drop = True) return0 = (return0['close']-return0['pre_close']) / return0['pre_close'] Rm_simple = list(return0) if time_interval: Rm_simple.append(0) Rm_simple = [(np.prod(1+np.array(Rm_simple[i-time_interval:i])))**(1/time_interval)-1 for i in range(time_interval, len(Rm_simple))] return Rm_simple3.3識別羊群效應(yīng)
Chang, Cheng & Khorana首先通過以下推導(dǎo)證明了理性情況下,由于個(gè)股對市場風(fēng)險(xiǎn)的敏感程度不同,市場收益率Rm劇烈波動(dòng)時(shí),組合收益率相對于Rm的離散程度會線性增加:
定義股票組合在t時(shí)刻的截面絕對離散度CSAD為
CSADt=1N∑1N∣Ri,t?Rm,t∣CSAD_{t} = \frac{1}{N}\sum_{1}^{N}{\left| R_{i,t}-R_{m,t} \right|}CSADt?=N1?1∑N?∣Ri,t??Rm,t?∣
根據(jù)CAPM,導(dǎo)出
E(CSADt)=1N∑i=1N∣βi?βm∣E(Rm,t?γ0)E(CSAD_{t})=\frac{1}{N}\sum_{i=1}^{N}{\left| \beta_{i}-\beta_{m} \right|}E(R_{m,t}-\gamma_{0})E(CSADt?)=N1?i=1∑N?∣βi??βm?∣E(Rm,t??γ0?)
可以看出無羊群效應(yīng)時(shí)CSAD與Rm關(guān)系為線性正相關(guān)。
Chang, Cheng & Khorana構(gòu)造了如下回歸,根據(jù)回歸中Rm,t系數(shù)b**是否顯著為負(fù)判斷是否存在羊群效應(yīng):b顯著時(shí),說明C S A D和Rm的關(guān)系為非線性;b**為負(fù)時(shí),如下圖所示,隨Rm增大,離散度會減速上升或加速下降。
為了計(jì)算股票組合的截面絕對離散度CSAD,首先需要得到股票成分股。該函數(shù)原創(chuàng)于我們的研報(bào)復(fù)現(xiàn)第一篇。
樣本空間:滬深300成分股,剔除 ST 股和上市未滿 60 日的新股,以及漲停、停牌、跌停(若按照原研報(bào)為每日的正常交易股票,但數(shù)據(jù)量過多,受硬件限制本文縮小了樣本范圍,只在180天更新一次指數(shù)成分股)
def get_stocks(date,index=None): ''' 功能: 根據(jù)日期,獲取該日滿足交易要求的股票相關(guān)數(shù)據(jù),即剔除ST股、上市未滿60天、停牌、跌漲停股 參數(shù): date,日期 index,指數(shù)代碼,在特定指數(shù)的成分股中選股。缺省時(shí)選股空間為全部A股 返回: DataFrame類型,索引為股票代碼,同時(shí)包含了價(jià)格數(shù)據(jù),方便后續(xù)使用 ''' stocks = jq.get_all_securities( types=['stock'], date=date )#該日正在上市的股票if index:#特定成分股 stock_codes = jq.get_index_stocks(index,date=date)#成分股 stocks = stocks[stocks.index.isin(stock_codes)]#上市日期大于60個(gè)自然日 # start_date 為 [datetime.date] 類型 stocks['start_date']=pd.to_datetime(stocks['start_date']) date = datetime.strptime(date,'%Y-%m-%d').date() date = pd.to_datetime(date) stocks['datedelta'] = date - stocks['start_date'] stocks = stocks[stocks['datedelta'] > timedelta(days=60)]#是否是ST股 stocks['is_st'] = jq.get_extras( info='is_st', security_list=list(stocks.index), count=1, end_date=date ).T#漲停、跌停、停牌 stocks_info = jq.get_price( security = list(stocks.index), fields=['close','high','low','high_limit','low_limit','paused'], count=1, end_date=date, panel=False ).set_index('code').drop('time',axis=1)stocks['price'] = stocks_info['close']#順便保存價(jià)格,方便后續(xù)運(yùn)算 stocks['paused'] = stocks_info['paused'] == 1#是否停牌 stocks['high_stop'] = stocks_info['high'] >= stocks_info['high_limit']#漲停 stocks['low_stop'] = stocks_info['low'] <= stocks_info['low_limit']#跌停 stocks = stocks[~(stocks['is_st'] | stocks['paused'] | stocks['high_stop'] | stocks['low_stop'])] return stocks為了同時(shí)使用聚寬和Tushare數(shù)據(jù),我們使用了兩個(gè)格式轉(zhuǎn)換的小函數(shù)
# ts日期轉(zhuǎn)聚寬日期 def ts2jq_date(tsdate): ''' tsdate必須是list 輸出list ''' date=['-'.join([i[0:4], i[4:6], i[6:]]) for i in tsdate] return date # 聚寬日期轉(zhuǎn)ts日期只需要date.replace('-', '')刪除'-'即可 # 聚寬股票代碼轉(zhuǎn)ts股票代碼 def jq2ts_code(jqcode): ''' 輸入聚寬的股票代碼(list) 返回tushare股票代碼(list) ''' tscode = [i.replace('XSHE', 'SZ').replace('XSHG', 'SH') for i in jqcode] return tscode接下來可以根據(jù)公式計(jì)算CSAD了
def cal_CSAD_cont(start_date, end_date, date_interval, index): ''' 為了減少運(yùn)行時(shí)間,股票不能每日篩選 date_interval:股票池更新間隔,int ''' CSAD = pd.DataFrame([]) start_date_ts = start_date.replace('-', '') end_date_ts = end_date.replace('-', '') trade_days = pro.trade_cal(start_date=start_date_ts, end_date=end_date_ts) trade_days = trade_days[trade_days['is_open']==1]['cal_date'] trade_days = list(trade_days) trade_days = ts2jq_date(trade_days) dates_update = trade_days[::date_interval] CSAD['cal_date'] = trade_days csad = [] stocks_rm = get_Rm_simple2(start_date, end_date) for i in range(len(trade_days)): if trade_days[i] in dates_update: #如果在dates_update就更新stocks stocks = get_stocks(trade_days[i], index) stocks = list(stocks.index) stocks = jq2ts_code(stocks) ts_date = trade_days[i].replace('-', '') ri = pro.daily(trade_date=ts_date, fields='ts_code,pct_chg') ri = ri[ri.ts_code.isin(stocks)].drop('ts_code',axis=1) csadi = float(np.mean(abs(ri-stocks_rm[i]))) csad.append(csadi) CSAD['CSAD'] = csad CSAD['rm'] = stocks_rm CSAD.set_index('cal_date') return CSAD對CSAD和綜合市場收益率Rm進(jìn)行OLS擬合,計(jì)算擬合的二次項(xiàng)系數(shù)beta2以及p值
def cal_beta(start_date, end_date, date_interval, index): # 返回start_date22天后的beta # 想要返回從start_date后的beta請使用 # real_date = get_trade_day_before(22,start_date) # start_date = real_date CSAD=cal_CSAD_cont(start_date, end_date, date_interval, index) x1=[] x2=[] p1 = [] p2 = [] rm_mean = [] for i in range(len(CSAD))[22:]: x=CSAD.iloc[i-22:i, 2] #pd.ix方法被1.0.0版本移除了 y=CSAD.iloc[i-22:i, 1] X = np.column_stack((x**2, x)) X = sm.add_constant(X) model = sm.OLS(y,X) results = model.fit() x1.append(results.params[1]) x2.append(results.params[2]) p1.append(results.pvalues[1]) p2.append(results.pvalues[2]) meanrm = np.mean(x) rm_mean.append(meanrm) beta = pd.DataFrame([]) beta['beta1']=x1 beta['pvalue1']=p1 beta['beta2']=x2 beta['pvalue2']=p2 beta['date']=list(CSAD.cal_date[22:]) beta['rm']=rm_mean beta.set_index('date',inplace = True)#默認(rèn)的drop=True失效了是怎么回事 return beta以滬深300為例
為了方便取用,我們將cal_beta( )的返回值保存于本地的excel中。然后繪制beta2的折線走勢圖以及p值的折線走勢圖。
HS300_beta = cal_beta('2007-01-01','2019-01-01',180, '000300.XSHG') HS300_beta.to_excel(excel_writer='beta.xlsx', sheet_name='sheet_1') # 繪制beta2的p值 df1 = pd.read_excel('beta.xlsx') print(df1) fig,ax=plt.subplots(figsize=(13,8)) plt.plot(df1.date,df1.pvalue2,'b',linewidth = 1) plt.grid(False) plt.title('07年1月1日至19年1月β2的p值折線走勢圖',fontsize=20) plt.hlines(y=0.05,xmin=df1.date[0],xmax=df1.date[len(df1)-1],color='red',linewidth = 1,alpha = 0.5) first_legend = plt.legend(['pvlaue-β2'],fontsize=15) ax1 = plt.gca().add_artist(first_legend) red_patch = mpatches.Patch(color = 'red',label = '0.05分界線',linewidth = 1,alpha = 0.5) plt.legend(handles = [red_patch],loc = 3,fontsize=15) plt.xlabel('時(shí)間',fontsize=15) plt.ylabel('pvalue',fontsize=15) for label in ax.xaxis.get_ticklabels(): # label is a Text instance label.set_color('red') label.set_rotation(45) label.set_fontsize(10) for label in ax.get_xticklabels(): label.set_visible(False) for label in ax.get_xticklabels()[::180]: label.set_visible(True) plt.tight_layout() ax.tick_params(bottom=False,top=False,left=False,right=False) #移除全部刻度線 plt.show() fig,ax=plt.subplots(figsize=(13,8)) plt.plot(df1.date,df1.beta2,'r') plt.grid(False) plt.title('07年1月1日至19年1月β2的折線走勢圖',fontsize=20) plt.legend(['β2'],fontsize=15) plt.xlabel('時(shí)間',fontsize=15) plt.ylabel('β2數(shù)值',fontsize=15) for label in ax.xaxis.get_ticklabels(): # label is a Text instance label.set_color('red') label.set_rotation(45) label.set_fontsize(10) for label in ax.get_xticklabels(): label.set_visible(False) for label in ax.get_xticklabels()[::180]: #每180個(gè)刻度顯示一個(gè) label.set_visible(True) plt.tight_layout() ax.tick_params(bottom=False,top=False,left=False,right=False) #移除全部刻度線 plt.show()方法一
通常的ols擬合,當(dāng)p值小于0.05時(shí),認(rèn)為beta2顯著。但是可以看出該方法給出的開倉信號過多。一個(gè)方法是將臨界的p值減小,例如減小到0.001
上漲時(shí)置信水平超過99.9%的信號出現(xiàn)了145次,
下跌時(shí)置信水平超過97.25%的信號出現(xiàn)了194次,
共出現(xiàn)信號339次
可以看出該方法給出的信號純度并不好。在08年的峰頂給出了買入信號,在14-15年的熊市中多次嘗試抄底,在震蕩市中表現(xiàn)也不佳。信號純度并不高。
方法二
使用某日前180日的所有ols擬合的β2作為總體的β2分布,用其上、下0.05分位數(shù)作為總體的置信限。
該方法得到的信號次數(shù)是幾乎確定的。假設(shè)β2的分布是隨時(shí)間平穩(wěn)的,那么前180天中在置信區(qū)間中的應(yīng)當(dāng)有20.05180=18次,即約有1/10的時(shí)間有信號。不過在股市這個(gè)隨時(shí)間有明顯變化的時(shí)間序列中,可能有信號的次數(shù)與理論有很大差異。
為了獲得更精確的市場狀態(tài),避免在震蕩市中誤判上漲行情,我們考慮原始均線策略。增加一個(gè)約束條件,當(dāng)30日均線大于30個(gè)交易日前的均線。不過我認(rèn)為30日均線顯示上漲,則30日收益率也應(yīng)該上漲。平均收益率代表幾何平均,均線代表線性平均,兩者不會有太大差異。
在平臺給的接口中只有判斷某日是否交易日的函數(shù)。為了配合若干交易日前的均線的計(jì)算,曲線救國定義了一個(gè)函數(shù)。該函數(shù)作用為獲取給定日期若干天前的交易日。簡單的做法是使用.rolling()方法,但是會填充一些NA值。
def get_trade_day_before(count, end_date): ''' 獲取end_date前count天的交易日,從該日到end_date一共count天 ''' return1 = jq.get_price('000002.XSHG',count = count,end_date=end_date,fields='close').fillna(0) trade_day_before = return1.index[0] return str(trade_day_before)[:10] # 增加一個(gè)均線策略 ###################################################################### def get_ma(start_date,end_date,time_interval,code = None): ''' 功能:計(jì)算滬深A(yù)股time_interval天均值作為市場均值 參數(shù):start_date,end_date, time_interval為int型數(shù)字 code為可選參數(shù),默認(rèn)計(jì)算滬深A(yù)股均值。也可以選擇其他指數(shù) 計(jì)算前time_interval天的平均值 返回值:list類型,time_interval天的平均值 ''' if not code: # 將time_interval天的指數(shù)價(jià)格拼接在start_date前面, # 然后rolling方法計(jì)算均值,最后丟掉前面的指數(shù)價(jià)格 return0 = jq.get_price(['000002.XSHG','399107.XSHE'],start_date=start_date,end_date=end_date,fields=['close','pre_close'] ).fillna(0) SH_market_return = return0.loc[return0['code']=='000002.XSHG'].reset_index(drop=True) SZ_market_return = return0.loc[return0['code']=='399107.XSHE'].reset_index(drop=True) return1 = jq.get_price(['000002.XSHG','399107.XSHE'],count = time_interval,end_date=start_date,fields=['close','pre_close'] ).fillna(0) SH_market_return1 = return1.loc[return1['code']=='000002.XSHG'].reset_index(drop=True) SH_market_return1.drop([len(SH_market_return1)-1],inplace=True) SH_market_return = SH_market_return1.append(SH_market_return).reset_index(drop = True) SZ_market_return1 = return1.loc[return1['code']=='399107.XSHE'].reset_index(drop=True) SZ_market_return1.drop([len(SZ_market_return1)-1],inplace=True) SZ_market_return = SZ_market_return1.append(SZ_market_return).reset_index(drop = True) SH_market_return = SH_market_return.close SZ_market_return = SZ_market_return.close ma_simple = (SH_market_return+SZ_market_return)/2 else: return0 = jq.get_price(code,start_date=start_date,end_date=end_date,fields=['close','pre_close'], ).fillna(0) return1 = jq.get_price(code,count = time_interval,end_date=start_date,fields=['close', 'pre_close'], ).fillna(0) return1 = return1.reset_index(drop=True) return1.drop([len(return1)-1],inplace=True) return0 = return1.append(return0).reset_index(drop = True) return0 = return0.close ma_simple = return0 ma = ma_simple.rolling(time_interval,min_periods=time_interval).mean() ma.drop(ma.index[:time_interval-1], axis=0, inplace = True) return ma分析ols擬合后的數(shù)據(jù),計(jì)算beta,MA5,MA10,MA20,22天平均收益率,180天2.5%,5%,10%分位數(shù)。
# 獲取標(biāo)的指數(shù)的beta,MA5,MA10,MA20,22天平均收益率,180天2.5%,5%,10%的分位數(shù) # 輸入的數(shù)據(jù)為cal_beta()的返回dataframe以及指數(shù)代碼 def get_analysis_frame(code, beta_frame): ''' 標(biāo)的指數(shù)的beta,MA5,MA10,MA20,22天平均收益率,180天2.5%,5%,10%的分位數(shù) ''' # 直接從beta_frame中讀取日期 start_date = beta_frame.iloc[0,0] end_date = beta_frame.iloc[len(beta_frame)-1,0] beta = beta_frame[['date','beta2','pvalue2']] R_MA22 = get_Rm_simple2(start_date, end_date, 22, code) MA30 = get_ma(start_date, end_date, 30, code) # 獲取30個(gè)交易日前的日期 start_date_before = get_trade_day_before(30, start_date) MA30_before = get_ma(start_date_before, end_date, 30, code) # 30個(gè)交易日前的日期到結(jié)束日期前30日的均線 MA30_before = MA30_before[:-29] MA5_before = get_ma(start_date_before, end_date, 5, code) MA5_before = MA30_before[:-4] MA10 = get_ma(start_date, end_date, 10, code) MA5 = get_ma(start_date, end_date, 5, code) beta['MA5'] = MA5 beta['MA5_before'] = MA5_before beta['MA10']= MA10 beta['MA30']= MA30 beta['MA30_before'] = MA30_before beta['R_MA22'] = R_MA22 beta['quantile-0.1'] = beta['beta2'].rolling(180,min_periods=180).quantile(0.1) beta['quantile-0.05'] = beta['beta2'].rolling(180,min_periods=180).quantile(0.05) beta['quantile-0.025'] = beta['beta2'].rolling(180,min_periods=180).quantile(0.025)"""為了避免過擬合的情況,分位數(shù)的計(jì)算方式為向前滾動(dòng)計(jì)算180天的分位數(shù) 180天即半年,是許多標(biāo)的指數(shù)成份股的調(diào)整周期 """beta = beta.fillna(method = 'bfill') return beta有均線時(shí):
df1 = pd.read_excel('beta.xlsx') analysis = get_analysis_frame('000300.XSHG',df1) ma30 = analysis.MA30 ma5 = analysis.MA5 ma30_before = analysis.MA30_before ma5_before = analysis.MA5_before rm22_mean = analysis.R_MA22 beta2 = analysis.beta2 q_0_05 = analysis['quantile-0.05'] q_01 = analysis['quantile-0.1'] # 增加條件:三十日均線大于三十日前的均線 # 線性平均和指數(shù)平均我覺得沒啥大的區(qū)別 # MA30如果下降,rm30也應(yīng)該下降 signal_up = [rm22_mean[i]>0 and beta2[i]<q_01[i] and ma30[i] > ma30_before[i] for i in range(len(beta2))] signal_down = [rm22_mean[i]<0 and beta2[i]<q_01[i] and ma5[i] < ma5_before[i] for i in range(len(beta2))] df1['signal_up'] = signal_up df1['signal_down'] = signal_down print('上漲時(shí)超過單側(cè)置信區(qū)間百分之5下限的beta信號出現(xiàn)了{}次,\n\ 下跌時(shí)超過單側(cè)置信區(qū)間百分之5下限的beta信號出現(xiàn)了{}次,\n\ 共出現(xiàn)單側(cè)置信區(qū)間下限為百分之10的beta信號{}次\n'.format(sum(signal_up),sum(signal_down),sum(signal_up)+sum(signal_down))) start_date = df1.date[0] end_date = list(df1['date'])[-1] ###信號坐標(biāo)## R_300 = jq.get_price('000300.XSHG',start_date=start_date,end_date=end_date,fields=['low','close']) fig = plt.subplots(figsize=(13,8)) plt.plot(R_300.close,'b') plt.grid(True) plt.title('圖七 改進(jìn)后的多空信號圖',fontsize=20) Y = R_300.close for i in range(len(signal_up)): if signal_up[i]: loc = int(Y[i]) plt.vlines(Y.index[i], loc-400,loc+400, color='red',linewidth = 1,alpha = 0.5) if signal_down[i]: loc = int(Y[i]) plt.vlines(Y.index[i], loc-400,loc+400, color='green',linewidth = 1,alpha = 0.5) first_legend = plt.legend(['000300.XSHG'],fontsize=20) ax = plt.gca().add_artist(first_legend) blue_patch = mpatches.Patch(color = 'red',label = '做多信號',linewidth = 1,alpha = 0.5) green_patch = mpatches.Patch(color = 'green',label = '做空信號',linewidth = 1,alpha = 0.5) plt.legend(handles = [blue_patch,green_patch],loc = 4,fontsize=20) plt.xlabel('時(shí)間',fontsize=15) plt.ylabel('滬深300指數(shù)收盤價(jià)',fontsize=15) plt.show()上漲時(shí)超過單側(cè)置信區(qū)間百分之5下限的beta信號出現(xiàn)了104次,
下跌時(shí)超過單側(cè)置信區(qū)間百分之5下限的beta信號出現(xiàn)了113次,
共出現(xiàn)單側(cè)置信區(qū)間下限為百分之10的beta信號217次
可以看出這種方法相當(dāng)保守,在09-10年之間沒有信號,11-12年的信號也踏空了。信號集中在大牛市和16-17年的慢牛。
多空策略
在加入均線策略控制信號噪聲以后,基本上沒有較大的誤判,與原研報(bào)的結(jié)果已經(jīng)很相近了。
3.4策略構(gòu)建和回測
寬基指數(shù)
策略標(biāo)的:上證綜指、上證50、滬深300、中小板綜合指數(shù) 股票組合:當(dāng)日標(biāo)的股票成分股,剔除ST股等非正常交易股票
回測時(shí)間:2007.01.01 – 2008.12.31 手續(xù)費(fèi)用:忽略 建倉成本:次日均價(jià) 策略步驟:計(jì)算向前 22
日(包括當(dāng)日)每天的成分股組合截面絕對離散度 CSAD,OLS 估計(jì) CCK 模型中
Rm,t^2的系數(shù)β2,若β2顯著為負(fù)則認(rèn)為當(dāng)日該組合存在羊群效應(yīng),根據(jù) 22
日內(nèi)指數(shù)平均收益率的正負(fù)區(qū)分羊群效應(yīng)發(fā)生時(shí)的市場趨勢為上漲/下跌,買入/賣出標(biāo)的指數(shù)并持倉22 交易日,持有期不重復(fù)開倉。
定義一個(gè)計(jì)算所有感興趣信息的函數(shù)
def cal_rate(code, start_date, end_date):'''獲取信號發(fā)生后的收益率,勝率,夏普比率等若干信息。分上漲和下跌兩種參數(shù):標(biāo)的指數(shù)代碼,回測開始和結(jié)束日期返回值:回測的數(shù)據(jù)dataframe類型,包含多空次數(shù),勝率,最大回撤,夏普比率累計(jì)策略收益率,dataframe類型,分做空和做多每次做多/空的收益率'''try:df1 = pd.read_excel('{}_{}_{}.xlsx'.format(code,start_date,end_date))except:real_date = get_trade_day_before(22,start_date)df1 = cal_beta(real_date,end_date,180,code)df1.to_excel(excel_writer='{}_{}_{}.xlsx'.format(code,start_date,end_date), sheet_name='sheet_1')df1 = pd.read_excel('{}_{}_{}.xlsx'.format(code,start_date,end_date))analysis = get_analysis_frame(code,df1)ma30 = analysis.MA30ma5 = analysis.MA5ma30_before = analysis.MA30_beforema5_before = analysis.MA5_beforerm22_mean = analysis.R_MA22beta2 = analysis.beta2q_0_05 = analysis['quantile-0.05']q_01 = analysis['quantile-0.1']# 增加條件:三十日均線大于三十日前的均線# 線性平均和指數(shù)平均我覺得沒啥大的區(qū)別# MA30如果下降,rm30也應(yīng)該下降signal_up = [rm22_mean[i]>0 and beta2[i]<q_01[i] and ma30[i] > ma30_before[i] for i in range(len(beta2))]signal_down = [rm22_mean[i]<0 and beta2[i]<q_01[i] and ma5[i] < ma5_before[i] for i in range(len(beta2))]# 次日持倉情況# 是否開倉標(biāo)志is_hold_open = [False]is_sell_open = [False]for i in range(1,len(signal_up)):if signal_up[i-1] and not any(signal_up[max(i-23,0):i-1]):is_hold_open.append(True)else:is_hold_open.append(False)if signal_down[i] and not any(signal_down[max(i-22,0):i]):is_sell_open.append(True)else:is_sell_open.append(False)df1['is_hold_open'] = is_hold_opendf1['is_sell_open'] = is_sell_open# 次日是否持倉標(biāo)記is_hold = [any(is_hold_open[max(0,i-22):i]) for i in range(len(is_hold_open))]is_sell = [any(is_sell_open[max(0,i-22):i]) for i in range(len(is_sell_open))]# 指數(shù)日收益率return0 = jq.get_price(code,start_date=start_date,end_date=end_date,fields=['close', 'pre_close'],).fillna(0)return0['rates'] = return0['close'] / return0['pre_close'] - 1# 當(dāng)日收益率,空倉時(shí)按照1計(jì)算rates_up = [return0.iloc[i,2]+1 if is_hold[max(i-1,0)] else 1 for i in range(len(return0))]rates_down = [-return0.iloc[i,2]+1 if is_sell[max(i-1,0)] else 1 for i in range(len(return0))]rates_up = np.array(rates_up)rates_down = np.array(rates_down)df1['rates_up'] = rates_updf1['rates_down'] = rates_down# 開倉次數(shù)open_up_times = sum(is_hold_open)open_down_times = sum(is_sell_open)rates_up_cum = []rates_down_cum = []# 分日累計(jì)收益率rates_daily_up = []rates_daily_down = []for i in range(len(is_hold_open)):if is_hold_open[i]:rates_up_cum.append(product(rates_up[i+1:i+23]))rates_daily_up.append(rates_up[i+1:i+23])if is_sell_open[i]:rates_down_cum.append(product(rates_down[i+1:i+23]))rates_daily_down.append(rates_down[i+1:i+23])rates_up_cum=np.array(rates_up_cum)rates_down_cum=np.array(rates_down_cum)# 勝次數(shù)up_win_times = sum(rates_up_cum>1)down_win_times = sum(rates_down_cum>1)# 勝率if open_up_times:win_rate_up = up_win_times/open_up_timeselse: win_rate_up = NaNif open_down_times:win_rate_down = down_win_times/open_down_timeselse: win_rate_down = NaN###計(jì)算最大回撤,只計(jì)算做多###re = []for k in range(len(rates_up)):retreat = max(rates_up[k]-rates_up[k:])re.append(retreat)max_retreat = max(re)###計(jì)算夏普比率,只計(jì)算做多###ex_pct_close = rates_up - 1 - 0.04/252sharpe = (ex_pct_close.mean() * (252)**0.5)/ex_pct_close.std()analysis_datas = np.array([open_up_times,open_down_times,win_rate_up,win_rate_down,max_retreat,sharpe]).reshape(1,6)back_analysis_data = pd.DataFrame(analysis_datas,columns =['做多次數(shù)','做空次數(shù)','做多勝率','做空勝率','最大回撤','夏普比率'],index = [code])rates_analysis = df1[['date','rates_up','rates_down']]rates_up_prod = [product(rates_up[:i]) for i in range(len(rates_up))]rates_down_prod = [product(rates_down[:i]) for i in range(len(rates_down))]rates_analysis['rates_up_prod'] = rates_up_prodrates_analysis['rates_down_prod'] = rates_down_prodreturn back_analysis_data,rates_analysis,rates_daily_up,rates_daily_down,rates_up_cum,rates_down_cum配合下面的函數(shù)使用,獲得分年度的策略表現(xiàn)。
def get_annual_performance(time_interval,code):# 對于某一指數(shù)的若干年的數(shù)據(jù)# 返回一個(gè)dataframe# 包含每年做多次數(shù),勝率,做空次數(shù),勝率,最大回撤,夏普比率performance = pd.DataFrame(columns =['做多次數(shù)','做多勝率','最大回撤-多','夏普比率-多','做空次數(shù)','做空勝率','最大回撤-空','夏普比率-空'])for i in range(len(time_interval)-1):start_date = time_interval[i]end_date = time_interval[i+1]back_analysis_data=cal_rate(code, start_date, end_date)[0].reset_index(drop=True)performance = performance.append(back_analysis_data, ignore_index=True)time_index = [i[0:4] for i in time_interval[:-1]]performance['年份'] = time_indexperformance.set_index('年份',inplace=True)return performance展示滬深300指數(shù)14-15年兩年
以14-15年滬深300指數(shù)為例
將分年度的表現(xiàn)組合起來,就得到了若干年的回測收益率。包含做多和做空。
可以看出,做多的效果尚可。但是做空的收益率很低(幾乎忽略不計(jì)),原因可能是我國對做空的限制導(dǎo)致大量做空的羊群效應(yīng)不顯著。研報(bào)中提到做空的羊群效應(yīng)持續(xù)時(shí)間很短,可以考慮對做空時(shí)間縮短。代碼如下。
code = '000300.XSHG'# 上證300 start_date = '2014-01-01' end_date = '2016-01-01'back_analysis_data,rates_analysis,rates_daily_up,rates_daily_down,rates_up_cum,rates_down_cum = cal_rate(code, start_date, end_date) print(back_analysis_data) rates_daily_down0 = rates_daily_down[0] # print(rates_daily_down0) rates_daily_down_cum = [product(rates_daily_down0[:i]) for i in range(len(rates_daily_down0))] # print(rates_daily_down_cum) fig = plt.subplots(figsize=(13,8)) plt.plot(rates_daily_down_cum,'green') plt.grid(True) plt.title('圖八,做空開倉后22日的累計(jì)收益率',fontsize=20) plt.xlabel('時(shí)間',fontsize=15) plt.ylabel('滬深300某次做空的收益率',fontsize=15) plt.show()rates_daily_up0 = rates_daily_up[0] # print(rates_daily_down0) rates_daily_up_cum = [product(rates_daily_up0[:i]) for i in range(len(rates_daily_up0))] fig = plt.subplots(figsize=(13,8)) plt.plot(rates_daily_up_cum,'green') plt.grid(True) plt.title('圖八,做多開倉后22日的累計(jì)收益率',fontsize=20) plt.xlabel('時(shí)間',fontsize=15) plt.ylabel('滬深300某次做多的收益率',fontsize=15) plt.show()下面以上證50為例,對收益率可視化
''' 上證50 ''' code = '000016.XSHG'# 上證50 # 時(shí)間取07-17年 time_interval = ['2007-01-01','2008-01-01','2009-01-01'] time_interval.extend(['20{}-01-01'.format(i) for i in range(10,17+2)]) performance = get_annual_performance(time_interval,code) performance.to_excel(excel_writer='07-17年上證50表現(xiàn).xlsx', sheet_name='sheet_1') start_date = '2007-01-01' end_date = '2008-01-01' back_analysis_data,rates_analysis,rates_daily_up,rates_daily_down,rates_buy_each_time,rates_sell_each_time = cal_rate(code, start_date, end_date)rates_daily_up0 = rates_daily_up[0] # print(rates_daily_down0) rates_daily_up_cum = [product(rates_daily_up0[:i]) for i in range(len(rates_daily_up0))] fig = plt.subplots(figsize=(13,8)) plt.plot(rates_daily_up_cum,'green') plt.grid(True) plt.title('圖八,做多開倉后22日的累計(jì)收益率',fontsize=20) plt.xlabel('時(shí)間',fontsize=15) plt.ylabel('07-08年上證50某次做多的收益率',fontsize=15) plt.show()fig,ax=plt.subplots(figsize=(13,8)) plt.plot(rates_analysis.date,rates_analysis.rates_up_prod,'b') plt.grid(False) plt.title('圖九,07-08年上證50做多收益率',fontsize=20) plt.legend(['收益率'],fontsize=15) plt.xlabel('時(shí)間',fontsize=15) plt.ylabel('收益率',fontsize=15) for label in ax.xaxis.get_ticklabels():# label is a Text instancelabel.set_color('red')label.set_rotation(45)label.set_fontsize(10) for i,label in enumerate(ax.get_xticklabels()):if i%30==0:label.set_visible(True)else:label.set_visible(False)plt.tight_layout() ax.tick_params(bottom=False,top=False,left=False,right=False) #移除全部刻度線 plt.show()最后,實(shí)現(xiàn)對07-08年上證綜指、上證50、滬深300、中小板綜合指數(shù)的回測。
''' 寬基指數(shù)做多收益率 ''' # 不知道什么原因,中證500和創(chuàng)業(yè)板指獲取不了成分股 index_code = ['000001.XSHG','000016.XSHG','000300.XSHG','399101.XSHE'] colors = ['red','orange','yellow','green'] # 時(shí)間取07-17年 time_interval = ['2007-01-01','2008-01-01','2009-01-01'] # time_interval.extend(['20{}-01-01'.format(i) for i in range(10,17+2)])fig,ax=plt.subplots(figsize=(13,8)) for i,code in enumerate(index_code):every_rate = get_everyyear_rate(time_interval, code)plt.plot(every_rate.index,every_rate.rate_prod_up,color = colors[i],label = code) plt.grid(False) plt.title('07-08年寬基指數(shù)做多收益率',fontsize=20) plt.legend() plt.xlabel('時(shí)間',fontsize=15) plt.ylabel('收益率',fontsize=15) for label in ax.xaxis.get_ticklabels():# label is a Text instancelabel.set_color('red')label.set_rotation(45)label.set_fontsize(10) for i,label in enumerate(ax.get_xticklabels()):if i%90==0:label.set_visible(True)else:label.set_visible(False)plt.tight_layout() ax.tick_params(bottom=False,top=False,left=False,right=False) #移除全部刻度線 plt.show()''' 上漲時(shí)分日累計(jì)收益率 ''' index_code = ['000001.XSHG','000016.XSHG','000300.XSHG','399101.XSHE'] colors = ['red','orange','yellow','green'] # 時(shí)間取07-17年 time_interval = ['2007-01-01','2008-01-01','2009-01-01'] # time_interval.extend(['20{}-01-01'.format(i) for i in range(10,17+2)])fig,ax=plt.subplots(figsize=(13,8))for i,code in enumerate(index_code):rates_daily_up = []for j in range(len(time_interval)-1):rates_daily_up.extend(cal_rate(code, time_interval[j], time_interval[j+1])[2])rates_daily_up0 = rates_daily_up[1]rates_daily_up_cum = [product(rates_daily_up0[:i]) for i in range(len(rates_daily_up0))]plt.plot(rates_daily_up_cum,color = colors[i],label = code) plt.grid(False) plt.title('07-08年上漲時(shí)分日累計(jì)收益率',fontsize=20) plt.legend() plt.xlabel('時(shí)間',fontsize=15) plt.ylabel('收益率',fontsize=15)plt.tight_layout() plt.show()''' 下跌時(shí)分日累計(jì)收益率 ''' index_code = ['000001.XSHG','000016.XSHG','000300.XSHG','399101.XSHE'] colors = ['red','orange','yellow','green'] # 時(shí)間取07-17年 time_interval = ['2007-01-01','2008-01-01','2009-01-01'] # time_interval.extend(['20{}-01-01'.format(i) for i in range(10,17+2)])fig,ax=plt.subplots(figsize=(13,8))for i,code in enumerate(index_code):rates_daily_up = []for j in range(len(time_interval)-1):rates_daily_up.extend(cal_rate(code, time_interval[j], time_interval[j+1])[3])rates_daily_up0 = rates_daily_up[1]rates_daily_up_cum = [product(rates_daily_up0[:i]) for i in range(18)]plt.plot(rates_daily_up_cum,color = colors[i],label = code) plt.grid(False) plt.title('07-08年下跌時(shí)分日累計(jì)收益率',fontsize=20) plt.legend() plt.xlabel('時(shí)間',fontsize=15) plt.ylabel('收益率',fontsize=15)plt.tight_layout() plt.show()
中小板指做空效果更好,可能低市值指數(shù)更適合做空。
行業(yè)指數(shù)
申萬一級行業(yè)指數(shù),并分成4大類,金融型,成長型,消費(fèi)型,周期型
策略標(biāo)的:以上4大類行業(yè)
股票組合:當(dāng)日標(biāo)的指數(shù)的成分股,剔除ST股等非正常交易狀態(tài)股票
回測時(shí)間:2014年3月1日至2019年9月1日
手續(xù)費(fèi)用:在此回測暫時(shí)忽略
策略步驟: (做多)
向前22日計(jì)算每天(包括當(dāng)天)的成份股組合截面絕對離散度CSAD,OLS估計(jì)CCK模型中Rm平方的系數(shù)β2。
向前計(jì)算180天內(nèi)的樣本單側(cè)置信區(qū)間10%下限。
向前計(jì)算22日內(nèi)標(biāo)的指數(shù)平均收益率的正負(fù)區(qū)間,運(yùn)用正負(fù)判斷市場趨勢
向前計(jì)算MA30
開倉:當(dāng)β2小于單側(cè)置信區(qū)間下限,標(biāo)的指數(shù)平均收益率為正,當(dāng)日MA30大于30天前的MA30時(shí),做多標(biāo)的指數(shù)。
清倉:持有22天后賣出。
(做空)
所需計(jì)算的指標(biāo)同上文做多策略。
開倉:當(dāng)β2小于單側(cè)置信區(qū)間下限,標(biāo)的指數(shù)平均收益率為負(fù),當(dāng)日MA30小于5天前的MA30時(shí),做空標(biāo)的指數(shù)。
清倉:持有22天后賣出。 為了方便對于行業(yè)指數(shù)和寬基指數(shù)同時(shí)使用以上函數(shù),我們重載了get_price,聚寬的get_price僅針對寬基指數(shù)。相比于聚寬的函數(shù),它僅能輸入fields參數(shù)為[‘close’,
‘pre_close’]。
同時(shí)對獲取成分股函數(shù)稍作更改,增加對行業(yè)成分股的支持。
def get_stocks(date,index=None):'''功能:根據(jù)日期,獲取該日滿足交易要求的股票相關(guān)數(shù)據(jù),即剔除ST股、上市未滿60天、停牌、跌漲停股參數(shù):date,日期index,指數(shù)代碼,也可以是行業(yè)代碼。在特定指數(shù)的成分股中選股。缺省時(shí)選股空間為全部A股返回:DataFrame類型,索引為股票代碼,同時(shí)包含了價(jià)格數(shù)據(jù),方便后續(xù)使用'''stocks = jq.get_all_securities(types=['stock'],date=date)#該日正在上市的股票if index:#特定成分股# 是否寬基指數(shù)if len(index)==11:stock_codes = jq.get_index_stocks(index,date=date)#成分股# 行業(yè)指數(shù)elif len(index)==6:stock_codes = jq.get_industry_stocks(index,date=date)stocks = stocks[stocks.index.isin(stock_codes)] #上市日期大于60個(gè)自然日# start_date 為 [datetime.date] 類型stocks['start_date']=pd.to_datetime(stocks['start_date'])date = datetime.strptime(date,'%Y-%m-%d').date()date = pd.to_datetime(date)stocks['datedelta'] = date - stocks['start_date']stocks = stocks[stocks['datedelta'] > timedelta(days=60)]#是否是ST股stocks['is_st'] = jq.get_extras(info='is_st',security_list=list(stocks.index),count=1, end_date=date).T#漲停、跌停、停牌stocks_info = jq.get_price(security = list(stocks.index),fields=['close','high','low','high_limit','low_limit','paused'],count=1,end_date=date,panel=False).set_index('code').drop('time',axis=1)stocks['price'] = stocks_info['close']#順便保存價(jià)格,方便后續(xù)運(yùn)算stocks['paused'] = stocks_info['paused'] == 1#是否停牌stocks['high_stop'] = stocks_info['high'] >= stocks_info['high_limit']#漲停stocks['low_stop'] = stocks_info['low'] <= stocks_info['low_limit']#跌停stocks = stocks[~(stocks['is_st'] | stocks['paused'] | stocks['high_stop'] | stocks['low_stop'])] return stocks最后將所有函數(shù)的jq.get_price改成我們重載的get_price即可。
以下是消費(fèi)行業(yè)指數(shù)為例,在07-08年的回測
消費(fèi)行業(yè)指數(shù)做多效果尚可,但是做空收益率沒有明顯優(yōu)勢,與原研報(bào)的結(jié)論相仿。
總結(jié)
作為本人第一個(gè)獨(dú)立完成的研報(bào)復(fù)現(xiàn),該研報(bào)參考了聚寬精英任務(wù)獲獎(jiǎng)作品——作者:Eden666的思路。受限于賬號等級和硬件水平,本人僅對研報(bào)完成了復(fù)現(xiàn),在結(jié)論方面,由于沒有足夠的數(shù)據(jù),沒有給出更深入的結(jié)論。根據(jù)我們的研究得到了以下結(jié)論:
1、收益率與市值相關(guān),高市值對于做多的羊群效應(yīng)收益可能更高
2、羊群效應(yīng)明顯不足,收益率不高,大部分時(shí)間為空倉狀態(tài),尤其是做空收益很差,可能造成很大損失。
總結(jié)
以上是生活随笔為你收集整理的研报复现系列(六)【国泰君安】基于CCK模型的股票市场羊群效应研究的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 从Curator实现分布式锁的源码再到羊
- 下一篇: linux编译n2n v2,在cento