• QQ空间
  • 回复
  • 收藏

【宽客策略源码】多因子选股(股票)

geek168 数据策略 2019-8-11 15:49 52004人围观

本策略每隔1个月定时触发,根据Fama-French三因子模型对每只股票进行回归,得到其alpha值。假设Fama-French三因子模型可以完全解释市场,则alpha为负表明市场低估该股,因此应该买入。       策略思路:
  • 计算市场收益率、个股的账面市值比和市值,并对后两个进行了分类,
  • 根据分类得到的组合分别计算其市值加权收益率、SMB和HML.
  • 对各个股票进行回归(假设无风险收益率等于0)得到alpha值.
  • 选取alpha值小于0并为最小的10只股票进入标的池
  • 平掉不在标的池的股票并等权买入在标的池的股票


回测数据:SHSE.000300的成份股回测时间:2017-07-01 08:00:00到2017-10-01 16:00:0


  1. # coding=utf-8
  2. from __future__ import print_function, absolute_import, unicode_literals
  3. import numpy as np
  4. from gm.api import *
  5. from pandas import DataFrame
  6. '''

  7. '''
  8. def init(context):
  9.     # 每月第一个交易日的09:40 定时执行algo任务
  10.     schedule(schedule_func=algo, date_rule='1m', time_rule='09:40:00')
  11.     print(order_target_percent(symbol='SHSE.600000', percent=0.5, order_type=OrderType_Market,
  12.                          position_side=PositionSide_Long))
  13.     # 数据滑窗
  14.     context.date = 20
  15.     # 设置开仓的最大资金量
  16.     context.ratio = 0.8
  17.     # 账面市值比的大/中/小分类
  18.     context.BM_BIG = 3.0
  19.     context.BM_MID = 2.0
  20.     context.BM_SMA = 1.0
  21.     # 市值大/小分类
  22.     context.MV_BIG = 2.0
  23.     context.MV_SMA = 1.0
  24. # 计算市值加权的收益率,MV为市值的分类,BM为账目市值比的分类
  25. def market_value_weighted(stocks, MV, BM):
  26.     select = stocks[(stocks.NEGOTIABLEMV == MV) & (stocks.BM == BM)]
  27.     market_value = select['mv'].values
  28.     mv_total = np.sum(market_value)
  29.     mv_weighted = [mv / mv_total for mv in market_value]
  30.     stock_return = select['return'].values
  31.     # 返回市值加权的收益率的和
  32.     return_total = []
  33.     for i in range(len(mv_weighted)):
  34.         return_total.append(mv_weighted[i] * stock_return[i])
  35.     return_total = np.sum(return_total)
  36.     return return_total
  37. def algo(context):
  38.     # 获取上一个交易日的日期
  39.     last_day = get_previous_trading_date(exchange='SHSE', date=context.now)
  40.     # 获取沪深300成份股
  41.     context.stock300 = get_history_constituents(index='SHSE.000300', start_date=last_day,
  42.                                                 end_date=last_day)[0]['constituents'].keys()
  43.     # 获取当天有交易的股票
  44.     not_suspended = get_history_instruments(symbols=context.stock300, start_date=last_day, end_date=last_day)
  45.     not_suspended = [item['symbol'] for item in not_suspended if not item['is_suspended']]
  46.     fin = get_fundamentals(table='tq_sk_finindic', symbols=not_suspended, start_date=last_day, end_date=last_day,
  47.                            fields='PB,NEGOTIABLEMV', df=True)
  48.     # 计算账面市值比,为P/B的倒数
  49.     fin['PB'] = (fin['PB'] ** -1)
  50.     # 计算市值的50%的分位点,用于后面的分类
  51.     size_gate = fin['NEGOTIABLEMV'].quantile(0.50)
  52.     # 计算账面市值比的30%和70%分位点,用于后面的分类
  53.     bm_gate = [fin['PB'].quantile(0.30), fin['PB'].quantile(0.70)]
  54.     fin.index = fin.symbol
  55.     x_return = []
  56.     # 对未停牌的股票进行处理
  57.     for symbol in not_suspended:
  58.         # 计算收益率
  59.         close = history_n(symbol=symbol, frequency='1d', count=context.date + 1, end_time=last_day, fields='close',
  60.                           skip_suspended=True, fill_missing='Last', adjust=ADJUST_PREV, df=True)['close'].values
  61.         stock_return = close[-1] / close[0] - 1
  62.         pb = fin['PB'][symbol]
  63.         market_value = fin['NEGOTIABLEMV'][symbol]
  64.         # 获取[股票代码. 股票收益率, 账面市值比的分类, 市值的分类, 流通市值]
  65.         if pb < bm_gate[0]:
  66.             if market_value < size_gate:
  67.                 label = [symbol, stock_return, context.BM_SMA, context.MV_SMA, market_value]
  68.             else:
  69.                 label = [symbol, stock_return, context.BM_SMA, context.MV_BIG, market_value]
  70.         elif pb < bm_gate[1]:
  71.             if market_value < size_gate:
  72.                 label = [symbol, stock_return, context.BM_MID, context.MV_SMA, market_value]
  73.             else:
  74.                 label = [symbol, stock_return, context.BM_MID, context.MV_BIG, market_value]
  75.         elif market_value < size_gate:
  76.             label = [symbol, stock_return, context.BM_BIG, context.MV_SMA, market_value]
  77.         else:
  78.             label = [symbol, stock_return, context.BM_BIG, context.MV_BIG, market_value]
  79.         if len(x_return) == 0:
  80.             x_return = label
  81.         else:
  82.             x_return = np.vstack([x_return, label])
  83.     stocks = DataFrame(data=x_return, columns=['symbol', 'return', 'BM', 'NEGOTIABLEMV', 'mv'])
  84.     stocks.index = stocks.symbol
  85.     columns = ['return', 'BM', 'NEGOTIABLEMV', 'mv']
  86.     for column in columns:
  87.         stocks[column] = stocks[column].astype(np.float64)
  88.     # 计算SMB.HML和市场收益率
  89.     # 获取小市值组合的市值加权组合收益率
  90.     smb_s = (market_value_weighted(stocks, context.MV_SMA, context.BM_SMA) +
  91.              market_value_weighted(stocks, context.MV_SMA, context.BM_MID) +
  92.              market_value_weighted(stocks, context.MV_SMA, context.BM_BIG)) / 3
  93.     # 获取大市值组合的市值加权组合收益率
  94.     smb_b = (market_value_weighted(stocks, context.MV_BIG, context.BM_SMA) +
  95.              market_value_weighted(stocks, context.MV_BIG, context.BM_MID) +
  96.              market_value_weighted(stocks, context.MV_BIG, context.BM_BIG)) / 3
  97.     smb = smb_s - smb_b
  98.     # 获取大账面市值比组合的市值加权组合收益率
  99.     hml_b = (market_value_weighted(stocks, context.MV_SMA, 3) +
  100.              market_value_weighted(stocks, context.MV_BIG, context.BM_BIG)) / 2
  101.     # 获取小账面市值比组合的市值加权组合收益率
  102.     hml_s = (market_value_weighted(stocks, context.MV_SMA, context.BM_SMA) +
  103.              market_value_weighted(stocks, context.MV_BIG, context.BM_SMA)) / 2
  104.     hml = hml_b - hml_s
  105.     close = history_n(symbol='SHSE.000300', frequency='1d', count=context.date + 1,
  106.                       end_time=last_day, fields='close', skip_suspended=True,
  107.                       fill_missing='Last', adjust=ADJUST_PREV, df=True)['close'].values
  108.     market_return = close[-1] / close[0] - 1
  109.     coff_pool = []
  110.     # 对每只股票进行回归获取其alpha值
  111.     for stock in stocks.index:
  112.         x_value = np.array([[market_return], [smb], [hml], [1.0]])
  113.         y_value = np.array([stocks['return'][stock]])
  114.         # OLS估计系数
  115.         coff = np.linalg.lstsq(x_value.T, y_value)[0][3]
  116.         coff_pool.append(coff)
  117.     # 获取alpha最小并且小于0的10只的股票进行操作(若少于10只则全部买入)
  118.     stocks['alpha'] = coff_pool
  119.     stocks = stocks[stocks.alpha < 0].sort_values(by='alpha').head(10)
  120.     symbols_pool = stocks.index.tolist()
  121.     positions = context.account().positions()
  122.     # 平不在标的池的股票
  123.     for position in positions:
  124.         symbol = position['symbol']
  125.         if symbol not in symbols_pool:
  126.             order_target_percent(symbol=symbol, percent=0, order_type=OrderType_Market,
  127.                                  position_side=PositionSide_Long)
  128.             print('市价单平不在标的池的', symbol)
  129.     # 获取股票的权重
  130.     percent = context.ratio / len(symbols_pool)
  131.     # 买在标的池中的股票
  132.     for symbol in symbols_pool:
  133.         order_target_percent(symbol=symbol, percent=percent, order_type=OrderType_Market,
  134.                              position_side=PositionSide_Long)
  135.         print(symbol, '以市价单调多仓到仓位', percent)
  136. if __name__ == '__main__':
  137.     '''
  138.     strategy_id策略ID,由系统生成
  139.     filename文件名,请与本文件名保持一致
  140.     mode实时模式:MODE_LIVE回测模式:MODE_BACKTEST
  141.     token绑定计算机的ID,可在系统设置-密钥管理中生成
  142.     backtest_start_time回测开始时间
  143.     backtest_end_time回测结束时间
  144.     backtest_adjust股票复权方式不复权:ADJUST_NONE前复权:ADJUST_PREV后复权:ADJUST_POST
  145.     backtest_initial_cash回测初始资金
  146.     backtest_commission_ratio回测佣金比例
  147.     backtest_slippage_ratio回测滑点比例
  148.     '''
  149.     run(strategy_id='strategy_id',
  150.         filename='main.py',
  151.         mode=MODE_BACKTEST,
  152.         token='token_id',
  153.         backtest_start_time='2017-07-01 08:00:00',
  154.         backtest_end_time='2017-10-01 16:00:00',
  155.         backtest_adjust=ADJUST_PREV,
  156.         backtest_initial_cash=10000000,
  157.         backtest_commission_ratio=0.0001,
  158.         backtest_slippage_ratio=0.0001)
复制代码
attach_152b19cc721815af.png


路过

雷人

握手

鲜花

鸡蛋
原作者: abctrader
关注微信