# 基础库导入

from __future__ import print_function
from __future__ import division

import warnings
warnings.filterwarnings('ignore')
warnings.simplefilter('ignore')

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline

import os
import sys
# 使用insert 0即只使用github，避免交叉使用了pip安装的abupy，导致的版本不一致问题
sys.path.insert(0, os.path.abspath('../'))
import abupy

# 使用沙盒数据，目的是和书中一样的数据环境
abupy.env.enable_example_env_ipython()

from abupy import AbuFactorSellNDay, AbuFactorBuyWD, AbuPickStockNTop
from abupy import AbuFactorBuyBreak, AbuFactorAtrNStop, AbuFactorPreAtrNStop, AbuWeekMonthBuy
from abupy import abu, AbuFactorCloseAtrNStop, ABuProgress, AbuMetricsBase, EMarketTargetType

"""
Explanation: ABU量化系统使用文档
<center>
        <img src="./image/abu_logo.png" alt="" style="vertical-align:middle;padding:10px 20px;"><font size="6" color="black"><b>第27节 狗股选股策略与择时策略的配合</b></font>
</center>

作者: 阿布
阿布量化版权所有 未经允许 禁止转载
abu量化系统github地址 (欢迎+star)
本节ipython notebook
上一节示例了周期均值回复短线择时策略，本节的内容将讲解选股策略与择时策略相互配合的示例。
择时与选股操作是交易系统中两大重点，它们之间的关系是相辅相成的，比如上一节实现的AbuFactorBuyWD策略本质上属于均值回复策略，因为它的买入前提是昨天下跌，今天开盘继续下跌，但是明天是周期内统计上涨概率最大的‘星期几’，量化交易本质上在策略中要做事情只有一个制造非均衡交易环境，目的就是为了达到最终非均衡结果赢的钱比输的多，择时策略和选股策略都是为了这一目标在努力，不同的择时策略应该搭配上适合的选股策略来达到1+1>2的目标，只有将选股和择时配合好，并且彻底理解你的策略，最终才能有好的结果。
首先导入本节需要使用的abupy中的模块：
End of explanation
"""


cash = 3000000
# 延用周期突破策略做为买入因子
buy_factors = [{'xd': 21, 'class': AbuFactorBuyBreak},
               {'xd': 42, 'class': AbuFactorBuyBreak}]
# 卖出策略也还是继续延用
sell_factors = [
    {'stop_loss_n': 1.0, 'stop_win_n': 3.0,
     'class': AbuFactorAtrNStop},
    {'class': AbuFactorPreAtrNStop, 'pre_atr_n': 1.5},
    {'class': AbuFactorCloseAtrNStop, 'close_atr_n': 1.5}
]

def run_loo_back(choice_symbols, ps=None, n_folds=2, start=None, end=None, only_info=False):
    """封装一个回测函数"""
    if choice_symbols[0].startswith('us'):
        abupy.env.g_market_target = EMarketTargetType.E_MARKET_TARGET_US
    else:
        abupy.env.g_market_target = EMarketTargetType.E_MARKET_TARGET_CN
    abu_result_tuple, _ = abu.run_loop_back(cash,
                                           buy_factors,
                                           sell_factors,
                                           ps,
                                           start=start,
                                           end=end,
                                           n_folds=n_folds,
                                           choice_symbols=choice_symbols)
    ABuProgress.clear_output()
    AbuMetricsBase.show_general(*abu_result_tuple, returns_cmp=only_info, 
                                only_info=only_info,
                                only_show_returns=True)
    return abu_result_tuple

# 使用沙盒内的美股做为回测目标
us_choice_symbols = ['usTSLA', 'usNOAH', 'usSFUN', 'usBIDU', 'usAAPL', 
                     'usGOOG', 'usWUBA', 'usVIPS']
_ = run_loo_back(us_choice_symbols)

"""
Explanation: 1. 狗股理论进行选股
狗股理论是美国基金经理迈克尔·奥希金斯于1991年提出的一种投资策略。

投资股票是为了获取回报，纸上富贵固然令人热血沸腾，但现金收入才是实实在在的回报。现金收入来自哪里？可以是公司分红，也可以是卖出股票后获得的差价，即股息和资本利得。
公司股息取决于经营状况，如果业绩稳定，分红政策稳定，那么就可以有稳定的现金回报。股价虽然最终取决于公司经营状况，但在很多情况下会发生背离，在市场火爆时股价被捧上天，在市场低迷时又被打入谷底，所以与股息回报相比，资本利得的稳定性不高，不易把握。

只所以被称作狗股理论是因为将股票比做骨头，将股息比喻为骨头上的肉，狗会先吃肉最多的骨头。

狗股策略具体的做法是，投资者每年年底从道琼斯工业平均指数成份股中找出10只股息率最高的股票，第二年买入这10支股票，一年后再找出10只股息率最高的成分股，卖出手中不在名单中的股票，买入新上榜单的股票，每年年初年底都重复这一投资动作，便可获取超过大盘的回报。
下面首先延用周期突破策略做为买入因子，卖出策略也还是继续延用，先回测未使用狗骨选股策略的情况，如下：
End of explanation
"""


