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

news2024/11/26 22:52:53

概述

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

数据采集处理

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

# 加载相应的库
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 的平均值,以此累推。

dateopenhighlowclosevolumeMA10
2023-01-037.277.287.177.2325892521.07.230000
2023-01-047.277.357.237.3130947081.07.270000
2023-01-057.377.387.307.3530162154.07.296667
2023-01-067.357.387.317.3420312881.07.307500
2023-01-097.387.387.307.3419612260.07.314000
2023-10-137.117.157.087.1019650410.07.102000
2023-10-167.127.137.047.0724907733.07.091000
2023-10-177.097.107.057.0919029143.07.084000
2023-10-187.077.117.057.0521485721.07.074000
2023-10-197.047.056.836.8461679771.07.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();

交易

处理数据
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()

查看收益
dateopenhighlowclosevolumeMA10signalorderpricecashtotalposition
2023-01-037.277.287.177.2325892521.07.23000000.07.231000.01000.00.0
2023-01-047.277.357.237.3130947081.07.2700001100.07.31269.01000.0100.0
2023-01-057.377.387.307.3530162154.07.29666710.07.35269.01004.0100.0
2023-01-067.357.387.317.3420312881.07.30750010.07.34269.01003.0100.0
2023-01-097.387.387.307.3419612260.07.31400010.07.34269.01003.0100.0
2023-10-137.117.157.087.1019650410.07.1020000-100.07.10946.0946.00.0
2023-10-167.127.137.047.0724907733.07.09100000.07.07946.0946.00.0
2023-10-177.097.107.057.0919029143.07.0840001100.07.09237.0946.0100.0
2023-10-187.077.117.057.0521485721.07.0740000-100.07.05942.0942.00.0
2023-10-197.047.056.836.8461679771.07.04500000.06.84942.0942.00.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();

处理数据
# 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()
查看收益
dateopenhighlowclosevolumeMA5MA20signalorderpricecashtotalposition
2023-01-037.277.287.177.2325892521.07.2300007.23000000.07.231000.01000.00.0
2023-01-047.277.357.237.3130947081.07.2700007.27000000.07.311000.01000.00.0
2023-01-057.377.387.307.3530162154.07.2966677.29666700.07.351000.01000.00.0
2023-01-067.357.387.317.3420312881.07.3075007.30750000.07.341000.01000.00.0
2023-01-097.387.387.307.3419612260.07.3140007.31400000.07.341000.01000.00.0
2023-10-137.117.157.087.1019650410.07.0600007.09150000.07.10911.0911.00.0
2023-10-167.127.137.047.0724907733.07.0680007.09500000.07.07911.0911.00.0
2023-10-177.097.107.057.0919029143.07.0840007.09750000.07.09911.0911.00.0
2023-10-187.077.117.057.0521485721.07.0900007.09700000.07.05911.0911.00.0
2023-10-197.047.056.836.8461679771.07.0300007.08650000.06.84911.0911.00.0

191 rows × 13 columns

总结

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1122475.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python基础入门例程1-NP1 Hello World!

描述 将字符串 Hello World! 存储到变量str中&#xff0c;再使用print语句将其打印出来。 输入描述&#xff1a; 无 输出描述&#xff1a; 一行输出字符串Hello World! 解答&#xff1a; str "Hello World!" print(str) 解释说明&#xff1a; 赋值变量&…

AD9371 官方例程HDL详解之JESD204B TX_CLK生成 (二)

AD9371 系列快速入口 AD9371ZCU102 移植到 ZCU106 &#xff1a; AD9371 官方例程构建及单音信号收发 ad9371_tx_jesd -->util_ad9371_xcvr接口映射&#xff1a; AD9371 官方例程之 tx_jesd 与 xcvr接口映射 AD9371 官方例程 时钟间的关系与生成 &#xff1a; AD9371 官方…

[H5动画制作系列]雪花随机产生飘落

