def test_should_use_new_spreads_even_if_config_not_changed(self, tmpdir):
        # given
        reloadable_config = ReloadableConfig(self.write_spread_importing_config(tmpdir))
        reloadable_config.logger = MagicMock()

        # when
        spread_feed = {
            "buySpread": "0.1",
            "sellSpread": "1.0"
        }
        config = reloadable_config.get_config(spread_feed)

        # then
        assert config["usedBuySpread"] == 0.2
        assert config["usedSellSpread"] == 3.0

        # and
        # [a log message that the config was loaded gets generated]
        assert reloadable_config.logger.info.call_count == 1

        # when
        spread_feed = {
            "buySpread": "0.2",
            "sellSpread": "0.5"
        }
        config = reloadable_config.get_config(spread_feed)

        # then
        assert config["usedBuySpread"] == 0.4
        assert config["usedSellSpread"] == 1.5

        # and
        # [no log message that the config was reloaded gets generated]
        # [as it was only parsed again]
        assert reloadable_config.logger.info.call_count == 1
    def test_should_read_file_again_if_changed(self, tmpdir):
        # given
        reloadable_config = ReloadableConfig(self.write_advanced_config(tmpdir, "b"))
        reloadable_config.logger = MagicMock()

        # when
        config = reloadable_config.get_config({})

        # then
        assert config["a"] == "b"

        # and
        # [a log message that the config was loaded gets generated]
        assert reloadable_config.logger.info.call_count == 1

        # when
        self.write_advanced_config(tmpdir, "z")
        config = reloadable_config.get_config({})

        # then
        assert config["a"] == "z"

        # and
        # [a log message that the config was reloaded gets generated]
        assert reloadable_config.logger.info.call_count == 2
    def test_should_use_new_spreads_even_if_config_not_changed(self, tmpdir):
        # given
        reloadable_config = ReloadableConfig(self.write_spread_importing_config(tmpdir))
        reloadable_config.logger = MagicMock()

        # when
        spread_feed = {
            "buySpread": "0.1",
            "sellSpread": "1.0"
        }
        config = reloadable_config.get_config(spread_feed)

        # then
        assert config["usedBuySpread"] == 0.2
        assert config["usedSellSpread"] == 3.0

        # and
        # [a log message that the config was loaded gets generated]
        assert reloadable_config.logger.info.call_count == 1

        # when
        spread_feed = {
            "buySpread": "0.2",
            "sellSpread": "0.5"
        }
        config = reloadable_config.get_config(spread_feed)

        # then
        assert config["usedBuySpread"] == 0.4
        assert config["usedSellSpread"] == 1.5

        # and
        # [a log message that the config was reloaded gets generated]
        assert reloadable_config.logger.info.call_count == 2
Ejemplo n.º 4
0
    def read(reloadable_config: ReloadableConfig, spread_feed: Feed,
             history: History):
        assert (isinstance(reloadable_config, ReloadableConfig))
        assert (isinstance(history, History))

        try:
            config = reloadable_config.get_config(spread_feed.get()[0])

            buy_bands = list(map(BuyBand, config['buyBands']))
            buy_limits = SideLimits(
                config['buyLimits'] if 'buyLimits' in config else [],
                history.buy_history)
            sell_bands = list(map(SellBand, config['sellBands']))
            sell_limits = SideLimits(
                config['sellLimits'] if 'sellLimits' in config else [],
                history.sell_history)
        except Exception as e:
            logging.getLogger().warning(
                f"Config file is invalid ({e}). Treating the config file as it has no bands."
            )

            buy_bands = []
            buy_limits = SideLimits([], history.buy_history)
            sell_bands = []
            sell_limits = SideLimits([], history.buy_history)

        return Bands(buy_bands=buy_bands,
                     buy_limits=buy_limits,
                     sell_bands=sell_bands,
                     sell_limits=sell_limits)
