mirror of
https://github.com/wassname/catalyst.git
synced 2026-07-01 21:29:57 +08:00
added and fixed performance tracker tests. still some failures to iron out in performance.
This commit is contained in:
@@ -72,6 +72,7 @@ class PerformanceTracker():
|
||||
self.todays_performance.calculate_performance()
|
||||
|
||||
def handle_market_close(self):
|
||||
qutil.LOGGER.debug("###########market close###############")
|
||||
self.market_open = self.market_open + self.calendar_day
|
||||
while not self.trading_environment.is_trading_day(self.market_open):
|
||||
if self.market_open > self.trading_environment.trading_days[-1]:
|
||||
@@ -98,12 +99,13 @@ class PerformanceTracker():
|
||||
######################################################################################################
|
||||
|
||||
#roll over positions to current day.
|
||||
self.todays_performance.calculate_performance()
|
||||
self.todays_performance = PerformancePeriod(
|
||||
self.market_open,
|
||||
self.market_close,
|
||||
self.todays_performance.positions,
|
||||
self.todays_performance.ending_value,
|
||||
self.capital_base
|
||||
self.todays_performance.ending_cash
|
||||
)
|
||||
|
||||
def handle_simulation_end(self):
|
||||
@@ -133,14 +135,18 @@ class Position():
|
||||
|
||||
def update(self, txn):
|
||||
if(self.sid != txn.sid):
|
||||
raise NameError('attempt to update position with transaction in different sid')
|
||||
raise NameError('updating position with txn for a different sid')
|
||||
#throw exception
|
||||
|
||||
if(self.amount + txn.amount == 0): #we're covering a short or closing a position
|
||||
self.cost_basis = 0.0
|
||||
self.amount = 0
|
||||
else:
|
||||
self.cost_basis = (self.cost_basis*self.amount + (txn.amount*txn.price))/(self.amount + txn.amount)
|
||||
prev_cost = self.cost_basis*self.amount
|
||||
txn_cost = txn.amount*txn.price
|
||||
total_cost = prev_cost + txn_cost
|
||||
total_shares = self.amount + txn.amount
|
||||
self.cost_basis = total_cost/total_shares
|
||||
self.amount = self.amount + txn.amount
|
||||
|
||||
def currentValue(self):
|
||||
@@ -148,35 +154,40 @@ class Position():
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return "sid: {sid}, amount: {amount}, cost_basis: {cost_basis}, last_sale: {last_sale}".format(
|
||||
sid=self.sid, amount=self.amount, cost_basis=self.cost_basis, last_sale=self.last_sale)
|
||||
template = "sid: {sid}, amount: {amount}, cost_basis: {cost_basis}, \
|
||||
last_sale: {last_sale}"
|
||||
return template.format(
|
||||
sid=self.sid,
|
||||
amount=self.amount,
|
||||
cost_basis=self.cost_basis,
|
||||
last_sale=self.last_sale
|
||||
)
|
||||
|
||||
class PerformancePeriod():
|
||||
|
||||
def __init__(self, period_start, period_end, initial_positions, initial_value, capital_base = None):
|
||||
def __init__(self, initial_positions, starting_value, starting_cash):
|
||||
self.ending_value = 0.0
|
||||
self.period_capital_used = 0.0
|
||||
self.period_start = period_start
|
||||
self.period_end = period_end
|
||||
self.positions = initial_positions #sid => position object
|
||||
self.starting_value = initial_value
|
||||
if(capital_base != None):
|
||||
self.capital_base = capital_base
|
||||
else:
|
||||
self.capital_base = 0
|
||||
self.starting_value = starting_value
|
||||
#cash balance at start of period
|
||||
self.starting_cash = starting_cash
|
||||
self.ending_cash = starting_cash
|
||||
|
||||
def calculate_performance(self):
|
||||
self.ending_value = self.calculate_positions_value()
|
||||
self.pnl = (self.ending_value - self.starting_value) - self.period_capital_used
|
||||
if(self.capital_base != 0):
|
||||
self.returns = self.pnl / self.starting_value
|
||||
|
||||
total_at_start = self.starting_cash + self.starting_value
|
||||
self.ending_cash = self.starting_cash + self.period_capital_used
|
||||
total_at_end = self.ending_cash + self.ending_value
|
||||
|
||||
self.pnl = total_at_end - total_at_start
|
||||
if(total_at_start != 0):
|
||||
self.returns = self.pnl / total_at_start
|
||||
else:
|
||||
self.returns = 0.0
|
||||
|
||||
def execute_transaction(self, txn):
|
||||
if(txn.dt > self.period_end):
|
||||
raise Exception("transaction dated {dt} attempted for period ending {ending}".
|
||||
format(dt=txn.dt, ending=self.period_end))
|
||||
if(not self.positions.has_key(txn.sid)):
|
||||
self.positions[txn.sid] = Position(txn.sid)
|
||||
self.positions[txn.sid].update(txn)
|
||||
@@ -195,5 +206,4 @@ class PerformancePeriod():
|
||||
self.positions[event.sid].last_date = event.dt
|
||||
|
||||
|
||||
|
||||
|
||||
+10
-14
@@ -17,16 +17,18 @@ class daily_return():
|
||||
return str(self.date) + " - " + str(self.returns)
|
||||
|
||||
class RiskMetrics():
|
||||
def __init__(self, start_date, end_date, returns, benchmark_returns, treasury_curves, trading_calendar):
|
||||
def __init__(self, start_date, end_date, returns, trading_environment):
|
||||
"""
|
||||
:param treasury_curves: {datetime in utc -> {duration label -> interest rate}}
|
||||
"""
|
||||
|
||||
self.treasury_curves = treasury_curves
|
||||
self.treasury_curves = trading_environment.treasury_curves
|
||||
self.start_date = start_date
|
||||
self.end_date = end_date
|
||||
self.trading_calendar = trading_calendar
|
||||
self.trading_environment = trading_environment
|
||||
self.algorithm_period_returns, self.algorithm_returns = self.calculate_period_returns(returns)
|
||||
benchmark_returns = [x for x in self.trading_environment.benchmark_returns if x.date >= returns[0].date and x.date <= returns[-1].date]
|
||||
|
||||
self.benchmark_period_returns, self.benchmark_returns = self.calculate_period_returns(benchmark_returns)
|
||||
if(len(self.benchmark_returns) != len(self.algorithm_returns)):
|
||||
raise Exception("Mismatch between benchmark_returns ({bm_count}) and algorithm_returns ({algo_count}) in range {start} : {end}".format(
|
||||
@@ -53,7 +55,7 @@ class RiskMetrics():
|
||||
return '\n'.join(statements)
|
||||
|
||||
def calculate_period_returns(self, daily_returns):
|
||||
returns = [x.returns for x in daily_returns if x.date >= self.start_date and x.date <= self.end_date and self.trading_calendar.is_trading_day(x.date)]
|
||||
returns = [x.returns for x in daily_returns if x.date >= self.start_date and x.date <= self.end_date and self.trading_environment.is_trading_day(x.date)]
|
||||
#qutil.LOGGER.debug("using {count} daily returns out of {total}".format(count=len(returns),total=len(daily_returns)))
|
||||
period_returns = 1.0
|
||||
for r in returns:
|
||||
@@ -165,18 +167,13 @@ class RiskMetrics():
|
||||
|
||||
class RiskReport():
|
||||
|
||||
def __init__(self, algorithm_returns, benchmark_returns, treasury_curves, trading_calendar):
|
||||
def __init__(self, algorithm_returns, benchmark_returns, treasury_curves, trading_environment):
|
||||
"""algorithm_returns needs to be a list of daily_return objects sorted in date ascending order"""
|
||||
|
||||
self.algorithm_returns = algorithm_returns
|
||||
self.bm_returns = [x for x in benchmark_returns if x.date >= self.algorithm_returns[0].date and x.date <= self.algorithm_returns[-1].date]
|
||||
self.treasury_curves = treasury_curves
|
||||
self.trading_calendar = trading_calendar
|
||||
self.trading_environment = trading_environment
|
||||
|
||||
qutil.LOGGER.debug("#### {start} thru {end} with {count} trading_days of {total} possible".format(start=self.algorithm_returns[0].date,
|
||||
end=self.algorithm_returns[-1].date,
|
||||
count=len(self.bm_returns),
|
||||
total=len(benchmark_returns)))
|
||||
|
||||
#calculate month ends
|
||||
self.month_periods = self.periodsInRange(1, self.algorithm_returns[0].date, self.algorithm_returns[-1].date)
|
||||
@@ -206,9 +203,7 @@ class RiskReport():
|
||||
start_date=cur_start,
|
||||
end_date=cur_end,
|
||||
returns=self.algorithm_returns,
|
||||
benchmark_returns=self.bm_returns,
|
||||
treasury_curves=self.treasury_curves,
|
||||
trading_calendar=self.trading_calendar
|
||||
trading_environment=self.trading_environment
|
||||
)
|
||||
|
||||
ends.append(cur_period_metrics)
|
||||
@@ -244,6 +239,7 @@ class TradingEnvironment(object):
|
||||
self.trading_days = []
|
||||
self.trading_day_map = {}
|
||||
self.treasury_curves = treasury_curves
|
||||
self.benchmark_returns = benchmark_returns
|
||||
for bm in benchmark_returns:
|
||||
self.trading_days.append(bm.date)
|
||||
self.trading_day_map[bm.date] = bm
|
||||
|
||||
+11
-7
@@ -23,14 +23,14 @@ def load_market_data():
|
||||
|
||||
|
||||
def create_trade(sid, price, amount, datetime):
|
||||
row = {
|
||||
row = zp.namedict({
|
||||
'source_id' : "test_factory",
|
||||
'type' : zp.DATASOURCE_TYPE.TRADE,
|
||||
'sid' : sid,
|
||||
'dt' : datetime,
|
||||
'price' : price,
|
||||
'volume' : amount
|
||||
}
|
||||
})
|
||||
return row
|
||||
|
||||
def create_trade_history(sid, prices, amounts, start_time, interval, trading_calendar):
|
||||
@@ -50,19 +50,23 @@ def create_trade_history(sid, prices, amounts, start_time, interval, trading_cal
|
||||
|
||||
return trades
|
||||
|
||||
def createTxn(sid, price, amount, datetime, btrid=None):
|
||||
txn = Transaction(sid=sid, amount=amount, dt = datetime,
|
||||
price=price, transaction_cost=-1*price*amount)
|
||||
def create_txn(sid, price, amount, datetime, btrid=None):
|
||||
txn = zp.namedict({
|
||||
'sid':sid,
|
||||
'amount':amount,
|
||||
'dt':datetime,
|
||||
'price':price,
|
||||
})
|
||||
return txn
|
||||
|
||||
def create_transaction_history(sid, priceList, amtList, startTime, interval, trading_calendar):
|
||||
def create_txn_history(sid, priceList, amtList, startTime, interval, trading_calendar):
|
||||
txns = []
|
||||
current = startTime
|
||||
|
||||
for price, amount in zip(priceList, amtList):
|
||||
|
||||
if trading_calendar.is_trading_day(current):
|
||||
txns.append(createTxn(sid, price, amount, current))
|
||||
txns.append(create_txn(sid, price, amount, current))
|
||||
current = current + interval
|
||||
|
||||
else:
|
||||
|
||||
@@ -0,0 +1,526 @@
|
||||
import unittest
|
||||
import copy
|
||||
import random
|
||||
import datetime
|
||||
|
||||
import zipline.test.factory as factory
|
||||
import zipline.util as qutil
|
||||
import zipline.protocol as zp
|
||||
import zipline.finance.performance as perf
|
||||
import zipline.finance.risk as risk
|
||||
class PerformanceTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
qutil.configure_logging()
|
||||
self.benchmark_returns, self.treasury_curves = \
|
||||
factory.load_market_data()
|
||||
|
||||
self.trading_environment = risk.TradingEnvironment(
|
||||
self.benchmark_returns,
|
||||
self.treasury_curves
|
||||
)
|
||||
|
||||
self.onesec = datetime.timedelta(seconds=1)
|
||||
self.oneday = datetime.timedelta(days=1)
|
||||
self.tradingday = datetime.timedelta(hours=6, minutes=30)
|
||||
random_index = random.randint(
|
||||
0,
|
||||
len(self.trading_environment.trading_days)
|
||||
)
|
||||
|
||||
self.dt = self.trading_environment.trading_days[random_index]
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def test_long_position(self):
|
||||
"""
|
||||
verify that the performance period calculates properly for a \
|
||||
single buy transaction
|
||||
"""
|
||||
#post some trades in the market
|
||||
trades = factory.create_trade_history(
|
||||
1,
|
||||
[10,10,10,11],
|
||||
[100,100,100,100],
|
||||
self.dt,
|
||||
self.onesec,
|
||||
self.trading_environment
|
||||
)
|
||||
|
||||
txn = factory.create_txn(1,10.0,100,self.dt + self.onesec)
|
||||
pp = perf.PerformancePeriod({}, 0.0, 1000.0)
|
||||
|
||||
pp.execute_transaction(txn)
|
||||
for trade in trades:
|
||||
pp.update_last_sale(trade)
|
||||
|
||||
pp.calculate_performance()
|
||||
|
||||
self.assertEqual(
|
||||
pp.period_capital_used,
|
||||
-1 * txn.price * txn.amount,
|
||||
"capital used should be equal to the opposite of the transaction \
|
||||
cost of sole txn in test"
|
||||
)
|
||||
|
||||
self.assertEqual(len(pp.positions),1,"should be just one position")
|
||||
|
||||
self.assertEqual(
|
||||
pp.positions[1].sid,
|
||||
txn.sid,
|
||||
"position should be in security with id 1")
|
||||
|
||||
self.assertEqual(
|
||||
pp.positions[1].amount,
|
||||
txn.amount,
|
||||
"should have a position of {sharecount} shares".format(
|
||||
sharecount=txn.amount
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
pp.positions[1].cost_basis,
|
||||
txn.price,
|
||||
"should have a cost basis of 10"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
pp.positions[1].last_sale,
|
||||
trades[-1]['price'],
|
||||
"last sale should be same as last trade. \
|
||||
expected {exp} actual {act}".format(
|
||||
exp=trades[-1]['price'],
|
||||
act=pp.positions[1].last_sale
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
pp.ending_value,
|
||||
1100,
|
||||
"ending value should be price of last trade times number of \
|
||||
shares in position"
|
||||
)
|
||||
|
||||
self.assertEqual(pp.pnl, 100, "gain of 1 on 100 shares should be 100")
|
||||
|
||||
def test_short_position(self):
|
||||
"""verify that the performance period calculates properly for a \
|
||||
single short-sale transaction"""
|
||||
trades_1 = factory.create_trade_history(
|
||||
1,
|
||||
[10,10,10,11],
|
||||
[100,100,100,100],
|
||||
self.dt,
|
||||
self.onesec,
|
||||
self.trading_environment
|
||||
)
|
||||
|
||||
txn = factory.create_txn(1, 10.0, -100, self.dt + self.onesec)
|
||||
pp = perf.PerformancePeriod({}, 0.0, 1000.0)
|
||||
|
||||
pp.execute_transaction(txn)
|
||||
for trade in trades_1:
|
||||
pp.update_last_sale(trade)
|
||||
|
||||
pp.calculate_performance()
|
||||
|
||||
self.assertEqual(
|
||||
pp.period_capital_used,
|
||||
-1 * txn.price * txn.amount,
|
||||
"capital used should be equal to the opposite of the transaction\
|
||||
cost of sole txn in test"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
len(pp.positions),
|
||||
1,
|
||||
"should be just one position")
|
||||
|
||||
self.assertEqual(
|
||||
pp.positions[1].sid,
|
||||
txn.sid,
|
||||
"position should be in security from the transaction"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
pp.positions[1].amount,
|
||||
-100,
|
||||
"should have a position of -100 shares"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
pp.positions[1].cost_basis,
|
||||
txn.price,
|
||||
"should have a cost basis of 10"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
pp.positions[1].last_sale,
|
||||
trades_1[-1]['price'],
|
||||
"last sale should be price of last trade"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
pp.ending_value,
|
||||
-1100,
|
||||
"ending value should be price of last trade times number of \
|
||||
shares in position"
|
||||
)
|
||||
|
||||
self.assertEqual(pp.pnl,-100,"gain of 1 on 100 shares should be 100")
|
||||
|
||||
#simulate additional trades, and ensure that the position value
|
||||
#reflects the new price
|
||||
trades_2 = factory.create_trade_history(
|
||||
1,
|
||||
[10,9],
|
||||
[100,100],
|
||||
trades_1[-1]['dt'] + self.onesec,
|
||||
self.onesec,
|
||||
self.trading_environment
|
||||
)
|
||||
|
||||
#simulate a rollover to a new period
|
||||
pp2 = perf.PerformancePeriod(
|
||||
pp.positions,
|
||||
pp.ending_value,
|
||||
pp.ending_cash
|
||||
)
|
||||
|
||||
for trade in trades_2:
|
||||
pp2.update_last_sale(trade)
|
||||
|
||||
pp2.calculate_performance()
|
||||
|
||||
self.assertEqual(
|
||||
pp2.period_capital_used,
|
||||
0,
|
||||
"capital used should be zero, there were no transactions in \
|
||||
performance period"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
len(pp2.positions),
|
||||
1,
|
||||
"should be just one position"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
pp2.positions[1].sid,
|
||||
txn.sid,
|
||||
"position should be in security from the transaction"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
pp2.positions[1].amount,
|
||||
-100,
|
||||
"should have a position of -100 shares"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
pp2.positions[1].cost_basis,
|
||||
txn.price,
|
||||
"should have a cost basis of 10"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
pp2.positions[1].last_sale,
|
||||
trades_2[-1].price,
|
||||
"last sale should be price of last trade"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
pp2.ending_value,
|
||||
-900,
|
||||
"ending value should be price of last trade times number of \
|
||||
shares in position")
|
||||
|
||||
self.assertEqual(
|
||||
pp2.pnl,
|
||||
200,
|
||||
"drop of 2 on -100 shares should be 200"
|
||||
)
|
||||
|
||||
#now run a performance period encompassing the entire trade sample.
|
||||
ppTotal = perf.PerformancePeriod({}, 0.0, 1000.0)
|
||||
|
||||
for trade in trades_1:
|
||||
ppTotal.update_last_sale(trade)
|
||||
|
||||
ppTotal.execute_transaction(txn)
|
||||
|
||||
for trade in trades_2:
|
||||
ppTotal.update_last_sale(trade)
|
||||
|
||||
ppTotal.calculate_performance()
|
||||
|
||||
self.assertEqual(
|
||||
ppTotal.period_capital_used,
|
||||
-1 * txn.price * txn.amount,
|
||||
"capital used should be equal to the opposite of the transaction \
|
||||
cost of sole txn in test"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
len(ppTotal.positions),
|
||||
1,
|
||||
"should be just one position"
|
||||
)
|
||||
self.assertEqual(
|
||||
ppTotal.positions[1].sid,
|
||||
txn.sid,
|
||||
"position should be in security from the transaction"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
ppTotal.positions[1].amount,
|
||||
-100,
|
||||
"should have a position of -100 shares"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
ppTotal.positions[1].cost_basis,
|
||||
txn.price,
|
||||
"should have a cost basis of 10"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
ppTotal.positions[1].last_sale,
|
||||
trades_2[-1].price,
|
||||
"last sale should be price of last trade"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
ppTotal.ending_value,
|
||||
-900,
|
||||
"ending value should be price of last trade times number of \
|
||||
shares in position")
|
||||
|
||||
self.assertEqual(
|
||||
ppTotal.pnl,
|
||||
100,
|
||||
"drop of 1 on -100 shares should be 100"
|
||||
)
|
||||
|
||||
def test_covering_short(self):
|
||||
"""verify performance where short is bought and covered, and shares \
|
||||
trade after cover"""
|
||||
|
||||
trades = factory.create_trade_history(
|
||||
1,
|
||||
[10,10,10,11,9,8,7,8,9,10],
|
||||
[100,100,100,100,100,100,100,100,100,100],
|
||||
self.dt,
|
||||
self.onesec,
|
||||
self.trading_environment
|
||||
)
|
||||
|
||||
short_txn = factory.create_txn(
|
||||
1,
|
||||
10.0,
|
||||
-100,
|
||||
self.dt + self.onesec
|
||||
)
|
||||
|
||||
cover_txn = factory.create_txn(1,7.0,100,self.dt + self.onesec * 6)
|
||||
pp = perf.PerformancePeriod({}, 0.0, 1000.0)
|
||||
|
||||
pp.execute_transaction(short_txn)
|
||||
pp.execute_transaction(cover_txn)
|
||||
|
||||
for trade in trades:
|
||||
pp.update_last_sale(trade)
|
||||
|
||||
pp.calculate_performance()
|
||||
|
||||
short_txn_cost = short_txn.price * short_txn.amount
|
||||
cover_txn_cost = cover_txn.price * cover_txn.amount
|
||||
|
||||
self.assertEqual(
|
||||
pp.period_capital_used,
|
||||
-1 * short_txn_cost - cover_txn_cost,
|
||||
"capital used should be equal to the net transaction costs"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
len(pp.positions),
|
||||
1,
|
||||
"should be just one position"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
pp.positions[1].sid,
|
||||
short_txn.sid,
|
||||
"position should be in security from the transaction"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
pp.positions[1].amount,
|
||||
0,
|
||||
"should have a position of -100 shares"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
pp.positions[1].cost_basis,
|
||||
0,
|
||||
"a covered position should have a cost basis of 0"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
pp.positions[1].last_sale,
|
||||
trades[-1].price,
|
||||
"last sale should be price of last trade"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
pp.ending_value,
|
||||
0,
|
||||
"ending value should be price of last trade times number of \
|
||||
shares in position"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
pp.pnl,
|
||||
300,
|
||||
"gain of 1 on 100 shares should be 300"
|
||||
)
|
||||
|
||||
def test_cost_basis_calc(self):
|
||||
trades = factory.create_trade_history(
|
||||
1,
|
||||
[10,11,11,12],
|
||||
[100,100,100,100],
|
||||
self.dt,
|
||||
self.onesec,
|
||||
self.trading_environment
|
||||
)
|
||||
|
||||
transactions = factory.create_txn_history(
|
||||
1,
|
||||
[10,11,11,12],
|
||||
[100,100,100,100],
|
||||
self.dt,
|
||||
self.onesec,
|
||||
self.trading_environment
|
||||
)
|
||||
|
||||
pp = perf.PerformancePeriod({}, 0.0, 1000.0)
|
||||
|
||||
for txn in transactions:
|
||||
pp.execute_transaction(txn)
|
||||
|
||||
for trade in trades:
|
||||
pp.update_last_sale(trade)
|
||||
|
||||
pp.calculate_performance()
|
||||
|
||||
self.assertEqual(
|
||||
pp.positions[1].last_sale,
|
||||
trades[-1].price,
|
||||
"should have a last sale of 12, got {val}".format(
|
||||
val=pp.positions[1].last_sale
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
pp.positions[1].cost_basis,
|
||||
11,
|
||||
"should have a cost basis of 11"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
pp.pnl,
|
||||
400
|
||||
)
|
||||
|
||||
saleTxn = factory.create_txn(
|
||||
1,
|
||||
10.0,
|
||||
-100,
|
||||
self.dt + self.onesec * 4)
|
||||
|
||||
down_tick = factory.create_trade(
|
||||
1,
|
||||
10.0,
|
||||
100,
|
||||
trades[-1].dt + self.onesec)
|
||||
|
||||
pp2 = perf.PerformancePeriod(
|
||||
copy.deepcopy(pp.positions),
|
||||
pp.ending_value,
|
||||
pp.ending_cash
|
||||
)
|
||||
|
||||
pp2.execute_transaction(saleTxn)
|
||||
pp2.update_last_sale(down_tick)
|
||||
|
||||
pp2.calculate_performance()
|
||||
self.assertEqual(
|
||||
pp2.positions[1].last_sale,
|
||||
10,
|
||||
"should have a last sale of 10, was {val}".format(val=pp2.positions[1].last_sale)
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
round(pp2.positions[1].cost_basis,2),
|
||||
11.33,
|
||||
"should have a cost basis of 11.33"
|
||||
)
|
||||
|
||||
#print "second period pnl is {pnl}".format(pnl=pp2.pnl)
|
||||
self.assertEqual(pp2.pnl, -800, "this period goes from +400 to -400")
|
||||
|
||||
pp3 = perf.PerformancePeriod({}, 0.0, 1000.0)
|
||||
|
||||
transactions.append(saleTxn)
|
||||
for txn in transactions:
|
||||
pp3.execute_transaction(txn)
|
||||
|
||||
trades.append(down_tick)
|
||||
for trade in trades:
|
||||
pp3.update_last_sale(trade)
|
||||
|
||||
pp3.calculate_performance()
|
||||
self.assertEqual(
|
||||
pp3.positions[1].last_sale,
|
||||
10,
|
||||
"should have a last sale of 10"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
round(pp3.positions[1].cost_basis,2),
|
||||
11.33,
|
||||
"should have a cost basis of 11.33"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
pp3.pnl,
|
||||
-400,
|
||||
"should be -400 for all trades and transactions in period"
|
||||
)
|
||||
|
||||
|
||||
def dtest_daily_performance_calc(self):
|
||||
hostedAlgo = factories.createAlgo("workingAlgo.py")
|
||||
btRecord = BackTestRun(duration_unit="Days",duration_count=5,capital_base=25000000)
|
||||
bt = BackTest(hostedAlgo,btRecord)
|
||||
start = bt.periodStart
|
||||
end = bt.periodEnd
|
||||
#print "{start} to {end}".format(start=start, end=end)
|
||||
|
||||
trades = factories.createTradeHistory(1,[10,11,12,11],[100,100,100,100],start, self.oneday)
|
||||
#createTransaction(self, sid, amount, price, dt, order_id)
|
||||
bt.createTransaction(1, 100, 10.0, trades[0].dt + 30*self.onesec, None)
|
||||
curPeriod = start
|
||||
bt.positions = {}
|
||||
dailyPeriods = []
|
||||
bt.initialValue = 0.0
|
||||
while (bt.mktClose) <= bt.periodEnd:
|
||||
bt.updatePerformance()
|
||||
dailyPeriods.append(bt.curPeriod)
|
||||
bt.nextMarketDay()
|
||||
|
||||
self.assertEqual(dailyPeriods[0].pnl,0,"the first day's performance should be zero")
|
||||
self.assertEqual(dailyPeriods[1].pnl,100,"the second day's pnl should be 100 but was {pnl}".format(pnl=dailyPeriods[1].pnl))
|
||||
|
||||
Reference in New Issue
Block a user