雪花图片参考: 全局代码: var max120; var index0; 第一帧代码: index; if(index<max){posX550*Math.random();posY220*Math.random()-100;scale0.8*Math.random()0.2;var snowflakenew lib.snowlink();snowflake.xposX;snowflake.yposY;snowflake.scaleXscale;snowflake…

SpringBoot 实体参数(用于请求参数比较多时使用)

字段必须和传参时一致&#xff0c;否则为null&#xff0c; 使用AITINS可以快速生成&#xff0c;SET GET方法 public class User {//字段必须和传参时一致&#xff0c;否则为nullprivate String user;private String password;public String getUser() {return user;}public vo…

使用screen实现服务器代码一直运行

1.安装screen sudo apt install screen 2.创建一个screen&#xff08;创建一个名为chatglm的新的链接&#xff0c;用来一直运行 screen -S chatglm 3.查看进程列表 screen -ls 创建之后&#xff0c;就可以在当前窗口利用cd命令进入要执行的项目中&#xff0c;开始执行&#xf…

MIT 6.s081操作系统实验 Lab2: system calls

文章目录 1 System call tracing1.1 主要思路1.2 系统调用流程 2 Sysinfo2.1 kernel/kalloc.c 此文件用于实现分配物理空间的函数2.1.1 结构体定义2.1.2 空闲链表初始化2.1.3 内存的分配和释放2.1.4 获取空闲内存字节数 2.2 kernel/proc.c 此文件用于进程管理2.3 sys_Sysinfo2.…

Openssl数据安全传输平台006:粘包的处理-代码框架及实现-TcpSocket.cpp

文章目录 0. 代码仓库1. TCP通信粘包问题2. 粘包、拆包表现形式2.1 正常情况2.2 两个包合并成一个包2.3 出现了拆包 3. 粘包的处理-参考仓库中的文件TcpSocket.cpp3.1 发送数据时候的处理3.2 接收数据时候的处理 0. 代码仓库 https://github.com/Chufeng-Jiang/OpenSSL_Secure_…

互联网Java工程师面试题·Java 面试篇·第四弹

目录 59、我们能自己写一个容器类&#xff0c;然后使用 for-each 循环码&#xff1f; 60、ArrayList 和 HashMap 的默认大小是多数&#xff1f; 61、有没有可能两个不相等的对象有有相同的 hashcode&#xff1f; 62、两个相同的对象会有不同的的 hash code 吗&#xff1f; …

有限小数题解(进制转换+某进制判断是否为无限小数)

给定一个 A 进制下的分数 a/b&#xff0c; 小蓝想把它化为 B 进制下的小数 c。 现在他想知道这个小数是不是一个有限小数。 Input 输入共一行&#xff0c;包含四个数 a, b, A, B&#xff0c;表示该分数和两种进制。 其中 A, B 使用十进制表示&#xff0c; a, b 中大于 9 的数…

2023最新UI酒桌喝酒游戏小程序源码 娱乐小程序源码 带流量主

2023最新UI酒桌喝酒游戏小程序源码 娱乐小程序源码 带流量主 修改增加了广告位&#xff0c;根据文档直接替换&#xff0c;原版本没有广告位 直接上传源码到开发者端即可 通过后改广告代码&#xff0c;然后关闭广告展示提交&#xff0c;通过后打开即可 无广告引流 流量主版…

【JavaEE】网络编程(网络编程基础、Socket套接字)

一、网络编程基础 1.1、什么是网络编程&#xff1f; 网络编程&#xff0c;指网络上的主机&#xff0c;通过不同的进程&#xff0c;以编程的方式实现网络通信&#xff08;或称为网络数据传输&#xff09; 注意&#xff1a;我们只要满足进程不同就行&#xff1b;所以即便是同一…

2023.10.19 关于设计模式 —— 单例模式

目录 引言 单例模式 饿汉模式 懒汉模式 懒汉模式线程安全问题 分析原因 引言 设计模式为编写代码的 约定 和 规范 阅读下面文章前建议点击下方链接明白 对象 和 类对象 对象和类对象 单例模式 单个实例&#xff08;对象&#xff09;在某些场景中有特定的类&#xff0c;…

VS采用nuget配置OpenGL

参考&#xff1a;如何配置Opengl编程环境_opengl配置_知心宝贝的博客-CSDN博客 这应该是最快的办法了&#xff0c;直接用nuget配置。 1.打开NuGet包管理器 2.搜索glew、glfw、glm、freeglut并点击安装即可 3.测试代码 能正常运行说明配置成功了 #include <GL/glew.h>…

Windows 安装 Java

1. 安装 JDK 从 Oracle 的官网下载的 JDK&#xff0c;例如 JDK 21 双击下载得到的 msi 文件&#xff0c;开始安装 JDK 选择要安装的文件路径&#xff08;我一般都默认&#xff09;&#xff1a; 等待安装&#xff1a; 安装完成&#xff1a; 2. 验证是否安装成功 2.1. 打开 cmd…

小插曲 -- 使用Visual Studio Code远程连接香橙派

在之前的学习中&#xff0c;代码的修改和保存都依赖于“vi”指令&#xff0c;而不得不承认vi指令的编辑界面非常原始&#xff0c;所以&#xff0c;如果可以将代码编辑放到更友好的环境里进行无疑是一件大快人心的事情。 本节介绍如何通过Visual Studio Code来进行远程连接: Vi…

Kubernetes 通过 Deployment 部署Jupyterlab

概要 在Kubernetes上部署jupyterlab服务&#xff0c;链接Kubernetes集群内的MySQL&#xff0c;实现简单的数据开发功能。 前置条件 镜像准备&#xff1a;自定义Docker镜像--Jupyterlab-CSDN博客 MySQL-Statefulset准备&#xff1a;StatefulSet 简单实践 Kubernetes-CSDN博客…

Nvidia显卡基础概念介绍

一、PCIe与SXM 1.1 Nvidia GPU PCIe PCIe(peripheral component interconnect express)是一种高速串行计算机扩展总线标准&#xff0c;是英特尔公司在2001年提出来的&#xff0c;它的出现主要是为了取代AGP接口&#xff0c;优点就是兼容性比较好&#xff0c;数据传输速率高、…

rust学习——字符串、字符串字面量、切片(slice)、字符串 slice

文章目录 字符串、字符串字面量、切片&#xff08;slice&#xff09;、字符串 slice01、字符串02、字符串字面量03、切片 &#xff08;slice&#xff09;04、字符串 slice 字符串 slice注意要点String 与 &str 的转换字符串深度剖析字符串 slice 作为函数参数例子001例子00…

SL8541 android系统环境+编译

1.Ubuntu系统的安装 最好使用ubuntu18.0.4 2.工具环境包的安装 // 安装Android8.1源码编译环境 sudo apt-get install openjdk-8-jdk --------------ok sudo apt-get install libx11-dev:i386 libreadline6-dev:i386 libgl1-mesa-dev g-multilib --------------ok sudo…

Git 保姆级使用教程

目录 一、Git介绍 二、Git 与 SVN 区别 三、Git 安装配置 1.Linux 平台上安装 Debian/Ubuntu Centos/RedHat 源码安装 2.Windows 平台上安装 3.Mac 平台上安装 四、安装完成后配置 五、创建版本仓库 六、Git常用命令 1.创建仓库命令 2.提交与修改 3.提交日志 4.…