def init_objects_unequal(parameters, seed, equality=1.0):
    """
    Init object for the distribution version of the model
    :param parameters:
    :param seed:
    :param equality: float to change init equality 0.01 is very unequal, to inf being very equal, the default 1.0 is equal to the uniform distribution
    :return:
    """
    np.random.seed(seed)
    random.seed(seed)

    traders = []
    n_traders = parameters["n_traders"]

    weight_f = (1 - parameters['strat_share_chartists']) * (
        1 - parameters['w_random'])
    weight_c = parameters['strat_share_chartists'] * (1 -
                                                      parameters['w_random'])

    f_points = int(weight_f * 100 * n_traders)
    c_points = int(weight_c * 100 * n_traders)
    r_points = int(parameters['w_random'] * 100 * n_traders)

    # create list of strategy points, shuffle it and divide in equal parts
    strat_points = ['f' for f in range(f_points)] + [
        'c' for c in range(c_points)
    ] + ['r' for r in range(r_points)]
    random.shuffle(strat_points)
    agent_points = np.array_split(strat_points, n_traders)

    max_horizon = parameters[
        'horizon'] * 2  # this is the max horizon of an agent if 100% fundamentalist
    historical_stock_returns = np.random.normal(0,
                                                parameters["std_fundamental"],
                                                max_horizon)

    init_distribution = np.random.power(equality, n_traders)
    # normalize
    init_distribution = init_distribution / init_distribution.sum()

    for idx in range(n_traders):
        weight_fundamentalist = list(agent_points[idx]).count('f') / float(
            len(agent_points[idx]))
        weight_chartist = list(agent_points[idx]).count('c') / float(
            len(agent_points[idx]))
        weight_random = list(agent_points[idx]).count('r') / float(
            len(agent_points[idx]))

        init_stocks = int(parameters["init_stocks"] * n_traders *
                          init_distribution[idx])
        init_money = parameters["init_stocks"] * parameters[
            'fundamental_value'] * n_traders * init_distribution[
                idx]  #TODO debug

        c_share_strat = div0(weight_chartist,
                             (weight_fundamentalist + weight_chartist))

        # initialize co_variance_matrix
        init_covariance_matrix = calculate_covariance_matrix(
            historical_stock_returns, parameters["std_fundamental"])

        lft_vars = TraderVariablesDistribution(weight_fundamentalist,
                                               weight_chartist, weight_random,
                                               c_share_strat, init_money,
                                               init_stocks,
                                               init_covariance_matrix,
                                               parameters['fundamental_value'])

        # determine heterogeneous horizon and risk aversion based on
        individual_horizon = parameters[
            'horizon']  #np.random.randint(10, parameters['horizon']) TODO, maybe change back

        individual_risk_aversion = parameters[
            "base_risk_aversion"]  # abs(np.random.normal(parameters["base_risk_aversion"], parameters["base_risk_aversion"] / 5.0))#parameters["base_risk_aversion"] * relative_fundamentalism
        individual_learning_ability = parameters[
            'average_learning_ability']  #min(abs(np.random.normal(parameters['average_learning_ability'], 0.1)), 1.0) #TODO what to do with std_dev

        lft_params = TraderParametersDistribution(individual_horizon,
                                                  individual_risk_aversion,
                                                  individual_learning_ability,
                                                  parameters['spread_max'])
        lft_expectations = TraderExpectations(parameters['fundamental_value'])
        traders.append(Trader(idx, lft_vars, lft_params, lft_expectations))

    orderbook = LimitOrderBook(parameters['fundamental_value'],
                               parameters["std_fundamental"], max_horizon,
                               parameters['ticks'])

    # initialize order-book returns for initial variance calculations
    orderbook.returns = list(historical_stock_returns)

    return traders, orderbook
