佐治亚理工学院 计算投资公开课第六周作业 投资策略分析平台

策略分析平台

在前两周的工作中,实现了股票价格低于门限值这一策略的event study,即根据门限值来看事件发生前后股票的价格。同时,完成了根据下单的指令来进行回测,计算策略执行期间每一天的价值,以及对投资结果的分析。这一周,要求把这三部分结合起来,能够实现根据门限值直接回测生成过去时间段的下单指令,并进行计算分析。


工具和方法

编程工具

编程语言是python,用到的开源量化分析软件包是 QuantSoftware ToolKit,同时用到numpy和datetime等包。

Event Study

Event Study是指对一类事件前后股票的表现进行研究,根据价格变化来确定投资策略。
佐治亚理工学院 计算投资公开课第六周作业 投资策略分析平台_第1张图片
可以看到,取事件为股票跌破7美元,取事件前后各二十天股票叠加,2012年的SP500指数股票2008到2009年趋势如上图。很显然,当这一类事件发生后,立即买入,5天后卖出,是一种不错的方案。如果更长时间后卖出,方差更大,风险更大。据此,可以确定下单指令:
Date, AAPL, BUY, 100
Date + 5 days, AAPL, SELL, 100

股值计算

这一部分是相对比较简单的,要从csv格式的文件里面读取指令,执行指令,并计算实际交易期间的资产价值,即持有股票的价格和现金之和。下单指令的顺序并不是按照日期排列的,因此计算时要比较小心。
这一部分要将生成的日期和对应价值写入csv文件,如下:
2008,01,03,50000.0
2008,01,04,49893.0
2008,01,07,49875.0
2008,01,08,49846.0
2008,01,09,49801.0
2008,01,10,49844.0
2008,01,11,49844.0
2008,01,14,49844.0
注意,这个文件仅记录最初买入到最后一次卖出期间的价格。下一步进行策略评估也是根据这些数据来做的。

策略评估

这一部分要求根据策略执行期间的资产价值计算总收益率、日均收益率、收益率标准差和Sharp Ratio。
计算日收益率时,第一天为0,以后每日收益率为相对前一天的增值比率。日均和标准差都是根据日收益率来计算的,是最常用的评估指标。Sharp Ratio是日均收益率和标准差之比进行年化得到,用以衡量收益和风险的比例。
按照之前所说的时间期限和策略,各参数如下:
The final value of the portfolio using the sample file is -- 2009,12,28,54824.0

Details of the Performance of the portfolio

Data Range :  2008-01-03 16:00:00  to  2009-12-28 16:00:00

Sharpe Ratio of Fund : 0.527865227084
Sharpe Ratio of $SPX : -0.184202673931

Total Return of Fund :  1.09648
Total Return of $SPX : 0.779305674563

Standard Deviation of Fund :  0.0060854156452
Standard Deviation of $SPX : 0.022004631521

Average Daily Return of Fund :  0.000202354576186
Average Daily Return of $SPX : -0.000255334653467
可以看到,与SPX,即标准普尔指数相比,这种投资策略在每个参数上面都有优势,是一种有效的投资策略。

平台实现

平台按照上文提到的功能模块划分为三部分。代码如下:

Event Study实现

这一部分除了对事件进行展示,还要按照之前提到的规则生成下单指令:
#how to work? evalate the command below in order
#execfile('hw4_event.py')
#execfile('hw4_order.py')
#execfile('hw4_anylize.py')

#change edge_price to change event


import pandas as pd
import numpy as np
import math
import copy
import QSTK.qstkutil.qsdateutil as du
import datetime as dt
import QSTK.qstkutil.DataAccess as da
import QSTK.qstkutil.tsutil as tsu
import QSTK.qstkstudy.EventProfiler as ep

