<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # 破解Alpha對沖策略——觀《量化分析師Python日記第14天》有感 > 來源:https://uqer.io/community/share/55ff6ce9f9f06c597265ef04 ## 寫在最前面: > 1. 不知不覺逛社區快半年了,通過優礦平臺認識了很多大牛,真心獲益匪淺,不管是編程方面還是金融方面,在此真心感謝優礦平臺,為你們的分享精神點個贊! > > 1. 再來說說寫作目的吧,估計自己還算是個社區活躍用戶,之前也分享過一些實用的帖子,然后某一天系統就發通知說感謝我對優礦的支持,內存已經幫我加到1GB,有效期1個月,從此媽媽再也不用擔心我跑策略out of memory了,嘿嘿~所以呢,一方面傳承優礦分享交流精神,另一方面也希望通過多為社區做貢獻獲得1GB內存更長時間,來個永久版最好啦! ## 本篇緣由: > 1. 最近市場的起起伏伏真是驚心動被迫,股指期貨投機交易也被狠狠的限制了,各種公募私募產品清盤處理。。。 > > 1. 我開始思考,是市場的問題還是投資者的問題,究竟怎樣的策略才能成為常勝將軍,如果可以選擇,我寧可做市場上的壽星而不是明星。 > > 1. 優礦給了我這個啟示,感謝社區大牛薛昆Kelvin的帖子量化分析師的Python日志第14天,告訴我該如何去做Alpha對沖策略 > > 1. 如果你讀到這里,強烈建議你先去認真讀完上述帖子,然后接著往下看。 > > 1. 上述帖子,對Alpha對沖策略從理論原理再到代碼實現都進行了詳細的講解,但是對于其中提到的一些新的函數(而且是特別重要的函數講述的不是特別清楚,只是說了一下大體方向),于是乎,筆者就順藤摸瓜,探探究竟。本篇就是講述自己對上述帖子的一些測試和自我體會,當然也秉承分享精神,展示筆者得意的Fama-french三因子策略。。。老舍不得的了,一定要給我加內存啊!! ## 關于本篇: > 1. 本篇首先對帖子中出現的不太清晰的函數進行相關猜測與測試,包括因子信號處理函數:去極值(`winsorize`)、中性化(`neutralize`)、標準化(`standardize`) > > 1. 隨后,對組合構建函數`simple_long_only`進行猜測 > > 1. 最后,以Fama-French三因子構建策略進行回測展示 首先來看三個因子處理函數,筆者結合各家券商研究報告中提到的類似處理進行大膽猜測,并進行測試 > 首先是去極值函數`winsorize`,大量券商研究報告都提到了這個方法,業內常用所謂的“3σ”原則,也就是先根據因子樣本計算出標準差,然后將其中大于 `u+3σ` 的置換為`u+3σ`,將小于`u-3σ` 的置換為`u-3σ`,這樣做的好處是可以消除因子極值對因子實際效果造成的不必要影響,下面舉例來說明 Tips:讀者可以首先在code模式下輸入 `winsorize`? 然后運行便可以得到該函數的說明文檔。 ```py import numpy as np import pandas as pd universe = set_universe('SH50') # 以上證50市盈率因子進行說明 data = DataAPI.MktStockFactorsOneDayGet(tradeDate='20150916', secID=universe, field='ticker,PE',pandas='1').set_index('ticker') data = data['PE'].to_dict() # winsorize之前數據 new_data = winsorize(data) # winsorize之后數據 df = pd.DataFrame(data=0, index=map(lambda x: x[:6], universe), columns=['before winsorize','after winsorize']) df['before winsorize'][data.keys()] = data.values() # 對比兩者數據進行展示 df['after winsorize'][new_data.keys()] = new_data.values() df.reset_index(inplace=True) ``` 接下來,我們可以看看`winsorize`前后數據的變化 ```py df ``` | | index | before winsorize | after winsorize | | --- | --- | | 0 | 600000 | 5.9624 | 5.962400 | | 1 | 600104 | 7.0826 | 7.082600 | | 2 | 600050 | 33.0181 | 33.018100 | | 3 | 600036 | 7.7177 | 7.717700 | | 4 | 600030 | 8.8612 | 8.861200 | | 5 | 600028 | 14.9741 | 14.974100 | | 6 | 600016 | 7.1609 | 7.160900 | | 7 | 600015 | 6.1384 | 6.138400 | | 8 | 600519 | 16.0515 | 16.051500 | | 9 | 601006 | 10.1080 | 10.108000 | | 10 | 601398 | 5.8342 | 5.834200 | | 11 | 600048 | 6.7609 | 6.760900 | | 12 | 601628 | 17.1175 | 17.117500 | | 13 | 601166 | 5.7557 | 5.755700 | | 14 | 601318 | 10.6240 | 10.624000 | | 15 | 601328 | 7.1577 | 7.157700 | | 16 | 601088 | 11.7254 | 11.725400 | | 17 | 601857 | 24.8742 | 24.874200 | | 18 | 601601 | 13.9766 | 13.976600 | | 19 | 601169 | 6.7160 | 6.716000 | | 20 | 600837 | 10.0223 | 10.022300 | | 21 | 601668 | 7.6202 | 7.620200 | | 22 | 601288 | 5.7281 | 5.728100 | | 23 | 601818 | 6.6163 | 6.616300 | | 24 | 600111 | 77.2052 | 77.205200 | | 25 | 601989 | 167.9813 | 130.981297 | | 26 | 601766 | 65.9834 | 65.983400 | | 27 | 600585 | 10.3818 | 10.381800 | | 28 | 600010 | -419.8877 | -86.645491 | | 29 | 601901 | 13.5459 | 13.545900 | | 30 | 600256 | 45.9370 | 45.937000 | | 31 | 600887 | 21.8070 | 21.807000 | | 32 | 601688 | 10.9483 | 10.948300 | | 33 | 600999 | 10.3119 | 10.311900 | | 34 | 600518 | 24.2080 | 24.208000 | | 35 | 600406 | 49.2598 | 49.259800 | | 36 | 600018 | 25.5060 | 25.506000 | | 37 | 600637 | 89.8189 | 89.818900 | | 38 | 600089 | 20.5017 | 20.501700 | | 39 | 601998 | 7.0787 | 7.078700 | | 40 | 600109 | 21.8898 | 21.889800 | | 41 | 600150 | 508.1892 | 130.981297 | | 42 | 600690 | 12.3600 | 12.360000 | | 43 | 600583 | 10.8489 | 10.848900 | | 44 | 600893 | 68.7643 | 68.764300 | | 45 | 601988 | 6.7982 | 6.798200 | | 46 | 601390 | 23.0430 | 23.043000 | | 47 | 600958 | 12.0603 | 12.060300 | | 48 | 601186 | 17.4490 | 17.449000 | | 49 | 601800 | 13.6934 | 13.693400 | 可以很明顯看到,大部分值都沒變,第25、28、41行所在股票的PE值得到了處理,過大或者過小都會被視為極值,會得到調整。筆者也計算了`u+3σ`,發現調整結果并沒有完全按照`3σ`原則,但是`winsorize`的作用已經得到了測試檢驗。 下面繪制一個對比圖可以更明顯看到`winsorize`前后數據的變化 ```py df.plot(figsize=(14,7)) <matplotlib.axes.AxesSubplot at 0x42bfa10> ``` ![](https://box.kancloud.cn/2016-07-30_579cb7352f731.png) 接下來是中性化函數neutralize > `neutralize`函數不太好做測試,但是根據`neutralize`的說明文檔,可以猜個大概出來。該函數的定義形式是`neutralize(raw_data, target_date, risk_module='short', industry_type='SW1')`,可以看到函數需要選擇風險模型、行業分類,由此不難推測出,輸入原始因子數據,由于原始因子數據是所有行業的,這里可能按照行業分類,對因子進行了行業中性處理(大概可以理解為將因子間的行業差異消除了,比如互聯網行業和銀行之間的PE本來就不在一個level上,`neutralize`之后可能就消除了這個因素,有點像對季節數據進行季節平滑處理) 再來看看標準化函數`standardize` > 這個函數應該非常好理解,也非常好測試,很多券商的研究報告都有提到過該處理方法,簡單來講就是`(因子值 - 因子均值)/ 因子標準差`,下面接前面的例子對`standardize`進行測試 ```py data1 = standardize(data) df1 = pd.DataFrame(data=0, index=map(lambda x: x[:6], universe), columns=['raw data','standardize function','standardize myself']) df1['raw data'][data.keys()] = data.values() # 原始數據 df1['standardize function'][data1.keys()] = data1.values() # 通過standardize函數計算的值 df1['standardize myself'] = (df1['raw data'] - df1['raw data'].mean()) / df1['raw data'].std() # 自己計算的值 df1 ``` | | raw data | standardize function | standardize myself | | --- | --- | | 600000 | 5.9624 | -0.178463 | -0.178463 | | 600104 | 7.0826 | -0.167042 | -0.167042 | | 600050 | 33.0181 | 0.097395 | 0.097395 | | 600036 | 7.7177 | -0.160566 | -0.160566 | | 600030 | 8.8612 | -0.148907 | -0.148907 | | 600028 | 14.9741 | -0.086580 | -0.086580 | | 600016 | 7.1609 | -0.166243 | -0.166243 | | 600015 | 6.1384 | -0.176669 | -0.176669 | | 600519 | 16.0515 | -0.075595 | -0.075595 | | 601006 | 10.1080 | -0.136195 | -0.136195 | | 601398 | 5.8342 | -0.179770 | -0.179770 | | 600048 | 6.7609 | -0.170322 | -0.170322 | | 601628 | 17.1175 | -0.064726 | -0.064726 | | 601166 | 5.7557 | -0.180571 | -0.180571 | | 601318 | 10.6240 | -0.130934 | -0.130934 | | 601328 | 7.1577 | -0.166276 | -0.166276 | | 601088 | 11.7254 | -0.119704 | -0.119704 | | 601857 | 24.8742 | 0.014361 | 0.014361 | | 601601 | 13.9766 | -0.096751 | -0.096751 | | 601169 | 6.7160 | -0.170779 | -0.170779 | | 600837 | 10.0223 | -0.137069 | -0.137069 | | 601668 | 7.6202 | -0.161560 | -0.161560 | | 601288 | 5.7281 | -0.180852 | -0.180852 | | 601818 | 6.6163 | -0.171796 | -0.171796 | | 600111 | 77.2052 | 0.547924 | 0.547924 | | 601989 | 167.9813 | 1.473472 | 1.473472 | | 601766 | 65.9834 | 0.433507 | 0.433507 | | 600585 | 10.3818 | -0.133403 | -0.133403 | | 600010 | -419.8877 | -4.520406 | -4.520406 | | 601901 | 13.5459 | -0.101142 | -0.101142 | | 600256 | 45.9370 | 0.229116 | 0.229116 | | 600887 | 21.8070 | -0.016912 | -0.016912 | | 601688 | 10.9483 | -0.127627 | -0.127627 | | 600999 | 10.3119 | -0.134116 | -0.134116 | | 600518 | 24.2080 | 0.007568 | 0.007568 | | 600406 | 49.2598 | 0.262995 | 0.262995 | | 600018 | 25.5060 | 0.020802 | 0.020802 | | 600637 | 89.8189 | 0.676533 | 0.676533 | | 600089 | 20.5017 | -0.030221 | -0.030221 | | 601998 | 7.0787 | -0.167081 | -0.167081 | | 600109 | 21.8898 | -0.016068 | -0.016068 | | 600150 | 508.1892 | 4.942212 | 4.942212 | | 600690 | 12.3600 | -0.113234 | -0.113234 | | 600583 | 10.8489 | -0.128641 | -0.128641 | | 600893 | 68.7643 | 0.461861 | 0.461861 | | 601988 | 6.7982 | -0.169941 | -0.169941 | | 601390 | 23.0430 | -0.004310 | -0.004310 | | 600958 | 12.0603 | -0.116289 | -0.116289 | | 601186 | 17.4490 | -0.061346 | -0.061346 | | 601800 | 13.6934 | -0.099638 | -0.099638 | 可以看到,猜測完全正確,得到的結果一模一樣!! 好了,三個因子處理函數已經猜完了,再來看看大頭吧,組合構建函數`simple_long_only`,同樣,結合幫助文檔來看。 > 在《量化分析師日記》中對該函數的說明是:“組合構建綜合考慮各因子大小,行業配置等因素,默認返回前30%的股票”。給我的直觀理解是,倘若給定100個股票,那么函數就根據股票的因子值以及行業分類選出其中最好的30%只股票,也就是30只股票以及他們各自的建倉權重。至于內部怎么實現的,我也只能猜測,估計選出來的30只股票行業配置要比較均勻,而且要因子值要優于沒有被選中的股票,比如我要選低估值的股票,那么就優先選擇低PE的,但是又不能直接選PE排名30%以下的那30只股票,因為還要考慮到行業配置均勻的問題,不然選出來的很可能都是同一個行業的(比如銀行、鋼鐵之類的),所以,個人猜測組合構建函數就是在因子值和行業配置均勻之間進行博弈,求得一個最優組合。。。下面,還是寫出猜想過程。 ```py factor = standardize(neutralize(winsorize(data),'20150916')) # 將原始數據進行處理,得到最終因子值 weight = simple_long_only(factor, '20150915') # 根據因子構建組合,獲得權重 df_factor = pd.DataFrame(data=np.nan, index=map(lambda x: x[:6], universe), columns=['factor','weight']) # 將因子值和最后的持倉權重對比 df_factor['factor'][factor.keys()] = factor.values() df_factor['weight'][weight.keys()] = weight.values() df_factor ``` | | factor | weight | } --- | --- | | 600000 | -3.236122e-01 | NaN | | 600104 | -7.364325e-15 | NaN | | 600050 | 3.671396e-15 | 0.015365 | | 600036 | 7.982552e-01 | 0.084137 | | 600030 | -3.042384e-01 | NaN | | 600028 | -2.572782e-01 | NaN | | 600016 | -1.811580e-01 | NaN | | 600015 | -2.869542e-01 | NaN | | 600519 | 4.488652e-01 | 0.044730 | | 601006 | -3.457882e-02 | NaN | | 601398 | 2.537750e-01 | 0.026748 | | 600048 | 6.228453e-15 | 0.047034 | | 601628 | 5.374184e-01 | NaN | | 601166 | -7.274821e-01 | NaN | | 601318 | 1.013404e+00 | 0.053078 | | 601328 | 4.274117e-01 | 0.045050 | | 601088 | -6.332870e-01 | NaN | | 601857 | -2.000576e-01 | NaN | | 601601 | -1.666304e-01 | NaN | | 601169 | -4.956554e-01 | NaN | | 600837 | 6.762916e-01 | 0.035421 | | 601668 | -9.654556e-01 | NaN | | 601288 | -1.756114e-01 | NaN | | 601818 | -5.174820e-03 | NaN | | 600111 | -4.047149e-14 | NaN | | 601989 | 2.298816e+00 | NaN | | 601766 | -1.463175e-14 | NaN | | 600585 | -2.109168e-14 | NaN | | 600010 | 3.274110e-14 | 0.016346 | | 601901 | 9.923986e-01 | 0.051978 | | 600256 | 2.572782e-01 | 0.025743 | | 600887 | -4.488652e-01 | NaN | | 601688 | 1.289595e-01 | NaN | | 600999 | -1.478773e-01 | NaN | | 600518 | -2.324500e-14 | NaN | | 600406 | 1.344937e+00 | 0.018264 | | 600018 | 3.457882e-02 | 0.043647 | | 600637 | 1.551461e-14 | 0.043608 | | 600089 | -1.344937e+00 | NaN | | 601998 | 1.893061e-01 | NaN | | 600109 | -2.198574e+00 | NaN | | 600150 | 2.320845e+00 | 0.025369 | | 600690 | 2.358953e-14 | 0.027154 | | 600583 | 8.333446e-01 | 0.022058 | | 600893 | -4.619661e+00 | NaN | | 601988 | 5.269000e-01 | 0.055536 | | 601390 | 6.569516e-01 | 0.045871 | | 600958 | -5.311522e-01 | NaN | | 601186 | 3.286287e-02 | NaN | | 601800 | 2.756411e-01 | NaN | 從上面的對比可以看到: 總共50只股票,最后只選取了19只,比較接近30%的比例,證明了之前的猜測 由于我們假設的是要買高PE的,所以可以看到,最后選出的19只股票的因子值(PE)相對沒有選中的都比較高,而且絕大多數權重都和因子值呈比例出現,至于沒有呈現比例的應該是基于行業配置均勻的考慮,所以說之前的猜想還是非常靠譜的,有興趣的讀者可以自行進一步研究。 同樣,也給出對比分析圖 ```py df_factor.plot(secondary_y='weight',figsize=(14,7)) <matplotlib.axes.AxesSubplot at 0x403e590> ``` ![](https://box.kancloud.cn/2016-07-30_579cb73546df6.png) 終于寫到最后一部分,內心是無比的糾結。。 有了因子處理以及組合構建之后,我們就可以自己找因子來構建組合了,大賽方還專門有獲取因子數據的DataAPI,真心贊一個! 那么,我就要開始分享我的策略了。。。優礦工作人員看到的話一定要給我加內存,或者什么VIP賬號啊啊啊!!! 策略思路來源就是經典的Fama-French三因子模型,三因子模型告訴我們,股票的收益可以由這三個因子來解釋:市場beta、股票市值、股票估值;同時,低估值、低市值的股票能夠獲得超額收益 那么,估值可以用市盈率來衡量(PE),市值可以用流通市值來衡量(LFLO),下面就給出策略回測效果 PS:回測區間從2012年8月1日~2015年8月1日,股票池為中證800,每月第一個交易日建倉 ```py # 導入包 from CAL.PyCAL import * import numpy as np import pandas as pd # 構建日期列表,以保證每月第一個交易日建倉 data=DataAPI.TradeCalGet(exchangeCD=u"XSHG",beginDate=u"20120731",field=['calendarDate','isWeekEnd','isMonthEnd'],pandas="1") data = data[data['isMonthEnd'] == 1] date_list = map(lambda x: x[0:4]+x[5:7]+x[8:10], data['calendarDate'].values.tolist()) start = '2012-08-01' # 回測起始時間 end = '2015-08-01' # 回測結束時間 universe = set_universe('HS300') + set_universe('ZZ500') # 股票池 benchmark = 'HS300' # 策略參考標準 capital_base = 10000000 # 起始資金 freq = 'd' # 策略類型,'d'表示日間策略使用日線回測,'m'表示日內策略使用分鐘線回測 refresh_rate = 1 # 調倉頻率 commission = Commission(buycost=0.0008, sellcost=0.0008) # 日期處理相關 cal = Calendar('China.SSE') period = Period('-1B') def initialize(account): # 初始化虛擬賬戶狀態 pass def handle_data(account): # 每個交易日的買入賣出指令 today = account.current_date today = Date.fromDateTime(account.current_date) # 向前移動一個工作日 yesterday = cal.advanceDate(today, period) yesterday = yesterday.toDateTime().strftime('%Y%m%d') if yesterday in date_list: Factor1 = DataAPI.MktStockFactorsOneDayGet(tradeDate=yesterday,secID=account.universe[:400],field=['secID','PE','LFLO'],pandas="1") Factor2 = DataAPI.MktStockFactorsOneDayGet(tradeDate=yesterday,secID=account.universe[400:],field=['secID','PE','LFLO'],pandas="1") Factor = pd.concat([Factor1, Factor2]) Factor['ticker'] = Factor['secID'].apply(lambda x: x[0:6]) Factor.set_index('ticker',inplace=True) # 市盈率PE Factor['PE'] = 1.0 / Factor['PE'] # 低市盈率 factor = Factor['PE'].dropna().to_dict() signal_PE = standardize(neutralize(winsorize(factor),yesterday)) # 因子處理 # 對數流通市值LFLO Factor['LFLO'] = 1.0 / Factor['LFLO'] # 低市值 factor = Factor['LFLO'].dropna().to_dict() signal_LFLO = standardize(neutralize(winsorize(factor),yesterday)) # 因子處理 # 構建組合score矩陣 Total_Score = pd.DataFrame(index=Factor.index, columns=factor_name, data=0) Total_Score['PE'][signal_PE.keys()] = signal_PE.values() Total_Score['LFLO'][signal_LFLO.keys()] = signal_LFLO.values() Total_Score['total_score'] = np.dot(Total_Score, np.array([0.5, 0.5])) # 綜合兩個因子的大小,不失一般性,等權求和 total_score = Total_Score['total_score'].to_dict() wts = simple_long_only(total_score,yesterday) # 組合構建函數 Factor['wts'] = np.nan Factor['wts'][wts.keys()] = wts.values() Factor = Factor[~np.isnan(Factor['wts'])] Factor.set_index('secID', inplace=True) Factor.drop(factor_name, axis=1, inplace=True) # 先賣出 sell_list = account.valid_secpos for stk in sell_list: order_to(stk, 0) # 再買入 buy_list = Factor.index total_money = account.referencePortfolioValue prices = account.referencePrice for stk in buy_list: if np.isnan(prices[stk]) or prices[stk] == 0: # 停牌或是還沒有上市等原因不能交易 continue order(stk, int(total_money * Factor.loc[stk]['wts'] / prices[stk] /100)*100) else: return ``` ![](https://box.kancloud.cn/2016-07-30_579cb73560fe0.jpg) 接下來,用組合累計收益減去基準累計收益就得到alpha收益,如下所示: ```py ((bt['portfolio_value']/bt['portfolio_value'][0] - 1) - ((1 + bt['benchmark_return']).cumprod() - 1)).plot(figsize=(14,7)) <matplotlib.axes.AxesSubplot at 0x25067110> ``` ![](https://box.kancloud.cn/2016-07-30_579cb73576773.png) 可以看到,將Fama-French三因子運用到中國市場,可以得到非常穩健的Alpha收益! 寫在最后面: 重要的事情說三遍,希望可以給點內存,給點內存,給點內存。。我是不是太直接了???歡迎交流~~~
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看