def test_rejected_callback_will_be_called(self):
        order = Order(order_type='Limit', side='Sell', qty=228, price=1000)
        order.order_id = 123456

        callback = Mock()
        order._on_reject = callback
        self.supervisor.add_order(order)

        self.exchange_mock.get_order_status_ws.return_value = 'Rejected'

        self.supervisor.run_cycle()

        sleep(1)

        callback.assert_called_once()
        self.assertListEqual([], self.supervisor._orders)
Example #2
0
    def enter_fb_method(self, qty: int, price_type: str, timeout: int, max_retry: int, deviation: int = None) -> None:
        """
        Fb method is placing n orders after timeout seconds, then entry market.

        :param qty: entry position size
        :param timeout: timeout before replacing the order
        :param price_type: may be last, first_ob, third_ob and deviant
        :param deviation: deviation from last price in percents
        :param max_retry: max order placing count
        """

        if price_type == 'first_ob':
            init_price = self.exchange.get_first_orderbook_price_ws(bid=qty > 0)
        elif price_type == 'third_ob':
            init_price = self.exchange.get_third_orderbook_price_ws(bid=qty > 0)
        elif price_type == 'deviant':
            if qty > 0:
                init_price = self.exchange.get_last_price_ws() * (100 + deviation) / 100
            else:
                init_price = self.exchange.get_last_price_ws() * (100 - deviation) / 100
        else:
            init_price = self.exchange.get_last_price_ws()

        init_price = to_nearest(init_price, self.exchange.conn.get_tick_size())

        entry_order = Order(order_type='Limit', qty=qty, side='Buy' if qty > 0 else 'Sell',
                            price=init_price, passive=False)
        for _ in range(max_retry):
            self.exchange.place_order(entry_order)
            for _ in range(timeout):
                if self.exchange.get_order_status_ws(entry_order) == 'Filled':
                    return
                sleep(1)
            self.exchange.cancel_order(entry_order)
        self.enter_by_market_order(qty=qty)
    def test_fb_negative_deviant_position_enter_while_running_cycle(self):
        self.exchange_mock.get_last_price_ws.return_value = 1000
        self.exchange_mock.get_order_status_ws.return_value = 'Filled'
        self.exchange_mock.conn.get_tick_size.return_value = 0.5

        self.supervisor.run_cycle()

        self.supervisor.stop_cycle()
        self.supervisor.enter_fb_method(qty=20,
                                        price_type='deviant',
                                        timeout=5,
                                        max_retry=3,
                                        deviation=-15)
        self.exchange_mock.get_position_size_ws.return_value = 20
        self.supervisor.run_cycle()

        sleep(1)

        order = Order(order_type='Limit',
                      qty=20,
                      price=850,
                      side='Buy',
                      passive=True)
        # assert that Supervisor change position only once with limit order
        self.exchange_mock.place_order.assert_called_once_with(order)
Example #4
0
    def test_move_order(self):
        order1 = Order(order_type='Limit', price=1000, qty=228, side='Sell')
        order1.order_id = 1234

        with responses.RequestsMock() as rsps:
            rsps.add(responses.PUT, settings.BASE_URL + '/order', json={})
            expected_order = {
                'orderID': 1234,
                'orderQty': 228,
                'price': 1001.0,
            }

            self.exchange.move_order(order1, to=1001)
            self.assertEqual(1, len(rsps.calls))
            self.assertEqual(json.dumps(expected_order),
                             rsps.calls[0].request.body)
    def test_stop_tracking_after_fill_order(self):
        order = Order(order_type='Stop', side='Sell', qty=228, stop_px=1000)
        self.exchange_mock.get_open_orders_ws.return_value = [order]
        self.exchange_mock.get_last_price_ws.return_value = 9000

        self.supervisor.add_trailing_order(order, 10)

        self.supervisor.run_cycle()

        order.order_id = 1234
        self.exchange_mock.get_open_orders_ws.return_value = []
        self.exchange_mock.get_order_status_ws.return_value = 'Filled'

        sleep(0.5)

        self.assertTrue(order.tracker.exited)
    def test_export_order_to_api_dict_no_empty(self):
        """Do not include empty parameters to result dict."""

        order = Order(order_type='Limit',
                      qty=228,
                      side='Buy',
                      price=Decimal(1000))
        expected_order_dict = {
            'symbol': 'XBTUSD',
            'ordType': 'Limit',
            'orderQty': 228,
            'side': 'Buy',
            'price': 1000.0,
        }
        api_order_dict = order.as_dict(include_empty=False)
        self.assertEqual(expected_order_dict, api_order_dict)