def find_events(ls_symbols, d_data):
    ''' Finding the event dataframe '''
    df_close = d_data['actual_close']
    ts_market = df_close['SPY']

    print "Finding Events"
    
    #events defined here
    edge_price=7.0
    buy_sell_order=''

    # Creating an empty dataframe
    df_events = copy.deepcopy(df_close)
    df_events = df_events * np.NAN

    # Time stamps for the event range
    ldt_timestamps = df_close.index

    for s_sym in ls_symbols:
        for i in range(1, len(ldt_timestamps)):
            # Calculating the returns for this timestamp
            #f_symprice_today = df_close[s_sym].ix[ldt_timestamps[i]]
            #f_symprice_yest = df_close[s_sym].ix[ldt_timestamps[i - 1]]
            #f_marketprice_today = ts_market.ix[ldt_timestamps[i]]
            #f_marketprice_yest = ts_market.ix[ldt_timestamps[i - 1]]
            #f_symreturn_today = (f_symprice_today / f_symprice_yest) - 1
            #f_marketreturn_today = (f_marketprice_today / f_marketprice_yest) - 1

            # Event is found if the symbol is down more then 3% while the
            # market is up more then 2%
            #if f_symreturn_today <= -0.03 and f_marketreturn_today >= 0.02:
            if df_close[s_sym].ix[ldt_timestamps[i-1]]>=edge_price and df_close[s_sym].ix[ldt_timestamps[i]]<edge_price:
                df_events[s_sym].ix[ldt_timestamps[i]] = 1
                tmp_time=ldt_timestamps[i]
                tmp_day=dt.date(tmp_time.year,tmp_time.month,tmp_time.day)
                buy_sell_order+=str(tmp_day).replace('-',',')+','+s_sym+',Buy,'+str(100)+','
                if i+5<len(ldt_timestamps):
                    tmp_time=ldt_timestamps[i+5]
                else:
                    tmp_time=ldt_timestamps[-1]
                tmp_day=dt.date(tmp_time.year,tmp_time.month,tmp_time.day)
                buy_sell_order+=str(tmp_day).replace('-',',')+','+s_sym+',Sell,'+str(100)+','

    order_list=list(buy_sell_order.split(','))
    del order_list[-1]
    order_array=np.array(order_list)
    order_array.resize((order_array.size/6,6))
    np.savetxt("myorder.csv",order_array,delimiter=',',fmt='%s')
    return df_events


if __name__ == '__main__':
    dt_start = dt.datetime(2008, 1, 1)
    dt_end = dt.datetime(2009, 12, 31)
    ldt_timestamps = du.getNYSEdays(dt_start, dt_end, dt.timedelta(hours=16))

    dataobj = da.DataAccess('Yahoo')
    ls_symbols = dataobj.get_symbols_from_list('sp5002012')
    ls_symbols.append('SPY')

    ls_keys = ['open', 'high', 'low', 'close', 'volume', 'actual_close']
    ldf_data = dataobj.get_data(ldt_timestamps, ls_symbols, ls_keys)
    d_data = dict(zip(ls_keys, ldf_data))

    for s_key in ls_keys:
        d_data[s_key] = d_data[s_key].fillna(method='ffill')
        d_data[s_key] = d_data[s_key].fillna(method='bfill')
        d_data[s_key] = d_data[s_key].fillna(1.0)

    df_events = find_events(ls_symbols, d_data)
    print "Creating Study"
    ep.eventprofiler(df_events, d_data, i_lookback=20, i_lookforward=20,
                s_filename='eventstudy.pdf', b_market_neutral=True, b_errorbars=True,
                s_market_sym='SPY')

股值计算

下单指令上一个模块已经生成,据此计算资产价值:
#how to work? evalate the command below in order
#execfile('hw4_event.py')
#execfile('hw4_order.py')
#execfile('hw4_anylize.py')

#change currentCash,dt_start,dt_end

import pandas as pd
import numpy as np
import math
import copy
import QSTK.qstkutil.qsdateutil as du
import datetime as dt
import QSTK.qstkutil.DataAccess as da
import QSTK.qstkutil.tsutil as tsu
import QSTK.qstkstudy.EventProfiler as ep


#get order
#sys.argv to get comman parameter
na_data = np.loadtxt('myorder.csv',dtype=np.str,delimiter=',')
#dtype={'names':('year','month','day','equity','buorsell','count'),    'formats':('i4','i4','i4','S5','S5','i4')},
na_dates=np.int_(na_data[:,0:3])
order=na_data[:,3:6]
ls_symbols=set(order[:,0])

#get equity price
dt_start = dt.datetime(2008, 1, 1)
dt_end = dt.datetime(2009, 12, 31)
ldt_timestamps = du.getNYSEdays(dt_start, dt_end, dt.timedelta(hours=16))

