fixes to the calculation of transactions and associated tests for long and short orders.

This commit is contained in:
fawce
2012-04-12 10:46:10 -04:00
parent b78097241a
commit aea2e1189c
6 changed files with 144 additions and 28 deletions
+4 -4
View File
@@ -357,13 +357,13 @@ for order:
order=str(order)
)
qutil.LOGGER.warn(warning)
orders = [ x for x in orders if x.amount - x.filled > 0 and x.dt.day >= event.dt.day]
orders = [ x for x in orders if abs(x.amount - x.filled) > 0 and x.dt.day >= event.dt.day]
self.open_orders[event.sid] = orders
if simulated_amount > 0:
if simulated_amount != 0:
return self.create_transaction(
event.sid,
simulated_amount,
+11 -11
View File
@@ -191,16 +191,16 @@ class SimulatedTrading(object):
- sid - an integer, which will be used as the security ID.
- order_count - the number of orders the test algo will place,
defaults to 100
- order_amount - the number of shares per order, defaults to 100
- trade_count - the number of trades to simulate, defaults to 100
- simulator_class - optional parameter that provides an alternative
subclass of ComponentHost to hold the whole zipline. Defaults to
:py:class:`zipline.simulator.Simulator`
- algorithm - optional parameter providing an algorithm. defaults
to :py:class:`zipline.test.algorithms.TestAlgorithm`
- random - optional parameter to request random trades. if present
:py:class:`zipline.sources.RandomEquityTrades` is the source. If
not :py:class:`ziplien.sources.SpecificEquityTrades` is the
source
- trade_source - optional parameter to specify trades, if present.
If not present :py:class:`ziplien.sources.SpecificEquityTrades`
is the source, with daily frequency in trades.
"""
assert isinstance(config, dict)
@@ -219,6 +219,11 @@ class SimulatedTrading(object):
order_count = config['order_count']
else:
order_count = 100
if config.has_key('order_amount'):
order_amount = config['order_amount']
else:
order_amount = 100
if config.has_key('trade_count'):
trade_count = config['trade_count']
@@ -235,12 +240,8 @@ class SimulatedTrading(object):
#-------------------
sids = [sid]
#-------------------
if config.has_key('random'):
trade_source = factory.create_random_trade_source(
sids,
trade_count,
trading_environment
)
if config.has_key('trade_source'):
trade_source = config['trade_source']
else:
trade_source = factory.create_daily_trade_source(
sids,
@@ -253,7 +254,6 @@ class SimulatedTrading(object):
if config.has_key('algorithm'):
test_algo = config['algorithm']
else:
order_amount = 100
test_algo = TestAlgorithm(
sid,
order_amount,
+2 -2
View File
@@ -76,8 +76,8 @@ class TestAlgorithm():
self.incr += 1
def get_sid_filter(self):
return [self.sid]
return [self.sid]
class NoopAlgorithm(object):
"""
Dolce fa niente.
+28 -1
View File
@@ -167,6 +167,7 @@ def create_random_trade_source(sid, trade_count, trading_environment):
return source
def create_daily_trade_source(sids, trade_count, trading_environment):
"""
creates trade_count trades for each sid in sids list.
first trade will be on trading_environment.period_start, and daily
@@ -176,12 +177,38 @@ def create_daily_trade_source(sids, trade_count, trading_environment):
Important side-effect: trading_environment.period_end will be modified
to match the day of the final trade.
"""
return create_trade_source(
sids,
trade_count,
timedelta(days=1),
trading_environment
)
def create_minutely_trade_source(sids, trade_count, trading_environment):
"""
creates trade_count trades for each sid in sids list.
first trade will be on trading_environment.period_start, and every minute
thereafter for each sid. Thus, two sids should result in two trades per
minute.
Important side-effect: trading_environment.period_end will be modified
to match the day of the final trade.
"""
return create_trade_source(
sids,
trade_count,
timedelta(minutes=1),
trading_environment
)
def create_trade_source(sids, trade_count, trade_time_increment, trading_environment):
trade_history = []
for sid in sids:
price = [10.1] * trade_count
volume = [100] * trade_count
start_date = trading_environment.first_open
trade_time_increment = timedelta(days=1)
generated_trades = create_trade_history(
sid,
+90 -6
View File
@@ -24,6 +24,7 @@ from zipline.lines import SimulatedTrading
from zipline.protocol_utils import namedict
DEFAULT_TIMEOUT = 15 # seconds
EXTENDED_TIMEOUT = 90
allocator = AddressAllocator(1000)
@@ -121,6 +122,38 @@ class FinanceTestCase(TestCase):
.format(n=zipline.sim.feed.pending_messages()))
@timed(EXTENDED_TIMEOUT)
def test_aggressive_buying(self):
# Simulation
# ----------
trade_count = 10 * 1000
self.zipline_test_config['order_count'] = 5 * 1000
self.zipline_test_config['trade_count'] = trade_count
self.zipline_test_config['order_amount'] = 100
self.zipline_test_config['environment'] = factory.create_trading_environment()
sid_list = [self.zipline_test_config['sid']]
self.zipline_test_config['trade_source'] = factory.create_minutely_trade_source(
sid_list,
trade_count,
self.zipline_test_config['environment']
)
zipline = SimulatedTrading.create_test_zipline(**self.zipline_test_config)
zipline.simulate(blocking=True)
self.assertTrue(zipline.sim.ready())
self.assertFalse(zipline.sim.exception)
# TODO: Make more assertions about the final state of the components.
self.assertEqual(zipline.sim.feed.pending_messages(), 0, \
"The feed should be drained of all messages, found {n} remaining." \
.format(n=zipline.sim.feed.pending_messages()))
@timed(DEFAULT_TIMEOUT)
def test_performance(self):
#provide enough trades to ensure all orders are filled.
@@ -218,9 +251,11 @@ class FinanceTestCase(TestCase):
)
# TODO: write a test that proves orders expire without being filled.
# TODO: write tests for short sales
# TODO: write a test to do massive buying or shorting.
@timed(DEFAULT_TIMEOUT)
def test_transaction_sim(self):
def test_partially_filled_orders(self):
# create a scenario where order size and trade size are equal
# so that orders must be spread out over several trades.
@@ -240,9 +275,25 @@ class FinanceTestCase(TestCase):
self.transaction_sim(**params)
# same scenario, but with short sales
params2 ={
'trade_count':360,
'trade_amount':100,
'trade_interval': timedelta(minutes=1),
'order_count':2,
'order_amount':-100,
'order_interval': timedelta(minutes=1),
'expected_txn_count':8,
'expected_txn_volume':2 * -100
}
self.transaction_sim(**params2)
@timed(DEFAULT_TIMEOUT)
def test_collapsing_orders(self):
# create a scenario where order.amount <<< trade.volume
# to test that several orders can be covered properly by one trade.
params2 ={
params1 ={
'trade_count':6,
'trade_amount':100,
'trade_interval': timedelta(hours=1),
@@ -254,11 +305,26 @@ class FinanceTestCase(TestCase):
'expected_txn_count':1,
'expected_txn_volume':24 * 1
}
self.transaction_sim(**params2)
self.transaction_sim(**params1)
# second verse, same as the first. except short!
params2 ={
'trade_count':6,
'trade_amount':100,
'trade_interval': timedelta(hours=1),
'order_count':24,
'order_amount':-1,
'order_interval': timedelta(minutes=1),
'expected_txn_count':1,
'expected_txn_volume':24 * -1
}
self.transaction_sim(**params2)
@timed(DEFAULT_TIMEOUT)
def test_partial_expiration_orders(self):
# create a scenario where orders expire without being filled
# entirely
params3 = {
params1 = {
'trade_count':100,
'trade_amount':100,
'trade_delay': timedelta(minutes=5),
@@ -271,7 +337,25 @@ class FinanceTestCase(TestCase):
'expected_txn_count' : 1,
'expected_txn_volume' : 25
}
self.transaction_sim(**params3)
self.transaction_sim(**params1)
# same scenario, but short sales.
params2 = {
'trade_count':100,
'trade_amount':100,
'trade_delay': timedelta(minutes=5),
'trade_interval': timedelta(days=1),
'order_count':3,
'order_amount':1000,
'order_interval': timedelta(minutes=30),
# because we placed an orders totaling less than 25% of one trade
# the simulator should produce just one transaction.
'expected_txn_count' : 1,
'expected_txn_volume' : 25
}
self.transaction_sim(**params2)
def transaction_sim(self, **params):
trade_count = params['trade_count']
+9 -4
View File
@@ -22,8 +22,15 @@ class PerformanceTestCase(unittest.TestCase):
0,
len(self.treasury_curves)
)
self.dt = self.treasury_curves.keys()[random_index]
self.end_dt = self.dt + datetime.timedelta(days=365)
for n in range(100):
self.dt = self.treasury_curves.keys()[random_index]
self.end_dt = self.dt + datetime.timedelta(days=365)
now = datetime.datetime.utcnow().replace(tzinfo=pytz.utc)
if self.end_dt <= now:
break
self.trading_environment = TradingEnvironment(
self.benchmark_returns,
self.treasury_curves,
@@ -505,8 +512,6 @@ shares in position"
price = 10.1
price_list = [price] * trade_count
volume = [100] * trade_count
#start_date = datetime.datetime.strptime("01/01/2011","%m/%d/%Y")
#start_date = start_date.replace(tzinfo=pytz.utc)
trade_time_increment = datetime.timedelta(days=1)
trade_history = factory.create_trade_history(
sid,