Neural Network Forex Trading System

In this post we are going to develop a neural network forex trading system. We will use a simple neural network called NNET. In recent years, artificial intelligence  and deep learning has taken over the trading world. Hedge funds and big banks are using these advanced cutting edge tools to beat the market on a daily basis. On the other hand, most retail traders have no clue what this deep learning thing is and how they can use artificial intelligence in trading. Have you noticed one thing in the past few years? Traditional technical analysis tools like MACD, Stochastic, CCI, Bollinger Bands don’t work like they used to work in the past. But one thing still works. That is price action that includes candlestick patterns. Did you read the post on a USDJPY trade that made 600 pips with a 10 pip stop loss? I use price action a lot in my trading. The key is to keep the risk as low as possible.

Time has come for you to start learning and using these new tools that big Wall Street firms are already using. In the last few years there has been a tremendous explosion in the computational power of computers. This has made it possible to use sophisticated data mining algorithmis to make predictions on market behavior. Neural Networks were developed many decades back. Neural networks work just like the human brain neurons. When there is a signal. it can only fire the neuron if it has sufficient strength. This is how the human brain does all its work. Neural networks also work on the same principle. If we have  an input signal, it should be above a certain threshold to trigger an output signal. Now there are many neural network software that are being sold in the market. You can buy a good neural network software from $500 to $2,000. It is easy to use these software as everything has been done for you. But I will take a different approach here. Instead of buying an expensive neural network software I suggest you develop your own neural network trading systems. So we will be developing a neural network forex trading system in this post. Keep on reading if you want to learn how to do it. Did you read this post on how to predict gold price using kernel ridge regression?

It might take you a few months to learn the theory behind neural networks but it will be worth the effort. Once you have developed the art of training neural network models, you can use it in any field. So if you are interested in knowing how these models are developed keep on reading this post. You should learn R as well as Python. R and Python are powerful data science scripting languages that allow you to do a lot of financial modelling that you cannot do in Excel. Time series analysis is very important when it comes to financial modelling. Price is a financial time series. We record the closing price after every 1 hour or 4 hours regularly. This sequence of closing price constitutes a time series. We as traders know that past price can be used to predict the future price. This is precisely what time series analysis also assumes. We can use past price to predict future price using autoregressive models. I have developed a course on Time Series Analysis for Traders that you can take a look at. Many traders have no idea how to use time series models in their trading. I show you how to use time series models in your trading and get great results.

Both R and Python are easy to learn. With a little effort you can learn these languages. In this post we will be using R. R is a powerful statistical language that is used widely in academia. You should R installed on your computer as well as RStudio. Both are open source. So you don’t have to pay anything for using R. I have developed a course on Python Machine Learning for Traders. In this course I show you how to develop different machine learning models for your trading system using Python. As said in the start algorithmic trading has become very popular now a days. Days of manual trading are coming to an end. You should start learning fundamentals of algorithmic trading. Today more than 80% of the trades that are being placed at Wall Street are being placed by algorithms. 21st century is being called the century of algorithms. Algorithms are revolutionizing all field of life from health, medicine, car driving, aeroplane flying, detecting bank frauds and all sorts of things. I have developed this course on Algorithmic Trading with Python that you can take a look at. Don’t worry I have also developed courses on R that traders can use.

Now when you develop a neural network model, feature selection is very important. Features are the inputs that you give to the model to do the calculations and make predictions. I have been trading for many years now and know the importance of candlesticks as said above. Candlestick patterns are good leading signals. Candlestick patterns are mostly 2 stick patterns and 3 stick patterns. So we will use Open, High, Low and Close of the price to develop features that try to model candlesticks. We will see if we can use these features to predict the market. We will be using high frequency data especially M1 timeframe. Yes, I am talking about 1 minute timeframe. Did you check this trend following high frequency trading system? The model that we develop can be used on other timeframes also like the 15 minute, 30 minute, 60 minute, 240 minute. We can do that later. If we can predict the price after each 5 minutes with lets say 80% accuracy we can use that knowledge to trade 5 minute forex binary options as well. Below is the candlestick chart of the 1 minute GBPUSD data.

1 Minute Candlestick chart

As you can see R can make very beautiful candlestick charts very fast. If you have a quadcore computer than you can make R even more fast by using Microsoft R Open. I explain eerything how to do it in my course on R. The idea behind using high frequency data was that I wanted to show how fast we can do the calculations and make the predictions using R. I will develop three Neural Network Forex Trading Models .

