def get_order(self, time, countdown, lob):
        """
        Creates a GDX trader's order
        :param time: Current time
        :param countdown: Time left in the current trading period
        :param lob: Current state of the limit order book
        :return: Order to be sent to the exchange
        """
        if len(self.orders) < 1:
            self.active = False
            order = None
        else:
            coid = max(self.orders.keys())
            self.active = True
            self.limit = self.orders[coid].price
            self.job = self.orders[coid].otype

            # calculate price
            if self.job == 'Bid':
                self.price = self.calc_p_bid(self.holdings - 1,
                                             self.remaining_offer_ops - 1)
            if self.job == 'Ask':
                self.price = self.calc_p_ask(self.holdings - 1,
                                             self.remaining_offer_ops - 1)

            order = Order(self.tid, self.job, int(self.price),
                          self.orders[coid].qty, time, self.orders[coid].coid,
                          self.orders[coid].toid)
            self.last_quote = order

        if self.first_turn or self.price == -1:
            return None
        return order
    def get_order(self, time, countdown, lob):
        """

        :param time: Current time
        :param countdown: Time until end of current market session
        :param lob: Limit order book
        :return: Trader order to be sent to exchange
        """
        if len(self.orders) < 1:
            self.active = False
            order = None
        else:
            coid = max(self.orders.keys())
            self.active = True
            self.limit = self.orders[coid].price
            self.job = self.orders[coid].otype
            if self.job == 'Bid':
                # currently a buyer (working a bid order)
                self.margin = self.margin_buy
            else:
                # currently a seller (working a sell order)
                self.margin = self.margin_sell
            quote_price = int(self.limit * (1 + self.margin))
            self.price = quote_price

            order = Order(self.tid, self.job, quote_price,
                          self.orders[coid].qty, time, self.orders[coid].coid,
                          self.orders[coid].toid)
            self.last_quote = order
        return order
    def get_order(self, time, countdown, lob):
        """
        :param time: Current time
        :param countdown: Time until end of market session
        :param lob: Limit order book
        :return: Trader order to be sent to exchange
        """
        lurk_threshold = 0.2
        shave_growth_rate = 3
        shave = int(1.0 / (0.01 + countdown /
                           (shave_growth_rate * lurk_threshold)))
        if (len(self.orders) < 1) or (countdown > lurk_threshold):
            order = None
        else:
            coid = max(self.orders.keys())
            limit_price = self.orders[coid].price
            otype = self.orders[coid].otype

            if otype == 'Bid':
                if lob['bids']['n'] > 0:
                    quote_price = lob['bids']['best'] + shave
                    quote_price = min(quote_price, limit_price)
                else:
                    quote_price = lob['bids']['worst']
            else:
                if lob['asks']['n'] > 0:
                    quote_price = lob['asks']['best'] - shave
                    quote_price = min(quote_price, limit_price)
                else:
                    quote_price = lob['asks']['worst']
            order = Order(self.tid, otype, quote_price, self.orders[coid].qty,
                          time, self.orders[coid].coid, self.orders[coid].toid)
            self.last_quote = order
        return order
 def get_order(self, time, countdown, lob):
     """
     Get's Shaver trader order by shaving/adding a penny to current best bid
     :param time: Current time
     :param countdown: Countdown to end of market session
     :param lob: Limit order book
     :return: The trader order to be sent to the exchange
     """
     if len(self.orders) < 1:
         order = None
     else:
         coid = max(self.orders.keys())
         limit_price = self.orders[coid].price
         otype = self.orders[coid].otype
         if otype == 'Bid':
             if lob['bids']['n'] > 0:
                 quote_price = lob['bids']['best'] + 1
                 quote_price = min(quote_price, limit_price)
             else:
                 quote_price = lob['bids']['worst']
         else:
             if lob['asks']['n'] > 0:
                 quote_price = lob['asks']['best'] - 1
                 quote_price = min(quote_price, limit_price)
             else:
                 quote_price = lob['asks']['worst']
         order = Order(self.tid, otype, quote_price, self.orders[coid].qty,
                       time, self.orders[coid].coid, self.orders[coid].toid)
         self.last_quote = order
     return order
 def get_order(self, time, countdown, lob):
     """
     Gets ZIC trader, limit price is randomly selected
     :param time: Current time
     :param countdown: Time until end of current market session
     :param lob: Limit order book
     :return: The trader order to be sent to the exchange
     """
     if len(self.orders) < 1:
         # no orders: return NULL
         order = None
     else:
         coid = max(self.orders.keys())
         min_price = lob['bids']['worst']
         max_price = lob['asks']['worst']
         limit = self.orders[coid].price
         otype = self.orders[coid].otype
         if otype == 'Bid':
             quote_price = random.randint(min_price, limit)
         else:
             quote_price = random.randint(limit, max_price)
             # NB should check it == 'Ask' and barf if not
         order = Order(self.tid, otype, quote_price, self.orders[coid].qty,
                       time, self.orders[coid].coid, self.orders[coid].toid)
         self.last_quote = order
     return order
 def get_order(self, time, countdown, lob):
     """
     Get's giveaway traders order - in this case the price is just the limit price from the customer order
     :param time: Current time
     :param countdown: Time until end of session
     :param lob: Limit order book
     :return: Order to be sent to the exchange
     """
     if len(self.orders) < 1:
         order = None
     else:
         coid = max(self.orders.keys())
         quote_price = self.orders[coid].price
         order = Order(self.tid, self.orders[coid].otype, quote_price,
                       self.orders[coid].qty, time, self.orders[coid].coid,
                       self.orders[coid].toid)
         self.last_quote = order
     return order
    def get_order(self, time, countdown, lob):
        """
        Creates an AA trader's order
        :param time: Current time
        :param countdown: Time left in the current trading period
        :param lob: Current state of the limit order book
        :return: Order to be sent to the exchange
        """
        if len(self.orders) < 1:
            self.active = False
            return None
        coid = max(self.orders.keys())
        self.active = True
        self.limit = self.orders[coid].price
        self.job = self.orders[coid].otype
        self.calc_target()

        if self.prev_best_bid_p is None:
            o_bid = 0
        else:
            o_bid = self.prev_best_bid_p
        if self.prev_best_ask_p is None:
            o_ask = self.market_max
        else:
            o_ask = self.prev_best_ask_p

        quote_price = TBSE_SYS_MIN_PRICE
        if self.job == 'Bid':  # BUYER
            if self.limit <= o_bid:
                return None
            if len(self.previous_transactions
                   ) > 0:  # has been at least one transaction
                o_ask_plus = (1 + self.r_shout_change_relative
                              ) * o_ask + self.r_shout_change_absolute
                quote_price = o_bid + ((min(self.limit, o_ask_plus) - o_bid) /
                                       self.offer_change_rate)
            else:
                if o_ask <= self.buy_target:
                    quote_price = o_ask
                else:
                    quote_price = o_bid + (
                        (self.buy_target - o_bid) / self.offer_change_rate)
        elif self.job == 'Ask':
            if self.limit >= o_ask:
                return None
            if len(self.previous_transactions
                   ) > 0:  # has been at least one transaction
                o_bid_minus = (1 - self.r_shout_change_relative
                               ) * o_bid - self.r_shout_change_absolute
                quote_price = o_ask - ((o_ask - max(self.limit, o_bid_minus)) /
                                       self.offer_change_rate)
            else:
                if o_bid >= self.sell_target:
                    quote_price = o_bid
                else:
                    quote_price = o_ask - (
                        (o_ask - self.sell_target) / self.offer_change_rate)

        order = Order(self.tid, self.job, int(quote_price),
                      self.orders[coid].qty, time, self.orders[coid].coid,
                      self.orders[coid].toid)
        self.last_quote = order
        return order
