def test_volume_orders_filter__adjust_buy_stop_orders(self): """ Test if StopOrders are adjusted in a correct way. Suppose, the StopOrder quantity will be much bigger than the current MarketOrder, because the position for the contract already existed for some time, e.g. - there exists an open LONG position of size 100 - the position needs to be adjusted to 200 - a new MarketOrder of size 100 is created - a new StopOrder of size -200 is created - the volume percentage limit limits the MarketOrder to 15% of avg daily volume (set to volume_value = 100) - then the StopOrder needs to be adjusted to -115 """ volume_percentage_limit = 0.15 volume_value = 100.0 data_handler = self._setup_data_handler(volume_value) volume_orders_verifier = VolumeOrdersFilter( data_handler, self.contract_ticker_mapper, volume_percentage_limit) # Initialize a list of orders, which do not exceed the maximum volume limit contract = self.contract_ticker_mapper.ticker_to_contract(self.ticker) buy_order = [ Order(contract, 100, MarketOrder(), TimeInForce.GTC), Order(contract, -200, StopOrder(1.0), TimeInForce.GTC) ] new_orders = volume_orders_verifier.adjust_orders(buy_order) expected_buy_order = [ Order(contract, 15, MarketOrder(), TimeInForce.GTC), Order(contract, -115, StopOrder(1.0), TimeInForce.GTC) ] self.assertCountEqual(new_orders, expected_buy_order)
def test_volume_orders_filter__adjust_sell_stop_orders(self): """ Test if StopOrders are adjusted in a correct way. The MarketOrder corresponding to StopOrder is a sell order. """ volume_percentage_limit = 0.15 volume_value = 100.0 data_handler = self._setup_data_handler(volume_value) volume_orders_verifier = VolumeOrdersFilter( data_handler, self.contract_ticker_mapper, volume_percentage_limit) # Initialize a list of orders, which do not exceed the maximum volume limit contract = self.contract_ticker_mapper.ticker_to_contract(self.ticker) sell_order = [ Order(contract, -100, MarketOrder(), TimeInForce.GTC), Order(contract, 200, StopOrder(1.0), TimeInForce.GTC) ] new_orders = volume_orders_verifier.adjust_orders(sell_order) expected_sell_order = [ Order(contract, -15, MarketOrder(), TimeInForce.GTC), Order(contract, 115, StopOrder(1.0), TimeInForce.GTC) ] self.assertCountEqual(new_orders, expected_sell_order)
def test_order_target_percent(self): quantity = 40 execution_style = StopOrder(4.20) time_in_force = TimeInForce.GTC orders = self.order_factory.target_percent_orders({self.contract: 0.5}, execution_style, time_in_force) self.assertEqual(orders[0], Order(self.contract, quantity, execution_style, time_in_force))
def test_market_opens_at_much_higher_price_than_it_closed_at_yesterday( self): self.buy_stop_loss_order = Order(self.msft_contract, quantity=1, execution_style=StopOrder(120.0), time_in_force=TimeInForce.GTC) self.exec_hanlder.accept_orders([self.buy_stop_loss_order]) self._set_bar_for_today(open=120.0, high=130.0, low=68.0, close=90.0, volume=100000000.0) self.exec_hanlder.on_market_close(...) assert_lists_equal([], self.exec_hanlder.get_open_orders()) verify(self.spied_monitor, times=3).record_transaction(...) verify(self.portfolio, times=3).transact_transaction(...) self.assertEqual(3, len(self.monitor.transactions)) actual_transaction_3 = self.monitor.transactions[2] self.assertEqual(self.msft_contract, actual_transaction_3.contract) self.assertEqual(1, actual_transaction_3.quantity) self.assertEqual(120.0, actual_transaction_3.price) self.assertEqual(0.0, actual_transaction_3.commission)
def test_market_opens_at_much_higher_price_than_it_closed_at_yesterday( self): self.buy_stop_loss_order = Order(self.msft_contract, quantity=1, execution_style=StopOrder(120.0), time_in_force=TimeInForce.GTC) self.exec_handler.assign_order_ids([self.buy_stop_loss_order]) self._set_bar_for_today(open_price=120.0, high_price=130.0, low_price=68.0, close_price=90.0, volume=100000000.0) self._trigger_single_time_event() self.exec_handler.on_market_close(...) assert_lists_equal([], self.exec_handler.get_open_orders()) expected_transactions = [ Transaction(self.timer.now(), self.msft_contract, -1, self.stop_loss_order_1.execution_style.stop_price, 0), Transaction(self.timer.now(), self.msft_contract, -1, self.stop_loss_order_2.execution_style.stop_price, 0), Transaction(self.timer.now(), self.msft_contract, 1, 120, 0), ] self.monitor.record_transaction.assert_has_calls( call(t) for t in expected_transactions) self.portfolio.transact_transaction.assert_has_calls( call(t) for t in expected_transactions) self.assertEqual(self.monitor.record_transaction.call_count, 3) self.assertEqual(self.portfolio.transact_transaction.call_count, 3)
def test_initial_risk_position_sizer_with_cap(self): """ Max leverage will be limited by position sizer to 1.5 """ fraction_at_risk = 0.01 # will give leverage of 2, that will be capped to 1.5 signal = Signal(self.ticker, Exposure.LONG, fraction_at_risk, self.last_price, self.timer.now()) orders = self.initial_risk_position_sizer.size_signals([signal]) self.assertEqual(len(orders), 2) # market order and stop order portfolio_value = self.initial_position / self.initial_allocation max_leverage = self.initial_risk_position_sizer.max_target_percentage target_quantity = int(np.floor(portfolio_value * max_leverage)) additional_contracts = target_quantity - self.initial_position self.assertEqual( orders[0], Order(self.ticker, additional_contracts, MarketOrder(), TimeInForce.OPG)) stop_price = self.last_price * (1 - fraction_at_risk) stop_quantity = -(self.initial_position + additional_contracts) self.assertEqual( orders[1], Order(self.ticker, stop_quantity, StopOrder(stop_price), TimeInForce.GTC))
def test_order_target_value(self): execution_style = StopOrder(4.20) time_in_force = TimeInForce.GTC quantity = 4 orders = self.order_factory.target_value_orders({self.contract: 140.0}, execution_style, time_in_force) self.assertEqual(orders[0], Order(self.contract, quantity, execution_style, time_in_force))
def test_initial_risk_position_sizer_without_cap(self): """ Max leverage will not be limited by position sizer """ fraction_at_risk = 0.23 signal = Signal(self.ticker, Exposure.LONG, fraction_at_risk, self.last_price) orders = self.initial_risk_position_sizer.size_signals([signal]) self.assertEqual(len(orders), 2) # market order and stop order portfolio_value = self.initial_position / self.initial_allocation target_quantity = int( np.floor(portfolio_value * self.initial_risk / fraction_at_risk)) additional_contracts = target_quantity - self.initial_position self.assertEqual( orders[0], Order(self.contract, additional_contracts, MarketOrder(), TimeInForce.OPG)) stop_price = self.last_price * (1 - fraction_at_risk) stop_quantity = -(self.initial_position + additional_contracts) self.assertEqual( orders[1], Order(self.contract, stop_quantity, StopOrder(stop_price), TimeInForce.GTC))
def test_order_value(self): value = 100.0 quantity = floor(100.0 / self.share_price) # type: int execution_style = StopOrder(4.20) time_in_force = TimeInForce.DAY orders = self.order_factory.value_orders({self.contract: value}, execution_style, time_in_force) self.assertEqual(orders[0], Order(self.contract, quantity, execution_style, time_in_force))
def test_order_percent(self): percentage = 0.5 execution_style = StopOrder(4.20) time_in_force = TimeInForce.GTC quantity = floor(percentage * self.current_portfolio_value / self.share_price) # type: int orders = self.order_factory.percent_orders({self.contract: percentage}, execution_style, time_in_force) self.assertEqual(orders[0], Order(self.contract, quantity, execution_style, time_in_force))
def setUp(self): self.start_date = str_to_date("2018-02-04") self.msft_contract = Contract(self.MSFT_TICKER_STR, security_type='SEK', exchange='TEST') self.msft_ticker = BloombergTicker(self.MSFT_TICKER_STR) self.contracts_to_tickers_mapper = DummyBloombergContractTickerMapper() timer = SettableTimer(initial_time=self.start_date) self.data_handler = mock(strict=True) self.scheduler = mock() self.commission_model = FixedCommissionModel(commission=0.0) self.monitor = _MonitorMock() self.spied_monitor = spy(self.monitor) self.portfolio = mock() slippage_model = PriceBasedSlippage(0.0) self.exec_hanlder = SimulatedExecutionHandler( self.data_handler, timer, self.scheduler, self.spied_monitor, self.commission_model, self.contracts_to_tickers_mapper, self.portfolio, slippage_model) self._set_current_msft_price(100.0) self.stop_loss_order_1 = Order(self.msft_contract, quantity=-1, execution_style=StopOrder(95.0), time_in_force=TimeInForce.GTC) self.stop_loss_order_2 = Order(self.msft_contract, quantity=-1, execution_style=StopOrder(90.0), time_in_force=TimeInForce.GTC) self.stop_loss_order_3 = Order(self.msft_contract, quantity=-1, execution_style=StopOrder(50.0), time_in_force=TimeInForce.DAY) self.exec_hanlder.accept_orders([ self.stop_loss_order_1, self.stop_loss_order_2, self.stop_loss_order_3 ])
def test_order_target(self): quantity = -5 execution_style = StopOrder(4.20) time_in_force = TimeInForce.DAY orders = self.order_factory.target_orders({self.ticker: 5}, execution_style, time_in_force) self.assertEqual( orders[0], Order(self.ticker, quantity, execution_style, time_in_force))
def openOrder(self, orderId: OrderId, ib_contract: Contract, ib_order: IBOrder, orderState: OrderState): super().openOrder(orderId, ib_contract, ib_order, orderState) if ib_order.orderType.upper() == 'STP': execution_style = StopOrder(ib_order.auxPrice) elif ib_order.orderType.upper() == 'MKT': execution_style = MarketOrder() else: error_message = "Order Type is not supported: {}".format( ib_order.orderType) self.logger.error(error_message) raise ValueError(error_message) if ib_order.action.upper() == 'SELL': quantity = -ib_order.totalQuantity elif ib_order.action.upper() == 'BUY': quantity = ib_order.totalQuantity else: error_message = "Order Action is not supported: {}".format( ib_order.action) self.logger.error(error_message) raise ValueError(error_message) if ib_order.tif.upper() == 'DAY': time_in_force = TimeInForce.DAY elif ib_order.tif.upper() == 'GTC': time_in_force = TimeInForce.GTC elif ib_order.tif.upper() == 'OPG': time_in_force = TimeInForce.OPG else: error_message = "Time in Force is not supported: {}".format( ib_order.tif) self.logger.error(error_message) raise ValueError(error_message) try: ticker = self.contract_ticker_mapper.contract_to_ticker( IBContract.from_ib_contract(ib_contract)) order = Order(ticker=ticker, quantity=quantity, execution_style=execution_style, time_in_force=time_in_force, order_state=orderState.status) order.id = int(orderId) self.order_list.append(order) except ValueError as e: self.logger.error( f"Open Order for contract {ib_contract} will be skipped due to the following error " f"during parsing: \n{e}")
def calculate_signals(self): last_price = self.data_handler.get_last_available_price(self.ticker) orders = self.order_factory.target_percent_orders({self.ticker: 1.0}, MarketOrder(), time_in_force=TimeInForce.OPG, tolerance_percentage=0.02) stop_price = last_price * (1 - self.percentage) execution_style = StopOrder(stop_price=stop_price) stop_order = self.order_factory.percent_orders({self.ticker: -1}, execution_style=execution_style, time_in_force=TimeInForce.DAY) self.broker.cancel_all_open_orders() self.broker.place_orders(orders) self.broker.place_orders(stop_order)
def openOrder(self, orderId: OrderId, ib_contract: IBContract, ib_order: IBOrder, orderState: OrderState): contract = Contract(ib_contract.symbol, ib_contract.secType, ib_contract.exchange) if ib_order.orderType.upper() == 'STP': execution_style = StopOrder(ib_order.auxPrice) elif ib_order.orderType.upper() == 'MKT': execution_style = MarketOrder() else: error_message = "Order Type is not supported: {}".format( ib_order.orderType) self.logger.error(error_message) raise ValueError(error_message) if ib_order.action.upper() == 'SELL': quantity = -ib_order.totalQuantity elif ib_order.action.upper() == 'BUY': quantity = ib_order.totalQuantity else: error_message = "Order Action is not supported: {}".format( ib_order.action) self.logger.error(error_message) raise ValueError(error_message) if ib_order.tif.upper() == 'DAY': time_in_force = TimeInForce.DAY elif ib_order.tif.upper() == 'GTC': time_in_force = TimeInForce.GTC elif ib_order.tif.upper() == 'OPG': time_in_force = TimeInForce.OPG else: error_message = "Time in Force is not supported: {}".format( ib_order.tif) self.logger.error(error_message) raise ValueError(error_message) order = Order(contract=contract, quantity=quantity, execution_style=execution_style, time_in_force=time_in_force, order_state=orderState.status) order.id = int(orderId) self.order_list.append(order)
def test_simple_position_sizer(self): fraction_at_risk = 0.02 signal = Signal(self.ticker, Exposure.LONG, fraction_at_risk) orders = self.simple_position_sizer.size_signals([signal]) quantity = np.floor(self.initial_position * (1 / self.initial_allocation - 1)) self.assertEqual(len(orders), 2) # market order and stop order self.assertEqual( orders[0], Order(self.contract, quantity, MarketOrder(), TimeInForce.OPG)) stop_price = self.last_price * (1 - fraction_at_risk) stop_quantity = -(self.initial_position + quantity) self.assertEqual( orders[1], Order(self.contract, stop_quantity, StopOrder(stop_price), TimeInForce.GTC))
def _generate_stop_order(self, contract, signal, market_order: Order): # stop_quantity = existing position size + recent market orders quantity stop_quantity = self._get_existing_position_quantity(contract) if market_order is not None: stop_quantity += market_order.quantity if stop_quantity != 0: stop_price = self._calculate_stop_price(signal) assert is_finite_number( stop_price), "Stop price should be a finite number" # put minus before the quantity as stop order has to go in the opposite direction stop_orders = self._order_factory.orders( {contract: -stop_quantity}, StopOrder(stop_price), TimeInForce.GTC) assert len(stop_orders) == 1, "Only one order should be generated" return stop_orders[0] else: # quantity is 0 - no need to place a stop order return None
def _generate_stop_order( self, signal, contract_to_market_order: Dict[Contract, Order]) -> Optional[Order]: """ As each of the stop orders relies on the precomputed stop_price, which considers a.o. last available price of the security, orders are being created separately for each of the signals. """ contract = self._contract_ticker_mapper.ticker_to_contract( signal.ticker) # stop_quantity = existing position size + recent market orders quantity stop_quantity = self._get_existing_position_quantity(contract) try: market_order = contract_to_market_order[contract] stop_quantity += market_order.quantity except KeyError: # Generated Market Order was equal to None pass if stop_quantity != 0: stop_price = self._calculate_stop_price(signal) if not is_finite_number(stop_price): self.logger.info("Stop price should be a finite number") return None stop_price = self._cap_stop_price(stop_price, signal) # put minus before the quantity as stop order has to go in the opposite direction stop_orders = self._order_factory.orders( {contract: -stop_quantity}, StopOrder(stop_price), TimeInForce.GTC) assert len(stop_orders) == 1, "Only one order should be generated" return stop_orders[0] else: # quantity is 0 - no need to place a stop order return None
def setUp(self): MarketOpenEvent.set_trigger_time({ "hour": 13, "minute": 30, "second": 0, "microsecond": 0 }) MarketCloseEvent.set_trigger_time({ "hour": 20, "minute": 0, "second": 0, "microsecond": 0 }) self.start_date = str_to_date("2018-02-04") self.number_of_minutes = 5 before_close = self.start_date + MarketCloseEvent.trigger_time( ) - RelativeDelta(minutes=self.number_of_minutes) self.msft_contract = Contract(self.MSFT_TICKER_STR, security_type='STK', exchange='TEST') self.msft_ticker = BloombergTicker(self.MSFT_TICKER_STR) contracts_to_tickers_mapper = SimulatedBloombergContractTickerMapper() self.timer = SettableTimer(initial_time=before_close) self.data_handler = Mock(spec=DataHandler) scheduler = Mock(spec=Scheduler) ScheduleOrderExecutionEvent.clear() # Set the periodic bar events to intraday trading IntradayBarEvent.frequency = Frequency.MIN_1 commission_model = FixedCommissionModel(commission=0.0) self.monitor = Mock(spec=AbstractMonitor) self.portfolio = Mock(spec=Portfolio) slippage_model = PriceBasedSlippage(0.0, self.data_handler, contracts_to_tickers_mapper) self.exec_handler = SimulatedExecutionHandler( self.data_handler, self.timer, scheduler, self.monitor, commission_model, contracts_to_tickers_mapper, self.portfolio, slippage_model, RelativeDelta(minutes=self.number_of_minutes)) self._set_last_available_price(100.0) self.stop_loss_order_1 = Order(self.msft_contract, quantity=-1, execution_style=StopOrder(95.0), time_in_force=TimeInForce.GTC) self.stop_loss_order_2 = Order(self.msft_contract, quantity=-1, execution_style=StopOrder(90.0), time_in_force=TimeInForce.GTC) self.stop_loss_order_3 = Order(self.msft_contract, quantity=-1, execution_style=StopOrder(50.0), time_in_force=TimeInForce.DAY) self.exec_handler.assign_order_ids([ self.stop_loss_order_1, self.stop_loss_order_2, self.stop_loss_order_3 ])