Neural Network Forex Trading System 1

In the first model we take the closing price as feature number 1. We then use it to develop features like (Close-Open)/Open and (high-Low)/Low. Now we lag these 3 input features 1 time and 2 time so that we have a total of 9 input features. We will use a simple neural network model known as a feed forward neural network with one hidden layer. The hidden layer has got 10 neurons. Lets’ see if we get a model that can make good predictions. Below is the R code.

> library(quantmod)
Loading required package: xts
Loading required package: zoo

Attaching package: ‘zoo’

The following objects are masked from ‘package:base’:

    as.Date, as.Date.numeric

Loading required package: TTR
Version 0.4-0 included new data defaults. See ?getSymbols.
> data <- read.csv("D:/MarketData/GBPUSD+1.csv", header = FALSE)
> colnames(data) <- c("Date", "Time", "Open", "High", "Low", "Close", "Volume")
> data1 <- as.xts(data[,-(1:2)], as.POSIXct(paste(data[,1],data[,2]),
+                                           format='%Y.%m.%d %H:%M'))
> tail(data1)
                       Open    High     Low   Close Volume
2017-05-17 16:26:00 1.29636 1.29661 1.29631 1.29634    215
2017-05-17 16:27:00 1.29633 1.29640 1.29615 1.29624    144
2017-05-17 16:28:00 1.29623 1.29624 1.29602 1.29603    195
2017-05-17 16:29:00 1.29604 1.29615 1.29593 1.29612    234
2017-05-17 16:30:00 1.29613 1.29616 1.29582 1.29585    234
2017-05-17 16:31:00 1.29584 1.29586 1.29578 1.29585     46
> data2<- data1[,"Close"]
> data2$CO <- (data1[, "Close"]-data1[,"Open"])/data1[,"Open"]
> data2$HL <- (data1[, "High"]-data1[,"Low"])/data1[,"Low"]
> data2$Close1 <- lag(data2$Close, k=1)
> data2$CO1 <- lag(data2$CO, k=1)
> data2$HL1 <- lag(data2$HL, k=1)
> data2$Close2 <- lag(data2$Close, k=2)
> data2$CO2 <- lag(data2$CO, k=2)
> data2$HL2 <- lag(data2$HL, k=2)
> tail(data2)
                      Close            CO           HL  Close1           CO1          HL1
2017-05-17 16:26:00 1.29634 -1.542781e-05 2.314261e-04 1.29635 -1.388321e-04 0.0002314208
2017-05-17 16:27:00 1.29624 -6.942677e-05 1.928789e-04 1.29634 -1.542781e-05 0.0002314261
2017-05-17 16:28:00 1.29603 -1.542936e-04 1.697505e-04 1.29624 -6.942677e-05 0.0001928789
2017-05-17 16:29:00 1.29612  6.172649e-05 1.697623e-04 1.29603 -1.542936e-04 0.0001697505
2017-05-17 16:30:00 1.29585 -2.160277e-04 2.623821e-04 1.29612  6.172649e-05 0.0001697623
2017-05-17 16:31:00 1.29585  7.717002e-06 6.173888e-05 1.29585 -2.160277e-04 0.0002623821
                     Close2           CO2          HL2
2017-05-17 16:26:00 1.29654  7.712895e-06 0.0001234168
2017-05-17 16:27:00 1.29635 -1.388321e-04 0.0002314208
2017-05-17 16:28:00 1.29634 -1.542781e-05 0.0002314261
2017-05-17 16:29:00 1.29624 -6.942677e-05 0.0001928789
2017-05-17 16:30:00 1.29603 -1.542936e-04 0.0001697505
2017-05-17 16:31:00 1.29612  6.172649e-05 0.0001697623
> pips<- 10000*(diff(data1$Close, k=5))
> n=2
> direction <- ifelse(pips > n, "Up",
+                          ifelse(pips < -n, "Down", "Range"))
> direction <- lag(direction, k=-5)
> tail(direction)
                    Close  
