量化投资:第3节 滑点策略与交易手续费

0
回复
6793
查看
[复制链接]

24

主题

15

回帖

227

积分

中级会员

积分
227
来源: 2019-10-31 23:14:30 显示全部楼层 |阅读模式
上一节使用AbuFactorBuyBreak和AbuFactorSellBreak且混入基本止盈止损策略AbuFactorAtrNStop,
风险控制止损策略AbuFactorPreAtrNStop,利润保护止盈策略AbuFactorCloseAtrNStop来提高交易的盈利效果。
本节将继续在上一节回测的基础上示例择时策略其它使用方法,首先完成上一节的回测准备,如下所示:
  1. <font style="background-color: white;">from abupy import AbuFactorBuyBreak, AbuFactorSellBreak
  2. from abupy import AbuFactorAtrNStop, AbuFactorPreAtrNStop, AbuFactorCloseAtrNStop
  3. from abupy import ABuPickTimeExecute, AbuBenchmark, AbuCapital

  4. # buy_factors 60日向上突破,42日向上突破两个因子
  5. buy_factors = [{'xd': 60, 'class': AbuFactorBuyBreak},
  6.                {'xd': 42, 'class': AbuFactorBuyBreak}]
  7. # 四个卖出因子同时并行生效
  8. sell_factors = [
  9.     {
  10.         'xd': 120,
  11.         'class': AbuFactorSellBreak
  12.     },
  13.     {
  14.         'stop_loss_n': 0.5,
  15.         'stop_win_n': 3.0,
  16.         'class': AbuFactorAtrNStop
  17.     },
  18.     {
  19.         'class': AbuFactorPreAtrNStop,
  20.         'pre_atr_n': 1.0
  21.     },
  22.     {
  23.         'class': AbuFactorCloseAtrNStop,
  24.         'close_atr_n': 1.5
  25.     }]
  26. benchmark = AbuBenchmark()
  27. capital = AbuCapital(1000000, benchmark)</font>
复制代码

1 滑点买入卖出价格确定及策略实现
第一节中实现的买入策略和卖出策略的编写,买入策略中确定买入只是通过make_buy_order函数,确定买单生成,卖出策略确定卖出订单
也只是通过fit_sell_order来提交卖单,那么执行订单,应该使用的什么价格买入或者卖出呢,abupy在默认的策略都是使用当天的均价买入卖出,
当然你可以实现多种复杂的当日交易策略,设置限价单、市价单,获取当日的分时数据再次进行策略分析执行操作,但是如果你的回测数量足够多的情况下,比如全市场回测,按照大数定理,这个均值执行其实是最好的模拟,而且简单、运行速度快。
滑点买入卖出价格确定具体实现代码请阅读AbuSlippageBuyMean和AbuSlippageSellMean,它们的实现都很简单
在买入滑点AbuSlippageBuyMean中有一个小策略当当天开盘价格直接下探7%时,放弃买单,看上一节回测结果中如下图这次交易,从图上就可以发现虽然是突破买入,但明显第二天执行买单时的价格是直线下跌的,且下跌不少,但还是成交了这笔交易。因为开盘下跌幅度没有达到7%的阀值,下面我们就过拟合这次交易避免买入,只为示例
103102.png

下面编写一个独立的Slippage策略,只简单修改g_open_down_rate的值为0.02


  1. <font style="background-color: white;">from abupy import AbuSlippageBuyBase, slippage
  2. # 修改买入下跌阀值为0.02
  3. g_open_down_rate = 0.02

  4. class AbuSlippageBuyMean2(AbuSlippageBuyBase):
  5.     """示例日内滑点均价买入类"""

  6.     @slippage.sbb.slippage_limit_up
  7.     def fit_price(self):
  8.         """
  9.         取当天交易日的最高最低均价做为决策价格
  10.         :return: 最终决策的当前交易买入价格
  11.         """
  12.         # TODO 基类提取作为装饰器函数,子类根据需要选择是否装饰,并且添加上根据order的call,put明确细节逻辑
  13.         if self.kl_pd_buy.pre_close == 0 or (self.kl_pd_buy.open / self.kl_pd_buy.pre_close) < (1 - g_open_down_rate):
  14.             # 开盘就下跌一定比例阀值,放弃单子
  15.             return np.inf
  16.         # 买入价格为当天均价,即最高,最低的平均,也可使用高开低收平均等方式计算
  17.         self.buy_price = np.mean([self.kl_pd_buy['high'], self.kl_pd_buy['low']])
  18.         # 返回最终的决策价格
  19.         return self.buy_price</font>
