BUG: Apply integer truncation to order amounts earlier in the pipeline.

Truncate non-integer order amounts in `TradingAlgorithm.order` instead of
`Blotter.order`.  This fixes an issue where non-integer orders coming out of
order_value can spuriously trigger a `LongOnly` trading guard.

Example:

sid.price == 2.0
order_value(sid, 5) -> order(sid, 2.5) -> truncated to order(sid, 2.0)
order_value(sid, -5) -> order(sid, -2.5) -> LongOnlyViolation b/c 2.0 - 2.5 < 0
This commit is contained in:
Scott Sanderson
2014-06-09 11:33:51 -04:00
parent 2a73873097
commit 49eaeeb6ae
2 changed files with 16 additions and 12 deletions
+16
View File
@@ -499,6 +499,22 @@ class TradingAlgorithm(object):
"""
Place an order using the specified parameters.
"""
def round_if_near_integer(a, epsilon=1e-4):
"""
Round a to the nearest integer if that integer is within an epsilon
of a.
"""
if abs(a - round(a)) <= epsilon:
return round(a)
else:
return a
# Truncate to the integer share count that's either within .0001 of
# amount or closer to zero.
# E.g. 3.9999 -> 4.0; 5.5 -> 5.0; -5.5 -> -5.0
amount = int(round_if_near_integer(amount))
# Raises a ZiplineError if invalid parameters are detected.
self.validate_order_params(sid,
amount,
-12
View File
@@ -88,18 +88,6 @@ class Blotter(object):
Stop order: order(sid, amount, StopOrder(price))
StopLimit order: order(sid, amount, StopLimitOrder(price))
"""
# This fixes a bug that if amount is e.g. -27.99999 due to
# floating point madness we actually want to treat it as -28.
def almost_equal_to(a, eps=1e-4):
if abs(a - round(a)) <= eps:
return round(a)
else:
return a
# Fractional shares are not supported.
amount = int(almost_equal_to(amount))
# just validates amount and passes rest on to TransactionSimulator
if amount == 0:
# Don't bother placing orders for 0 shares.
return