# 基础库导入

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()

us_choice_symbols = ['usTSLA', 'usNOAH', 'usSFUN', 'usBIDU', 'usAAPL', 'usGOOG', 'usWUBA', 'usVIPS']
cn_choice_symbols = ['002230', '300104', '300059', '601766', '600085', '600036', '600809', '000002', '002594', '002739']
hk_choice_symbols = ['hk03333', 'hk00700', 'hk02333', 'hk01359', 'hk00656', 'hk03888', 'hk02318']
from abupy import ABuSymbolPd, AbuUpDownTrend, AbuDownUpTrend, AbuUpDownGolden, AbuMetricsBase
from abupy import AbuFactorCloseAtrNStop, AbuFactorAtrNStop, AbuFactorPreAtrNStop, tl, ABuProgress
from abupy import GridSearch, AbuCrossVal, WidgetCrossVal, EMarketTargetType, abu, WidgetGridSearch

"""
Explanation: ABU量化系统使用文档
<center>
        <img src="./image/abu_logo.png" alt="" style="vertical-align:middle;padding:10px 20px;"><font size="6" color="black"><b>第32节 策略有效性的验证</b></font>
</center>

作者: 阿布
阿布量化版权所有 未经允许 禁止转载
abu量化系统github地址 (欢迎+star)
本节ipython notebook
上一节讲解资金仓位管理与买入策略搭配的示例，本节讲解策有效性的验证。
首先导入本节需要使用的abupy中的模块： 
本节大多数内容无法使用沙盒数据运行，需要下载缓存，百度云地址如下：
csv格式美股，A股，港股，币类，期货6年日k数据 密码: gvtr
End of explanation
"""


# 初始资金量
cash = 3000000
def run_loo_back(choice_symbols, ps=None, n_folds=3, 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()
    metrics = AbuMetricsBase.show_general(*abu_result_tuple, returns_cmp=only_info, 
                                only_info=only_info,
                                only_show_returns=True)
    return abu_result_tuple, metrics

"""
    买入策略使用AbuDownUpTrend:
    短线基数xd=30: 30个交易日整体趋势为上涨趋势, 长线下跌乘数基数, 海龟突破的30日突破
    长线乘数past_factor=4: xd * 4 = 30 * 4 = 120 过去120个交易日整体趋势为下跌趋势
    趋势角度阀值down_deg_threshold: 判定趋势是否为上涨下跌的拟合角度值为+-4
"""
buy_factors = [{'class': AbuDownUpTrend, 'xd': 30, 'past_factor': 4, 'down_deg_threshold': -4}]
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}]
# 开始回测
_, _ = run_loo_back(us_choice_symbols, only_info=True)

"""
Explanation: 1. UI界面: Grid Search寻找策略最优参数
30，31两节都使用了abupy内置的一个长短线买入策略AbuDownUpTrend，策略默认的参数实现如下：
1. 寻找长线下跌的股票，比如一个季度(4个月)整体趋势为下跌趋势
2. 短线走势上涨的股票，比如一个月整体趋势为上涨趋势
3. 最后使用海龟突破的xd日突破策略作为策略最终买入信号
上面策略描述的‘一个季度(4个月)’，‘xd日突破’都是策略的默认参数，通过改变设置因子的默认参数可以修改值，而且做为判断趋势是否为上涨下跌的趋势拟合角度阀值也是可以设置的，如下将策略修改为：

寻找长线下跌的股票，过去120个交易日整体趋势为下跌趋势
短线走势上涨的股票，过去30个交易日整体趋势为上涨趋势
最后使用30日突破策略作为策略最终买入信号
判定趋势是否为上涨下跌的拟合角度值为+-4

代码示例如下：
End of explanation
"""


# 直接启动grid search界面
WidgetGridSearch()()

"""
Explanation: 从回测结果中可以看到最终收益为正值，由于使用较高的止盈位，偏倚重盈亏比值，胜率不高，最终策略是否应该使用这组参数，即最优参数的选择在‘第7节-寻找策略最优参数和评分’中讲解过，这里不过多赘述，只示例更简洁的接口使用以及ui操作。
首先示例ui界面的操作使用WidgetGridSearch，如下：
备注：

ui具体操作步骤在之后的视频教程中详细示例
更多界面操作请直接运行abupy/abupy_ui/文件夹下的界面功能文件

更多界面操作简单示例
End of explanation
"""


buy_factors = {'class': AbuDownUpTrend, 'xd': [20, 30, 40], 
               'past_factor': [3, 4, 5], 'down_deg_threshold': [-2, -3, -4]}
