【量化交易笔记】11.移动平均交易策略

概述

上一节我们建立了最最简单的交易策略,尽管有了盈利,但实际操作上是不可行的。本节将运用移动平均指标,包括单一移动平均策略和双移动平均策略,来建立经典的移动平均策略。

数据采集处理

本文采用上一节的相同数据,为了方便,可以将数据保证在本地。为了完整性本次仍将提供完整代码,便于小伙伴们复现。

# 加载相应的库
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import baostock as bs
import warnings
warnings.filterwarnings("ignore")
plt.rcParams['font.family'] = ['sans-serif']
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus']=False

lg = bs.login()
#指定一下获取股票数据的起始日期和截止日期
#这里就用2023年1月1日至今日的数据
start_date = '2023-01-01'
end_date = '2023-10-19'
#创建数据表,这里选择下载的股票代码为600000

rs=bs.query_history_k_data_plus('600000.sh', 
 "date,open,high,low,close,volume",
    start_date=start_date, end_date=end_date,
frequency="d", adjustflag="3")
# .get_data()
#下面来检查一下数据表的前5行
data=rs.get_data()

cols=["open","high","low","close","volume"]
data[cols]=data[cols].astype('float')
data['date']=pd.to_datetime(data['date'])
data.set_index('date',inplace=True)
data.to_csv("./data/sh600000.csv")

单一移动平均策略

这个策略的核心思想:利用趋势,低买高卖。一般选择一个适当的平均指标来衡量市场的趋势。比如,可以使用简单移动平均线 (SMA) 或指数移动平均线 (EMA) 等,这些指标在之前的文章已作详细说明。
策略: 当股价向上且向上穿过N日均,说明股价向上突破,此时买入;当股价下降且向下穿过N日均线时,说明股价整体出现下跌趋势,此时卖出!
接下来,我将以10日均线(均价)为例进行讲解。

获取均值

如果直接用rolling 函数或 talib.MA获取移动平均值,会出现前面的数值为空情况。

data.close.rolling(window=10).mean()

date
2023-01-03 NaN
2023-01-04 NaN
2023-01-05 NaN
2023-01-06 NaN
2023-01-09 NaN

2023-10-13 7.102
2023-10-16 7.091
2023-10-17 7.084
2023-10-18 7.074
2023-10-19 7.045
Name: close, Length: 191, dtype: float64

为了更好的平滑这个曲线,我们做了一些简单的处理,还不到这些10天的空值,均由前面的平均给定。
最后获得均值合并到data数据中。

#这里使用10日均线
period = 10
#设置一个空列表,用来存储每10天的价格
avg_10 = []
#再设置一个空列表,用来存储每10天价格的均值
avg_value = []
#设置一个循环
for price in data['close']:
    #把每天的价格传入到avg_10列表
    avg_10.append(price)
    #当列表中存储的数值多于10个时
    if len(avg_10) > period:
        #就把前面传入的价格数据删掉,确保列表中只有10天的数据
        del avg_10[0]
    #将10天数据的均值传入到avg_value列表中
    avg_value.append(np.mean(avg_10))
#把计算好的10日均价写到股票价格数据表中    
data['MA10']=avg_value
data

如下表,MA10 ,第一个数据,即为close 本身,第二个数据为7.23和7.31 的平均值,以此累推。

date open high low close volume MA10
2023-01-03 7.27 7.28 7.17 7.23 25892521.0 7.230000
2023-01-04 7.27 7.35 7.23 7.31 30947081.0 7.270000
2023-01-05 7.37 7.38 7.30 7.35 30162154.0 7.296667
2023-01-06 7.35 7.38 7.31 7.34 20312881.0 7.307500
2023-01-09 7.38 7.38 7.30 7.34 19612260.0 7.314000
2023-10-13 7.11 7.15 7.08 7.10 19650410.0 7.102000
2023-10-16 7.12 7.13 7.04 7.07 24907733.0 7.091000
2023-10-17 7.09 7.10 7.05 7.09 19029143.0 7.084000
2023-10-18 7.07 7.11 7.05 7.05 21485721.0 7.074000
2023-10-19 7.04 7.05 6.83 6.84 61679771.0 7.045000

191 rows × 6 columns

设置交易信号

当股价向上且向上穿过10日均线,说明股价向上突破,此时买入;当股价下降且向下穿过10日均线时,说明股价整体出现下跌趋势,此时卖出!

# 定义买入和卖出的条件  
# data['buy_signal'] = np.where(data['close'] > data['MA10'], 1, 0)  
# data['sell_signal'] = np.where(data['close'] < data['MA10'], -1, 0)
data['signal']=np.where(data['close'] > data['MA10'], 1, 0) 
data['order']=data['signal'].diff()*100

原本分别设置买入和卖出,导致算法复杂,统一修改成上节,便于操作,结果也不受影响,最后根据交易信号确定买卖数量。
这里有一个注意事项,在操作中,可能会出现,先卖后买的情况,即在data中先有-100的值,遇到这种情况,需要作一些调整,或直接修改为0,即可。