stock_pickers = [{'class': AbuPickStockNTop,
                  'symbol_pool': us_choice_symbols, 'n_top': 3}]
abu_result_tuple = run_loo_back(us_choice_symbols, stock_pickers)

"""
Explanation: 狗股理论使用的是参考值为股息率，很多基于狗股理论的选股策略进行了基因变种，如使用PEG替换股息率进行选股，或者直接使用上一年度的涨幅值做为选股参数，基于基本面数据进行选股的示例将在之后的章节进行示例，本节首先基于涨幅值做为狗股选股的参数。
abupy中内置的选股因子AbuPickStockNTop即是在选股周期上对多只股票涨跌幅进行排序，选取top n个股票做为交易目标，如下示例使用AbuPickStockNTop在选股周期内选择涨幅最大的top3做为交易目标，如下：
End of explanation
"""


set(abu_result_tuple.orders_pd.symbol)

"""
Explanation: 结果如上所示，下面从交易单中可以看到整个择时周期内策略只对三支股票进行了择时：
End of explanation
"""


buy_factors = [{'xd': 21, 'class': AbuFactorBuyBreak, 'stock_pickers': stock_pickers},
               {'xd': 42, 'class': AbuFactorBuyBreak, 'stock_pickers': stock_pickers}]
abu_result_tuple = run_loo_back(us_choice_symbols)

"""
Explanation: 上面虽然也达到了选股的目的，但是整个择时周期内只运行了一次选股策略，即只完成了静态选股，后面的择时周期内很有可能随着时间的推进，选股目标发生了变化，为了解决这个问题，可以将选股策略序列做为择时因子策略的一个参数进行构造，如下所示，这样选股策略即做为择时策略的一个专属因子，它默认在择时周期内每一个月都会重新进行一次选股策略，即完成了动态选股（也可以设置参数改变周期），使用如下所示：
End of explanation
"""


set(abu_result_tuple.orders_pd.symbol)

"""
Explanation: 结果如上所示，下面从交易单中可以看到整个择时周期内策略对多支股票进行了择时，并非只有3支：
End of explanation
"""


orders_pd = abu_result_tuple.orders_pd
date_ind = orders_pd.index

def print_month_trade(base_year, range_month):
    month_fmt = lambda year, mt: '{}-0{}-01'.format(
        year, mt) if mt < 10 else '{}-{}-01'.format(year, mt)
    for month in range_month:
        if month < 12:
            next_month = month + 1
            trade_year = base_year
        else:
            next_month = 1
            trade_year = base_year + 1 
        print('{}-{}月选股交易品种{}'.format(trade_year, month, 
                                       set(orders_pd[(date_ind > month_fmt(trade_year, month)) & 
                                                     (date_ind < month_fmt(trade_year, next_month))].symbol)))
        
print_month_trade(2014, np.arange(9, 13))
print_month_trade(2015, np.arange(1, 13))
print_month_trade(2016, np.arange(1, 8))

"""
Explanation: 下面更进一步打印出择时周期内每一个月的择时交易目标，可以看到每一个月最多的交易目标数量是3个，即这个月选股结果的3个交易目标都发生了突破的情况，如下所示：
End of explanation
"""


"""
    构建选股策略使用AbuPickStockNTop，n_top=3, 注意参数xd=20, 
    即选股分析周期为20天(选股目标为上一个月涨幅最高的3支股票)
"""
stock_pickers = [{'class': AbuPickStockNTop,
                  'symbol_pool': us_choice_symbols, 'n_top': 3, 'xd': 20}]
"""
    买入因子AbuWeekMonthBuy参数is_buy_month=True在月末买入，
    stock_pickers做为买入因子的专属选股因子进行动态选股
"""
buy_factors = [{'class': AbuWeekMonthBuy, 'is_buy_month': True, 
                'stock_pickers': stock_pickers}]
"""卖出因子使用AbuFactorSellNDay，sell_n=20, 即针对买入的股票持有20个交易日（一个月）后卖出"""
sell_factors = [{'class': AbuFactorSellNDay, 'sell_n': 20, 'is_sell_today': True}]
# 开始回测
abu_result_tuple = run_loo_back(us_choice_symbols)

"""
Explanation: 上面的选股策略分别使用静态和动态两种模式实现，有些类似静态市盈率和动态市盈率的计算。
2. 选股策略与择时策略的配合
本节的重点是讲解择时策略和选股策略的配合，达到1+1>2的目标，首先你要彻底理解你的策略，上面示例的狗骨策略选取的是正向涨幅最大的top支股票，为配合这个选股结果最简单的择时策略可以是每一个月进行一次买入操作，买入上一个月涨幅最高的top支股票，持有一个月后卖出，是一个趋势跟踪的策略：
End of explanation
"""