sell_factors = [{'class': AbuFactorAtrNStop, 'stop_loss_n': [0.5, 1.0, 1.5], 
                'stop_win_n': [0.5, 1.0, 2.0, 3.0]},
               ]
# 使用类方法GridSearch.grid_search进行最优参数查找
scores, score_tuple_array = GridSearch.grid_search(us_choice_symbols, buy_factors, sell_factors)

"""
Explanation: 2. 代码实现Grid Search寻找策略最优参数
如下可以使用GridSearch.grid_search寻找买入AbuDownUpTrend策略参数：

短线基数'xd': [20, 30, 40]
长线乘数'past_factor': [3, 4, 5]
趋势角度阀值'down_deg_threshold':  [-2, -3, -4]

寻找卖出AbuFactorAtrNStop策略参数：

止损atr倍数stop_loss_n：[0.5, 1.0, 1.5]
止盈atr倍数stop_win_n：[0.5, 1.0, 2.0, 3.0]
End of explanation
"""


GridSearch.show_top_score_metrics(scores, score_tuple_array, top_cnt=3)

"""
Explanation: 上述最优结果输出为对所有参数评分结果的最优结果，由于策略的苛刻买入条件导致整体交易数量少，所以很多时候需要根据top n的输出结果来进行筛选使用的最优参数。
使用show_top_score_metrics多显示一些最优结果，top_cnt为正数时显示评分最高的参数组合及回测结果，top_cnt为负数时相反，如下top_cnt=3显示度量结果最好的3组参数组合及回测结果：
End of explanation
"""


GridSearch.show_top_score_metrics(scores, score_tuple_array, top_cnt=-1)

"""
Explanation: 如下top_cnt=-1显示度量结果最差的回测参数组合及回测结果：
End of explanation
"""


# 交叉相关性策略验证只支持本地非沙盒数据模式
abupy.env.disable_example_env_ipython()
# 使用上面grid search结果的top1参数组合进行验证
buy_factors = [{'class': AbuDownUpTrend, 'down_deg_threshold': -2, 'past_factor': 5, 'xd': 20}]
sell_factors = [{'stop_loss_n': 1, 'stop_win_n': 0.5,
                 'class': AbuFactorAtrNStop},
                {'class': AbuFactorPreAtrNStop, 'pre_atr_n': 1.5},
                {'class': AbuFactorCloseAtrNStop, 'close_atr_n': 1.5}]

cross_val = AbuCrossVal()
cross_val.fit(buy_factors, sell_factors, cv=10)

"""
Explanation: 3 交叉相关性策略验证
通过gird search可以暂时选定策略的参数，但是进一步验证策略的有效性，普适性就需要扩大策略的回测范围以及symbol数量，可以使用全市场对策略进行多年的历史回测来验证策略的有效性，但更推荐使用abupy中内置的交叉相关性策略验证模块，它整体思路如下：

选择一个市场中所有的symbol，计算所有的symbol与大盘指标的相关系数值
根据计算出的相关系数值将全市场中的symbol切分成多个组
依次从各个symbol相关系数组中多次进行抽取几个symbol进行历史交易策略回测
合并计算所有相关系数组的交易回测度量结果，进行输出
合并计算每一个相关系数组中多次进行抽取的symbol的交易回测度量结果，进行输出

abupy中内置的AbuCrossVal实现了上述功能，下面为使用示例：
首先使用上面grid search结果的top1参数组合进行验证：


买入策略:[{'past_factor': 3, 'xd': 30, 'down_deg_threshold': -4}]


卖出策略:[{'stop_loss_n': 1.5, 'stop_win_n': 3.0}]


代码如下所示：
End of explanation
"""


def constraints(scores, score_tuple_array, top_cnt):
    result_top = []
    for sc_ind in scores.index:
        for sell_factor in score_tuple_array[sc_ind].sell_factors:
            if 'stop_win_n' in sell_factor and sell_factor['stop_win_n'] == 3.0:
                result_top.append(score_tuple_array[sc_ind])
        if len(result_top) >= top_cnt:
            return result_top
    return result_top
# top_cnt=1：输出stop_win_n=3.0下最好的参数组合度量结果
GridSearch.show_top_constraints_metrics(constraints, scores, score_tuple_array, top_cnt=1)