图形查看

利用图形,查看具体操作信号的位置。

#设置图像尺寸为12*8
plt.figure(figsize=(15,10))
#绘制股价的变化
plt.plot(data['close'],lw=2, c='k',label='股价')
#绘制10日均线
plt.plot(data['MA10'], '--',lw=2, c='b',label='10日均线')

#如果当天股价下跌给出买入信号,用正三角表示
plt.scatter(data['close'].loc[data.order>0].index,
        data['close'][data.order>0],
        marker = '^', s=80, c='r')

#如果当天股价上涨,标出卖出信号,用倒三角表示
plt.scatter(data['close'].loc[data.order<0].index,
        data['close'][data.order<0],
        marker = 'v', s=80, c='g')

#添加图注和网格
plt.legend()
plt.grid()
#将图像进行显示
plt.show();

【量化交易笔记】11.移动平均交易策略_第1张图片

处理数据
df=data.copy()
df['price']=df['close']
df.fillna(0.0,inplace=True)
df=df.fillna(0)
#一般情况下,在A股市场,买入或卖出至少为100股,即1手
df['order'] = df['signal'].diff()*100
df.fillna(0.0,inplace=True)
df['cash']=np.NaN
df['total']=np.NaN
df['position']=df['order'].cumsum()
df.cash.iloc[0]=1000

for i in range(1,len(df)):
    if (df.order.iloc[i]==0):
        df.cash.iloc[i] =df.cash.iloc[i-1]
    else:
        df.cash.iloc[i] =df.cash.iloc[i-1]-df.price.iloc[i]*df.order.iloc[i]
df['total'] =df['cash']+df['position']*df['price']
# 有一些警告错误 可以关闭
#为了让直观看到自己的总资产变化
#我们用图形来进行展示
#设置图形的尺寸是10*6
plt.figure(figsize=(10,6))
#分别绘制总资产和持仓股票市值的变化
plt.plot(df['total'],label='总市值')
plt.plot(df['order'].cumsum()*df['price'],'--',
        label='股票市值')
#增加网格,调整一下图注的位置,就可以显示图像了
plt.grid()
plt.legend(loc='center right')
plt.show()

【量化交易笔记】11.移动平均交易策略_第2张图片

查看收益
date open high low close volume MA10 signal order price cash total position
2023-01-03 7.27 7.28 7.17 7.23 25892521.0 7.230000 0 0.0 7.23 1000.0 1000.0 0.0
2023-01-04 7.27 7.35 7.23 7.31 30947081.0 7.270000 1 100.0 7.31 269.0 1000.0 100.0
2023-01-05 7.37 7.38 7.30 7.35 30162154.0 7.296667 1 0.0 7.35 269.0 1004.0 100.0
2023-01-06 7.35 7.38 7.31 7.34 20312881.0 7.307500 1 0.0 7.34 269.0 1003.0 100.0
2023-01-09 7.38 7.38 7.30 7.34 19612260.0 7.314000 1 0.0 7.34 269.0 1003.0 100.0
2023-10-13 7.11 7.15 7.08 7.10 19650410.0 7.102000 0 -100.0 7.10 946.0 946.0 0.0
2023-10-16 7.12 7.13 7.04 7.07 24907733.0 7.091000 0 0.0 7.07 946.0 946.0 0.0
2023-10-17 7.09 7.10 7.05 7.09 19029143.0 7.084000 1 100.0 7.09 237.0 946.0 100.0
2023-10-18 7.07 7.11 7.05 7.05 21485721.0 7.074000 0 -100.0 7.05 942.0 942.0 0.0
2023-10-19 7.04 7.05 6.83 6.84 61679771.0 7.045000 0 0.0 6.84 942.0 942.0 0.0
小结

本次的单一移动均值策略,居然亏损了,但我们分析一下整理效果。

双移动平均策略

顾名思义,双移动平均策略,就是用两条不同的均线来判定股票走势。一般情况,在两条均线中,一条是短期均线(如5日均线),另一条是长期均线(如20日均线)【5日均线又称周均线,20日均线又称月均线,因为一周有5个交易日,一月有21个交易】。

仍以上述数据,只增加5日均值线和20日均值线合并到data。

为了方便定义了一个函数。 在设置交易信号不同,其余均与上述方法相同。

def getSMA(data,period=10):
    # period = 10
    #设置一个空列表,用来存储每10天的价格
    avg_10 = []
    #再设置一个空列表,用来存储每10天价格的均值
    avg_value = []
    #设置一个循环
    for price in data['close']:
        #把每天的价格传入到avg_10列表
        avg_10.append(price)
        #当列表中存储的数值多于10个时
        if len(avg_10) > period:
            #就把前面传入的价格数据删掉,确保列表中只有10天的数据
            del avg_10[0]
        #将10天数据的均值传入到avg_value列表中
        avg_value.append(np.mean(avg_10))
    return avg_value