Exemplo n.º 2
0
def init_objects(parameters, seed):
    """
    Init object for the distribution version of the model
    :param parameters:
    :param seed:
    :return:
    """
    np.random.seed(seed)
    random.seed(seed)

    traders = []
    n_traders = parameters["n_traders"]

    weight_f = (1 - parameters['strat_share_chartists']) * (
        1 - parameters['w_random'])
    weight_c = parameters['strat_share_chartists'] * (1 -
                                                      parameters['w_random'])

    f_points = int(weight_f * 100 * n_traders)
    c_points = int(weight_c * 100 * n_traders)
    r_points = int(parameters['w_random'] * 100 * n_traders)

    # create list of strategy points, shuffle it and divide in equal parts
    strat_points = ['f' for f in range(f_points)] + [
        'c' for c in range(c_points)
    ] + ['r' for r in range(r_points)]
    random.shuffle(strat_points)
    agent_points = np.array_split(strat_points, n_traders)

    max_horizon = parameters[
        'horizon'] * 2  # this is the max horizon of an agent if 100% fundamentalist
    historical_stock_returns = np.random.normal(0,
                                                parameters["std_fundamental"],
                                                max_horizon)

    for idx in range(n_traders):
        weight_fundamentalist = list(agent_points[idx]).count('f') / float(
            len(agent_points[idx]))
        weight_chartist = list(agent_points[idx]).count('c') / float(
            len(agent_points[idx]))
        weight_random = list(agent_points[idx]).count('r') / float(
            len(agent_points[idx]))

        init_stocks = int(np.random.uniform(0, parameters["init_stocks"]))
        init_money = np.random.uniform(
            0, (parameters["init_stocks"] * parameters['fundamental_value']))

        if weight_random < 1.0:
            c_share_strat = div0(weight_chartist,
                                 (weight_fundamentalist + weight_chartist))
        else:
            c_share_strat = 0.0

        # initialize co_variance_matrix
        init_covariance_matrix = calculate_covariance_matrix(
            historical_stock_returns, parameters["std_fundamental"])

        lft_vars = TraderVariables(weight_fundamentalist, weight_chartist,
                                   weight_random, c_share_strat, init_money,
                                   init_stocks, init_covariance_matrix,
                                   parameters['fundamental_value'])

        # determine heterogeneous horizon and risk aversion based on
        individual_horizon = np.random.randint(10, parameters['horizon'])

        individual_risk_aversion = abs(
            np.random.normal(parameters["base_risk_aversion"],
                             parameters["base_risk_aversion"] / 5.0)
        )  #parameters["base_risk_aversion"] * relative_fundamentalism
        individual_learning_ability = min(
            abs(np.random.normal(parameters['average_learning_ability'], 0.1)),
            1.0)  #TODO what to do with std_dev

        lft_params = TraderParameters(individual_horizon,
                                      individual_risk_aversion,
                                      individual_learning_ability,
                                      parameters['spread_max'])
        lft_expectations = TraderExpectations(parameters['fundamental_value'])
        traders.append(Trader(idx, lft_vars, lft_params, lft_expectations))

    # Add market maker with 100k money and 1k stocks
    mm_tradervariables = TraderVariables(
        weight_fundamentalist=0,
        weight_chartist=0,
        weight_random=0,
        c_share_strat=0,
        money=100000,
        stocks=1000,
        covariance_matrix=0,
        init_price=parameters['fundamental_value'])
    mm_traderparams = TraderParameters(ref_horizon=0,
                                       risk_aversion=0,
                                       learning_ability=0.0,
                                       max_spread=10000)
    mm_traderexp = TraderExpectations(parameters['fundamental_value'])
    market_maker = Trader(0, mm_tradervariables, mm_traderparams, mm_traderexp)

    orderbook = LimitOrderBook(parameters['fundamental_value'],
                               parameters["std_fundamental"], max_horizon,
                               parameters['ticks'])

    # initialize order-book returns for initial variance calculations
    orderbook.returns = list(historical_stock_returns)

    return traders, orderbook, market_maker
