Skip to content

Commit

Permalink
优化择时买入基础类,减少上层策略冗余编码
Browse files Browse the repository at this point in the history
  • Loading branch information
bbfamily committed Sep 3, 2017
1 parent 730c6f4 commit 72bd820
Show file tree
Hide file tree
Showing 10 changed files with 182 additions and 57 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ logging.log
p.sh
*.pyc
tmp
test.py
test.py
/abupy/RomDataBu/df_kl.h5
41 changes: 41 additions & 0 deletions abupy/FactorBuyBu/ABuFactorBuyBase.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,47 @@ def fit_day(self, today):
# pass


class AbuFactorBuyTD(AbuFactorBuyBase):
"""很多策略中在fit_day中不仅仅使用今天的数据,经常使用昨天,前天数据,方便获取昨天,前天的封装"""

def read_fit_day(self, today):
"""
覆盖base函数完成:
1. 为fit_day中截取昨天self.yesterday
2. 为fit_day中截取前天self.bf_yesterday
:param today: 当前驱动的交易日金融时间序列数据
:return: 生成的交易订单AbuOrder对象
"""
if self.skip_days > 0:
self.skip_days -= 1
return None

# 今天这个交易日在整个金融时间序列的序号
self.today_ind = int(today.key)
# 回测中默认忽略最后一个交易日
if self.today_ind >= self.kl_pd.shape[0] - 1:
return None

# 忽略不符合买入的天(统计周期内前2天,因为需要昨天和前天)
if self.today_ind < 2:
return None

# 为fit_day中截取昨天
self.yesterday = self.kl_pd.iloc[self.today_ind - 1]
# 为fit_day中截取前天
self.bf_yesterday = self.kl_pd.iloc[self.today_ind - 2]

return self.fit_day(today)

def _init_self(self, **kwargs):
"""raise NotImplementedError"""
raise NotImplementedError('NotImplementedError _init_self')

def fit_day(self, today):
"""raise NotImplementedError"""
raise NotImplementedError('NotImplementedError fit_day')


class AbuFactorBuyXD(AbuFactorBuyBase):
"""以周期为重要参数的策略,xd代表参数'多少天'如已周期为参数可直接继承使用"""

Expand Down
31 changes: 7 additions & 24 deletions abupy/FactorBuyBu/ABuFactorBuyDemo.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import numpy as np
import pandas as pd

from .ABuFactorBuyBase import AbuFactorBuyBase, BuyCallMixin
from .ABuFactorBuyBase import AbuFactorBuyBase, AbuFactorBuyXD, AbuFactorBuyTD, BuyCallMixin
from .ABuFactorBuyBreak import AbuFactorBuyBreak
from ..TLineBu.ABuTL import AbuTLine
from ..FactorBuyBu.ABuBuyFactorWrap import AbuLeastPolyWrap
Expand All @@ -20,19 +20,16 @@


# noinspection PyAttributeOutsideInit
class AbuSDBreak(AbuFactorBuyBase, BuyCallMixin):
class AbuSDBreak(AbuFactorBuyXD, BuyCallMixin):
"""示例买入因子: 在AbuFactorBuyBreak基础上进行降低交易频率,提高系统的稳定性处理"""

def _init_self(self, **kwargs):
super(AbuSDBreak, self)._init_self(**kwargs)
# 外部可以设置poly阀值,self.poly在fit_month中和每一个月大盘计算的poly比较,若是大盘的poly大于poly认为走势震荡
self.poly = kwargs.pop('poly', 2)
# 是否封锁买入策略进行择时交易
self.lock = False

# 下面的代码和AbuFactorBuyBase的实现一摸一样
self.xd = kwargs['xd']
self.factor_name = '{}:{}'.format(self.__class__.__name__, self.xd)

def fit_month(self, today):
# fit_month即在回测策略中每一个月执行一次的方法
# 策略中拥有self.benchmark,即交易基准对象,AbuBenchmark实例对象,benchmark.kl_pd即对应的市场大盘走势
Expand Down Expand Up @@ -66,20 +63,13 @@ def fit_day(self, today):
# 如果封锁策略进行交易的情况下,策略不进行择时
return None

