Merge pull request #220 from quantopian/minimum_price_variation

Merging branch "Minimum price variation"

Ensure limit prices conform to a minimum price variation of a penny, which works for most stocks (not, for instance, BRK). The rounding "midpoint" is custom and depends on buy/sell direction, instead of .5 of a penny.
This commit is contained in:
Richard Frank
2013-09-23 13:45:05 -07:00
2 changed files with 49 additions and 0 deletions
+35
View File
@@ -0,0 +1,35 @@
import math
from nose_parameterized import parameterized
from unittest import TestCase
from zipline.finance.blotter import round_for_minimum_price_variation
class BlotterTestCase(TestCase):
@parameterized.expand([(0.00, 0.00),
(0.01, 0.01),
(0.0005, 0.00),
(1.006, 1.00),
(1.0095, 1.01),
(1.00949, 1.00),
(1.0005, 1.00)])
def test_round_for_minimum_price_variation_buy(self, price, expected):
result = round_for_minimum_price_variation(price, is_buy=True)
self.assertEqual(result, expected)
self.assertEqual(math.copysign(1.0, result),
math.copysign(1.0, expected))
@parameterized.expand([(0.00, 0.00),
(0.01, 0.01),
(0.0005, 0.00),
(1.006, 1.01),
(1.0005, 1.00),
(1.00051, 1.01),
(1.0095, 1.01)])
def test_round_for_minimum_price_variation_sell(self, price, expected):
result = round_for_minimum_price_variation(price, is_buy=False)
self.assertEqual(result, expected)
self.assertEqual(math.copysign(1.0, result),
math.copysign(1.0, expected))
+14
View File
@@ -42,6 +42,17 @@ ORDER_STATUS = Enum(
)
# On an order to buy, between .05 below to .95 above a penny, use that penny.
# On an order to sell, between .05 above to .95 below a penny, use that penny.
# buy: [.0095, .0195) -> round to .01, sell: (.0005, .0105] -> round to .01
def round_for_minimum_price_variation(x, is_buy, diff=(0.0095 - .005)):
# relies on rounding half away from zero, unlike numpy's bankers' rounding
rounded = round(x - (diff if is_buy else -diff), 2)
if zp_math.tolerant_equals(rounded, 0.0):
return 0.0
return rounded
class Blotter(object):
def __init__(self):
@@ -107,6 +118,9 @@ class Blotter(object):
raise OverflowError("Can't order more than %d shares" %
self.max_shares)
if limit_price:
limit_price = round_for_minimum_price_variation(limit_price,
amount > 0)
order = Order(
dt=self.current_dt,
sid=sid,