本程序仅为个人学习测试使用,不做商业用途,据此操作,后果自负
主要的策略就是通过计算涨幅和平均值来判断买入与卖出的时机。
因为我自己本身对股票了解不多,所以我写的东西要为其他策略可能还是会有点困难的。
框架解释
- 获取数据
用爬虫等相关操作获取到数据,并保存到本地,以避免重复爬取浪费时间与性能 - 将本地的数据导入我们的程序
- 通过保存的数据计算涨幅,并获取涨幅最大的股票
- 计算某段时间内的平均价格
- 实行买卖的判断
- 买操作
- 卖操作
- 画图,实现数据可视化
代码实现
做最开始的初始化
1. 输入参数的初始化
- codes
传入所需要分析的代码
列表格式,建议在定义对象之前就写好这个列表
默认是空,也就是 “[]” - in_date
数据分析最开始的日期
默认是2014-01-01
这个日期在后续的计算中会一直后推,直到等于time - time
数据分析的截止日期
默认是系统现在的日期,而且要格式化为“年-月-日” - time_span_inc
用于分析涨幅
默认13,即求13天内的涨幅 - time_span_avg
用于分析平均价格
默认13,即求13天内的平均价格 - funds
是你手上现有的资金
默认为10w - path
传入保存地址
2. 全局变量的初始化
- self.df = None
保存从本地文件中获取的数据 - self.the_code = {“code”: None, “price”: None}
保存你手上持有的股票和买入的价格
在后续中可以改为列表,然后再添加一个字典属性:持有量 - self.change = {“funds”: [], “date”: []}
获取所持有资金的变化数据,为后面 数据可视化做准备
3.示例
def __init__(self, codes=[], in_date="2014-01-01", time=time.strftime('%Y-%m-%d', time.localtime(time.time())),
time_span_inc=13, time_span_avg=13, funds=100000, path="D:/"):
self.codes = codes # 所需要获取的股票
self.in_date = in_date # 数据处理开始日期
self.time = time # 数据处理结束日期
self.time_span_inc = time_span_inc # 涨幅跨度
self.time_span_avg = time_span_avg # 平均值跨度
self.funds = funds # 现有资金
self.path = path # 获取股票数据保存的地址
self.df = None
self.the_code = {"code": None, "price": None}
self.change = {"funds": [], "date": []}
在互联网上获取数据
因为爬取的方法很多很多,我只用了最简单的方法
主要是通过循环获取传入的股票代码的相关信息,并格式化保存到本地
这个方法是只需要运行一次,把数据保存下来以后可以重复使用,方便改数据调整方案。
示例
- 获取的初步结果,所有相关数据都有,但是我们只要收盘的数据
- 筛选并保存数据
首先将“Close”这一列的标签索引重命名,然后只把这一列保存下来
这个就是保存的格式
在个别电脑上会出现只保留后面的数据,而不保存表头的情况,建议在保存到本地之后检查一下格式是否和下面是否相同
- 相关代码
# 获取相关数据
def get_all_data(self):
# 获取数据的时间段-结束时间
end = datetime.date.today()
for code in self.codes:
# 这个方法是pandas_datareader.data里面自带的,通过传入股票代码可以在相关网站获取对应数据
stock = web.DataReader(code, "yahoo", self.in_date, end)
# 将“Close”重命名为对应的code
stock.rename(columns={"Close": code}, inplace=True)
stock[code].to_csv(self.path + code + ".csv") # 保存到本地
获取本地的数据
将本地的数据获取到目前的程序里面来
# 获取Excel中数据,并存到data列表中
def get_data(self):
# 直接将self.df初始化为第一支股票对应的数据,后面的数据只需要在这个基础上列相加就好了
self.df = pd.read_csv(self.path + self.codes[0] + ".csv")
# 通过循环获取所有的数据
for code in self.codes[1:]:
# 获取csv文件
df = pd.read_csv(self.path + code + ".csv")
# 数据合并
self.df = pd.merge(self.df, df, left_on="Date", right_on="Date", how="outer")
# 将索引设置为date
self.df.set_index('Date', inplace=True)
获取某一天之前n天前的数据
这里的n是前面初始化的时候定义的time_span_inc与time_span_avg中更大的那个,为了在后续的处理中可以有充足的数据
获取的方法最好用切片,效率高也看起来方便,但是我的返回值时用的numpy,后面的操作基本上是根据numpy来的,所以懒得改了
# 获取那n天的数据,格式是numpy
def get_n_day(self, in_data):
try:
# 获取输入那天的位置索引
position_index = self.df.index.get_loc(in_data)
# 如果位置索引小于计算所需要的数字,那么说明数据量不够,
if position_index>=max(self.time_span_avg, self.time_span_inc):
# 用于保存每支股票的数据
code_list = []
for code in range(len(self.codes)):
# 用于保存当前股票的数据
i_list = []
for i in range(max(self.time_span_avg, self.time_span_inc)):
# 获取数据
i_list.append(self.df.iloc[position_index - i - 1, code])
code_list.append(i_list)
# 这个地方应该要用切片来获取数据的,这么改虽然麻烦但是看得懂啊(其实就是懒得改了)
return np.array(code_list)
# 数据量不够,不能带入进行计算
else:
return np.array([0])
except Exception as e:
print("获取"+in_data+"的近日数据失败,估计这天没开盘,没有数据")
return np.array([0])
获取涨幅与平均价格
在获取涨幅的时候,还顺便获取了涨幅最大的股票代码号的序列号
# 计算涨幅
def get_increase(self, result):
inc = []
for code in range(len(self.codes)):
# 今天的价格/time_span_inc天前的价格 - 1
increase = (result[code][0] / result[code][self.time_span_inc - 1]) - 1
inc.append(increase)
# 不仅仅获取了对应的涨幅,还获取了涨幅最大的股票代码号的序列号
return inc, inc.index(max(inc))
# 计算平均价格
def get_avg(self, result):
avg_result = result[:, :self.time_span_avg].mean(axis=1)
return avg_result
模拟买卖
- 买的条件是判断现在的价格大于平均价格,涨幅最大而且要大于0
- 卖的条件是现在持有的股票不再是涨幅第一,或者现价小于近些天的平均价格
- 买卖的过程基本上就是重置手上持有的股票和持有的资金来完成的
1. 判断是买还是卖
# 判断实行买卖
def buy_sell(self, increase, avg, result):
# 参考数据
# ([0.05955, 0.02632, 0.058093], 0) 最后一个是涨幅最大的代码号
# [1.68161 3.96707 5.50792] 这段时间的平均价格
# [1.70799 4.01599 5.62799] 当前天的价格
# 获取现在涨幅最大股票的价格和平均价格
new_price = result[increase[1]]
avg_price = avg[increase[1]]
# 如果没有持有的股票则考虑买入
if self.the_code["code"] is None:
# 满足条件:现在的价格大于平均价格,涨幅最大而且要大于0
if new_price > avg_price:
self.buy(self.codes[increase[1]], new_price)
# 如果现在持有的股票不再是涨幅第一,或者现价小于近些天的平均价,或者到了最后一天更新时间
elif (self.the_code["code"] != self.codes[increase[1]]) | (new_price < avg_price) | (self.in_date == self.time):
# 当天卖
self.sell(result)
# 当天卖,当天买
if (new_price > avg_price) & (increase[0][increase[1]] > 0):
self.buy(self.codes[increase[1]], new_price)
else:
print(self.in_date + "继续持有:", self.the_code["code"])
2. 卖
def sell(self, result):
# 因为卖出了,更新现有资金,并将现在持有的股票清空,用现在的价格/先前的价格再*现有的资金
print("卖" + "*" * 30)
print("在" + self.in_date + "卖出")
print("全部卖出:", self.the_code["code"])
# 卖出价格是持有股票(self.the_code["code"])的现价
sell_price = result[self.codes.index(str(self.the_code["code"]))]
print("卖出价格", sell_price)
# 更新价格
self.funds = self.funds * (sell_price / self.the_code["price"])
self.change["funds"].append(self.funds)
self.change["date"].append(self.in_date)
self.the_code["code"] = None
self.the_code["price"] = None
print("资金持有变为:", self.funds)
print("*" * 30)
3. 买
def buy(self, code, new_price):
# 更新持有股票和对应买入价格
self.the_code["code"] = code
self.the_code["price"] = new_price
self.change["funds"].append(self.funds)
self.change["date"].append(self.in_date)
print("在" + self.in_date + "买入")
print("将资金全部买入", self.the_code["code"])
print("买入价格:", self.the_code["price"])
print("资金持有变为:", self.funds)
print("*" * 30)
后续辅助功能
- 将时间后推
tim就是后推的天数
没有返回值,直接操作in_date
# 输入某天获取到第二天的str
def get_next_day(self, tim=1):
timeArray = time.strptime(self.in_date, "%Y-%m-%d")
timeStamp = int(time.mktime(timeArray))
timeStamp = timeStamp + 86400 * tim
timeArray = time.localtime(timeStamp)
self.in_date = time.strftime("%Y-%m-%d", timeArray)
- 画一个能看的图
# 画图
def draw(self):
x = self.change["date"]
y = self.change["funds"]
# 设置图片大小
plt.figure(figsize=(12, 8), dpi=80)
# 绘图(折线)
plt.plot(x, y, color="red", linewidth=2, alpha=0.6)
# 设置轴的刻度
plt.xticks(list(x)[::20], x[::20], rotation=45) # rotation 为旋转角度
# 展示图片
plt.show()
最后的执行框架
- 获取本地数据
- 从第一天开始循环,将in_date后推
- 获取涨幅与平均价格
- 判断买卖,并实行
- 在循环结束之后输出现有的资金,和最大持有资金
- 画一个折线图反映变化
# 主函数
def the_main(self):
self.get_data()
while self.in_date != self.time:
# 获取涨幅以及平均价格
np_result = self.get_n_day(self.in_date)
# 得确定获取的数据是非为空
if np_result.any():
increase = self.get_increase(np_result)
# 再次确定获取的数据是非为空
if increase != 0:
avg = self.get_avg(np_result)
# 进行买卖等操作
self.buy_sell(increase, avg, np_result[:, 0])
# 进入下一天
self.get_next_day()
print("现在持有", self.funds)
print(max(self.change["funds"]))
self.draw()
画出来的图如下
在最后你持有了31973.95元
到此结束
最后附上全部代码
# -*- coding: utf-8 -*-
# 股票交易模拟
import datetime
import time
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import pandas_datareader.data as web
class try_day:
def __init__(self, codes=[], in_date="2014-01-01", time=time.strftime('%Y-%m-%d', time.localtime(time.time())),
time_span_inc=13, time_span_avg=13, funds=100000, path="D:/"):
self.codes = codes # 所需要获取的股票
self.in_date = in_date # 数据处理开始日期
self.time = time # 数据处理结束日期
self.time_span_inc = time_span_inc # 涨幅跨度
self.time_span_avg = time_span_avg # 平均值跨度
self.funds = funds # 现有资金
self.path = path # 获取股票数据保存的地址
self.df = None
self.the_code = {"code": None, "price": None}
self.change = {"funds": [], "date": []}
# 获取相关数据
def get_all_data(self):
end = datetime.date.today()
for code in self.codes:
stock = web.DataReader(code, "yahoo", self.in_date, end)
stock.rename(columns={"Close": code}, inplace=True)
stock[code].to_csv(self.path + code + ".csv") # 保存到本地
# 获取Excel中数据,并存到data列表中
def get_data(self):
self.df = pd.read_csv(self.path + self.codes[0] + ".csv")
for code in self.codes[1:]:
df = pd.read_csv(self.path + code + ".csv")
self.df = pd.merge(self.df, df, left_on="Date", right_on="Date", how="outer")
self.df.set_index('Date', inplace=True)
# 获取那n天的数据,格式是numpy
def get_n_day(self, in_data):
try:
position_index = self.df.index.get_loc(in_data)
if position_index >= max(self.time_span_avg, self.time_span_inc):
code_list = []
for code in range(len(self.codes)):
i_list = []
for i in range(max(self.time_span_avg, self.time_span_inc)):
i_list.append(self.df.iloc[position_index - i - 1, code])
code_list.append(i_list)
return np.array(code_list)
else:
return np.array([0])
except Exception as e:
return np.array([0])
# 计算涨幅
def get_increase(self, result):
inc = []
for code in range(len(self.codes)):
increase = (result[code][0] / result[code][self.time_span_inc - 1]) - 1
inc.append(increase)
return inc, inc.index(max(inc))
# 计算平均价格
def get_avg(self, result):
avg_result = result[:, :self.time_span_avg].mean(axis=1)
return avg_result
# 判断实行买卖
def buy_sell(self, increase, avg, result):
new_price = result[increase[1]]
avg_price = avg[increase[1]]
if self.the_code["code"] is None:
if new_price > avg_price:
self.buy(self.codes[increase[1]], new_price)
elif (self.the_code["code"] != self.codes[increase[1]]) | (new_price < avg_price) | (self.in_date == self.time):
# 当天卖
self.sell(result)
# 当天卖,当天买
if (new_price > avg_price) & (increase[0][increase[1]] > 0):
self.buy(self.codes[increase[1]], new_price)
else:
print(self.in_date + "继续持有:", self.the_code["code"])
def sell(self, result):
print("卖" + "*" * 30)
print("在" + self.in_date + "卖出")
print("全部卖出:", self.the_code["code"])
sell_price = result[self.codes.index(str(self.the_code["code"]))]
print("卖出价格", sell_price)
self.funds = self.funds * (sell_price / self.the_code["price"])
self.change["funds"].append(self.funds)
self.change["date"].append(self.in_date)
self.the_code["code"] = None
self.the_code["price"] = None
print("资金持有变为:", self.funds)
print("*" * 30)
def buy(self, code, new_price):
self.the_code["code"] = code
self.the_code["price"] = new_price
self.change["funds"].append(self.funds)
self.change["date"].append(self.in_date)
print("在" + self.in_date + "买入")
print("将资金全部买入", self.the_code["code"])
print("买入价格:", self.the_code["price"])
print("资金持有变为:", self.funds)
print("*" * 30)
# 画图
def draw(self):
x = self.change["date"]
y = self.change["funds"]
plt.figure(figsize=(12, 8), dpi=80)
plt.plot(x, y, color="red", linewidth=2, alpha=0.6)
plt.xticks(list(x)[::20], x[::20], rotation=45) # rotation 为旋转角度
plt.show()
# 主函数
def the_main(self):
self.get_data()
while self.in_date != self.time:
np_result = self.get_n_day(self.in_date)
if np_result.any():
increase = self.get_increase(np_result)
if increase != 0:
avg = self.get_avg(np_result)
self.buy_sell(increase, avg, np_result[:, 0])
# 进入下一天
self.get_next_day()
print("现在持有", self.funds)
print(max(self.change["funds"]))
self.draw()
# 输入某天获取到第二天的str
def get_next_day(self, tim=1):
timeArray = time.strptime(self.in_date, "%Y-%m-%d")
timeStamp = int(time.mktime(timeArray))
timeStamp = timeStamp + 84600 * tim
timeArray = time.localtime(timeStamp)
self.in_date = time.strftime("%Y-%m-%d", timeArray)
if __name__ == '__main__':
codes = ['159915.SZ', '510300.SS', '510500.SS']
p = try_day(path="D:/", codes=codes, time='2019-12-31')
# p.get_all_data()
p.the_main()