用python写了一个简单的多空交易的测试代码
其中的空头部分用分级基金A,多头部分用跟踪同样指数的etf基金
测试的不是很理想。
由于刚开始接触python,所以在编写代码时已实现功能为主,还有很多可以改进的地方。十分欢迎有相同爱好的朋友一起交流。代码如下
from __future__ import division
import tushare as ts
import pandas as pd
import numpy as np
import csv
init_money=100000
def file2data(filename): #将文件中的历史数据以一个列表的形式返回
str_filename="../data/"+filename
f=open(str_filename) #打开文件
lines=f.readlines() #读取文件中的所有行
lst=[] #一个空的列表,用于存储文件数据
i=0
for line in lines:
if i==0: #跳过第一行
i+=1
continue
else:
lst.append(line)
f.close()
return lst #返回数据
def equalData(filename): #将两个投资标的的历史数据进行等长化处理,filename用于一个列表数据更好
lst=[] #空列表用于存储各标的的历史数据
lens=[] #空列表用于存储各标的历史数据的长度
for i in range(len(filename)):
lst.append(file2data(filename[i]))
lens.append(len(lst[i]))
#此时lst为一个二维列表,两个元素,每一个元素为对应投资标的的历史数据
if lens[0]==lens[1]: #如果长度相等,则返回两个列表,分别对应各自的历史数据
return lst[0],lst[1]
elif lens[0]
return lst[0],data
else:
data=lst[0][:lens[1]]
return data,lst[1]
def reverseData(hist_data): #按照时间顺序重新排列数据(由远及近)
data=hist_data[::-1]
return data
def initOrder(data1,data2): #建仓函数,用于第一次交易,返回两个投资标的的持仓数
money_for_target1=money_for_target2=init_money*0.5 #可以用在单一标的上的资金
data1_list=str2list(data1[0]) #将字符串类型的历史数据转换为列表类型
data2_list=str2list(data2[0])
price_target1=float(data1_list[3]) #将字符串类型的收盘价转换为整数类型
price_target2=float(data2_list[3])
share_for_target1=round(money_for_target1/price_target1,0)
share_for_target1=share_for_target1-share_for_target1%100 #确保持仓数为100的整数倍
share_for_target2=round(money_for_target2/price_target2,0)
share_for_target2=share_for_target2-share_for_target2%100
remain_money=init_money-price_target1*share_for_target1-price_target2*share_for_target2
return data1_list[0],share_for_target1,float(data1_list[3]),share_for_target2,float(data2_list[3]),remain_money
def str2list(data): #因为历史数据中的每一行都是以字符串格式存储,所以需要将每一行的历史数据换成列表形式
list_data=data.split(',')
return list_data
def rebalance(sum_money,price_for_short,price_for_bull):
#仓位再平衡函数,将仓位重新回到50-50
#输入参数:当前总价值、空头部分单价、多头部分单价
#输出参数:再平衡后的空头部分持股,多头部分持股数,现金余额
money_for_short=money_for_bull=sum_money*0.5 #可以用在单一标的上的资金
share_for_short=round(money_for_short/price_for_short,0)
share_for_short=share_for_short-share_for_short%100
share_for_bull=round(money_for_bull/price_for_bull,0)
share_for_bull=share_for_bull-share_for_bull%100
remain_money=sum_money-price_for_short*share_for_short-price_for_short*share_for_short
return share_for_short,share_for_bull,remain_money
def is_year_first_tradeday(data): #判断是否为一年的第一个交易日,为简化模型,分级A的折算在第一个交易日进行
#输入参数为转换后的历史数据(即以时间为标准由远及近排列)
#判断依据为前后两条数据的年份不同,则后一条数据即为当年的第一个交易日
#返回参数为一个列表,用于存储所有历史数据中的第一个年度交易日
year_first_record=data[0][0:4] #第一条历史记录的年份
year_previous_record=year_first_record #前一条历史记录的年份
year_list=[]
for i in range(len(data)-1):
year_current_record=data[i+1][0:4]
if year_current_record==year_previous_record:
year_previous_record=year_current_record
#continue
else:
year_list.append(data[i+1][0:10])
year_previous_record=year_current_record
return year_list
def experiment_test(file_list):
#该函数用于测试策略效果
#读取数据文件
#进行第一次初始化交易
#每个交易日用收盘价
#如果符合条件则在后一个交易日用收盘价进行再平衡
#判断这个交易日是否是当年的第一个交易日,如果是第一个交易日则进行再平衡
#输入参数为数据文件列表
data1,data2=equalData(file_list) #data_short表示空头数据, data_bull表示多头数据
data_short=reverseData(data1) #按照时间反序排列
data_bull=reverseData(data2)
date="" #交易日期
share_for_short=0 #空头份额
share_for_bull=0 #多头份额
remain_money=0 #剩余金额
sum_money=0 #总市值
year_list=is_year_first_tradeday(data_short) #用于找出回测期间内每年的第一个交易日
#开始进行第一次交易,即初始化建仓交易
date,share_for_short,init_short_price,share_for_bull,init_bull_price,remain_money=initOrder(data_short,data_bull)
sum_money=share_for_short*init_short_price+share_for_bull*init_bull_price+remain_money
print "the trade_date is %s,the hold share for short is %d,the hold share for bull is %d,the remain money is %f, the total value is %d" % (date,share_for_short,share_for_bull,remain_money,sum_money)
#跳过第一条历史数据
data_short.pop(0)
data_bull.pop(0)
for i in range(len(data_short)):
date=data_short[i][0:10] #获取交易日期,此时data_short类表中的每一条历史数据还是以整个字符串为的形式
#将空头、多头的历史数据均转换成每一个元素都是列表的形式
pre_list_data_short=str2list(data_short[i])
pre_list_data_bull=str2list(data_bull[i])
#获取当天两个标的的收盘价
price_for_short=float(pre_list_data_short[4])
price_for_bull=float(pre_list_data_bull[4])
sum_money=share_for_short*price_for_short+share_for_bull*price_for_bull+remain_money #当日持仓总市值
short_value=share_for_short*price_for_short #当日空头部分总市值
bull_value=share_for_bull*price_for_bull #当日多头部分总市值
short_part=short_value/sum_money #分别计算多空占比
bull_part=bull_value/sum_money
if abs(short_part-bull_part)>0.4: #即有一部分的仓位占比超过了70%,需要开始进行再平衡操作
share_for_short,share_for_bull,remain_monry=rebalance(sum_money,price_for_short,price_for_bull)
print "%s rebalance" % date
elif (date in year_list): #如果是每年的第一个交易日,则进行定折
print "fist"
short_real_value=share_for_short*1.004 #按照4%的收益率进行折算
bull_real_value=share_for_bull*price_for_bull #计算多头部分的市值
sum_money=real_sum_money=short_real_value+bull_real_value+remain_money #计算折算后的总市值
share_for_short,share_for_bull,remain_monry=rebalance(real_sum_money,price_for_short,price_for_bull)
print "the trade_date is %s,the hold share for short is %d,the part is %f,the hold share for bull is %d,part is %f,the remain money is %f, the total value is %d" % (date,share_for_short,short_part,share_for_bull,bull_part,remain_money,sum_money)