Example #7
0
 def add_order(self, order: Order) -> None:
     if order.is_valid():
         self._orders.append(order)
         self.logger.info(f'New order: {order.order_type} {order.side} {order.qty} by '
                          f'{order.price or order.stop_px}')
     else:
         raise ValueError('Order is not valid.')
    def test_move_changed_order(self):
        """Test that Supervisor will amend order, not cancel it."""

        order1 = Order(order_type='Limit', qty=228, price=1000, side='Buy')
        order1_copy = Order(order_type='Limit',
                            qty=228,
                            price=1000,
                            side='Buy')
        self.supervisor.add_order(order1)

        self.exchange_mock.get_open_orders_ws.return_value = [order1_copy]
        order1.move(to=1001)

        self.supervisor.cancel_needless_orders()

        self.exchange_mock.bulk_cancel_orders.assert_not_called()
        self.exchange_mock.move_order.assert_called_once_with(order=order1)
    def test_cancel_needless_order(self):
        order1 = Order()
        self.exchange_mock.get_open_orders_ws.return_value = [order1]
        expected_orders = [order1]
        self.supervisor.cancel_needless_orders()

        # assert that Supervisor try to cancel needless order
        self.exchange_mock.bulk_cancel_orders.assert_called_once_with(expected_orders)
    def add_trailing_order(self, order: Order, offset: int) -> None:
        """

        :param order: Order instance
        :param offset: value in percents, distance from extremum
        """

        if order.is_valid():
            order.is_trailing = True
            tick_size = self.exchange.conn.get_tick_size()
            test = 'testnet' in self.exchange.conn.base_url
            order.tracker = TrailingShell(order=order,
                                          offset=offset,
                                          tick_size=tick_size,
                                          test=test)
            order.tracker.start_trailing(
                initial_price=self.exchange.get_last_price_ws())
            self.orders.append(order)
Example #11
0
 def setUp(self) -> None:
     self.order = Order(order_type='Stop',
                        qty=228,
                        stop_px=900,
                        side='Sell')
     self.trailing_order = TrailingShell(order=self.order,
                                         offset=10,
                                         tick_size=0.5,
                                         test=True,
                                         init_ws=False)
Example #12
0
 def test_same_orders_equality(self):
     order1 = Order(order_type='Limit',
                    qty=228,
                    side='Buy',
                    price=Decimal(1000),
                    stop_px=None,
                    hidden=True,
                    close=False,
                    reduce_only=False,
                    passive=True)
     order2 = Order(order_type='Limit',
                    qty=228,
                    side='Buy',
                    price=Decimal(1000),
                    stop_px=None,
                    hidden=True,
                    close=False,
                    reduce_only=False,
                    passive=True)
     self.assertEqual(order1, order2)
    def test_check_filled_stop_order(self):
        def order_status_mock(_order):
            if _order == order:
                return 'Triggered'

        on_filled_mock = Mock()

        self.exchange_mock.get_order_status_ws.side_effect = order_status_mock

        order = Order(order_type='Stop', qty=228, stop_px=1000, side='Buy')
        order.order_id = '1234'
        order._on_fill = on_filled_mock
        self.supervisor.add_order(order)
        self.supervisor.check_needed_orders()

        # assert that we didn`t place this order
        self.exchange_mock.place_order.not_called(order)
        # assert that Supervisor forget this order
        self.assertNotIn(order, self.supervisor.orders)
        # assert that Supervisor call matching callback
        on_filled_mock.assert_called_once()
 def test_fb_entry_third_orderbook_price(self):
     self.exchange_mock.get_third_orderbook_price_ws.return_value = 1000
     self.exchange_mock.get_order_status_ws.return_value = 'Filled'
     self.exchange_mock.conn.get_tick_size.return_value = 0.5
     self.supervisor.enter_fb_method(
         qty=228,
         price_type='third_ob',
         max_retry=5,
         timeout=1
     )
     order = Order(order_type='Limit', qty=228, price=1000, side='Buy', passive=True)
     self.exchange_mock.place_order.assert_called_once_with(order)