def customer_orders(time, coid, traders, trader_stats, order_sched, pending,
                    verbose):
    """
    Produce and distribute customer orders to traders
    Mostly unaltered from original BSE code by Dave Cliff
    :param time: current curr_time
    :param coid: last used customer order ID
    :param traders: List of traders
    :param trader_stats: number of buyers and number of sellers
    :param order_sched: order schedule
    :param pending: pending orders to be distributed
    :param verbose: should verbose logging be printed to console
    :return: List containing left over pending orders, cancellations to be made and the final customer ID used
    """
    def sys_min_check(price):
        """
        Check if order price is below system minimum price and sets price to be minimum if it is
        :param price: Order price
        :return: new order price
        """
        if price < TBSE_SYS_MIN_PRICE:
            print('WARNING: price < bse_sys_min -- clipped')
            price = TBSE_SYS_MIN_PRICE
        return price

    def sys_max_check(price):
        """
        Check if order price is above system maximum price and sets price to be maximum if it is
        :param price: Order price
        :return: new order price
        """
        if price > TBSE_SYS_MAX_PRICE:
            print('WARNING: price > bse_sys_max -- clipped')
            price = TBSE_SYS_MAX_PRICE
        return price

    def get_order_price(i, schedule, schedule_end, n, stepmode, time_of_issue):
        """
        Calculates order price for a new customer order
        :param i: Index of either buyer or seller trader
        :param schedule: Order schedule
        :param schedule_end: End curr_time of order schedule
        :param n: number of buyers or sellers
        :param stepmode: Stepmode of order schedule
        :param time_of_issue: Time order should be issued after
        :return: Order price
        """
        # pylint: disable=too-many-branches,too-many-statements
        if config.useInputFile:
            if len(schedule[0]) > 2:
                offset_function = schedule[0][2][0]
                offset_function_params = [schedule_end] + list(
                    schedule[0][2][1])
                if callable(offset_function):
                    # same offset for min and max
                    offset_min = offset_function(time_of_issue,
                                                 offset_function_params)
                    offset_max = offset_min
                else:
                    sys.exit(
                        'FAIL: 3rd argument of schedule in get_order_price() should be [callable_fn [params]]'
                    )
                if len(schedule[0]) > 3:
                    # if second offset function is specfied, that applies only to the max value
                    offset_function = schedule[0][3][0]
                    offset_function_params = [schedule_end] + list(
                        schedule[0][3][1])
                    if callable(offset_function):
                        # this function applies to max
                        offset_max = offset_function(time_of_issue,
                                                     offset_function_params)
                    else:
                        sys.exit(
                            'FAIL: 4th argument of schedule in get_order_price() should be [callable_fn [params]]'
                        )
            else:
                offset_min = 0.0
                offset_max = 0.0
        else:
            # does the first schedule range include optional dynamic offset function(s)?
            if len(schedule[0]) > 2:
                offset_function = schedule[0][2]
                if callable(offset_function):
                    # same offset for min and max
                    offset_min = offset_function(time_of_issue)
                    offset_max = offset_min
                else:
                    sys.exit(
                        'FAIL: 3rd argument of schedule in get_order_price() not callable'
                    )
                if len(schedule[0]) > 3:
                    # if second offset function is specfied, that applies only to the max value
                    offset_function = schedule[0][3]
                    if callable(offset_function):
                        # this function applies to max
                        offset_max = offset_function(time_of_issue)
                    else:
                        sys.exit(
                            'FAIL: 4th argument of schedule in get_order_price() not callable'
                        )
            else:
                offset_min = 0.0
                offset_max = 0.0

        p_min = sys_min_check(offset_min + min(schedule[0][0], schedule[0][1]))
        p_max = sys_max_check(offset_max + max(schedule[0][0], schedule[0][1]))
        p_range = p_max - p_min
        step_size = p_range / (n - 1)
        half_step = round(step_size / 2.0)

        if stepmode == 'fixed':
            new_order_price = p_min + int(i * step_size)
        elif stepmode == 'jittered':
            new_order_price = p_min + int(i * step_size) + random.randint(
                -half_step, half_step)
        elif stepmode == 'random':
            if len(schedule) > 1:
                # more than one schedule: choose one equiprobably
                s = random.randint(0, len(schedule) - 1)
                p_min = sys_min_check(min(schedule[s][0], schedule[s][1]))
                p_max = sys_max_check(max(schedule[s][0], schedule[s][1]))
            new_order_price = random.randint(p_min, p_max)
        else:
            sys.exit('ERROR: Unknown stepmode in schedule')
        new_order_price = sys_min_check(sys_max_check(new_order_price))
        return new_order_price

    # pylint: disable=too-many-branches
    def get_issue_times(n_traders, stepmode, interval, shuffle,
                        fit_to_interval):
        """
        Produces issue times for orders
        :param n_traders: The number of traders
        :param stepmode:
        :param interval: Gap between each set of orders
        :param shuffle: Boolean of whether it should be shuffled or not
        :param fit_to_interval:
        :return: Issue times
        """
        interval = float(interval)
        if n_traders < 1:
            sys.exit('FAIL: n_traders < 1 in get_issue_times()')
        elif n_traders == 1:
            t_step = interval
        else:
            t_step = interval / (n_traders - 1)
        arr_time = 0
        order_issue_times = []
        for i in range(n_traders):
            if stepmode == 'periodic':
                arr_time = interval
            elif stepmode == 'drip-fixed':
                arr_time = i * t_step
            elif stepmode == 'drip-jitter':
                arr_time = i * t_step + t_step * random.random()
            elif stepmode == 'drip-poisson':
                # poisson requires a bit of extra work
                inter_arrival_time = random.expovariate(n_traders / interval)
                arr_time += inter_arrival_time
            else:
                sys.exit('FAIL: unknown t-stepmode in get_issue_times()')
            order_issue_times.append(arr_time)

        # at this point, arr_time is the last arrival i
        if fit_to_interval and ((arr_time > interval) or
                                (arr_time < interval)):
            # generated sum of inter-arrival times longer than the interval
            # squish them back so that last arrival falls at t=interval
            for i in range(n_traders):
                order_issue_times[i] = interval * (order_issue_times[i] /
                                                   arr_time)
        # optionally randomly shuffle the times
        if shuffle:
            for i in range(n_traders):
                i = (n_traders - 1) - i
                j = random.randint(0, i)
                tmp = order_issue_times[i]
                order_issue_times[i] = order_issue_times[j]
                order_issue_times[j] = tmp
        return order_issue_times

    def get_sched_mode(curr_time, order_schedule):
        """
        Extract details of the order_schedule
        :param curr_time: Current time
        :param order_schedule: Order Schedule
        :return: The range of values the orders can take, the stepmode and the schedule end time.
        """
        schedrange = None
        stepmode = None
        sched_end_time = None
        got_one = False
        for schedule in order_schedule:
            if schedule['from'] <= curr_time < schedule['to']:
                # within the timezone for this schedule
                schedrange = schedule['ranges']
                stepmode = schedule['stepmode']
                sched_end_time = schedule['to']
                got_one = True
                break  # jump out the loop -- so the first matching timezone has priority over any others
        if not got_one:
            sys.exit(
                f'Fail: t={curr_time:5.2f} not within any timezone in order_schedule={order_schedule}'
            )
        return schedrange, stepmode, sched_end_time

    n_buyers = trader_stats['n_buyers']
    n_sellers = trader_stats['n_sellers']

    shuffle_times = True

    cancellations = []

    if len(pending) < 1:
        # list of pending (to-be-issued) customer orders is empty, so generate a new one
        new_pending = []

        # demand side (buyers)
        issue_times = get_issue_times(n_buyers, order_sched['timemode'],
                                      order_sched['interval'], shuffle_times,
                                      True)
        order_type = 'Bid'
        (sched, mode, sched_end) = get_sched_mode(time, order_sched['dem'])
        for t in range(n_buyers):
            issue_time = time + issue_times[t]
            t_name = f'B{str(t).zfill(2)}'
            order_price = get_order_price(t, sched, sched_end, n_buyers, mode,
                                          issue_time)
            order = Order(t_name, order_type, order_price, 1, issue_time, coid,
                          -3.14)
            new_pending.append(order)
            coid += 1

        # supply side (sellers)
        issue_times = get_issue_times(n_sellers, order_sched['timemode'],
                                      order_sched['interval'], shuffle_times,
                                      True)
        order_type = 'Ask'
        (sched, mode, sched_end) = get_sched_mode(time, order_sched['sup'])
        for t in range(n_sellers):
            issue_time = time + issue_times[t]
            t_name = f'S{str(t).zfill(2)}'
            order_price = get_order_price(t, sched, sched_end, n_sellers, mode,
                                          issue_time)
            order = Order(t_name, order_type, order_price, 1, issue_time, coid,
                          -3.14)
            new_pending.append(order)
            coid += 1
    else:
        # there are pending future orders: issue any whose timestamp is in the past
        new_pending = []
        for order in pending:
            if order.time < time:
                # this order should have been issued by now
                # issue it to the trader
                t_name = order.tid
                response = traders[t_name].add_order(order, verbose)
                if verbose:
                    print(f'Customer order: {response} {order}')
                if response == 'LOB_Cancel':
                    cancellations.append(t_name)
                    if verbose:
                        print(f'Cancellations: {cancellations}')
                # and then don't add it to new_pending (i.e., delete it)
            else:
                # this order stays on the pending list
                new_pending.append(order)
    return [new_pending, cancellations, coid]