动量策略 - Momentum Strategy
0. 引库
import numpy as np
import pandas as pd
import tushare as ts
import matplotlib.pyplot as plt
import seaborn
plt.style.use('seaborn')
import matplotlib as mpl
%matplotlib inline
mpl.rcParams['font.family'] = 'serif'
import warnings; warnings.simplefilter('ignore')
1. 数据准备 & 回测准备
data = ts.get_k_data('hs300', start = '2010-01-01', end='2017-06-30')[['date','close']]
data.rename(columns={'close': 'price'}, inplace=True)
data.set_index('date', inplace = True)
data.head()
| price |
---|
date | |
---|
2010-01-04 | 3535.229 |
---|
2010-01-05 | 3564.038 |
---|
2010-01-06 | 3541.727 |
---|
2010-01-07 | 3471.456 |
---|
2010-01-08 | 3480.130 |
---|
2. 策略开发思路
data['returns'] = np.log(data['price'] / data['price'].shift(1))
data.head()
| price | returns |
---|
date | | |
---|
2010-01-04 | 3535.229 | NaN |
---|
2010-01-05 | 3564.038 | 0.008116 |
---|
2010-01-06 | 3541.727 | -0.006280 |
---|
2010-01-07 | 3471.456 | -0.020040 |
---|
2010-01-08 | 3480.130 | 0.002496 |
---|
data['position'] = np.sign(data['returns'])
data.head()
| price | returns | position |
---|
date | | | |
---|
2010-01-04 | 3535.229 | NaN | NaN |
---|
2010-01-05 | 3564.038 | 0.008116 | 1.0 |
---|
2010-01-06 | 3541.727 | -0.006280 | -1.0 |
---|
2010-01-07 | 3471.456 | -0.020040 | -1.0 |
---|
2010-01-08 | 3480.130 | 0.002496 | 1.0 |
---|
data['strategy'] = data['position'].shift(1) * data['returns']
data.head(10)
| price | returns | position | strategy |
---|
date | | | | |
---|
2010-01-04 | 3535.229 | NaN | NaN | NaN |
---|
2010-01-05 | 3564.038 | 0.008116 | 1.0 | NaN |
---|
2010-01-06 | 3541.727 | -0.006280 | -1.0 | -0.006280 |
---|
2010-01-07 | 3471.456 | -0.020040 | -1.0 | 0.020040 |
---|
2010-01-08 | 3480.130 | 0.002496 | 1.0 | -0.002496 |
---|
2010-01-11 | 3482.052 | 0.000552 | 1.0 | 0.000552 |
---|
2010-01-12 | 3534.916 | 0.015068 | 1.0 | 0.015068 |
---|
2010-01-13 | 3421.144 | -0.032715 | -1.0 | -0.032715 |
---|
2010-01-14 | 3469.051 | 0.013906 | 1.0 | -0.013906 |
---|
2010-01-15 | 3482.738 | 0.003938 | 1.0 | 0.003938 |
---|
3. 策略可视化
data[['returns', 'strategy']].cumsum().apply(np.exp).plot(figsize=(10, 6));

4. 策略优化之思路——参数优化和穷举
data['position_5'] = np.sign(data['returns'].rolling(5).mean())
data['strategy_5'] = data['position_5'].shift(1) * data['returns']
data[['returns', 'strategy_5']].dropna().cumsum().apply(np.exp).plot(figsize=(10, 6))

参数寻优——使用离散Return计算方法
data['returns_dis'] = data['price'] / data['price'].shift(1) - 1
data['returns_dis_cum'] = (data['returns_dis'] + 1).cumprod()
data.head()
| price | returns | position | strategy | position_5 | strategy_5 | returns_dis | returns_dis_cum |
---|
date | | | | | | | | |
---|
2010-01-04 | 3535.229 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
---|
2010-01-05 | 3564.038 | 0.008116 | 1.0 | NaN | NaN | NaN | 0.008149 | 1.008149 |
---|
2010-01-06 | 3541.727 | -0.006280 | -1.0 | -0.006280 | NaN | NaN | -0.006260 | 1.001838 |
---|
2010-01-07 | 3471.456 | -0.020040 | -1.0 | 0.020040 | NaN | NaN | -0.019841 | 0.981961 |
---|
2010-01-08 | 3480.130 | 0.002496 | 1.0 | -0.002496 | NaN | NaN | 0.002499 | 0.984414 |
---|
price_plot = ['returns_dis_cum']
type(price_plot)
list
for days in [10,20,30,60]:
price_plot.append('sty_cumr_%dd' % days)
data['position_%dd' % days] = np.where(data['returns'].rolling(days).mean()>0, 1, -1)
data['strategy_%dd' % days] = data['position_%dd' % days].shift(1) * data['returns']
data['sty_cumr_%dd' % days] = (data['strategy_%dd' % days] + 1).cumprod()
data.head()
| price | returns | position | strategy | position_5 | strategy_5 | returns_dis | returns_dis_cum | position_10d | strategy_10d | sty_cumr_10d | position_20d | strategy_20d | sty_cumr_20d | position_30d | strategy_30d | sty_cumr_30d | position_60d | strategy_60d | sty_cumr_60d |
---|
date | | | | | | | | | | | | | | | | | | | | |
---|
2010-01-04 | 3535.229 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | -1 | NaN | NaN | -1 | NaN | NaN | -1 | NaN | NaN | -1 | NaN | NaN |
---|
2010-01-05 | 3564.038 | 0.008116 | 1.0 | NaN | NaN | NaN | 0.008149 | 1.008149 | -1 | -0.008116 | 0.991884 | -1 | -0.008116 | 0.991884 | -1 | -0.008116 | 0.991884 | -1 | -0.008116 | 0.991884 |
---|
2010-01-06 | 3541.727 | -0.006280 | -1.0 | -0.006280 | NaN | NaN | -0.006260 | 1.001838 | -1 | 0.006280 | 0.998113 | -1 | 0.006280 | 0.998113 | -1 | 0.006280 | 0.998113 | -1 | 0.006280 | 0.998113 |
---|
2010-01-07 | 3471.456 | -0.020040 | -1.0 | 0.020040 | NaN | NaN | -0.019841 | 0.981961 | -1 | 0.020040 | 1.018115 | -1 | 0.020040 | 1.018115 | -1 | 0.020040 | 1.018115 | -1 | 0.020040 | 1.018115 |
---|
2010-01-08 | 3480.130 | 0.002496 | 1.0 | -0.002496 | NaN | NaN | 0.002499 | 0.984414 | -1 | -0.002496 | 1.015574 | -1 | -0.002496 | 1.015574 | -1 | -0.002496 | 1.015574 | -1 | -0.002496 | 1.015574 |
---|
price_plot
['returns_dis_cum',
'sty_cumr_10d',
'sty_cumr_20d',
'sty_cumr_30d',
'sty_cumr_60d']
data[price_plot].dropna().plot(
title='HS300 Multi Parameters Momuntum Strategy',
figsize=(10, 6), style=['--', '--', '--', '--','--'])

