目录
(1)低买高卖简单回测
“低买高卖”策略。每次都在相对低点买入,并且在相对高点卖出(跌了买,涨了卖)。下面进行简单回测:
1.导入库
# 导入库
from pandas_datareader import data as dt
import yfinance as yf
yf.pdr_override()
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
2.获取数据
start_date = '2020-01-01'
end_date = '2020-03-20'
# 提取数据:中国平安股票数据
zgpa = dt.get_data_yahoo('601318.ss',start_date,end_date)
zgpa.head()
输出:
3.设定交易信号
# 交易信号
zgpa_signanl = pd.DataFrame(index = zgpa.index) # 日期index
zgpa_signanl['price'] = zgpa['Adj Close'] # 调整价格作为股票价格
zgpa_signanl['diff'] = zgpa_signanl['price'].diff() #股价变化
zgpa_signanl = zgpa_signanl.fillna(0.0) #第一个空值填充为0
zgpa_signanl['signal'] = np.where(zgpa_signanl['diff']>=0,0,1) #股价上涨或不变记为0,下跌记为1
zgpa_signanl['order'] = zgpa_signanl['signal'].diff()*100 #买入/卖出1手(100股),股价上涨:卖出,股价下跌:买入
zgpa_signanl.head()
输出:
4.回测
# 回测
initial_cash = 20000.00 # 初始资金2万元
# 股票市值
zgpa_signanl['stock'] = zgpa_signanl['order']*zgpa_signanl['price']
# 剩余现金=初始现金-现金流变化的累加
zgpa_signanl['cash'] = initial_cash -(zgpa_signanl['order'].diff()*zgpa_signanl['price']).cumsum()
#总资产=股票+现金
zgpa_signanl['total'] = zgpa_signanl['stock'] +zgpa_signanl['cash']
# 绘图
plt.figure(figsize=(8,5))
plt.plot(zgpa_signanl['total'],label = 'total') #总资产
plt.plot(zgpa_signanl['order'].cumsum()*zgpa_signanl['price'],'--',label = 'stock value') #持仓股票市值
plt.grid(True)
plt.legend(loc='center right')
plt.show()
输出:
(2)单一移动平均策略
当股价上升且向上穿过N日的均线时,说明股价向上突破,此时下单买入;当股价下降且向下穿过N日均线,说明股价出现下跌趋势,此时下单卖出。或者当M日均价上升穿过N日的均线(M < N)时,说明股票处上升的趋势,下单买入;反之,当M日均价下降且穿过N日均线时,说明股票处于下降趋势,下单卖出。
1.这里指标使用10日均线:
period = 10 #10日均线
avg_10=[] # 储存10天的价格
avg_value=[] # 储存10天价格的均值
for price in zgpa['Adj Close']:
avg_10.append(price)
if len(avg_10) >period:
del avg_10[0]
avg_value.append(np.mean(avg_10))
# 将10日均价写到股票价格数据表中
zgpa = zgpa.assign(avg_10 = pd.Series(avg_value, index= zgpa.index))
zgpa.head()
输出:
2.画个10日均线的图
plt.figure(figsize=(10,6))
plt.plot(zgpa['Adj Close'],lw = 2,c ='k',label = 'stock price') # stock price,股价
plt.plot(zgpa['avg_10'],'--',lw =2 ,c ='b',label = 'avg_10') # avg_10,10日均价
plt.grid(True)
plt.legend()
plt.show()
输出:
(3)双移动平均策略
使用两条均线判断股价未来的走势。一条是长期均线(如20日均线),另一条是短期均线(如5日均线)。这种策略基于这样一种假设:股票价格的动量会朝着短期均线的方向移动。当短期均线穿过过长期均线,超过长期移动平均线时,动量将向上,此时股价可能会上涨。然而,如果短期均线的移动方向相反,则股价可能下跌。
1.策略代码:
strategy = pd.DataFrame(index=zgpa.index)
strategy['signal'] = 0 # 存储交易信号
strategy['avg_5'] = zgpa['Adj Close'].rolling(5).mean() # 5日均价
strategy['avg_10'] = zgpa['Adj Close'].rolling(10).mean() # 10日均价
strategy['signal'] = np.where(strategy['avg_5']>strategy['avg_10'],1,0) # 5日均价avg_5>10日均价avg_10时动量向上,股价可能上涨,标记为1(avg_5<avg_10时,股价可能下跌,标记为0)
strategy['order'] = strategy['signal'].diff() # 信号从0变1,买入,从1变0,卖出
strategy
输出:
2.画个图看看该策略在哪里买/卖
plt.figure(figsize=(10,5))
plt.plot(zgpa['Adj Close'],lw = 2,label = 'price') # stock price
plt.plot(strategy['avg_5'],lw = 2,ls ='--',label = 'avg5') # stock price
plt.plot(strategy['avg_10'],lw = 2,ls ='-.',label = 'avg10') # stock price
plt.scatter(strategy.loc[strategy['order']==1].index,
zgpa['Adj Close'][strategy['order']==1],
marker='^',s = 80, c = 'r', label = 'Buy') # 标出买入信号
plt.scatter(strategy.loc[strategy['order']==-1].index,
zgpa['Adj Close'][strategy['order']==-1],
marker='v',s = 80, c = 'g', label = 'Sell') # 标出卖出信号
plt.legend()
plt.grid(True)
plt.show()
输出:
3.对该策略回测:
## 回测
initial_cash = 20000.00 # 初始资金2万元
positions = pd.DataFrame(index = strategy.index).fillna(0)
positions['stock'] = strategy['signal']*100 # 买卖1手100股
portfolio = pd.DataFrame()
portfolio['stock value'] = positions.multiply(zgpa['Adj Close'], axis = 0) # 持仓股票*股价=股票市值
# multiply 对应位置上两个对象元素的乘积
order = positions.diff() # 下单数量
# 剩余资金 = 初始资金 - 下单金额的总和
portfolio['cash'] = initial_cash - order.multiply(zgpa['Adj Close'], axis = 0).cumsum()
# 总资产 = 剩余资金 + 持仓股票市值
portfolio['total'] = portfolio['cash'] + portfolio['stock value']
portfolio
输出(看看portfolio):
画个图看下:
plt.figure(figsize=(10,5))
plt.plot(portfolio['total'], lw=2, label = 'total') # 总资产
plt.plot(portfolio['stock value'], lw=2, label = 'stock value') # 持仓股票市值
plt.legend()
plt.grid(True)
plt.show()
该时间区间市场下跌,该策略总体上还是亏钱的。
(4)海龟策略
在股价超过过去N个交易日的股价最高点时买入,在股价低于过去N个交易日的股价最低点时卖出。上述的若干个最高点和最低点会组成一个通道,称“唐奇安通道”。
使用过去N天的股价最高点和过去N天的股价最低点生成唐奇安通道。一般来说,N会设置为20。不过因为我们下载的股票数据时间范围跨度比较小,所以选择了使用过去5日的股价最高点和最低点来进行演示。
1.策略信号:
turtle = pd.DataFrame(index= zgpa.index)
turtle['high'] = zgpa['Adj Close'].shift(1).rolling(5).max()
turtle['low'] = zgpa['Adj Close'].shift(1).rolling(5).min()
turtle['buy'] = zgpa['Adj Close'] > turtle['high'] #股价突破上沿,买入
turtle['sell'] = zgpa['Adj Close'] < turtle['low'] #股价突破下沿,卖出
turtle
输出:
2.根据交易信号和仓位进行下单
交易信号为“买入”且空仓时,才下买入订单;交易信号为“卖出”且有持仓股票时,才会下卖出订单
turtle['orders'] = 0 # 初始订单状态为0
position = 0 # 初始仓位0
for k in range(len(turtle)):
if turtle.buy[k] and position == 0: # 买入信号为true且空仓时买入1手:
turtle.orders[k] = 1
position = 1
elif turtle.sell[k] and position > 0:
turtle.orders[k] = -1
position = 0
turtle
输出:
3.画图看下买/卖时机:
plt.figure(figsize=(10, 5))# 设置画布的尺寸0
plt.plot(zgpa['Adj Close'], lw=2, label='stock') #股价折线图
# 唐奇安通道的上沿
plt.plot(turtle['high'], lw=2, ls='--',c='r', label='high')
# 唐奇安通道的下沿
plt.plot(turtle['low'], lw=2, ls='--',c='g', label='low')
#将买入信号用正三角标识
plt.scatter(
turtle.loc[turtle.orders == 1].index,
zgpa['Adj Close'][turtle.orders == 1],
marker='^', s=80, c='r', label='buy')
#将卖出信号用倒三角标识
plt.scatter(turtle.loc[turtle.orders == -1].index,
zgpa['Adj Close'][turtle.orders == -1],
marker='v', s=80, c='g', label='shell')
plt.legend()
plt.grid()
plt.show()
输出:
4.回测:
# 回测
# 初始资金
initial_cash = 20000
# 创建新的数据表,序号和turtle数据表一致
positions = pd.DataFrame(index=turtle.index).fillna(0.0)
# 每次交易为1手(100股),仓位既买单和卖单的累积加和
positions['stock'] = 100 * turtle['orders'].cumsum()
# 创建投资组合数据表
portfolio = positions.multiply(zgpa['Adj Close'], axis=0)
# 持仓市值为持仓股票数乘以股价
portfolio['holding_values'] = positions.multiply(zgpa['Adj Close'], axis=0)
# 计算出仓位的变化
pos_diff = positions.diff()
# 剩余的现金是初始资金减去仓位变化产生的现金流累计加和
portfolio['cash'] = initial_cash - (pos_diff.multiply(zgpa['Adj Close'], axis=0)).cumsum()
# 总资产既持仓股票市值加剩余现金
portfolio['total'] = portfolio['cash'] + portfolio['holding_values']
portfolio
输出:
画图看下:
#画图
plt.figure(figsize=(10, 5))
plt.plot(portfolio['total'], label='total')
plt.plot(portfolio['holding_values'], '--', label='holding_values') #持仓股票是市值
plt.legend()
plt.grid()
plt.show()
输出:
可以看到该策略还是亏钱(最后剩余19549元)的。但是比双移动平均策略(剩余19413元)赔的少一些,表明该策略更优。