mirror of
https://github.com/wassname/catalyst.git
synced 2026-09-12 12:12:04 +08:00
fixes to the calculation of transactions and associated tests for long and short orders.
This commit is contained in:
@@ -357,13 +357,13 @@ for order:
|
|||||||
order=str(order)
|
order=str(order)
|
||||||
)
|
)
|
||||||
qutil.LOGGER.warn(warning)
|
qutil.LOGGER.warn(warning)
|
||||||
|
|
||||||
|
orders = [ x for x in orders if abs(x.amount - x.filled) > 0 and x.dt.day >= event.dt.day]
|
||||||
orders = [ x for x in orders if x.amount - x.filled > 0 and x.dt.day >= event.dt.day]
|
|
||||||
self.open_orders[event.sid] = orders
|
self.open_orders[event.sid] = orders
|
||||||
|
|
||||||
|
|
||||||
if simulated_amount > 0:
|
if simulated_amount != 0:
|
||||||
return self.create_transaction(
|
return self.create_transaction(
|
||||||
event.sid,
|
event.sid,
|
||||||
simulated_amount,
|
simulated_amount,
|
||||||
|
|||||||
+11
-11
@@ -191,16 +191,16 @@ class SimulatedTrading(object):
|
|||||||
- sid - an integer, which will be used as the security ID.
|
- sid - an integer, which will be used as the security ID.
|
||||||
- order_count - the number of orders the test algo will place,
|
- order_count - the number of orders the test algo will place,
|
||||||
defaults to 100
|
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
|
- trade_count - the number of trades to simulate, defaults to 100
|
||||||
- simulator_class - optional parameter that provides an alternative
|
- simulator_class - optional parameter that provides an alternative
|
||||||
subclass of ComponentHost to hold the whole zipline. Defaults to
|
subclass of ComponentHost to hold the whole zipline. Defaults to
|
||||||
:py:class:`zipline.simulator.Simulator`
|
:py:class:`zipline.simulator.Simulator`
|
||||||
- algorithm - optional parameter providing an algorithm. defaults
|
- algorithm - optional parameter providing an algorithm. defaults
|
||||||
to :py:class:`zipline.test.algorithms.TestAlgorithm`
|
to :py:class:`zipline.test.algorithms.TestAlgorithm`
|
||||||
- random - optional parameter to request random trades. if present
|
- trade_source - optional parameter to specify trades, if present.
|
||||||
:py:class:`zipline.sources.RandomEquityTrades` is the source. If
|
If not present :py:class:`ziplien.sources.SpecificEquityTrades`
|
||||||
not :py:class:`ziplien.sources.SpecificEquityTrades` is the
|
is the source, with daily frequency in trades.
|
||||||
source
|
|
||||||
"""
|
"""
|
||||||
assert isinstance(config, dict)
|
assert isinstance(config, dict)
|
||||||
|
|
||||||
@@ -219,6 +219,11 @@ class SimulatedTrading(object):
|
|||||||
order_count = config['order_count']
|
order_count = config['order_count']
|
||||||
else:
|
else:
|
||||||
order_count = 100
|
order_count = 100
|
||||||
|
|
||||||
|
if config.has_key('order_amount'):
|
||||||
|
order_amount = config['order_amount']
|
||||||
|
else:
|
||||||
|
order_amount = 100
|
||||||
|
|
||||||
if config.has_key('trade_count'):
|
if config.has_key('trade_count'):
|
||||||
trade_count = config['trade_count']
|
trade_count = config['trade_count']
|
||||||
@@ -235,12 +240,8 @@ class SimulatedTrading(object):
|
|||||||
#-------------------
|
#-------------------
|
||||||
sids = [sid]
|
sids = [sid]
|
||||||
#-------------------
|
#-------------------
|
||||||
if config.has_key('random'):
|
if config.has_key('trade_source'):
|
||||||
trade_source = factory.create_random_trade_source(
|
trade_source = config['trade_source']
|
||||||
sids,
|
|
||||||
trade_count,
|
|
||||||
trading_environment
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
trade_source = factory.create_daily_trade_source(
|
trade_source = factory.create_daily_trade_source(
|
||||||
sids,
|
sids,
|
||||||
@@ -253,7 +254,6 @@ class SimulatedTrading(object):
|
|||||||
if config.has_key('algorithm'):
|
if config.has_key('algorithm'):
|
||||||
test_algo = config['algorithm']
|
test_algo = config['algorithm']
|
||||||
else:
|
else:
|
||||||
order_amount = 100
|
|
||||||
test_algo = TestAlgorithm(
|
test_algo = TestAlgorithm(
|
||||||
sid,
|
sid,
|
||||||
order_amount,
|
order_amount,
|
||||||
|
|||||||
@@ -76,8 +76,8 @@ class TestAlgorithm():
|
|||||||
self.incr += 1
|
self.incr += 1
|
||||||
|
|
||||||
def get_sid_filter(self):
|
def get_sid_filter(self):
|
||||||
return [self.sid]
|
return [self.sid]
|
||||||
|
|
||||||
class NoopAlgorithm(object):
|
class NoopAlgorithm(object):
|
||||||
"""
|
"""
|
||||||
Dolce fa niente.
|
Dolce fa niente.
|
||||||
|
|||||||
+28
-1
@@ -167,6 +167,7 @@ def create_random_trade_source(sid, trade_count, trading_environment):
|
|||||||
return source
|
return source
|
||||||
|
|
||||||
def create_daily_trade_source(sids, trade_count, trading_environment):
|
def create_daily_trade_source(sids, trade_count, trading_environment):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
creates trade_count trades for each sid in sids list.
|
creates trade_count trades for each sid in sids list.
|
||||||
first trade will be on trading_environment.period_start, and daily
|
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
|
Important side-effect: trading_environment.period_end will be modified
|
||||||
to match the day of the final trade.
|
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 = []
|
trade_history = []
|
||||||
for sid in sids:
|
for sid in sids:
|
||||||
price = [10.1] * trade_count
|
price = [10.1] * trade_count
|
||||||
volume = [100] * trade_count
|
volume = [100] * trade_count
|
||||||
start_date = trading_environment.first_open
|
start_date = trading_environment.first_open
|
||||||
trade_time_increment = timedelta(days=1)
|
|
||||||
|
|
||||||
generated_trades = create_trade_history(
|
generated_trades = create_trade_history(
|
||||||
sid,
|
sid,
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ from zipline.lines import SimulatedTrading
|
|||||||
from zipline.protocol_utils import namedict
|
from zipline.protocol_utils import namedict
|
||||||
|
|
||||||
DEFAULT_TIMEOUT = 15 # seconds
|
DEFAULT_TIMEOUT = 15 # seconds
|
||||||
|
EXTENDED_TIMEOUT = 90
|
||||||
|
|
||||||
allocator = AddressAllocator(1000)
|
allocator = AddressAllocator(1000)
|
||||||
|
|
||||||
@@ -121,6 +122,38 @@ class FinanceTestCase(TestCase):
|
|||||||
.format(n=zipline.sim.feed.pending_messages()))
|
.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)
|
@timed(DEFAULT_TIMEOUT)
|
||||||
def test_performance(self):
|
def test_performance(self):
|
||||||
#provide enough trades to ensure all orders are filled.
|
#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)
|
@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
|
# create a scenario where order size and trade size are equal
|
||||||
# so that orders must be spread out over several trades.
|
# so that orders must be spread out over several trades.
|
||||||
@@ -240,9 +275,25 @@ class FinanceTestCase(TestCase):
|
|||||||
|
|
||||||
self.transaction_sim(**params)
|
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
|
# create a scenario where order.amount <<< trade.volume
|
||||||
# to test that several orders can be covered properly by one trade.
|
# to test that several orders can be covered properly by one trade.
|
||||||
params2 ={
|
params1 ={
|
||||||
'trade_count':6,
|
'trade_count':6,
|
||||||
'trade_amount':100,
|
'trade_amount':100,
|
||||||
'trade_interval': timedelta(hours=1),
|
'trade_interval': timedelta(hours=1),
|
||||||
@@ -254,11 +305,26 @@ class FinanceTestCase(TestCase):
|
|||||||
'expected_txn_count':1,
|
'expected_txn_count':1,
|
||||||
'expected_txn_volume':24 * 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
|
# create a scenario where orders expire without being filled
|
||||||
# entirely
|
# entirely
|
||||||
params3 = {
|
params1 = {
|
||||||
'trade_count':100,
|
'trade_count':100,
|
||||||
'trade_amount':100,
|
'trade_amount':100,
|
||||||
'trade_delay': timedelta(minutes=5),
|
'trade_delay': timedelta(minutes=5),
|
||||||
@@ -271,7 +337,25 @@ class FinanceTestCase(TestCase):
|
|||||||
'expected_txn_count' : 1,
|
'expected_txn_count' : 1,
|
||||||
'expected_txn_volume' : 25
|
'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):
|
def transaction_sim(self, **params):
|
||||||
trade_count = params['trade_count']
|
trade_count = params['trade_count']
|
||||||
|
|||||||
@@ -22,8 +22,15 @@ class PerformanceTestCase(unittest.TestCase):
|
|||||||
0,
|
0,
|
||||||
len(self.treasury_curves)
|
len(self.treasury_curves)
|
||||||
)
|
)
|
||||||
self.dt = self.treasury_curves.keys()[random_index]
|
for n in range(100):
|
||||||
self.end_dt = self.dt + datetime.timedelta(days=365)
|
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.trading_environment = TradingEnvironment(
|
||||||
self.benchmark_returns,
|
self.benchmark_returns,
|
||||||
self.treasury_curves,
|
self.treasury_curves,
|
||||||
@@ -505,8 +512,6 @@ shares in position"
|
|||||||
price = 10.1
|
price = 10.1
|
||||||
price_list = [price] * trade_count
|
price_list = [price] * trade_count
|
||||||
volume = [100] * 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_time_increment = datetime.timedelta(days=1)
|
||||||
trade_history = factory.create_trade_history(
|
trade_history = factory.create_trade_history(
|
||||||
sid,
|
sid,
|
||||||
|
|||||||
Reference in New Issue
Block a user