Example #15
0
 def test_orders_are_not_equal(self):
     order1 = Order(order_type='Limit',
                    qty=228,
                    side='Buy',
                    price=Decimal(1000),
                    stop_px=None,
                    hidden=True,
                    close=False,
                    reduce_only=False,
                    passive=True)
     order2 = Order(
         order_type='Limit',
         qty=229,  # Other quantity
         side='Buy',
         price=Decimal(1000),
         stop_px=None,
         hidden=True,
         close=False,
         reduce_only=False,
         passive=True)
     self.assertNotEqual(order1, order2)
Example #16
0
 def test_import_stop_order_from_dict(self):
     order_dict = {
         'symbol': 'XBTUSD',
         'clOrdID': None,
         'orderID': None,
         'ordType': 'Stop',
         'orderQty': 228,
         'side': 'Buy',
         'price': None,
         'stopPx': 1000.0,
         'displayQty': 0,
         'execInst': 'Close,ReduceOnly,ParticipateDoNotInitiate'
     }
     expected_order = Order(order_type='Stop',
                            qty=228,
                            side='Buy',
                            stop_px=Decimal(1000),
                            hidden=True,
                            close=True,
                            reduce_only=True,
                            passive=True)
     self.assertTrue(expected_order == Order.from_dict(order_dict))
    def test_validation_error_while_placing_order(self):
        validation_error = requests.HTTPError()
        validation_error.response = Mock()
        validation_error.response.text = 'Order price is above the liquidation price of current'
        self.exchange_mock.place_order.side_effect = validation_error

        order = Order(order_type='Limit', qty=228, price=1000, side='Buy')
        self.supervisor.add_order(order)
        self.supervisor.check_needed_orders()

        # assert that method has been called
        self.exchange_mock.place_order.assert_called_once_with(order)
        # assert that we catch the exception and forget the order
        self.assertNotIn(order, self.supervisor.orders)
Example #18
0
 def test_export_order_to_api_dict_include_empty(self):
     order = Order(order_type='Limit',
                   qty=228,
                   side='Buy',
                   price=Decimal(1000),
                   stop_px=None,
                   hidden=True,
                   close=True,
                   reduce_only=True,
                   passive=True)
     expected_order_dict = {
         'symbol': 'XBTUSD',
         'clOrdID': None,
         'orderID': None,
         'ordType': 'Limit',
         'orderQty': 228,
         'side': 'Buy',
         'price': 1000.0,
         'stopPx': None,
         'displayQty': 0,
         'execInst': 'Close,ReduceOnly,ParticipateDoNotInitiate'
     }
     api_order_dict = order.as_dict(include_empty=True)
     self.assertEqual(expected_order_dict, api_order_dict)
    def test_fb_entry_last_price_timedout(self):
        self.exchange_mock.get_last_price_ws.return_value = 1000
        self.exchange_mock.get_order_status_ws.return_value = 'New'
        self.exchange_mock.conn.get_tick_size.return_value = 0.5
        self.supervisor.enter_fb_method(
            qty=228,
            price_type='last',
            max_retry=3,
            timeout=1
        )
        order = Order(order_type='Limit', qty=228, price=1000, side='Buy', passive=True)
        expected_calls = [call(order)] * 3
        self.exchange_mock.place_order.assert_has_calls(expected_calls)

        self.exchange_mock.place_market_order.assert_called_once_with(qty=228)
    def test_sell_order_will_follow_the_price(self):
        order = Order(order_type='Stop', side='Sell', qty=228, stop_px=1000)
        self.exchange_mock.get_open_orders_ws.return_value = [order]

        self.exchange_mock.get_last_price_ws.return_value = 1000

        self.supervisor.add_trailing_order(order, offset=10)
        self.supervisor.run_cycle()

        self.exchange_mock.get_last_price_ws.return_value = 7000
        order.tracker.max_price = 7000
        self.assertEqual(6300, order.stop_px)

        self.exchange_mock.get_last_price_ws.return_value = 8000
        order.tracker.max_price = 8000
        self.assertEqual(7200, order.stop_px)
