diff --git a/agent/execution/ExecutionAgent.py b/agent/execution/ExecutionAgent.py index b12954f0b..cca80fbc4 100644 --- a/agent/execution/ExecutionAgent.py +++ b/agent/execution/ExecutionAgent.py @@ -27,6 +27,8 @@ def __init__(self, id, name, type, symbol, starting_cash, self.trade = trade self.log_orders = log_orders + self.state = 'AWAITING_WAKEUP' + def kernelStopping(self): super().kernelStopping() if self.trade: @@ -79,17 +81,18 @@ def handleOrderAcceptance(self, currentTime, msg): log_print('[---- {} - {} ----]: ACCEPTED QUANTITY : {}'.format(self.name, currentTime, accepted_qty)) def placeOrders(self, currentTime): - if currentTime == self.execution_time_horizon[-2]: + if currentTime.floor('1s') == self.execution_time_horizon[-2]: self.placeMarketOrder(symbol=self.symbol, quantity=self.rem_quantity, is_buy_order=self.direction == 'BUY') - elif currentTime in self.execution_time_horizon[:-2]: + elif currentTime.floor('1s') in self.execution_time_horizon[:-2]: bid, _, ask, _ = self.getKnownBidAsk(self.symbol) - if currentTime == self.start_time: + if currentTime.floor('1s') == self.start_time: self.arrival_price = (bid + ask) / 2 log_print("[---- {} - {} ----]: Arrival Mid Price {}".format(self.name, currentTime, self.arrival_price)) - qty = self.schedule[pd.Interval(currentTime, currentTime+datetime.timedelta(minutes=1))] + qty = self.schedule[pd.Interval(currentTime.floor('1s'), + currentTime.floor('1s')+datetime.timedelta(minutes=1))] price = ask if self.direction == 'BUY' else bid self.placeLimitOrder(symbol=self.symbol, quantity=qty, is_buy_order=self.direction == 'BUY', limit_price=price) diff --git a/agent/execution/PassiveAgent.py b/agent/execution/PassiveAgent.py deleted file mode 100644 index ea698cfb6..000000000 --- a/agent/execution/PassiveAgent.py +++ /dev/null @@ -1,53 +0,0 @@ -from agent.TradingAgent import TradingAgent -from util.util import log_print - - -class PassiveAgent(TradingAgent): - """ - PassiveAgent class representing an agent placing LIMIT orders in the order book - Attributes: - symbol (str): Name of the stock traded - timestamp (datetime): order placement time stamp - direction (str): order direction ('BUY' or 'SELL') - quantity (int): order quantity - limit_price (float): order limit price - log_orders (bool): log the order(s) placed - """ - - def __init__(self, id, name, type, symbol, starting_cash, - timestamp, direction, quantity, limit_price=None, - log_orders=False, random_state=None): - super().__init__(id, name, type, starting_cash=starting_cash, log_orders=log_orders, random_state=random_state) - self.symbol = symbol - self.timestamp = timestamp - self.direction = direction - self.quantity = quantity - self.limit_price = limit_price - self.log_orders = log_orders - self.state = 'AWAITING_WAKEUP' - - def wakeup(self, currentTime): - can_trade = super().wakeup(currentTime) - if not can_trade: return - elif currentTime == self.timestamp: - if self.limit_price: - self.placeLimitOrder(symbol=self.symbol, quantity=self.quantity, - is_buy_order=self.direction == 'BUY', limit_price=self.limit_price) - log_print('[---- {} - {} ----]: LIMIT ORDER PLACED - {} @ {}'.format(self.name, currentTime, - self.quantity, self.limit_price)) - else: - self.getCurrentSpread(self.symbol) - self.state = 'AWAITING_SPREAD' - - def receiveMessage(self, currentTime, msg): - super().receiveMessage(currentTime, msg) - if self.state == 'AWAITING_SPREAD' and msg.body['msg'] == 'QUERY_SPREAD': - bid, _, ask, _ = self.getKnownBidAsk(self.symbol) - limit_price = bid if self.direction == 'BUY' else ask - self.placeLimitOrder(symbol=self.symbol, quantity=self.quantity, - is_buy_order=self.direction == 'BUY', limit_price=limit_price) - log_print('[---- {} - {} ----]: LIMIT ORDER PLACED - {} @ {}'.format(self.name, currentTime, - self.quantity, limit_price)) - - def getWakeFrequency(self): - return self.timestamp - self.mkt_open \ No newline at end of file diff --git a/agent/execution/VWAPExecutionAgent.py b/agent/execution/VWAPExecutionAgent.py index 938f4f887..8058d2306 100644 --- a/agent/execution/VWAPExecutionAgent.py +++ b/agent/execution/VWAPExecutionAgent.py @@ -43,7 +43,7 @@ def generate_schedule(self): def synthetic_volume_profile(date, freq): mkt_open = pd.to_datetime(date.date()) + pd.to_timedelta('09:30:00') mkt_close = pd.to_datetime(date.date()) + pd.to_timedelta('16:00:00') - day_range = pd.date_range(mkt_open, mkt_close, freq=f'{freq}s') + day_range = pd.date_range(mkt_open, mkt_close, freq=freq) vol_profile = {} for t, x in zip(day_range, range(int(-len(day_range) / 2), int(len(day_range) / 2), 1)): diff --git a/agent/execution/util.py b/agent/execution/util.py deleted file mode 100644 index a146ecad3..000000000 --- a/agent/execution/util.py +++ /dev/null @@ -1,154 +0,0 @@ -import pandas as pd -import numpy as np -import matplotlib.pyplot as plt - - -def synthetic_volume_profile(date, freq): - mkt_open = pd.to_datetime(date.date()) + pd.to_timedelta('09:30:00') - mkt_close = pd.to_datetime(date.date()) + pd.to_timedelta('16:00:00') - day_range = pd.date_range(mkt_open, mkt_close, freq=f'{freq}s') - - vol_profile = {} - for t, x in zip(day_range, range(int(-len(day_range) / 2), int(len(day_range) / 2), 1)): - vol_profile[t] = x ** 2 + 2 * x + 2 - - factor = 1.0 / sum(vol_profile.values()) - vol_profile = {k: v * factor for k, v in vol_profile.items()} - return vol_profile - - -class Metrics: - """ Utility class to calculate metrics associated with the order book orders and snapshots. - Attributes: - orderbook_df: A pandas Dataframe describing the order book snapshots in time - orders_df: A pandas Dataframe describing the orders stream - """ - - def __init__(self, symbol, date, orderbook_df, orders_df, bps=False): - self.symbol = symbol - self.date = date - self.orderbook_df = orderbook_df - self.orders_df = orders_df - self.bps = bps - - def mid_price(self): - """Returns the mid price in the form of a pandas series (Orderbook specific)""" - mid_price = (self.orderbook_df.ask_price_1 + self.orderbook_df.bid_price_1) / 2 - return mid_price * 10000 if self.bps else mid_price - - def spread(self, type='naive'): - """Returns the spread in the form of a pandas series (Orderbook specific)""" - spread = None - if type == 'naive': - spread = self.orderbook_df.ask_price_1 - self.orderbook_df.bid_price_1 - elif type == 'effective': - num_price_levels = 5 - volume_weighted_ask = sum([self.orderbook_df[f'ask_price_{level}'] * self.orderbook_df[f'ask_size_{level}'] - for level in range(1, num_price_levels + 1)]) / \ - sum(self.orderbook_df[f'ask_size_{level}'] for level in range(1, num_price_levels + 1)) - volume_weighted_bid = sum([self.orderbook_df[f'bid_price_{level}'] * self.orderbook_df[f'bid_size_{level}'] - for level in range(1, num_price_levels + 1)]) / \ - sum(self.orderbook_df[f'bid_size_{level}'] for level in range(1, num_price_levels + 1)) - spread = volume_weighted_ask.ffill() - volume_weighted_bid.ffill() - return spread * 10000 if self.bps else spread - - def volume_order_imbalance(self): - """Returns the volume order imbalance in the form of a pandas series (Orderbook specific)""" - return self.orderbook_df.bid_size_1 / (self.orderbook_df.ask_size_1 + self.orderbook_df.bid_size_1) - - def order_flow_imbalance(self, tick_size=0.01, sampling_freq='1ms'): - """Returns the order flow imbalance in the form of a pandas series (Orderbook specific)""" - ofi_df = self.orderbook_df[['ask_price_1', 'ask_size_1', 'bid_price_1', 'bid_size_1']].copy().reset_index() - ofi_df['mid_price_change'] = ((ofi_df['bid_price_1'] + ofi_df['ask_price_1']) / 2).diff().div(tick_size) - ofi_df['PB_prev'] = ofi_df['bid_price_1'].shift() - ofi_df['SB_prev'] = ofi_df['bid_size_1'].shift() - ofi_df['PA_prev'] = ofi_df['ask_price_1'].shift() - ofi_df['SA_prev'] = ofi_df['ask_size_1'].shift() - ofi_df = ofi_df.dropna() - bid_geq = ofi_df['bid_price_1'] >= ofi_df['PB_prev'] - bid_leq = ofi_df['bid_price_1'] <= ofi_df['PB_prev'] - ask_geq = ofi_df['ask_price_1'] >= ofi_df['PA_prev'] - ask_leq = ofi_df['ask_price_1'] <= ofi_df['PA_prev'] - ofi_df['OFI'] = pd.Series(np.zeros(len(ofi_df))) - ofi_df['OFI'].loc[bid_geq] += ofi_df['bid_size_1'][bid_geq] - ofi_df['OFI'].loc[bid_leq] -= ofi_df['SB_prev'][bid_leq] - ofi_df['OFI'].loc[ask_geq] += ofi_df['SA_prev'][ask_geq] - ofi_df['OFI'].loc[ask_leq] -= ofi_df['ask_size_1'][ask_leq] - ofi_df = ofi_df.set_index('index') - ofi_df = ofi_df[['mid_price_change', 'OFI']].resample(sampling_freq).sum().dropna() - return ofi_df.OFI - - @staticmethod - def twap(executed_trades_df): - return executed_trades_df['fill_price'].cumsum() / pd.Series(np.arange(1, len(executed_trades_df) + 1), - executed_trades_df.index) - @staticmethod - def vwap(executed_trades_df): - return (executed_trades_df['fill_price'] * executed_trades_df['quantity']).cumsum() / executed_trades_df[ - 'quantity'].cumsum() - - @staticmethod - def slippage(exchange_executed_trades_df, agent_executed_trades_df, trade_direction, benchmark='VWAP', bps=True): - direction = 1 if trade_direction == 'BUY' else -1 - if benchmark == 'VWAP': - slippage = direction * (Metrics.vwap(exchange_executed_trades_df) - Metrics.vwap(agent_executed_trades_df)) / \ - Metrics.vwap(exchange_executed_trades_df) - elif benchmark == 'TWAP': - slippage = direction * (Metrics.twap(exchange_executed_trades_df) - Metrics.twap(agent_executed_trades_df)) / \ - Metrics.twap(exchange_executed_trades_df) - slippage = slippage[~slippage.isnull()] - return slippage * 10000 if bps else slippage - - -class Plots: - """ Utility class to plot metrics associated with the order book orders and snapshots. - Attributes: - orderbook_df: A pandas Dataframe describing the order book snapshots in time - orders_df: A pandas Dataframe describing the orders stream - """ - - def __init__(self, symbol, date, orderbook_df, orders_df, log_folder, bps=False): - self.log_folder = log_folder - self.metrics = Metrics(symbol, date, orderbook_df, orders_df, bps) - - def plot_mid_price(self, bps=False): - fig, ax = plt.subplots(nrows=1, ncols=1) - fig.set_size_inches(30, 10) - ax.set_title(f"Mid Price ({'bps' if bps else '$'})", size=24, fontweight='bold') - ax.set_xlabel("Time", size=20) - ax.set_ylabel("Mid Price", size=20) - ax.set_facecolor("white") - ax.plot(self.metrics.mid_price()) - plt.savefig(self.log_folder + '/' + 'Mid_Price.jpg', bbox_inches='tight') - - def plot_spread(self, bps=True): - fig, ax = plt.subplots(nrows=1, ncols=1) - fig.set_size_inches(30, 10) - ax.set_title(f"Spread ({'bps' if bps else '$'})", size=24, fontweight='bold') - ax.set_xlabel("Time", size=20) - ax.set_ylabel("Spread", size=20) - ax.set_facecolor("white") - ax.plot(self.metrics.spread(type='naive'), label='Naive Spread') - ax.plot(self.metrics.spread(type='effective'), label='Effective Spread') - ax.legend() - plt.savefig(self.log_folder + '/' + 'Spread.jpg', bbox_inches='tight') - - def plot_volume_order_imbalance(self): - fig, ax = plt.subplots(nrows=1, ncols=1) - fig.set_size_inches(30, 10) - ax.set_title("Volume Order Imbalance", size=24, fontweight='bold') - ax.set_xlabel("Time", size=20) - ax.set_ylabel("Volume Order Imbalance", size=20) - ax.set_facecolor("white") - ax.plot(self.metrics.volume_order_imbalance()) - plt.savefig(self.log_folder + '/' + 'Volume_Order_Imbalance.jpg', bbox_inches='tight') - - def plot_order_flow_imbalance(self): - fig, ax = plt.subplots(nrows=1, ncols=1) - fig.set_size_inches(30, 10) - ax.set_title("Order Flow Imbalance", size=24, fontweight='bold') - ax.set_xlabel("Time", size=20) - ax.set_ylabel("Order Flow Imbalance", size=20) - ax.set_facecolor("white") - ax.plot(self.metrics.order_flow_imbalance()) - plt.savefig(self.log_folder + '/' + 'Order_Flow_Imbalance.jpg', bbox_inches='tight') \ No newline at end of file diff --git a/config/execution_iabs_plots.py b/config/execution.py similarity index 52% rename from config/execution_iabs_plots.py rename to config/execution.py index 255d96dc2..9876fc442 100644 --- a/config/execution_iabs_plots.py +++ b/config/execution.py @@ -5,25 +5,27 @@ import datetime as dt from dateutil.parser import parse +from Kernel import Kernel +from util import util +from util.order import LimitOrder +from util.oracle.SparseMeanRevertingOracle import SparseMeanRevertingOracle +from util.oracle.ExternalFileOracle import ExternalFileOracle +from model.LatencyModel import LatencyModel + from agent.ExchangeAgent import ExchangeAgent from agent.NoiseAgent import NoiseAgent from agent.ValueAgent import ValueAgent -from agent.market_makers.MarketMakerAgent import MarketMakerAgent +from agent.market_makers.AdaptiveMarketMakerAgent import AdaptiveMarketMakerAgent from agent.examples.MomentumAgent import MomentumAgent from agent.execution.TWAPExecutionAgent import TWAPExecutionAgent from agent.execution.VWAPExecutionAgent import VWAPExecutionAgent from agent.execution.POVExecutionAgent import POVExecutionAgent -from Kernel import Kernel -from util import util -from util.order import LimitOrder -from util.oracle.ExternalFileOracle import ExternalFileOracle - ######################################################################################################################## ############################################### GENERAL CONFIG ######################################################### -parser = argparse.ArgumentParser(description='Detailed options for market replay config.') +parser = argparse.ArgumentParser(description='Detailed options for the config.') parser.add_argument('-c', '--config', @@ -39,17 +41,12 @@ help='historical date being simulated in format YYYYMMDD.') parser.add_argument('-f', '--fundamental-file-path', - required=True, + required=False, help="Path to external fundamental file.") parser.add_argument('-e', '--execution_agents', action='store_true', help='Flag to add the execution agents') -parser.add_argument('-p', - '--pov', - type=float, - default=0.1, - help='Participation of Volume level') parser.add_argument('-s', '--seed', type=int, @@ -66,8 +63,6 @@ parser.add_argument('--config_help', action='store_true', help='Print argument options for this config file') -parser.add_argument('--wide-book', action='store_true', - help='Store orderbook in `wide` format') args, remaining_args = parser.parse_known_args() @@ -89,9 +84,9 @@ ######################## Agents Config ######################################################################### # Historical date to simulate. -historical_date_pd = pd.to_datetime(args.historical_date) -mkt_open = historical_date_pd + pd.to_timedelta('10:45:00') -mkt_close = historical_date_pd + pd.to_timedelta('11:45:00') +historical_date = pd.to_datetime(args.historical_date) +mkt_open = historical_date + pd.to_timedelta('09:30:00') +mkt_close = historical_date + pd.to_timedelta('11:30:00') agent_count, agents, agent_types = 0, [], [] @@ -99,24 +94,42 @@ symbol = args.ticker starting_cash = 10000000 # Cash in this simulator is always in CENTS. -# Oracle +# Choice between two oracles for generating the fundamental time series +# (1) Sparse Mean Reverting Oracle + +r_bar = 1e5 +symbols = {symbol: {'r_bar': r_bar, + 'kappa': 1.67e-16, + 'sigma_s': 0, + 'fund_vol': 1e-8, # volatility of fundamental time series. + 'megashock_lambda_a': 2.77778e-18, + 'megashock_mean': 1e3, + 'megashock_var': 5e4, + 'random_state': np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32))}} + +oracle = SparseMeanRevertingOracle(mkt_open, mkt_close, symbols) + + +# (2) External File Oracle +""" symbols = { - symbol : { + symbol: { 'fundamental_file_path': args.fundamental_file_path, - 'random_state': np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32, dtype='uint64')) + 'random_state': np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32)) } } oracle = ExternalFileOracle(symbols) +r_bar = oracle.fundamentals[symbol].values[0] +""" -r_bar = util.get_value_from_timestamp(oracle.fundamentals[symbol], mkt_open) - -sigma_n = r_bar / 10 -kappa = 1.67e-15 -lambda_a = 7e-13 +# Agents: # 1) Exchange Agent +# How many orders in the past to store for transacted volume computation +# stream_history_length = int(pd.to_timedelta(args.mm_wake_up_freq).total_seconds() * 100) +stream_history_length = 25000 agents.extend([ExchangeAgent(id=0, - name="EXCHANGE_AGENT", + name="ExchangeAgent", type="ExchangeAgent", mkt_open=mkt_open, mkt_close=mkt_close, @@ -124,23 +137,26 @@ log_orders=True, pipeline_delay=0, computation_delay=0, - stream_history=sys.maxsize, + stream_history=stream_history_length, book_freq=0, - wide_book=args.wide_book, - random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32, - dtype='uint64')))]) + wide_book=True, + random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32)))]) agent_types.extend("ExchangeAgent") agent_count += 1 # 2) Noise Agents num_noise = 5000 +noise_mkt_open = historical_date + pd.to_timedelta("09:00:00") +noise_mkt_close = historical_date + pd.to_timedelta("16:00:00") agents.extend([NoiseAgent(id=j, - name="NOISE_AGENT_{}".format(j), + name="NoiseAgent_{}".format(j), type="NoiseAgent", symbol=symbol, starting_cash=starting_cash, - wakeup_time=util.get_wake_time(mkt_open, mkt_close), - random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32, dtype='uint64'))) + wakeup_time=util.get_wake_time(noise_mkt_open, noise_mkt_close), + log_orders=False, + log_to_file=False, + random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32))) for j in range(agent_count, agent_count + num_noise)]) agent_count += num_noise agent_types.extend(['NoiseAgent']) @@ -148,36 +164,58 @@ # 3) Value Agents num_value = 100 agents.extend([ValueAgent(id=j, - name="VALUE_AGENT_{}".format(j), + name="ValueAgent_{}".format(j), type="ValueAgent", symbol=symbol, starting_cash=starting_cash, - sigma_n=sigma_n, + sigma_n=r_bar / 10, r_bar=r_bar, - kappa=kappa, - lambda_a=lambda_a, - random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32, dtype='uint64'))) + kappa=1.67e-15, + lambda_a=7e-11, + log_orders=False, + log_to_file=False, + random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32))) for j in range(agent_count, agent_count + num_value)]) agent_count += num_value agent_types.extend(['ValueAgent']) -# 4) Market Maker Agent -num_mm_agents = 1 -agents.extend([MarketMakerAgent(id=j, - name="MARKET_MAKER_AGENT_{}".format(j), - type='MarketMakerAgent', - symbol=symbol, - starting_cash=starting_cash, - min_size=200, - max_size=201, - wake_up_freq="1S", - log_orders=False, - random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32, - dtype='uint64'))) - for j in range(agent_count, agent_count + num_mm_agents)]) -agent_count += num_mm_agents -agent_types.extend('MarketMakerAgent') +# 4) Market Maker Agents +""" +window_size == Spread of market maker (in ticks) around the mid price +pov == Percentage of transacted volume seen in previous `mm_wake_up_freq` that + the market maker places at each level +num_ticks == Number of levels to place orders in around the spread +wake_up_freq == How often the market maker wakes up + +""" + +# each elem of mm_params is tuple (window_size, pov, num_ticks, wake_up_freq, min_order_size) +mm_params = [('adaptive', 0.025, 10, '10S', 1), + ('adaptive', 0.025, 10, '10S', 1)] + +num_mm_agents = len(mm_params) + +agents.extend([AdaptiveMarketMakerAgent(id=j, + name="AdaptiveMarketMakerAgent_{}".format(j), + type='AdaptiveMarketMakerAgent', + symbol=symbol, + starting_cash=starting_cash, + pov=mm_params[idx][1], + min_order_size=mm_params[idx][4], + window_size=mm_params[idx][0], + num_ticks=mm_params[idx][2], + wake_up_freq=mm_params[idx][3], + cancel_limit_delay=50, + skew_beta=0, + level_spacing=5, + spread_alpha=0.75, + backstop_quantity=50000, + log_orders=True, + random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32))) + for idx, j in enumerate(range(agent_count, agent_count + num_mm_agents))]) +agent_count += num_mm_agents +agent_types.extend('AdaptiveMarketMakerAgent') # 5) Momentum Agents num_momentum_agents = 25 @@ -188,9 +226,9 @@ starting_cash=starting_cash, min_size=1, max_size=10, - log_orders=False, - random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32, - dtype='uint64'))) + wake_up_freq='20s', + log_orders=True, + random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32))) for j in range(agent_count, agent_count + num_momentum_agents)]) agent_count += num_momentum_agents agent_types.extend("MomentumAgent") @@ -198,94 +236,100 @@ # 6) Execution Agent Config trade = True if args.execution_agents else False -#### Participation of Volume Agent parameters -pov_agent_start_time = '11:00:00' -pov_agent_end_time = '11:30:00' -pov_proportion_of_volume = args.pov -pov_quantity = 12e5 -pov_frequency = '1min' -pov_direction = "BUY" +execution_agent_start_time = historical_date + pd.to_timedelta("10:00:00") +execution_agent_end_time = historical_date + pd.to_timedelta("11:00:00") +execution_quantity = 12e5 +execution_frequency = '1min' +execution_direction = "BUY" +execution_time_horizon = pd.date_range(start=execution_agent_start_time, end=execution_agent_end_time, + freq=execution_frequency) -pov_agent = POVExecutionAgent(id=agent_count, - name='POV_EXECUTION_AGENT', - type='ExecutionAgent', - symbol=symbol, - starting_cash=starting_cash, - start_time=historical_date_pd+pd.to_timedelta(pov_agent_start_time), - end_time=historical_date_pd+pd.to_timedelta(pov_agent_end_time), - freq=pov_frequency, - lookback_period=pov_frequency, - pov=pov_proportion_of_volume, - direction=pov_direction, - quantity=pov_quantity, - trade=trade, - log_orders=True, - random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32, - dtype='uint64'))) - -execution_agents = [pov_agent] - -""" twap_agent = TWAPExecutionAgent(id=agent_count, - name='TWAP_EXECUTION_AGENT', + name='TWAPExecutionAgent', type='ExecutionAgent', symbol=symbol, starting_cash=0, - start_time=historical_date_pd + pd.to_timedelta('11:00:00'), - end_time=historical_date_pd + pd.to_timedelta('13:00:00'), - freq=60, - direction='BUY', - quantity=12e3, + direction=execution_direction, + quantity=execution_quantity, + execution_time_horizon=execution_time_horizon, + freq=execution_frequency, trade=trade, log_orders=True, - random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32, - dtype='uint64'))) + random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32))) execution_agents = [twap_agent] -""" + """ vwap_agent = VWAPExecutionAgent(id=agent_count, - name='VWAP_EXECUTION_AGENT', + name='VWAPExecutionAgent', type='ExecutionAgent', symbol=symbol, starting_cash=0, - start_time=historical_date_pd + pd.to_timedelta('10:00:00'), - end_time=historical_date_pd + pd.to_timedelta('12:00:00'), - freq=60, - direction='BUY', - quantity=12e3, + direction=execution_direction, + quantity=execution_quantity, + execution_time_horizon=execution_time_horizon, + freq=execution_frequency, volume_profile_path=None, trade=trade, log_orders=True, - random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32, - dtype='uint64'))) + random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32))) execution_agents = [vwap_agent] + +pov_agent = POVExecutionAgent(id=agent_count, + name='POVExecutionAgent', + type='ExecutionAgent', + symbol=symbol, + starting_cash=starting_cash, + start_time=execution_agent_start_time, + end_time=execution_agent_end_time, + freq=execution_frequency, + lookback_period=execution_frequency, + pov=0.1, + direction=execution_direction, + quantity=execution_quantity, + trade=trade, + log_orders=True, + random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32))) +execution_agents = [pov_agent] """ + agents.extend(execution_agents) agent_types.extend("ExecutionAgent") agent_count += 1 -print("Number of Agents: {}".format(agent_count)) - ######################################################################################################################## ########################################### KERNEL AND OTHER CONFIG #################################################### -kernel = Kernel("Market Replay Kernel", - random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32, dtype='uint64'))) +kernel = Kernel("Kernel", random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32))) + +kernelStartTime = historical_date +kernelStopTime = mkt_close + pd.to_timedelta('00:01:00') -kernelStartTime = historical_date_pd + pd.to_timedelta('10:44:00') -kernelStopTime = historical_date_pd + pd.to_timedelta('11:46:00') +defaultComputationDelay = 50 # 50 nanoseconds -defaultComputationDelay = 0 -latency = np.zeros((agent_count, agent_count)) -noise = [0.0] +# LATENCY +latency_rstate = np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32)) +pairwise = (agent_count, agent_count) + +# All agents sit on line from Seattle to NYC +nyc_to_seattle_meters = 3866660 +pairwise_distances = util.generate_uniform_random_pairwise_dist_on_line(0.0, nyc_to_seattle_meters, agent_count, + random_state=latency_rstate) +pairwise_latencies = util.meters_to_light_ns(pairwise_distances) + +model_args = { + 'connected': True, + 'min_latency': pairwise_latencies +} +latency_model = LatencyModel(latency_model='deterministic', + random_state=latency_rstate, + kwargs=model_args) +# KERNEL kernel.runner(agents=agents, startTime=kernelStartTime, stopTime=kernelStopTime, - agentLatency=latency, - latencyNoise=noise, + agentLatencyModel=latency_model, defaultComputationDelay=defaultComputationDelay, - defaultLatency=0, oracle=oracle, log_dir=args.log_dir) diff --git a/config/execution_iabs.py b/config/execution_iabs.py deleted file mode 100644 index 8acaca635..000000000 --- a/config/execution_iabs.py +++ /dev/null @@ -1,255 +0,0 @@ -import argparse -import numpy as np -import pandas as pd -import sys -import datetime as dt -from dateutil.parser import parse - -from agent.ExchangeAgent import ExchangeAgent -from agent.NoiseAgent import NoiseAgent -from agent.ValueAgent import ValueAgent -from agent.examples.MarketMakerAgent import MarketMakerAgent -from agent.examples.MomentumAgent import MomentumAgent - -from agent.execution.TWAPExecutionAgent import TWAPExecutionAgent -from agent.execution.VWAPExecutionAgent import VWAPExecutionAgent - -from Kernel import Kernel -from util import util -from util.order import LimitOrder -from util.oracle.ExternalFileOracle import ExternalFileOracle - -######################################################################################################################## -############################################### GENERAL CONFIG ######################################################### - -parser = argparse.ArgumentParser(description='Detailed options for market replay config.') - -parser.add_argument('-c', - '--config', - required=True, - help='Name of config file to execute') -parser.add_argument('-t', - '--ticker', - required=True, - help='Ticker (symbol) to use for simulation') -parser.add_argument('-d', '--historical-date', - required=True, - type=parse, - help='historical date being simulated in format YYYYMMDD.') -parser.add_argument('-f', - '--fundamental-file-path', - required=True, - help="Path to external fundamental file.") -parser.add_argument('-e', - '--execution_agents', - action='store_true', - help='Flag to add the execution agents') -parser.add_argument('-s', - '--seed', - type=int, - default=None, - help='numpy.random.seed() for simulation') -parser.add_argument('-l', - '--log_dir', - default=None, - help='Log directory name (default: unix timestamp at program start)') -parser.add_argument('-v', - '--verbose', - action='store_true', - help='Maximum verbosity!') -parser.add_argument('--config_help', - action='store_true', - help='Print argument options for this config file') - -args, remaining_args = parser.parse_known_args() - -if args.config_help: - parser.print_help() - sys.exit() - -seed = args.seed # Random seed specification on the command line. -if not seed: seed = int(pd.Timestamp.now().timestamp() * 1000000) % (2 ** 32 - 1) -np.random.seed(seed) - -util.silent_mode = not args.verbose -LimitOrder.silent_mode = not args.verbose - -simulation_start_time = dt.datetime.now() -print("Simulation Start Time: {}".format(simulation_start_time)) -print("Configuration seed: {}".format(seed)) - -######################## Agents Config ######################################################################### - -# Historical date to simulate. -historical_date_pd = pd.to_datetime(args.historical_date) -mkt_open = historical_date_pd + pd.to_timedelta('09:30:00') -mkt_close = historical_date_pd + pd.to_timedelta('16:00:00') - -agent_count, agents, agent_types = 0, [], [] - -# Hyperparameters -symbol = args.ticker -starting_cash = 10000000 # Cash in this simulator is always in CENTS. - -# Oracle -symbols = { - symbol : { - 'fundamental_file_path': args.fundamental_file_path, - 'random_state': np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32, dtype='uint64')) - } -} -oracle = ExternalFileOracle(symbols) - -r_bar = oracle.fundamentals[symbol].values[0] -sigma_n = r_bar / 10 -kappa = 1.67e-15 -lambda_a = 1e-12 - -# 1) Exchange Agent -agents.extend([ExchangeAgent(id=0, - name="EXCHANGE_AGENT", - type="ExchangeAgent", - mkt_open=mkt_open, - mkt_close=mkt_close, - symbols=[symbol], - log_orders=True, - pipeline_delay=0, - computation_delay=0, - stream_history=10, - book_freq=0, - random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32, - dtype='uint64')))]) -agent_types.extend("ExchangeAgent") -agent_count += 1 - -# 2) Noise Agents -num_noise = 5000 -agents.extend([NoiseAgent(id=j, - name="NOISE_AGENT_{}".format(j), - type="NoiseAgent", - symbol=symbol, - starting_cash=starting_cash, - wakeup_time=util.get_wake_time(mkt_open, mkt_close), - random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32, dtype='uint64'))) - for j in range(agent_count, agent_count + num_noise)]) -agent_count += num_noise -agent_types.extend(['NoiseAgent']) - -# 3) Value Agents -num_value = 100 -agents.extend([ValueAgent(id=j, - name="VALUE_AGENT_{}".format(j), - type="ValueAgent", - symbol=symbol, - starting_cash=starting_cash, - sigma_n=sigma_n, - r_bar=r_bar, - kappa=kappa, - lambda_a=lambda_a, - random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32, dtype='uint64'))) - for j in range(agent_count, agent_count + num_value)]) -agent_count += num_value -agent_types.extend(['ValueAgent']) - -# 4) Market Maker Agent -num_mm_agents = 1 -agents.extend([MarketMakerAgent(id=j, - name="MARKET_MAKER_AGENT_{}".format(j), - type='MarketMakerAgent', - symbol=symbol, - starting_cash=starting_cash, - min_size=500, - max_size=1000, - wake_up_freq="1min", - log_orders=False, - random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32, - dtype='uint64'))) - for j in range(agent_count, agent_count + num_mm_agents)]) -agent_count += num_mm_agents -agent_types.extend('MarketMakerAgent') - - -# 5) Momentum Agents -num_momentum_agents = 25 -agents.extend([MomentumAgent(id=j, - name="MOMENTUM_AGENT_{}".format(j), - type="MomentumAgent", - symbol=symbol, - starting_cash=starting_cash, - min_size=1, - max_size=10, - log_orders=False, - random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32, - dtype='uint64'))) - for j in range(agent_count, agent_count + num_momentum_agents)]) -agent_count += num_momentum_agents -agent_types.extend("MomentumAgent") - -# 6) Execution Agent Config -trade = True if args.execution_agents else False - -twap_agent = TWAPExecutionAgent(id=agent_count, - name='TWAP_EXECUTION_AGENT', - type='ExecutionAgent', - symbol=symbol, - starting_cash=0, - start_time=historical_date_pd + pd.to_timedelta('11:00:00'), - end_time=historical_date_pd + pd.to_timedelta('13:00:00'), - freq=60, - direction='BUY', - quantity=12e3, - trade=trade, - log_orders=True, - random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32, - dtype='uint64'))) -execution_agents = [twap_agent] -""" -vwap_agent = VWAPExecutionAgent(id=agent_count, - name='VWAP_EXECUTION_AGENT', - type='ExecutionAgent', - symbol=symbol, - starting_cash=0, - start_time=historical_date_pd + pd.to_timedelta('10:00:00'), - end_time=historical_date_pd + pd.to_timedelta('12:00:00'), - freq=60, - direction='BUY', - quantity=12e3, - volume_profile_path=None, - trade=trade, - log_orders=True, - random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32, - dtype='uint64'))) -execution_agents = [vwap_agent] -""" -agents.extend(execution_agents) -agent_types.extend("ExecutionAgent") -agent_count += 1 - -print("Number of Agents: {}".format(agent_count)) - -######################################################################################################################## -########################################### KERNEL AND OTHER CONFIG #################################################### - -kernel = Kernel("Market Replay Kernel", - random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32, dtype='uint64'))) - -kernelStartTime = historical_date_pd -kernelStopTime = historical_date_pd + pd.to_timedelta('16:01:00') - -defaultComputationDelay = 0 -latency = np.zeros((agent_count, agent_count)) -noise = [0.0] - -kernel.runner(agents=agents, - startTime=kernelStartTime, - stopTime=kernelStopTime, - agentLatency=latency, - latencyNoise=noise, - defaultComputationDelay=defaultComputationDelay, - defaultLatency=0, - oracle=oracle, - log_dir=args.log_dir) - -simulation_end_time = dt.datetime.now() -print("Simulation End Time: {}".format(simulation_end_time)) -print("Time taken to run simulation: {}".format(simulation_end_time - simulation_start_time)) \ No newline at end of file diff --git a/config/execution_marketreplay.py b/config/execution_marketreplay.py deleted file mode 100644 index d55a137f9..000000000 --- a/config/execution_marketreplay.py +++ /dev/null @@ -1,193 +0,0 @@ -import argparse -import numpy as np -import pandas as pd -import sys -import datetime as dt - -from agent.ExchangeAgent import ExchangeAgent -from agent.examples.MarketReplayAgent import MarketReplayAgent - -from agent.execution.TWAPExecutionAgent import TWAPExecutionAgent -from agent.execution.VWAPExecutionAgent import VWAPExecutionAgent - -from Kernel import Kernel -from util import util -from util.order import LimitOrder - -######################################################################################################################## -############################################### GENERAL CONFIG ######################################################### - -parser = argparse.ArgumentParser(description='Detailed options for market replay config.') - -parser.add_argument('-c', - '--config', - required=True, - help='Name of config file to execute') -parser.add_argument('-t', - '--ticker', - required=True, - help='Name of the stock/symbol') -parser.add_argument('-d', - '--date', - required=True, - help='Historical date') -parser.add_argument('-e', - '--execution_agents', - action='store_true', - help='Flag to add the execution agents') -parser.add_argument('-s', - '--seed', - type=int, - default=None, - help='numpy.random.seed() for simulation') -parser.add_argument('-l', - '--log_dir', - default=None, - help='Log directory name (default: unix timestamp at program start)') -parser.add_argument('-v', - '--verbose', - action='store_true', - help='Maximum verbosity!') -parser.add_argument('--config_help', - action='store_true', - help='Print argument options for this config file') - -args, remaining_args = parser.parse_known_args() - -if args.config_help: - parser.print_help() - sys.exit() - -seed = args.seed # Random seed specification on the command line. -if not seed: seed = int(pd.Timestamp.now().timestamp() * 1000000) % (2 ** 32 - 1) -np.random.seed(seed) - -util.silent_mode = not args.verbose -LimitOrder.silent_mode = not args.verbose - -simulation_start_time = dt.datetime.now() -print("Simulation Start Time: {}".format(simulation_start_time)) -print("Configuration seed: {}".format(seed)) -print("Symbol: {}".format(args.ticker)) -print("Date: {}".format(args.date)) - -######################## Agents Config ######################################################################### - -# Historical date to simulate. -historical_date = args.date -historical_date_pd = pd.to_datetime(historical_date) -symbol = args.ticker - -agent_count, agents, agent_types = 0, [], [] - -# 1) Exchange Agent -mkt_open = historical_date_pd + pd.to_timedelta('09:00:00') -mkt_close = historical_date_pd + pd.to_timedelta('16:00:00') - -print("Market Open : {}".format(mkt_open)) -print("Market Close: {}".format(mkt_close)) - -agents.extend([ExchangeAgent(id=0, - name="EXCHANGE_AGENT", - type="ExchangeAgent", - mkt_open=mkt_open, - mkt_close=mkt_close, - symbols=[symbol], - log_orders=True, - pipeline_delay=0, - computation_delay=0, - stream_history=10, - book_freq=0, - random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32, - dtype='uint64')))]) -agent_types.extend("ExchangeAgent") -agent_count += 1 - -# 2) Market Replay Agent -file_name = f'DOW30/{symbol}/{symbol}.{historical_date}' -orders_file_path = f'/efs/data/{file_name}' - -agents.extend([MarketReplayAgent(id=1, - name="MARKET_REPLAY_AGENT", - type='MarketReplayAgent', - symbol=symbol, - log_orders=False, - date=historical_date_pd, - start_time=mkt_open, - end_time=mkt_close, - orders_file_path=orders_file_path, - processed_orders_folder_path='/efs/data/marketreplay/', - starting_cash=0, - random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32, - dtype='uint64')))]) -agent_types.extend("MarketReplayAgent") -agent_count += 1 - -# 3) Execution Agent Config -trade = True if args.execution_agents else False - -twap_agent = TWAPExecutionAgent(id=agent_count, - name='TWAP_EXECUTION_AGENT', - type='ExecutionAgent', - symbol=symbol, - starting_cash=0, - start_time=historical_date_pd + pd.to_timedelta('10:00:00'), - end_time=historical_date_pd + pd.to_timedelta('12:00:00'), - freq=60, - direction='BUY', - quantity=12e3, - trade=trade, - log_orders=True, - random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32, - dtype='uint64'))) -execution_agents = [twap_agent] -""" -vwap_agent = VWAPExecutionAgent(id=agent_count, - name='VWAP_EXECUTION_AGENT', - type='ExecutionAgent', - symbol=symbol, - starting_cash=0, - start_time=historical_date_pd + pd.to_timedelta('10:00:00'), - end_time=historical_date_pd + pd.to_timedelta('12:00:00'), - freq=60, - direction='BUY', - quantity=12e3, - volume_profile_path=None, - trade=trade, - log_orders=True, - random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32, - dtype='uint64'))) -execution_agents = [vwap_agent] -""" -agents.extend(execution_agents) -agent_types.extend("ExecutionAgent") -agent_count += 1 - -print("Number of Agents: {}".format(agent_count)) - -######################################################################################################################## -########################################### KERNEL AND OTHER CONFIG #################################################### - -kernel = Kernel("Market Replay Kernel", - random_state=np.random.RandomState(seed=np.random.randint(low=0, high=2 ** 32, dtype='uint64'))) - -kernelStartTime = historical_date_pd -kernelStopTime = historical_date_pd + pd.to_timedelta('16:01:00') - -defaultComputationDelay = 0 -latency = np.zeros((agent_count, agent_count)) -noise = [0.0] - -kernel.runner(agents=agents, - startTime=kernelStartTime, - stopTime=kernelStopTime, - agentLatency=latency, - latencyNoise=noise, - defaultComputationDelay=defaultComputationDelay, - defaultLatency=0, - oracle=None, - log_dir=args.log_dir) - -simulation_end_time = dt.datetime.now() -print("Simulation End Time: {}".format(simulation_end_time)) -print("Time taken to run simulation: {}".format(simulation_end_time - simulation_start_time)) \ No newline at end of file diff --git a/data/synthetic_fundamental.bz2 b/data/synthetic_fundamental.bz2 new file mode 100644 index 000000000..8a50e2810 Binary files /dev/null and b/data/synthetic_fundamental.bz2 differ diff --git a/scripts/execution.sh b/scripts/execution.sh new file mode 100755 index 000000000..8578d0897 --- /dev/null +++ b/scripts/execution.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +seed=123456789 +ticker=ABM +date=20200101 + +log=execution_${seed} + +# (1) Sparse Mean Reverting Oracle Fundamental +python -u abides.py -c execution -t ${ticker} -d ${date} -l ${log} -s ${seed} -e + +# (2) External Fundamental file +#fundamental_path=${PWD}/data/synthetic_fundamental.bz2 +#python -u abides.py -c execution -t ${ticker} -d ${date} -f ${fundamental_path} -l ${log} -s ${seed} -e \ No newline at end of file diff --git a/scripts/execution_iabs.sh b/scripts/execution_iabs.sh deleted file mode 100755 index 1b20f2aec..000000000 --- a/scripts/execution_iabs.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -obc_script=${PWD}/util/formatting/convert_order_book.py -osc_script=${PWD}/util/formatting/convert_order_stream.py - -seed=123456789 -ticker=IABS -date=19910602 -nlevels=10 -fundamental_path=/efs/data/get_real_data/mid_prices/ORDERBOOK_IBM_FREQ_ALL_20190628_mid_price.bz2 - -# Baseline (No Execution Agents) -baseline_log=execution_iabs_no_${seed} -python -u abides.py -c execution_iabs -t ${ticker} -d $date -f ${fundamental_path} -l ${baseline_log} -s $seed -python -u $obc_script ${PWD}/log/${baseline_log}/ORDERBOOK_${ticker}_FULL.bz2 ${ticker} ${nlevels} -o ${PWD}/log/${baseline_log} -python -u $osc_script ${PWD}/log/${baseline_log}/EXCHANGE_AGENT.bz2 ${ticker} ${nlevels} plot-scripts -o ${PWD}/log/${baseline_log} - -# With Execution Agents -execution_log=execution_iabs_yes_${seed} -python -u abides.py -c execution_iabs -t $ticker -d $date -f ${fundamental_path} -l ${execution_log} -s $seed -e -python -u $obc_script ${PWD}/log/${execution_log}/ORDERBOOK_${ticker}_FULL.bz2 ${ticker} ${nlevels} -o ${PWD}/log/${execution_log} -python -u $osc_script ${PWD}/log/${execution_log}/EXCHANGE_AGENT.bz2 ${ticker} ${nlevels} plot-scripts -o ${PWD}/log/${execution_log} - -rm -rf ${PWD}/log/${baseline_log}/NOISE* -rm -rf ${PWD}/log/${baseline_log}/VALUE* -rm -rf ${PWD}/log/${baseline_log}/MOMENTUM* - -rm -rf ${PWD}/log/${execution_log}/NOISE* -rm -rf ${PWD}/log/${execution_log}/VALUE* -rm -rf ${PWD}/log/${execution_log}/MOMENTUM* \ No newline at end of file diff --git a/scripts/execution_marketreplay.sh b/scripts/execution_marketreplay.sh deleted file mode 100755 index 3c45b72a7..000000000 --- a/scripts/execution_marketreplay.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -obc_script=${PWD}/util/formatting/convert_order_book.py -osc_script=${PWD}/util/formatting/convert_order_stream.py - -seed=123456789 -ticker=IBM -date=20190628 -nlevels=50 - -# Baseline (No Execution Agents) -baseline_log=marketreplay_${ticker}_${date} -python -u abides.py -c execution_marketreplay -t $ticker -d $date -l ${baseline_log} -s $seed -python -u $obc_script ${PWD}/log/${baseline_log}/ORDERBOOK_${ticker}_FULL.bz2 ${ticker} ${nlevels} -o ${PWD}/log/${baseline_log} -python -u $osc_script ${PWD}/log/${baseline_log}/EXCHANGE_AGENT.bz2 ${ticker} ${nlevels} plot-scripts -o ${PWD}/log/${baseline_log} - -# With Execution Agents -execution_log=execution_marketreplay_${ticker}_${date} -python -u abides.py -c execution_marketreplay -t $ticker -d $date -l ${execution_log} -s $seed -e -python -u $obc_script ${PWD}/log/${execution_log}/ORDERBOOK_${ticker}_FULL.bz2 ${ticker} ${nlevels} -o ${PWD}/log/${execution_log} -python -u $osc_script ${PWD}/log/${execution_log}/EXCHANGE_AGENT.bz2 ${ticker} ${nlevels} plot-scripts -o ${PWD}/log/${execution_log} \ No newline at end of file