获得5日和20均线
df=data.copy()
#把分别计算好5日和20日均线    
df['MA5']=getSMA(df,5)
df['MA20']=getSMA(df,20)
# 删除多余的列
df.drop(['MA10','signal','order'],axis=1,inplace=True)
设置交易信号

当5日均线上穿20日均线就买入,当5日均线下穿20日均线就卖出。

# 定义买入和卖出的条件  
df['signal']=np.where(df['MA5'] > df['MA20'], 1, 0) 
df['order']=df['signal'].diff()*100
画交易点图形
#设置图像尺寸为12*8
plt.figure(figsize=(12,8))
#绘制股价的变化
plt.plot(df['close'],lw=2, c='k',label='股价')
#绘制10日均线
plt.plot(df['MA5'], '--',lw=2, c='b',label='5日均线')
plt.plot(df['MA20'], '*',lw=2, c='g',label='20日均线')

#如果当天股价下跌给出买入信号,用正三角表示
plt.scatter(df['close'].loc[df.order>0].index,
        df['close'][df.order>0],
        marker = '^', s=80, c='r')

#如果当天股价上涨,标出卖出信号,用倒三角表示
plt.scatter(df['close'].loc[df.order<0].index,
        df['close'][df.order<0],
        marker = 'v', s=80, c='g')

#添加图注和网格
plt.legend()
plt.grid()
#将图像进行显示
plt.show();

【量化交易笔记】11.移动平均交易策略_第3张图片

处理数据
# df=data.copy()
df['price']=df['close']
df.fillna(0.0,inplace=True)
df=df.fillna(0)
#一般情况下,在A股市场,买入或卖出至少为100股,即1手
df['order'] = df['signal'].diff()*100
df.fillna(0.0,inplace=True)
df['cash']=np.NaN
df['total']=np.NaN
df['position']=df['order'].cumsum()
df.cash.iloc[0]=1000
for i in range(1,len(df)):
    if (df.order.iloc[i]==0):
        df.cash.iloc[i] =df.cash.iloc[i-1]
    else:
        df.cash.iloc[i] =df.cash.iloc[i-1]-df.price.iloc[i]*df.order.iloc[i]
df['total'] =df['cash']+df['position']*df['price']
plt.figure(figsize=(10,6))
#分别绘制总资产和持仓股票市值的变化
plt.plot(df['total'],label='总市值')
plt.plot(df['order'].cumsum()*df['price'],'--',
        label='股票市值')
#增加网格,调整一下图注的位置,就可以显示图像了
plt.grid()
plt.legend(loc='center right')
plt.show()
查看收益
date open high low close volume MA5 MA20 signal order price cash total position
2023-01-03 7.27 7.28 7.17 7.23 25892521.0 7.230000 7.230000 0 0.0 7.23 1000.0 1000.0 0.0
2023-01-04 7.27 7.35 7.23 7.31 30947081.0 7.270000 7.270000 0 0.0 7.31 1000.0 1000.0 0.0
2023-01-05 7.37 7.38 7.30 7.35 30162154.0 7.296667 7.296667 0 0.0 7.35 1000.0 1000.0 0.0
2023-01-06 7.35 7.38 7.31 7.34 20312881.0 7.307500 7.307500 0 0.0 7.34 1000.0 1000.0 0.0
2023-01-09 7.38 7.38 7.30 7.34 19612260.0 7.314000 7.314000 0 0.0 7.34 1000.0 1000.0 0.0
2023-10-13 7.11 7.15 7.08 7.10 19650410.0 7.060000 7.091500 0 0.0 7.10 911.0 911.0 0.0
2023-10-16 7.12 7.13 7.04 7.07 24907733.0 7.068000 7.095000 0 0.0 7.07 911.0 911.0 0.0
2023-10-17 7.09 7.10 7.05 7.09 19029143.0 7.084000 7.097500 0 0.0 7.09 911.0 911.0 0.0
2023-10-18 7.07 7.11 7.05 7.05 21485721.0 7.090000 7.097000 0 0.0 7.05 911.0 911.0 0.0
2023-10-19 7.04 7.05 6.83 6.84 61679771.0 7.030000 7.086500 0 0.0 6.84 911.0 911.0 0.0

191 rows × 13 columns

总结

  1. 这几个策略都没有取得良好的效果,这是因为移动平均策略是适合趋势市场。这个震荡的市场效果不理想。
  2. 以上回测,并没有加入交易费用,是不全面的。
  3. 回测的图形不是很直观,没有看到收益情况,需要查表格最后才能看明白。
  4. 回测应有收益曲线,基准曲线等
  5. 回测一些相关参数,如 α 和 β \alpha 和 \beta αβ,最大回测,年化收益等
  6. 完整的策略,包括 指标、标的、择时以及风控等组成。
    在以后文章中,将逐步增加以上相关知识点。

在此警告:文章中的所有内容,不能给你构成投资的理由。

你可能感兴趣的:(量化交易,笔记,python)