G ENERATING R EALISTIC S
TOCK
M ARKET
O RDER
S TREAMS
Anonymous authors
Paper under double-blind review
A BSTRACT
We propose an approach to generate realistic and high-ﬁdelity stock market data based on generative adversarial networks.
We model the order stream as a stochastic process with ﬁnite history dependence, and employ a conditional Wasserstein
GAN to capture history dependence of orders in a stock market.
We test our approach with actual market and synthetic data on a number of different statistics, and ﬁnd the generated data to be close to real data.
I NTRODUCTION
Financial markets are among the most well-studied and closely watched complex multiagent systems in existence.
Well-functioning ﬁnancial markets are critical to the operation of a complex global economy, and small changes in the efﬁciency or stability of such markets can have enormous ramiﬁcations.
Accurate modeling of ﬁnancial markets can support improved design and regulation of these critical institutions.
There is a vast literature on ﬁnancial market modeling, though still a large gap between the state-of-art and the ideal.
Analytic approaches provide insight through highly stylized model forms.
Agent-based models accommodate greater dynamic complexity, and are often able to reproduce “stylized facts” of real-world markets (LeBaron, 2006).
Currently lacking, however, is a simulation capable of producing market data at high ﬁdelity and high realism.
Our aim is to develop such a model, to support a range of market design and analysis problems.
This work provides a ﬁrst step, learning a high-ﬁdelity generator from real stock market data streams.
Our main contribution is an approach to produce stock market data that is close to real market data, using a Wasserstein generative adversarial network (WGAN) (Arjovsky et al., 2017).
There are many challenges that we overcome as part of this contribution.
The ﬁrst is how to represent a stream of stock market orders as data that can be used in a WGAN.
Towards this end, we assume the stock market data stream to arise from a stochastic process with ﬁnite (but long) memory dependence.
The stochastic process view also makes precise the conditional distribution that the generator is learning as well the joint distribution that the critic of the WGAN distinguishes by estimating the earth-mover distance.
The second main challenge is the design of the network architecture.
We choose a conditional
WGAN to capture the history dependence of the stochastic process, with both the generator and critic conditional on history of orders and the time of day.
A single LSTM layer is used to summarize the history succinctly.
The internal architecture of both the generator and critic uses a standard convolutional structure.
The generator outputs the next stock market order as well as how this order changes the active orders in the market.
Part of the generator output, which updates the active market orders, is produced using a pre-trained network to approximate the deterministic buy and sell order matching in the stock market.
Finally, we experiment with synthetic and real market data.
The synthetic data is produced using a stock market simulator that has been used in several agent-based ﬁnancial studies.
The real data was obtained from OneMarketData, a ﬁnancial data provider and publisher of the OneTick database product.
We evaluate the generated data using various statistics such as the distribution of price and quantity of orders, inter-arrival times of orders, and the best bid and best ask evolution over time.
We ﬁnd the generated data matches the corresponding statistics in real data (simulated or actual stock market) closely.
R ELATED W ORK AND B ACKGROUND
WGAN is a popular and well-known variant of GANs (Goodfellow et al., 2014).
Most prior work on generation of sequences using GANs has been in the domain of text generation (Press et al., 2017; Zhang et al., 2017).
However, since the space of word representations is not continuous, the semantics change with nearby word representation, and given a lack of agreement on the metrics for measuring goodness of sentences, producing good quality text using GANs is still an active area of research.
Stock market data does not suffer from this representation problem but the history dependence for stock markets can be much longer than for text generation.
In a sequence of recent papers, Xiao et al.
(2017; 2018) have introduced GAN-based methods for generating point processes.
The proposed methods generate the time for when the next event will occur.
The authors have also explored the use of these techniques to generate the time for transaction events in stock markets.
Our problem is richer as we aim to generate the actual limit orders including time, order type, price, and quantity information.
Deep neural networks and machine learning techniques have been used on ﬁnancial data mostly for prediction of transaction price (Hiransha et al., 2018;
Bao et al., 2017; Qian, 2017) and for prediction of actual returns (Abe & Nakayama, 2018).
As stated, our goal is not market prediction per se, but rather market modeling.
Whereas the problems of learning to predict and generate may overlap (e.g., both aim to capture regularity in the domain), the evaluation criteria and end product are quite distinct.
The stock market is a venue where equities or stocks of publicly held companies are traded.
Nearly all stock markets follow the continuous double auction (CDA) mechanism (Friedman, 1993).
Traders submit bids, or limit orders , specifying the maximum price at which they would be willing to buy a speciﬁed quantity of a security, or the minimum price at which they would be willing to sell a quantity.
The order book is a store that maintains the set of active orders: those submitted but not yet transacted or canceled.
CDAs are continuous in the sense that when a new order matches an existing (incumbent) order in the order book, the market clears immediately and the trade is executed at the price of the incumbent order—which is then removed from the order book.
Orders may be submitted at any time, and a buy order matches and transacts with a sell order when the limits of both parties can be mutually satisﬁed.
For example, as shown in Figure 1 if a limit buy order with price $10.01 and quantity 100 arrives and the order book has the best offered sell price at $10.01 with quantity 100 then the arriving order matches an incumbent exactly.
However, the next buy order does not match any sell, and the following sell order partially matches what is then the best buy in the order book.
The limit order book maintains the current active orders in the market (or the state of the market), which can be described in terms of the quantity offered to buy or sell across the range of price levels.
Each order arrival changes the market state, recorded as an update to the order book.
After processing any arrived order every buy price level is higher than all sell price levels, and the best bid refers to the lowest buy price level and the best ask refers to the highest sell price level.
See Figurefor an illustration.
The order book is often approximated by few (e.g., ten) price levels above the best bid and ten price levels below the best ask; as these prices are typically the ones that dictate the transactions in the market.
There are various kinds of traders in a stock market, ranging from individual investors to large investing ﬁrms.
Thus, there is a wide variation in the nature of orders submitted for a security.
We aim to generate orders for a security in aggregate (not per agent) that is close to the aggregate orders generated in a real market.
We focus on generating orders and do not attempt to generate transactions in the stock market.
This is justiﬁed as the CDA mechanism is deterministic and transactions can be determined exactly given a stream of orders.
S TOCK -GAN 3. S TOCK M ARKET O RDERS AS A S TOCHASTIC P ROCESS
We model stock market orders as a stochastic process.
Recall that a stochastic process is a collection of random variables indexed by a set of numbers.
We view the stock market orders for a given chunk of time of day ∆ t as a collection of vector valued random variable { x i } i ∈ N indexed by the limit order sequence number in N = { 1 , . . . , n } .
The components of the random vector x
i include the time interval d i , type of order t i , limit order price p i , limit order quantity q i , and the best bid a i and best ask b i .
The time interval d i speciﬁes the difference in time between the current order i and previous order i − 1 (in precision of milliseconds); the range of d i is ﬁnite.
The type of order can be buy, sell, cancel buy, or cancel sell (represented in two bits).
The price and quantity are restricted to lie within ﬁnite bounds.
The price range is discretized in units of US cents and the quantity range is discretized in units of the equity (non-negative integers).
The best bid and best ask are limit orders themselves and are speciﬁed by price and quantity.
Observe that we assume the stochastic process depends on the discrete time of day ∆ t, which we will make explicit in the next paragraph.
We divide the time in a day into 25 equal intervals and ∆ t refers to the index of the interval.
A visual representation of x i is shown in Figure 2(a).
Following the terminology prevalent for stochastic processes, the above process is discrete time and discrete space (note that discrete time in this terminology here refers to the discreteness of the index set N ).
We assume a ﬁnite history dependence of the current output x
i , that is, P ( x i | x i − 1 , . . .
, ∆ t ) =
P ( x i | x i − 1 , . .
, x i − m , ∆ t ) .
Such dependence is justiﬁed by the observation that recent orders mostly determine the transactions and transaction price in the market as orders that have been in the market for long either get transacted or canceled.
Further, the best bid and best ask serves as an (approximate) sufﬁcient statistic for events beyond the history length m .
While this process is not a Markov chain, it forms what is known as a higher order Markov chain, which implies that the process given by y i
=
( x i , . . .
, x i − m +1 ) is a Markov chain for any given time interval ∆ t. We assume that this chain formed by y i has a stationary distribution (i.e., it is irreducible and positive recurrent).
A Markov chain is a stationary stochastic process if it starts with its stationary distribution.
After some initial mixing time, the Markov chain does reach its stationary distribution, thus, we assume that the process is stationary by throwing away some initial data for the day.
Also, for the jumps across two time intervals ∆ t, we assume the change in stationary distribution is small and hence the mixing happens very quickly.
A stationary process means that P ( x i , . . .
, x i − m +1 | ∆ t ) has the same distribution for any i .
In practice we do not know m .
However, we can assume a larger history length k + 1 > m , and then it is straightforward to check that y t
= ( x i , . . .
, x i − k ) is a Markov chain and the claims above hold with m − 1 replaced by k .
We choose k = 20 .
3. WGAN A RCHITECTURE AND W ORKING
Given the above stochastic process view of the problem, we design a conditional WGAN with a recurrent architecture to learn the real conditional distribution P r ( x i | x i − 1 , . . .
, x i − k , ∆ t ) .
We use the subscript r to refer to real distributions and the subscript g to refer to generated distributions.
The real data x 1 , x 2 , . .
. is a realization of the stochastic process.
It is worth noting that even though P ( x i , . . .
, x i − k | ∆ t ) has the same distribution for any i , the realized real data sequence x i , . . .
x i − k is correlated with any overlapping sequnce x
i + k (cid:48) , . . .
x i − k + k (cid:48) for k ≥ k (cid:48) ≥ − k .
Our data points for training (stated in detail in the next paragraph) are sequences x i , . . .
x i − k , and to ensure independence in a batch we make sure that the sequences chosen in a batch are sufﬁciently far apart.
[Figure]
Architecture : The architecture is shown in Figure 2.
Our proposed WGAN is conditional (Mirza & Osindero, 2014) with both the generator and critic conditioned on a k length history and the time interval ∆ t .
The history is condensed to one vector using a single LSTM layer.
This vector and some uniform noise is fed to a fully connected layer layer followed by a convolutional structure.
The generator outputs the next x i (realization of x i ) and the critic outputs one real number.
Note that when training both generator and critic are fed history from real data, but when the generator executes after training it is fed its own generated data as history.
As stated earlier, the generator output includes the best bid and ask.
As the best bid and ask can be inferred deterministically from the current order and the previous best bid and ask (for most orders), we use another neural network (with frozen weights during GAN training) to output the best bid and ask.
We call this the CDA network.
The CDA network is trained separately using a standard MSE loss (see Appendix C).
Critic details :
When fed real data, the critic can be seen as a function c w of s i =
( x i , . . .
, x i − k , ∆ t ) , where w are the weights of the critic network.
As argued earlier, samples in a batch that are chosen from real data that are spaced at least
k apart are i.i.d.
samples of P r .
Then for m samples fed to the critic, 1 m (cid:80) mi =1 c w ( s i ) estimates E s ∼ P r ( c w ( s )) .
When fed generated data (with the ten price levels determined from the output order and previous ten levels), by similar reasoning 1 m (cid:80) mi =1 c w ( s i ) estimates E s ∼ P g
( c w ( s ))
when the samples are sufﬁciently apart (recall that the history is always real data).
Thus, the critic computes the Wasserstein distance between the joint distributions P r ( x i , . . .
, x i − k , ∆ t ) and P g ( x i , . . .
, x i − k , ∆ t ) .
Further, we use a gradient penalty term in the loss function for the critic instead of clipping weights as proposed in the original WGAN paper (Arjovsky et al., 2017) because of the better performance as revealed in prior work (Gulrajani et al., 2017).
Generator details : The generator learns the conditional distribution P g ( x i | x i − 1 , . . .
, x i − k , ∆ t ) .
Along with the real history, the generator represents the distribution P g ( x i , . . .
, x i − k , ∆ t ) =
P g ( x i | x i − 1 , . . .
, x i − k , ∆ t )
P r ( x i − 1 , . . .
, x i − k , ∆ t ) .
The loss functions used is the standard WGAN loss function with a gradient penalty term (Gulrajani et al., 2017).
The critic is trained 100 times in each iteration and as already stated, the notable part in constructing the training data is that for each mini-batch the sequence of orders chosen (including history) is far away from any other sequence in that mini-batch (see Appendix C for code snippets).
E XPERIMENTAL R ESULTS
We apply and evaluate Stock-GAN on two types of data sets composed of orders from an agentbased market simulator and from a real stock market, respectively.
We describe each data set in detail and then compare key metrics and distributions of our generated orders with ground truth orders from the agent-based simulator and real stock markets.
4. S YNTHETIC AND R EAL D ATA
Synthetic data : We ﬁrst evaluate Stock-GAN on synthetic orders generated from an agent-based market simulator.
Previously adopted to study a variety of issues in ﬁnancial markets (e.g., market making and manipulation), the simulator captures stylized facts of the complex ﬁnancial market with speciﬁed stochastic processes and distributions (Wellman & Wah, 2017).
We brieﬂy describe the market simulator below.
In the simulation, the market operates over a ﬁnite time horizon.
Agents enter and reenter the market according to a Poisson process with an arrival rate of 0.005.
On each arrival these traders submit a limit order to the market (replacing their previous order, if any), indicating the price at which they are willing to buy or sell a single unit of the security.
The market environment is populated by 32 traders, representing investors.
Each investor has an individual valuation for the security made up of private and common components.
The common component is represented by a fundamental value, which can be viewed as the intrinsic value of the security.
This fundamental value varies over time according to a mean-reverting stochastic process.
The private component of value captures the preference contribution of the individual agent’s reason for trading this security at the current time (e.g., investment, liquidity, diversiﬁcation).
The private valuations are drawn from a speciﬁed distribution at the start of a simulation.
The common and private components are effectively added together to determine each agents valuation of the security.
Agents accrue private value on each transaction, and at the end of the trading horizon evaluate their accumulated inventory on the basis of a prediction of the end-time fundamental.
Given the market mechanism and valuation model for the simulation, investors pursue their trading objectives by executing a trading strategy in that environment.
A popular trading strategy we adopt in the simulator is the zero-intelligence (ZI) strategy (Farmer et al., 2005).
The ZI trader shades its bid from its current valuation of the stock by a random offset.
We use about 300,000 orders generated by the simulator as our synthetic data.
The price output by the simulator is normalized to lie in the interval [ − 1 , 1] .
Real data : We obtained real limit-order streams from OneMarketData, who provided access for our research to their OneTick database for selected time periods and securities.
The provided data streams comprise order submissions and cancellations across multiple exchanges at millisecond granularity.
In experiments, we evaluate in the performance of Stock-GAN on two securities: a small capitalization stock, Patriot National (PN), and a large capitalization stock, Alphabet Inc (GOOG).
The two stocks differ in several key aspects, including investment sector, market activity intensity, price range, liquidity etc., and thus their order patterns represent distinct dynamic processes.
By training Stock-GAN with historical data for individual stocks, we can generate limit-order streams that capture key characteristics of each.
Relative to our simulated agent-based market, the real market limit orders tend be very noisy including many orders at extreme prices far from the range where transactions occur.
Since our interest is primarily on behavior that can affect market outcomes, we focus on limit orders in the relevant range near the best bid and ask.
Speciﬁcally, in a preprocessing step, we eliminate limit orders that never appear within ten levels of the best bid and ask prices.
In the experiment reported here, we use historical real market data of PN during one trading day in August 2016, and GOOG during one trading day in August 2017.
After preprocessing, the PN daily order stream has about 20,000 orders and GOOG has about 230,000.
4. E VALUATION S TATISTICS
We generate a number of orders equal to the number of real orders used to train the WGAN.
We evaluate our generated order stream in comparison to real data using the following statistics:
Price.
Distribution over price for the day’s limit orders, by order type.
Quantity.
Distribution over quantity for the day’s limit orders, by order type.
Inter-arrival time.
Distribution over inter-arrival durations for the day’s limit orders, by order type.
Intensity evolution.
Number of orders for consecutive 1000-second chunks of time.
Best bid/ask evolution.
Changes in the best bid and ask over time as new orders arrive.
[Figure]
A note on cancellation:
In our generation process, cancellation type orders are not contingent on the order book.
We use a heuristic which is to match the generated cancellation order to the closest priced order in the book.
Cancellations that are too far from any existing order to be a plausible match are ignored.
4. R ESULTS
In describing our results, “real” refers to simulated or actual stock market data and “fake” refers to generated data.
Figure 3 presents statistics on buy orders for the three cases when the real data is simulated, PN, or GOOG.
For simulated data, the price and inter-arrival distribution matches the real distribution quite closely.
The quantity for the simulated data is always one, which is also trivially captured in the generated data.
For PN and GOOG, the quantity distribution misses out on some peaks but gets most of the peaks in the real distribution.
The inter-arrival time distribution matches quite closely (note that the axis has been scaled for inter-arrival time to highlight the peaks and show the full range of time).
The price distribution matches closely for GOOG, but is slightly off for PN, which could be due to the low amount of data for PN.
Figure 4 presents statistics on sell orders for the three cases when the real data is simulated, PN, or GOOG.
The results for sell orders are quite similar to buy orders.
Results for cancellations are included in the appendix.
Figure 5 presents order intensity as a function of time (number of orders in every chunk ofsecs normalized by max number) for the simulated, PN, and GOOG markets.
As in the graphs for other statistics, generated WGAN results are compared with the measured intensities in the real data.
The intensities show similar trends, though for the real markets there is signiﬁcant variation. The
[Figure] [Figure] differences are particularly large for PN, likely due to the relatively smaller magnitude of trading volume for that stock.
In Figure 6, we show the change in best buy/ask as a function of time for the simulated, PN, and
GOOG markets.
The generated results looks similar to real data in range and variation over time for simulated data.
The similarity to real best bid/ask is better for GOOG than PN, which could possibly be due to more data available for GOOG.
Quantitative measures : The ﬁgures till now show that the price distribution appears like a normal distribution and the inter-arrival time appears like a geometric distribution (geometric is discrete version of exponential).
We ﬁt these standard distributions to the real price and inter-arrival distribution and compare the total variation (TV) distance between the real and ﬁtted vs real and generated
[Figure]
[Table]
distributions.
The quantity distribution does not appear like any standard distribution, hence we do not evaluate it by ﬁtting.
The results in Table 1 show that the generated price distribution is almost as close to the real one as the ﬁtted price distribution.
The generated inter-arrival distribution is much closer to the real one than the ﬁtted price distribution.
A point to note is that the actual price and quantity is a stochastic process with dependence on history, thus, the ﬁtted distributions will not be helpful in generating the correct intensities or best bid and best ask evolution.
A note on architectural choices : Various parts of our architecture were developed iteratively to improve the results that we obtained in a previous iteration.
The input of ∆ t to the generator and critic is critical to get the time trend in the intensity for the GOOG stock.
The CDA network and the best bid and ask in history was added to improve the results for best bid/ask variation over time. Comparision with baseline : We also implemented a variational recurrent generative network but found its performance to be worse than our approach (shown in Appendix B).
C ONCLUSION
Our results reveal that GANs can be used to simulate a stock market.
While our results are promising, there are open issues that provide for further research material.
One experimental aspect is to try different size of the network in the WGAN, possibly dependent on the data size of the given stock and testing with many different variety of stocks.
Another open research issue is to output cancellations in a more intelligent manner than the heuristic approach we use now.
Overall, our work provides fertile ground for future research at the intersection of deep learning and ﬁnance.
A A DDITIONAL R ESULTS
Below we show results for buy order cancellation and sell order cancellation using the exact same measures as for the buy and sell orders in the main paper.
The results also are similar to buy or sell results earlier. [Figure] [Figure] [Figure]
[Figure]
B V ARIATIONAL RECURRENT NEURAL NETWORK
We use the variational recurrent network as another baseline generative model.
The architecture is exactly same as the work Chung et al. We used the code available at https: //github.com/phreeza/tensorflow-vrnn , but modiﬁed it. Our modiﬁcation was to enable not forcing the output to be Gaussian as done in Chung et al. (2015), as those produced much worse results.
Instead, we use a MSE loss.
We also modiﬁed the input size, etc. to make the neural network structure compatible with our problem.
The exact change to the code changing the loss function is shown below: kl loss = tf kl gaussgauss(enc mu , enc sigma , prior mu , prior sigma)
# we replace the maximium likelihood loss with the mse loss below mse loss = tf.losses.mean squared error(y,dec rho) return tf.reduce mean(kl loss + mse loss)
The results in Figure 9 for GOOG buy order only and in Figure 10 for all types of GOOG orders shows that the entropy of the output is high (when comparing price and inter-arrival distributions) and the performance is worse than our GAN.
In particular, the generated (fake) price distribution is wider than the real one (or the one generated by the GAN).
The generated inter-arrival distribution is almost uniform over the discrete time points and not concentrated at 0.
The quantity distribution matches the real one, somewhat similarly like our GAN approach, but it generates some negative values unlike our GAN approach (which could be discarded).
The intensity distribution is also somewhat close to the real intensity.
The results are similar for other types of orders.
C C ODE S NIPPETS
Here we present codes snippets that show the architecture of the GAN.
First, we start with the CDA network that is trained independently with MSE loss:
input his
=
Input(shape=(8,))
G = Sequential(name=’discriminator’)
G.add(Dense(256 ∗ 3,input dim=8))
G.add(BatchNormalization()) G.add(Activation(’relu’)) G.add(Reshape((16,16,3)))
G.add(Conv2D(128,(3,3),padding=’same’))
G.add(BatchNormalization()) G.add(Activation(’relu’))
G.add(Conv2D(64,(3,3),padding=’same’))
G.add(BatchNormalization()) G.add(Activation(’relu’))
G.add(Conv2D(32,(3,3),padding=’same’))
G.add(BatchNormalization()) G.add(Activation(’relu’))
G.add(Flatten())
G.add(Dense(4))
outputvec=G(inputhis) self .net =
Model(inputs=input his , outputs=output vec) optimizer =
Adam(0.0001)
self .net.
compile
(optimizer=optimizer , loss=’mean squared error’) self .net.summary(
Input LSTM structure for both Generator and Critic are shown below ########### Input for both Generator and Critic ####################### # history orders of shape ( self .historyLength , self .orderLength)
history =
Input(shape=( self .historyLength , self .orderLength), \ name=’history full’)
# current time slot: Integer , from 0 tohistory input = Input(shape=(1,), name=’history time’)
# noise input of shape ( self .noiseLength)
noise input 1 = Input(shape=( self .noiseLength ,), name=’noise input 1’)
# Real order of shape(( self .mini batch size , self .orderLength)
truth input = Input(shape=( self .mini batch size , \ self .orderLength ,1),name=’truth input’)
# lstm at Generator to extract history orders features lstm output =
LSTM( self .lstm out length)(history)
# lstm at Critic to extract history orders features lstm output
h = LSTM( self .lstm out length ,name=’lstm critic’)(history)
# concatenate history features with noise gen input =
Concatenate(axis= − 1)([history input ,lstm output ,noise input 1])
The
############# Generator ######################## # Input: gen input , shape( self .noiseLength+ self .lstm out length + 1)
# Output: gen output 1 , shape( self .mini batch size , self .orderLength
− 4) dropout = 0.
G 1 = Sequential(name=’generator 1’)
G 1.add(Dense(( self .orderLength − 4) ∗ self .mini batch size ∗ 100, \ input dim= self .noiseLength+ self .lstm out length + 1))
G 1.add(BatchNormalization())
G 1.add(Activation(’relu’))
G 1.add(Reshape(( int ( self .mini batch size), int ( self .orderLength − 4), 100)))
G 1.add(UpSampling2D())
G 1.add(Dropout(dropout))
G 1.add(UpSampling2D())
G 1.add(Conv2DTranspose(32, 32, padding=’same’))
G 1.add(BatchNormalization())
G 1.add(Activation(’relu’))
G 1.add(Conv2DTranspose(16,32 , padding=’same’))
G 1.add(BatchNormalization())
G 1.add(Activation(’relu’))
G 1.add(Conv2DTranspose(8, 32, padding=’same’))
G 1.add(BatchNormalization())
G 1.add(Activation(’relu’))
G 1.add(MaxPooling2D((2,2)))
G 1.add(Conv2DTranspose(1, 32, padding=’same’))
G 1.add(Activation(’tanh’))
G 1.add(MaxPooling2D((2,2)))
gen output 1 = G 1(gen input)
#CDA network(train
offline)
#Input: cda input , shape( self .mini batch size , 8) #Output: gen output 2 , shape( self .mini batch size , 4) G 2 =
Sequential(name=’orderbook gen’)
G 2.add(Dense(256 ∗ 3,input dim=8))
G 2.add(BatchNormalization())
G 2.add(Activation(’relu’))
G 2.add(Reshape((16, 16, 3)))
G 2.add(Conv2D(128,(3,3),padding=’same’))
G 2.add(BatchNormalization())
G 2.add(Activation(’relu’))
G 2.add(Conv2D(64, (3,3),padding=’same’))
G 2.add(BatchNormalization())
G 2.add(Activation(’relu’))
G 2.add(Conv2D(32,(3,3),padding=’same’))
G 2.add(BatchNormalization())
G 2.add(Activation(’relu’))
G 2.add(Flatten()) G 2.add(Dense(4))
# extract the last best bid/ask from history as the history of CDA orderbook history =
Lambda( lambda x: x[:, − 1,5:], output shape=(4,))(history)
# gen output 1 is output of generator gen output reshaped =
Reshape(( self .orderLength
− 4,))(gen output 1) # remove time as it is not needed for CDA network gen output without time =
Lambda( lambda x: x[:,1:], output shape=(4,))(gen output reshaped) cda input = Concatenate(axis=1)
([ gen output without time ,orderbook history]) gen output 2 =
G 2(cda input)
#Output of Generator , shape( self .mini batch size , self .orderLength) concatentated # with output of the CDA network to get final output gen output =
Concatenate(axis=2)([gen output 1 , \
Reshape(( self .mini batch size , 4, 1))(generator output 2)])
The structure of the critic is shown below ############# Critic ################## # Input of Critic, merge history input , lstm output h and gen output/truth input discriminator input fake = (Concatenate(axis=2) \ ([Reshape((1, 1,1))(history input), \
Reshape((1, self .lstm out length ,1))
( lstm output h), gen output]))
discriminator input truth = Concatenate(axis=2) \ ([Reshape((1, 1,1))(history input), \
Reshape((1, self .lstm out length ,1))
( lstm output h), truth input])
#random − weighted average of real and generated samples − following # Improved WGAN work averaged samples =
RandomWeightedAverage() \ ([discriminator input fake , discriminator input truth])
#Critic #Input: discriminator input fake/discriminator input truth
#Ouput: score
D = Sequential(name=’discriminator’)
D.add(Conv2D(512,(3,3),padding=’same’,
input shape=( self .mini batch size , \
self .orderLength+ self .lstm out length+1,1))) D.add(Activation(’relu’)) D.add(Conv2D(256,(3,3),padding=’same’)) D.add(Activation(’relu’)) D.add(Conv2D(128,(3,3),padding=’same’)) D.add(Activation(’relu’)) D.add(Flatten()) D.add(Dense(1))
self .D
=
D discriminator output fake =
D(discriminator input fake) discriminator output truth =
D(discriminator input truth) averaged samples output = D(averaged samples)
#Def gradient penalty loss partial gp loss = partial( self .gradient penalty loss , averaged samples=averaged samples , gradient penalty weight=1) partial gp loss.
name = ’gradient penalty’
The full model ############### Model Definition ################ # Generator model
# Input: [history input ,history ,noise input 1] # Output: gen output self .gen
=
Model(inputs=[history input ,history ,noise input 1], outputs= gen output) #Model Truth: self .model truth =
Model(inputs=[history input ,history ,noise input 1 ,truth input], \ outputs=[discriminator output fake ,discriminator output truth , \ averaged samples output]) #Model Fake: self .model fake
=
Model(inputs=[history input ,history ,noise input 1], \ outputs= discriminator output fake)
#Optimizer optimizer = Adam(0.0001, beta 1=0.5, beta 2=0.9)
#Compile Models #Generator self .gen.
compile (optimizer=optimizer , loss=’binary crossentropy’) self .gen.summary
() #Model Truth − Generator is not trainable here for layer in self
.model truth.layers:
layer.trainable =
False self .model truth.get layer(name=’discriminator’).trainable =
True self .model truth.get
layer(name=’lstm critic’).trainable =
True self .model truth.
compile (optimizer=optimizer , \ loss=[ self .w loss , self .w
loss ,partial gp loss])
#Model Fake − critic is not trainable here for layer in self .model fake.layers: layer.trainable =
True self .model fake.get layer(name=’discriminator’).trainable =
False self .model fake.get
layer(name=’lstm critic’).trainable =
False self .model fake.
compile (optimizer=optimizer ,
loss= self .w loss) #print summary self .model fake.summary() self .model truth.summary()