服务好的丹阳网站建设,机械加工订单,网站手机版,做企业官网教程跳转到根目录#xff1a;知行合一#xff1a;投资篇 
已完成#xff1a; 1、投资技术   1.1.1 投资-编程基础-numpy   1.1.2 投资-编程基础-pandas   1.2 金融数据处理   1.3 金融数据可视化 2、投资方法论   2.1.1 预期年化收益率   2.1.2 一个关于yaxb的…跳转到根目录知行合一投资篇 
已完成 1、投资技术   1.1.1 投资-编程基础-numpy   1.1.2 投资-编程基础-pandas   1.2 金融数据处理   1.3 金融数据可视化 2、投资方法论   2.1.1 预期年化收益率   2.1.2 一个关于yaxb的故事   2.1.3-数据标准化   2.1.4-相关性分析   2.2.1.1-一个关于定投的故(姿)事(势)   2.2.1.2-网格交易   2.2.1.3-移动平均线 
3、投资实证   [3.1 2023这一年] 被鸽 文章目录 1. 网格策略说明1.1. 策略说明1.2. 策略数据样例 2. 策略实现2.1. 数据结构定义2.2. 加载数据2.3. 主代码2.3.1. 先看看 want_buy2.3.2. 买入today_buy2.3.3. want_sell2.3.4. 卖出today_sell 2.4. 完整代码 3. 收益如何 1. 网格策略说明 
网格交易说最简单的就是跌了买涨了卖。 
为了少说废话这里就举1个例子 
nameopenclosedate操作操作价格券商ETF0.980.982016/9/14买0.98券商ETF0.9830.9622016/9/26买0.97券商ETF0.9821.0052016/10/18卖1券商ETF1.0011.0252016/10/24卖1.01券商ETF1.0011.0252016/10/24卖1.02券商ETF1.0211.0312016/11/1卖1.03券商ETF1.0251.052016/11/3卖1.04券商ETF1.0251.052016/11/3卖1.05券商ETF1.0551.0882016/11/11卖1.06券商ETF1.0551.0882016/11/11卖1.07券商ETF1.0551.0882016/11/11卖1.08券商ETF1.091.0972016/11/14卖1.09券商ETF1.0641.0342016/12/5买1.06券商ETF1.0641.0342016/12/5买1.05券商ETF1.0641.0342016/12/5买1.04券商ETF1.0381.0262016/12/8买1.03券商ETF1.0291.0012016/12/12买1.02券商ETF1.0291.0012016/12/12买1.01券商ETF1.0010.9882016/12/14买1券商ETF1.0010.9882016/12/14买0.99券商ETF0.9810.9762016/12/19买0.98券商ETF0.9750.9632016/12/23买0.97券商ETF0.9951.0022017/2/20卖1券商ETF0.9740.9622017/3/22买0.97券商ETF0.9730.962017/3/29买0.96券商ETF0.950.9442017/4/19买0.95券商ETF0.9420.9372017/5/2买0.94 
1.1. 策略说明 网格的大小可以自己定义我定义的是0.01作为一个网格  买入一笔后如果后面价格涨了x*0.01这里x可以自己定我定的是3那么就卖出  买入一笔后如果后面价格跌了y*0.01y一般就是1也就是1网那么就继续买入  卖出一笔后如果后面价格跌了x*0.01这里x可以自己定我定的是3那么就买入  卖出一笔后如果后面价格涨了y*0.01y一般就是1也就是1网那么就继续卖出  第一天默认就是买入  后续是按照收盘价和之前买入或卖出的价格进行比较看是否进行买或卖的操作  
1.2. 策略数据样例 
2016/9/14买入价0.98那么我们预期要么是在1.01卖出要么是在0.97买入 
到了2016/9/26收盘价是0.962是0.97的那我们就直接认为我们挂单了0.97买入 
在2016/9/26以0.97买入之后我们预期要么是在1.00卖出要么是在0.96买入 
到了2016/10/18收盘价是1.005是1.00的我们可以成交1笔卖出。 
2. 策略实现 
2.1. 数据结构定义 def __init__(self, security, start_dateNone, end_dateNone) - None:super().__init__()# security是这次执行的代码一般类似512000510300之类# start_date、end_date会在所有的数据行中截取对应的时间片段。不传值那默认就是None就会用全量数据进行测算。# step_price每个网格的大小比如我们测算的是512000是1左右净值的1%作为网格是合适的。# steps买入一笔后如果后面价格涨了x*0.01这里x可以自己定我定的是3那么就卖出。这里的x就是我们这里的stepsself.args  {security: security, start_date: start_date, end_date: end_date, step_price: 0.01, steps: 3}# 加载数据后面会具体解释加载过程self.daily_df  self.load_data_2_df()# 保留本次交易的行数据方便后面的使用比如判断上一笔是买入还是卖出操作self.last_transaction  None# 交易历史最后输出到csv方便查看self.transactions  []2.2. 加载数据 def load_data_2_df(self):# 这个案例是通过既有数据来跑的512000包含了从 2016-09-14 ~ 2024-03-29 的日线数据。# 如果想要自己获取数据可以参考之前的文章https://blog.csdn.net/sdfiiiiii/article/details/135289226包含了从qstock获取、处理、存储数据等df  pd.read_csv(https://gitee.com/kelvin11/public-resources/raw/master/512000.csv)# 如果指定了start_date、end_date就进行数据的切割if self.args[start_date]:df  df[df[date]  self.args[start_date]]if self.args[end_date]:df  df[df[date]  self.args[end_date]]# 转换为日期类型df[date]  df[date].apply(pd.to_datetime, format%Y-%m-%d)# 按照日期的正序排序防止数据错位df.sort_values(bydate, ascendingTrue)# 设置dataframe的索引后面取数比较简便一些.df  df.set_index(date)# 设置了date为索引之后dataframe里面就没有date这一列了有时候为了方便处理还是把date给加上df[date]  df.index.tolist()return df2.3. 主代码 
主代码意味着这里是执行流程的核心。 
其实也比较简单就是遍历dataframe逐行处理数据即可。 def process(self):# dataframe的遍历逐行处理数据for index, row in self.daily_df.iterrows():# index是索引就是日期row是Series类型一行完整的数据if index ! self.daily_df.iloc[0][date]:# 非第一天# 1.1 今日收盘价如果  上次操作的价格那么可能要买。if row[close]  self.last_transaction[价格]:# want_buy为什么可能要买因为在网格中如果之前是买入当天价格下跌没有达到下一网是不买的。这个逻辑在want_buy内部实现self.want_buy(rowrow)# 1.2 今日收盘价如果  上次操作的价格那么可能要卖want_sellelif row[close]  self.last_transaction[价格]:# want_sell其实和want_buy是同样的解释。因为在网格中如果收盘价大于上次交易价格但没有达到实际要卖出的价格那也是不卖的。这个逻辑在want_sell内部实现self.want_sell(rowrow)else:# hold方法其实什么都没做return None。意思就是持有不操作。self.hold(rowrow)print()else:# 是第一天默认就是买入# want_buy方法是指可能要买为什么可能要买因为在网格中如果之前是买入当天价格下跌没有达到下一网是不买的。这个逻辑在want_buy内部实现self.want_buy(rowrow)# 遍历完所有的数据之后将所有的交易记录通过pandas存储到csv文件中df  pd.DataFrame(self.transactions)df.to_csv(%s交易记录.csv % self.args[security], indexNone)2.3.1. 先看看 want_buy def want_buy(self, row):if row[date]  self.daily_df.iloc[0][date]:# 这个是第一天的逻辑直接执行买入。today_buy就真的是买入操作了主要是记录买入价格和当前行数据this_trans  self.today_buy(row, row[close])# 将买入行相关的信息存储到self.transactions操作历史中方便整体输出self.transactions.append(this_trans)else:# 这里就不是第一天了要去判断是否能买的到比如价格下跌没到下一网位置就不买的。if self.last_transaction[操作]  买:# 如果上一次的操作是买那么要构造1个买入价格的阶梯 [上次买入价格, 今日收盘价]按照网格大小构建一个阶梯price_stairs。# 用到的是numpy的arange方法举例:print(np.arange(10,1,-2)) # 输出[10  8  6  4  2]start_buy_price  self.last_transaction[价格]price_stairs  np.arange(start_buy_price, row[close], -self.args[step_price])print(前一天是买入价格%s。收盘价下跌, 构造的买入阶梯是%s % (self.last_transaction[价格], price_stairs))if len(price_stairs)  1:# 因为第一个价格是上次买入价格所以不要包含在本次的买入阶梯里面做一些切割[1:]price_stairs  price_stairs[1:]for price in price_stairs:# 一般保留3位小数即可price  round(price, 3)# today_buy是真正的做买入动作了。this_trans  self.today_buy(row, price)# 将买入信息集中存储到transactions列表中后续输出到文件self.transactions.append(this_trans)else:print(未达到买入阶梯价今日不执行买入)elif self.last_transaction[操作]  卖:# 构造买入阶梯价2. 如果上一次是卖出那么要从-3*网格开始买start_buy_price  self.last_transaction[价格] - self.args[steps] * self.args[step_price]# 构造价格区间的方法跟上面是一样的。price_stairs  np.arange(start_buy_price, row[close], -self.args[step_price])print(前一天是卖出价格%s。收盘价下跌构造的买入阶梯是%s % (self.last_transaction[价格], price_stairs))if start_buy_price  row[close]:# 如果期望开始买的价格正好是当天收盘价其实就是此次要买入的价格price_stairs  np.array([start_buy_price])if len(price_stairs)  1:# 下面的逻辑跟上面是一样的。for price in price_stairs:price  round(price, 3)this_trans  self.today_buy(row, price)self.transactions.append(this_trans)else:print(未达到买入阶梯价今日不执行买入)2.3.2. 买入today_buy 
上面方法用到的today_buy方法其实就几行代码 def today_buy(self, row, price):# 扩充行数据元素增加2列操作  买价格  priceself.daily_df.loc[row[date], 操作]  买self.daily_df.loc[row[date], 价格]  price# 将买入这个操作的信息保存在临时变量 self.last_transaction 中方便后面处理能快速定位到上次交易是买入还是卖出以及其价格self.last_transaction  self.daily_df.loc[row[date]]return self.last_transaction2.3.3. want_sell 
want_sell和want_buy方法及其相似就不逐行解释了。 def want_sell(self, row):# 构造卖出阶梯价1. 如果前一天是卖出那么构造卖出就是按照下一个网格卖if self.last_transaction[操作]  卖:start_sell_price  self.last_transaction[价格]price_stairs  np.arange(start_sell_price, row[close], self.args[step_price])print(前一天是卖出价格%s。收盘价上涨, 构造的卖出阶梯是%s % (self.last_transaction[价格], price_stairs))if len(price_stairs)  1:price_stairs  price_stairs[1:]for price in price_stairs:price  round(price, 3)this_trans  self.today_sell(row, price)self.transactions.append(this_trans)else:print(未达到卖出阶梯价今日不执行卖出)# 构造卖出阶梯价2. 如果前一天是买入出那么要从3*网格开始卖elif self.last_transaction[操作]  买:start_sell_price  self.last_transaction[价格]  self.args[steps] * self.args[step_price]price_stairs  np.arange(start_sell_price, row[close], self.args[step_price])print(前一天是买入价格%s。收盘价上涨构造的买卖出阶梯是%s % (self.last_transaction[价格], price_stairs))print(price_stairs)if start_sell_price  row[close]:price_stairs  np.array([start_sell_price])if len(price_stairs)  1:for price in price_stairs:price  round(price, 3)this_trans  self.today_sell(row, price)self.transactions.append(this_trans)else:print(未达到卖出阶梯价今日不执行卖出)2.3.4. 卖出today_sell 
也是跟today_buy大同小异没什么要特别说明的地方。 def today_sell(self, row, price):# 扩充行数据元素增加2列操作  卖价格  priceself.daily_df.loc[row[date], 操作]  卖self.daily_df.loc[row[date], 价格]  price# 将卖出这个操作的信息保存在临时变量 self.last_transaction 中方便后面处理能快速定位到上次交易是买入还是卖出以及其价格self.last_transaction  self.daily_df.loc[row[date]]return self.last_transaction2.4. 完整代码 
完整的代码已经全部都解释完了就是上面的几个方法组合起来就结束整体代码量加上充分的注释163行。 
废话少说上干货直接就能跑~ 
import pandas as pd
import numpy as npclass FixedGrid:def __init__(self, security, start_dateNone, end_dateNone) - None:super().__init__()# security是这次执行的代码一般类似512000510300之类# start_date、end_date会在所有的数据行中截取对应的时间片段。不传值那默认就是None就会用全量数据进行测算。# step_price每个网格的大小比如我们测算的是512000是1左右净值的1%作为网格是合适的。# steps买入一笔后如果后面价格涨了x*0.01这里x可以自己定我定的是3那么就卖出。这里的x就是我们这里的stepsself.args  {security: security, start_date: start_date, end_date: end_date, step_price: 0.01, steps: 3}# 加载数据后面会具体解释加载过程self.daily_df  self.load_data_2_df()# 保留本次交易的行数据方便后面的使用比如判断上一笔是买入还是卖出操作self.last_transaction  None# 交易历史最后输出到csv方便查看self.transactions  []def load_data_2_df(self):# 这个案例是通过既有数据来跑的512000包含了从 2016-09-14 ~ 2024-03-29 的日线数据。# 如果想要自己获取数据可以参考之前的文章https://blog.csdn.net/sdfiiiiii/article/details/135289226包含了从qstock获取、处理、存储数据等df  pd.read_csv(https://gitee.com/kelvin11/public-resources/raw/master/512000.csv)# 如果指定了start_date、end_date就进行数据的切割if self.args[start_date]:df  df[df[date]  self.args[start_date]]if self.args[end_date]:df  df[df[date]  self.args[end_date]]# 转换为日期类型df[date]  df[date].apply(pd.to_datetime, format%Y-%m-%d)# 按照日期的正序排序防止数据错位df.sort_values(bydate, ascendingTrue)# 设置dataframe的索引后面取数比较简便一些.df  df.set_index(date)# 设置了date为索引之后dataframe里面就没有date这一列了有时候为了方便处理还是把date给加上df[date]  df.index.tolist()return dfdef today_buy(self, row, price):# 扩充行数据元素增加2列操作  买价格  priceself.daily_df.loc[row[date], 操作]  买self.daily_df.loc[row[date], 价格]  price# 将买入这个操作的信息保存在临时变量 self.last_transaction 中方便后面处理能快速定位到上次交易是买入还是卖出以及其价格self.last_transaction  self.daily_df.loc[row[date]]return self.last_transactiondef today_sell(self, row, price):# 扩充行数据元素增加2列操作  卖价格  priceself.daily_df.loc[row[date], 操作]  卖self.daily_df.loc[row[date], 价格]  price# 将卖出这个操作的信息保存在临时变量 self.last_transaction 中方便后面处理能快速定位到上次交易是买入还是卖出以及其价格self.last_transaction  self.daily_df.loc[row[date]]return self.last_transactiondef want_buy(self, row):if row[date]  self.daily_df.iloc[0][date]:# 这个是第一天的逻辑直接执行买入。today_buy就真的是买入操作了主要是记录买入价格和当前行数据this_trans  self.today_buy(row, row[close])# 将买入行相关的信息存储到self.transactions操作历史中方便整体输出self.transactions.append(this_trans)else:# 这里就不是第一天了要去判断是否能买的到比如价格下跌没到下一网位置就不买的。if self.last_transaction[操作]  买:# 如果上一次的操作是买那么要构造1个买入价格的阶梯 [上次买入价格, 今日收盘价]按照网格大小构建一个阶梯price_stairs。# 用到的是numpy的arange方法举例:print(np.arange(10,1,-2)) # 输出[10  8  6  4  2]start_buy_price  self.last_transaction[价格]price_stairs  np.arange(start_buy_price, row[close], -self.args[step_price])print(前一天是买入价格%s。收盘价下跌, 构造的买入阶梯是%s % (self.last_transaction[价格], price_stairs))if len(price_stairs)  1:# 因为第一个价格是上次买入价格所以不要包含在本次的买入阶梯里面做一些切割[1:]price_stairs  price_stairs[1:]for price in price_stairs:# 一般保留3位小数即可price  round(price, 3)# today_buy是真正的做买入动作了。this_trans  self.today_buy(row, price)# 将买入信息集中存储到transactions列表中后续输出到文件self.transactions.append(this_trans)else:print(未达到买入阶梯价今日不执行买入)elif self.last_transaction[操作]  卖:# 构造买入阶梯价2. 如果上一次是卖出那么要从-3*网格开始买start_buy_price  self.last_transaction[价格] - self.args[steps] * self.args[step_price]# 构造价格区间的方法跟上面是一样的。price_stairs  np.arange(start_buy_price, row[close], -self.args[step_price])print(前一天是卖出价格%s。收盘价下跌构造的买入阶梯是%s % (self.last_transaction[价格], price_stairs))if start_buy_price  row[close]:# 如果期望开始买的价格正好是当天收盘价其实就是此次要买入的价格price_stairs  np.array([start_buy_price])if len(price_stairs)  1:# 下面的逻辑跟上面是一样的。for price in price_stairs:price  round(price, 3)this_trans  self.today_buy(row, price)self.transactions.append(this_trans)else:print(未达到买入阶梯价今日不执行买入)def want_sell(self, row):# 构造卖出阶梯价1. 如果前一天是卖出那么构造卖出就是按照下一个网格卖if self.last_transaction[操作]  卖:start_sell_price  self.last_transaction[价格]price_stairs  np.arange(start_sell_price, row[close], self.args[step_price])print(前一天是卖出价格%s。收盘价上涨, 构造的卖出阶梯是%s % (self.last_transaction[价格], price_stairs))if len(price_stairs)  1:price_stairs  price_stairs[1:]for price in price_stairs:price  round(price, 3)this_trans  self.today_sell(row, price)self.transactions.append(this_trans)else:print(未达到卖出阶梯价今日不执行卖出)# 构造卖出阶梯价2. 如果前一天是买入出那么要从3*网格开始卖elif self.last_transaction[操作]  买:start_sell_price  self.last_transaction[价格]  self.args[steps] * self.args[step_price]price_stairs  np.arange(start_sell_price, row[close], self.args[step_price])print(前一天是买入价格%s。收盘价上涨构造的买卖出阶梯是%s % (self.last_transaction[价格], price_stairs))print(price_stairs)if start_sell_price  row[close]:price_stairs  np.array([start_sell_price])if len(price_stairs)  1:for price in price_stairs:price  round(price, 3)this_trans  self.today_sell(row, price)self.transactions.append(this_trans)else:print(未达到卖出阶梯价今日不执行卖出)def hold(self, row):return Nonedef process(self):# dataframe的遍历逐行处理数据for index, row in self.daily_df.iterrows():# index是索引就是日期row是Series类型一行完整的数据if index ! self.daily_df.iloc[0][date]:# 非第一天# 1.1 今日收盘价如果  上次操作的价格那么可能要买。if row[close]  self.last_transaction[价格]:# want_buy为什么可能要买因为在网格中如果之前是买入当天价格下跌没有达到下一网是不买的。这个逻辑在want_buy内部实现self.want_buy(rowrow)# 1.2 今日收盘价如果  上次操作的价格那么可能要卖want_sellelif row[close]  self.last_transaction[价格]:# want_sell其实和want_buy是同样的解释。因为在网格中如果收盘价大于上次交易价格但没有达到实际要卖出的价格那也是不卖的。这个逻辑在want_sell内部实现self.want_sell(rowrow)else:# hold方法其实什么都没做return None。意思就是持有不操作。self.hold(rowrow)print()else:# 是第一天默认就是买入# want_buy方法是指可能要买为什么可能要买因为在网格中如果之前是买入当天价格下跌没有达到下一网是不买的。这个逻辑在want_buy内部实现self.want_buy(rowrow)# 遍历完所有的数据之后将所有的交易记录通过pandas存储到csv文件中df  pd.DataFrame(self.transactions)df.to_csv(%s交易记录.csv % self.args[security], indexNone)if __name__  __main__:# this  FixedGrid(security512000, start_date2020-09-14, end_date2023-09-14)this  FixedGrid(security512000, start_dateNone, end_dateNone)this.process() 
3. 收益如何 
其实这里还是要说的网格这个东西我们还缺了一个前置条件 
你得有足够的持仓在手里你才能在想卖的时候卖出对吧你得有足够的money在手机你才能在想买入的时候买对吧 
所以这个策略是为了告诉我们如果我都ok那理论上一年下来我能卖出多少回也就是做了多少次的T能赚多少钱 
我把这个统计的结果文件放在了512000交易记录 
为了对这个策略有信心我只看每年卖出了多少回 插个题外话这个策略有很多改进的地方比如 
可以不按照close收盘价来算而是按照当天的最高或最低价来算网格可以有增强比如增加网格数量或者增加买入、卖出份额等 
我自己已经非常忠实的执行了网格交易有一段时间对结果还是比较满意的贴一下记录有兴趣的可以交流。