def split_n_order(self, order: Order, inter_distance: float,
                      child_count: int) -> List[Order]:
        new_orders = []
        # calculate new amount
        new_amount = order.amount / child_count

        # create positions list
        positions = []
        if (child_count % 2) != 0:
            positions.append(0)  # child_count is odd
        # add positive
        positions += [x for x in range(1, 1 + int(child_count / 2))
                      ]  # if n=4: [1, 2]
        # add negative
        positions += [-x for x in range(1, 1 + int(child_count / 2))
                      ]  # if n=4: [1, 2, -1, -2]

        # loop positions
        for n in positions:
            new_price = order.price + inter_distance * n
            new_order = Order(session_id=order.session_id,
                              order_id=f'CHILD({n:+})',
                              pt_id=order.pt_id,
                              k_side=order.k_side,
                              price=new_price,
                              amount=new_amount,
                              status=OrderStatus.MONITOR,
                              uid=Order.get_new_uid(),
                              name=order.name + f'({n:+})')
            new_order.split_count = order.split_count + 1
            new_order.compensation_count = order.compensation_count
            new_order.concentration_count = order.concentration_count
            # add to monitor and pending_orders table
            self.pob.monitor.append(new_order)

            # add to return list
            new_orders.append(new_order)

        # delete original order from orders book
        self.pob.monitor.remove(order)

        return new_orders
    def concentrate_orders(self, orders: List[Order], ref_mp: float,
                           ref_gap: float) -> bool:
        """concentration does not include n-split
        :param orders: list of orders to concentrate
        :param ref_mp: market price to concentrate to
        :param ref_gap: concentration gap
        :return: True if concentration is successful
        """
        # check orders list is not empty
        if len(orders) < 1:
            return False

        # get equivalent amount and total
        amount, total, _, _ = BalanceManager.get_balance_for_list(orders)

        # get equivalent pair b1-s1
        s1_p, b1_p, s1_qty, b1_qty = get_compensation(cmp=ref_mp,
                                                      gap=ref_gap,
                                                      qty_bal=amount,
                                                      price_bal=total,
                                                      buy_fee=self.buy_fee,
                                                      sell_fee=self.sell_fee)

        # validate values received for b1 and s1
        if s1_p < 0 or b1_p < 0 or s1_qty < 0 or b1_qty < 0:
            log.critical(
                f'!!!!!!!!!! negative value(s) after compensation: b1p: {b1_p} - b1q: {b1_qty} !!!!!!!!!!'
                f'- s1p: {s1_p} - s1q: {s1_qty}')
            return False
        else:
            # get pt_id of all orders to concentrate
            pt_ids = []
            for order in orders:
                if order.pt_id not in pt_ids:
                    pt_ids.append(order.pt_id)

            # increment counter and save mapping between count and pt_ids
            # at this point the concentration is sure and will return True
            self.concentrator_count += 1
            self.concentrated_pt_id.append((self.concentrator_count, pt_ids))
            print(self.concentrated_pt_id)

            # change pending orders with this pt_id to new pt_id
            new_pt_id = f'C-{self.concentrator_count:04}'
            for order in self.pob.get_pending_orders():
                if order.pt_id in pt_ids:
                    order.pt_id = new_pt_id
            # change pt_id also in traded orders
            self.tob.set_new_pt_id(new_pt_id=new_pt_id, pt_id_list=pt_ids)

            # create both orders
            session_id = orders[0].session_id  # same session_id
            b1 = Order(session_id=session_id,
                       order_id='CONCENTRATED',
                       pt_id=new_pt_id,
                       k_side=k_binance.SIDE_BUY,
                       price=b1_p,
                       amount=b1_qty,
                       status=OrderStatus.MONITOR,
                       uid=Order.get_new_uid(),
                       name='con-b1')
            s1 = Order(session_id=session_id,
                       order_id='CONCENTRATED',
                       pt_id=new_pt_id,
                       k_side=k_binance.SIDE_SELL,
                       price=s1_p,
                       amount=s1_qty,
                       status=OrderStatus.MONITOR,
                       uid=Order.get_new_uid(),
                       name='con-s1')

            # add new orders to appropriate list
            self.pob.monitor.append(b1)
            self.pob.monitor.append(s1)

            # delete original orders from list
            for order in orders:
                self.pob.monitor.remove(order)

            # log
            log.info('////////// ORDER COMPENSATED //////////')
            for order in orders:
                log.info(f'initial order:  {order}')
                log.info(f'compensation count: {order.compensation_count}')
            log.info(f'compensated b1: {b1}')
            log.info(f'compensated s1: {s1}')

            # update concentrated variables and inverse counter
            b1.concentration_count = 1
            s1.concentration_count = 1

            # update variables
            b1.compensation_count = 0
            b1.split_count = 0
            s1.compensation_count = 0
            s1.split_count = 0

            # split n
            # self.split_n_order(order=b1, inter_distance=interdistance_after_concentration, child_count=n_for_split)
            # self.split_n_order(order=s1, inter_distance=interdistance_after_concentration, child_count=n_for_split)

            return True
    def concentrate_for_liquidity(self, order: Order, ref_mp: float,
                                  ref_gap: float) -> bool:
        """concentration does not include n-split
        :param order: order to concentrate
        :param ref_mp: market price to concentrate to
        :param ref_gap: concentration gap
        :return: True if concentration is successful
        """

        # get equivalent amount and total
        amount, total, _, _ = BalanceManager.get_balance_for_list([order])

        # get equivalent pair b1-s1
        s1_p, b1_p, s1_qty, b1_qty = get_compensation(cmp=ref_mp,
                                                      gap=ref_gap,
                                                      qty_bal=amount,
                                                      price_bal=total,
                                                      buy_fee=self.buy_fee,
                                                      sell_fee=self.sell_fee)

        # validate values received for b1 and s1
        if s1_p < 0 or b1_p < 0 or s1_qty < 0 or b1_qty < 0:
            log.critical(
                f'!!!!!!!!!! negative value(s) after compensation: b1p: {b1_p} - b1q: {b1_qty} !!!!!!!!!!'
                f'- s1p: {s1_p} - s1q: {s1_qty}')
            return False
        else:

            # increment counter and save mapping between count and pt_ids
            # at this point the concentration is sure and will return True
            self.concentrator_count += 1

            # create both orders
            session_id = order.session_id  # same session_id
            b1 = Order(session_id=session_id,
                       order_id='CONCENTRATED',
                       pt_id=order.pt_id,
                       k_side=k_binance.SIDE_BUY,
                       price=b1_p,
                       amount=b1_qty,
                       status=OrderStatus.MONITOR,
                       uid=Order.get_new_uid(),
                       name='con-b1')
            s1 = Order(session_id=session_id,
                       order_id='CONCENTRATED',
                       pt_id=order.pt_id,
                       k_side=k_binance.SIDE_SELL,
                       price=s1_p,
                       amount=s1_qty,
                       status=OrderStatus.MONITOR,
                       uid=Order.get_new_uid(),
                       name='con-s1')

            # add new orders to appropriate list
            self.pob.monitor.append(b1)
            self.pob.monitor.append(s1)

            # delete original order from list
            self.pob.monitor.remove(order)

            # log
            print(f'concentrated order: {order}')
            log.info(f'initial order:  {order}')
            log.info(f'concentration count: {order.concentration_count}')
            log.info(f'compensated b1: {b1}')
            log.info(f'compensated s1: {s1}')

            # update concentrated variables and inverse counter
            b1.concentration_count = 1
            s1.concentration_count = 1

            # update variables
            b1.compensation_count = 0
            b1.split_count = 0
            s1.compensation_count = 0
            s1.split_count = 0

            return True