策略总览
适用环境
比较适合上涨下跌趋势比较明确的时间。
大跌以后第一天,不宜于这个策略。大跌以后第一天,因为波动大,有可能会有人抄底。
核心逻辑
这个策略本质上是趋势跟踪策略,通过策略指标判断策略趋势,然后押注上涨或者下跌。
由于市场开盘价容易受到消息影响高开或者低开,因此判断趋势使用的是相对于开盘价的涨幅,这样就能避开消息的影响,只关注资金的趋势。
市场虽然可以交易到15点,但一般来说,上午是主动交易时间。
经过统计,11点前的交易资金量和11点后的交易资金量基本持平。但不同的地方在于:
11点前是主动交易,主动引导股市,主动去买入和卖出;
11点后是被动交易,看今天股市涨了或者跌了,才去交易。
就像我们以前大学里面,上午做作业的,都是主动学习的,我们下午做作业的,基本就是抄上午的人的作业,属于一群不爱学习的。
每日11点相对于开盘涨跌幅,基本可以代表当日的情绪情况。
策略信号
设定:
当日11点涨幅:T0
近4日11点涨幅加权值:avg_4 = ( T0 × 0.4 + T-1 × 0.3 + T-2 × 0.2 + T-3 × 0.1 )
做多:
T0 > 0 且 avg_4 > 0 且 ( T0 > 0.3% 或 avg_4 > 0.3% )
做空:
T0 < 0 且 avg_4 < 0
空仓:
不符合做多和做空的情况
导入包
from jqdata import *
from datetime import datetime,timedelta
import pandas as pd
方法
策略:获取交易信号
def get_signal(se):
pct = 0.3
#策略信号
T0 = se['T-0']
avg_4 = (se['T-0']*0.4+se['T-1']*0.3+se['T-2']*0.2+se['T-3']*0.1)
'''
# 原策略
boolean = T0>pct/100 or avg_4>pct/100
if boolean:
return 1
else:
return -1
'''
# 做多
boolean1 = T0>pct/100
boolean2 = avg_4>0
boolean3 = T0>0
boolean4 = avg_4>pct/100
boolean_long = (boolean1 and boolean2) or (boolean3 and boolean4)
boolean_long = (boolean2 and boolean3) and (boolean1 or boolean4)
# 做空
boolean1 = avg_4<0
boolean2 = T0<0
boolean_short = (boolean1 and boolean2) #and (boolean1 or boolean4)
if boolean_long:
# 同时大于0.3%,做多
return 1
elif boolean_short:
# 同时小于0,做空
return -1
else:
# 其他情况,不做
return 0
最大回测
def get_maxdown_ratio(df):
se = df['cumprod_value']
se = se.drop_duplicates(keep='last')
df = se.to_frame('values')
df['max2here'] = df['values'].expanding().max()
df['dd2here'] = df['values'] / df['max2here']
df['maxd2here'] = 1 - df['dd2here']
return df#df['maxd2here'].max()
年化收益
def get_annual_return(df):
start = df.index[0]
end = df.index[-1]
start_date = datetime.strptime(start,'%Y-%m-%d')
end_date = datetime.strptime(end,'%Y-%m-%d')
days = (end_date - start_date) / timedelta(1)
cumprod_value = df['cumprod_value'].iloc[-1] / df['cumprod_value'].iloc[0]
annual = cumprod_value ** (1/(days/365)) - 1
return annual
索提诺比率
def get_sortino(df):
se = df['cumprod_value']
start = df.index[0]
end = df.index[-1]
start_date = datetime.strptime(start,'%Y-%m-%d')
end_date = datetime.strptime(end,'%Y-%m-%d')
# 年化收益
day_list = se.index.tolist()
days = (end_date - start_date).days
ann_r = (se.iloc[-1]/se.iloc[0])**(365/days) -1
pct_change = se.pct_change()
pct_change = pct_change.dropna()
# 策略波下行波动率
num = len(pct_change)
se = pct_change - pct_change.mean()
se = se[se<0]
vol = np.sqrt(((se)**2).sum()*250/(num))
# 索提诺比率
Sortino = (ann_r - 0.04) / vol
return (Sortino)
设置
指数
index = '000852.XSHG'
securities_df = get_all_securities(types=['index'])
name = securities_df.loc[index].display_name
index,name
('000852.XSHG', '中证1000指数')
时间
end = datetime.now()
start = end - timedelta(11*365)
# start = '2015-08-01'
start,end
(datetime.datetime(2011, 8, 29, 16, 7, 7, 854820),
datetime.datetime(2022, 8, 26, 16, 7, 7, 854820))
数据准备
日数据
days_df = get_price(index,start_date = start,end_date = end ,fields= ['open','close'],fq = 'pre',panel=False)
days_df['day_change'] = days_df['close'].pct_change()
days_df = days_df.dropna()
分钟数据
实际的开始和结束时间
trade_dates = list(map(lambda x:x.strftime("%Y-%m-%d"),get_trade_days(start_date = days_df.index[0], end_date = days_df.index[-1])))
trade_dates[0],trade_dates[-1]
('2014-10-20', '2022-08-26')
data = []
for date in trade_dates[:]:
# 每日分钟曲线
minutes_df = get_price(index,start_date = date + " 09:00:00", end_date = date + " 15:00:00",fields=['close'],fq = 'pre',frequency='1m',panel=False)
# 分钟
minutes_df['time'] = list(map(lambda x:x.strftime('%H:%M'),minutes_df.index))
# 开盘价
open_price = days_df.loc[date,'open']
# 11点和15点相对开盘涨跌幅
close_1100 = minutes_df[minutes_df['time']=='11:00']['close'].iloc[0]
# close_1100 = minutes_df[minutes_df['time']=='10:00']['close'].iloc[0]
close_1500 = minutes_df[minutes_df['time']=='15:00']['close'].iloc[0]
pct_change1 = close_1100 / open_price - 1 # 11点涨跌幅
pct_change2 = close_1500 / open_price - 1 # 15点涨跌幅
data.append([date,open_price,close_1100,close_1500,pct_change1,pct_change2])
minute_df = pd.DataFrame(data,columns=['date','close_0931','close_1100','close_1500','pct_change1','pct_change2'])
minute_df = minute_df.dropna()
minute_df.head()
| date | close_0931 | close_1100 | close_1500 | pct_change1 | pct_change2 | |
|---|---|---|---|---|---|---|
| 0 | 2014-10-20 | 6068.78 | 6122.24 | 6154.52 | 0.008809 | 0.014128 |
| 1 | 2014-10-21 | 6153.02 | 6163.15 | 6103.01 | 0.001646 | -0.008128 |
| 2 | 2014-10-22 | 6102.46 | 6117.28 | 6027.51 | 0.002429 | -0.012282 |
| 3 | 2014-10-23 | 6009.84 | 5980.27 | 5930.48 | -0.004920 | -0.013205 |
| 4 | 2014-10-24 | 5930.57 | 5953.57 | 5941.93 | 0.003878 | 0.001915 |
下单信号以及下单后变动幅度
result_df = minute_df.copy()
# 信号:11点相对开盘涨幅
result_df['pct_signal'] = result_df['pct_change1']
df = result_df[['date','pct_signal','close_0931','close_1100']]
# 11点下单后到下一个交易日11点的变动
df['close_11_change'] = df['close_1100'].pct_change()
/opt/conda/lib/python3.6/site-packages/ipykernel_launcher.py:3: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead
See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
This is separate from the ipykernel package so we can avoid doing imports until
df.head()
| date | pct_signal | close_0931 | close_1100 | close_11_change | |
|---|---|---|---|---|---|
| 0 | 2014-10-20 | 0.008809 | 6068.78 | 6122.24 | NaN |
| 1 | 2014-10-21 | 0.001646 | 6153.02 | 6163.15 | 0.006682 |
| 2 | 2014-10-22 | 0.002429 | 6102.46 | 6117.28 | -0.007443 |
| 3 | 2014-10-23 | -0.004920 | 6009.84 | 5980.27 | -0.022397 |
| 4 | 2014-10-24 | 0.003878 | 5930.57 | 5953.57 | -0.004465 |
最近4个交易日的交易信号
# 前面8个交易日的涨跌幅
days_list = list(range(0,4))
days_list.reverse()
for i in days_list:
col_name = 'T-{}'.format(i)
df[col_name] = df['pct_signal'].shift(i)
df.index = df.date
# 去掉重复的列:已经转换为T-0
df = df.drop(columns = 'pct_signal')
df = df.dropna()
/opt/conda/lib/python3.6/site-packages/ipykernel_launcher.py:6: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead
See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
df.head()
| date | close_0931 | close_1100 | close_11_change | T-3 | T-2 | T-1 | T-0 | |
|---|---|---|---|---|---|---|---|---|
| date | ||||||||
| 2014-10-23 | 2014-10-23 | 6009.84 | 5980.27 | -0.022397 | 0.008809 | 0.001646 | 0.002429 | -0.004920 |
| 2014-10-24 | 2014-10-24 | 5930.57 | 5953.57 | -0.004465 | 0.001646 | 0.002429 | -0.004920 | 0.003878 |
| 2014-10-27 | 2014-10-27 | 5927.15 | 5981.17 | 0.004636 | 0.002429 | -0.004920 | 0.003878 | 0.009114 |
| 2014-10-28 | 2014-10-28 | 6021.65 | 6098.55 | 0.019625 | -0.004920 | 0.003878 | 0.009114 | 0.012771 |
| 2014-10-29 | 2014-10-29 | 6179.46 | 6228.55 | 0.021317 | 0.003878 | 0.009114 | 0.012771 | 0.007944 |
回测
获取交易信号
# 获取交易信号
df['signal'] = df.apply(lambda x:get_signal(x),axis=1)
计算每次交易收益情况
position = 1
# 交易信号对应的涨跌幅
df['change'] = df['close_11_change'].shift(-1)
# 计算多空收益
df['profit_change'] = df['change'] * df['signal'] * position
# 计算净值
df['value'] = df['profit_change'] + 1
# 累计净值
df['cumprod_value'] = df['value'].cumprod()
df
| date | close_0931 | close_1100 | close_11_change | T-3 | T-2 | T-1 | T-0 | signal | change | profit_change | value | cumprod_value | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| date | |||||||||||||
| 2014-10-23 | 2014-10-23 | 6009.8400 | 5980.2700 | -0.022397 | 0.008809 | 0.001646 | 0.002429 | -0.004920 | -1 | -0.004465 | 0.004465 | 1.004465 | 1.004465 |
| 2014-10-24 | 2014-10-24 | 5930.5700 | 5953.5700 | -0.004465 | 0.001646 | 0.002429 | -0.004920 | 0.003878 | 1 | 0.004636 | 0.004636 | 1.004636 | 1.009121 |
| 2014-10-27 | 2014-10-27 | 5927.1500 | 5981.1700 | 0.004636 | 0.002429 | -0.004920 | 0.003878 | 0.009114 | 1 | 0.019625 | 0.019625 | 1.019625 | 1.028925 |
| 2014-10-28 | 2014-10-28 | 6021.6500 | 6098.5500 | 0.019625 | -0.004920 | 0.003878 | 0.009114 | 0.012771 | 1 | 0.021317 | 0.021317 | 1.021317 | 1.050858 |
| 2014-10-29 | 2014-10-29 | 6179.4600 | 6228.5500 | 0.021317 | 0.003878 | 0.009114 | 0.012771 | 0.007944 | 1 | 0.008288 | 0.008288 | 1.008288 | 1.059567 |
| 2014-10-30 | 2014-10-30 | 6248.4700 | 6280.1700 | 0.008288 | 0.009114 | 0.012771 | 0.007944 | 0.005073 | 1 | -0.010646 | -0.010646 | 0.989354 | 1.048287 |
| 2014-10-31 | 2014-10-31 | 6242.8400 | 6213.3100 | -0.010646 | 0.012771 | 0.007944 | 0.005073 | -0.004730 | 0 | 0.005337 | 0.000000 | 1.000000 | 1.048287 |
| 2014-11-03 | 2014-11-03 | 6217.1000 | 6246.4700 | 0.005337 | 0.007944 | 0.005073 | -0.004730 | 0.004724 | 1 | 0.000852 | 0.000852 | 1.000852 | 1.049180 |
| 2014-11-04 | 2014-11-04 | 6275.0000 | 6251.7900 | 0.000852 | 0.005073 | -0.004730 | 0.004724 | -0.003699 | -1 | -0.002946 | 0.002946 | 1.002946 | 1.052271 |
| 2014-11-05 | 2014-11-05 | 6222.9400 | 6233.3700 | -0.002946 | -0.004730 | 0.004724 | -0.003699 | 0.001676 | 0 | -0.001542 | -0.000000 | 1.000000 | 1.052271 |
| 2014-11-06 | 2014-11-06 | 6223.0800 | 6223.7600 | -0.001542 | 0.004724 | -0.003699 | 0.001676 | 0.000109 | 0 | 0.012545 | 0.000000 | 1.000000 | 1.052271 |
| 2014-11-07 | 2014-11-07 | 6297.3500 | 6301.8400 | 0.012545 | -0.003699 | 0.001676 | 0.000109 | 0.000713 | 0 | -0.013302 | -0.000000 | 1.000000 | 1.052271 |
| 2014-11-10 | 2014-11-10 | 6236.2000 | 6218.0100 | -0.013302 | 0.001676 | 0.000109 | 0.000713 | -0.002917 | -1 | -0.014958 | 0.014958 | 1.014958 | 1.068011 |
| 2014-11-11 | 2014-11-11 | 6252.3900 | 6125.0000 | -0.014958 | 0.000109 | 0.000713 | -0.002917 | -0.020375 | -1 | -0.009523 | 0.009523 | 1.009523 | 1.078182 |
| 2014-11-12 | 2014-11-12 | 6005.8600 | 6066.6700 | -0.009523 | 0.000713 | -0.002917 | -0.020375 | 0.010125 | 0 | -0.008184 | -0.000000 | 1.000000 | 1.078182 |
| 2014-11-13 | 2014-11-13 | 6116.9800 | 6017.0200 | -0.008184 | -0.002917 | -0.020375 | 0.010125 | -0.016341 | -1 | -0.002445 | 0.002445 | 1.002445 | 1.080818 |
| 2014-11-14 | 2014-11-14 | 6002.0500 | 6002.3100 | -0.002445 | -0.020375 | 0.010125 | -0.016341 | 0.000043 | 0 | 0.014878 | 0.000000 | 1.000000 | 1.080818 |
| 2014-11-17 | 2014-11-17 | 6016.5400 | 6091.6100 | 0.014878 | 0.010125 | -0.016341 | 0.000043 | 0.012477 | 1 | 0.003510 | 0.003510 | 1.003510 | 1.084611 |
| 2014-11-18 | 2014-11-18 | 6095.8700 | 6112.9900 | 0.003510 | -0.016341 | 0.000043 | 0.012477 | 0.002808 | 1 | 0.006643 | 0.006643 | 1.006643 | 1.091817 |
| 2014-11-19 | 2014-11-19 | 6134.3700 | 6153.6000 | 0.006643 | 0.000043 | 0.012477 | 0.002808 | 0.003135 | 1 | 0.002881 | 0.002881 | 1.002881 | 1.094963 |
| 2014-11-20 | 2014-11-20 | 6174.8900 | 6171.3300 | 0.002881 | 0.012477 | 0.002808 | 0.003135 | -0.000577 | 0 | 0.007347 | 0.000000 | 1.000000 | 1.094963 |
| 2014-11-21 | 2014-11-21 | 6183.4700 | 6216.6700 | 0.007347 | 0.002808 | 0.003135 | -0.000577 | 0.005369 | 1 | 0.009679 | 0.009679 | 1.009679 | 1.105560 |
| 2014-11-24 | 2014-11-24 | 6281.5600 | 6276.8400 | 0.009679 | 0.003135 | -0.000577 | 0.005369 | -0.000751 | 0 | 0.011600 | 0.000000 | 1.000000 | 1.105560 |
| 2014-11-25 | 2014-11-25 | 6295.6800 | 6349.6500 | 0.011600 | -0.000577 | 0.005369 | -0.000751 | 0.008573 | 1 | 0.011610 | 0.011610 | 1.011610 | 1.118396 |
| 2014-11-26 | 2014-11-26 | 6400.5900 | 6423.3700 | 0.011610 | 0.005369 | -0.000751 | 0.008573 | 0.003559 | 1 | 0.006595 | 0.006595 | 1.006595 | 1.125772 |
| 2014-11-27 | 2014-11-27 | 6450.5100 | 6465.7300 | 0.006595 | -0.000751 | 0.008573 | 0.003559 | 0.002360 | 1 | 0.002399 | 0.002399 | 1.002399 | 1.128472 |
| 2014-11-28 | 2014-11-28 | 6476.5700 | 6481.2400 | 0.002399 | 0.008573 | 0.003559 | 0.002360 | 0.000721 | 0 | -0.014803 | -0.000000 | 1.000000 | 1.128472 |
| 2014-12-01 | 2014-12-01 | 6478.0000 | 6385.3000 | -0.014803 | 0.003559 | 0.002360 | 0.000721 | -0.014310 | -1 | 0.011030 | -0.011030 | 0.988970 | 1.116025 |
| 2014-12-02 | 2014-12-02 | 6385.9500 | 6455.7300 | 0.011030 | 0.002360 | 0.000721 | -0.014310 | 0.010927 | 1 | -0.007434 | -0.007434 | 0.992566 | 1.107729 |
| 2014-12-03 | 2014-12-03 | 6465.4600 | 6407.7400 | -0.007434 | 0.000721 | -0.014310 | 0.010927 | -0.008927 | -1 | 0.022699 | -0.022699 | 0.977301 | 1.082584 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 2022-07-18 | 2022-07-18 | 6868.3000 | 6907.9500 | -0.008041 | 0.005218 | 0.015640 | 0.010761 | 0.005773 | 1 | 0.014721 | 0.014721 | 1.014721 | 326.941779 |
| 2022-07-19 | 2022-07-19 | 6956.8600 | 7009.6400 | 0.014721 | 0.015640 | 0.010761 | 0.005773 | 0.007587 | 1 | 0.011012 | 0.011012 | 1.011012 | 330.542055 |
| 2022-07-20 | 2022-07-20 | 7028.7900 | 7086.8300 | 0.011012 | 0.010761 | 0.005773 | 0.007587 | 0.008257 | 1 | 0.001918 | 0.001918 | 1.001918 | 331.175916 |
| 2022-07-21 | 2022-07-21 | 7098.2700 | 7100.4200 | 0.001918 | 0.005773 | 0.007587 | 0.008257 | 0.000303 | 1 | -0.006566 | -0.006566 | 0.993434 | 329.001478 |
| 2022-07-22 | 2022-07-22 | 7081.5600 | 7053.8000 | -0.006566 | 0.007587 | 0.008257 | 0.000303 | -0.003920 | 0 | -0.004985 | -0.000000 | 1.000000 | 329.001478 |
| 2022-07-25 | 2022-07-25 | 7037.7600 | 7018.6400 | -0.004985 | 0.008257 | 0.000303 | -0.003920 | -0.002717 | -1 | -0.003777 | 0.003777 | 1.003777 | 330.244145 |
| 2022-07-26 | 2022-07-26 | 6949.1900 | 6992.1300 | -0.003777 | 0.000303 | -0.003920 | -0.002717 | 0.006179 | 1 | 0.011367 | 0.011367 | 1.011367 | 333.998052 |
| 2022-07-27 | 2022-07-27 | 7018.2200 | 7071.6100 | 0.011367 | -0.003920 | -0.002717 | 0.006179 | 0.007607 | 1 | 0.016145 | 0.016145 | 1.016145 | 339.390396 |
| 2022-07-28 | 2022-07-28 | 7138.3200 | 7185.7800 | 0.016145 | -0.002717 | 0.006179 | 0.007607 | 0.006649 | 1 | -0.006264 | -0.006264 | 0.993736 | 337.264536 |
| 2022-07-29 | 2022-07-29 | 7152.3300 | 7140.7700 | -0.006264 | 0.006179 | 0.007607 | 0.006649 | -0.001616 | 0 | 0.000804 | 0.000000 | 1.000000 | 337.264536 |
| 2022-08-01 | 2022-08-01 | 7100.7100 | 7146.5100 | 0.000804 | 0.007607 | 0.006649 | -0.001616 | 0.006450 | 1 | -0.031830 | -0.031830 | 0.968170 | 326.529567 |
| 2022-08-02 | 2022-08-02 | 7098.1800 | 6919.0400 | -0.031830 | 0.006649 | -0.001616 | 0.006450 | -0.025237 | -1 | 0.013626 | -0.013626 | 0.986374 | 322.080220 |
| 2022-08-03 | 2022-08-03 | 6968.2900 | 7013.3200 | 0.013626 | -0.001616 | 0.006450 | -0.025237 | 0.006462 | 0 | -0.008554 | -0.000000 | 1.000000 | 322.080220 |
| 2022-08-04 | 2022-08-04 | 6953.9800 | 6953.3300 | -0.008554 | 0.006450 | -0.025237 | 0.006462 | -0.000093 | -1 | 0.004904 | -0.004904 | 0.995096 | 320.500699 |
| 2022-08-05 | 2022-08-05 | 6975.8900 | 6987.4300 | 0.004904 | -0.025237 | 0.006462 | -0.000093 | 0.001654 | 0 | 0.022290 | 0.000000 | 1.000000 | 320.500699 |
| 2022-08-08 | 2022-08-08 | 7082.5400 | 7143.1800 | 0.022290 | 0.006462 | -0.000093 | 0.001654 | 0.008562 | 1 | 0.004637 | 0.004637 | 1.004637 | 321.986729 |
| 2022-08-09 | 2022-08-09 | 7168.9600 | 7176.3000 | 0.004637 | -0.000093 | 0.001654 | 0.008562 | 0.001024 | 1 | 0.005415 | 0.005415 | 1.005415 | 323.730302 |
| 2022-08-10 | 2022-08-10 | 7183.1800 | 7215.1600 | 0.005415 | 0.001654 | 0.008562 | 0.001024 | 0.004452 | 1 | 0.007225 | 0.007225 | 1.007225 | 326.069275 |
| 2022-08-11 | 2022-08-11 | 7241.7300 | 7267.2900 | 0.007225 | 0.008562 | 0.001024 | 0.004452 | 0.003530 | 1 | 0.005006 | 0.005006 | 1.005006 | 327.701575 |
| 2022-08-12 | 2022-08-12 | 7287.6500 | 7303.6700 | 0.005006 | 0.001024 | 0.004452 | 0.003530 | 0.002198 | 0 | -0.005790 | -0.000000 | 1.000000 | 327.701575 |
| 2022-08-15 | 2022-08-15 | 7218.0900 | 7261.3800 | -0.005790 | 0.004452 | 0.003530 | 0.002198 | 0.005997 | 1 | 0.011284 | 0.011284 | 1.011284 | 331.399477 |
| 2022-08-16 | 2022-08-16 | 7290.2400 | 7343.3200 | 0.011284 | 0.003530 | 0.002198 | 0.005997 | 0.007281 | 1 | -0.004321 | -0.004321 | 0.995679 | 329.967521 |
| 2022-08-17 | 2022-08-17 | 7330.4800 | 7311.5900 | -0.004321 | 0.002198 | 0.005997 | 0.007281 | -0.002577 | 0 | 0.003525 | 0.000000 | 1.000000 | 329.967521 |
| 2022-08-18 | 2022-08-18 | 7315.4100 | 7337.3600 | 0.003525 | 0.005997 | 0.007281 | -0.002577 | 0.003001 | 1 | -0.004868 | -0.004868 | 0.995132 | 328.361162 |
| 2022-08-19 | 2022-08-19 | 7353.4300 | 7301.6400 | -0.004868 | 0.007281 | -0.002577 | 0.003001 | -0.007043 | -1 | -0.007430 | 0.007430 | 1.007430 | 330.800832 |
| 2022-08-22 | 2022-08-22 | 7214.4800 | 7247.3900 | -0.007430 | -0.002577 | 0.003001 | -0.007043 | 0.004562 | 1 | 0.003608 | 0.003608 | 1.003608 | 331.994426 |
| 2022-08-23 | 2022-08-23 | 7275.3800 | 7273.5400 | 0.003608 | 0.003001 | -0.007043 | 0.004562 | -0.000253 | 0 | -0.019670 | -0.000000 | 1.000000 | 331.994426 |
| 2022-08-24 | 2022-08-24 | 7312.5500 | 7130.4700 | -0.019670 | -0.007043 | 0.004562 | -0.000253 | -0.024900 | -1 | -0.017924 | 0.017924 | 1.017924 | 337.945255 |
| 2022-08-25 | 2022-08-25 | 7065.3400 | 7002.6600 | -0.017924 | 0.004562 | -0.000253 | -0.024900 | -0.008871 | -1 | 0.010549 | -0.010549 | 0.989451 | 334.380307 |
| 2022-08-26 | 2022-08-26 | 7032.6553 | 7076.5303 | 0.010549 | -0.000253 | -0.024900 | -0.008871 | 0.006239 | 0 | NaN | NaN | NaN | NaN |
1913 rows × 13 columns
df = df.dropna()
df.cumprod_value.iloc[-1]
334.38030705364923
导出回测结果到文件
today = pd.datetime.today().strftime("%Y-%m-%d")
df.to_csv('{}指数涨幅判断{}.csv'.format(name,today))
以指数作为比较基准
start = df.index[0]
end = df.index[-1]
index_price_df = get_price(index,start_date = start,end_date = end,fields=['close'],panel=False,fq=None)
index_price_df.index = list(map(lambda x:x.strftime("%Y-%m-%d"),index_price_df.index))
index_price_df['cumprod_value'] = df['cumprod_value']
index_price_df['close'] = index_price_df['close'] / index_price_df['close'].iloc[0]
index_price_df.index.tolist()[0]
'2014-10-23'
index_price_df
| close | cumprod_value | |
|---|---|---|
| 2014-10-23 | 1.000000 | 1.004465 |
| 2014-10-24 | 1.001931 | 1.009121 |
| 2014-10-27 | 1.012876 | 1.028925 |
| 2014-10-28 | 1.039321 | 1.050858 |
| 2014-10-29 | 1.053321 | 1.059567 |
| 2014-10-30 | 1.052459 | 1.048287 |
| 2014-10-31 | 1.046898 | 1.048287 |
| 2014-11-03 | 1.058260 | 1.049180 |
| 2014-11-04 | 1.050812 | 1.052271 |
| 2014-11-05 | 1.050041 | 1.052271 |
| 2014-11-06 | 1.060486 | 1.052271 |
| 2014-11-07 | 1.050197 | 1.052271 |
| 2014-11-10 | 1.053306 | 1.068011 |
| 2014-11-11 | 1.015601 | 1.078182 |
| 2014-11-12 | 1.031967 | 1.078182 |
| 2014-11-13 | 1.015732 | 1.080818 |
| 2014-11-14 | 1.013316 | 1.080818 |
| 2014-11-17 | 1.028724 | 1.084611 |
| 2014-11-18 | 1.035248 | 1.091817 |
| 2014-11-19 | 1.044361 | 1.094963 |
| 2014-11-20 | 1.043072 | 1.094963 |
| 2014-11-21 | 1.052321 | 1.105560 |
| 2014-11-24 | 1.061132 | 1.105560 |
| 2014-11-25 | 1.077896 | 1.118396 |
| 2014-11-26 | 1.085096 | 1.125772 |
| 2014-11-27 | 1.091411 | 1.128472 |
| 2014-11-28 | 1.092342 | 1.128472 |
| 2014-12-01 | 1.077899 | 1.116025 |
| 2014-12-02 | 1.088876 | 1.107729 |
| 2014-12-03 | 1.098387 | 1.082584 |
| ... | ... | ... |
| 2022-07-15 | 1.155525 | 322.198781 |
| 2022-07-18 | 1.172575 | 326.941779 |
| 2022-07-19 | 1.181914 | 330.542055 |
| 2022-07-20 | 1.198908 | 331.175916 |
| 2022-07-21 | 1.193038 | 329.001478 |
| 2022-07-22 | 1.186177 | 329.001478 |
| 2022-07-25 | 1.171728 | 330.244145 |
| 2022-07-26 | 1.184966 | 333.998052 |
| 2022-07-27 | 1.197109 | 339.390396 |
| 2022-07-28 | 1.205670 | 337.264536 |
| 2022-07-29 | 1.200144 | 337.264536 |
| 2022-08-01 | 1.210770 | 326.529567 |
| 2022-08-02 | 1.173257 | 322.080220 |
| 2022-08-03 | 1.166292 | 322.080220 |
| 2022-08-04 | 1.173650 | 320.500699 |
| 2022-08-05 | 1.195519 | 320.500699 |
| 2022-08-08 | 1.210039 | 321.986729 |
| 2022-08-09 | 1.215315 | 323.730302 |
| 2022-08-10 | 1.216104 | 326.069275 |
| 2022-08-11 | 1.230000 | 327.701575 |
| 2022-08-12 | 1.218928 | 327.701575 |
| 2022-08-15 | 1.228147 | 331.399477 |
| 2022-08-16 | 1.235080 | 329.967521 |
| 2022-08-17 | 1.235763 | 329.967521 |
| 2022-08-18 | 1.239603 | 328.361162 |
| 2022-08-19 | 1.219869 | 330.800832 |
| 2022-08-22 | 1.229003 | 331.994426 |
| 2022-08-23 | 1.231944 | 331.994426 |
| 2022-08-24 | 1.186988 | 337.945255 |
| 2022-08-25 | 1.183093 | 334.380307 |
1912 rows × 2 columns
最大回撤
max_down_df = get_maxdown_ratio(df)
max_down = max_down_df['maxd2here'].max()
max_down
0.203109424118747
index_price_df['max_down'] = max_down_df['maxd2here']
年化收益
annual = get_annual_return(df)
annual
1.0968404462878532
索提诺比率
sortino = get_sortino(df)
sortino
6.645217051360931
总体胜率
total_win_ratio = len(df[df['profit_change']>0])/len(df[df['profit_change']!=0])
total_win_ratio
0.62
做多情况
# 做多数量
long_signal = df[df['signal']==1]
# 胜率
long_win_ratio = len(long_signal[long_signal['profit_change']>0])/len(long_signal)
long_win_ratio
# 盈亏比
long_win_lost = abs(long_signal['profit_change'][long_signal['profit_change']>0].mean() / long_signal['profit_change'][long_signal['profit_change']<0].mean())
做空情况
short_signal = df[df['signal']==-1]
# 胜率
short_win_ratio = len(short_signal[short_signal['profit_change']>0])/len(short_signal)
short_win_lost = abs(short_signal['profit_change'][short_signal['profit_change']>0].mean() / short_signal['profit_change'][short_signal['profit_change']<0].mean())
len(short_signal),"%.2f%%"%(round(short_win_ratio*100,2)),round(short_win_lost,2)
(560, '57.50%', 1.38)
绘图
import matplotlib.pyplot as plt
plt.rcParams['axes.unicode_minus']=False
plt.rcParams['font.sans-serif'] = ['FangSong'] # 指定默认字体
fig = plt.figure(figsize=(16,5))
fig.set_facecolor('white')
ax1 = fig.add_subplot(111)
ax1.grid(True)
tmp = index_price_df.copy()
# tmp = tmp.head(500)
# tmp = tmp.loc["2022-01-01":].dropna()
tmp['close']/= tmp['close'].iloc[0]
tmp['cumprod_value']/= tmp['cumprod_value'].iloc[0]
x = tmp.index.tolist()
y1 = tmp.cumprod_value.tolist()
y2 = tmp.close.tolist()
y3 = tmp.max_down.tolist()
ax1.plot(x,y1,color='red', label = '指数多空交易净值')
ax1.set_title('指数多空交易与{}点位'.format(name))
xticklist = []
xlabellist = []
date_list = x
num = int(len(date_list) / 10)
for idx,date in enumerate(date_list):
if idx % num == 0 :
xticklist.append(idx)
xlabellist.append(date)
ax1.set_xticks(xticklist)
ax1.set_xticklabels(xlabellist, rotation=0, fontsize='large')
ax1.set_ylabel(u'指数多空交易净值', fontsize='large')
ax1.set_xlabel(u'日期', fontsize='large')
ax1.plot(x,y2,label='{}'.format(name))
ax1.set_ylabel(u'{}'.format(name), fontsize='large')
#for num in date_num_list:
ax2 = ax1.twinx()
ax2.set_xticks(xticklist)
ax2.set_xticklabels(xlabellist, rotation=0, fontsize='large')
ax2.bar(x,y3,color='gray',label='最大回撤')
ax2.set_ylabel(u'最大回撤', fontsize='large')
#ax2.axhline(y=0,color='black',ls='-')
ax1.legend(loc=2, ncol=3, shadow=True)
ax2.legend(loc=4, ncol=3, shadow=True)
plt.show()

统计结果
cols = ['类型','数量','胜率','盈亏比']
data = [
["做多",len(long_signal),"%.2f%%"%(round(long_win_ratio*100,2)),round(long_win_lost,2)],
["做空",len(short_signal),"%.2f%%"%(round(short_win_ratio*100,2)),round(short_win_lost,2)],
["空仓",len(df)-len(long_signal)-len(short_signal),0,0]
]
pd.DataFrame(data,columns = cols)
| 类型 | 数量 | 胜率 | 盈亏比 | |
|---|---|---|---|---|
| 0 | 做多 | 740 | 65.41% | 1.23 |
| 1 | 做空 | 560 | 57.50% | 1.38 |
| 2 | 空仓 | 612 | 0 | 0.00 |
cols = ['累计净值','年化收益','最大回撤','索提诺比率']
data = [[round(df.cumprod_value.iloc[-1],2),"%.2f%%"%(round(annual*100,2)),
"%.2f%%"%(round(max_down*100,2)),(round(sortino,2))]]
pd.DataFrame(data,columns = cols)
| 累计净值 | 年化收益 | 最大回撤 | 索提诺比率 | |
|---|---|---|---|---|
| 0 | 334.38 | 109.68% | 20.31% | 6.65 |