2017-05-17 16:26:00 "Range"
2017-05-17 16:27:00 NA     
2017-05-17 16:28:00 NA     
2017-05-17 16:29:00 NA     
2017-05-17 16:30:00 NA     
2017-05-17 16:31:00 NA     
> direction <- factor(direction)
> x <- nrow(data2)
> train2<- data2[(x-300):(x-100),]
> valid2<- data2[(x-99):(x-6),]
> test2<- data2[(x-5):x,]
> trainmean<- apply(train2,2,mean)
> trainstd<- apply(train2,2,sd)
> trainidn<- (matrix(1,dim(train2)[1],dim(train2)[2]))
> valiidn<- (matrix(1,dim(valid2)[1],dim(valid2)[2]))
> testidn<- (matrix(1,dim(test2)[1],dim(test2)[2]))
> norm_train<- (train2 - t(trainmean*t(trainidn))) / t(trainstd*t(trainidn))
> norm_valid<- (valid2 - t(trainmean*t(valiidn))) / t(trainstd*t(valiidn))
> norm_test<- (test2 - t(trainmean*t(testidn))) / t(trainstd*t(testidn))
> traindir<- direction[(x-300):(x-100)]
> validir<- direction[(x-99):(x-6)]
> testdir<- direction[(x-5):x]
> library(caret)
Loading required package: lattice
Loading required package: ggplot2
> library(e1071)
> m3 <- train(norm_train, traindir,
+             method = "nnet",
+             tuneGrid = expand.grid(
+               .size = c(10),
+               .decay = 0.1),
+             trControl = trainControl(method = "none"),
+             MaxNWts = 50000,
+             maxit = 1000)
Loading required package: nnet
# weights:  133
initial  value 342.317162 
iter  10 value 114.701962
iter  20 value 101.922012
iter  30 value 95.614864
iter  40 value 92.497605
iter  50 value 90.573152
iter  60 value 89.866768
iter  70 value 89.506106
iter  80 value 89.417506
iter  90 value 89.384365
iter 100 value 89.375842
iter 110 value 89.374413
iter 120 value 89.366938
iter 130 value 89.365762
final  value 89.365750 
converged

We have developed a neural network model which is very simple. it consists of only one hidden layer with 10 neurons. Firsst we check how well the model trained on the training data. We divided the data into training data and validation data. Training data was used to train the neural network.Let’s check how well it has trained itself on the training data.

> direction1 <- predict(m3)
> caret::confusionMatrix(xtabs(~direction1 + traindir))
Confusion Matrix and Statistics

          traindir
direction1 Down Range  Up
     Down     7     0   0
     Range    8   163  13
     Up       0     0  10

Overall Statistics
                                          
               Accuracy : 0.8955          
                 95% CI : (0.8447, 0.9342)
    No Information Rate : 0.8109          
    P-Value [Acc > NIR] : 0.0007871       
                                          
                  Kappa : 0.581           
 Mcnemar's Test P-Value : NA              

Statistics by Class:

                     Class: Down Class: Range Class: Up
Sensitivity              0.46667       1.0000   0.43478
Specificity              1.00000       0.4474   1.00000
Pos Pred Value           1.00000       0.8859   1.00000
Neg Pred Value           0.95876       1.0000   0.93194
Prevalence               0.07463       0.8109   0.11443
Detection Rate           0.03483       0.8109   0.04975
Detection Prevalence     0.03483       0.9154   0.04975
Balanced Accuracy        0.73333       0.7237   0.71739

Now you can see above our neural network model has trained well. It is giving an accuracy off 89% on the training data. We will call it in sample accuracy. What we want is our model to predict on unseen data and check what is the accuracy level. This is known as out of sample accuracy. So we use data that we call validation data that our model has not seen and see the accuracy that it is giving. Did you read the post on why it is difficult to predict exchange rates using statistical models?

> vali_pred <- predict(m3, norm_valid)
> head(vali_pred)
[1] Range Range Range Range Range Range
Levels: Down Range Up
> barplot(table(direction))
> caret::confusionMatrix(xtabs(~vali_pred + validir))
Confusion Matrix and Statistics

         validir
vali_pred Down Range Up
    Down     0     1  0
    Range    7    68 11
    Up       1     6  0

Overall Statistics
                                          
               Accuracy : 0.7234          
                 95% CI : (0.6215, 0.8107)
    No Information Rate : 0.7979          
    P-Value [Acc > NIR] : 0.96923         
                                          
                  Kappa : -0.0621         
 Mcnemar's Test P-Value : 0.07284         

Statistics by Class:

                     Class: Down Class: Range Class: Up
Sensitivity              0.00000      0.90667   0.00000
Specificity              0.98837      0.05263   0.91566
Pos Pred Value           0.00000      0.79070   0.00000
Neg Pred Value           0.91398      0.12500   0.87356
Prevalence               0.08511      0.79787   0.11702
Detection Rate           0.00000      0.72340   0.00000
Detection Prevalence     0.01064      0.91489   0.07447
Balanced Accuracy        0.49419      0.47965   0.45783