Exemplo n.º 3
0
def pb_distr_model(traders, orderbook, parameters, seed=1):
    """
    The main model function of distribution model where trader stocks are tracked.
    :param traders: list of Agent objects
    :param orderbook: object Order book
    :param parameters: dictionary of parameters
    :param seed: integer seed to initialise the random number generators
    :return: list of simulated Agent objects, object simulated Order book
    """
    random.seed(seed)
    np.random.seed(seed)
    fundamental = [parameters["fundamental_value"]]
    orderbook.tick_close_price.append(fundamental[-1])

    traders_by_wealth = [t for t in traders]

    for tick in range(parameters['horizon'] + 1, parameters["ticks"] +
                      parameters['horizon'] + 1):  # for init history
        if tick == parameters['horizon'] + 1:
            print('Start of simulation ', seed)

        # update money and stocks history for agents
        for trader in traders:
            trader.var.money.append(trader.var.money[-1])
            trader.var.stocks.append(trader.var.stocks[-1])
            trader.var.wealth.append(
                trader.var.money[-1] + trader.var.stocks[-1] *
                orderbook.tick_close_price[-1])  # TODO debug
            trader.var.weight_fundamentalist.append(
                trader.var.weight_fundamentalist[-1])
            trader.var.weight_chartist.append(trader.var.weight_chartist[-1])
            trader.var.weight_random.append(trader.var.weight_random[-1])

        # sort the traders by wealth to TODo debug
        traders_by_wealth.sort(key=lambda x: x.var.wealth[-1], reverse=True)

        # evolve the fundamental value via random walk process
        fundamental.append(
            max(
                fundamental[-1] +
                parameters["std_fundamental"] * np.random.randn(), 0.1))

        # allow for multiple trades in one day
        for turn in range(parameters["trades_per_tick"]):
            # select random sample of active traders
            active_traders = random.sample(
                traders, int((parameters['trader_sample_size'])))

            mid_price = np.mean(
                [orderbook.highest_bid_price,
                 orderbook.lowest_ask_price])  #TODO debug
            fundamental_component = np.log(fundamental[-1] / mid_price)

            orderbook.returns[-1] = (
                mid_price - orderbook.tick_close_price[-2]
            ) / orderbook.tick_close_price[-2]  #TODO debug
            chartist_component = np.cumsum(
                orderbook.returns[:-len(orderbook.returns) -
                                  1:-1]) / np.arange(
                                      1., float(len(orderbook.returns) + 1))

            for trader in active_traders:
                # Cancel any active orders
                if trader.var.active_orders:
                    for order in trader.var.active_orders:
                        orderbook.cancel_order(order)
                    trader.var.active_orders = []

                def evolve(probability):
                    return random.random() < probability

                # Evolve an expectations parameter by learning from a successful trader or mutate at random
                if evolve(trader.par.learning_ability):
                    wealthy_trader = traders_by_wealth[random.randint(
                        0, parameters['trader_sample_size'])]
                    trader.var.c_share_strat = np.mean([
                        trader.var.c_share_strat,
                        wealthy_trader.var.c_share_strat
                    ])
                else:
                    trader.var.c_share_strat = min(
                        max(
                            trader.var.c_share_strat *
                            (1 + parameters['mutation_intensity'] *
                             np.random.randn()), 0.01), 0.99)

                # update fundamentalist & chartist weights
                total_strat_weight = trader.var.weight_fundamentalist[
                    -1] + trader.var.weight_chartist[-1]
                trader.var.weight_chartist[
                    -1] = trader.var.c_share_strat * total_strat_weight
                trader.var.weight_fundamentalist[-1] = (
                    1 - trader.var.c_share_strat) * total_strat_weight

                # record sentiment in orderbook
                orderbook.sentiment.append(
                    np.array([
                        trader.var.weight_fundamentalist[-1],
                        trader.var.weight_chartist[-1],
                        trader.var.weight_random[-1]
                    ]))

                # Update trader specific expectations
                noise_component = parameters['std_noise'] * np.random.randn()

                # Expectation formation
                trader.exp.returns['stocks'] = (
                    trader.var.weight_fundamentalist[-1] * np.divide(
                        1,
                        float(trader.par.horizon) *
                        parameters["fundamentalist_horizon_multiplier"]) *
                    fundamental_component + trader.var.weight_chartist[-1] *
                    chartist_component[trader.par.horizon - 1] +
                    trader.var.weight_random[-1] * noise_component)
                fcast_price = mid_price * np.exp(trader.exp.returns['stocks'])
                trader.var.covariance_matrix = calculate_covariance_matrix(
                    orderbook.returns[-trader.par.horizon:],
                    parameters["std_fundamental"])

                # employ portfolio optimization algo
                ideal_trader_weights = portfolio_optimization(trader, tick)

                # Determine price and volume
                trader_price = np.random.normal(fcast_price, trader.par.spread)
                position_change = (ideal_trader_weights['stocks'] *
                                   (trader.var.stocks[-1] * trader_price +
                                    trader.var.money[-1])) - (
                                        trader.var.stocks[-1] * trader_price)
                volume = int(div0(position_change, trader_price))

                # Trade:
                if volume > 0:
                    bid = orderbook.add_bid(trader_price, volume, trader)
                    trader.var.active_orders.append(bid)
                elif volume < 0:
                    ask = orderbook.add_ask(trader_price, -volume, trader)
                    trader.var.active_orders.append(ask)

            # Match orders in the order-book
            while True:
                matched_orders = orderbook.match_orders()
                if matched_orders is None:
                    break
                # execute trade
                matched_orders[3].owner.sell(
                    matched_orders[1], matched_orders[0] * matched_orders[1])
                matched_orders[2].owner.buy(
                    matched_orders[1], matched_orders[0] * matched_orders[1])

        # Clear and update order-book history
        orderbook.cleanse_book()
        orderbook.fundamental = fundamental

    return traders, orderbook