复制代码

上面编写的AbuSlippageBuyMean2类实现即为滑点买入类的实现:
  • 滑点买入类需要继承自AbuSlippageBuyBase
  • 滑点买入类需要实现fit_price来确定交易单执行当日的最终买入价格
  • slippage_limit_up装饰器是针对a股涨停板买入价格决策的装饰器,处理买入成功概率,根据概率决定是否能买入,及涨停下的买入价格决策,涨停下买入价格模型为,越靠近涨停价格买入成交概率越大,即在涨停下预期以靠近涨停价格买入,
备注:slippage_limit_up及slippage_limit_down具体实现可阅读源代码,后面的章节有示例演示使用
但是滑点类时什么时候被实例化使用的呢,怎么使用我们自己写的这个滑点类呢?首先看买入因子基类AbuFactorBuyBase,在每个买入因子初始化的时候即把默认的滑点类以及仓位管理类(稍后讲解)赋值,如下片段代码所示:
详情请查看AbuFactorBuyBas源代码
  1. <font style="background-color: white;">class AbuFactorBuyBase(six.with_metaclass(ABCMeta, ABuParamBaseClass)):
  2.     def __init__(self, capital, kl_pd, **kwargs):
  3.         # 走势数据
  4.         self.kl_pd = kl_pd
  5.         # 资金情况数据
  6.         self.capital = capital
  7.         # 滑点类,默认AbuSlippageBuyMean
  8.         self.slippage_class = kwargs['slippage'] \
  9.             if 'slippage' in kwargs else AbuSlippageBuyMean
  10.         # 仓位管理,默认AbuAtrPosition
  11.         self.position_class = kwargs['position'] \
  12.             if 'position' in kwargs else AbuAtrPosition
  13.         if 'win_rate' in kwargs:
  14.             self.win_rate = kwargs['win_rate']
  15.         if 'gains_mean' in kwargs:
  16.             self.gains_mean = kwargs['gains_mean']
  17.         if 'losses_mean' in kwargs:
  18.             self.losses_mean = kwargs['losses_mean']
  19.         self._init_self(**kwargs)</font>
复制代码

之后因子在每次生效产生买单的时候会触发AbuOrder实例对象的fit_buy_order()函数,fit_buy_order()中将滑点类,仓位管理类实例化后,执行买入价格及数量确定,代码片段如下所示,详情请查看源代码。

  1. <font style="background-color: white;">def fit_buy_order(self, day_ind, factor_object):
  2.     kl_pd = factor_object.kl_pd
  3.     # 要执行买入当天的数据
  4.     kl_pd_buy = kl_pd.iloc[day_ind + 1]
  5.     # 买入因子名称
  6.     factor_name = factor_object.factor_name \
  7.         if hasattr(factor_object, 'factor_name') else 'unknown'
  8.     # 滑点类设置
  9.     slippage_class = factor_object.slippage_class
  10.     # 仓位管理类设置
  11.     position_class = factor_object.position_class
  12.     # 初始资金,也可修改策略使用剩余资金
  13.     read_cash = factor_object.capital.read_cash
  14.     # 实例化滑点类
  15.     fact = slippage_class(kl_pd_buy, factor_name)
  16.     # 执行fit_price(), 计算出买入价格
  17.     bp = fact.fit_price()
  18.     # 如果滑点类中决定不买入,撤单子,bp就返回正无穷
  19.     if bp < np.inf:
  20.         # 实例化仓位管理类
  21.         position = position_class(kl_pd_buy, factor_name, bp,
  22.                                   read_cash)
  23.         # 执行fit_position(),通过仓位管理计算买入的数量
  24.         buy_stock_cnt = int(position.fit_position(factor_object))
  25.         if buy_stock_cnt < 1:
  26.             return</font>
复制代码