You can see the accuracy of our model has dropped from 89% to 72% when we switch from seen data to unseen data. Can we do something to increase our accuracy level on unseen data? In the above model we have lagged our input features 3 times, lets do it 5 times and see if it helps in improving accuracy.

Neural Network Forex Trading System 2

As said above we increased the inputs by lagging the input features 2 more time. Model building is all trial and error. We need a lot of patience to build a good model. We have expert knowledge regarding our field which is the currency market in this case. We know candlestick patterns have predictive power. We also know looking at the past 4-5 candles we can predict in which direction market is going to move. So lets; use 5 lags in this improved model and see if we can increase the accuracy for our out of sample model. Out of sample data is very important for us as it is unseen and its acccuracy will tell us how much success rate we can expect from our trading system based on that model. We have divided market movement into 3 classes. Movement below 2 pips will be classed as ranging. Movement above 2 pips will be classed as Up and movement below 2 pips will be classed as down movement.

> #load the libraries
> library(quantmod)
> #import the data
> data <- read.csv("D:/MarketData/GBPUSD+1.csv", header = FALSE)
> colnames(data) <- c("Date", "Time", "Open", "High", "Low", "Close", "Volume")
> #convert dataframe to an xts object
> data1 <- as.xts(data[,-(1:2)], as.POSIXct(paste(data[,1],data[,2]),
+                                           format='%Y.%m.%d %H:%M'))
> 
> tail(data1)
                       Open    High     Low   Close Volume
2017-05-17 16:26:00 1.29636 1.29661 1.29631 1.29634    215
2017-05-17 16:27:00 1.29633 1.29640 1.29615 1.29624    144
2017-05-17 16:28:00 1.29623 1.29624 1.29602 1.29603    195
2017-05-17 16:29:00 1.29604 1.29615 1.29593 1.29612    234
2017-05-17 16:30:00 1.29613 1.29616 1.29582 1.29585    234
2017-05-17 16:31:00 1.29584 1.29586 1.29578 1.29585     46
> 
> data2<- data1[,"Close"]
> data2$CO <- (data1[, "Close"]-data1[,"Open"])/data1[,"Open"]
> data2$HL <- (data1[, "High"]-data1[,"Low"])/data1[,"Low"]
> data2$Close1 <- lag(data2$Close, k=1)
> data2$CO1 <- lag(data2$CO, k=1)
> data2$HL1 <- lag(data2$HL, k=1)
> data2$Close2 <- lag(data2$Close, k=2)
> data2$CO2 <- lag(data2$CO, k=2)
> data2$HL2 <- lag(data2$HL, k=2)
> data2$Close3 <- lag(data2$Close, k=3)
> data2$CO3 <- lag(data2$CO, k=3)
> data2$HL3 <- lag(data2$HL, k=3)
> data2$Close4 <- lag(data2$Close, k=4)
> data2$CO4 <- lag(data2$CO, k=4)
> data2$HL4 <- lag(data2$HL, k=4)
> tail(data2)
                      Close            CO           HL  Close1           CO1          HL1
2017-05-17 16:26:00 1.29634 -1.542781e-05 2.314261e-04 1.29635 -1.388321e-04 0.0002314208
2017-05-17 16:27:00 1.29624 -6.942677e-05 1.928789e-04 1.29634 -1.542781e-05 0.0002314261
2017-05-17 16:28:00 1.29603 -1.542936e-04 1.697505e-04 1.29624 -6.942677e-05 0.0001928789
2017-05-17 16:29:00 1.29612  6.172649e-05 1.697623e-04 1.29603 -1.542936e-04 0.0001697505
2017-05-17 16:30:00 1.29585 -2.160277e-04 2.623821e-04 1.29612  6.172649e-05 0.0001697623
2017-05-17 16:31:00 1.29585  7.717002e-06 6.173888e-05 1.29585 -2.160277e-04 0.0002623821
                     Close2           CO2          HL2  Close3           CO3          HL3
