mirror of
https://github.com/wassname/catalyst.git
synced 2026-09-09 11:19:23 +08:00
rounded out tests to cover more trading cases (spreading, collapsing, expiring)
This commit is contained in:
@@ -308,8 +308,7 @@ class TransactionSimulator(qmsg.BaseTransform):
|
||||
orders = sorted(orders, key=lambda o: o.dt)
|
||||
else:
|
||||
return None
|
||||
|
||||
total_order = 0
|
||||
|
||||
dt = event.dt
|
||||
expired = []
|
||||
total_order = 0
|
||||
@@ -318,7 +317,7 @@ class TransactionSimulator(qmsg.BaseTransform):
|
||||
direction = 1.0
|
||||
for order in orders:
|
||||
|
||||
if(order.dt <= event.dt):
|
||||
if(order.dt < event.dt):
|
||||
|
||||
# orders are only good on the day they are issued
|
||||
if order.dt.day < event.dt.day:
|
||||
@@ -336,7 +335,7 @@ class TransactionSimulator(qmsg.BaseTransform):
|
||||
volume_share = direction * (desired_order) / event.volume
|
||||
if volume_share > .25:
|
||||
volume_share = .25
|
||||
simulated_amount = volume_share * event.volume * direction
|
||||
simulated_amount = int(volume_share * event.volume * direction)
|
||||
simulated_impact = (volume_share)**2 * .1 * direction * event.price
|
||||
|
||||
order.filled += (simulated_amount - total_order)
|
||||
@@ -347,12 +346,20 @@ class TransactionSimulator(qmsg.BaseTransform):
|
||||
break
|
||||
|
||||
if simulated_amount == 0:
|
||||
warning = "Calculated a zero volume transation on trade: {event}"
|
||||
warning = warning.format(event=str(event))
|
||||
warning = """
|
||||
Calculated a zero volume transation on trade:
|
||||
{event}
|
||||
for order:
|
||||
{order}
|
||||
"""
|
||||
warning = warning.format(
|
||||
event=str(event),
|
||||
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]
|
||||
|
||||
self.open_orders[event.sid] = orders
|
||||
|
||||
|
||||
@@ -477,7 +484,15 @@ class TradingEnvironment(object):
|
||||
|
||||
return len(self.period_trading_days)
|
||||
|
||||
|
||||
def is_market_hours(self, test_date):
|
||||
if not self.is_trading_day(test_date):
|
||||
return False
|
||||
|
||||
mkt_open = self.set_NYSE_time(test_date, 9, 30)
|
||||
#TODO: half days?
|
||||
mkt_close = self.set_NYSE_time(test_date, 16, 00)
|
||||
|
||||
return test_date >= mkt_open and test_date <= mkt_close
|
||||
|
||||
def is_trading_day(self, test_date):
|
||||
dt = self.normalize_date(test_date)
|
||||
|
||||
@@ -68,7 +68,7 @@ def get_next_trading_dt(current, interval, trading_calendar):
|
||||
next = current
|
||||
while True:
|
||||
next = next + interval
|
||||
if trading_calendar.is_trading_day(next):
|
||||
if trading_calendar.is_market_hours(next):
|
||||
break
|
||||
|
||||
return next
|
||||
@@ -79,9 +79,9 @@ def create_trade_history(sid, prices, amounts, interval, trading_calendar):
|
||||
|
||||
for price, amount in zip(prices, amounts):
|
||||
|
||||
current = get_next_trading_dt(current, interval, trading_calendar)
|
||||
trade = create_trade(sid, price, amount, current)
|
||||
trades.append(trade)
|
||||
current = get_next_trading_dt(current, interval, trading_calendar)
|
||||
|
||||
assert len(trades) == len(prices)
|
||||
return trades
|
||||
|
||||
@@ -218,51 +218,105 @@ class FinanceTestCase(TestCase):
|
||||
)
|
||||
|
||||
|
||||
# TODO: write a test that proves orders expire without being filled.
|
||||
@timed(DEFAULT_TIMEOUT)
|
||||
def test_transaction_sim(self):
|
||||
|
||||
trade_count = 40
|
||||
# create a scenario where order size and trade size are equal
|
||||
# so that orders must be spread out over several trades.
|
||||
params ={
|
||||
'trade_count':360,
|
||||
'trade_amount':100,
|
||||
'trade_interval': timedelta(minutes=1),
|
||||
'order_count':2,
|
||||
'order_amount':100,
|
||||
'order_interval': timedelta(minutes=1),
|
||||
# because we placed an order for 100 shares, and the volume
|
||||
# of each trade is 100, the simulator should spread the order
|
||||
# into 4 trades of 25 shares per order.
|
||||
'expected_txn_count':8,
|
||||
'expected_txn_volume':2 * 100
|
||||
}
|
||||
|
||||
self.transaction_sim(**params)
|
||||
|
||||
# create a scenario where order.amount <<< trade.volume
|
||||
# to test that several orders can be covered properly by one trade.
|
||||
params2 ={
|
||||
'trade_count':6,
|
||||
'trade_amount':100,
|
||||
'trade_interval': timedelta(hours=1),
|
||||
'order_count':24,
|
||||
'order_amount':1,
|
||||
'order_interval': timedelta(minutes=1),
|
||||
# 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':24 * 1
|
||||
}
|
||||
self.transaction_sim(**params2)
|
||||
|
||||
# create a scenario where orders expire without being filled
|
||||
# entirely
|
||||
params3 = {
|
||||
'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(**params3)
|
||||
|
||||
def transaction_sim(self, **params):
|
||||
trade_count = params['trade_count']
|
||||
trade_amount = params['trade_amount']
|
||||
trade_interval = params['trade_interval']
|
||||
trade_delay = params.get('trade_delay')
|
||||
order_count = params['order_count']
|
||||
order_amount = params['order_amount']
|
||||
order_interval = params['order_interval']
|
||||
expected_txn_count = params['expected_txn_count']
|
||||
expected_txn_volume = params['expected_txn_volume']
|
||||
|
||||
trading_environment = factory.create_trading_environment()
|
||||
trade_sim = TransactionSimulator()
|
||||
price = [10.1] * trade_count
|
||||
volume = [100] * trade_count
|
||||
start_date = trading_environment.first_open
|
||||
one_day = timedelta(days=1)
|
||||
one_hour = timedelta(hours=1)
|
||||
sid = 1
|
||||
|
||||
generated_trades = factory.create_trade_history(
|
||||
sid,
|
||||
price,
|
||||
volume,
|
||||
one_hour,
|
||||
trade_interval,
|
||||
trading_environment
|
||||
)
|
||||
|
||||
trade_1 = generated_trades.pop()
|
||||
trade_sim.transform(trade_1)
|
||||
|
||||
order_amount = 100
|
||||
order_count = 2
|
||||
|
||||
for i in range(order_count):
|
||||
order = namedict(
|
||||
{
|
||||
'sid':sid,
|
||||
'amount':order_amount,
|
||||
'type':zp.DATASOURCE_TYPE.ORDER,
|
||||
'dt' : start_date + i * one_day
|
||||
'dt' : start_date + i * order_interval
|
||||
})
|
||||
|
||||
sim_state = trade_sim.transform(order)
|
||||
|
||||
# there should not be a new transaction from an order.
|
||||
self.assertTrue(sim_state['name'] == trade_sim.get_id)
|
||||
self.assertTrue(sim_state['value'] == None)
|
||||
# there should not be a new transaction from an order.
|
||||
self.assertTrue(sim_state['name'] == trade_sim.get_id)
|
||||
self.assertTrue(sim_state['value'] == None)
|
||||
|
||||
# there should now be one open order in the sid
|
||||
|
||||
# there should now be one open order list stored under the sid
|
||||
oo = trade_sim.open_orders
|
||||
self.assertEqual(len(oo), 1)
|
||||
self.assertTrue(oo.has_key(sid))
|
||||
order_list = oo[sid]
|
||||
self.assertEqual(order_count, len(order_list))
|
||||
@@ -273,31 +327,29 @@ class FinanceTestCase(TestCase):
|
||||
|
||||
transactions = []
|
||||
for trade in generated_trades:
|
||||
if trade_delay:
|
||||
trade.dt = trade.dt + trade_delay
|
||||
|
||||
sim_state = trade_sim.transform(trade)
|
||||
|
||||
self.assertEqual(sim_state['name'], trade_sim.get_id)
|
||||
|
||||
|
||||
if sim_state['value']:
|
||||
transactions.append(sim_state['value'])
|
||||
transactions.append(sim_state['value'])
|
||||
|
||||
|
||||
if len(trade_sim.open_orders[sid]) == 0:
|
||||
break
|
||||
|
||||
total_volume = 0
|
||||
for txn in transactions:
|
||||
total_volume += txn.amount
|
||||
|
||||
self.assertEqual(total_volume, order_count * order_amount)
|
||||
|
||||
# because we placed an order for 100 shares, and the volume
|
||||
# of each trade is 100, the simulator should spread the order
|
||||
# into 4 trades of 25 shares per order.
|
||||
self.assertEqual(len(transactions), 4 * order_count)
|
||||
self.assertEqual(total_volume, expected_txn_volume)
|
||||
self.assertEqual(len(transactions), expected_txn_count)
|
||||
|
||||
# the open orders should now be empty
|
||||
oo = trade_sim.open_orders
|
||||
self.assertTrue(oo.has_key(sid))
|
||||
order_list = oo[sid]
|
||||
self.assertEqual(0, len(order_list))
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user