import os
import sys
from pathlib import PurePath
this_file_path = PurePath(os.getcwd())
sys.path.append(str(this_file_path.parents[1]))
from typing import Dict, Tuple, List, Union
from collections import namedtuple
import pandas as pd
import numpy as np
import empyrical as ep
from talib import BETA
from scr import LoadData
from hugos_toolkit.BackTestTemplate import get_backtesting
from hugos_toolkit.BackTestReport.tear import analysis_rets, analysis_trade
import seaborn as sns
import matplotlib as mpl
import matplotlib.pyplot as plt
plt.rcParams["font.sans-serif"] = ["SimHei"] # 用来正常显示中文标签
plt.rcParams["axes.unicode_minus"] = False # 用来正常显示负号
def calc_gsisi(
close_ser: pd.Series, pivot_swprice: pd.DataFrame, window: int, pct_window: int = 5
) -> pd.Series:
"""投资者情绪指数
Args:
close_ser (pd.Series): 默认为沪深300收盘价
pivot_swprice (pd.DataFrame): 申万收盘数据 index-date columns-swcode values-close
window (int): sw与close计算beta的窗口
pct_window (int, optional): 收益率计算窗口. Defaults to 5.
Returns:
pd.Series: spearman index-date values-spearman
"""
# 周度收益
sw_pct: pd.DataFrame = pivot_swprice.pct_change(pct_window)
index_pct: pd.Series = close_ser.pct_change(pct_window)
# 计算beta
beta_roll5: pd.DataFrame = sw_pct.apply(lambda x: BETA(x, index_pct, window))
# spearman
return sw_pct.corrwith(beta_roll5, method="spearman", axis=1)
def get_gsisi_bt(
index_price: pd.DataFrame,
pivot_swprice: pd.DataFrame,
window: int,
pct_window: int = 5,
**kw
) -> namedtuple:
"""GSISI回测
Args:
index_price (pd.DataFrame): 默认为hs300收盘价 index-date values-close
pivot_swprice (pd.DataFrame): 申万行业收盘为 index-date columns-sw_code values-close
window (int): beta与pct的滚动窗口
pct_window (int, optional): 收益率计算窗口. Defaults to 5.
Returns:
namedtuple: bt_result result,cerebro
"""
data: pd.DataFrame = index_price.copy()
data["GSISI"] = calc_gsisi(index_price["close"], pivot_swprice, window, pct_window)
data.dropna(subset=["GSISI"], inplace=True)
data.index = pd.to_datetime(data.index)
return get_backtesting(data, "HS300", **kw)
def opt_strat(
index_price: pd.DataFrame,
pivot_swprice: pd.DataFrame,
window: int,
pct_window: int = 5,
score_name: str = "_SQN",
**kw
) -> float:
"""用于寻找参数
Args:
index_price (pd.DataFrame): 默认为hs300收盘价 index-date values-close
pivot_swprice (pd.DataFrame): 申万行业收盘为 index-date columns-sw_code values-close
window (int): beta与pct的滚动窗口
pct_window (int, optional): 收益率计算窗口. Defaults to 5.
score_name (str, optional): 目标参数. Defaults to "_SQN".
Returns:
float: 目标参数的数值
"""
bt_res: namedtuple = get_gsisi_bt(
index_price, pivot_swprice, window, pct_window, **kw
)
anzs = bt_res.result[0].analyzers
fields: Dict = {
"_Sharpe": "sharperatio",
"_SQN": "sqn",
"_Returns": "rnorm",
}
return anzs.getbyname(score_name).get_analysis()[fields[score_name]]
基本思路是首先计算28个申万一级行业周收益率以及其相对沪深300指数的周Beta系数;然后测算28个申万一级行业周收益率与其周Beta系数的Spearman秩相关系数;最后以Spearman秩相关系数为基础构建国信投资者情绪指数GSISI。
# 数据加载
data = LoadData()
pivot_swprice:pd.DataFrame = data.pivot_swprice
index_price:pd.DataFrame = data.index_price
benchmark_close: pd.Series = index_price["close"]
benchmark_close.index = pd.to_datetime(benchmark_close.index)
# 滚动5日收益率
window1:int = 5
sw_pct:pd.DataFrame = pivot_swprice.pct_change(window1)
index_pct:pd.Series = benchmark_close.pct_change(window1)
window2: int = 50
# 计算beta
beta_roll5: pd.DataFrame = sw_pct.apply(
lambda x: BETA(x, index_pct, window2)
)
# 计算每个行业与基准的spearman
sw_roll5_pct_rank: pd.DataFrame = sw_pct.rolling(window2).rank()
sw_roll5_beta_rank: pd.DataFrame = beta_roll5.rolling(window2).rank()
sw_spearman: pd.DataFrame = sw_roll5_beta_rank.rolling(window2).corr(sw_roll5_pct_rank)
spearman_roll5: pd.DataFrame = calc_gsisi(benchmark_close, pivot_swprice, window2)
当国信投资者情绪指数$GSISI \geq 31.7$时,投资乐观情绪上扬;当国信投资者情绪指数$GSISI \leq -31.7$时,投资悲观情绪蔓延。
该阈值来源为:
对Spearman秩相关系数进行显著性检验,显著性水平$\alpha=0.05$,n=28,查表得Spearman秩相关系数的临界值为0.317
这里我们的申万有31个行业,显著性水平$\alpha=0.05$,n=31,查表临界值应该为0.301
从数据上来看开平仓标准差不多是信号的2倍标准差,感觉可以使用该数据
avg:float = spearman_roll5.mean()
std:float = spearman_roll5.std()
std_up:float = avg + std * 2
std_low:float = avg - std * 2
print('均值:{:.4f},2 Std:{:.4f},-2 Std:{:.4f}'.format(avg,std_up,std_low))
ax = sns.histplot(spearman_roll5)
ax.axvline(std_up,ls='--',color='red')
ax.axvline(std_low,ls='--',color='green')
ax.axvline(avg,ls='--',color='black');
均值:0.0032,2 Std:0.3828,-2 Std:-0.3765
benchmark_close.plot(figsize=(18, 4), color="r", label="Close")
spearman_roll5.plot(color="darkgray", label="GSISI", secondary_y=True, alpha=0.5)
plt.axhline(std_up, ls="--", color="red", alpha=0.5)
plt.axhline(std_low, ls="--", color="green", alpha=0.5);
若GSISI连续两次发出看多(或看空)信号,则看多(或看空)沪深300指数,且保持这个判断,直到连续两次看空(或看多)信号出现,则发生看空(或看多)沪深300指数的反转判断;若GSISI发出多空交叉互现信号,则除最新信号外,前面的交叉信号作废,以最新信号为判断起点,按照前面两条准则重新分析后面的信号。
具体步骤是:
import ipywidgets as ipw
import gradient_free_optimizers as gfo
search_space = {'window': np.arange(5, 100),'pct_window':np.arange(5,21,5)}
iterations = 200
opt = gfo.EvolutionStrategyOptimizer(search_space)
opt.search(lambda x:opt_strat(index_price,pivot_swprice,**x,score_name='_Returns',show_log=False), n_iter=iterations,verbosity=['progress_bar'])
best_params_window = {'window': opt.best_para['window'],'pct_window':opt.best_para['pct_window']}
# _Sharpe/_Returns {'window': 35, 'pct_window': 15} rets 13.01% sharpe 79.76%
# _SQN {'window': 88, 'pct_window': 15} rets 9.11% sharpe 61.07%
bt_res = get_gsisi_bt(index_price,pivot_swprice,**best_params_window)
2014-04-11, 收盘价Close, 2270.67 2014-04-11, 设置买单 BUY CREATE, 2270.67,信号为:0.42,阈值为:0.30 2014-04-14, BUY EXECUTED, ref:1860, Price: 2268.17, Cost: 948951086.94, Comm 189790.22, Size: 418378.00, Stock: HS300 2014-05-29, 收盘价Close, 2155.16 2014-05-29, 设置卖单SELL CREATE, 2155.16信号为:-0.46,阈值为:-0.30 2014-05-30, SELL EXECUTED, ref:1861, Price: 2156.16, Cost: 948951086.94, Comm 1082510.08, Size: -418378.00, Stock: HS300 2014-09-04, 收盘价Close, 2426.22 2014-09-04, 设置买单 BUY CREATE, 2426.22,信号为:0.32,阈值为:0.30 2014-09-05, BUY EXECUTED, ref:1862, Price: 2433.41, Cost: 906955043.97, Comm 181391.01, Size: 372709.00, Stock: HS300 2015-06-26, 收盘价Close, 4336.19 2015-06-26, 设置卖单SELL CREATE, 4336.19信号为:-0.31,阈值为:-0.30 2015-06-29, SELL EXECUTED, ref:1863, Price: 4446.40, Cost: 906955043.97, Comm 1988653.86, Size: -372709.00, Stock: HS300 2015-09-10, 收盘价Close, 3357.56 2015-09-10, 设置买单 BUY CREATE, 3357.56,信号为:0.35,阈值为:0.30 2015-09-11, BUY EXECUTED, ref:1864, Price: 3350.21, Cost: 1611423257.29, Comm 322284.65, Size: 480991.00, Stock: HS300 2015-10-13, 收盘价Close, 3445.04 2015-10-13, 设置卖单SELL CREATE, 3445.04信号为:-0.38,阈值为:-0.30 2015-10-14, SELL EXECUTED, ref:1865, Price: 3430.80, Cost: 1611423257.29, Comm 1980218.91, Size: -480991.00, Stock: HS300 2015-11-24, 收盘价Close, 3753.89 2015-11-24, 设置买单 BUY CREATE, 3753.89,信号为:0.46,阈值为:0.30 2015-11-25, BUY EXECUTED, ref:1866, Price: 3748.13, Cost: 1647061605.29, Comm 329412.32, Size: 439435.00, Stock: HS300 2015-12-17, 收盘价Close, 3755.89 2015-12-17, 设置卖单SELL CREATE, 3755.89信号为:-0.74,阈值为:-0.30 2015-12-18, SELL EXECUTED, ref:1867, Price: 3754.58, Cost: 1647061605.29, Comm 1979875.01, Size: -439435.00, Stock: HS300 2016-06-01, 收盘价Close, 3160.55 2016-06-01, 设置买单 BUY CREATE, 3160.55,信号为:0.38,阈值为:0.30 2016-06-02, BUY EXECUTED, ref:1868, Price: 3158.35, Cost: 1648937601.94, Comm 329787.52, Size: 522089.00, Stock: HS300 2017-05-19, 收盘价Close, 3403.85 2017-05-19, 设置卖单SELL CREATE, 3403.85信号为:-0.38,阈值为:-0.30 2017-05-22, SELL EXECUTED, ref:1869, Price: 3400.25, Cost: 1648937601.94, Comm 2130279.71, Size: -522089.00, Stock: HS300 2017-07-14, 收盘价Close, 3703.09 2017-07-14, 设置买单 BUY CREATE, 3703.09,信号为:0.39,阈值为:0.30 2017-07-17, BUY EXECUTED, ref:1870, Price: 3700.99, Cost: 1766730522.93, Comm 353346.10, Size: 477367.00, Stock: HS300 2018-03-06, 收盘价Close, 4066.56 2018-03-06, 设置卖单SELL CREATE, 4066.56信号为:-0.31,阈值为:-0.30 2018-03-07, SELL EXECUTED, ref:1871, Price: 4063.09, Cost: 1766730522.93, Comm 2327504.19, Size: -477367.00, Stock: HS300 2018-05-11, 收盘价Close, 3872.84 2018-05-11, 设置买单 BUY CREATE, 3872.84,信号为:0.35,阈值为:0.30 2018-05-14, BUY EXECUTED, ref:1872, Price: 3887.26, Cost: 1936581743.50, Comm 387316.35, Size: 498187.00, Stock: HS300 2018-05-30, 收盘价Close, 3723.37 2018-05-30, 设置卖单SELL CREATE, 3723.37信号为:-0.36,阈值为:-0.30 2018-05-31, SELL EXECUTED, ref:1873, Price: 3748.93, Cost: 1936581743.50, Comm 2241198.88, Size: -498187.00, Stock: HS300 2018-09-13, 收盘价Close, 3236.57 2018-09-13, 设置买单 BUY CREATE, 3236.57,信号为:0.45,阈值为:0.30 2018-09-14, BUY EXECUTED, ref:1874, Price: 3243.15, Cost: 1865219107.01, Comm 373043.82, Size: 575125.00, Stock: HS300 2018-11-08, 收盘价Close, 3212.77 2018-11-08, 设置卖单SELL CREATE, 3212.77信号为:-0.39,阈值为:-0.30 2018-11-09, SELL EXECUTED, ref:1875, Price: 3192.98, Cost: 1865219107.01, Comm 2203635.61, Size: -575125.00, Stock: HS300 2018-11-26, 收盘价Close, 3141.24 2018-11-26, 设置买单 BUY CREATE, 3141.24,信号为:0.33,阈值为:0.30 2018-11-27, BUY EXECUTED, ref:1876, Price: 3153.09, Cost: 1838475738.63, Comm 367695.15, Size: 583072.00, Stock: HS300 2019-03-12, 收盘价Close, 3755.35 2019-03-12, 设置卖单SELL CREATE, 3755.35信号为:-0.36,阈值为:-0.30 2019-03-13, SELL EXECUTED, ref:1877, Price: 3758.33, Cost: 1838475738.63, Comm 2629655.28, Size: -583072.00, Stock: HS300 2019-03-27, 收盘价Close, 3743.39 2019-03-27, 设置买单 BUY CREATE, 3743.39,信号为:0.66,阈值为:0.30 2019-03-28, BUY EXECUTED, ref:1878, Price: 3731.43, Cost: 2157066849.92, Comm 431413.37, Size: 578080.00, Stock: HS300 2019-04-01, 收盘价Close, 3973.93 2019-04-01, 设置卖单SELL CREATE, 3973.93信号为:-0.65,阈值为:-0.30 2019-04-02, SELL EXECUTED, ref:1879, Price: 3985.94, Cost: 2157066849.92, Comm 2765031.58, Size: -578080.00, Stock: HS300 2019-04-19, 收盘价Close, 4120.61 2019-04-19, 设置买单 BUY CREATE, 4120.61,信号为:0.38,阈值为:0.30 2019-04-22, BUY EXECUTED, ref:1880, Price: 4126.09, Cost: 2303773468.87, Comm 460754.69, Size: 558343.00, Stock: HS300 2019-05-29, 收盘价Close, 3663.91 2019-05-29, 设置卖单SELL CREATE, 3663.91信号为:-0.33,阈值为:-0.30 2019-05-30, SELL EXECUTED, ref:1881, Price: 3648.49, Cost: 2303773468.87, Comm 2444527.35, Size: -558343.00, Stock: HS300 2019-06-05, 收盘价Close, 3597.10 2019-06-05, 设置买单 BUY CREATE, 3597.10,信号为:0.31,阈值为:0.30 2019-06-06, BUY EXECUTED, ref:1882, Price: 3601.04, Cost: 2046859944.32, Comm 409371.99, Size: 568408.00, Stock: HS300 2019-07-30, 收盘价Close, 3870.32 2019-07-30, 设置卖单SELL CREATE, 3870.32信号为:-0.34,阈值为:-0.30 2019-07-31, SELL EXECUTED, ref:1883, Price: 3856.36, Cost: 2046859944.32, Comm 2630386.00, Size: -568408.00, Stock: HS300 2019-09-27, 收盘价Close, 3852.65 2019-09-27, 设置买单 BUY CREATE, 3852.65,信号为:0.39,阈值为:0.30 2019-09-30, BUY EXECUTED, ref:1884, Price: 3842.45, Cost: 2173833885.52, Comm 434766.78, Size: 565741.00, Stock: HS300 2020-04-15, 收盘价Close, 3797.36 2020-04-15, 设置卖单SELL CREATE, 3797.36信号为:-0.38,阈值为:-0.30 2020-04-16, SELL EXECUTED, ref:1885, Price: 3777.42, Cost: 2173833885.52, Comm 2564451.15, Size: -565741.00, Stock: HS300 2020-05-26, 收盘价Close, 3872.77 2020-05-26, 设置买单 BUY CREATE, 3872.77,信号为:0.41,阈值为:0.30 2020-05-27, BUY EXECUTED, ref:1886, Price: 3873.70, Cost: 2142318795.40, Comm 428463.76, Size: 553042.00, Stock: HS300 2020-08-17, 收盘价Close, 4815.23 2020-08-17, 设置卖单SELL CREATE, 4815.23信号为:-0.47,阈值为:-0.30 2020-08-18, SELL EXECUTED, ref:1887, Price: 4816.09, Cost: 2142318795.40, Comm 3196198.96, Size: -553042.00, Stock: HS300 2020-10-19, 收盘价Close, 4755.49 2020-10-19, 设置买单 BUY CREATE, 4755.49,信号为:0.38,阈值为:0.30 2020-10-20, BUY EXECUTED, ref:1888, Price: 4751.85, Cost: 2631462544.43, Comm 526292.51, Size: 553777.00, Stock: HS300 2021-03-26, 收盘价Close, 5037.99 2021-03-26, 设置卖单SELL CREATE, 5037.99信号为:-0.37,阈值为:-0.30 2021-03-29, SELL EXECUTED, ref:1889, Price: 5051.75, Cost: 2631462544.43, Comm 3357054.72, Size: -553777.00, Stock: HS300 2021-06-17, 收盘价Close, 5101.89 2021-06-17, 设置买单 BUY CREATE, 5101.89,信号为:0.41,阈值为:0.30 2021-06-18, BUY EXECUTED, ref:1890, Price: 5105.44, Cost: 2789510576.57, Comm 557902.12, Size: 546380.00, Stock: HS300 2021-12-03, 收盘价Close, 4901.02 2021-12-03, 设置卖单SELL CREATE, 4901.02信号为:-0.39,阈值为:-0.30 2021-12-06, SELL EXECUTED, ref:1891, Price: 4908.94, Cost: 2789510576.57, Comm 3218575.35, Size: -546380.00, Stock: HS300 2022-03-02, 收盘价Close, 4578.60 2022-03-02, 设置买单 BUY CREATE, 4578.60,信号为:0.36,阈值为:0.30 2022-03-03, BUY EXECUTED, ref:1892, Price: 4595.48, Cost: 2691875645.97, Comm 538375.13, Size: 585766.00, Stock: HS300 2022-05-20, 收盘价Close, 4077.60 2022-05-20, 设置卖单SELL CREATE, 4077.60信号为:-0.55,阈值为:-0.30 2022-05-23, SELL EXECUTED, ref:1893, Price: 4080.56, Cost: 2691875645.97, Comm 2868305.31, Size: -585766.00, Stock: HS300 2022-11-25, 收盘价Close, 3775.78 2022-11-25, 设置买单 BUY CREATE, 3775.78,信号为:0.42,阈值为:0.30 2022-11-28, BUY EXECUTED, ref:1894, Price: 3702.27, Cost: 2345636217.47, Comm 469127.24, Size: 633567.00, Stock: HS300
print(best_params_window)
report2ts = analysis_rets(benchmark_close, bt_res.result, use_widgets=True)
report2trade = analysis_trade(benchmark_close, bt_res.result, use_widgets=True)
{'window': 30, 'pct_window': 15}
d:\anaconda3\lib\site-packages\jupyter_client\session.py:718: UserWarning: Message serialization failed with: Out of range float values are not JSON compliant Supporting this message is deprecated in jupyter-client 7, please make sure your message is JSON-compliant d:\anaconda3\lib\site-packages\jupyter_client\session.py:718: UserWarning: Message serialization failed with: Out of range float values are not JSON compliant Supporting this message is deprecated in jupyter-client 7, please make sure your message is JSON-compliant
box_layout = ipw.Layout(
display="space-between", border="3px solid black", align_items="inherit"
)
ipw.VBox(
[ report2ts.risk_table,
report2ts.cumulative_chart,
report2trade.position_chart,
report2trade.pnl_chart,
report2ts.maxdrawdowns_chart,
],
layout=box_layout,
)
VBox(children=(FigureWidget({ 'data': [{'cells': {'align': [left, center], 'font':…
from plotly.offline import iplot
from plotly.offline import init_notebook_mode
init_notebook_mode()
for chart in [ report2ts.risk_table,
report2ts.cumulative_chart,
report2trade.position_chart,
report2trade.pnl_chart,
report2ts.maxdrawdowns_chart,
]:
iplot(chart)