Exemplo n.º 4
0
def exuberance_inequality_model(traders, orderbook, parameters, seed=1):
    """
    The main model function of distribution model where trader stocks are tracked.
    :param traders: list of Agent objects
    :param orderbook: object Order book
    :param parameters: dictionary of parameters
    :param seed: integer seed to initialise the random number generators
    :return: list of simulated Agent objects, object simulated Order book
    """
    random.seed(seed)
    np.random.seed(seed)
    fundamental = [parameters["fundamental_value"]]
    orderbook.tick_close_price.append(fundamental[-1])

    traders_by_wealth = [t for t in traders]

    for tick in range(parameters['horizon'] + 1, parameters["ticks"] + parameters['horizon'] + 1):  # for init history
        if tick == parameters['horizon'] + 1:
            print('Start of simulation ', seed)

        # update money and stocks history for agents
        for trader in traders:
            trader.var.money.append(trader.var.money[-1])
            trader.var.stocks.append(trader.var.stocks[-1])
            trader.var.wealth.append(trader.var.money[-1] + trader.var.stocks[-1] * orderbook.tick_close_price[-1])
            trader.var.real_wealth.append(trader.var.money[-1] + trader.var.stocks[-1] * fundamental[-1])

        # sort the traders by wealth to
        traders_by_wealth.sort(key=lambda x: x.var.wealth[-1], reverse=True)

        # For simplicity, the fundamental value does not change.
        fundamental.append(fundamental[-1])

        # select random sample of active traders
        active_traders = random.sample(traders, int((parameters['trader_sample_size'])))

        mid_price = np.mean([orderbook.highest_bid_price, orderbook.lowest_ask_price])
        fundamental_component = np.log(fundamental[-1] / mid_price)

        orderbook.returns[-1] = (mid_price - orderbook.tick_close_price[-2]) / orderbook.tick_close_price[-2]
        chartist_component = np.cumsum(orderbook.returns[:-len(orderbook.returns) - 1:-1]
                                       ) / np.arange(1., float(len(orderbook.returns) + 1))

        for trader in active_traders:
            # Cancel any active orders
            if trader.var.active_orders:
                for order in trader.var.active_orders:
                    orderbook.cancel_order(order)
                trader.var.active_orders = []

            # Update trader specific expectations
            noise_component = parameters['std_noise'] * np.random.randn()

            # Expectation formation
            trader.exp.returns['stocks'] = (
                    trader.var.weight_fundamentalist[-1] * np.divide(1, float(trader.par.horizon) * parameters["fundamentalist_horizon_multiplier"]) * fundamental_component +
                    trader.var.weight_chartist[-1] * chartist_component[trader.par.horizon - 1] +
                    trader.var.weight_random[-1] * noise_component)
            fcast_price = mid_price * np.exp(trader.exp.returns['stocks'])
            trader.var.covariance_matrix = calculate_covariance_matrix(orderbook.returns[-trader.par.horizon:],
                                                                       parameters["std_noise"])

            # employ portfolio optimization algo
            ideal_trader_weights = portfolio_optimization(trader, tick)

            # Determine price and volume
            trader_price = np.random.normal(fcast_price, trader.par.spread)
            position_change = (ideal_trader_weights['stocks'] * (trader.var.stocks[-1] * trader_price + trader.var.money[-1])
                      ) - (trader.var.stocks[-1] * trader_price)
            volume = int(div0(position_change, trader_price))

            # Trade:
            if volume > 0:
                bid = orderbook.add_bid(trader_price, volume, trader)
                trader.var.active_orders.append(bid)
            elif volume < 0:
                ask = orderbook.add_ask(trader_price, -volume, trader)
                trader.var.active_orders.append(ask)

            # Match orders in the order-book
            while True:
                matched_orders = orderbook.match_orders()
                if matched_orders is None:
                    break
                # execute trade
                matched_orders[3].owner.sell(matched_orders[1], matched_orders[0] * matched_orders[1])
                matched_orders[2].owner.buy(matched_orders[1], matched_orders[0] * matched_orders[1])

        # Clear and update order-book history
        orderbook.cleanse_book()
        orderbook.fundamental = fundamental

    print('last mid-price was: ', mid_price)

    return traders, orderbook
def init_objects(parameters, seed):
    """
    Init object for the distribution version of the model
    :param parameters:
    :param seed:
    :return:
    """
    np.random.seed(seed)
    random.seed(seed)

    traders = []
    n_traders = parameters["n_traders"]

    weight_f = (1 - parameters['strat_share_chartists']) * (
        1 - parameters['w_random'])
    weight_c = parameters['strat_share_chartists'] * (1 -
                                                      parameters['w_random'])

    f_points = int(weight_f * 100 * n_traders)
    c_points = int(weight_c * 100 * n_traders)
    r_points = int(parameters['w_random'] * 100 * n_traders)

    # create list of strategy points, shuffle it and divide in equal parts
    strat_points = ['f' for f in range(f_points)] + [
        'c' for c in range(c_points)
    ] + ['r' for r in range(r_points)]
    random.shuffle(strat_points)
    agent_points = np.array_split(strat_points, n_traders)

    max_horizon = parameters[
        'horizon'] * 2  # this is the max horizon of an agent if 100% fundamentalist
    historical_stock_returns = list(
        np.random.normal(0, parameters["std_fundamental"], max_horizon))

    total_stocks = 0

    for idx in range(n_traders):
        weight_fundamentalist = list(agent_points[idx]).count('f') / float(
            len(agent_points[idx]))
        weight_chartist = list(agent_points[idx]).count('c') / float(
            len(agent_points[idx]))
        weight_random = list(agent_points[idx]).count('r') / float(
            len(agent_points[idx]))

        init_stocks = int(np.random.uniform(0, parameters["init_assets"]))
        total_stocks += init_stocks
        init_money = np.random.uniform(
            0, (init_stocks * parameters['fundamental_value'] *
                parameters['money_multiplier']))

        c_share_strat = div0(weight_chartist,
                             (weight_fundamentalist + weight_chartist))

        # initialize co_variance_matrix
        init_covariance_matrix = calculate_covariance_matrix(
            historical_stock_returns)
        init_active_orders = []

        lft_vars = TraderVariables(weight_fundamentalist, weight_chartist,
                                   weight_random, c_share_strat, init_money,
                                   init_stocks, init_covariance_matrix,
                                   parameters['fundamental_value'],
                                   init_active_orders)

        # determine heterogeneous horizon and risk aversion
        individual_horizon = np.random.randint(10, parameters['horizon'])

        individual_risk_aversion = abs(
            np.random.normal(parameters["base_risk_aversion"],
                             parameters["base_risk_aversion"] / 5.0))
        individual_learning_ability = min(
            abs(np.random.normal(parameters['average_learning_ability'], 0.1)),
            1.0)

        lft_params = TraderParameters(individual_horizon,
                                      individual_risk_aversion,
                                      individual_learning_ability,
                                      parameters['spread_max'])

        exp_returns = {'risky_asset': 0.0, 'money': 0.0}
        lft_expectations = TraderExpectations(parameters['fundamental_value'],
                                              exp_returns)
        traders.append(Trader(idx, lft_vars, lft_params, lft_expectations))

    # initialize central bank with assets at target
    asset_target_cb = int(parameters["qe_perc_size"] * total_stocks)
    asset_target = [asset_target_cb for t in range(parameters['ticks'])]

    ## TODO new
    # Determine QE volume
    if parameters["qe_end"] - parameters["qe_start"] > 0:
        QE_periods = parameters["qe_end"] - parameters["qe_start"]
        total_QE_volume = parameters["qe_perc_size"] * total_stocks
        period_volume = int(total_QE_volume / QE_periods)

        #asset_target = [0 for t in range(parameters['ticks'])]
        for t in range(parameters['ticks']):
            if t in range(parameters["qe_start"], parameters["qe_end"]):
                asset_target[t] = asset_target[t - 1] + period_volume
            elif t >= parameters["qe_end"]:
                asset_target[t] = asset_target[t - 1]
        ##

    cb_assets = [asset_target_cb for t in range(parameters['ticks'])]

    currency = np.zeros(parameters["ticks"])
    currency -= np.array(cb_assets) * parameters["fundamental_value"]

    asset_demand = 0

    init_active_orders_cb = []

    cb_pars = CBParameters(0.0)
    cb_vars = CBVariables(cb_assets, currency, asset_demand, asset_target,
                          init_active_orders_cb)

    central_bank = CentralBank(cb_vars, cb_pars)

    # initialize the order book
    order_book = LimitOrderBook(parameters['fundamental_value'],
                                parameters["std_fundamental"], max_horizon,
                                parameters['ticks'])
    order_book.returns = list(historical_stock_returns)
    order_book.qe_period = [False for t in range(parameters['ticks'])]
    order_book.qt_period = [False for t in range(parameters['ticks'])]

    return traders, central_bank, order_book