5. 策略优化思路之—— High Frequency Data用于Momentum策略
hs300_hf = ts.get_k_data('hs300', ktype='5')
hs300_hf.head(10)
| date | open | close | high | low | volume | amount | turnoverratio | code |
---|
0 | 2019-03-28 14:55 | 3724.82 | 3725.07 | 3725.68 | 3724.42 | 3806742.0 | {} | 0.0000 | hs300 |
---|
1 | 2019-03-28 15:00 | 3725.07 | 3728.40 | 3728.40 | 3724.90 | 2450800.0 | {} | 0.0000 | hs300 |
---|
2 | 2019-03-29 09:35 | 3739.77 | 3754.43 | 3754.51 | 3739.77 | 10164739.0 | {} | 0.0000 | hs300 |
---|
3 | 2019-03-29 09:40 | 3754.44 | 3763.63 | 3763.94 | 3748.90 | 6341654.0 | {} | 0.0000 | hs300 |
---|
4 | 2019-03-29 09:45 | 3764.66 | 3763.26 | 3764.84 | 3759.35 | 5622416.0 | {} | 0.0000 | hs300 |
---|
5 | 2019-03-29 09:50 | 3763.44 | 3762.66 | 3763.89 | 3754.12 | 5955572.0 | {} | 0.0000 | hs300 |
---|
6 | 2019-03-29 09:55 | 3762.87 | 3766.84 | 3769.46 | 3762.87 | 4600542.0 | {} | 0.0000 | hs300 |
---|
7 | 2019-03-29 10:00 | 3766.66 | 3758.50 | 3766.66 | 3757.00 | 5533989.0 | {} | 0.0000 | hs300 |
---|
8 | 2019-03-29 10:05 | 3758.20 | 3765.39 | 3765.39 | 3758.20 | 3587029.0 | {} | 0.0000 | hs300 |
---|
9 | 2019-03-29 10:10 | 3766.19 | 3775.06 | 3775.15 | 3766.19 | 3993704.0 | {} | 0.0000 | hs300 |
---|
hs300_hf.set_index('date',inplace = True)
hs300_hf.index = hs300_hf.index.to_datetime()
hs300_hf.info()
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 482 entries, 2019-03-28 14:55:00 to 2019-04-12 15:00:00
Data columns (total 8 columns):
open 482 non-null float64
close 482 non-null float64
high 482 non-null float64
low 482 non-null float64
volume 482 non-null float64
amount 482 non-null object
turnoverratio 482 non-null object
code 482 non-null object
dtypes: float64(5), object(3)
memory usage: 33.9+ KB
hs300_hf['2019-04-01':'2019-04-02'].head()
| open | close | high | low | volume | amount | turnoverratio | code |
---|
2019-04-01 09:35:00 | 3901.17 | 3923.72 | 3929.06 | 3901.17 | 25618175.0 | {} | 0.0000 | hs300 |
---|
2019-04-01 09:40:00 | 3923.72 | 3952.36 | 3952.74 | 3921.96 | 14481548.0 | {} | 0.0000 | hs300 |
---|
2019-04-01 09:45:00 | 3952.36 | 3952.94 | 3955.06 | 3951.27 | 13020862.0 | {} | 0.0000 | hs300 |
---|
2019-04-01 09:50:00 | 3952.94 | 3965.55 | 3967.12 | 3952.94 | 11968217.0 | {} | 0.0000 | hs300 |
---|
2019-04-01 09:55:00 | 3965.55 | 3953.47 | 3965.55 | 3951.80 | 9892385.0 | {} | 0.0000 | hs300 |
---|
hs300_hf['returns'] = np.log(hs300_hf['close'] / hs300_hf['close'].shift(1))
hs300_hf['position'] = np.sign(hs300_hf['returns'].rolling(10).mean())
hs300_hf['strategy'] = hs300_hf['position'].shift(1) * hs300_hf['returns']
hs300_hf[['returns', 'strategy']].dropna().cumsum().apply(np.exp).plot(figsize=(10, 6), style=['--', '--'])
