1. 程式人生 > >量化白馬股輪動策略

量化白馬股輪動策略

股票資料的獲取見均線策略第一篇,回測的資料分析見均線策略第三篇。

不同的策略在於Strategy類中的買入賣出的判斷。

首先什麼是白馬股,白馬股,是指長期績優、回報率高並具有較高投資價值的股票。因其有關的資訊已經公開,業績較為明朗,同時又兼有業績優良、高成長、低風險的特點,因而具備較高的投資價值,往往為投資者所看好。以上來自百度百科。

關注過公眾號招財大牛貓的朋友一定對白馬股比較熟悉,由於ipo的提速,監管層對概念股的打壓,藍籌績優股從2017年至今都表現較好,白馬股更是漲幅感人。該策略是從大牛貓推薦的50只白馬股作為選股池,每15個交易日調倉一次,調整為這期間漲幅最大的5只,其實也是一種趨勢策略,認為趨勢不會輕易反轉,強者恆強的思路。

找不到股社群的原貼了,50只股票的list如下:

code_list = ['600519', '000858', '002304', '002415', '002236', '600597', '603288', \
                 '000895', '601318', '601336', '000651', '000333',\
                 '600036', '600104', '600816', '600674', '600690', \
                 '002081', '600886', '000625', '600887','000423', '600276', \
                 '000963', '600535', '600271', '002294', '300072', '000538', '600066', '000002',\
                 '002271', '600406', '600900', '002027', '000063', '002594', '601238', '600518', \
                 '600703', '600309', '002310', '002466', '002142', '601111', '601211', '600487', \
                 '002008', '600383', '600660']

不用回測也知道這個策略效果很好,因為市場已經驗證過了,本篇只是借這個思路,coding下輪動策略的實現方式。

strategy類下的成員變數與其他成員函式見均線策略第二篇,本篇就不贅述了,重點記錄下輪動的實現方式。

    def change_position(self, date):
        #按date之前15天漲幅排序
        sorted_yield_df = self.get_sorted_yield(date)
        #code: open_price,去除停盤的股票,將可買的股票按順序加入到available_buy_dict中,此處全部加入的原因
        #而不是隻加入要買的5只,是為了避免購買每隻股票的額度可能都不夠買1手,導致最終的持倉不夠5只股票。
        available_buy_dict = OrderedDict()
        for code in sorted_yield_df['code']:
            df = self.data_repository.get_onecode_df(code)
            if df[df['date'] == date].empty:
                continue
            else:
                buy_open_price = float(df[df['date'] == date]['open'])
                available_buy_dict[code] = buy_open_price

        #求得持倉股票中是否可賣
        available_sell_dict = {}
        for code in self.position_dict.keys():
            df = self.data_repository.get_onecode_df(code)
            if df[df['date'] == date].empty:
                available_sell_dict[code] = 0
            else:
                sell_open_price = float(df[df['date'] == date]['open'])
                available_sell_dict[code] = sell_open_price

        #sell operation 賣出持倉中不在top5中的
        for code in list(self.position_dict.keys()):
            amount = self.position_dict[code]
            sell_open_price = available_sell_dict[code]
            #賣出既不在待買清單的,又可以賣掉的股票
            if code not in available_buy_dict and amount > 0 and sell_open_price > 0:
                commission = self.cal_cost_function(sell_open_price, amount)
                #更改現金
                self.cash += sell_open_price * amount
                self.cash -= commission
                #更改持倉
                del self.position_dict[code]
                #加入trade記錄, -1代表賣出操作
                self.trade.add_trade(code, sell_open_price, amount, date, -1, commission)

        #從待買清單中按順序購買滿剩餘倉位,最多持有5只股票
        for code in available_buy_dict.keys():
            if len(self.position_dict) >= 5:
                break
            if code not in self.position_dict:
                buy_open_price = available_buy_dict[code]
                amount = self.get_buy_amount(code, buy_open_price)
                if amount > 0:
                    commission = self.cal_cost_function(buy_open_price, amount)
                    # 更改現金
                    self.cash -= buy_open_price * amount
                    self.cash -= commission
                    # 更改持倉
                    self.position_dict[code] = amount
                    # 加入trade記錄,+1為買入標識
                    self.trade.add_trade(code, buy_open_price, amount, date, 1, commission)
    def get_sorted_yield(self, date):
        yield_list = []
        for code in self.code_list:
            df = self.data_repository.get_onecode_df(code)
            df = df[df['date'] <= date].tail(self.step_day)['close']
            if df.empty:
                yield_in_step_day = 0
            else:
                yield_in_step_day = df.iloc[-1] / df.iloc[0]
            yield_list.append(yield_in_step_day)

(1)首先回測日之前的15個交易日內,每隻股票的漲幅值,並從大到小排列;(2)從這些裡面逐一判斷回測日是否停盤(即df[df['date'] == date].empty是否為True),沒有停盤的加入到available_buy_dict中,key為股票code,value為買入的開盤價(因為回測策略是按開盤價買入),因為加入到dict中的順序是按收益率的高低加入的,普通dict是按key的unicode碼排序的,而不是加入的順序,故該處使用的OrderedDict;(3)逐一判斷持倉的股票回測日的賣出價格並加入到available_sell_dict中,如果某隻股票回測日停盤,則賣出價為0,同樣key為股票code,value為賣出的開盤價;(4)先遍歷持倉,並賣出不在available_buy_dict中並且可賣(即sell_dict中對應的值大於0)的股票,賣出後改變cash與持倉;(5)再買入剩餘的位置。