mirror of
https://github.com/wassname/catalyst.git
synced 2026-07-04 06:08:26 +08:00
Merge fix for stop orders.
Pull in patch that fixes the stop orders so they behave correctly with regard to the price being greater or less than the stop for both buys and sells. Also, update unit test and add a refactoring on top of the fix to make each sell/buy, stop and limit combination more clear.
This commit is contained in:
@@ -238,7 +238,12 @@ class SlippageTestCase(TestCase):
|
||||
'open': 3.5
|
||||
},
|
||||
'expected': {
|
||||
'transaction': None
|
||||
'transaction': {
|
||||
'price': 4.001,
|
||||
'dt': pd.Timestamp('2006-01-05 14:31', tz='UTC'),
|
||||
'amount': 100,
|
||||
'sid': 133,
|
||||
}
|
||||
}
|
||||
},
|
||||
'long | price lt stop': {
|
||||
@@ -260,13 +265,8 @@ class SlippageTestCase(TestCase):
|
||||
'open': 4.0
|
||||
},
|
||||
'expected': {
|
||||
'transaction': {
|
||||
'price': 3.500875,
|
||||
'dt': pd.Timestamp('2006-01-05 14:31', tz='UTC'),
|
||||
'amount': 100,
|
||||
'sid': 133,
|
||||
}
|
||||
},
|
||||
'transaction': None
|
||||
}
|
||||
},
|
||||
'short | price gt stop': {
|
||||
'order': {
|
||||
@@ -287,12 +287,7 @@ class SlippageTestCase(TestCase):
|
||||
'open': 3.0
|
||||
},
|
||||
'expected': {
|
||||
'transaction': {
|
||||
'price': 3.499125,
|
||||
'dt': pd.Timestamp('2006-01-05 14:31', tz='UTC'),
|
||||
'amount': -100,
|
||||
'sid': 133,
|
||||
}
|
||||
'transaction': None
|
||||
}
|
||||
},
|
||||
'short | price lt stop': {
|
||||
@@ -314,7 +309,12 @@ class SlippageTestCase(TestCase):
|
||||
'open': 3.0
|
||||
},
|
||||
'expected': {
|
||||
'transaction': None
|
||||
'transaction': {
|
||||
'price': 2.99925,
|
||||
'dt': pd.Timestamp('2006-01-05 14:31', tz='UTC'),
|
||||
'amount': -100,
|
||||
'sid': 133,
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -282,13 +282,16 @@ class Order(object):
|
||||
Update internal state based on price triggers and the
|
||||
trade event's price.
|
||||
"""
|
||||
stop_reached, limit_reached = \
|
||||
stop_reached, limit_reached, sl_stop_reached = \
|
||||
check_order_triggers(self, event)
|
||||
if (stop_reached, limit_reached) \
|
||||
!= (self.stop_reached, self.limit_reached):
|
||||
self.dt = event.dt
|
||||
self.stop_reached = stop_reached
|
||||
self.limit_reached = limit_reached
|
||||
if sl_stop_reached:
|
||||
# Change the STOP LIMIT order into a LIMIT order
|
||||
self.stop = None
|
||||
|
||||
def handle_split(self, split_event):
|
||||
ratio = split_event.ratio
|
||||
@@ -329,7 +332,6 @@ class Order(object):
|
||||
For a market order, True.
|
||||
For a stop order, True IFF stop_reached.
|
||||
For a limit order, True IFF limit_reached.
|
||||
For a stop-limit order, True IFF (stop_reached AND limit_reached)
|
||||
"""
|
||||
if self.stop and not self.stop_reached:
|
||||
return False
|
||||
|
||||
+44
-12
@@ -23,6 +23,11 @@ from functools import partial
|
||||
from zipline.protocol import DATASOURCE_TYPE
|
||||
import zipline.utils.math_utils as zp_math
|
||||
|
||||
SELL = 0
|
||||
BUY = 1
|
||||
STOP = 1 << 1
|
||||
LIMIT = 1 << 2
|
||||
|
||||
|
||||
def check_order_triggers(order, event):
|
||||
"""
|
||||
@@ -31,30 +36,57 @@ def check_order_triggers(order, event):
|
||||
For market orders, will return (False, False).
|
||||
For stop orders, limit_reached will always be False.
|
||||
For limit orders, stop_reached will always be False.
|
||||
For stop limit orders a Boolean is returned to flag
|
||||
that the stop has been reached.
|
||||
|
||||
Orders that have been triggered already (price targets reached),
|
||||
the order's current values are returned.
|
||||
"""
|
||||
if order.triggered:
|
||||
return (order.stop_reached, order.limit_reached)
|
||||
return (order.stop_reached, order.limit_reached, False)
|
||||
|
||||
stop_reached = False
|
||||
limit_reached = False
|
||||
# if the stop price is reached, simply set stop_reached
|
||||
if order.stop is not None:
|
||||
if (order.direction * (event.price - order.stop) <= 0):
|
||||
# convert stop -> limit or market
|
||||
stop_reached = True
|
||||
sl_stop_reached = False
|
||||
|
||||
order_type = 0
|
||||
|
||||
if order.amount > 0:
|
||||
order_type |= BUY
|
||||
else:
|
||||
order_type |= SELL
|
||||
|
||||
if order.stop is not None:
|
||||
order_type |= STOP
|
||||
|
||||
# if the limit price is reached, we execute this order at
|
||||
# (event.price + simulated_impact)
|
||||
# we skip this order with a continue when the limit is not reached
|
||||
if order.limit is not None:
|
||||
# if limit conditions not met, then continue
|
||||
if (order.direction * (event.price - order.limit) <= 0):
|
||||
order_type |= LIMIT
|
||||
|
||||
if order_type == BUY | STOP | LIMIT:
|
||||
if event.price >= order.stop:
|
||||
sl_stop_reached = True
|
||||
if event.price <= order.limit:
|
||||
limit_reached = True
|
||||
elif order_type == SELL | STOP | LIMIT:
|
||||
if event.price <= order.stop:
|
||||
sl_stop_reached = True
|
||||
if event.price >= order.limit:
|
||||
limit_reached = True
|
||||
elif order_type == BUY | STOP:
|
||||
if event.price >= order.stop:
|
||||
stop_reached = True
|
||||
elif order_type == SELL | STOP:
|
||||
if event.price <= order.stop:
|
||||
stop_reached = True
|
||||
elif order_type == BUY | LIMIT:
|
||||
if event.price <= order.limit:
|
||||
limit_reached = True
|
||||
elif order_type == SELL | LIMIT:
|
||||
# This is a SELL LIMIT order
|
||||
if event.price >= order.limit:
|
||||
limit_reached = True
|
||||
|
||||
return (stop_reached, limit_reached)
|
||||
return (stop_reached, limit_reached, sl_stop_reached)
|
||||
|
||||
|
||||
def transact_stub(slippage, commission, event, open_orders):
|
||||
|
||||
Reference in New Issue
Block a user