pd.options.display.max_rows = 21
abu_result_tuple.orders_pd.filter(['symbol', 'buy_date', 'buy_factor', 
                                   'sell_date', 'sell_type_extra', 'profit'])[:21]

"""
Explanation: 针对美股市场的回测如上所示，下面输出交易单可以看到每一个月在月底都买入了3支股票，持有一个月后卖出操作，如下：
End of explanation
"""


# A股中的沙盒symbol数据
cn_choice_symbols = ['002230', '300104', '300059', '601766', '600085', 
                     '600036', '600809', '000002', '002594']
# 和上面类似，只是symbol_pool=cn_choice_symbols
stock_pickers = [{'class': AbuPickStockNTop,
                  'symbol_pool': cn_choice_symbols, 'n_top': 3, 'xd': 20}]
buy_factors = [{'class': AbuWeekMonthBuy, 'is_buy_month': True, 
                'stock_pickers': stock_pickers}]
abu_result_tuple = run_loo_back(cn_choice_symbols)

"""
Explanation: 下面将市场切换到沙盒数据中的A股以相同的选股，择时策略进行回测，如下：
End of explanation
"""


stock_pickers = [{'class': AbuPickStockNTop,
                  'symbol_pool': cn_choice_symbols, 'n_top': 3, 
                  'direction_top': -1, 'xd': 20}]
buy_factors = [{'class': AbuWeekMonthBuy, 'is_buy_month': True, 
                'stock_pickers': stock_pickers}]
abu_result_tuple = run_loo_back(cn_choice_symbols, only_info=True)

"""
Explanation: 上面使用的择时策略AbuWeekMonthBuy本身是一个中性策略，没有明确的方向，即择时策略本身不属于趋势跟踪也不属于均值回复，通过搭配不同的选股策略才能明确择时策略本身的属性，如上例配合形成的趋势跟踪策略。
将上例AbuPickStockNTop选股策略中的direction_top=-1, AbuPickStockNTop选股策略中direction_top参数的意义为选取方向，默认为1，即选取涨幅最高的n_top个股票，传递-1即选取跌幅最高的n_top个股票，这样搭配上AbuWeekMonthBuy最终生效的策略将变成一个均值回复策略，买入上一个月跌幅最高的top支股票，每一个月进行一次买入操作, 持有一个月后卖出，如下所示：
End of explanation
"""


"""
    xd=40: 为匹配择时策略AbuFactorBuyWD中的默认周期
    direction_top=-1: 选取跌幅最高的n_top
"""
stock_pickers = [{'class': AbuPickStockNTop,
                  'symbol_pool': us_choice_symbols, 'n_top': 3, 
                  'direction_top': -1, 'xd': 40}]
"""
    买入因子AbuFactorBuyWD参数stock_pickers做为买入因子的专属选股因子进行动态选股
"""
buy_factors = [{'class': AbuFactorBuyWD, 'stock_pickers': stock_pickers}]
# 卖出策略使用AbuFactorSellNDay，sell_n=1即只持有一天
sell_factors = [{'class': AbuFactorSellNDay, 'sell_n': 1, 'is_sell_today': True}]
abu_result_tuple = run_loo_back(us_choice_symbols, 
                                start='2013-07-26', end='2016-07-26', only_info=True)

"""
Explanation: 3. 进一步制造非均衡，提高交易概率优势
上面示例了中性择时策略配合选股策略分别形成趋势跟踪与均值回复策略，下面将示例通过选股策略配合原本带属性的择时策略，进一步在整个策略中制造非均衡，提高交易概率优势
下面在上一节使用的AbuFactorBuyWD的基础上配合使用AbuPickStockNTop做为选股策略，AbuFactorBuyWD的择时策略为

默认以40天为周期(8周)结合涨跌阀值计算周几适合买入
回测运行中每一个月重新计算一次上述的周几适合买入
在策略日任务中买入信号为：昨天下跌，今天开盘也下跌，且明天是计算出来的上涨概率大的'周几'

下面构建AbuPickStockNTop时也以xd=40天为一个周期，计算周期内跌幅最大的3个股票，即整个策略在上面3条择时策略的基础上又添加了一条如下：
4. 每一个月计算一次上两个月(xd=40)中跌幅最大的3支股票做为交易目标
实现如下所示：
End of explanation
"""


buy_factors = [{'class': AbuFactorBuyWD}]
sell_factors = [{'class': AbuFactorSellNDay, 'sell_n': 1, 'is_sell_today': True}]
abu_result_tuple = run_loo_back(us_choice_symbols, 
                                start='2013-07-26', end='2016-07-26', only_info=True)

"""
Explanation: 回测结果上所示，下面使用上一节不使用选股策略进行配合的回测进行对比，如下：
End of explanation
"""


