我正在尝试对大熊猫的移动平均交叉策略进行回测。
首先,我定义了一个类(账簿),其中包含库存数量,现金量和资产总额。
该类中有3个函数,可在生成购买或出售信号时计算图书状态。
这是我的代码,但是当我测试时,我会发现股票和总资产的计算错误。
出什么问题了?
import pandas as pd
from pandas_datareader import data as pdr
# download dataframe
test = pdr.get_data_yahoo("SPY", start="2000-01-01")
class Book:
def __init__(self, stocks = 0, money = 100, asset = 0):
self.stocks = stocks
self.money = money
self.asset = asset
def buy(self, price):
if self.money == 0:
return
self.stocks += self.money/price
self.money -= (price * self.stocks)
self.asset = self.stocks * price + self.money
def sell(self, price):
if self.stocks == 0:
return
self.money += (price * self.stocks)
self.stocks = 0
self.asset = self.stocks * price + self.money
def assetEvaluate(self,price):
self.asset = self.stocks * price + self.money
test['ma20'] = test.Close.rolling(20).mean()
def macrossover(df, book):
result = []
for i, r in df.iterrows():
if df.Close[i] > df.ma20[i]:
book.buy(df.Close[i])
elif df.Close[i] < df.ma20[i]:
book.sell(df.Close[i])
else:
book.assetEvaluate(df.Close[i])
result.append([i,df.Close[i], book.stocks, book.money, book.asset])
df = pd.DataFrame(result, columns=['date','close','stocks','money','asset'])
df.set_index('date', inplace=True)
print(df)
a = Book()
macrossover(test,a)
结果
close stocks money asset
date
1999-12-31 146.875000 0.000000 1.000000e+02 100.000000
2000-01-03 145.437500 0.000000 1.000000e+02 100.000000
2000-01-04 139.750000 0.000000 1.000000e+02 100.000000
2000-01-05 140.000000 0.000000 1.000000e+02 100.000000
2000-01-06 137.750000 0.000000 1.000000e+02 100.000000
2000-01-07 145.750000 0.000000 1.000000e+02 100.000000
2000-01-10 146.250000 0.000000 1.000000e+02 100.000000
2000-01-11 144.500000 0.000000 1.000000e+02 100.000000
2000-01-12 143.062500 0.000000 1.000000e+02 100.000000
2000-01-13 145.000000 0.000000 1.000000e+02 100.000000
2000-01-14 146.968704 0.000000 1.000000e+02 100.000000
2000-01-18 145.812500 0.000000 1.000000e+02 100.000000
2000-01-19 147.000000 0.000000 1.000000e+02 100.000000
2000-01-20 144.750000 0.000000 1.000000e+02 100.000000
2000-01-21 144.437500 0.000000 1.000000e+02 100.000000
2000-01-24 140.343704 0.000000 1.000000e+02 100.000000
2000-01-25 141.937500 0.000000 1.000000e+02 100.000000
2000-01-26 140.812500 0.000000 1.000000e+02 100.000000
2000-01-27 140.250000 0.000000 1.000000e+02 100.000000
2000-01-28 135.875000 0.000000 1.000000e+02 100.000000
2000-01-31 139.562500 0.000000 1.000000e+02 100.000000
2000-02-01 140.937500 0.000000 1.000000e+02 100.000000
2000-02-02 141.062500 0.000000 1.000000e+02 100.000000
2000-02-03 143.187500 0.698385 0.000000e+00 100.000000
2000-02-04 142.593704 0.000000 9.958530e+01 99.585302
2000-02-07 142.375000 0.000000 9.958530e+01 99.585302
2000-02-08 144.312500 0.690067 1.421085e-14 99.585302
2000-02-09 141.281204 0.000000 9.749350e+01 97.493505
2000-02-10 141.562500 0.000000 9.749350e+01 97.493505
2000-02-11 138.687500 0.000000 9.749350e+01 97.493505
... ... ... ...
2018-08-08 285.459991 0.002378 0.000000e+00 0.654858
2018-08-09 285.070007 0.002378 0.000000e+00 0.654858
2018-08-10 283.160004 0.002378 0.000000e+00 0.654858
2018-08-13 282.100006 0.000000 6.707412e-01 0.670741
2018-08-14 283.899994 0.002363 0.000000e+00 0.670741
2018-08-15 281.779999 0.000000 6.657325e-01 0.665732
2018-08-16 284.059998 0.002344 0.000000e+00 0.665732
2018-08-17 285.059998 0.002344 0.000000e+00 0.665732
2018-08-20 285.670013 0.002344 0.000000e+00 0.665732
2018-08-21 286.339996 0.002344 0.000000e+00 0.665732
2018-08-22 286.170013 0.002344 0.000000e+00 0.665732
2018-08-23 285.790009 0.002344 0.000000e+00 0.665732
2018-08-24 287.510010 0.002344 0.000000e+00 0.665732
2018-08-27 289.779999 0.002344 0.000000e+00 0.665732
2018-08-28 289.920013 0.002344 0.000000e+00 0.665732
2018-08-29 291.480011 0.002344 0.000000e+00 0.665732
2018-08-30 290.299988 0.002344 0.000000e+00 0.665732
2018-08-31 290.309998 0.002344 0.000000e+00 0.665732
2018-09-04 289.809998 0.002344 0.000000e+00 0.665732
2018-09-05 289.029999 0.002344 0.000000e+00 0.665732
2018-09-06 288.160004 0.002344 0.000000e+00 0.665732
2018-09-07 287.600006 0.002344 0.000000e+00 0.665732
2018-09-10 288.100006 0.002344 0.000000e+00 0.665732
2018-09-11 289.049988 0.002344 0.000000e+00 0.665732
2018-09-12 289.119995 0.002344 0.000000e+00 0.665732
2018-09-13 290.829987 0.002344 0.000000e+00 0.665732
2018-09-14 290.880005 0.002344 0.000000e+00 0.665732
2018-09-17 289.339996 0.002344 0.000000e+00 0.665732
2018-09-18 290.910004 0.002344 0.000000e+00 0.665732
2018-09-19 291.220001 0.002344 0.000000e+00 0.665732
答案 0 :(得分:1)
我更新了您的代码,如下所示,现在它似乎有所改进和改进,我在df
中添加了用于调试目的的其他信息,您可以根据需要将其删除:
import pandas as pd
from pandas_datareader import data as pdr
# download dataframe
test = pdr.get_data_yahoo("SPY", start="2000-01-01")
class Book:
def __init__(self, stocks = 0, money = 100.0, asset = 0.0):
self.stocks = stocks
self.money = money
self.asset = asset
def buy(self, price):
if self.money == 0:
self.asset = price * self.stocks + self.money
return ''
stocks = self.money / price
self.stocks += stocks
self.money -= (price * stocks)
self.asset = price * self.stocks + self.money
return 'buy'
def sell(self, price):
if self.stocks == 0:
return ''
self.money += price * self.stocks
self.stocks = 0
self.asset = self.money
return 'sell'
def assetEvaluate(self,price):
self.asset = self.stocks * price + self.money
return ''
test['ma20'] = test.Close.rolling(20).mean()
def macrossover(df, book):
result = []
trade = ''
for i, r in df.iterrows():
if df.Close[i] > df.ma20[i]:
trade = book.buy(df.Close[i])
elif df.Close[i] < df.ma20[i]:
trade = book.sell(df.Close[i])
else:
trade = book.assetEvaluate(df.Close[i])
result.append([i, df.Close[i], df.ma20[i], book.stocks, book.money, book.asset, trade])
df = pd.DataFrame(result, columns=['date', 'close', 'ma20', 'stocks', 'money', 'asset', 'trade'])
df.set_index('date', inplace=True)
print(df)
a = Book()
macrossover(test, a)
现在输出数据帧如下:
close ma20 stocks money asset trade
date
1999-12-31 146.875000 NaN 0.000000 1.000000e+02 100.000000
2000-01-03 145.437500 NaN 0.000000 1.000000e+02 100.000000
2000-01-04 139.750000 NaN 0.000000 1.000000e+02 100.000000
2000-01-05 140.000000 NaN 0.000000 1.000000e+02 100.000000
2000-01-06 137.750000 NaN 0.000000 1.000000e+02 100.000000
2000-01-07 145.750000 NaN 0.000000 1.000000e+02 100.000000
2000-01-10 146.250000 NaN 0.000000 1.000000e+02 100.000000
2000-01-11 144.500000 NaN 0.000000 1.000000e+02 100.000000
2000-01-12 143.062500 NaN 0.000000 1.000000e+02 100.000000
2000-01-13 145.000000 NaN 0.000000 1.000000e+02 100.000000
2000-01-14 146.968704 NaN 0.000000 1.000000e+02 100.000000
2000-01-18 145.812500 NaN 0.000000 1.000000e+02 100.000000
2000-01-19 147.000000 NaN 0.000000 1.000000e+02 100.000000
2000-01-20 144.750000 NaN 0.000000 1.000000e+02 100.000000
2000-01-21 144.437500 NaN 0.000000 1.000000e+02 100.000000
2000-01-24 140.343704 NaN 0.000000 1.000000e+02 100.000000
2000-01-25 141.937500 NaN 0.000000 1.000000e+02 100.000000
2000-01-26 140.812500 NaN 0.000000 1.000000e+02 100.000000
2000-01-27 140.250000 NaN 0.000000 1.000000e+02 100.000000
2000-01-28 135.875000 143.128120 0.000000 1.000000e+02 100.000000
2000-01-31 139.562500 142.762495 0.000000 1.000000e+02 100.000000
2000-02-01 140.937500 142.537495 0.000000 1.000000e+02 100.000000
2000-02-02 141.062500 142.603120 0.000000 1.000000e+02 100.000000
2000-02-03 143.187500 142.762495 0.698385 0.000000e+00 100.000000 buy
2000-02-04 142.593704 143.004681 0.000000 9.958530e+01 99.585302 sell
2000-02-07 142.375000 142.835931 0.000000 9.958530e+01 99.585302
2000-02-08 144.312500 142.739056 0.690067 1.421085e-14 99.585302 buy
2000-02-09 141.281204 142.578116 0.000000 9.749350e+01 97.493505 sell
2000-02-10 141.562500 142.503116 0.000000 9.749350e+01 97.493505
2000-02-11 138.687500 142.187491 0.000000 9.749350e+01 97.493505
... ... ... ... ... ... ...
2018-08-10 283.160004 282.158501 0.377979 0.000000e+00 107.028495
2018-08-13 282.100006 282.296501 0.000000 1.066278e+02 106.627838 sell
2018-08-14 283.899994 282.468001 0.375582 0.000000e+00 106.627838 buy
2018-08-15 281.779999 282.504001 0.000000 1.058316e+02 105.831606 sell
2018-08-16 284.059998 282.707001 0.372568 0.000000e+00 105.831606 buy
2018-08-17 285.059998 282.976001 0.372568 0.000000e+00 106.204173
2018-08-20 285.670013 283.249501 0.372568 0.000000e+00 106.431446
2018-08-21 286.339996 283.486002 0.372568 0.000000e+00 106.681060
2018-08-22 286.170013 283.594002 0.372568 0.000000e+00 106.617729
2018-08-23 285.790009 283.716502 0.372568 0.000000e+00 106.476152
2018-08-24 287.510010 284.021002 0.372568 0.000000e+00 107.116969
2018-08-27 289.779999 284.512502 0.372568 0.000000e+00 107.962694
2018-08-28 289.920013 284.942003 0.372568 0.000000e+00 108.014859
2018-08-29 291.480011 285.473004 0.372568 0.000000e+00 108.596064
2018-08-30 290.299988 285.868503 0.372568 0.000000e+00 108.156425
2018-08-31 290.309998 286.204002 0.372568 0.000000e+00 108.160154
2018-09-04 289.809998 286.462502 0.372568 0.000000e+00 107.973870
2018-09-05 289.029999 286.635002 0.372568 0.000000e+00 107.683268
2018-09-06 288.160004 286.770003 0.372568 0.000000e+00 107.359136
2018-09-07 287.600006 286.896503 0.372568 0.000000e+00 107.150499
2018-09-10 288.100006 287.143503 0.372568 0.000000e+00 107.336783
2018-09-11 289.049988 287.491002 0.372568 0.000000e+00 107.690715
2018-09-12 289.119995 287.752002 0.372568 0.000000e+00 107.716798
2018-09-13 290.829987 288.204501 0.372568 0.000000e+00 108.353885
2018-09-14 290.880005 288.545502 0.372568 0.000000e+00 108.372521
2018-09-17 289.339996 288.759502 0.372568 0.000000e+00 107.798763
2018-09-18 290.910004 289.021501 0.372568 0.000000e+00 108.383697
2018-09-19 291.220001 289.265501 0.372568 0.000000e+00 108.499192
2018-09-20 293.579987 289.636000 0.372568 0.000000e+00 109.378447
2018-09-21 292.040009 289.948500 0.372568 0.000000e+00 108.804701
我注意到了几件事,首先是在某些计算期间money
变为负数,这是由于Python中的浮点计算限制所致,请参阅Floating Point Arithmetic: Issues and Limitations了解更多详细信息。如果您选择仅将money
作为整数,则可以使用floor division(即使用运算符stocks
)来在代码中解析负数//
。如果将stocks
作为整数,请更新我的上述代码,并用以下代码替换buy
函数:
def buy(self, price):
stocks = self.money // price
if self.money == 0 | stocks == 0:
self.asset = price * self.stocks + self.money
return ''
self.stocks += stocks
self.money -= (price * stocks)
self.asset = price * self.stocks + self.money
return 'buy'
第二件事是,在buy
功能中,当我们购买stocks
时,money
仅需要针对新的stocks
进行减少,而不能针对整个可用范围进行减少。
对于所有使用round(number[, ndigits])进行乘法和除法的浮点数,还可以将其舍入到小数点后两位。
希望这对您有所帮助。