Ejemplo n.º 5
0
class GasPriceFile(GasPrice):
    """Gas price configuration dynamically reloadable from a file.

    It is roughly an equivalent of implementation of :py:class:`pymaker.gas.IncreasingGasPrice`,
    but it uses `ReloadableConfig` to read the gas parameters from a file, and will dynamically
    reload that file whenever it changes. It allows to update the gas price dynamically
    for running keepers.

    Attributes:
        filename: Filename of the configuration file.
    """
    def __init__(self, filename: str):
        assert(isinstance(filename, str))

        self.reloadable_config = ReloadableConfig(filename)

    def get_gas_price(self, time_elapsed: int) -> Optional[int]:
        assert(isinstance(time_elapsed, int))

        config = self.reloadable_config.get_config()
        gas_price = config.get('gasPrice', None)
        gas_price_increase = config.get('gasPriceIncrease', None)
        gas_price_increase_every = config.get('gasPriceIncreaseEvery', None)
        gas_price_max = config.get('gasPriceMax', None)

        if gas_price is not None:
            if gas_price_increase and gas_price_increase_every:
                strategy = IncreasingGasPrice(gas_price, gas_price_increase, gas_price_increase_every, gas_price_max)
            else:
                strategy = FixedGasPrice(gas_price)
        else:
            strategy = DefaultGasPrice()

        return strategy.get_gas_price(time_elapsed=time_elapsed)
    def test_should_read_file_again_if_changed(self, tmpdir):
        # given
        reloadable_config = ReloadableConfig(self.write_advanced_config(tmpdir, "b"))

        # when
        config = reloadable_config.get_config({})

        # then
        assert config["a"] == "b"

        # when
        self.write_advanced_config(tmpdir, "z")
        config = reloadable_config.get_config({})

        # then
        assert config["a"] == "z"
Ejemplo n.º 7
0
    def test_should_read_file_again_if_changed(self, tmpdir):
        # given
        reloadable_config = ReloadableConfig(
            self.write_advanced_config(tmpdir, "b"))

        # when
        config = reloadable_config.get_config()

        # then
        assert config["a"] == "b"

        # when
        self.write_advanced_config(tmpdir, "z")
        config = reloadable_config.get_config()

        # then
        assert config["a"] == "z"
Ejemplo n.º 8
0
    def __init__(self, reloadable_config: ReloadableConfig):
        assert (isinstance(reloadable_config, ReloadableConfig))

        config = reloadable_config.get_config()
        self.buy_bands = list(map(BuyBand, config['buyBands']))
        self.sell_bands = list(map(SellBand, config['sellBands']))

        if self._bands_overlap(self.buy_bands) or self._bands_overlap(
                self.sell_bands):
            raise Exception(f"Bands in the config file overlap")
Ejemplo n.º 9
0
    def read(reloadable_config: ReloadableConfig, spread_feed: Feed,
             control_feed: Feed, history: History):
        assert (isinstance(reloadable_config, ReloadableConfig))
        assert (isinstance(spread_feed, Feed))
        assert (isinstance(control_feed, Feed))
        assert (isinstance(history, History))

        try:
            config = reloadable_config.get_config(spread_feed.get()[0])
            control_feed_value = control_feed.get()[0]

            buy_bands = list(map(BuyBand, config['buyBands']))
            buy_limits = SideLimits(
                config['buyLimits'] if 'buyLimits' in config else [],
                history.buy_history)
            sell_bands = list(map(SellBand, config['sellBands']))
            sell_limits = SideLimits(
                config['sellLimits'] if 'sellLimits' in config else [],
                history.sell_history)

            if 'canBuy' not in control_feed_value or 'canSell' not in control_feed_value:
                logging.getLogger().warning(
                    "Control feed expired. Assuming no buy bands and no sell bands."
                )

                buy_bands = []
                sell_bands = []

            else:
                if not control_feed_value['canBuy']:
                    logging.getLogger().warning(
                        "Control feed says we shall not buy. Assuming no buy bands."
                    )
                    buy_bands = []

                if not control_feed_value['canSell']:
                    logging.getLogger().warning(
                        "Control feed says we shall not sell. Assuming no sell bands."
                    )
                    sell_bands = []

        except Exception as e:
            logging.getLogger().exception(
                f"Config file is invalid ({e}). Treating the config file as it has no bands."
            )

            buy_bands = []
            buy_limits = SideLimits([], history.buy_history)
            sell_bands = []
            sell_limits = SideLimits([], history.buy_history)

        return Bands(buy_bands=buy_bands,
                     buy_limits=buy_limits,
                     sell_bands=sell_bands,
                     sell_limits=sell_limits)