2017-05-17 16:26:00 1.29654  7.712895e-06 0.0001234168 1.29652 -1.079697e-04 0.0002853793
2017-05-17 16:27:00 1.29635 -1.388321e-04 0.0002314208 1.29654  7.712895e-06 0.0001234168
2017-05-17 16:28:00 1.29634 -1.542781e-05 0.0002314261 1.29635 -1.388321e-04 0.0002314208
2017-05-17 16:29:00 1.29624 -6.942677e-05 0.0001928789 1.29634 -1.542781e-05 0.0002314261
2017-05-17 16:30:00 1.29603 -1.542936e-04 0.0001697505 1.29624 -6.942677e-05 0.0001928789
2017-05-17 16:31:00 1.29612  6.172649e-05 0.0001697623 1.29603 -1.542936e-04 0.0001697505
                     Close4           CO4          HL4
2017-05-17 16:26:00 1.29665  2.545668e-04 0.0003471579
2017-05-17 16:27:00 1.29652 -1.079697e-04 0.0002853793
2017-05-17 16:28:00 1.29654  7.712895e-06 0.0001234168
2017-05-17 16:29:00 1.29635 -1.388321e-04 0.0002314208
2017-05-17 16:30:00 1.29634 -1.542781e-05 0.0002314261
2017-05-17 16:31:00 1.29624 -6.942677e-05 0.0001928789
> 
> #direction<- data.frame(matrix(NA,dim(data2)[1],1))
> pips<- 10000*(diff(data1$Close, k=5))
> n=2
> direction <- ifelse(pips > n, "Up",
+                     ifelse(pips < -n, "Down", "Range"))
> 
> direction <- lag(direction, k=-5)
> tail(direction)
                    Close  
2017-05-17 16:26:00 "Range"
2017-05-17 16:27:00 NA     
2017-05-17 16:28:00 NA     
2017-05-17 16:29:00 NA     
2017-05-17 16:30:00 NA     
2017-05-17 16:31:00 NA     
> direction <- factor(direction)
> 
> x <- nrow(data2)
> 
> train2<- data2[(x-500):(x-200),]
> valid2<- data2[(x-199):(x-6),]
> test2<- data2[(x-5):x,]
> 
> 
> trainmean<- apply(train2,2,mean)
> trainstd<- apply(train2,2,sd)
> 
> 
> trainidn<- (matrix(1,dim(train2)[1],dim(train2)[2]))
> valiidn<- (matrix(1,dim(valid2)[1],dim(valid2)[2]))
> testidn<- (matrix(1,dim(test2)[1],dim(test2)[2]))
> 
> 
> norm_train<- (train2 - t(trainmean*t(trainidn))) / t(trainstd*t(trainidn))
> norm_valid<- (valid2 - t(trainmean*t(valiidn))) / t(trainstd*t(valiidn))
> norm_test<- (test2 - t(trainmean*t(testidn))) / t(trainstd*t(testidn))
> 
> 
> traindir<- direction[(x-500):(x-200)]
> validir<- direction[(x-199):(x-6)]
> testdir<- direction[(x-5):x]
> 
> library(caret)
> library(e1071)
> m3 <- train(norm_train, traindir,
+             method = "nnet",
+             tuneGrid = expand.grid(
+               .size = c(40),
+               .decay = 0.1),
+             trControl = trainControl(method = "none"),
+             MaxNWts = 50000,
+             maxit = 1000)
# weights:  763
initial  value 508.087290 
iter  10 value 214.118369
iter  20 value 163.135688
iter  30 value 133.087117
iter  40 value 116.461972
iter  50 value 110.088698
iter  60 value 105.385186
iter  70 value 103.180276
iter  80 value 102.339347
iter  90 value 101.762369
iter 100 value 100.844553
iter 110 value 100.235618
iter 120 value 99.877796
iter 130 value 99.623222
iter 140 value 99.450404
iter 150 value 99.301965
iter 160 value 99.256903
iter 170 value 99.185848
iter 180 value 99.115716
iter 190 value 99.007136
iter 200 value 98.988401
iter 210 value 98.974478
iter 220 value 98.966304
iter 230 value 98.962196
iter 240 value 98.958897
iter 250 value 98.956157
iter 260 value 98.954591
iter 270 value 98.953682
iter 280 value 98.953133
iter 290 value 98.951986
iter 300 value 98.941686
iter 310 value 98.916859
iter 320 value 98.887349
iter 330 value 98.876204
iter 340 value 98.871270
iter 350 value 98.867634
iter 360 value 98.861265
iter 370 value 98.859520
iter 380 value 98.857350
iter 390 value 98.809161
iter 400 value 98.794699
iter 410 value 98.791562
iter 420 value 98.790035
iter 430 value 98.789596
iter 440 value 98.789251
iter 450 value 98.788956
iter 460 value 98.788709
iter 470 value 98.788521
iter 480 value 98.788423
iter 490 value 98.788364
iter 500 value 98.788318
iter 510 value 98.788301
final  value 98.788299 
converged
> 
> #calculate acccuracy on the training data
> direction1 <- predict(m3)
> caret::confusionMatrix(xtabs(~direction1 + traindir))
Confusion Matrix and Statistics

          traindir