# 下面的代码和AbuFactorBuyBase的实现一摸一样
if self.today_ind < self.xd - 1:
return None
# 今天的收盘价格达到xd天内最高价格则符合买入条件
if today.close == self.kl_pd.close[self.today_ind - self.xd + 1:self.today_ind + 1].max():
# 把突破新高参数赋值skip_days,这里也可以考虑make_buy_order确定是否买单成立,但是如果停盘太长时间等也不好
self.skip_days = self.xd
# 生成买入订单, 由于使用了今天的收盘价格做为策略信号判断,所以信号发出后,只能明天买
if today.close == self.xd_kl.close.max():
return self.buy_tomorrow()
return None


@AbuLeastPolyWrap()
class AbuTwoDayBuy(AbuFactorBuyBase, BuyCallMixin):
class AbuTwoDayBuy(AbuFactorBuyTD, BuyCallMixin):
"""示例AbuLeastPolyWrap,混入BuyCallMixin,即向上突破触发买入event"""

def _init_self(self, **kwargs):
Expand All @@ -92,14 +82,10 @@ def fit_day(self, today):
:param today: 当前驱动的交易日金融时间序列数据
:return:
"""
# 忽略不符合买入的天(统计周期内前第1天, 因为要用到昨天的交易数据)
if self.today_ind == 0:
return None

# 今天的涨幅
td_change = today.p_change
# 昨天的涨幅
yd_change = self.kl_pd.ix[self.today_ind - 1].p_change
yd_change = self.yesterday.p_change

if td_change > 0 and 0 < yd_change < td_change:
# 连续涨两天, 且今天的涨幅比昨天还高 ->买入, 用到了今天的涨幅,只能明天买
Expand Down Expand Up @@ -172,10 +158,7 @@ def _init_self(self, **kwargs):
与AbuFactorBuyBreak基本相同,唯一区别是关键子参数中添加了通过AbuFactorBuyBreakUmpDemo记录训练好的决策器
self.hit_ml = kwargs['hit_ml']
"""
# 突破参数 xd, 比如20,30,40天...突破, 不要使用kwargs.pop('xd', 20), 明确需要参数xq
self.xd = kwargs['xd']
# 在输出生成的orders_pd中显示的名字
self.factor_name = '{}:{}'.format(self.__class__.__name__, self.xd)
super(AbuFactorBuyBreakHitPredictDemo, self)._init_self(**kwargs)
# 添加了通过AbuFactorBuyBreakUmpDemo记录训练好的决策器
self.hit_ml = kwargs['hit_ml']

Expand Down
111 changes: 111 additions & 0 deletions abupy/FactorBuyBu/ABuFactorBuyWD.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# -*- encoding:utf-8 -*-
"""
买入择时示例因子:黄金分割线买入择时因子
"""

from __future__ import absolute_import
from __future__ import print_function
from __future__ import division

from .ABuFactorBuyBase import AbuFactorBuyTD, BuyCallMixin

__author__ = '阿布'
__weixin__ = 'abu_quant'


class AbuFactorBuyWD(AbuFactorBuyTD, BuyCallMixin):
"""
示例短线:日胜率均值回复策略
1. 默认以40天为周期(8周)结合涨跌阀值计算周几适合买入
2. 回测运行中每一月重新计算一次上述的周几适合买入
3. 在策略日任务中买入信号为:昨天下跌,今天开盘也下跌,且明天是计算出来的上涨概率大的'周几'
"""

def _init_self(self, **kwargs):
"""
kwargs中可选参数:buy_dw: 代表周期胜率阀值,默认0.55即55%
kwargs中可选参数:buy_dwm: 代表涨幅比例阀值系数,默认0.618
kwargs中可选参数:dw_period: 代表分析dw,dwm所使用的交易周期,默认40天周期(8周)
"""
self.buy_dw = kwargs.pop('buy_dw', 0.55)
self.buy_dwm = kwargs.pop('buy_dwm', 0.618)
self.dw_period = kwargs.pop('dw_period', 40)