Ejemplo n.º 10
0
    def test_should_reevaluate_if_other_config_file_changed(self, tmpdir):
        # given
        reloadable_config = ReloadableConfig(self.write_importing_config(tmpdir))

        # when
        self.write_global_config(tmpdir, 17.0, 11.0)
        config = reloadable_config.get_config({})

        # then
        assert len(config) == 2
        assert config["firstValueMultiplied"] == 34.0
        assert config["secondValueMultiplied"] == 33.0

        # when
        self.write_global_config(tmpdir, 18.0, 3.0)
        config = reloadable_config.get_config({})

        # then
        assert len(config) == 2
        assert config["firstValueMultiplied"] == 36.0
        assert config["secondValueMultiplied"] == 9.0
Ejemplo n.º 11
0
    def __init__(self, reloadable_config: ReloadableConfig, history: History):
        assert (isinstance(reloadable_config, ReloadableConfig))
        assert (isinstance(history, History))

        config = reloadable_config.get_config()
        self.buy_bands = list(map(BuyBand, config['buyBands']))
        self.buy_limits = SideLimits(
            config['buyLimits'] if 'buyLimits' in config else [],
            history.buy_history)
        self.sell_bands = list(map(SellBand, config['sellBands']))
        self.sell_limits = SideLimits(
            config['sellLimits'] if 'sellLimits' in config else [],
            history.sell_history)

        if self._bands_overlap(self.buy_bands) or self._bands_overlap(
                self.sell_bands):
            raise Exception(f"Bands in the config file overlap")
Ejemplo n.º 12
0
    def __init__(self, reloadable_config: ReloadableConfig, spread_feed: Feed, history: History):
        assert(isinstance(reloadable_config, ReloadableConfig))
        assert(isinstance(history, History))

        try:
            config = reloadable_config.get_config(spread_feed.get()[0])

            self.buy_bands = list(map(BuyBand, config['buyBands']))
            self.buy_limits = SideLimits(config['buyLimits'] if 'buyLimits' in config else [], history.buy_history)
            self.sell_bands = list(map(SellBand, config['sellBands']))
            self.sell_limits = SideLimits(config['sellLimits'] if 'sellLimits' in config else [], history.sell_history)
        except Exception as e:
            self.logger.warning(f"Config file is invalid ({e}). Treating the config file as it has no bands.")

            self.buy_bands = []
            self.buy_limits = SideLimits([], history.buy_history)
            self.sell_bands = []
            self.sell_limits = SideLimits([], history.buy_history)

        if self._bands_overlap(self.buy_bands) or self._bands_overlap(self.sell_bands):
            self.logger.warning("Bands in the config file overlap. Treating the config file as it has no bands.")

            self.buy_bands = []
            self.sell_bands = []