direction1 Down Range  Up
     Down    33     0   0
     Range    0   228   1
     Up       0     0  39

Overall Statistics
                                          
               Accuracy : 0.9967          
                 95% CI : (0.9816, 0.9999)
    No Information Rate : 0.7575          
    P-Value [Acc > NIR] : < 2.2e-16       
                                          
                  Kappa : 0.9916          
 Mcnemar's Test P-Value : NA              

Statistics by Class:

                     Class: Down Class: Range Class: Up
Sensitivity               1.0000       1.0000    0.9750
Specificity               1.0000       0.9863    1.0000
Pos Pred Value            1.0000       0.9956    1.0000
Neg Pred Value            1.0000       1.0000    0.9962
Prevalence                0.1096       0.7575    0.1329
Detection Rate            0.1096       0.7575    0.1296
Detection Prevalence      0.1096       0.7608    0.1296
Balanced Accuracy         1.0000       0.9932    0.9875
> 
> #calculate accuracy on validation data
> vali_pred <- predict(m3, norm_valid)
> head(vali_pred)
[1] Range Range Range Range Range Range
Levels: Down Range Up
> barplot(table(direction))
> caret::confusionMatrix(xtabs(~vali_pred + validir))
Confusion Matrix and Statistics

         validir
vali_pred Down Range  Up
    Down     1     9   0
    Range   11   152  17
    Up       2     1   1

Overall Statistics
                                        
               Accuracy : 0.7938        
                 95% CI : (0.73, 0.8484)
    No Information Rate : 0.8351        
    P-Value [Acc > NIR] : 0.9465680     
                                        
                  Kappa : 0.061         
 Mcnemar's Test P-Value : 0.0009289     

Statistics by Class:

                     Class: Down Class: Range Class: Up
Sensitivity             0.071429       0.9383  0.055556
Specificity             0.950000       0.1250  0.982955
Pos Pred Value          0.100000       0.8444  0.250000
Neg Pred Value          0.929348       0.2857  0.910526
Prevalence              0.072165       0.8351  0.092784
Detection Rate          0.005155       0.7835  0.005155
Detection Prevalence    0.051546       0.9278  0.020619
Balanced Accuracy       0.510714       0.5316  0.519255

In the above model we increased the hidden layer neurons from 10 to 40 plus we increased the inputs from 12 to 16. This increased the in sample accuracy from 89% to 99% while out of sample accuracy increased from 72% to 79%. We want an out of sample accuracy of 85% at least before we start trading live with our model. We try to re-engineer the features and see if it helps.

Neural Network Forex Trading System 3

This is our third model. In this model we will be using candlestick pattern concepts like the candlestick body which is just close minue open and the candlestick range which is just the high minus low. We will also use concept of Upper Shadow which is the difference between the High and the Close for a bullish candle and High and the Low for a bearish candle for a calculating the Upper Shadow. For the Lower Shadow we take the difference between the Open and Low for a bullish candle and Close and Low for a bearish candle.

> #load the libraries
> library(quantmod)
> #import the data
> data <- read.csv("D:/MarketData/GBPUSD+1.csv", header = FALSE)
> colnames(data) <- c("Date", "Time", "Open", "High", "Low", "Close", "Volume")
> #convert dataframe to an xts object
> data1 <- as.xts(data[,-(1:2)], as.POSIXct(paste(data[,1],data[,2]),
+                                           format='%Y.%m.%d %H:%M'))
> 
> tail(data1)
                       Open    High     Low   Close Volume