# combine_kl_pd中包含择时金融时间数据与择时之前一年的金融时间数据, 先取出择时开始之前的周期数据
last_kl = self.combine_kl_pd.loc[:self.kl_pd.index[0]]
if last_kl.shape[0] > self.dw_period:
last_kl = last_kl[-self.dw_period:]
# 开始计算周几买,_make_buy_date把结果被放在self.buy_date_week序列中
self._make_buy_date(last_kl)

def fit_month(self, today):
"""月任务,每一个重新取之前一年的金融时间序列数据,重新计算一遍'周几买'"""
end_ind = self.combine_kl_pd[self.combine_kl_pd.date == today.date].key.values[0]
start_ind = end_ind - self.dw_period if end_ind - self.dw_period > 0 else 0
# 根据当前的交易日,切片过去的一年金融时间序列
last_kl = self.combine_kl_pd.iloc[start_ind:end_ind]
# 重新计算一遍'周几买'
self._make_buy_date(last_kl)

def fit_day(self, today):
"""日任务:昨天下跌,今天开盘也下跌,根据今天是周几,在不在序列self.buy_date_week中决定今天买不买"""
if self.yesterday.p_change < 0 and today.open < self.yesterday.close \
and int(today.date_week) in self.buy_date_week:
# 由于没有用到今天的收盘价格等,可以直接使用buy_today
return self.buy_today()
return None

# noinspection PyProtectedMember
def _make_buy_date(self, last_kl):
"""
根据金融时间周期数据切片具体计算'周几'买上涨概率大
:param last_kl: 金融时间周期数据切片,pd.DataFrame
"""
from ..UtilBu import ABuKLUtil

self.buy_date_week = []
# 计算周期内,周期的胜率
last_dw = ABuKLUtil.date_week_win(last_kl)
# 摘取大于阀值self.buy_dw的'周几',buy_dw默认0.55
last_dw_vd = last_dw[last_dw.win >= self.buy_dw]
"""
eg: last_dw_vd
0 1 win
date_week
周四 3 5 0.62
周五 2 6 0.75
"""
if len(last_dw_vd) > 0:
# 如果胜率有符合要求的,使用周几平均涨幅计算date_week_mean
last_dwm = ABuKLUtil.date_week_mean(last_kl)
# 摘取满足胜率的last_dw_vd
last_dwm_vd = last_dwm.loc[last_dw_vd.index]
"""
eg: last_dwm_vd
_p_change
date_week
周四 1.55
周五 1.12
"""
# 阀值计算方式1
dwm1 = abs(last_dwm.sum()).values[0] / self.buy_dwm
# 阀值计算方式2
dwm2 = abs(last_dwm._p_change).mean() / self.buy_dwm
# 如果symbol多可以使用&的关系
dm_effect = (last_dwm_vd._p_change > dwm1) | (last_dwm_vd._p_change > dwm2)
buy_date_loc = last_dwm_vd[dm_effect].index
"""
eg: buy_date_loc
Index(['周四', '周五'], dtype='object', name='date_week')
"""
if len(buy_date_loc) > 0:
# 如果涨跌幅阀值也满足,tolist,eg:['周一', '周二', '周三', '周四', '周五']
dw_index = last_dw.index.tolist()
# 如果是一周5个交易日的就是4,如果是比特币等7天交易日的就是6
max_ind = len(dw_index) - 1
for bdl in buy_date_loc:
sell_ind = dw_index.index(bdl)
buy_ind = sell_ind - 1 if sell_ind > 0 else max_ind
self.buy_date_week.append(buy_ind)
5 changes: 4 additions & 1 deletion abupy/FactorBuyBu/__init__.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
from __future__ import absolute_import

from .ABuFactorBuyBase import AbuFactorBuyBase, AbuFactorBuyXD, BuyCallMixin, BuyPutMixin
from .ABuFactorBuyBase import AbuFactorBuyBase, AbuFactorBuyXD, AbuFactorBuyTD, BuyCallMixin, BuyPutMixin
from .ABuFactorBuyBreak import AbuFactorBuyBreak, AbuFactorBuyPutBreak
from .ABuFactorBuyWD import AbuFactorBuyWD
from .ABuFactorBuyDemo import AbuSDBreak, AbuTwoDayBuy, AbuFactorBuyBreakUmpDemo
from .ABuFactorBuyDemo import AbuFactorBuyBreakReocrdHitDemo, AbuFactorBuyBreakHitPredictDemo