"""
Explanation: 上最终输出为

合并计算所有相关系数组的交易回测度量结果
合并计算每一个相关系数组中多次进行抽取的symbol的交易回测度量结果
由于涉及随机抽取所以这里每次的运行结果都会不一样

有效性判定需要根据整体策略的风格来判断：
上面的激进风格的止盈止损设置，止盈位倍数(1) < 止损位倍数(0.5)，且值偏低：

代表交易策略整体风格为均值回复类型
需要交叉相关性分组结果的高胜率，盈亏比可以偏低
注意很多组的平均获利期望都比平均亏损期望低。

普适性的判定需要在有效性的基础上针对不同相关性组进行判定：

上面回测结果中所有相关性回测组的胜率都大于55%, 一定程度代表普适性很好
一般10组中有7组以上符合判定就可以认为策略良好。

上面的运行结果由于使用随机抽取的原因每一次的运行结果都不同，所以可以多运行几次，也可以切换不同的交易市场进行验证。
上面验证策略使用了低止盈位形成的整体策略风格为均值回复，如果要构建整体策略风格为趋势跟踪需要比较高的止盈位，但上面使用
GridSearch.show_top_score_metrics

输出前top个结果都是均值回复类型的策略参数，可以使用
GridSearch.show_top_constraints_metrics

限制top的排序结果，比如下面的示例要求止盈位stop_win_n的值为3.0的情况下上面所有度量结果的排序top，代码如下所示：
End of explanation
"""


# grid search结果的带参数限制条件的top1参数
buy_factors = [{'class': AbuDownUpTrend, 'down_deg_threshold': -3, 'past_factor': 4, 'xd': 20}]
# 限制条件为stop_win_n值为3.0
sell_factors = [{'stop_loss_n': 1.5, 'stop_win_n': 3.0,
                 'class': AbuFactorAtrNStop},
                {'class': AbuFactorPreAtrNStop, 'pre_atr_n': 1.5},
                {'class': AbuFactorCloseAtrNStop, 'close_atr_n': 1.5}]
cross_val.fit(buy_factors, sell_factors, cv=10)

"""
Explanation: 上面带参数限制条件的输出即为止盈位为3.0情况下的最好度量结果以及参数组合。
下面将带参数限制条件的top1参数组合进行相关交叉验证：

买入策略:[{'past_factor': 4, 'xd': 20, 'down_deg_threshold': -4}]
卖出策略:[{'stop_loss_n': 1.0, 'stop_win_n': 3.0}]

代码以及运行结果如下所示：
End of explanation
"""


cross_val.show_cross_val_se(start=8, end=10)

"""
Explanation: 上面的整体风格的止盈止损设置，止盈位倍数(3.0) >> 止损位倍数(1.0)，止盈位偏高：

代表交易策略整体风格为趋势跟踪类型
总体交叉验证交易度量胜率低于50%符合趋势跟踪风格，
所有平均获利期望 > 平均亏损期望低。
有一组相关回测组的胜率以及盈亏比都偏低导致收益总盈亏为负值

为保证策略的有效，可以多次运行进行验证。
fit运行后所有的交易回测结果都可以使用show_cross_val_se进行查看，通过start，end参数设置游标，如下示例：
End of explanation
"""


cross_val.plot_all_cross_val_orders()

"""
Explanation: 4. 人工分析比人工智能要聪明, 懂事
除了上述最终度量结果做为策略最终有效性，普适性的判别标准外，还可以把上面相关交叉验证回测的所有交易都通过可视化接口保存在本地，然后一个一个的查看交易的买入点与卖出点，排查策略是否存在问题，以及是否存在改进方案等。
可以使用plot_all_cross_val_orders接口对交叉验证回测的所有交易进行保存，保存结果如下所示：

备注：再次强调：对交易策略结果的人工分析，注重分析失败的结果以及是否存在改进方案，改进方案是否会引进新的问题是非常重要的，不要排斥人工, 人工分析比人工智能要聪明, 懂事。
End of explanation
"""


if abupy.env.g_is_mac_os:
    !open $abupy.env.g_project_data_dir
else:
    !echo $abupy.env.g_project_data_dir

"""
Explanation: 保存完成后，快照将保存在~/abu/data/save_png下当前日期的文件夹中，可使用如下命令直接打开查看：
End of explanation
"""


WidgetCrossVal()()

"""
Explanation: 5. UI界面: 交叉相关性策略验证
与寻找最优参数类似，可以使用WidgetCrossVal进行界面操作进行交叉相关性策略验证，如下：
备注：

ui具体操作步骤在之后的视频教程中详细示例
更多界面操作请直接运行abupy/abupy_ui/文件夹下的界面功能文件

更多界面操作简单示例
End of explanation
"""


