A Deep Reinforcement Learning Trader without Offline Training
Boian Lazov

TL;DR
This paper presents a fully online trading algorithm using Double Deep Q-learning with Fast Learning Networks, capable of adapting to market conditions without offline training, and demonstrates its effectiveness on cryptocurrency data.
Contribution
The paper introduces a novel online trading method that does not require offline training, using a specific reinforcement learning setup with a profit conservation mechanism.
Findings
The algorithm outperforms random trading strategies.
It adapts well to different market trends.
It performs effectively on real cryptocurrency data.
Abstract
In this paper we pursue the question of a fully online trading algorithm (i.e. one that does not need offline training on previously gathered data). For this task we use Double Deep -learning in the episodic setting with Fast Learning Networks approximating the expected reward . Additionally, we define the possible terminal states of an episode in such a way as to introduce a mechanism to conserve some of the money in the trading pool when market conditions are seen as unfavourable. Some of these money are taken as profit and some are reused at a later time according to certain criteria. After describing the algorithm, we test it using the 1-minute-tick data for Cardano's price on Binance. We see that the agent performs better than trading with randomly chosen actions on each timestep. And it does so when tested on the whole dataset as well as on different subsets, capturing…
| Mean [USDT] | Median [USDT] | St. Dev. [USDT] | ||
|---|---|---|---|---|
| Non-random | 263.928 | 241.903 | 113.334 | 0.031 |
| Random | 189.703 | 153.028 | 121.777 | 0.226 |
| 68.192 | 64.020 | 32.131 |
| Mean [USDT] | Median [USDT] | St. Dev. [USDT] | ||
|---|---|---|---|---|
| Non-random | ||||
| Random | ||||
| Mean [USDT] | Median [USDT] | St. Dev. [USDT] | ||
|---|---|---|---|---|
| Non-random | ||||
| Random | ||||
| Mean [USDT] | Median [USDT] | St. Dev. [USDT] | ||
|---|---|---|---|---|
| Non-random | ||||
| Random | ||||
Peer Reviews
No public reviews on file for this paper yet. If you reviewed it on a platform where reviews are public (OpenReview, ICLR, NeurIPS, ICML), you can paste yours below so the community can read it here.
Videos
No videos yet. Explain this paper in a talk, walkthrough, or lecture? Add one.
Taxonomy
MethodsTest
A Deep Reinforcement Learning Trader without Offline Training
Boian Lazov [email protected] Department of Mathematics, University of Architecture, Civil Engineering and Geodesy, 1164 Sofia, Bulgaria
Abstract
In this paper we pursue the question of a fully online trading algorithm (i.e. one that does not need offline training on previously gathered data). For this task we use Double Deep -learning in the episodic setting with Fast Learning Networks approximating the expected reward . Additionally, we define the possible terminal states of an episode in such a way as to introduce a mechanism to conserve some of the money in the trading pool when market conditions are seen as unfavourable. Some of these money are taken as profit and some are reused at a later time according to certain criteria. After describing the algorithm, we test it using the 1-minute-tick data for Cardano’s price on Binance. We see that the agent performs better than trading with randomly chosen actions on each timestep. And it does so when tested on the whole dataset as well as on different subsets, capturing different market trends.
1 Introduction
In recent years algorithmic trading on financial markets is increasingly replacing humans [1]. One can find numerous estimates for the market share of automated traders with some sources giving over for US equity trading [1], while others citing as high as for forex trading.111See for example https://www.quantifiedstrategies.com/what-percentage-of-trading-is-algorithmic/.
Such sources, however, do not seem that reliable, since data is generally not openly available. Nevertheless, there are many (paid) reports that give a general idea of the scope of automated trading:
https://www.grandviewresearch.com/industry-analysis/algorithmic-trading-market-report,
https://www.mordorintelligence.com/industry-reports/algorithmic-trading-market,
https://www.alliedmarketresearch.com/algorithmic-trading-market-A08567.. Regardless of the actual figures, intelligent automation is increasingly used in our world and promises to be applicable in some very complex domains, where analytic solutions are either not known or very hard to obtain.
There are many possible approaches to developing a trading algorithm, but recently one direction of research has been receiving much attention, namely machine learning based approaches. In particular, Reinforcement Learning (RL) has been a really promising way to solve some very difficult problems in other areas (like learning to play various games like Go [2] and StarCraft II [3] at the expert level) and is now being adapted to make decisions and execute trades in the trading setting. This area of research is very active and fairly new (see for example [4]).
As promising as it is, RL suffers from one big problem, namely the generalisation one (as does all of machine learning in fact). More specifically, once the agent (or neural network) is trained on a set of data and good performance is achieved, it is generally hard to translate this training to a new dataset and keep the performance. Furthermore, the training set usually needs to be very large and the agent needs to replay it many times. This is obviously not a good situation for a trading algorithm, since the market is considered a stochastic system and as such it changes rapidly and continuously. If we hope to be able to predict its movement, it should mostly be short term. For this the trader should be able to adapt quickly to current information.
There are many proposed ways to try to deal with said problem of generalisation (both in supervised learning and RL), but one that seems both promising and simple is the idea to learn only the output weights of a neural network. It is implemented in partiular in two algorithms – Extreme Learning Machine (ELM) [5, 6] and Fast Learning Network (FLM) [7], and one can borrow the structure of the FLN to use as an approximator to the -function. As we will see later this will be a success and we will obtain a RL agent that performs better than random even when the market’s overall trend is downward.
This paper is organised as follows: in section 2 we will describe the algorithm in detail, namely all of the components of the RL (section 2.1) as well as the neural network (section 2.2); in section 3 we will discuss briefly how to implement the algorithm and then we will test it on historical market data against an algorithm, that takes random actions on each timestep; finally, we will end with some concluding remarks (section 4).
2 The algorithm
Our goal is to build a simple trading algorithm, which uses a pool of money to trade for some asset, by observing the state of the market. More precisely our agent needs to collect some information about the market, then open a position (ideally, executing a trade). Then it needs to wait for more information to calculate whether the trade was a good one and the process repeats.
We will build our trader in the framework of RL. More precisely we will use a double -learning algorithm with approximation [8]. It is generally thought that combining -learning with approximation should be avoided, because of instabilities, but there are examples of successfully using such algorithms [9, 10]. To approximate the -functions we will use the structure of a FLN [7]. On top of that we will also propose a savings mechanism to deal with “bearish” markets. The idea is to take money out of the trading pool, so that part of it is never used again and can be taken as profit and another part is returned to the trading pool when the market conditions seem favourable. We will now go through all the specific components one by one.
2.1 Double Q-learning
2.1.1 State
We will use a standard double Q-learning algorithm. It will be episodic with a continuous state space. As is usual, we will add an index to variables to denote the current time step and for the next time step. First we will see how to construct the state of the environment. As we will be using approximaiton of the -function by a neural network the state will be defined by a feature vector.
The algorithm is initialised with 3 pools of money, denoted by , and . refers to the current pool with which to trade. denotes an amount of money, that are saved and never again used. This gives a convenient way to use the profit, without disturbing the operation of the trader and also safeguards somewhat against big losses. refers to a pool of money, that are stored for later use when some conditions are met. Finally, there are also the assets that the agent will buy, denoted by . and will be included in the calculation of the state.
Next, we need a variable, which will be used to determine when to move money to and . We denote this by . We will describe this in more detail later, but briefly and will be increased (and decreased), when the value of becomes greater than .
To calculate the state, our trader also needs information for the market. It consists of one price, recorded at the beginning of the episode, denoted by , and consecutive prices, denoted by , , , and , recorded at some intervals.
The next thing that is needed to calculate the state is the trading volume. More precisely, the trader records the volumes from the intervals immediately preceding the ones with recorded prices. These volumes are then used to calculate a few averages, that will be included in the feature vector. These are a simple moving average of the previous values of the volume, denoted by as well as the average volume of the current state’s data points, denoted by . The last recorded volume (associated with ) is also used in the feature vector and it is denoted by .
Finally, we also include in the feature vector the Relative Strength Index (RSI), calculated with the recorded prices (15 prices, as is standard), as well as the relative movements of the price and relative movements of the movements and so on, i.e.
[TABLE]
With all of the above the feature vector has the following form:
[TABLE]
2.1.2 Actions and rewards
Next, we want to define the set of actions that the agent can take. They are simple – buy, sell or hold. We denote the set of actions as . Actions to denote buying. Action means the agent buys coins for money, action – for and so on. Actions to denote selling for a fixed amount of money – again at increments of . Finally, action denotes holding. Actions, of course, can fail due to insufficient funds and this will be reflected in the reward.
This leads us to the next ingredient of the RL algorithm – the reward signal. In order to calculate the reward we first need to keep a record of the total wealth of the agent, associated with a given state,
[TABLE]
Thus after trading and recording the next prices, the wealth changes. Using this, we choose the reward to be polynomial in the change of the wealth, i.e.
[TABLE]
The above formula helps to discourage too risky actions (i.e. ones with changes in the wealth that are too big). Additionally, if the attempted action fails, the reward is instead
[TABLE]
2.1.3 Terminal state
As we mentioned earlier, we are using a savings mechanism. It ties in with the terminal state. We will consider three different terminal states. The first terminal state is the one in which
[TABLE]
where is a parameter that changes when encountering a terminal state. This means that the current money pool of the agent is greater than some threshold. Before beginning the new episode, the extra money is distributed between three pools: goes to a savings pool , which is never again used to trade; – to a reserves pool , which might be used again later; and is left in the money pool (so that after this still ). Then is increased to the current value of plus . The reward for going into this state is modified – it is the usual plus the amount of money that was added to , i.e. .
The second terminal state is determined by four conditions:
[TABLE]
Here is a hyperparameter, which sets the lowest possible value of . In general the meaning of RSI is open to interpretation [11, 12], but if all of the above conditions are met, we take this as an indication that the market conditions are favourable (while the agent is low on money), so half of the money in are redistributed to the money pool to be used for trading again. After this is again changed – this time to , and the new episode begins.
The final terminal state is determined by the following:
[TABLE]
Here the market is seen as unfavourable so the only thing to do is to change to , so that it will be easier to redistribute some of the money later.
2.1.4 Policy and hyperparameters
As is standard we use an -greedy policy. It picks actions based on the value of the average of the two -functions in a given state. In general the choice of is not a trivial task and the performance of the algorithm can vary greatly depending on this choice. There are many suggestions on how to successfully manage this balance of exploration and exploitation (using for example decay of [8], change point detection [13], adaptation based on value differences [14], etc.). What we want here is to be able to explore sufficiently when the market conditions change, which is very important for a fully online algorithm. In line with this we use a simple decay of , but mixed with a probabilistic reset to a larger value. More precisely, first initialise a counter to [math]. Before each choice of an action is either incremented by or with probability it is reset to , if (this resets to about ). Afterwards, is calculated according to the formula
[TABLE]
Next, we want to choose a learning rate . It is well-known that the learning rate in gradient descent methods greatly affects the performance of a neural network (or of the RL algorithm using it) [15]. To avoid fixing the learning rate manually, we choose to use a cyclical one [16, 17]. More precisely, a counter is initialised to [math]. Then, before taking the previously chosen action is calculated using the formula [17]
[TABLE]
after which is incremented by . This means that varies between and with a period of steps.
2.2 Fast learning network
2.2.1 Preliminaries
Now we need to describe the neural network, that will approximate the -function, namely FLN. FLNs use a parallel connection of two feedforward neural networks – one has a single hidden layer, while the other has none [7]. The hidden layer weights are random and fixed and only the output weights are learned. If, in addition, we choose the output neurons’ activation function to be the identity function and fix all the biases to zero, this effectively means that the approximating function is linear in the feature vector with additional fixed nonlinear terms from the hidden layer.
More precisely, we can denote the input and output as and , respectively:
[TABLE]
where and are the respective sizes of the input and the output. Also, for shortness of notation, we denote the hidden layer output as
[TABLE]
Here is the activation function of the hidden layer and is the input to the hidden layer. is the hidden layer size.
The weights are denoted by (input to output layers), (input to hidden layers) and (hidden to output layers):
[TABLE]
Now the -th component of the output vector is calculated by the following formula [7]:
[TABLE]
We can shorten the above to
[TABLE]
The optimisation is then performed only with respect to and .
2.2.2 Weight renormalisation
One common problem that we can encounter is that the weights in the neural network may diverge. This is especially true when the learning rate is large (but a large learning rate might help with adaptation). The above is a problem, since it is generally accepted that very large weights correlate with overfitting the training set and poor generalisation [18]. There are many ways to try to deal with this, but one simple method is to just renormalise the weight vector [19]. We do something similar with the output weights and .
More precisely, consider the output . It is obtained by scalar multiplication of the weight vector
[TABLE]
with the concatenation of the input and the hidden layer output . The vector (2.25) itself is the concatenation of the -th row of and the -th row of and it is learned by stochastic gradient descent. A record of the maximal value of its norm is kept. If the weight vector is longer than after an update, it is rescaled by a factor of . This keeps the weights from diverging and allows us to use large learning rates (the exact values of and are hyperparameters and will be specified later, but them being larger should help the agent adapt quickly).
3 Implementation details and testing
3.1 Observing, trading, hyperparameters
Before implementing the algorithm we need to consider a few points, namely how to record prices, how to trade, how exactly to structure the FLN and the values of the hyperparameters. The first question that needs to be answered is how often to record a price (and volume) for the feature vector. In principle the intervals can be of any length. One advantage of automated trading is that it can react quickly to the market. In line with this we want the intervals to be short, e.g. minute (more precisely the price is recorded in the beginning of the -minute interval). However, a trade occurs right after observing prices, which in practice means that trades are performed every minutes. This means that, depending on the volatility of the market, consecutive trades might happen on similar (often the same) prices and the profit from this is very small. Possibly too small to compensate for the trading fee. To counter this the observed price passes through a filter before being recorded, such that the relative change between two prices is greater than .
The next question is how exactly to trade. In testing we just assume that the trade occurs at the last recorded price . To ensure this in practice one should use limit orders instead of market orders to avoid slippage. However, this poses the problem that the trade might not be executed at all (or at least not before new prices are recorded and it’s time to trade again). To ensure that it has up-to-date information the trader should cancel the order before the next prices are recorded, e.g. after recording . Additionally, in such cases one can include the same negative reward as for trade failure due to insufficient funds to try to discourage orders that are later cancelled.
Next, we need to describe the neural network in more detail. It’s input is the feature vector (2.5), representing the state, while in its output we include one node for each action, i.e.
[TABLE]
This means that there are input nodes and output nodes. The size of the hidden layer is a hyperparameter of the algorithm and it is fixed to . Then the weight martices , and are , and , respectively, while the weight vector (2.25) has components.
From the above we see that there is a separate weight vector (2.25) for each action . After choosing and taking the action only the respective weight vector should be updated. So the gradient of with respect to the weights is just the concatenation of and and it is the same for all actions.
For the neuron activation function we choose to use the logistic function, i.e.
[TABLE]
and the feature vector (2.5) is scaled, so that its norm is , before feeding it into the neural network.
Finally, we need to fix the rest of the hyperparameters (in addition to the hidden layer size). For the discount factor we choose . The lowest possible value of is fixed to . While we have eliminated the need to choose , there is still a hyperparameter to fix and it is the probability for a reset of . We choose this to be . Likewise, we are not choosing the learning rate . Nevertheless, there are still hyperparameters to fix there also, namely , and . We choose the following values: , and .
3.2 Testing
With the above considerations in mind here we present the results of testing the algorithm (the whole code for which is written in Mathematica and is included in appendix A) on historical market data. Because we are using previously recorded prices, as already mentioned, there are a few things we can’t account for, one of which is that in testing the order and the trade are the same, i.e. the order is always fully fulfilled at exactly the recorded price. Also, the precision is much higher when testing as we may use numbers with many digits. In real world applications one needs to round appropriately (e.g. when using part of , when placing an order, etc.).
In principle nothing stops us from usign the trader in any market, but our tests are performed on historical data for the ADA/USDT cryptocurrency pair on Binance from the pair listing on to . We first pass the data through a filter as described in section 3.1. Then we use subsets of the filtered data. One is the whole dataset (figure 1(a)), while the other three are attempts to capture different market conditions – a “bearish” (figure 1(b)), a “bullish” (figure 1(c)) and a “mixed” (figure 1(d)) market.
For each dataset we perform runs of the algorithm. Each run starts with , , , and and we record the performance in terms of the sum of the wealth (2.6) and the and pools, i.e.
[TABLE]
as well as the value of alone, at the end of the run. In order to evaluate the effectiveness of the algorithm we also perform runs with randomly selected actions on each time step. In this case , as the savings mechanism depends on multiple reinforcement learning ingredients.
After this we arrange the data in histograms (figures 2, 3, 4 and 5), which also include the sample minimum and maximum, and calculate the sample means, medians, standard deviations, as well as the empirical probabilities for finishing a run with . The last is included as a measure of the risk of losing money after a run. All of these are arranged in tables 1, 2, 3 and 4. As can be seen, our algorithm performs better than random in all datasets.
In particular, for the full dataset we observe an increase in the mean value of of about when taking non-random actions versus random ones. The median also increases – by . Additionally, the probability for finishing a run with (i.e. for losing money) is smaller when taking non-random actions.
Similar calculations can be made for the rest of the datasets. In all of the cases the algorithm has higher mean and median values of when compared to random, as well as lower values of , i.e. it makes more money on average and has a lower chance to lose money. Probably the most interesting case is the “bearish” market one, as there it is the hardest to make a profit. This is reflected in our results as the differences with random are the smallest. More specifically, the mean and median of are about larger and the probability of – about smaller.
For completeness we also include the relative performance in the other two datasets. In terms of the mean of our algorithm performs about better in the “bullish” dataset and better in the “mixed” dataset. In terms of the median the increases are and for the “bullish” and “mixed” cases, respectively. Finally, in terms of the decreases are and , respectively, for the two cases.
4 Conclusion
In this paper we attempted to tackle the challenging problem of market prediction using machine learning. More specifically, we introduced a deep reinforcement learning agent that is meant to adapt to market conditions and trade fully online. The main components of our algorithm are a more or less standard Double -learning framework coupled with a Fast Learning Network, used to approximate the -functions. On top of that we added a mechanism, which takes money out of the trading pool, both as a means to take profit and to boost performance by reusing some of it at a more favourable moment.
After this we tested the algorithm on historical market data, which was chosen so that it captures different market conditions. We observed that our agent performs better than random on all datasets – both in terms of profit and probability of loss at the end of a run through the data. Furthermore, it did so even in a “bearish” market, when the overall market trend is downward and, most importantly, without any prior learning on big offline dataset. We can view the latter as the main strength of our algorithm.
Appendix A Mathematica code
\boldsymbol{\text{CloseKernels}[];}\\ \boldsymbol{\text{LaunchKernels}[];}\\ \boldsymbol{\text{ClearAll}[\text{{``}Global\grave{}*{''}}]}\\ \boldsymbol{\text{SetDirectory}[\text{NotebookDirectory}[]]}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Read the CSV file with the prices and volumes. *)}}\\ \boldsymbol{\text{data}=\text{Import}[\text{{``}ADA-USDT.csv{''}}];}\\ \boldsymbol{\text{uprices}=\text{Table}[\text{data}[[k,2]],\{k,2,\text{Length}[\text{data}]\}];\text{simlength}=\text{Length}[\text{uprices}]}\\ \boldsymbol{\text{uvolumes}=\text{Table}[\text{data}[[k,6]]/10000000,\{k,2,\text{Length}[\text{data}]\}];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Pass the prices through a filer and make a list of the new prices and preceding volumes. *)}}\\ \boldsymbol{\text{prices}=\text{Table}[0,\{k,1,\text{simlength}\}];}\\ \boldsymbol{\text{volumes}=\text{Table}[0,\{k,1,\text{simlength}\}];}\\ \boldsymbol{\text{pinit}=\text{uprices}[[2]];}\\ \boldsymbol{\text{prices}[[1]]=\text{uprices}[[2]];}\\ \boldsymbol{\text{volumes}[[1]]=\text{uvolumes}[[1]];}\\ \boldsymbol{l=2;}\\ \boldsymbol{i=3;}\\ \boldsymbol{\text{While}[i\leq\text{simlength},}\\ \boldsymbol{\text{If}[\text{Abs}[\text{uprices}[[i]]-\text{pinit}]/\text{pinit}>0.01,\text{pinit}=\text{uprices}[[i]];\text{prices}[[l]]=\text{uprices}[[i]];\text{volumes}[[l]]=\text{uvolumes}[[i-1]];l=l+1];}\\ \boldsymbol{i=i+1}\\ \boldsymbol{];}\\ \boldsymbol{\text{prices}=\text{DeleteCases}[\text{prices},0];}\\ \boldsymbol{\text{simlength}=\text{Length}[\text{prices}]}\\ \boldsymbol{\text{volumes}=\text{DeleteCases}[\text{volumes},0];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Plot the prices and volumes. *)}}\\ \boldsymbol{\text{ListPlot}[\text{prices},\text{Joined}\to\text{True},\text{PlotRange}\to\text{Full}]}\\ \boldsymbol{\text{ListPlot}[\text{volumes},\text{Joined}\to\text{True},\text{PlotRange}\to\text{Full}]}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Parameters. *)}}\\ \boldsymbol{\text{vsize}=5;\text{(* Number of data points for each state. *)}}\\ \boldsymbol{\text{fsize}=1+\text{vsize}+1+1+1+1+1+1+1+1+1+1+4+3+2+1+1\text{(* Size of the feature vector. *)}}\\ \boldsymbol{\text{hlsize}=50;\text{(* Size of the hidden layer. *)}}\\ \boldsymbol{\text{gamma}=0.05;\text{(* Discount factor. *)}}\\ \boldsymbol{\text{probeps}=0.0001;\text{(* Probability to reset the value of epsilon. *)}}\\ \boldsymbol{\text{mlimn}=75.;\text{(* The lowest possible value of mlim. *)}}\\ \boldsymbol{\text{runs}=1000;\text{(* Number of test runs. *)}}\\ \boldsymbol{}\\ \boldsymbol{\text{feat}=\text{Table}[0.,\{k,1,\text{fsize}\}];\text{(* Feature vector. *)}}\\ \boldsymbol{\text{pr}=\text{Table}[0.,\{k,1,\text{vsize}\}];\text{(* List of prices in the state. *)}}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Hidden layer outputs. *)}}\\ \boldsymbol{\text{hlayer1}=\text{Table}[0.,\{k,1,\text{hlsize}\}];}\\ \boldsymbol{\text{hlayer2}=\text{Table}[0.,\{k,1,\text{hlsize}\}];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Gradients of the Q-functions. *)}}\\ \boldsymbol{\text{gradq1}=\text{Table}[0.,\{k,1,\text{hlsize}+\text{fsize}\}];}\\ \boldsymbol{\text{gradq1new}=\text{Table}[0.,\{k,1,\text{hlsize}+\text{fsize}\}];}\\ \boldsymbol{\text{gradq2}=\text{Table}[0.,\{k,1,\text{hlsize}+\text{fsize}\}];}\\ \boldsymbol{\text{gradq2new}=\text{Table}[0.,\{k,1,\text{hlsize}+\text{fsize}\}];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Q-functions. *)}}\\ \boldsymbol{\text{qarr1}=\text{Table}[0.,\{k,1,19\}];}\\ \boldsymbol{\text{qarr2}=\text{Table}[0.,\{k,1,19\}];}\\ \boldsymbol{\text{qarr}=\text{Table}[0.,\{k,1,19\}];}\\ \boldsymbol{\text{qarr1new}=\text{Table}[0.,\{k,1,19\}];}\\ \boldsymbol{\text{qarr2new}=\text{Table}[0.,\{k,1,19\}];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Lists to write sav and twth at the end of each run. *)}}\\ \boldsymbol{\text{savarray}=\text{Table}[0.,\{k,1,\text{runs}\}];}\\ \boldsymbol{\text{twtharray}=\text{Table}[0.,\{k,1,\text{runs}\}];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Relative movements of the price, etc. *)}}\\ \boldsymbol{\text{nmd1}=\text{Table}[0.,\{k,1,\text{vsize}-1\}];}\\ \boldsymbol{\text{nmd2}=\text{Table}[0.,\{k,1,\text{vsize}-2\}];}\\ \boldsymbol{\text{nmd3}=\text{Table}[0.,\{k,1,\text{vsize}-3\}];}\\ \boldsymbol{\text{nmd4}=\text{Table}[0.,\{k,1,\text{vsize}-4\}];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* The activation function. *)}}\\ \boldsymbol{\text{Plot}[1./(1.+\text{Exp}[-x]),\{x,-6.,6.\}]}\\ \boldsymbol{}\\ \boldsymbol{m=1;\text{While}[m\leq\text{runs},}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Initial weights are randomly generated. Then the weights between the input and hidden layers are rescaled. *)}}\\ \boldsymbol{\text{whi1}=\text{Table}[\text{RandomReal}[\{-1.,1.\}],\{k,1,\text{hlsize}\},\{l,1,\text{fsize}\}];}\\ \boldsymbol{j=1;\text{While}[j\leq\text{hlsize},\text{whi1}[[j]]=\text{whi1}[[j]]/\text{Norm}[\text{whi1}[[j]]];j=j+1];}\\ \boldsymbol{\text{whi2}=\text{Table}[\text{RandomReal}[\{-1.,1.\}],\{k,1,\text{hlsize}\},\{l,1,\text{fsize}\}];}\\ \boldsymbol{j=1;\text{While}[j\leq\text{hlsize},\text{whi2}[[j]]=\text{whi2}[[j]]/\text{Norm}[\text{whi2}[[j]]];j=j+1];}\\ \boldsymbol{\text{wout1}=\text{Table}[\text{RandomReal}[\{-1.,1.\}],\{l,1,19\},\{k,1,\text{hlsize}+\text{fsize}\}];}\\ \boldsymbol{\text{wout2}=\text{Table}[\text{RandomReal}[\{-1.,1.\}],\{l,1,19\},\{k,1,\text{hlsize}+\text{fsize}\}];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Initial values of the counters for epsilon and alpha. *)}}\\ \boldsymbol{\text{ialpha}=-1;}\\ \boldsymbol{\text{ieps}=0;}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Tables to be used for calculating RSI. *)}}\\ \boldsymbol{\text{rsip}=\text{Table}[0.,\{l,1,15\}];}\\ \boldsymbol{\text{rsiu}=\text{Table}[0.,\{l,1,14\}];}\\ \boldsymbol{\text{rsid}=\text{Table}[0.,\{l,1,14\}];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Variables for the average volumes and the max norms of the output weights. *)}}\\ \boldsymbol{\text{mv}=\text{Table}[0.,\{j,1,20\}];}\\ \boldsymbol{\text{nv}=0;}\\ \boldsymbol{\text{av}=0.;}\\ \boldsymbol{\text{max1}=1.;}\\ \boldsymbol{\text{max2}=1.;}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Initial mon, cns, sav, res, mlim. *)}}\\ \boldsymbol{\text{mon}=100.;\text{cns}=0.;\text{sav}=0.;\text{res}=0.;\text{mlim}=\text{mon};\text{mdf}=0;}\\ \boldsymbol{}\\ \boldsymbol{i=1;}\\ \boldsymbol{}\\ \boldsymbol{\text{Label}[\text{episode}];\text{(* Label used to start a new episode. *)}}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Initial price in the episode. *)}}\\ \boldsymbol{\text{ipr}=\text{prices}[[i*5-\text{vsize}+1]];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Calculate the average volumes. *)}}\\ \boldsymbol{\text{cav}=(\text{volumes}[[i*5-4]]+\text{volumes}[[i*5-3]]+\text{volumes}[[i*5-2]]+\text{volumes}[[i*5-1]]+\text{volumes}[[i*5]])/5.;}\\ \boldsymbol{j=1;\text{While}[j\leq 19,\text{mv}[[j]]=\text{mv}[[j+1]];j=j+1];}\\ \boldsymbol{\text{mv}[[j]]=\text{cav};}\\ \boldsymbol{\text{av}=\text{Mean}[\text{mv}];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Observe the prices in the current state. *)}}\\ \boldsymbol{j=1;\text{While}[j\leq\text{vsize},\text{pr}[[j]]=\text{prices}[[i*5-\text{vsize}+j]];j=j+1];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Relative price movements. *)}}\\ \boldsymbol{j=1;\text{While}[j\leq\text{vsize}-1,}\\ \boldsymbol{\text{nmd1}[[j]]=(\text{pr}[[j+1]]-\text{pr}[[j]])/\text{pr}[[j]];}\\ \boldsymbol{j=j+1];}\\ \boldsymbol{j=1;\text{While}[j\leq\text{vsize}-2,}\\ \boldsymbol{\text{nmd2}[[j]]=(\text{nmd1}[[j+1]]-\text{nmd1}[[j]])/\text{Abs}[\text{nmd1}[[j]]];}\\ \boldsymbol{j=j+1];}\\ \boldsymbol{j=1;\text{While}[j\leq\text{vsize}-3,}\\ \boldsymbol{\text{nmd3}[[j]]=(\text{nmd2}[[j+1]]-\text{nmd2}[[j]])/\text{Abs}[\text{nmd2}[[j]]];}\\ \boldsymbol{j=j+1];}\\ \boldsymbol{j=1;\text{While}[j\leq\text{vsize}-4,}\\ \boldsymbol{\text{nmd4}[[j]]=(\text{nmd3}[[j+1]]-\text{nmd3}[[j]])/\text{Abs}[\text{nmd3}[[j]]];}\\ \boldsymbol{j=j+1];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Calculate RSI for 15 points. *)}}\\ \boldsymbol{j=1;\text{While}[j\leq 10,\text{rsip}[[j]]=\text{rsip}[[j+5]];j=j+1];}\\ \boldsymbol{j=1;\text{While}[j\leq\text{vsize},\text{rsip}[[10+j]]=\text{pr}[[j]];j=j+1];}\\ \boldsymbol{j=1;\text{While}[j\leq 14,}\\ \boldsymbol{\text{If}[\text{rsip}[[j+1]]>\text{rsip}[[j]],\text{rsiu}[[j]]=\text{rsip}[[j+1]]-\text{rsip}[[j]];\text{rsid}[[j]]=0.];}\\ \boldsymbol{\text{If}[\text{rsip}[[j+1]]<\text{rsip}[[j]],\text{rsid}[[j]]=\text{rsip}[[j]]-\text{rsip}[[j+1]];\text{rsiu}[[j]]=0.];}\\ \boldsymbol{\text{If}[\text{rsip}[[j+1]]==\text{rsip}[[j]],\text{rsiu}[[j]]=0.;\text{rsid}[[j]]=0.];}\\ \boldsymbol{j=j+1];}\\ \boldsymbol{\text{If}[\text{Mean}[\text{rsid}]==0.,\text{rsi}=100.,\text{rsi}=100.-(100./(1.+(\text{Mean}[\text{rsiu}]/\text{Mean}[\text{rsid}])))];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Construct the feature vector (same for all actions). *)}}\\ \boldsymbol{\text{feat}[[1]]=0.;}\\ \boldsymbol{j=1;k=1;\text{While}[j\leq\text{vsize},\text{feat}[[k+1]]=\text{pr}[[j]];k=k+1;j=j+1];}\\ \boldsymbol{\text{feat}[[k+1]]=\text{ipr};k=k+1;}\\ \boldsymbol{\text{feat}[[k+1]]=(\text{pr}[[\text{vsize}]]-\text{ipr})/\text{ipr};k=k+1;}\\ \boldsymbol{\text{feat}[[k+1]]=\text{mon};k=k+1;}\\ \boldsymbol{\text{feat}[[k+1]]=\text{cns};k=k+1;}\\ \boldsymbol{\text{feat}[[k+1]]=\text{cav};k=k+1;}\\ \boldsymbol{\text{feat}[[k+1]]=\text{av};k=k+1;}\\ \boldsymbol{\text{feat}[[k+1]]=(\text{cav}-\text{av})/\text{av};k=k+1;}\\ \boldsymbol{\text{feat}[[k+1]]=(\text{volumes}[[i*5]]-\text{av})/\text{av};k=k+1;}\\ \boldsymbol{\text{feat}[[k+1]]=(\text{volumes}[[i*5]]-\text{cav})/\text{cav};k=k+1;}\\ \boldsymbol{\text{feat}[[k+1]]=\text{rsi};k=k+1;}\\ \boldsymbol{j=1;\text{While}[j\leq\text{vsize}-1,\text{feat}[[k+1]]=\text{nmd1}[[j]];k=k+1;j=j+1];}\\ \boldsymbol{j=1;\text{While}[j\leq\text{vsize}-2,\text{feat}[[k+1]]=\text{nmd2}[[j]];k=k+1;j=j+1];}\\ \boldsymbol{j=1;\text{While}[j\leq\text{vsize}-3,\text{feat}[[k+1]]=\text{nmd3}[[j]];k=k+1;j=j+1];}\\ \boldsymbol{j=1;\text{While}[j\leq\text{vsize}-4,\text{feat}[[k+1]]=\text{nmd4}[[j]];k=k+1;j=j+1];}\\ \boldsymbol{\text{feat}[[k+1]]=\text{mlim};k=k+1;}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Rescale the feature vector, so its norm is 6. *)}}\\ \boldsymbol{\text{feat}=6.*(\text{feat}/\text{Norm}[\text{feat}]);}\\ \boldsymbol{\text{feat}[[1]]=1.;}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Output of the hidden layers. *)}}\\ \boldsymbol{n=1;\text{While}[n\leq\text{hlsize},}\\ \boldsymbol{q=0;j=1;\text{While}[j\leq\text{fsize},q=q+\text{whi1}[[n,j]]*\text{feat}[[j]];j=j+1];}\\ \boldsymbol{\text{hlayer1}[[n]]=1./(1.+\text{Exp}[-q]);}\\ \boldsymbol{q=0;j=1;\text{While}[j\leq\text{fsize},q=q+\text{whi2}[[n,j]]*\text{feat}[[j]];j=j+1];}\\ \boldsymbol{\text{hlayer2}[[n]]=1./(1.+\text{Exp}[-q]);}\\ \boldsymbol{n=n+1];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Gradients of the Q-functions, taken in the current state (same for all actions), which are just}}\\ \boldsymbol{\text{concatenations of the feature vector with the hidden layer outputs. *)}}\\ \boldsymbol{j=1;\text{While}[j\leq\text{fsize},\text{gradq1}[[j]]=\text{feat}[[j]];j=j+1];}\\ \boldsymbol{j=1;\text{While}[j\leq\text{hlsize},\text{gradq1}[[\text{fsize}+j]]=\text{hlayer1}[[j]];j=j+1];}\\ \boldsymbol{j=1;\text{While}[j\leq\text{fsize},\text{gradq2}[[j]]=\text{feat}[[j]];j=j+1];}\\ \boldsymbol{j=1;\text{While}[j\leq\text{hlsize},\text{gradq2}[[\text{fsize}+j]]=\text{hlayer2}[[j]];j=j+1];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Calculate wth in the current state. *)}}\\ \boldsymbol{\text{wth}=\text{mon}+(\text{cns}*\text{prices}[[i*5]]);}\\ \boldsymbol{}\\ \boldsymbol{\text{While}[(i+1)*5\leq\text{simlength},}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Calculate Q1 and Q2 for every action in the given state. *)}}\\ \boldsymbol{n=1;\text{While}[n\leq 19,}\\ \boldsymbol{q=0;j=1;\text{While}[j\leq\text{hlsize}+\text{fsize},q=q+\text{wout1}[[n,j]]*\text{gradq1}[[j]];j=j+1];}\\ \boldsymbol{\text{qarr1}[[n]]=q;}\\ \boldsymbol{q=0;j=1;\text{While}[j\leq\text{hlsize}+\text{fsize},q=q+\text{wout2}[[n,j]]*\text{gradq2}[[j]];j=j+1];}\\ \boldsymbol{\text{qarr2}[[n]]=q;}\\ \boldsymbol{n=n+1];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Calculate the average of Q1 and Q2, which is needed to choose an action. *)}}\\ \boldsymbol{\text{qarr}=(\text{qarr1}+\text{qarr2})/2.;}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Update epsilon. *)}}\\ \boldsymbol{\text{rand}=\text{RandomReal}[];}\\ \boldsymbol{\text{If}[((\text{rand}\leq\text{probeps})\&\&(\text{ieps}\geq\text{Ceiling}[(\text{Exp}[1./0.2]-2.)/5.])),\text{ieps}=\text{Ceiling}[(\text{Exp}[1./0.2]-2.)/5.],\text{ieps}=\text{ieps}+1];}\\ \boldsymbol{\text{eps}=1./\text{Log}[\text{ieps}*5.+2.];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Choose an action. *)}}\\ \boldsymbol{\text{rand}=\text{RandomReal}[];}\\ \boldsymbol{\text{If}[\text{rand}\leq\text{eps},a=\text{RandomInteger}[\{1,19\}],a=\text{Ordering}[\text{qarr},-1][[1]]];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Update alpha. *)}}\\ \boldsymbol{\text{ialpha}=\text{ialpha}+1;}\\ \boldsymbol{\text{alpha}=0.001+(1./2.)*(1.-0.001)*(1.+\text{Cos}[\text{Pi}*\text{ialpha}/1000.]);}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Take the chosen action. *)}}\\ \boldsymbol{\text{fff}=1.;\text{(* This tracks for insufficient mon or cns in order to reflect it in the reward later. *)}}\\ \boldsymbol{\text{If}[a==1,\text{If}[\text{mon}<10.,\text{fff}=-1.;\text{Goto}[\text{fail}]];\text{cns}=\text{cns}+((99.9/100.)*(10./\text{prices}[[i*5]]));\text{mon}=\text{mon}-10.];}\\ \boldsymbol{\text{If}[a==2,\text{If}[\text{mon}<20.,\text{fff}=-1.;\text{Goto}[\text{fail}]];\text{cns}=\text{cns}+((99.9/100.)*(20./\text{prices}[[i*5]]));\text{mon}=\text{mon}-20.];}\\ \boldsymbol{\text{If}[a==3,\text{If}[\text{mon}<30.,\text{fff}=-1.;\text{Goto}[\text{fail}]];\text{cns}=\text{cns}+((99.9/100.)*(30./\text{prices}[[i*5]]));\text{mon}=\text{mon}-30.];}\\ \boldsymbol{\text{If}[a==4,\text{If}[\text{mon}<40.,\text{fff}=-1.;\text{Goto}[\text{fail}]];\text{cns}=\text{cns}+((99.9/100.)*(40./\text{prices}[[i*5]]));\text{mon}=\text{mon}-40.];}\\ \boldsymbol{\text{If}[a==5,\text{If}[\text{mon}<50.,\text{fff}=-1.;\text{Goto}[\text{fail}]];\text{cns}=\text{cns}+((99.9/100.)*(50./\text{prices}[[i*5]]));\text{mon}=\text{mon}-50.];}\\ \boldsymbol{\text{If}[a==6,\text{If}[\text{mon}<60.,\text{fff}=-1.;\text{Goto}[\text{fail}]];\text{cns}=\text{cns}+((99.9/100.)*(60./\text{prices}[[i*5]]));\text{mon}=\text{mon}-60.];}\\ \boldsymbol{\text{If}[a==7,\text{If}[\text{mon}<70.,\text{fff}=-1.;\text{Goto}[\text{fail}]];\text{cns}=\text{cns}+((99.9/100.)*(70./\text{prices}[[i*5]]));\text{mon}=\text{mon}-70.];}\\ \boldsymbol{\text{If}[a==8,\text{If}[\text{mon}<80.,\text{fff}=-1.;\text{Goto}[\text{fail}]];\text{cns}=\text{cns}+((99.9/100.)*(80./\text{prices}[[i*5]]));\text{mon}=\text{mon}-80.];}\\ \boldsymbol{\text{If}[a==9,\text{If}[\text{mon}<90.,\text{fff}=-1.;\text{Goto}[\text{fail}]];\text{cns}=\text{cns}+((99.9/100.)*(90./\text{prices}[[i*5]]));\text{mon}=\text{mon}-90.];}\\ \boldsymbol{\text{If}[a==10,\text{If}[\text{prices}[[i*5]]*\text{cns}<10.,\text{fff}=-1.;\text{Goto}[\text{fail}]];\text{mon}=\text{mon}+((99.9/100.)*(10.));\text{cns}=\text{cns}-(10./\text{prices}[[i*5]])];}\\ \boldsymbol{\text{If}[a==11,\text{If}[\text{prices}[[i*5]]*\text{cns}<20.,\text{fff}=-1.;\text{Goto}[\text{fail}]];\text{mon}=\text{mon}+((99.9/100.)*(20.));\text{cns}=\text{cns}-(20./\text{prices}[[i*5]])];}\\ \boldsymbol{\text{If}[a==12,\text{If}[\text{prices}[[i*5]]*\text{cns}<30.,\text{fff}=-1.;\text{Goto}[\text{fail}]];\text{mon}=\text{mon}+((99.9/100.)*(30.));\text{cns}=\text{cns}-(30./\text{prices}[[i*5]])];}\\ \boldsymbol{\text{If}[a==13,\text{If}[\text{prices}[[i*5]]*\text{cns}<40.,\text{fff}=-1.;\text{Goto}[\text{fail}]];\text{mon}=\text{mon}+((99.9/100.)*(40.));\text{cns}=\text{cns}-(40./\text{prices}[[i*5]])];}\\ \boldsymbol{\text{If}[a==14,\text{If}[\text{prices}[[i*5]]*\text{cns}<50.,\text{fff}=-1.;\text{Goto}[\text{fail}]];\text{mon}=\text{mon}+((99.9/100.)*(50.));\text{cns}=\text{cns}-(50./\text{prices}[[i*5]])];}\\ \boldsymbol{\text{If}[a==15,\text{If}[\text{prices}[[i*5]]*\text{cns}<60.,\text{fff}=-1.;\text{Goto}[\text{fail}]];\text{mon}=\text{mon}+((99.9/100.)*(60.));\text{cns}=\text{cns}-(60./\text{prices}[[i*5]])];}\\ \boldsymbol{\text{If}[a==16,\text{If}[\text{prices}[[i*5]]*\text{cns}<70.,\text{fff}=-1.;\text{Goto}[\text{fail}]];\text{mon}=\text{mon}+((99.9/100.)*(70.));\text{cns}=\text{cns}-(70./\text{prices}[[i*5]])];}\\ \boldsymbol{\text{If}[a==17,\text{If}[\text{prices}[[i*5]]*\text{cns}<80.,\text{fff}=-1.;\text{Goto}[\text{fail}]];\text{mon}=\text{mon}+((99.9/100.)*(80.));\text{cns}=\text{cns}-(80./\text{prices}[[i*5]])];}\\ \boldsymbol{\text{If}[a==18,\text{If}[\text{prices}[[i*5]]*\text{cns}<90.,\text{fff}=-1.;\text{Goto}[\text{fail}]];\text{mon}=\text{mon}+((99.9/100.)*(90.));\text{cns}=\text{cns}-(90./\text{prices}[[i*5]])];}\\ \boldsymbol{\text{(* }\text{If}[a==19,\text{HOLD}];\text{ *)}}\\ \boldsymbol{\text{Label}[\text{fail}];}\\ \boldsymbol{\text{If}[((\text{mon}<0.)\|(\text{cns}<0.)),\text{Print}[\text{{``}Negative mon or cns!{''}}]];\text{(* Just to be sure. *)}}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Observe the reward (by calculating wth in the next state). *)}}\\ \boldsymbol{\text{wthnew}=\text{mon}+(\text{cns}*\text{prices}[[(i+1)*5]]);}\\ \boldsymbol{\text{rew}=\text{wthnew}-\text{wth};}\\ \boldsymbol{\text{If}[\text{fff}<0.,}\\ \boldsymbol{\text{rew}=\text{rew}-((\text{rew}/2.){}^{\wedge}2)-0.1,}\\ \boldsymbol{\text{rew}=\text{rew}-((\text{rew}/2.){}^{\wedge}2)];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Observe the prices in the next state. *)}}\\ \boldsymbol{j=1;\text{While}[j\leq\text{vsize},\text{pr}[[j]]=\text{prices}[[(i+1)*5-\text{vsize}+j]];j=j+1];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Calculate RSI for 15 points. *)}}\\ \boldsymbol{j=1;\text{While}[j\leq 10,\text{rsip}[[j]]=\text{rsip}[[j+5]];j=j+1];}\\ \boldsymbol{j=1;\text{While}[j\leq\text{vsize},\text{rsip}[[10+j]]=\text{pr}[[j]];j=j+1];}\\ \boldsymbol{j=1;\text{While}[j\leq 14,}\\ \boldsymbol{\text{If}[\text{rsip}[[j+1]]>\text{rsip}[[j]],\text{rsiu}[[j]]=\text{rsip}[[j+1]]-\text{rsip}[[j]];\text{rsid}[[j]]=0.];}\\ \boldsymbol{\text{If}[\text{rsip}[[j+1]]<\text{rsip}[[j]],\text{rsid}[[j]]=\text{rsip}[[j]]-\text{rsip}[[j+1]];\text{rsiu}[[j]]=0.];}\\ \boldsymbol{\text{If}[\text{rsip}[[j+1]]==\text{rsip}[[j]],\text{rsiu}[[j]]=0.;\text{rsid}[[j]]=0.];}\\ \boldsymbol{j=j+1];}\\ \boldsymbol{\text{If}[\text{Mean}[\text{rsid}]==0.,\text{rsi}=100.,\text{rsi}=100.-(100./(1.+(\text{Mean}[\text{rsiu}]/\text{Mean}[\text{rsid}])))];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Check for the three possible terminal states. *)}}\\ \boldsymbol{\text{If}[\text{mon}>\text{mlim},\text{(* Terminal state 1. *)}}\\ \boldsymbol{\text{rew}=\text{rew}+((\text{mon}-\text{mlim})*0.34);}\\ \boldsymbol{\text{rand}=\text{RandomReal}[];}\\ \boldsymbol{\text{If}[\text{rand}\leq 0.5,}\\ \boldsymbol{\text{wout1}[[a]]=\text{wout1}[[a]]+\text{alpha}*(\text{rew}-\text{qarr1}[[a]])*\text{gradq1};}\\ \boldsymbol{\text{max1}=\text{Max}[\text{max1},\text{Norm}[\text{wout1}[[a]]]];}\\ \boldsymbol{\text{If}[\text{Norm}[\text{wout1}[[a]]]>1.,\text{wout1}[[a]]=\text{wout1}[[a]]/\text{max1}],}\\ \boldsymbol{\text{wout2}[[a]]=\text{wout2}[[a]]+\text{alpha}*(\text{rew}-\text{qarr2}[[a]])*\text{gradq2};}\\ \boldsymbol{\text{max2}=\text{Max}[\text{max2},\text{Norm}[\text{wout2}[[a]]]];}\\ \boldsymbol{\text{If}[\text{Norm}[\text{wout2}[[a]]]>1.,\text{wout2}[[a]]=\text{wout2}[[a]]/\text{max2}]];}\\ \boldsymbol{\text{mdf}=\text{mon}-\text{mlim};}\\ \boldsymbol{\text{sav}=\text{sav}+(\text{mdf}*0.34);}\\ \boldsymbol{\text{res}=\text{res}+(\text{mdf}*0.33);}\\ \boldsymbol{\text{mon}=\text{mlim}+(\text{mdf}*0.33);}\\ \boldsymbol{\text{mlim}=\text{mon}+\text{mdf};}\\ \boldsymbol{i=i+2;}\\ \boldsymbol{\text{If}[i*5>\text{simlength},\text{Goto}[\text{break}]];}\\ \boldsymbol{\text{Goto}[\text{episode}],}\\ \boldsymbol{\text{If}[((\text{wthnew}<\text{mlimn})\&\&(\text{qarr}[[a]]>0.)\&\&(\text{rsi}>70.)),\text{(* Terminal state 2. *)}}\\ \boldsymbol{\text{rand}=\text{RandomReal}[];}\\ \boldsymbol{\text{If}[\text{rand}\leq 0.5,}\\ \boldsymbol{\text{wout1}[[a]]=\text{wout1}[[a]]+\text{alpha}*(\text{rew}-\text{qarr1}[[a]])*\text{gradq1};}\\ \boldsymbol{\text{max1}=\text{Max}[\text{max1},\text{Norm}[\text{wout1}[[a]]]];}\\ \boldsymbol{\text{If}[\text{Norm}[\text{wout1}[[a]]]>1.,\text{wout1}[[a]]=\text{wout1}[[a]]/\text{max1}],}\\ \boldsymbol{\text{wout2}[[a]]=\text{wout2}[[a]]+\text{alpha}*(\text{rew}-\text{qarr2}[[a]])*\text{gradq2};}\\ \boldsymbol{\text{max2}=\text{Max}[\text{max2},\text{Norm}[\text{wout2}[[a]]]];}\\ \boldsymbol{\text{If}[\text{Norm}[\text{wout2}[[a]]]>1.,\text{wout2}[[a]]=\text{wout2}[[a]]/\text{max2}]];}\\ \boldsymbol{\text{mon}=\text{mon}+(\text{res}/2.);\text{res}=\text{res}-(\text{res}/2.);}\\ \boldsymbol{\text{If}[\text{mon}\geq\text{mlimn},\text{mlim}=\text{mon},\text{mlim}=\text{mlimn}];}\\ \boldsymbol{i=i+2;}\\ \boldsymbol{\text{If}[i*5>\text{simlength},\text{Goto}[\text{break}]];}\\ \boldsymbol{\text{Goto}[\text{episode}]];}\\ \boldsymbol{\text{If}[((\text{wthnew}\geq\text{mlimn})\&\&(\text{qarr}[[a]]<0.)\&\&(\text{rsi}<30.)),\text{(* Terminal state 3. *)}}\\ \boldsymbol{\text{rand}=\text{RandomReal}[];}\\ \boldsymbol{\text{If}[\text{rand}\leq 0.5,}\\ \boldsymbol{\text{wout1}[[a]]=\text{wout1}[[a]]+\text{alpha}*(\text{rew}-\text{qarr1}[[a]])*\text{gradq1};}\\ \boldsymbol{\text{max1}=\text{Max}[\text{max1},\text{Norm}[\text{wout1}[[a]]]];}\\ \boldsymbol{\text{If}[\text{Norm}[\text{wout1}[[a]]]>1.,\text{wout1}[[a]]=\text{wout1}[[a]]/\text{max1}],}\\ \boldsymbol{\text{wout2}[[a]]=\text{wout2}[[a]]+\text{alpha}*(\text{rew}-\text{qarr2}[[a]])*\text{gradq2};}\\ \boldsymbol{\text{max2}=\text{Max}[\text{max2},\text{Norm}[\text{wout2}[[a]]]];}\\ \boldsymbol{\text{If}[\text{Norm}[\text{wout2}[[a]]]>1.,\text{wout2}[[a]]=\text{wout2}[[a]]/\text{max2}]];}\\ \boldsymbol{\text{mlim}=\text{wthnew};}\\ \boldsymbol{i=i+2;}\\ \boldsymbol{\text{If}[i*5>\text{simlength},\text{Goto}[\text{break}]];}\\ \boldsymbol{\text{Goto}[\text{episode}]]];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Calculate the average volumes. *)}}\\ \boldsymbol{\text{cav}=(\text{volumes}[[(i+1)*5-4]]+\text{volumes}[[(i+1)*5-3]]+\text{volumes}[[(i+1)*5-2]]+\text{volumes}[[(i+1)*5-1]]+\text{volumes}[[(i+1)*5]])/5.;}\\ \boldsymbol{j=1;\text{While}[j\leq 19,\text{mv}[[j]]=\text{mv}[[j+1]];j=j+1];}\\ \boldsymbol{\text{mv}[[j]]=\text{cav};}\\ \boldsymbol{\text{av}=\text{Mean}[\text{mv}];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Relative price movements. *)}}\\ \boldsymbol{j=1;\text{While}[j\leq\text{vsize}-1,}\\ \boldsymbol{\text{nmd1}[[j]]=(\text{pr}[[j+1]]-\text{pr}[[j]])/\text{pr}[[j]];}\\ \boldsymbol{j=j+1];}\\ \boldsymbol{j=1;\text{While}[j\leq\text{vsize}-2,}\\ \boldsymbol{\text{nmd2}[[j]]=(\text{nmd1}[[j+1]]-\text{nmd1}[[j]])/\text{Abs}[\text{nmd1}[[j]]];}\\ \boldsymbol{j=j+1];}\\ \boldsymbol{j=1;\text{While}[j\leq\text{vsize}-3,}\\ \boldsymbol{\text{nmd3}[[j]]=(\text{nmd2}[[j+1]]-\text{nmd2}[[j]])/\text{Abs}[\text{nmd2}[[j]]];}\\ \boldsymbol{j=j+1];}\\ \boldsymbol{j=1;\text{While}[j\leq\text{vsize}-4,}\\ \boldsymbol{\text{nmd4}[[j]]=(\text{nmd3}[[j+1]]-\text{nmd3}[[j]])/\text{Abs}[\text{nmd3}[[j]]];}\\ \boldsymbol{j=j+1];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Construct the feature vector (same for all actions). *)}}\\ \boldsymbol{\text{feat}[[1]]=0.;}\\ \boldsymbol{j=1;k=1;\text{While}[j\leq\text{vsize},\text{feat}[[k+1]]=\text{pr}[[j]];k=k+1;j=j+1];}\\ \boldsymbol{\text{feat}[[k+1]]=\text{ipr};k=k+1;}\\ \boldsymbol{\text{feat}[[k+1]]=(\text{pr}[[\text{vsize}]]-\text{ipr})/\text{ipr};k=k+1;}\\ \boldsymbol{\text{feat}[[k+1]]=\text{mon};k=k+1;}\\ \boldsymbol{\text{feat}[[k+1]]=\text{cns};k=k+1;}\\ \boldsymbol{\text{feat}[[k+1]]=\text{cav};k=k+1;}\\ \boldsymbol{\text{feat}[[k+1]]=\text{av};k=k+1;}\\ \boldsymbol{\text{feat}[[k+1]]=(\text{cav}-\text{av})/\text{av};k=k+1;}\\ \boldsymbol{\text{feat}[[k+1]]=(\text{volumes}[[(i+1)*5]]-\text{av})/\text{av};k=k+1;}\\ \boldsymbol{\text{feat}[[k+1]]=(\text{volumes}[[(i+1)*5]]-\text{cav})/\text{cav};k=k+1;}\\ \boldsymbol{\text{feat}[[k+1]]=\text{rsi};k=k+1;}\\ \boldsymbol{j=1;\text{While}[j\leq\text{vsize}-1,\text{feat}[[k+1]]=\text{nmd1}[[j]];k=k+1;j=j+1];}\\ \boldsymbol{j=1;\text{While}[j\leq\text{vsize}-2,\text{feat}[[k+1]]=\text{nmd2}[[j]];k=k+1;j=j+1];}\\ \boldsymbol{j=1;\text{While}[j\leq\text{vsize}-3,\text{feat}[[k+1]]=\text{nmd3}[[j]];k=k+1;j=j+1];}\\ \boldsymbol{j=1;\text{While}[j\leq\text{vsize}-4,\text{feat}[[k+1]]=\text{nmd4}[[j]];k=k+1;j=j+1];}\\ \boldsymbol{\text{feat}[[k+1]]=\text{mlim};k=k+1;}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Rescale the feature vector, so its norm is 6. *)}}\\ \boldsymbol{\text{feat}=6.*(\text{feat}/\text{Norm}[\text{feat}]);}\\ \boldsymbol{\text{feat}[[1]]=1.;}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Output of the hidden layers. *)}}\\ \boldsymbol{n=1;\text{While}[n\leq\text{hlsize},}\\ \boldsymbol{q=0;j=1;\text{While}[j\leq\text{fsize},q=q+\text{whi1}[[n,j]]*\text{feat}[[j]];j=j+1];}\\ \boldsymbol{\text{hlayer1}[[n]]=1./(1.+\text{Exp}[-q]);}\\ \boldsymbol{q=0;j=1;\text{While}[j\leq\text{fsize},q=q+\text{whi2}[[n,j]]*\text{feat}[[j]];j=j+1];}\\ \boldsymbol{\text{hlayer2}[[n]]=1./(1.+\text{Exp}[-q]);}\\ \boldsymbol{n=n+1];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Gradients of the Q-functions, taken in the next state (same for all actions), which are just}}\\ \boldsymbol{\text{concatenations of the feature vector with the hidden layer outputs. *)}}\\ \boldsymbol{j=1;\text{While}[j\leq\text{fsize},\text{gradq1new}[[j]]=\text{feat}[[j]];j=j+1];}\\ \boldsymbol{j=1;\text{While}[j\leq\text{hlsize},\text{gradq1new}[[\text{fsize}+j]]=\text{hlayer1}[[j]];j=j+1];}\\ \boldsymbol{j=1;\text{While}[j\leq\text{fsize},\text{gradq2new}[[j]]=\text{feat}[[j]];j=j+1];}\\ \boldsymbol{j=1;\text{While}[j\leq\text{hlsize},\text{gradq2new}[[\text{fsize}+j]]=\text{hlayer2}[[j]];j=j+1];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Calculate Q1 and Q2 for every action in the next state. *)}}\\ \boldsymbol{n=1;\text{While}[n\leq 19,}\\ \boldsymbol{q=0;j=1;\text{While}[j\leq\text{hlsize}+\text{fsize},q=q+\text{wout1}[[n,j]]*\text{gradq1new}[[j]];j=j+1];}\\ \boldsymbol{\text{qarr1new}[[n]]=q;}\\ \boldsymbol{q=0;j=1;\text{While}[j\leq\text{hlsize}+\text{fsize},q=q+\text{wout2}[[n,j]]*\text{gradq2new}[[j]];j=j+1];}\\ \boldsymbol{\text{qarr2new}[[n]]=q;}\\ \boldsymbol{n=n+1];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Update the output weights. *)}}\\ \boldsymbol{\text{rand}=\text{RandomReal}[];}\\ \boldsymbol{\text{If}[\text{rand}\leq 0.5,}\\ \boldsymbol{\text{amax}=\text{Ordering}[\text{qarr1new},-1][[1]];}\\ \boldsymbol{\text{wout1}[[a]]=\text{wout1}[[a]]+\text{alpha}*(\text{rew}+\text{gamma}*\text{qarr2new}[[\text{amax}]]-\text{qarr1}[[a]])*\text{gradq1};}\\ \boldsymbol{\text{max1}=\text{Max}[\text{max1},\text{Norm}[\text{wout1}[[a]]]];}\\ \boldsymbol{\text{If}[\text{Norm}[\text{wout1}[[a]]]>1.,\text{wout1}[[a]]=\text{wout1}[[a]]/\text{max1}],}\\ \boldsymbol{\text{amax}=\text{Ordering}[\text{qarr2new},-1][[1]];}\\ \boldsymbol{\text{wout2}[[a]]=\text{wout2}[[a]]+\text{alpha}*(\text{rew}+\text{gamma}*\text{qarr1new}[[\text{amax}]]-\text{qarr2}[[a]])*\text{gradq2};}\\ \boldsymbol{\text{max2}=\text{Max}[\text{max2},\text{Norm}[\text{wout2}[[a]]]];}\\ \boldsymbol{\text{If}[\text{Norm}[\text{wout2}[[a]]]>1.,\text{wout2}[[a]]=\text{wout2}[[a]]/\text{max2}]];}\\ \boldsymbol{}\\ \boldsymbol{\text{(* Make the next value of wth and the next states current. *)}}\\ \boldsymbol{\text{wth}=\text{wthnew};}\\ \boldsymbol{\text{gradq1}=\text{gradq1new};}\\ \boldsymbol{\text{gradq2}=\text{gradq2new};}\\ \boldsymbol{}\\ \boldsymbol{i=i+1;}\\ \boldsymbol{}\\ \boldsymbol{];}\\ \boldsymbol{}\\ \boldsymbol{\text{Label}[\text{break}];\text{(* Label used to exit the loop, if there are no more points in the test data. *)}}\\ \boldsymbol{\text{savarray}[[m]]=\text{sav};\text{(* Write sav at the end of the current run. *)}}\\ \boldsymbol{\text{twtharray}[[m]]=\text{sav}+\text{wthnew}+\text{res};\text{(* Write twth at the end of the current run. *)}}\\ \boldsymbol{}\\ \boldsymbol{m=m+1}\\ \boldsymbol{}\\ \boldsymbol{];}\\ \boldsymbol{}\\ \boldsymbol{\text{Export}[\text{{``}sav.txt{''}},\text{savarray}]}\\ \boldsymbol{\text{Export}[\text{{``}total.txt{''}},\text{twtharray}]}
The reference list from the paper itself. Each links out to its DOI / PubMed record.
- 1[1] P. Treleaven, M. Galas, V. Lalchand, Algorithmic trading review, Communications of the ACM 56(11), 76-85 (2013).
- 2[2] D. Silver, A. Huang, C. J. Maddison et al. , Mastering the game of Go with deep neural networks and tree search, Nature 529, 484-489 (2016).
- 3[3] O. Vinyals, I. Babuschkin, W. M. Czarnecki et al. , Grandmaster level in Star Craft II using multi-agent reinforcement learning, Nature 575, 350-354 (2019).
- 4[4] A. Millea, Deep Reinforcement Learning for Trading—A Critical Survey, Data 6(11), 119 (2021).
- 5[5] G.-B. Huang, Q.-Y. Zhu, C.-K. Siew, Extreme learning machine: a new learning scheme of feedforward neural networks, 2004 IEEE International Joint Conference on Neural Networks, Budapest, Hungary, Volume 2, 985-990 (2004).
- 6[6] S. Ding, X. Xu, R. Nie, Extreme learning machine and its applications, Neural Computing and Applications 25, 549-556 (2014).
- 7[7] G. Li, P. Niu, X. Duan, X. Zhang, Fast learning network: a novel artificial neural network with a fast learning speed, Neural Computing and Applications 24, 1683-1695 (2014).
- 8[8] R. S. Sutton, A. G. Barto, Reinforcement Learning: An Introduction, 2nd Ed., The MIT Press, Cambridge, MA (2018).