Exemplo n.º 6
0
def qe_model(traders,
             central_bank,
             orderbook,
             parameters,
             scenario=None,
             seed=1):
    """
    The main model function of distribution model where trader stocks are tracked.
    :param traders: list of Agent objects
    :param orderbook: object Order book
    :param parameters: dictionary of parameters
    :param scenario: can be the following strings: BUSTQE, BUSTQT, BOOMQE, BOOMQT
    :param seed: integer seed to initialise the random number generators
    :return: list of simulated Agent objects, object simulated Order book
    """
    random.seed(seed)
    np.random.seed(seed)

    fundamentals = [parameters["fundamental_value"]]

    orderbook.tick_close_price.append(fundamentals[-1])

    traders_by_wealth = [t for t in traders]

    for tick in range(parameters['horizon'] + 1,
                      parameters["ticks"] + parameters['horizon'] + 1):
        qe_tick = tick - parameters[
            'horizon'] - 1  # correcting for the memory period necessary for traders

        if tick == parameters['horizon'] + 1:
            print('Start of simulation ', seed)

        print(tick)
        # if tick == 419:
        #     print('debug')

        # update money and stocks history for agents
        for trader in traders:
            trader.var.money.append(trader.var.money[-1])
            trader.var.assets.append(trader.var.assets[-1])  #TODO debug
            trader.var.wealth.append(trader.var.money[-1] +
                                     trader.var.assets[-1] *
                                     orderbook.tick_close_price[-1])
            trader.var.weight_fundamentalist.append(
                trader.var.weight_fundamentalist[-1])
            trader.var.weight_chartist.append(trader.var.weight_chartist[-1])
            trader.var.weight_random.append(trader.var.weight_random[-1])

        # update money and assets for central bank
        central_bank.var.assets[qe_tick] = central_bank.var.assets[qe_tick - 1]
        if scenario in ['BUSTQE', 'BUSTQT', 'BOOMQE', 'BOOMQT', 'BLR']:
            central_bank.var.asset_target[
                qe_tick] = central_bank.var.asset_target[qe_tick - 1]
        central_bank.var.currency[qe_tick] = central_bank.var.currency[qe_tick
                                                                       - 1]

        # sort the traders by wealth to
        traders_by_wealth.sort(key=lambda x: x.var.wealth[-1], reverse=True)

        # evolve the fundamental value via random walk process
        if True:  #TODO decide what to use.
            fundamentals.append(
                max(
                    ornstein_uhlenbeck_evolve(
                        parameters["fundamental_value"], fundamentals[-1],
                        parameters["std_fundamental"],
                        parameters['bond_mean_reversion'], seed), 0.1))
        else:
            fundamentals.append(
                max(
                    fundamentals[-1] +
                    parameters["std_fundamentals"] * np.random.randn(), 0.1))

        # allow for multiple trades in one day
        for turn in range(parameters["trades_per_tick"]):
            # Allow the central bank to do Quantitative Easing ####################################################
            if central_bank.var.active_orders:
                for order in central_bank.var.active_orders:
                    orderbook.cancel_order(order)
                    central_bank.var.active_orders = []

            # calculate PF ratio
            mid_price = np.mean(
                [orderbook.highest_bid_price, orderbook.lowest_ask_price])

            pf = mid_price / fundamentals[-1]
            # if pf is too high, decrease asset target otherwise increase asset target
            quantity_available = int(central_bank.var.assets[qe_tick] *
                                     parameters['cb_size'])  # TODO debug

            if scenario == 'BUSTQE':
                if pf < 1 - parameters['cb_pf_range']:
                    # buy assets
                    central_bank.var.asset_target[
                        qe_tick] += quantity_available
            elif scenario == 'BUSTQT':
                if pf < 1 - parameters['cb_pf_range']:
                    # sell assets
                    central_bank.var.asset_target[
                        qe_tick] -= quantity_available
                    central_bank.var.asset_target[qe_tick] = max(
                        central_bank.var.asset_target[qe_tick], 0)

            elif scenario == 'BOOMQE':
                if pf > 1 + parameters['cb_pf_range']:
                    # buy assets
                    central_bank.var.asset_target[
                        qe_tick] += quantity_available

            elif scenario == 'BOOMQT':
                if pf > 1 + parameters['cb_pf_range']:
                    # sell assets
                    central_bank.var.asset_target[
                        qe_tick] -= quantity_available
                    central_bank.var.asset_target[qe_tick] = max(
                        central_bank.var.asset_target[qe_tick], 0)

            elif scenario == 'BLR':
                if pf > 1 + parameters['cb_pf_range']:
                    # sell assets
                    central_bank.var.asset_target[
                        qe_tick] -= quantity_available
                    central_bank.var.asset_target[qe_tick] = max(
                        central_bank.var.asset_target[qe_tick], 0)
                elif pf < 1 - parameters['cb_pf_range']:
                    # buy assets
                    central_bank.var.asset_target[
                        qe_tick] += quantity_available

            # determine demand
            cb_demand = int(central_bank.var.asset_target[qe_tick] -
                            central_bank.var.assets[qe_tick])  # TODO debug

            # Submit QE orders:
            if cb_demand > 0:
                bid = orderbook.add_bid(orderbook.lowest_ask_price, cb_demand,
                                        central_bank)
                central_bank.var.active_orders.append(bid)
                print('cb QE')
                orderbook.qe_period[qe_tick] = True
            elif cb_demand < 0:
                ask = orderbook.add_ask(orderbook.highest_bid_price,
                                        -cb_demand, central_bank)
                central_bank.var.active_orders.append(ask)
                print('cb QT')
                orderbook.qt_period[qe_tick] = True

            # END QE ##############################################################################################
            # select random sample of active traders
            active_traders = random.sample(
                traders, int((parameters['trader_sample_size'])))

            orderbook.returns[-1] = (
                mid_price - orderbook.tick_close_price[-2]
            ) / orderbook.tick_close_price[-2]  #todo is this correct

            fundamental_component = np.log(fundamentals[-1] / mid_price)
            chartist_component = np.cumsum(
                orderbook.returns[:-len(orderbook.returns) -
                                  1:-1]) / np.arange(
                                      1., float(len(orderbook.returns) + 1))

            for trader in active_traders:
                # Cancel any active orders
                if trader.var.active_orders:
                    for order in trader.var.active_orders:
                        orderbook.cancel_order(order)
                    trader.var.active_orders = []

                def evolve(probability):
                    return random.random() < probability

                # Evolve an expectations parameter by learning from a successful trader or mutate at random
                if evolve(trader.par.learning_ability):
                    wealthy_trader = traders_by_wealth[random.randint(
                        0, parameters['trader_sample_size'])]
                    trader.var.c_share_strat = np.mean([
                        trader.var.c_share_strat,
                        wealthy_trader.var.c_share_strat
                    ])
                else:
                    trader.var.c_share_strat = min(
                        max(
                            trader.var.c_share_strat *
                            (1 + parameters['mutation_intensity'] *
                             np.random.randn()), 0.01), 0.99)

                # update fundamentalist & chartist weights
                total_strat_weight = trader.var.weight_fundamentalist[
                    -1] + trader.var.weight_chartist[-1]
                trader.var.weight_chartist[
                    -1] = trader.var.c_share_strat * total_strat_weight
                trader.var.weight_fundamentalist[-1] = (
                    1 - trader.var.c_share_strat) * total_strat_weight

                # record sentiment in orderbook
                orderbook.sentiment.append(
                    np.array([
                        trader.var.weight_fundamentalist[-1],
                        trader.var.weight_chartist[-1],
                        trader.var.weight_random[-1]
                    ]))

                # Update trader specific expectations
                noise_component = parameters['std_noise'] * np.random.randn()

                # Expectation formation
                #TODO fix below exp returns per stock asset
                trader.exp.returns['risky_asset'] = (
                    trader.var.weight_fundamentalist[-1] * np.divide(
                        1,
                        float(trader.par.horizon) *
                        parameters["fundamentalist_horizon_multiplier"]) *
                    fundamental_component + trader.var.weight_chartist[-1] *
                    chartist_component[trader.par.horizon - 1] +
                    trader.var.weight_random[-1] * noise_component)

                fcast_price = mid_price * np.exp(
                    trader.exp.returns['risky_asset'])

                obs_rets = orderbook.returns[-trader.par.horizon:]
                if sum(np.abs(obs_rets)) > 0:
                    observed_returns = obs_rets
                else:
                    observed_returns = np.diff(
                        fundamentals)[-trader.par.horizon:]

                trader.var.covariance_matrix = calculate_covariance_matrix(
                    observed_returns)  #TODo debug, does this work as intended?

                # employ portfolio optimization algo TODO if observed returns 0 (replace with stdev fundamental?
                ideal_trader_weights = portfolio_optimization(
                    trader,
                    tick)  #TODO debug, does this still work as intended

                # Determine price and volume
                trader_price = np.random.normal(fcast_price, trader.par.spread)
                position_change = (ideal_trader_weights['risky_asset'] *
                                   (trader.var.assets[-1] * trader_price +
                                    trader.var.money[-1])) - (
                                        trader.var.assets[-1] * trader_price)
                volume = int(div0(position_change, trader_price))

                # Trade:
                if volume > 0:
                    volume = min(
                        volume, int(div0(trader.var.money[-1], trader_price))
                    )  # the trader can only buy new stocks with money
                    bid = orderbook.add_bid(trader_price, volume, trader)
                    trader.var.active_orders.append(bid)
                elif volume < 0:
                    volume = max(
                        volume, -trader.var.assets[-1]
                    )  # the trader can only sell as much assets as it owns
                    ask = orderbook.add_ask(trader_price, -volume, trader)
                    trader.var.active_orders.append(ask)

            # Match orders in the order-book
            while True:
                matched_orders = orderbook.match_orders()
                if matched_orders is None:
                    break
                # execute trade
                matched_orders[3].owner.sell(
                    matched_orders[1], matched_orders[0] * matched_orders[1],
                    qe_tick)
                matched_orders[2].owner.buy(
                    matched_orders[1], matched_orders[0] * matched_orders[1],
                    qe_tick)

        # Clear and update order-book history
        orderbook.cleanse_book()
        orderbook.fundamental = fundamentals

    return traders, central_bank, orderbook
