期货联动

大宗商品&商品股票联动策略

策略思想:

策略思想来自中信期货研究报告,外盘大宗商品价格和A股黄金概念板块的价格存在很强的相关性

策略规则

  • 商品收盘价格突破m周期最高价买入;
  • 从买入信号发生的次日开始计算净值,默认假设以第二天开盘价进行买入;
  • 单笔交易自净值最高点下跌超过一定比例则平仓,移动止损;
  • 忽略持仓期间重复出现的开仓信号.

策略实现

读取数据

读入商品和股票数据,并计算伦敦金的每日收益率

1
2
3
4
commodity_df = pd.read_csv('data/XAU.csv', index_col=0, parse_dates=True)['2007':]
stock_df = pd.read_csv('data/concept_return.csv', index_col=0, parse_dates=True)['2007':]
commodity_df['com_return'] = commodity_df.c.pct_change()
commodity_df.head()

黄金板块股票的每日收益率:

1
stock_df.head()

计算开仓信号

当今天黄金收盘价高于过去10日平均时发出开仓信号:

1
2
3
4
long_signal_period = 10
commodity_df['rolling_max'] = commodity_df.h.rolling(long_signal_period).max().shift(1)
commodity_df['open_signal'] = np.where((commodity_df.c > commodity_df.rolling_max), 1, 0)
commodity_df.head(20)

alt text

计算持仓信号

每天判断账户净值水平,当账户离当前交易下的最高净值下跌10%时移动止损或止盈:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
holding = False                   
net = 1 # 记录单笔交易净值
net_max = 1 # 记录单笔交易账户净值的最大值,作为是否卖出的判断依据

for idx, row in stock_df.iterrows():
if holding:
net *= (row.equity_return + 1)
net_max = max(net, net_max)
stock_df.loc[idx, 'holding_signal'] = 1 # holding_signal记录是否持仓;

if net < net_max * stop_net: # 检查是否触发平仓条件: 最高净值下跌超过10%则平仓
holding = False
net = 1 # 重置单笔策略净值为初始值1
net_max = 1


else: # 不持仓的情况
stock_df.loc[idx, 'holding_signal'] = 0
if row['open_signal'] == 1: # 在不持仓时检查时候出现开仓信号,出现开仓信号,当天发出开仓信号,在下一天进行股票买入
holding = True # 开仓信号出现的下一天开始持仓
stock_df.head(20)

计算组合净值

计算组合累积净值

1
2
3
stock_df['portfolio_return'] = stock_df.equity_return * stock_df.holding_signal   # 投资组合收益率 
stock_df['portfolio'] = (stock_df.portfolio_return + 1).cumprod()
stock_df.tail()

benchmark 净值计算

分别计算黄金板块股票和大宗商品黄金的累积收益

1
2
3
stock_df['stocks'] = (stock_df.equity_return + 1).cumprod()                     # 黄金板块股票累积收益
commodity_df['commodity'] = (commodity_df.com_return + 1).cumprod() # 大宗商品黄金累积收益
stock_df['commodity'] = commodity_df.commodity

生成净值曲线

1
2
3
4
import seaborn
stock_df[['portfolio', 'stocks', 'commodity']].plot(figsize=(12,8))
plt.legend()
plt.show()

策略评价

Sharpe Ratio

1
2
3
p_return = stock_df.portfolio_return
sharpe_ratio = np.sqrt(252) * np.mean(p_return) / np.std(p_return)
sharpe_ratio

得到Sharpe Ratio:1.2354358698026058

最大回撤

1
2
3
4
stock_df['cum_max'] = stock_df.portfolio.cummax()
stock_df['drawdown'] = 1 - stock_df.portfolio / stock_df.cum_max
max_drawdown = stock_df.drawdown.max()
max_drawdown

得到最大回撤:0.38804582454916203