mirror of
https://github.com/wassname/catalyst.git
synced 2026-07-02 16:46:13 +08:00
Pulled in PR 137 to add new order methods.
This commit is contained in:
+21
-7
@@ -21,7 +21,13 @@ from zipline.utils.test_utils import setup_logger
|
||||
import zipline.utils.factory as factory
|
||||
from zipline.test_algorithms import (TestRegisterTransformAlgorithm,
|
||||
RecordAlgorithm,
|
||||
TestOrderAlgorithm)
|
||||
TestOrderAlgorithm,
|
||||
TestOrderValueAlgorithm,
|
||||
TestTargetAlgorithm,
|
||||
TestOrderPercentAlgorithm,
|
||||
TestTargetPercentAlgorithm,
|
||||
TestTargetValueAlgorithm)
|
||||
|
||||
from zipline.sources import (SpecificEquityTrades,
|
||||
DataFrameSource,
|
||||
DataPanelSource)
|
||||
@@ -166,9 +172,17 @@ class TestTransformAlgorithm(TestCase):
|
||||
self.assertEqual(algo.data_frequency, 'minute')
|
||||
self.assertEqual(algo.annualizer, 10)
|
||||
|
||||
def test_orders_executed(self):
|
||||
algo = TestOrderAlgorithm(
|
||||
sim_params=self.sim_params,
|
||||
data_frequency='daily'
|
||||
)
|
||||
algo.run(self.df)
|
||||
def test_order_methods(self):
|
||||
AlgoClasses = [TestOrderAlgorithm,
|
||||
TestOrderValueAlgorithm,
|
||||
TestTargetAlgorithm,
|
||||
TestOrderPercentAlgorithm,
|
||||
TestTargetPercentAlgorithm,
|
||||
TestTargetValueAlgorithm]
|
||||
|
||||
for AlgoClass in AlgoClasses:
|
||||
algo = AlgoClass(
|
||||
sim_params=self.sim_params,
|
||||
data_frequency='daily'
|
||||
)
|
||||
algo.run(self.df)
|
||||
|
||||
@@ -231,7 +231,7 @@ class TestBatchTransform(TestCase):
|
||||
# consecutive (of window length) numbers up till the end.
|
||||
for i in range(algo.window_length, len(test_history)):
|
||||
np.testing.assert_array_equal(
|
||||
range(i - algo.window_length + 1, i + 1),
|
||||
range(i - algo.window_length + 2, i + 2d),
|
||||
test_history[i].values.flatten()
|
||||
)
|
||||
|
||||
|
||||
@@ -366,6 +366,12 @@ class TradingAlgorithm(object):
|
||||
def order(self, sid, amount, limit_price=None, stop_price=None):
|
||||
return self.blotter.order(sid, amount, limit_price, stop_price)
|
||||
|
||||
def order_value(self, sid, value, limit_price=None, stop_price=None):
|
||||
last_price = self.trading_client.current_data[sid].price
|
||||
return self.blotter.order_value(sid, value, last_price,
|
||||
limit_price=limit_price,
|
||||
stop_price=stop_price)
|
||||
|
||||
@property
|
||||
def recorded_vars(self):
|
||||
return copy(self._recorded_vars)
|
||||
@@ -430,3 +436,66 @@ class TradingAlgorithm(object):
|
||||
assert data_frequency in ('daily', 'minute')
|
||||
self.data_frequency = data_frequency
|
||||
self.annualizer = ANNUALIZER[self.data_frequency]
|
||||
|
||||
def order_percent(self, sid, percent, limit_price=None, stop_price=None):
|
||||
"""
|
||||
Place an order in the specified security corresponding to the given
|
||||
percent of the current portfolio value.
|
||||
|
||||
Note that percent must expressed as a decimal (0.50 means 50\%).
|
||||
"""
|
||||
value = self.portfolio.portfolio_value * percent
|
||||
return self.order_value(sid, value, limit_price, stop_price)
|
||||
|
||||
def target(self, sid, target, limit_price=None, stop_price=None):
|
||||
"""
|
||||
Place an order to adjust a position to a target number of shares. If
|
||||
the position doesn't already exist, this is equivalent to placing a new
|
||||
order. If the position does exist, this is equivalent to placing an
|
||||
order for the difference between the target number of shares and the
|
||||
current number of shares.
|
||||
"""
|
||||
if sid in self.portfolio.positions:
|
||||
current_position = self.portfolio.positions[sid].amount
|
||||
req_shares = target - current_position
|
||||
return self.order(sid, req_shares, limit_price, stop_price)
|
||||
else:
|
||||
return self.order(sid, target, limit_price, stop_price)
|
||||
|
||||
def target_value(self, sid, target, limit_price=None, stop_price=None):
|
||||
"""
|
||||
Place an order to adjust a position to a target value. If
|
||||
the position doesn't already exist, this is equivalent to placing a new
|
||||
order. If the position does exist, this is equivalent to placing an
|
||||
order for the difference between the target value and the
|
||||
current value.
|
||||
"""
|
||||
if sid in self.portfolio.positions:
|
||||
current_position = self.portfolio.positions[sid].amount
|
||||
current_price = self.portfolio.positions[sid].last_sale_price
|
||||
current_value = current_position * current_price
|
||||
req_value = target - current_value
|
||||
return self.order_value(sid, req_value, limit_price, stop_price)
|
||||
else:
|
||||
return self.order_value(sid, target, limit_price, stop_price)
|
||||
|
||||
def target_percent(self, sid, target, limit_price=None, stop_price=None):
|
||||
"""
|
||||
Place an order to adjust a position to a target percent of the
|
||||
current portfolio value. If the position doesn't already exist, this is
|
||||
equivalent to placing a new order. If the position does exist, this is
|
||||
equivalent to placing an order for the difference between the target
|
||||
percent and the current percent.
|
||||
|
||||
Note that target must expressed as a decimal (0.50 means 50\%).
|
||||
"""
|
||||
if sid in self.portfolio.positions:
|
||||
current_position = self.portfolio.positions[sid].amount
|
||||
current_price = self.portfolio.positions[sid].last_sale_price
|
||||
current_value = current_position * current_price
|
||||
else:
|
||||
current_value = 0
|
||||
target_value = self.portfolio.portfolio_value * target
|
||||
|
||||
req_value = target_value - current_value
|
||||
return self.order_value(sid, req_value, limit_price, stop_price)
|
||||
|
||||
@@ -18,6 +18,7 @@ import uuid
|
||||
from copy import copy
|
||||
from logbook import Logger
|
||||
from collections import defaultdict
|
||||
import numpy as np
|
||||
|
||||
import zipline.errors
|
||||
import zipline.protocol as zp
|
||||
@@ -93,7 +94,7 @@ class Blotter(object):
|
||||
|
||||
# just validates amount and passes rest on to TransactionSimulator
|
||||
# Tell the user if they try to buy 0 shares of something.
|
||||
if amount == 0:
|
||||
if int(amount) == 0:
|
||||
zero_message = "Requested to trade zero shares of {psid}".format(
|
||||
psid=sid
|
||||
)
|
||||
@@ -124,6 +125,31 @@ class Blotter(object):
|
||||
|
||||
return order.id
|
||||
|
||||
def order_value(self, sid, value, last_price,
|
||||
limit_price=None, stop_price=None):
|
||||
"""
|
||||
Place an order by desired value rather than desired number of shares.
|
||||
If the requested sid is found in the universe, the requested value is
|
||||
divided by its price to imply the number of shares to transact.
|
||||
|
||||
value > 0 :: Buy/Cover
|
||||
value < 0 :: Sell/Short
|
||||
Market order: order(sid, value)
|
||||
Limit order: order(sid, value, limit_price)
|
||||
Stop order: order(sid, value, None, stop_price)
|
||||
StopLimit order: order(sid, value, limit_price, stop_price)
|
||||
"""
|
||||
if np.allclose(last_price, 0):
|
||||
zero_message = "Price of 0 for {psid}; can't infer value".format(
|
||||
psid=sid
|
||||
)
|
||||
log.debug(zero_message)
|
||||
# Don't place any order
|
||||
return
|
||||
else:
|
||||
amount = value / last_price
|
||||
return self.order(sid, amount, limit_price, stop_price)
|
||||
|
||||
def cancel(self, order_id):
|
||||
if order_id not in self.orders:
|
||||
return
|
||||
|
||||
@@ -326,7 +326,7 @@ def create_test_df_source(sim_params=None, bars='daily'):
|
||||
if i >= market_open and i <= market_close:
|
||||
new_index.append(i)
|
||||
index = new_index
|
||||
x = np.arange(0, len(index))
|
||||
x = np.arange(1, len(index) + 1)
|
||||
|
||||
df = pd.DataFrame(x, index=index, columns=[0])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user