2017-05-17 16:26:00 1.29636 1.29661 1.29631 1.29634    215
2017-05-17 16:27:00 1.29633 1.29640 1.29615 1.29624    144
2017-05-17 16:28:00 1.29623 1.29624 1.29602 1.29603    195
2017-05-17 16:29:00 1.29604 1.29615 1.29593 1.29612    234
2017-05-17 16:30:00 1.29613 1.29616 1.29582 1.29585    234
2017-05-17 16:31:00 1.29584 1.29586 1.29578 1.29585     46
> 
> data2<- data1[,"Close"]
> data2$Body <- data1[, "Close"]-data1[,"Open"]
> data2$Range <- data1[, "High"]-data1[,"Low"]
> data2$US <- ifelse(data1[, "Close"] > data1[ ,"Open"], 
+                    data1[, "High"]-data1[, "Close"], 
+                    data1[, "High"]-data1[, "Open"])
> data2$LS <- ifelse(data1[, "Close"] > data1[ ,"Open"], 
+                    data1[, "Open"]-data1[, "Low"], 
+                    data1[, "Close"]-data1[, "Low"])
> data2$Close1 <- lag(data2$Close, k=1)
> data2$Body1 <- lag(data2$Body, k=1)
> data2$Range1 <- lag(data2$Range, k=1)
> data2$US1 <- lag(data2$US, k=1)
> data2$LS1 <- lag(data2$LS, k=1)
> data2$Close2 <- lag(data2$Close, k=2)
> data2$Body2 <- lag(data2$Body, k=2)
> data2$Range2 <- lag(data2$Range, k=2)
> data2$US2 <- lag(data2$US, k=2)
> data2$LS2 <- lag(data2$LS, k=2)
> tail(data2)
                      Close     Body   Range      US      LS  Close1    Body1  Range1     US1
2017-05-17 16:26:00 1.29634 -0.00002 0.00030 0.00025 0.00003 1.29635 -0.00018 0.00030 0.00011
2017-05-17 16:27:00 1.29624 -0.00009 0.00025 0.00007 0.00009 1.29634 -0.00002 0.00030 0.00025
2017-05-17 16:28:00 1.29603 -0.00020 0.00022 0.00001 0.00001 1.29624 -0.00009 0.00025 0.00007
2017-05-17 16:29:00 1.29612  0.00008 0.00022 0.00003 0.00011 1.29603 -0.00020 0.00022 0.00001
2017-05-17 16:30:00 1.29585 -0.00028 0.00034 0.00003 0.00003 1.29612  0.00008 0.00022 0.00003
2017-05-17 16:31:00 1.29585  0.00001 0.00008 0.00001 0.00006 1.29585 -0.00028 0.00034 0.00003
                        LS1  Close2    Body2  Range2     US2     LS2
2017-05-17 16:26:00 0.00001 1.29654  0.00001 0.00016 0.00004 0.00011
2017-05-17 16:27:00 0.00003 1.29635 -0.00018 0.00030 0.00011 0.00001
2017-05-17 16:28:00 0.00009 1.29634 -0.00002 0.00030 0.00025 0.00003
2017-05-17 16:29:00 0.00001 1.29624 -0.00009 0.00025 0.00007 0.00009
2017-05-17 16:30:00 0.00011 1.29603 -0.00020 0.00022 0.00001 0.00001
2017-05-17 16:31:00 0.00003 1.29612  0.00008 0.00022 0.00003 0.00011
> 
> #direction<- data.frame(matrix(NA,dim(data2)[1],1))
> pips<- 10000*(diff(data1$Close, k=5))
> n=2.5
> direction <- ifelse(pips > n, "Up",
+                     ifelse(pips < -n, "Down", "Range"))
> 
> direction <- lag(direction, k=-5)
> tail(direction)
                    Close  