卖出因子的滑点操作及仓位管理与买入类似,读者可以自行阅读源代码。
由以上代码我们可以发现通过buy_factors的字典对象中传入slippage便可以自行设置滑点类,由于上图显示的交易是60日突破产生的买单,所以我们只修改60日突破的字典对象,执行后可以看到如下图所示,过滤了两个60日突破的买单,即过滤了上图所示的交易,代码如下所示:
备注:实际上如果只是修改g_open_down_rate的值,可以通过模块全局变量直接修改,本节只为示例使用流程
  1. <font style="background-color: white;"># 针对60使用AbuSlippageBuyMean2
  2. buy_factors2 = [{'slippage': AbuSlippageBuyMean2, 'xd': 60,
  3.                  'class': AbuFactorBuyBreak},
  4.                 {'xd': 42, 'class': AbuFactorBuyBreak}]
  5. capital = AbuCapital(1000000, benchmark)
  6. orders_pd, action_pd, _ = ABuPickTimeExecute.do_symbols_with_same_factors(['usTSLA'],
  7.                                                                             benchmark,
  8.                                                                             buy_factors2,
  9.                                                                             sell_factors,
  10.                                                                             capital,
  11.                                                                             show=True)</font>
复制代码
103103.png
2. 交易手续费的计算以及自定义手续费
交易必然会产生手续费,手续费的计算在ABuCommission模块中,比如本例中使用的的美股交易回测,使用的手续费计算代码如下所示:
  1. <font style="background-color: white;">def calc_commission_us(trade_cnt, price):
  2.     """
  3.     美股计算交易费用:每股0.01,最低消费2.99
  4.     :param trade_cnt: 交易的股数(int)
  5.     :param price: 每股的价格(美元)(暂不使用,只是保持接口统一)
  6.     :return: 计算结果手续费
  7.     """
  8.     # 每股手续费0.01
  9.     commission = trade_cnt * 0.01
  10.     if commission < 2.99:
  11.         # 最低消费2.99
  12.         commission = 2.99
  13.     return commission
  14.     </font>
复制代码


针对不同市场美股,a股,港股,比特币,期货有不同计算手续费的方法,更多详情请阅读ABuCommission模块源代码
下面先看看之前的回测交易中产生的手续费情况,查看代码如下所示:
capital.commission.commission_df

103104.png


如果你想把自己的计算手续费的方法使用在回测中,只需要编写手续费函数,示例如下所示:

  1. <font style="background-color: white;">def calc_commission_us2(trade_cnt, price):
  2.     """
  3.         手续费统一7美元
  4.     """
  5.     return 7</font>
复制代码

如上编写的手续费函数统一每次买入卖出都是7美元手续费,手续费函数有两个参数一个trade_cnt代表买入(卖出)股数,
另一个参数是price,代表买入(卖出)价格,下面使用这个自定义的手续费方法做回测,代码如下所示:

  1. <font style="background-color: white;"># 构造一个字典key='buy_commission_func', value=自定义的手续费方法函数
  2. commission_dict = {'buy_commission_func': calc_commission_us2}
  3. # 将commission_dict做为参数传入AbuCapital
  4. capital = AbuCapital(1000000, benchmark, user_commission_dict=commission_dict)
  5. # 除了手续费自定义外,回测其它设置不变,show=False不可视化回测交易
  6. orders_pd, action_pd, _ = ABuPickTimeExecute.do_symbols_with_same_factors(['usTSLA'],
  7.                                                                             benchmark,
  8.                                                                             buy_factors2,
  9.                                                                             sell_factors,
  10.                                                                             capital,
  11.                                                                             show=False)
  12. # 回测完成后查看手续费情况
  13. capital.commission.commission_df</font>
复制代码
103105.png


从上面回测交易手续费结果可以看到,买入的手续费都变成了7元,卖出手续费还是之前的算法,下面的回测将买入卖出手续费计算方法都变成使用自定义的方法,代码如下所示:
# 卖出字典key='sell_commission_func', 指向同一个手续费方法,当然也可以定义不同的方法
commission_dict = {'buy_commission_func': calc_commission_us2, 'sell_commission_func': calc_commission_us2}
# 将commission_dict做为参数传入AbuCapital
capital = AbuCapital(1000000, benchmark, user_commission_dict=commission_dict)
# 除了手续费自定义外,回测其它设置不变,show=False不可视化回测交易
orders_pd, action_pd, _ = ABuPickTimeExecute.do_symbols_with_same_factors(['usTSLA'],
                                                                            benchmark,
                                                                            buy_factors2,
                                                                            sell_factors,
                                                                            capital,
                                                                            show=False)
# 回测完成后查看手续费情况
capital.commission.commission_df
103107.png

从回测结果即可以看到所有买入卖出的手续费都是7美元


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 免费注册
关注微信