Exemplo n.º 7
0
def ABM_model(traders, orderbook, market_maker, parameters, seed=1):
    """
    The main model function of distribution model where trader stocks are tracked.
    :param traders: list of Agent objects
    :param orderbook: object Order book
    :param parameters: dictionary of parameters
    :param seed: integer seed to initialise the random number generators
    :return: list of simulated Agent objects, object simulated Order book
    """
    random.seed(seed)
    np.random.seed(seed)
    fundamental = [parameters["fundamental_value"]]
    orderbook.tick_close_price.append(fundamental[-1])

    traders_by_wealth = [t for t in traders]
    initial_mm_wealth = market_maker.var.wealth[0]

    for tick in range(parameters['horizon'] + 1, parameters["ticks"] + parameters['horizon'] + 1): # for init history
        if tick == parameters['horizon'] + 1:
            print('Start of simulation ', seed)

        # update money and stocks history for agents
        for trader in traders:
            trader.var.money.append(trader.var.money[-1])
            trader.var.stocks.append(trader.var.stocks[-1])
            trader.var.wealth.append(trader.var.money[-1] + trader.var.stocks[-1] * orderbook.tick_close_price[-1])

        #TODO Jakob and / or Adrien update variables of the market maker here (similar to above)

        # sort the traders by wealth to
        traders_by_wealth.sort(key=lambda x: x.var.wealth[-1], reverse=True)

        # evolve the fundamental value via random walk process
        fundamental.append(max(
            ornstein_uhlenbeck_evolve(parameters["fundamental_value"], fundamental[-1], parameters["std_fundamental"],
                                      parameters['mean_reversion'], seed), 0.1))

        # allow for multiple trades in one day
        for turn in range(parameters["trades_per_tick"]):
            # select random sample of active traders
            active_traders = random.sample(traders, int((parameters['trader_sample_size'])))

            mid_price = np.mean([orderbook.highest_bid_price, orderbook.lowest_ask_price])
            fundamental_component = np.log(fundamental[-1] / mid_price)

            orderbook.returns[-1] = (mid_price - orderbook.tick_close_price[-2]) / orderbook.tick_close_price[-2]
            chartist_component = np.cumsum(orderbook.returns[:-len(orderbook.returns) - 1:-1]
                                           ) / np.arange(1., float(len(orderbook.returns) + 1))

            # Market maker quotes best ask and bid whenever money/inventory permits
            # TODO Jakob / Adrien make the market maker use stock / money / wealth data over time (see traders)
            inventory = market_maker.var.stocks[0]
            inventory_value = mid_price*inventory
            cash = market_maker.var.money[0]
            wealth = cash + inventory_value
            print(f"Market maker has money {cash} and stocks {inventory}")
            print(f"Stocks are worth {inventory_value} and thus MM's wealth is {wealth}")
            print(f"Profit margin thus far is {round(wealth/initial_mm_wealth-1,2)*100}%")

            if cash > 0:
                bid = orderbook.add_bid(orderbook.highest_bid_price, 1, market_maker)
                market_maker.var.active_orders.append(bid)
            if inventory > 0:
                ask = orderbook.add_ask(orderbook.lowest_ask_price, 1, market_maker)
                market_maker.var.active_orders.append(ask)

            for trader in active_traders:
                # Cancel any active orders
                if trader.var.active_orders:
                    for order in trader.var.active_orders:
                        orderbook.cancel_order(order)
                    trader.var.active_orders = []

                # Update trader specific expectations
                noise_component = parameters['std_noise'] * np.random.randn()

                # Expectation formation
                trader.exp.returns['stocks'] = (
                        trader.var.weight_fundamentalist[-1] * np.divide(1, float(trader.par.horizon) * parameters["fundamentalist_horizon_multiplier"]) * fundamental_component +
                        trader.var.weight_chartist[-1] * chartist_component[trader.par.horizon - 1] +
                        trader.var.weight_random[-1] * noise_component)
                fcast_price = mid_price * np.exp(trader.exp.returns['stocks'])
                trader.var.covariance_matrix = calculate_covariance_matrix(orderbook.returns[-trader.par.horizon:],
                                                                           parameters["std_fundamental"])

                # employ portfolio optimization algo
                ideal_trader_weights = portfolio_optimization(trader, tick)

                # Determine price and volume
                trader_price = np.random.normal(fcast_price, trader.par.spread)
                position_change = (ideal_trader_weights['stocks'] * (trader.var.stocks[-1] * trader_price + trader.var.money[-1])
                          ) - (trader.var.stocks[-1] * trader_price)
                volume = int(div0(position_change, trader_price))

                # Trade:
                if volume > 0:
                    bid = orderbook.add_bid(trader_price, volume, trader)
                    trader.var.active_orders.append(bid)
                elif volume < 0:
                    ask = orderbook.add_ask(trader_price, -volume, trader)
                    trader.var.active_orders.append(ask)

            # Match orders in the order-book
            while True:
                matched_orders = orderbook.match_orders()
                if matched_orders is None:
                    break
                # execute trade
                matched_orders[3].owner.sell(matched_orders[1], matched_orders[0] * matched_orders[1])
                matched_orders[2].owner.buy(matched_orders[1], matched_orders[0] * matched_orders[1])

        # Clear and update order-book history
        orderbook.cleanse_book()
        orderbook.fundamental = fundamental

    return traders, orderbook, market_maker