2017-05-17 16:26:00 "Range"
2017-05-17 16:27:00 NA     
2017-05-17 16:28:00 NA     
2017-05-17 16:29:00 NA     
2017-05-17 16:30:00 NA     
2017-05-17 16:31:00 NA     
> direction <- factor(direction)
> 
> x <- nrow(data2)
> 
> train2<- data2[(x-300):(x-100),]
> valid2<- data2[(x-99):(x-6),]
> test2<- data2[(x-5):x,]
> 
> 
> trainmean<- apply(train2,2,mean)
> trainstd<- apply(train2,2,sd)
> 
> 
> trainidn<- (matrix(1,dim(train2)[1],dim(train2)[2]))
> valiidn<- (matrix(1,dim(valid2)[1],dim(valid2)[2]))
> testidn<- (matrix(1,dim(test2)[1],dim(test2)[2]))
> 
> 
> norm_train<- (train2 - t(trainmean*t(trainidn))) / t(trainstd*t(trainidn))
> norm_valid<- (valid2 - t(trainmean*t(valiidn))) / t(trainstd*t(valiidn))
> norm_test<- (test2 - t(trainmean*t(testidn))) / t(trainstd*t(testidn))
> 
> 
> traindir<- direction[(x-300):(x-100)]
> validir<- direction[(x-99):(x-6)]
> testdir<- direction[(x-5):x]
> 
> library(caret)
> library(e1071)
> m3 <- train(norm_train, traindir,
+             method = "nnet",
+             tuneGrid = expand.grid(
+               .size = c(10),
+               .decay = 0.1),
+             trControl = trainControl(method = "none"),
+             MaxNWts = 50000,
+             maxit = 1000)
# weights:  193
initial  value 147.861754 
iter  10 value 73.658023
iter  20 value 55.491725
iter  30 value 48.026823
iter  40 value 46.252393
iter  50 value 45.742213
iter  60 value 45.279406
iter  70 value 45.071429
iter  80 value 45.004627
iter  90 value 44.975451
iter 100 value 44.908225
iter 110 value 44.851912
iter 120 value 44.682247
iter 130 value 44.633824
iter 140 value 44.577723
iter 150 value 44.481995
iter 160 value 44.344888
iter 170 value 44.071917
iter 180 value 43.951889
iter 190 value 43.923721
iter 200 value 43.919855
iter 210 value 43.919536
final  value 43.919523 
converged
> 
> # calculate in sample accuracy
> #calculate acccuracy on the training data
> direction1 <- predict(m3)
> caret::confusionMatrix(xtabs(~direction1 + traindir))
Confusion Matrix and Statistics

          traindir
direction1 Down Range  Up
     Down     9     0   0
     Range    1   175   0
     Up       0     0  16

Overall Statistics
                                          
               Accuracy : 0.995           
                 95% CI : (0.9726, 0.9999)
    No Information Rate : 0.8706          
    P-Value [Acc > NIR] : 2.498e-11       
                                          
                  Kappa : 0.9783          
 Mcnemar's Test P-Value : NA              

Statistics by Class:

                     Class: Down Class: Range Class: Up
Sensitivity              0.90000       1.0000    1.0000
Specificity              1.00000       0.9615    1.0000
Pos Pred Value           1.00000       0.9943    1.0000
Neg Pred Value           0.99479       1.0000    1.0000
Prevalence               0.04975       0.8706    0.0796
Detection Rate           0.04478       0.8706    0.0796
Detection Prevalence     0.04478       0.8756    0.0796
Balanced Accuracy        0.95000       0.9808    1.0000
> 
> #calculate out of sample accuracy
> vali_pred <- predict(m3, norm_valid)
> head(vali_pred)
[1] Range Range Range Range Range Range
Levels: Down Range Up
> barplot(table(direction))
> caret::confusionMatrix(xtabs(~vali_pred + validir))
Confusion Matrix and Statistics

         validir
vali_pred Down Range Up
    Down     0     1  0
    Range    5    79  7
    Up       0     2  0

Overall Statistics
                                          
               Accuracy : 0.8404          
                 95% CI : (0.7505, 0.9078)
    No Information Rate : 0.8723          
    P-Value [Acc > NIR] : 0.8595          
                                          
                  Kappa : -0.0406         
 Mcnemar's Test P-Value : NA              

Statistics by Class:

                     Class: Down Class: Range Class: Up
Sensitivity              0.00000       0.9634   0.00000
Specificity              0.98876       0.0000   0.97701
Pos Pred Value           0.00000       0.8681   0.00000
Neg Pred Value           0.94624       0.0000   0.92391
Prevalence               0.05319       0.8723   0.07447
Detection Rate           0.00000       0.8404   0.00000
Detection Prevalence     0.01064       0.9681   0.02128
Balanced Accuracy        0.49438       0.4817   0.48851

Now we have further improved our model. You can see the in sample accuracy is 99.5%. Out of sample accuracy has increased to 84%. But we are facing another problem noww. kappa which tells us how much the predictions are depending on random chance is quite low which is a bad sign. This model may not bee good for actual trading. We need to improve the model so tht Kappa goes above something like 0.5. Below is a plot that explains why kappa is so low.

Neural Network Forex Trading System

You can see most of the time the market is ranging. So just by predicting that the market is ranging, our model can make a correct prediction. But we will not be using this range prediction in our trading. We will be using the Up and down prediction in our trading. But as you can see in the above plot most of the time market in ranging and it moves up and down quite seldom. We need to be very accuracte about the Up and Down prediction. So we need to work more on the model. But I think we are working in the right direction and we can further improve our model using better deep learning models. Did you watch this documentary on UK Billionaire traders?

0 Comments