Ejemplo n.º 13
0
class LeverjMarketMakerKeeper:
    """Keeper acting as a market maker on leverj."""

    logger = logging.getLogger()

    def __init__(self, args: list):
        parser = argparse.ArgumentParser(prog='leverj-market-maker-keeper')

        parser.add_argument(
            "--leverj-api-server",
            type=str,
            default="https://test.leverj.io",
            help=
            "Address of the leverj API server (default: 'https://test.leverj.io')"
        )

        parser.add_argument("--account-id",
                            type=str,
                            default="",
                            help="Address of leverj api account id")

        parser.add_argument("--api-key",
                            type=str,
                            default="",
                            help="Address of leverj api key")

        parser.add_argument("--api-secret",
                            type=str,
                            default="",
                            help="Address of leverj api secret")

        parser.add_argument(
            "--leverj-timeout",
            type=float,
            default=9.5,
            help=
            "Timeout for accessing the Leverj API (in seconds, default: 9.5)")

        parser.add_argument("--rpc-host",
                            type=str,
                            default="localhost",
                            help="JSON-RPC host (default: `localhost')")

        parser.add_argument("--rpc-port",
                            type=int,
                            default=8545,
                            help="JSON-RPC port (default: `8545')")

        parser.add_argument("--rpc-timeout",
                            type=int,
                            default=10,
                            help="JSON-RPC timeout (in seconds, default: 10)")

        parser.add_argument(
            "--eth-from",
            type=str,
            required=True,
            help="Ethereum account from which to watch our trades")

        parser.add_argument(
            "--eth-key",
            type=str,
            nargs='*',
            help=
            "Ethereum private key(s) to use (e.g. 'key_file=aaa.json,pass_file=aaa.pass')"
        )

        parser.add_argument("--config",
                            type=str,
                            required=True,
                            help="Bands configuration file")

        parser.add_argument("--price-feed",
                            type=str,
                            required=True,
                            help="Source of price feed")

        parser.add_argument(
            "--price-feed-expiry",
            type=int,
            default=120,
            help="Maximum age of the price feed (in seconds, default: 120)")

        parser.add_argument("--spread-feed",
                            type=str,
                            help="Source of spread feed")

        parser.add_argument(
            "--spread-feed-expiry",
            type=int,
            default=3600,
            help="Maximum age of the spread feed (in seconds, default: 3600)")

        parser.add_argument("--control-feed",
                            type=str,
                            help="Source of control feed")

        parser.add_argument(
            "--control-feed-expiry",
            type=int,
            default=86400,
            help="Maximum age of the control feed (in seconds, default: 86400)"
        )

        parser.add_argument("--order-history",
                            type=str,
                            help="Endpoint to report active orders to")

        parser.add_argument(
            "--order-history-every",
            type=int,
            default=30,
            help=
            "Frequency of reporting active orders (in seconds, default: 30)")

        parser.add_argument(
            "--refresh-frequency",
            type=int,
            default=3,
            help="Order book refresh frequency (in seconds, default: 3)")

        parser.add_argument(
            "--pair",
            type=str,
            required=True,
            help="Token pair (sell/buy) on which the keeper will operate")

        parser.add_argument("--leverage",
                            type=float,
                            default=1.0,
                            help="Leverage chosen for futures orders")

        parser.add_argument(
            "--buffer-factor",
            type=float,
            default=0.6,
            help=
            "Should not go below 0.5, basically how much capital can you use on one side of quotes"
        )

        parser.add_argument("--debug",
                            dest='debug',
                            action='store_true',
                            help="Enable debug output")

        self.arguments = parser.parse_args(args)

        self.web3 = Web3(
            HTTPProvider(
                endpoint_uri=
                f"http://{self.arguments.rpc_host}:{self.arguments.rpc_port}",
                request_kwargs={"timeout": self.arguments.rpc_timeout}))

        self.web3.eth.defaultAccount = self.arguments.eth_from
        register_keys(self.web3, self.arguments.eth_key)

        setup_logging(self.arguments)

        self.bands_config = ReloadableConfig(self.arguments.config)
        self.price_feed = PriceFeedFactory().create_price_feed(self.arguments)
        self.spread_feed = create_spread_feed(self.arguments)
        self.control_feed = create_control_feed(self.arguments)
        self.order_history_reporter = create_order_history_reporter(
            self.arguments)
        self.target_price_lean = Wad(0)
        self.leverage = self.arguments.leverage
        self.buffer_factor = self.arguments.buffer_factor

        self.history = History()

        self.leverj_api = LeverjFuturesAPI(
            web3=self.web3,
            api_server=self.arguments.leverj_api_server,
            account_id=self.arguments.account_id,
            api_key=self.arguments.api_key,
            api_secret=self.arguments.api_secret,
            timeout=self.arguments.leverj_timeout)

        self.order_book_manager = OrderBookManager(
            refresh_frequency=self.arguments.refresh_frequency)
        self.order_book_manager.get_orders_with(
            lambda: self.leverj_api.get_orders(self.pair()))
        self.order_book_manager.get_balances_with(
            lambda: self.leverj_api.get_balances())
        self.order_book_manager.cancel_orders_with(
            lambda order: self.leverj_api.cancel_order(order.order_id))
        self.order_book_manager.enable_history_reporting(
            self.order_history_reporter, self.our_buy_orders,
            self.our_sell_orders)
        self.order_book_manager.start()

    def main(self):
        with Lifecycle() as lifecycle:
            lifecycle.initial_delay(1)
            lifecycle.on_startup(self.startup)
            lifecycle.every(1, self.synchronize_orders)
            lifecycle.on_shutdown(self.shutdown)

    def startup(self):
        quote_increment = self.leverj_api.get_tickSize(self.pair())
        self.precision = -(int(log10(float(quote_increment))) + 1)

    def shutdown(self):
        self.order_book_manager.cancel_all_orders()

    def pair(self):
        name_to_id_map = {'BTCDAI': '1', 'ETHDAI': '2'}
        return name_to_id_map[self.arguments.pair.upper()]

    def token_sell(self) -> str:
        return self.arguments.pair.upper()[:3]

    def token_buy(self) -> str:
        return self.arguments.pair.upper()[3:]

    def allocated_balance(self, token: str) -> Wad:
        quote_asset_address = self.leverj_api.get_product(
            self.pair())["quote"]["address"]
        # for perpetual contracts, the quote balance is allocated across instruments and sides to enter into trades
        total_available = self.leverj_api.get_plasma_balance(
            quote_asset_address)
        # adjust for leverage
        total_available = Decimal(total_available) * Decimal(self.leverage)
        self.logger.debug(f'total_available: {total_available}')
        return self._allocate_to_pair(total_available).get(token)

    def _allocate_to_pair(self, total_available):
        # total number of instruments across which the total_available balance is distributed
        # total_available is denominated in quote units
        total_number_of_instruments = 1

        # there are 2 partitions for allocation per instrument
        # the dai amount is divided in 2, one for the buy side and another for the sell side
        number_of_partitions_for_allocation = Wad.from_number(
            total_number_of_instruments * 2)

        # buffer_adjustment_factor is a small intentional buffer to avoid allocating the maximum possible.
        # the allocated amount is a little smaller than the maximum possible allocation
        # and that is determined by the buffer_adjustment_factor
        buffer_adjustment_factor = Wad.from_number(self.buffer_factor)

        base = self.arguments.pair.upper()[:3]
        quote = self.arguments.pair.upper()[3:]
        target_price = self.price_feed.get_price()
        product = self.leverj_api.get_product(self.pair())
        minimum_order_quantity = self.leverj_api.get_minimum_order_quantity(
            self.pair())
        minimum_quantity_wad = Wad.from_number(minimum_order_quantity)

        if ((base == product['baseSymbol'])
                and (quote == product['quoteSymbol'])):
            if ((target_price is None) or (target_price.buy_price is None)
                    or (target_price.sell_price is None)):
                base_allocation = Wad(0)
                quote_allocation = Wad(0)
                self.logger.debug(
                    f'target_price not available to calculate allocations')
            else:
                average_price = (target_price.buy_price +
                                 target_price.sell_price) / Wad.from_number(2)
                # at 1x average_price * minimum_quantity_wad is the minimum_required_balance
                # multiplying this minimum_required_balance by 2 to avoid sending very small orders to the exchange
                minimum_required_balance = average_price * minimum_quantity_wad * Wad.from_number(
                    2)
                # conversion_divisor is the divisor that determines how many chunks should Dai be distributed into.
                # It considers the price of the base to convert into base denomination.
                conversion_divisor = average_price * number_of_partitions_for_allocation * buffer_adjustment_factor
                open_position_for_base = self.leverj_api.get_position_in_wad(
                    base)
                total_available_wad = Wad.from_number(
                    Decimal(total_available) /
                    Decimal(Decimal(10)**Decimal(18)))
                base_allocation = total_available_wad / conversion_divisor
                quote_allocation = total_available_wad / (
                    number_of_partitions_for_allocation *
                    buffer_adjustment_factor)
                self.logger.debug(
                    f'open_position_for_base: {open_position_for_base}')
                # bids are made basis quote_allocation and asks basis base_allocation
                # if open position is net long then quote_allocation is adjusted.
                # if open position is net too long then target_price is adjusted to reduce price of the asks/offers
                if (open_position_for_base.value > 0):
                    open_position_for_base_in_quote = open_position_for_base * average_price
                    net_adjusted_quote_value = quote_allocation.value - abs(
                        open_position_for_base_in_quote.value)
                    self.logger.debug(
                        f'net_adjusted_quote_value: {net_adjusted_quote_value}'
                    )
                    quote_allocation = Wad(
                        net_adjusted_quote_value
                    ) if net_adjusted_quote_value > minimum_required_balance.value else Wad(
                        0)
                    # if open position is within 1 Wad range or more than quote allocations then target price is leaned down by 0.1 percent
                    if Wad(net_adjusted_quote_value) < Wad(1):
                        self.target_price_lean = Wad.from_number(0.999)
                    else:
                        self.target_price_lean = Wad(0)
                elif (open_position_for_base.value < 0):
                    # if open position is net short then base_allocation is adjusted
                    # if open position is net too short then target_price is adjusted to increase price of the bids
                    net_adjusted_base_value = base_allocation.value - abs(
                        open_position_for_base.value)
                    minimum_required_balance_in_base = minimum_required_balance / average_price
                    self.logger.debug(
                        f'net_adjusted_base_value: {net_adjusted_base_value}')
                    base_allocation = Wad(
                        net_adjusted_base_value
                    ) if net_adjusted_base_value > minimum_required_balance_in_base.value else Wad(
                        0)
                    # if open position is within 1 Wad range or more than base allocations then target price is leaned up by 0.1 percent
                    if Wad(net_adjusted_base_value) < Wad(1):
                        self.target_price_lean = Wad.from_number(1.001)
                    else:
                        self.target_price_lean = Wad(0)
        else:
            base_allocation = Wad(0)
            quote_allocation = Wad(0)

        allocation = {base: base_allocation, quote: quote_allocation}
        self.logger.debug(f'allocation: {allocation}')
        return allocation

    def our_sell_orders(self, our_orders: list) -> list:
        return list(filter(lambda order: order.is_sell, our_orders))

    def our_buy_orders(self, our_orders: list) -> list:
        return list(filter(lambda order: not order.is_sell, our_orders))

    def adjust_target_price(self, target_price: Price) -> Price:
        assert (isinstance(target_price, Price))
        target_price_lean = self.target_price_lean
        if ((target_price is None) or (target_price.buy_price is None)
                or (target_price.sell_price is None)):
            return target_price
        if target_price_lean.value == 0:
            return target_price
        else:
            self.logger.debug(f'target_price_lean: {target_price_lean}')
            adjusted_target_price = target_price
            adjusted_target_price.buy_price = (
                target_price.buy_price) * target_price_lean
            adjusted_target_price.sell_price = (
                target_price.sell_price) * target_price_lean
            return adjusted_target_price

    def sumMaxSellAmounts(self) -> Wad:
        #amounts produced are in base token
        sell_bands_list = self.bands_config.get_config(
            self.spread_feed.get()[0])['sellBands']
        sum_max_sell = 0.0
        for band_dict in sell_bands_list:
            sum_max_sell = sum_max_sell + float(band_dict["maxAmount"])
        return Wad.from_number(sum_max_sell)

    def sumMaxBuyAmounts(self) -> Wad:
        #amounts produced are in base token
        buy_bands_list = self.bands_config.get_config(
            self.spread_feed.get()[0])['buyBands']
        target_price = self.price_feed.get_price()
        sum_max_buy = 0.0
        for band_dict in buy_bands_list:
            sum_max_buy = sum_max_buy + float(band_dict["maxAmount"])

        if ((target_price is None) or (target_price.buy_price is None)
                or (target_price.sell_price is None)):
            self.logger.info(
                f'target_price is not available, defaulting to large sum max buy amount'
            )
            return Wad.from_number(2.0) * self.sumMaxSellAmounts()
        else:
            average_price = (target_price.buy_price +
                             target_price.sell_price) / Wad.from_number(2)
            baseTokenSumBuyAmount = Wad.from_number(1.2) * Wad.from_number(
                sum_max_buy) / average_price

        return baseTokenSumBuyAmount

    def synchronize_orders(self):
        bands = Bands.read(self.bands_config, self.spread_feed,
                           self.control_feed, self.history)
        order_book = self.order_book_manager.get_order_book()
        target_price = self.price_feed.get_price()
        target_price = self.adjust_target_price(target_price)
        self.logger.debug(
            f'target_price buy_price: {target_price.buy_price}, target_price sell_price: {target_price.sell_price}'
        )
        # Cancel orders
        cancellable_orders = bands.cancellable_orders(
            our_buy_orders=self.our_buy_orders(order_book.orders),
            our_sell_orders=self.our_sell_orders(order_book.orders),
            target_price=target_price)
        if len(cancellable_orders) > 0:
            self.order_book_manager.cancel_orders(cancellable_orders)
            return

        # Do not place new orders if order book state is not confirmed
        if order_book.orders_being_placed or order_book.orders_being_cancelled:
            self.logger.info(
                "Order book is in progress, not placing new orders")
            return

        # Place new orders
        new_orders = bands.new_orders(
            our_buy_orders=self.our_buy_orders(order_book.orders),
            our_sell_orders=self.our_sell_orders(order_book.orders),
            our_buy_balance=self.allocated_balance(self.token_buy()),
            our_sell_balance=self.allocated_balance(self.token_sell()),
            target_price=target_price)[0]
        self.place_orders(new_orders)

    def place_orders(self, new_orders: List[NewOrder]):
        def place_order_function(new_order_to_be_placed):
            price = round(new_order_to_be_placed.price, self.precision + 2)
            amount = new_order_to_be_placed.pay_amount if new_order_to_be_placed.is_sell else new_order_to_be_placed.buy_amount
            self.logger.debug(f'amount: {amount}')
            leverage_in_wad = Wad.from_number(self.leverage)
            maxSellAmountConfig = self.sumMaxSellAmounts()
            maxBuyAmountConfig = self.sumMaxBuyAmounts()
            base = self.arguments.pair.upper()[:3]
            open_position_for_base = self.leverj_api.get_position_in_wad(base)
            if new_order_to_be_placed.is_sell == True and open_position_for_base > maxSellAmountConfig:
                #placing reduce only sell order
                order_id = str(
                    self.leverj_api.place_order(self.pair(), price, 'LMT',
                                                new_order_to_be_placed.is_sell,
                                                price, amount, leverage_in_wad,
                                                True))
            elif open_position_for_base < Wad.from_number(
                    -1
            ) * maxBuyAmountConfig and new_order_to_be_placed.is_sell == False:
                #placing reduce only buy order
                order_id = str(
                    self.leverj_api.place_order(self.pair(), price, 'LMT',
                                                new_order_to_be_placed.is_sell,
                                                price, amount, leverage_in_wad,
                                                True))
            else:
                #placing regular leverage order that requires DAI or stable coin capital
                order_id = str(
                    self.leverj_api.place_order(self.pair(), price, 'LMT',
                                                new_order_to_be_placed.is_sell,
                                                price, amount, leverage_in_wad,
                                                False))
            return Order(order_id=order_id,
                         pair=self.pair(),
                         is_sell=new_order_to_be_placed.is_sell,
                         price=price,
                         amount=amount)

        for new_order in new_orders:
            self.order_book_manager.place_order(
                lambda new_order=new_order: place_order_function(new_order))