def init_objects(parameters, seed):
    """
    Init object for the distribution version of the model
    :param parameters:
    :param seed:
    :return:
    """
    np.random.seed(seed)
    random.seed(seed)

    traders = []
    n_traders = parameters["n_traders"]

    weight_f = (1 - parameters['strat_share_chartists']) * (
        1 - parameters['w_random'])
    weight_c = parameters['strat_share_chartists'] * (1 -
                                                      parameters['w_random'])

    f_points = int(weight_f * 100 * n_traders)
    c_points = int(weight_c * 100 * n_traders)
    r_points = int(parameters['w_random'] * 100 * n_traders)

    # create list of strategy points, shuffle it and divide in equal parts
    strat_points = ['f' for f in range(f_points)] + [
        'c' for c in range(c_points)
    ] + ['r' for r in range(r_points)]
    random.shuffle(strat_points)
    agent_points = np.array_split(strat_points, n_traders)

    max_horizon = int(
        parameters['horizon'] * parameters['fundamentalist_horizon_multiplier']
    ) + 1  # to offset rounding
    historical_stock_returns = np.random.normal(0, parameters["std_noise"],
                                                max_horizon)

    for idx in range(n_traders):
        weights = []
        for typ in ['f', 'c', 'r']:
            weights.append(
                list(agent_points[idx]).count(typ) /
                float(len(agent_points[idx])))

        init_stocks = int(np.random.uniform(0, parameters["init_stocks"]))
        init_money = np.random.uniform(
            0, (parameters["init_stocks"] * parameters['fundamental_value']))

        # If there are chartists (c) & fundamentalists (f) in the model, keep track of the fraction between c & f.
        if weights[2] < 1.0:
            c_share_strat = div0(weights[1], (weights[0] + weights[1]))
        else:
            c_share_strat = 0.0

        init_covariance_matrix = calculate_covariance_matrix(
            historical_stock_returns, parameters["std_noise"])

        trader_vars = TraderVariables(weights[0], weights[1], weights[2],
                                      c_share_strat, init_money, init_stocks,
                                      init_covariance_matrix,
                                      parameters['fundamental_value'])

        individual_horizon = np.random.randint(10, parameters['horizon'])

        individual_risk_aversion = abs(
            np.random.normal(parameters["base_risk_aversion"],
                             parameters["base_risk_aversion"] / 5.0)
        )  #parameters["base_risk_aversion"] * relative_fundamentalism

        trader_params = TraderParameters(individual_horizon,
                                         individual_risk_aversion,
                                         parameters['spread_max'])
        trader_expectations = TraderExpectations(
            parameters['fundamental_value'])
        traders.append(
            Trader(idx, trader_vars, trader_params, trader_expectations))

    order_book = LimitOrderBook(parameters['fundamental_value'],
                                parameters["std_noise"], max_horizon,
                                parameters['ticks'])

    # initialize order-book returns for initial variance calculations
    order_book.returns = list(historical_stock_returns)

    return traders, order_book