Example #21
0
 def get_open_orders_ws(self):
     return [Order.from_dict(o) for o in self.conn.open_orders()]
Example #22
0
 def test_make_close_stop_order_with_no_qty(self):
     order = Order(order_type='Stop', side='Sell', stop_px=1000, close=True)
     self.assertTrue(order.is_valid())
    def test_check_unplaced_order(self):
        order = Order(order_type='Limit', qty=228, price=1000, side='Buy')
        self.supervisor.add_order(order)
        self.supervisor.check_needed_orders()

        self.exchange_mock.place_order.assert_called_once_with(order)
Example #24
0
TEST_API_KEY = 'your-api-key'
TEST_API_SECRET = 'your-api-secret'

if __name__ == '__main__':
    # Exchange is an interface-like class to call api methods
    exchange = Exchange(api_key=TEST_API_KEY, api_secret=TEST_API_SECRET,
                        test=True, symbol='XBTUSD')
    supervisor = Supervisor(interface=exchange)

    # you can disable managing position or orders by set to False needed properties
    # supervisor.manage_position = False
    # supervisor.manage_orders = False

    # create Order objects
    stop_loss = Order(order_type='Stop', stop_px=2000, qty=10, side='Sell')
    tp1 = Order(order_type='Limit', price=15000, qty=6, side='Sell', passive=True)
    tp2 = Order(order_type='Limit', price=20000, qty=4, side='Sell', hidden=True)

    trailing_stop = Order(order_type='Stop', stop_px=3000, qty=10, side='Sell')

    # attach some callbacks to stop-loss, note that events starts with "_"
    # DO NOT USE stop_cycle() method in callbacks!!! It causes deadlock
    stop_loss._on_reject = lambda: print('Rejected')
    stop_loss._on_fill = lambda: print('We lost position(')

    input('Enter to run cycle')
    supervisor.run_cycle()
    input('Enter to add orders to needed')
    supervisor.add_order(stop_loss)
    supervisor.add_order(tp1)
Example #25
0
 def test_make_stop_order_with_no_qty(self):
     order = Order(order_type='Stop', side='Sell', stop_px=1000)
     self.assertFalse(order.is_valid())
Example #26
0
 def get_order_by_clordid_ws(self, clordid):
     orders = self.conn.get_orders()
     orders = list(filter(lambda x: x['clOrdID'] == clordid, orders))
     if len(orders) == 1:
         return Order.from_dict(orders[0])
     return None
Example #27
0
 def get_filled_orders_ws(self):
     return [Order.from_dict(o) for o in self.conn.filled_orders()]
 def test_add_empty_order(self):
     with self.assertRaises(ValueError):
         self.supervisor.add_order(Order())
    def test_remove_order(self):
        new_order = Order(order_type='Limit', qty=228, price=1000, side='Buy')
        self.supervisor.add_order(new_order)

        self.supervisor.remove_order(new_order)
        self.assertNotIn(new_order, self.supervisor.orders)
    def test_several_filled_orders_at_the_same_time(self):
        order_1 = Order(order_type='Limit', side='Sell', qty=228, price=1000)
        order_1.order_id = 123456
        order_2 = Order(order_type='Limit', side='Sell', qty=229, price=1001)
        order_2.order_id = 123457
        order_3 = Order(order_type='Limit', side='Sell', qty=2210, price=1002)
        order_3.order_id = 123458
        order_4 = Order(order_type='Limit', side='Sell', qty=2211, price=1003)
        order_4.order_id = 123459

        callback_1 = Mock()
        callback_2 = Mock()
        callback_3 = Mock()
        callback_4 = Mock()
        order_1._on_fill = callback_1
        order_2._on_fill = callback_2
        order_3._on_fill = callback_3
        order_4._on_fill = callback_4
        self.supervisor.add_order(order_1)
        self.supervisor.add_order(order_2)
        self.supervisor.add_order(order_3)
        self.supervisor.add_order(order_4)

        self.exchange_mock.get_order_status_ws.return_value = 'Filled'

        self.supervisor.run_cycle()

        sleep(1)

        callback_1.assert_called_once()
        callback_2.assert_called_once()
        callback_3.assert_called_once()
        callback_4.assert_called_once()
        self.assertListEqual([], self.supervisor._orders)