前言: 采用新的策略, 简单均线突破策略, 以5日均线和10日均线判断标准, 5日均线向上突破10日均线的时候买入, 5日均线向下突破10日均线的时候卖出.
代码如下:
import numpy as np
import pandas as pd
import datetime
from matplotlib import pyplot as plt
#读取股票数据, 格式为Stkcd,Trddt,Opnprc,Hiprc,Loprc,Clsprc,Dretwd,Adjprcwd,一共10784个数据
data = pd.read_csv("TRD_Dalyr.csv")
#将时间转换为标准格式xx-xx-xx
data.Trddt = pd.to_datetime(data.Trddt, format = "%Y%m%d")
#print(data)
cls = data[data['Stkcd'] == 1]['Clsprc']
adjcls = data[data['Stkcd'] == 1]['Adjprcwd']
adjcls = adjcls / (adjcls.iloc[-1] / cls.iloc[-1])
#plt.plot(cls)
#plt.plot(adjcls)
#plt.show()
stk = data['Stkcd']
#剔除重复的代码
stk = stk.drop_duplicates()
for row in stk:
print(row)
#cls代表取股票代码为000001(其实是stk的第一行)的股票交易日期和收盘价
cls = data[data['Stkcd'] == stk.iloc[0]][['Trddt', 'Clsprc']]
#构建一个pandas数据结构DataFrame,将交易日期作为行、股票代码作为列
cls2 = pd.DataFrame(cls['Clsprc'].tolist(), index = cls['Trddt'].tolist(), columns = [stk.iloc[0]])
print(cls2)
#如法炮制,取股票代码stk第二行的代码对应的数据
cls3 = data[data['Stkcd'] == stk.iloc[1]][['Trddt', 'Clsprc']]
cls4 = pd.DataFrame(cls3['Clsprc'].tolist(), index = cls3['Trddt'].tolist(), columns = [stk.iloc[1]])
print(cls4)
#将两个合并, axis = 1表示列对齐。
df = pd.concat([cls2, cls4], axis = 1)
print(df)
#查找空值,并且填补空值, np.where返回的是一个list,其中第一个元素为空值所处的行号
nan_fill = np.where(np.isnan(df))[0][0]
print(nan_fill)
print(df.iloc[nan_fill])
print(df.iloc[nan_fill - 2: nan_fill + 1])
#用pad的方式填补空值,即取空值后的第一个数字,填补以前的空值
df = df.fillna(method = 'pad')
print(df.iloc[nan_fill - 2: nan_fill + 1])
df.plot()
#plt.show()
#重新拷贝一份df, 可以用bool变量赋予参数deep, 默认为True, 即temp_df改变不影响df的值, 如果是false则会影响df的值
temp_df = df.copy()
params = [5, 10, 20]
for p in params:
#以window长度为p, 计算移动平均值, 也就是计算均线
temp_df["ma" + str(p)] = temp_df[stk.iloc[0]].rolling(p).mean()
#截取2017年9月1日之后的数据
temp_df = temp_df[temp_df.index >= np.datetime64(datetime.date(2017, 9, 1))]
figure, axis = plt.subplots()
param_colors = [(1, 0.7, 0.2), (0, 0.7, 0.9), (0.9, 0.5, 0.9)]
for (i, p) in enumerate(params):
temp_df[['ma' + str(p)]].plot(kind = 'line', ax = axis, color = param_colors[i], use_index = True)
#plt.show()
def back_test(p, stk):
result = pd.DataFrame(index = [p.index])
result['Position'] = 0
result['Action'] = np.nan
result['Profit'] = 0
result['Accumulate_profit'] = 0
result['Net_value'] = 0
result['Daily_return'] = 0
performance = {
'Count': 0,
'Win_rate': 0,
'Return': 0,
'Sharpe': 0,
#最大回撤
'Max_drawdown': 0
}
#len(p)指的是DataFrame的行数, 引用DataFrame的列标号, 可以用p.ma5, 也可以用p["ma5"]
row_index = p.index.strftime("%Y-%m-%d")
#print("begin")
#print(result)
#print(p)
#print(len(row_index))
for index in range(len(p)):
if index == 0:
continue
#从index = 1开始
#print(len(p), index, result.loc[row_index[index - 1], "Position"])
result.loc[p.index[index], "Position"] = result.loc[row_index[index - 1], "Position"]
ma5 = p.ma5[index]
ma10 = p.ma10[index]
prema5 = p.ma5[index - 1]
prema10 = p.ma10[index - 1]
#如果index = p的长度 - 1, 那么index就是最后一个元素的下标
if index == len(p) - 1:
if result['Position'][index] == 1:
result["Position"][index] = 0
result['Action'][index] = 1
else:
if result['Position'][index] == 0:
#判断5日均线是否上穿10日均线, 如果是, 那么position = 1, action = 1
if ma5 > ma10 and prema5 < ma10:
result['Position'][index] = 1
result['Action'][index] = 1
else:
#position != 0, 说明已经持仓, 那么只有在5日均线下穿10日均线的时候, 才会卖出
if ma5 < ma10 and prema5 > ma10:
result.loc[row_index[index], "Position"] = 0
result.loc[row_index[index], "Action"] = -1
#至此,已经得出了该投资策略的买入、卖出信号点,可以依据这个1、-1、0的变化,确定是否买卖
#p[stk][1 : -1].values表示从stk = 1, 即000001上证综指下标为1的项开始,一直到倒数第二项为止(-1)
print(result)
print(p[stk][1:-1].values)
print(p[stk][0:-2].values)
print()
c = list(map(lambda x: x[0] - x[1], zip(p[stk][1:-1].values,p[stk][0:-2].values)))
print(c)
print(np.array(c))
print(result['Position'][0:-2].values)
c = np.multiply(np.array(c), np.array(result['Position'][0: -2].values)).tolist()
print(c)
result.loc[1 : -1, 'Profit'] = c
result["Accumulate_profit"] = result["Profit"].values.cumsum()
result["Net_value"] = (result["Accumulate_profit"].values + p[stk][0]) / p[stk][0]
result.loc[1 : -1, 'Daily_return'] = (result['Net_value'][1 : -1].values - result["Net_value"][0 : -2].values) / result['Net_value'][1 : -1].values
performance['Count'] = (result['Action'] == 1).sum()
performance['Return'] = sharpe_ratio(result['Daily_return'].values)
performance['Max_drawdown'] = Max_drawdown(result['Net_value'].values)
return result, performance
def sharpe_ratio(return_list):
#计算sharpe比率
average_return = np.mean(return_list)
annual_return = average_return * 252
return_stdev = np.std(return_list)
annual_volume = return_stdev * np.sqrt(252)
#默认一共有252个工作日, 无风险利率为0.02
sharpe_ratio = (annual_return - 0.02) / annual_volume
return sharpe_ratio
def Max_drawdown(value_list):
print(value_list)
#计算最大回撤
i = np.argmax((np.maximum.accumulate(value_list) - value_list) / np.maximum.accumulate(value_list)) # 结束位置
#开始位置
j = np.argmax(value_list[:i])
return(value_list[j] - value_list[i]) / (value_list[j])
print(back_test(temp_df, stk.iloc[0]))