dataobj = da.DataAccess('Yahoo')

#why close?
#close for Adjusted Close ;actual_close for actual close
ls_keys = 'close'#['open', 'high', 'low', 'close', 'volume', 'actual_close']
ldf_data = dataobj.get_data(ldt_timestamps, ls_symbols, ls_keys)

#calc portfolio
currentCash=50000
currentEquity=dict()
byOrSellDict={'Buy':1,'Sell':-1}

#dateInd=0
#currentDate=dt.datetime(na_dates[dateInd,0],na_dates[dateInd,1],na_dates[dateInd,2])+dt.timedelta(hours=16)
#orders=[dt.datetime(na_dates[dateInd,0],na_dates[dateInd,1],na_dates[dateInd,2])+dt.timedelta(hours=16),
#    [order[dateInd,0],order[dateInd,1],int(order[dateInd,2])] for dateInd in range(na_data.shape[0])]
orders={}
for dateInd in range(na_data.shape[0]):
    tmpDate=dt.datetime(na_dates[dateInd,0],na_dates[dateInd,1],na_dates[dateInd,2])+dt.timedelta(hours=16)
    if tmpDate in orders.keys():
        orders[tmpDate].append([order[dateInd,0],order[dateInd,1],int(order[dateInd,2])])
    else:orders[tmpDate]=[[order[dateInd,0],order[dateInd,1],int(order[dateInd,2])]]

#calc first and last day of order
order_date_list=list(orders.keys())
order_date_list.sort()
first_day=order_date_list[0]
last_day=order_date_list[-1]

values=''
for i in ldt_timestamps:
    if i<first_day or i>last_day:
        continue
    if i in orders.keys():
        for singleOrder in orders[i]:
            equity=singleOrder[0]
            byOrSell=singleOrder[1]
            count=singleOrder[2]
            if equity in currentEquity.keys():
                currentEquity[equity]+=count*byOrSellDict[byOrSell]
            else:currentEquity[equity]=count*byOrSellDict[byOrSell]
            currentCash+=-ldf_data[equity][i]*count*byOrSellDict[byOrSell]
    
            print '----------------------',i,equity,byOrSell,count
            print currentEquity
            
            #dateInd+=1
            #currentDate=dt.datetime(na_dates[dateInd,0],na_dates[dateInd,1],na_dates[dateInd,2])+dt.timedelta(hours=16)
    
    #calc portfolia value
    portfValue=currentCash
    for tmpEqui in currentEquity.keys():
        portfValue+=ldf_data[tmpEqui][i]*currentEquity[tmpEqui]
    print i,portfValue
    values+=str(dt.date(i.year,i.month,i.day)).replace('-',',')+','+str(portfValue)+','
    
values_list=list(values.split(','))
del values_list[-1]
values_array=np.array(values_list)
values_array.resize((values_array.size/4,4))
np.savetxt("hw4values.csv",values_array,delimiter=',',fmt='%s')

策略评估

根据上一模块生成了日期--价值文件计算策略的各种指标:
#how to work? evalate the command below in order
#execfile('hw4_event.py')
#execfile('hw4_order.py')
#execfile('hw4_anylize.py')

from math import sqrt

date_values = np.loadtxt('hw4values.csv',dtype=np.str,delimiter=',')
values=np.float_(date_values[:,3])

total_return=values[-1]/values[0]

#dayly_return=values[1:]/values[:-1]-1
dayly_return=tsu.returnize0(values)

std=np.std(dayly_return)

ave_dayly_ret=np.mean(dayly_return)

sharp_ratio=ave_dayly_ret/std*sqrt(252)

print 'Total Return of Fund: ',total_return
print 'Standard Deviation of Fund: ',std
print 'Average Daily Return of Fund: ',ave_dayly_ret
print 'Sharpe Ratio of Fund: ',sharp_ratio

平台整合

顺序执行上述三个文件即可得到策略的各种指标,完成策略的回测:
execfile('hw4_event.py')
execfile('hw4_order.py')
execfile('hw4_anylize.py')





你可能感兴趣的:(佐治亚理工学院 计算投资公开课第六周作业 投资策略分析平台)