__all__ = [
'AbuFactorBuyBase',
'AbuFactorBuyXD',
'AbuFactorBuyTD',
'BuyCallMixin',
'BuyPutMixin',
'AbuFactorBuyBreak',
'AbuFactorBuyWD',
'AbuFactorBuyPutBreak',
'AbuFactorBuyBreakUmpDemo',
'AbuFactorBuyBreakReocrdHitDemo',
Expand Down
2 changes: 1 addition & 1 deletion abupy/FactorSellBu/ABuFactorSellBase.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,4 +209,4 @@ def support_direction(self):

def fit_day(self, today, orders):
"""raise NotImplementedError"""
raise NotImplementedError('NotImplementedError support_direction')
raise NotImplementedError('NotImplementedError fit_day')
3 changes: 2 additions & 1 deletion abupy/FactorSellBu/ABuFactorSellNDay.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class AbuFactorSellNDay(AbuFactorSellBase):
def _init_self(self, **kwargs):
"""kwargs中可以包含: 参数sell_n:代表买入后持有的天数,默认1天"""
self.sell_n = kwargs.pop('sell_n', 1)
self.is_sell_today = kwargs.pop('is_sell_today', False)
self.sell_type_extra = '{}:sell_n={}'.format(self.__class__.__name__, self.sell_n)

def support_direction(self):
Expand All @@ -36,4 +37,4 @@ def fit_day(self, today, orders):
order.keep_days += 1
if order.keep_days >= self.sell_n:
# 只要超过self.sell_n即卖出
self.sell_tomorrow(order)
self.sell_today(order) if self.is_sell_today else self.sell_tomorrow(order)
6 changes: 4 additions & 2 deletions abupy/MarketBu/ABuSymbolPd.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,8 +338,10 @@ def combine_pre_kl_pd(kl_pd, n_folds=1):
pre_kl_pd = make_kl_df(kl_pd.name, data_mode=ABuEnv.EMarketDataSplitMode.E_DATA_SPLIT_SE, n_folds=n_folds,
end=end)
# 再合并两段时间序列,pre_kl_pd[:-1]跳过重复的end
pre_kl_pd = kl_pd if pre_kl_pd is None else pre_kl_pd[:-1].append(kl_pd)
return pre_kl_pd
combine_kl = kl_pd if pre_kl_pd is None else pre_kl_pd[:-1].append(kl_pd)
# 根据combine_kl长度重新进行key计算
combine_kl['key'] = list(range(0, len(combine_kl)))
return combine_kl


def calc_atr(kline_df):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1294,10 +1294,7 @@
" 与AbuFactorBuyBreak基本相同,唯一区别是关键子参数中添加了通过AbuFactorBuyBreakUmpDemo记录训练好的决策器\n",
" self.hit_ml = kwargs['hit_ml']\n",
" \"\"\"\n",
" # 突破参数 xd, 比如20,30,40天...突破, 不要使用kwargs.pop('xd', 20), 明确需要参数xq\n",
" self.xd = kwargs['xd']\n",
" # 在输出生成的orders_pd中显示的名字\n",
" self.factor_name = '{}:{}'.format(self.__class__.__name__, self.xd)\n",
" super(AbuFactorBuyBreakHitPredictDemo, self)._init_self(**kwargs)\n",
" # 添加了通过AbuFactorBuyBreakUmpDemo记录训练好的决策器\n",
" self.hit_ml = kwargs['hit_ml']\n",
"\n",
Expand All @@ -1316,7 +1313,7 @@
" if result == -1:\n",
" return True\n",
" return False\n",
"\n",
" \n",
"# 通过import的方式导入AbuFactorBuyBreakHitPredictDemo\n",
"# 因为在windows系统上,启动并行后,在ipython notebook中定义的类会在子进程中无法找到\n",
"from abupy import AbuFactorBuyBreakHitPredictDemo"
Expand Down
Loading

0 comments on commit 72bd820

Please sign in to comment.