Neural Networks and Stock / Forex Trading

An example of using Neural Networks for stock and Forex trading.

Visibility is very important for this site. If you like it please link to this URL or use our online form to add your reciprocal link. Learn more about reciprocal links


Neural Networks and Stock / Forex Trading

An example of using the Cortex Feedforward Backpropagation Neural Network Application.

This example uses the Cortex built-in scripting language, so please read the Scripting language guide first.

Contents

About

In this free online tutorial you will find the "full cycle" of using Cortex for Forex trading (or stock trading, the idea is the same).

You will learn how to choose inputs for the Neural Network, and how to decide what to use as the output.

You will find an example of a ready to use script that allows to perform optimization of both the Neural Network (number of neurons) and the trading parameters (stop loss etc.)

Finally (the part that is not present in most tutorials), you will learn what to do next. After all, Cortex cannot do real time trading, you need to use somrething like Trade Station, MetaStocks or MetaTrader. How to port the Neural Network from Cortex to your favorite trading platform? Do you have to deal with DLLs, ActiveX controls and low-level programming? The answer is NO.
Cortex comes with the easy to use feature that allows you to easily port the resulting (trained) Neural Network to the scripting language of your trading platform. No DLLs, DDE, ActiveX or any other low-level solutions - everything is plain and simple.

Important note: this is NOT a "how to trade" tutorial. Instead, it tells you how to use Cortex, but you still need to invent your own trading system. The one we use here is barely a starting point, and shouldn't be used for Forex trading "as is". The idea of this text is to teach you to create NN-based trading systems and to port them to the trading platform of your choice. The example is, however, ovesimplified, and can only be used as the illustration of trading principles. Same way, the MACD trading system, that can be found in many tutorials, is not working well anymore (as markets have changed), but still is a good example of using indicators for mechanical trading.
In two words: do your own analysis.

Another important note: the tutorial is using examples, lots of them. To make your life easier, I have included them all, not just fragments. However it makes the text much longer. Also, I am going from the very first, clumsy, trading systems, to more advanced, every time explaining what had been improved and why. Be patient, or jump directly to the section you need.

Final important note: the code is not something carved in stone, it could change while this text was written. The final versions of script files are included in Cortex archive.

What is wrong with the "simple" examples?

In the Cortex user's guide we used a simple example of a Neural Network, predicting the price of GENZ stock. To find out what is wrong with this approach, let's do the same "simple" example, using MSFT.TXT, instead of the GENZ.TXT (use 800 records in the learning set, as MSFT.TXT is a little bit shorter, then GENZ.TXT).

GENZ MSFT

It just wouldn't work! Why?

The reason will become evident, if you ask yourself: "What is the reason NN can predict future values, on the first place?"

The answer is: it is learning to recognize patterns, and if there is a hidden logic in these patterns, then even a new pattern (with the same logic) will be recognized.

That's a trick - "with the same logic". There is not even one, but three problems here.

First of all, if you look at the Microsoft's stock price, you will notice, that it was going down in the "learning" part of our data, and sideways - in the "testing" part. So it is possible, that the logic had changed.

Second, and even more important - WHAT IS THE PATTERN? You see, if we teached the network in the range 10 - 100, and then presented it with something in the 1 to 3 range - they are different patterns! 10, 20, 30 and 1, 2, 3 look similar to the human because - BECAUSE - we have this ability to divide by ten, when presented with numbers ending with zero. It is what is called a pre-processing of the data, and by default, the NN can not do it.

Can we teach it? Of course. What is it EXACTLY we need to teach it?

This is the third, and the most important one. We do not need the stock price prediction! We do not care! What we need is a trading signal.

Now, wait a minute! We need a) to have our input (both learning and testing) in the same range, and we need b) to be able to make trading decisions based on it? Isn't it what we call an indicator? Bingo?

So, that's what we are going to do - we will build an indicator, to feed it to the NN as an input, and we will try to get a prediction of the indicator value, not the worthless stock price!

In our first example, we will load stock quotes from the disk, open the Neural Network file and start the learning - all in an automated mode.

Create a new script file (or open the one that came with the Cortex archive) and call it stocks_nn.tsc.

First of all, we need to download the price values from the MSFT.TXT file. We are going to use the CLV indicator (see below), but to calculate it, we need split-adjusted values for High and Low, not just for close. Here is how to get them.

stocks_nn.tsc, part 1

void main()
{
    string strStockPath = 
        "c:\\S_Projects\\CortexPro\\data\\samples\\stocks\\msft.txt";
    double bIsPathRelative = 0;

    array arrDate = CREATE_ARRAY(0);
    array arrHigh = CREATE_ARRAY(0);
    array arrLow = CREATE_ARRAY(0);
    array arrC = CREATE_ARRAY(0);
    array arrClose = CREATE_ARRAY(0);

    // Loading data
    TABLE_LOADER(strStockPath, bIsPathRelative, 0, "", 1, 
        "<!-- chart1", 0, arrDate, 2, arrHigh, 
        3, arrLow, 4, arrC, 6, arrClose);

    // Adjust for splits
    double dSplit;
    for(double i = 0; i < ARRAY_SIZE(arrClose); i = i + 1)
    {
        dSplit = arrC[i] / arrClose[i];
        arrHigh[i] = arrHigh[i] / dSplit;
        arrLow[i] = arrLow[i] / dSplit;
    }

The first line assigns the path to the strStockPath variable, of course, you will have to edit it, if your data file is located in the different directory.

In the second line we specify, that this path is not relative (the "relative" to the location of Cortex.exe file).

The TABLE_LOADER receives the path, the empty string for the "start line", 1 - to skip the first line (column names), part of the file's footer line (the last line in MSFT.TXT does not contain data), it is also instructed to load the column number 0 (and call it arrDate), 2 (arrHigh), 3 (arrLow), 4 (arrC) and 6 (arrClose).
For a full description of TABLE_LOADER, see the SLANG reference guide.

Then we calculate split, by dividing the Adjusted Close by Close, and use this value to adjust Low and High.

The MSFT.TXT file contains newest data FIRST, while we want them LAST. Nevertheless, just for the sake of an exercise, we are not using "reverse" parameters of TABLE_LOADER, instead, we will reverse arrays "by hand".

stocks_nn.tsc, part 2

    // Reversing the arrays
    double nArraySize = ARRAY_SIZE(arrClose) / 2 + 1;

    for(i = 0; i < nArraySize; i = i + 1)
    {
        arrClose[i] = arrClose[nArraySize - i - 1];
        arrLow[i] = arrLow[nArraySize - i - 1];
        arrHigh[i] = arrHigh[nArraySize - i - 1];
    }

Next, we need to create an indicator. Let's say, it is going to be a Close Location Value indicator, though in the "real life" I would probably use more than one indicator as the NN input.

The Close Location Value indicator is calculated like

CLV = ((Close - Low) - (High - Close)) / (High - Low), where Close, Low and High are for the interval, not necessarily for a single bar. Note, that we want it in the 0 - 1 range, to make it easier to normalize to our NN's range (which is, again, 0-1).

stocks_nn.tsc, part 3

    array arrClv = CREATE_ARRAY(0);
    double nInterval = 7;

    array arrPeriodLow = CREATE_ARRAY(0);
    array arrPeriodHigh = CREATE_ARRAY(0);

    arrPeriodLow = MVMIN(arrLow, nInterval);
    arrPeriodHigh = MVMAX(arrHigh, nInterval);

    nArraySize = ARRAY_SIZE(arrClose);
                        
    for(i = 0; i < nInterval; i = i + 1)
    {
        arrClv[i] = 0;
    }
    
    double dClose;
    double dLow;
    double dHigh;
    for(i = nInterval; i < nArraySize; i = i + 1)
    {
        dClose = arrClose[i];
        dLow = arrPeriodLow[i];
        dHigh = arrPeriodHigh[i];

        // / 2 + 1 to confine to 0...1 instead of -1...1
        arrClv[i] = (((dClose - dLow) - (dHigh - dClose))
            / (dHigh - dLow)) / 2 + 0.5; 
    }

Next, we need to create a lag file. Let's use lags equal to 1, 2... 9 (For details on file functions, see the SLANG reference guide). Note, that the Cortex's NN dialog can produce simple lags automatically (you can use a "Generate lag" button). But later in this text, we are going to work with complex lags (which means, they are not 1, 2, 3... but 1, 3, 64... whatever), so we need to create the code that can handle this task in a more flexible way.

stocks_nn.tsc, part 4

    string strLagFileName = 
        "c:\S_Projects\CortexPro\data\samples\stocks\msft_ind.lgg";
    double hFile = F_OPEN(strLagFileName, "wb");

    F_PRINT(hFile, "%s\r\n", 
        "Number,Clv,Clv-1,Clv-2,Clv-3,Clv-4,Clv-5,
            Clv-6,Clv-7,Clv-8,Clv-9"); 

    double nMaxLag = 9 + nInterval;

    for(i = nMaxLag; i < ARRAY_SIZE(arrClose); i = i + 1)
    {
        F_PRINT(hFile, "%.0f", i - nMaxLag + 1);
        for(double j = 0; j < nMaxLag + 1; j = j + 1)
        {
            F_PRINT(hFile, ",%f", arrClv[i - j]);     
        }
        F_PRINT(hFile, "%s", "\r\n");
    }

    F_CLOSE(hFile);

Having the lag file, we are ready to create our first neural network. This function takes a lot of parameters, so be carefull. However, the code is really simple.

By the way, most of this code can be removed, if you think you can handle numbers, instead of meaningfull names in your code, however, that would be a very bad coding practice.

stocks_nn.tsc, part 5

    double nSkipBefore = 0;
    double nSkipAfter = 0;
    string strStartLine = "";
    string strEndLine = "";

    // We already have them reversed
    double bReverseArrays = 0;        
    
    array arrColumns= CREATE_ARRAY(0);
    array_s arrColumnNames = CREATE_ARRAY_S(0); 
    array arrLags = CREATE_ARRAY(0);

    // Inputs
    arrLags[0] = 0;
    arrColumns[0] = 6;
    arrColumnNames[0] = "Adj. Close*"; 

    // Outputs
    arrColumns[1] = 6;
    arrColumnNames[1] = "Adj. Close*"; 

    for(i = 0; i < 10; i = i + 1)
    {
        arrLags[i] = i;
    }

    array arrInputColumns = CREATE_ARRAY(0);
    // 0  - Number, 1 - Clv, our input begins at column No 2
    arrInputColumns[0] = 2;
    arrInputColumns[1] = 3;
    arrInputColumns[2] = 4;
    arrInputColumns[3] = 5;
    arrInputColumns[4] = 6;
    arrInputColumns[5] = 7;
    arrInputColumns[6] = 8;
    arrInputColumns[7] = 9;
    arrInputColumns[8] = 10;

    array_s arrInputColumnNames = CREATE_ARRAY_S(0);
    arrInputColumnNames[0] = "Clv-1"; 
    arrInputColumnNames[1] = "Clv-2"; 
    arrInputColumnNames[2] = "Clv-3"; 
    arrInputColumnNames[3] = "Clv-4"; 
    arrInputColumnNames[4] = "Clv-5"; 
    arrInputColumnNames[5] = "Clv-6"; 
    arrInputColumnNames[6] = "Clv-7"; 
    arrInputColumnNames[7] = "Clv-8"; 
    arrInputColumnNames[8] = "Clv-9";
 
    array arrOutputColumns = CREATE_ARRAY(0);
    arrOutputColumns[0] = 1;    // Clv

    array_s arrOutputColumnNames = CREATE_ARRAY_S(0);
    arrOutputColumnNames[0] = "Clv";

    double nExtractRecords = 800;
    double dStopError = 0;
    double nStopEpoch = 0; 
    double nNeuronsLayer1 = 9;
    double nNeuronsLayer2 = 7;
    double nNeuronsLayer3 = 1;
    double nNeuronsLayer4 = 0;
    double nLayers = 3; 
    double nActivation = 0;
    double nAdjustRange = 1.0;

    array arrOutTabInputColumns = CREATE_ARRAY(0);
    arrOutTabInputColumns[0] = 0;    // Number

    array_s arrOutTabInputColumnNames = CREATE_ARRAY_S(0);
    arrOutTabInputColumnNames[0] = "No";

    array arrOutTabOutputColumns = CREATE_ARRAY(0);
    arrOutTabOutputColumns[0] = 1;
    // Outputs will be added to the same list, right after inputs
    arrOutTabOutputColumns[1] = 10;

    array_s arrOutTabOutputColumnNames = CREATE_ARRAY_S(0);
    arrOutTabOutputColumnNames[0] = "Clv"; 
    arrOutTabOutputColumnNames[1] = "NN: Clv" ;

    string strNnFileName = 
        "c:\\S_Projects\\CortexPro\\data\\samples\\nn\\msft_ind.nn";

    CREATE_NN(strNnFileName, bIsPathRelative, strLagFileName, 
        bIsPathRelative, nSkipBefore, nSkipAfter, strStartLine, 
        strEndLine,
        bReverseArrays, arrInputColumns, arrInputColumnNames, 
        arrOutputColumns, arrOutputColumnNames, nExtractRecords, 
        dStopError, nStopEpoch, nNeuronsLayer1, nNeuronsLayer2, 
        nNeuronsLayer3, nNeuronsLayer4, nLayers, nActivation, 
        nAdjustRange, arrOutTabInputColumns, 
        arrOutTabInputColumnNames,
        arrOutTabOutputColumns, arrOutTabOutputColumnNames);

Now, after we have a network and the lagged file with data, we need to teach the network. The lag file (msft_ind.lgg) has 1074 records, so it is reasonable to use 800 as a learning set, and the remaining 274 as a testing set.

You can, of course, open a network file and to click the "Run" button on the "Learning" tab. But as this is an introduction to advanced Cortex programming, let's use SLANG scripting language instead.

The following code brings up the modal dialog with ann NN settings. Note, that if you want to have a privilege of clicking the "Run" button, you need to change the

stocks_nn.tsc, part 6

    double bStartLearning = 1; 
    double bResumeScript = 1;
    double bReset = 1;

    OPEN_NN_FILE(strNnFileName, bIsPathRelative, 
        bStartLearning, bResumeScript, bReset);

The bStartLearning can be 0, in which case the dialog will wait for your input, or 1, then the learning will begin aytomatically.

The bResumeScript, if equals 1, will resume the script, if you close the dialog by clicking the OK button.

The bReset is used to reset the network before the learning begins.


Run the script, and wait for the epoch counter to exceed 1000, then click "Stop". Go to the "Apply" tab, and click "Apply". This will run the entire data set (both learning and testing) through the NN, and create the .APL file, containing both original input-output, and the NN-generated prediction, this way you can easily plot them and compate against each other.

Go to the "Output" tab, select msft_ind.apl file, click "Browse file", "Select fields", then select the "No" in the left list box, and (by holding down the CTRL key while selecting with the mouse) Clv and NN:Clv in the right list box. Click "Chart" to see how good our prediction is. Well... It is more or less good, from what we can say by looking at it. Still, nothing extraordinary...

This was just an example of what you can do with SLANG scripting, and how to automate Cortex's routine tasks. However, until now, we did nothing you couldn't do "by hand". Well... almost nothing, because if you want to create a custom lag file, with, say, Clv-100, Clv-50, Clv-25... columns, then you will have to use SLANG (or Excel...), because you cannot do in in Cortex without scripting.

Network - what to optimize?

Here is our next problem. Do we need a good-looking prediction, or do we need the one we can use to trade with profit? The question seems odd, but just think about it for a moment. Let's say we have a VERY good 1-hour prediction. 95% accurate. Still, how far can the price go in one hour? Not too far, I am afraid. Compare it to the situation, when you have a rather inaccurate 10-hours prediction. Will it be better?

To answer this question, we need to actually trade, a simple comparison of the mean errors produced by the two NNs will not help.

The second part (of the same problem) is in the way we define a "good prediction". Let's say we have a network, that produces the prediction, which is 75% accurate. Compare it to the NN, that is producing 100% accurate prediction. The last one is better. Now, DIVIDE the output (prediction) of the 100% accurate NN by 10. We will have a VERY inaccurate network, as its signal is nowhere near the signal we used as a "desired output". And yet, it can be used same way we used 100% accurate NN, all we have to do is to multiply it to 10!

See, the NN is created, by tuning the mean quadratic error, and not the correlation, so, at least in theory, a better NN can show poor results, when used for the actual stock / Forex trading.

To solve this problem, we need to test our NNs using trading, and to use results of this trading (profit and drawdowns) to decide, if this NN is better than the other one.

Let's do it. Let's create a program, that can be used to fine-tune NN, and this time, by fine-tuning, we will mean trading results.

Few short notes

First of all, in our example above, the "automatic" learning will never stop, because we haven't specified any stop criteria. In the dialog, or in the CREATE_NN function, you can provide the min. error (when the NN reaches it, it stops and, if bResumeScript is set to 1, the dialog will close and the script will resume). Also yo can provide the maximum number of epochs, or both. I am not using it in the example below, at least not always, because I am planning to watch the learning and to click STOP when I think the NN is ready. If you want to do it in fully automatic mode, pay attention to these parameters.

Second. One of the ways to make a network smaller, faster and more accurate, is to begin with the small network, and increase it's size, neuron by neuron. Obwiously, the number of the input neurons is determined by the number of input data columns (but we can vary them, too), and the number of output neurons should be equal to the number of output data columns (usually one, but not necessarily). This means we need to optimize the number of neurons in the hidden layer(s).

Also, as I have mentioned, we don't really know which data to use. Will Clv-15 (15 days delayed) increase the accuracy of our prediction? Do we need Clv-256? Will it be better to use both of them in the same NN, or will adding Clv-256 ruin our performance?

Using nested cycles to try different input parameters, you can:

  1. Create the NN, same way we did it for the stock data (let me repeate, for the NN, there is no difference between stocks and FOREX, it just happened that I have couple of high quality data files for FOREX that I want to process, while writing this text).
  2. Try different combinations of lags.
  3. Try different number of neurons in the hidden layer.
  4. ... and different combinations of different indicators...
  5. ...and so on.

However, if you try all possible combinations of all possible parameters, you will NEVER get your results, no mater how fast your computer is. Below, we will use couple of tricks to reduce calculations to a bare minimum.

By the way, it may seem, that if you start from one hidden neuron, then increase it to 2, 3 and so on, and at some point the error (quality of the prediction) or the profit (if you test the NN by trading using it) will begin to go down, then you have your winner. Unfortunately, I cannot prove, that after the first "performance peak" there can be no second one. It means, that the error may go like 100, 30, 20, 40, 50 (it was just at its minimum, right?) and then 30, 20, 10, 15, ... (the second minimum). We just have to test all reasonable numbers.

Third. Optimization is a two-edged sword. If you over-optimize your code, it may not work outside the data you used to fine-tune it. I will do my best to avoid this pitfall. If you want to do additional optimizations to your code or NN, I advise you to do a research in the Internet, to learn more about hidden problems of this approach. ALso, I am going to pay some attention to the smoothness of the profit curve. The profit that looks like 0, -500, 1000, -100, 10000 may be great, but the profit 0, 100, 200, 300, 400... is better, as it is less risky. We may talk about it later.

Finally, for this example we are going to use FOREX, rather than stock prices. From the point of view of the NN there is no difference, and from my point - Forex is much more fun to trade. If you prefer stocks, the code can easily be modified.

A trading system to play with

First of all, let's create a prototype of our code, one that can easily be optimized in future. It is going to be a trading system, that uses a Neural Network to trade and produces a chart (profit against trade number). It will also calculate drawdown, as a measure of robustness of our trading system.

forex_nn_01.tsc, part 1

void main()
{
    OUT_CLEANUP();

    // ***** Loading data
    string strDataFileName = 
        "c:\\S_Projects\\CortexPro\\data\\samples\\forex\\eurusd_h1.txt";
    double bIsPathRelative = 0;

    array arrDate = CREATE_ARRAY(0);
    array arrTime = CREATE_ARRAY(0);
    array arrOpen = CREATE_ARRAY(0);
    array arrHigh = CREATE_ARRAY(0);
    array arrLow = CREATE_ARRAY(0);
    array arrClose = CREATE_ARRAY(0);

    TABLE_LOADER(strDataFileName, bIsPathRelative, 0, "", 0, "", 0, 
        arrDate, 1, arrTime, 2, arrOpen, 3, arrHigh, 4, arrLow, 5, 
        arrClose);

    double nExtractRecords = 0.8 * ARRAY_SIZE(arrClose);

    double nInterval = 7;
    array arrClv = CreateClv(nInterval);

    array arrLags = CREATE_ARRAY(0);
    arrLags[0] = 0; arrLags[1] = 1; arrLags[2] = 2; 
    arrLags[3] = 3; arrLags[4] = 4; arrLags[5] = 5; 
    arrLags[6] = 6; arrLags[7] = 7; arrLags[8] = 8; 
    arrLags[9] = 9;

    double nMaxInterval = nInterval;
    double nMaxLag = arrLags[ARRAY_SIZE(arrLags) - 1];
    double nRemoveFirst = nMaxLag + nMaxInterval;

    string strLagFileName = 
        "c:\\S_Projects\\CortexPro\\data\\stocks_nn\\forex\\eurusd_h1.lgg";
    CreateLagFile(strLagFileName, arrLags, nRemoveFirst);

    double dStopError = 0;
    double nStopEpoch = 0; 
    double nNeuronsLayer2 = 7;
    string strNnFileName = 
        "c:\\S_Projects\\CortexPro\\data\\stocks_nn\\nn\\eurusd_h1.nn";
    NewNn(arrLags, dStopError, nStopEpoch, nNeuronsLayer2, 
        strNnFileName);

    TeachNn();

    double dStopLoss = 0.005;    // 50 points
    double dBuyLevel = 0.3;
    double dSellLevel = 0.7;
    TestNet();

    string strForexName = "EURUSD_H1";
    Chart(strForexName);

    PRINT("%s\r\n", "Done");
}

The main difference here is that we use functions, instead of placing all the code in the main block of the program. This way it is much easier to manage.

Second, we have a TestNet function. I am using a very simple algorythm of trading . The CLV indicator is confined to 0 - 1 interval (our version of CLV is), so when the indicator crosses up the dBuyLevel (see code above), I am buying, when it is crossing down the dSellLevel, I am selling.

Obviously, it is not the best trading strategy, but it will do for our purpose (just for now). If you want to improve it, here are some pointers. First, you may want to have a system, that is not ALWAYS in the market. Second, you may want to use more than one indicator as inputs, and maybe, more than one NN, so that the trading decision is made based on few predicted indicators. We will add some improvements to the trading algorythm later.

We use some standard assumptions of the FOREX trading: spread is 5 points, leverade is 100, min. lot is $100 (mini-FOREX).

Let's take a look at our "trading" system. Once again, it is an oversimplified one. An important note: the TestNn() is called last, and it has access to all variables that were created to that point. So if you see a variable that I am using, without initializing it, it probably means that it was initialized in NewNn(), TeachNn() or some other function that was called prior to TestNn().

To make things easier, comments are placed in the code.

forex_nn_01.tsc, part 2

void TestNet()
{
    PRINT("%s\r\n", "Testing the NN");
    
    // Array to hold the balance (total amount of money)
    // that we have after each trade. The initial amount 
    // is $1000
    array arrBalance = CREATE_ARRAY(0);
    arrBalance[0] = 1000;

    // Array to keep bar number, at which the trade happened
    array arrBars = CREATE_ARRAY(0);
    arrBars[0] = 0;

    // We use so-called mini-forex, that is supported by
    // most of brockers. A minimum lot size is $100
    double dLotSize = 100;    // 0.1 lot
    
    // Opened trade. -1 buy, 1 sell, 0 - none. In this simple
    // system, we can only have one opened position in a time
    double nType = 0;            

    // The result of NN calculations
    array arrNn = CREATE_ARRAY(0);

    // Open the NN, apply it to the lag file (created above, in
    // main() function) 
    double hNN = OPEN_NN(strNnFileName, bIsPathRelative);
    APPLY_NN(hNN, nExtractRecords, 1.0, 1, arrClv, arrLags, 1, arrNn);
    CLOSE_NN(hNN);

    // We are going to keep log of all our trades. I am using
    // a simple text file, that is supposed to look nice when
    // opened in Windows Notepad. If you need, change number
    // of tabs, spaces and so on, to make columns alined in your
    // editor of a choice, or save the file with HTML decorations,
    // as a table.
    string strReportFileName = 
        "c:\\s_projects\\CortexPro\\data\\stocks_nn\\report.txt";
    
    // A header to place in the REPORT.TXT    
    string str = 
        "Number  Operation  Price  Closed  ClosePrice  Total";

    double hFile = F_OPEN(strReportFileName, "wb");
    F_PRINT(hFile, "%s\r\n", str);

    // Initial values of constants we use
    double dSpread = 0.0005;
    double bStop;
    double dMaxDrawDown = 0;

    // Used to calculate drawdown
    double dCurrentMax = arrBalance[0];

    // Number of current trades, at the end, it holds the
    // total number of trades
    double nTradeNumber = 0;

    // Price at which the trade was opened. When we close the 
    // trade, it is used to calculate the profit
    double dOpenPrice;

    // Price at which stop loss will work.
    double dStop;

    // for all testing data, except for the first
    // few records, where the indicator is not
    // defined
    for(double nBar = nRemoveFirst + 1; 
        nBar < ARRAY_SIZE(arrClose); nBar = nBar + 1)
    {
        if(nType != 0) // If we have an open trade
        {
            // Will become 1 if stop loss have been fired
            bStop = 0;
            double dClosedAt;    // Execution price
            
            // If BUY and stop loss reached
            if(nType == -1 && arrLow[nBar] <= dStop)
            {
                arrBalance[ARRAY_SIZE(arrBalance)] = 
                    arrBalance[ARRAY_SIZE(arrBalance) - 1] 
                    + 100 * (arrLow [nBar] - dOpenPrice) * dLotSize;
                bStop = 1;
                dClosedAt = arrLow[nBar];
            }
            else
            {
                if(nType == 1 && arrHigh[nBar] >= dStop - dSpread)
                {
                    arrBalance[ARRAY_SIZE(arrBalance)] = 
                        arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
                        100 * (dOpenPrice - arrHigh[nBar] - dSpread) 
                        * dLotSize;
                    bStop = 1;
                    dClosedAt = arrHigh[nBar];
                }
            }

            // If stop loss have been fired
            if(bStop == 1)
            {
                nType = 0;
                arrBars[ARRAY_SIZE(arrBars)] = nBar;
                
                F_PRINT(hFile, "Stop    %f", dClosedAt, "    %f\r\n", 
                    arrBalance[ARRAY_SIZE(arrBalance) - 1]);
            }
        }

        // A drawdown is calculated in relation to the price
        // maximum that (so far) was reached.
        double dDrawDown = (dCurrentMax - 
            arrBalance[ARRAY_SIZE(arrBalance) - 1]) / dCurrentMax;
        dMaxDrawDown = MAX(dMaxDrawDown, dDrawDown);
        dCurrentMax = MAX(dCurrentMax, 
            arrBalance[ARRAY_SIZE(arrBalance) - 1]);

        // This code is not required, but will increase the
        // speed dramatically, by ignoring "bad" trading systems
        // In future, we may use dMaxDrawDown here. 
        if(arrBalance[ARRAY_SIZE(arrBalance) - 1] < 500)
        {
            break nBar;
        }

        // If we have met the "buy" conditions, and we are
        // not in a long position yet.
        if(nType != -1 && 
            arrNn[nBar - 1] <= dBuyLevel && 
            arrNn[nBar] >= dBuyLevel)
        {
            if(nType == 1)    // Close short positions, if any
            {
                arrBalance[ARRAY_SIZE(arrBalance)] = 
                    arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
                    100 * (dOpenPrice - arrHigh[nBar] - dSpread) 
                    * dLotSize;
                arrBars[ARRAY_SIZE(arrBars)] = nBar;
                F_PRINT(hFile, "Close    %f", arrHigh[nBar], 
                    "    %f\r\n", arrBalance[ARRAY_SIZE(arrBalance) - 1]);
            }

            // Buy long
            dOpenPrice = arrHigh[nBar];
            dStop = dOpenPrice - dStopLoss;
            nType = -1;
            
            nTradeNumber = nTradeNumber + 1;

            F_PRINT(hFile, "%.0f    ", nTradeNumber, 
                "Buy        %f    ", arrHigh[nBar]);
        }
        else    // If we have met "sell" conditions, and we are
                // not in short position yet. 
        {
            if(nType != 1 && 
                arrNn[nBar - 1] >= dSellLevel && 
                arrNn[nBar] <= dSellLevel)
            {
                if(nType == -1)    // Close long positions, if any
                {
                    arrBalance[ARRAY_SIZE(arrBalance)] =
                        arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
                        100 * (arrLow[nBar] - dOpenPrice) * dLotSize;
                    arrBars[ARRAY_SIZE(arrBars)] = nBar;
                
                    F_PRINT(hFile, "Close    %f", arrLow[nBar], 
                        "    %f\r\n", 
                        arrBalance[ARRAY_SIZE(arrBalance) - 1]);
                }

                // Sell short
                dOpenPrice = arrLow[nBar];
                dStop = dOpenPrice + dStopLoss;
                nType = 1;

                nTradeNumber = nTradeNumber + 1;

                F_PRINT(hFile, "%.0f    ", nTradeNumber, 
                    "Sell    %f    ", arrLow[nBar]);
            }
        }
    }
    // If at the end we have open positions, close them

    if(nType == 1)
    {
        arrBalance[ARRAY_SIZE(arrBalance)] = 
            arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
            100 * (dOpenPrice - arrHigh[nBar - 1] 
            - dSpread) * dLotSize;
        arrBars[ARRAY_SIZE(arrBars)] = nBar - 1;
        F_PRINT(hFile, "Close    %f", arrHigh[nBar], "    %f\r\n", 
            arrBalance[ARRAY_SIZE(arrBalance) - 1]);
    }
    else
    {
        if(nType == -1)
        {
            arrBalance[ARRAY_SIZE(arrBalance)] = 
            arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
                100 * (arrLow[nBar - 1] - dOpenPrice) * dLotSize;
            arrBars[ARRAY_SIZE(arrBars)] = nBar - 1;

            F_PRINT(hFile, "Close    %f", arrLow[nBar], "    %f\r\n",  
                arrBalance[ARRAY_SIZE(arrBalance) - 1]);
        }
    }

    F_PRINT(hFile, "Balance: %f", 
        arrBalance[ARRAY_SIZE(arrBalance) - 1], 
        ", Max. Drawdown: %f", dMaxDrawDown,
        ", Number of trades: %.0f", nTradeNumber);
    
    F_CLOSE(hFile);
}

Few words about the drawdown. There are few ways of calculating it, and we are using what I consider the most "honest". The drawdown is a measure of instability of our system. What is a chance, that it will loose money? Lets say the initial amount is $1000. If the profit goes 100, 200, 300, 400..., the drawdown is 0. If it goes 100, 200, 100..., then the drawdown is 0.1 (10%), as we have just lost an amount, equal to 1/10 of the initial deposit (from 1200 to 1100).

I would strongly advice against using trading systems with large drawdowns.

Also, here I use a drawdown, that is to be used with variable lot size. However, in the actual samples, that come with the eBook, you will see another version:

double dDrawDown = (dCurrentMax - 
    arrBalance[ARRAY_SIZE(arrBalance) - 1]) / 1000; //dCurrentMax;
dMaxDrawDown = MAX(dMaxDrawDown, dDrawDown);
    dCurrentMax = MAX(dCurrentMax, 
        arrBalance[ARRAY_SIZE(arrBalance) - 1]);

As you can see, here we always use 1000 (the initial amount) to calculate the drawdown. The reason is simple: we always use the same lot size (no money management yet), so there is no difference, how much money we have already accumulated on our account, an average profit should be constant. The worse possible scenario in this case looks like this: from the very beginning ($1000 on account) we are loosing money. If we use 1000$ to calculate the drawdown, we will get the worse drawdown. This will help us not to trick ourselves. For example, say, we traded for some time, and we have $10,000$ on our account. Then we loose some money, and we now have $8,000. Then we have recovered, and got $12,000. Good trading system? Probably not.

Let's repeat the logic again, as it is very important (and it will become even more important, when we start doing money management). We trade using fixed size lots. So, statistically, there is no guarantee, that the maximum loss will not happen at the very beginning, when we only have $1000. And if it happens, we will have -1000$ (10,000 - 8,000), so the trading system is probably too risky.

When we talk about the money management (probably, not in this text), we will have to use different approach to drawdown calculation.


Note, that in this trading system, I am using the worse possible scenario: I am buying using High and selling, using Low. Many testers do not follow these rules, and create trading systems, that work fine on historical data. But in the real life, these trading systems have very poor performance. Why?

Take a look at the price bar. It has Open, High, Low and Close. Do you know, how the price was moving inside the bar? No. So, let's say, your trading system generated a "buy" signal, at the bottom of the price bar (if dLow < dBuyLevel then buy). What should be the price of the execution for this order? Can it be "Low"? No. Because you have no guarantee, that the price within the bar moved from Open to Low, produced your trading signal and then jumped up to the High and stayed there for the rest of the bar duration.

One way to GUARANTEE, that in real trading your system will perform as good as during tests (or better), is to test it with the WORSE possible prices. Buy at High, close at Low. Sell at Low, close at High.

The other way is to trade only once per bar, when it just formed, and to use the PREVIOUS bars to calculate indicators. If you use this approach, you can open and close positions using the last (current) bar's OPEN. However, you cannot use its High, Low or Close, because you don't have them yet - the bar had just opened.


Note that I am using dLotSize equal 0.1 lot ($100). Obviously, in the "real" trading, you will benefit greatly, if the lot size is calculated depending on the money you have, something like:

forex_nn_01.tsc, part 3

dLotSize = FLOOR(arrBalance[
    ARRAY_SIZE(arrBalance) - 1] / 1000) * 100;
if(dLotSize < 100)
{
    dLotSize = 100;
}

However, we are doing testing here, not trading. And for testing, we need, among other things, to see how smooth the profit curve is. This is much easier to do if the lot size is the same (in ideal situation, for dLotSize = 100 we will get a straight line, with some positive slope, while in case of the adjustable lot size we will get an exponent, that is much harder to analyze).

Later in this text, we will apply money management rules to our trading system, but not yet.

After we are done with the last part of our testing function, let's walk through the rest of the code.

The following function creates a CLV indicator. It takes the interval as a parameter, which means that we can call it many times, during the optimization, passing different numbers.

Note, that I am using the NN that works in the 0 - 1 interval. The data can be normalized, of course, but I chose to divide the indicator by 2 and to add 0.5, so that it is in 0 - 1 range.

forex_nn_01.tsc, part 4

array CreateClv(double nInterval)
{    
    PRINT("%s\r\n", "Creating CLV indicator");

    array arrClv = CREATE_ARRAY(0);
    array arrPeriodLow = CREATE_ARRAY(0);
    array arrPeriodHigh = CREATE_ARRAY(0);

    double nArraySize = ARRAY_SIZE(arrClose);

    array arrPeriodLow = MVMIN(arrLow, nInterval);
    array arrPeriodHigh = MVMAX(arrHigh, nInterval);

    for(double i = 0; i < nInterval; i = i + 1) 
    {
        arrClv[i] = 0;
    }
    
    double dClose;
    double dLow;
    double dHigh;
    for(i = nInterval; i < nArraySize; i = i + 1)
    {
        dClose = arrClose[i];
        dLow = arrPeriodLow[i];
        dHigh = arrPeriodHigh[i];

        // / 2 + 1 to confine to 0...1 instead of -1...1
        arrClv[i] = (((dClose - dLow) - (dHigh - dClose))
            / (dHigh - dLow)) / 2 + 0.5; 
    }

    return arrClv;
}

To make lag file, we can use the CREATE_LAG_FILE function. Alternatively, we can do it by explicitly providing all the necessary code. In this case, we have more control, and we are going to need it, if we begin varying number of lagged columns and so on.

forex_nn_01.tsc, part 5

void CreateLagFile(string strLagFileName, array arrLags, 
    double nRemoveFirst)
{
    PRINT("%s\r\n", "Creating lag file");

    hFile = F_OPEN(strLagFileName, "wb");

    F_PRINT(hFile, "%s", "No,Clv,");
    
    for(i = 1; i < ARRAY_SIZE(arrLags); i = i + 1)
    {
        F_PRINT(hFile, ",Clv-%.0f", arrLags[i]); 
    }
    F_PRINT(hFile, "%s", "\r\n");

    for(i = nRemoveFirst; i < ARRAY_SIZE(arrClose); i = i + 1)
    {
        F_PRINT(hFile, "%.0f", i - nRemoveFirst + 1);
        for(double j = 0; j < ARRAY_SIZE(arrLags); j = j + 1)
        {
            F_PRINT(hFile, ",%f", arrClv[i - arrLags[j]]);     
        }
        F_PRINT(hFile, "%s", "\r\n");
    }

    F_CLOSE(hFile);
}

The nRemoveFirst parameter is important. Many functions, like indicators, moving averages, lag generators, for that mater, do not work well within the first few records of the dataset. Let's say we have MA(14) - what will it place in the records 1 - 13? So we choose to simply remove the first few (unreliable) records.

For the NewNn, as well as for all functions of this program, we need to pass as parameters only what can be changed during optimization process. For example, there is no need to pass a "skip before" parameter, as it is always the same.

forex_nn_01.tsc, part 6

void NewNn(array arrLags, double dStopError, 
    double nStopEpoch, double nNeuronsLayer2, 
    string strNnFileName)
{
    PRINT("%s\r\n", "Creating NN");

    double nSkipBefore = 0;
    double nSkipAfter = 0;
    string strStartLine = "";
    string strEndLine = "";
    double bReverseArrays = 0;

    // Inputs
    array arrInputColumns = CREATE_ARRAY(0);
    array_s arrInputColumnNames = CREATE_ARRAY_S(0);
    
    // 0  - Number, 1 - Clv, our input begins at column No 2

    for(double nInputCol = 1; 
        nInputCol < ARRAY_SIZE(arrLags); 
        nInputCol = nInputCol + 1) 
    {
        arrInputColumns[nInputCol - 1] = nInputCol + 1;
        arrInputColumnNames[nInputCol - 1] = 
            "Clv-" + NUM2STR(arrLags[nInputCol], "%.0f"); 
    }

    array arrOutputColumns = CREATE_ARRAY(0);
    arrOutputColumns[0] = 1;    // Clv

    array_s arrOutputColumnNames = CREATE_ARRAY_S(0);
    arrOutputColumnNames[0] = "Clv";

//  dStopError = 0;
//  nStopEpoch = 0; 
    
    // To do: Modify if more than one indicator is used
    double nNeuronsLayer1 = ARRAY_SIZE(arrLags);
//  double nNeuronsLayer2 = 7;
    double nNeuronsLayer3 = 1;
    double nNeuronsLayer4 = 0;
    double nLayers = 3; 
    double nActivation = 0;
    double nAdjustRange = 1.0;

    array arrOutTabInputColumns = CREATE_ARRAY(0);
    arrOutTabInputColumns[0] = 0;    // Number

    array_s arrOutTabInputColumnNames = CREATE_ARRAY_S(0);
    arrOutTabInputColumnNames[0] = "No";

    array arrOutTabOutputColumns = CREATE_ARRAY(0);
    // Desired output and NN output will be added to the 
    // same list, right after inputs
    arrOutTabOutputColumns[0] = ARRAY_SIZE(arrLags);    
    arrOutTabOutputColumns[1] = ARRAY_SIZE(arrLags) + 1;

    array_s arrOutTabOutputColumnNames = CREATE_ARRAY_S(0);
    arrOutTabOutputColumnNames[0] = "Clv"; 
    arrOutTabOutputColumnNames[1] = "NN: Clv" ;

    CREATE_NN(strNnFileName, bIsPathRelative, strLagFileName, 
        bIsPathRelative, nSkipBefore, nSkipAfter, strStartLine, 
        strEndLine, bReverseArrays, arrInputColumns, 
        arrInputColumnNames, arrOutputColumns, 
        arrOutputColumnNames, nExtractRecords, dStopError, 
        nStopEpoch, nNeuronsLayer1, nNeuronsLayer2, 
        nNeuronsLayer3, nNeuronsLayer4, nLayers, nActivation,
        nAdjustRange, arrOutTabInputColumns, 
        arrOutTabInputColumnNames, arrOutTabOutputColumns, 
        arrOutTabOutputColumnNames);
}

The TeachNn function simply brings up the NN dialog.

forex_nn_01.tsc, part 7

void TeachNn()
{
    PRINT("%s\r\n", "Opening NN dialog, teaching the NN");

    double bStartLearning = 1; 
    double bResumeScript = 1;
    double bReset = 1;

    OPEN_NN_FILE(strNnFileName, bIsPathRelative, 
        bStartLearning, bResumeScript, bReset);
}

Finally, we need a charting function. It is not mandatory, but it is always a good idea to see what our profit line looks like. The following code uses the XML to produce a chart, so it is a good idea to read the tutorial. Alternatively, you can draw the chart, rather than saving it in a file. To do it, use one of the samples, that are in the samples/scripts directory. Finally, you can modify the code, to produce HTML, rather than XML. HTML is easier to learn, but the code itself will be a bit less readable.

forex_nn_01.tsc, part 8

void Chart(strForexName)
{
    string strXML = "";
    strXML = strXML + "<forex>\r\n";

    string strPath = 
        "c:\\S_Projects\\CortexPro\\data\\stocks_nn\\images\\";
    
    string strProfitPath = strPath + strForexName + ".png";    
    
    strXML = strXML + "\t<symbol>\r\n";
    strXML = strXML + "\t\t<symbol>\r\n";
    strXML = strXML + "\t\t\t" + strForexName + 
        NUM2STR(arrBalance[ARRAY_SIZE(arrBalance) - 1], ", 
            Profit: %f") +  
        NUM2STR(nTradeNumber, ", Number of trades: %.0f") +
        NUM2STR(dMaxDrawDown, ", Max. Drawdown: %f\r\n");
    strXML = strXML + "\t\t</symbol>\r\n";

    strXML = strXML + SAVE_CHART(400, 500, 
        0, strProfitPath, arrBars, arrBalance);

    strXML = strXML + "\t</symbol>\r\n";
    strXML = strXML + "</forex>\r\n";

    SAVE_XML(strPath, "chart_forex_nn", "chart_forex_nn", 
        "root", strXML);
    SHOW_XML(strPath + "chart_forex_nn.xml");
}

Compile and Run the script.

Well... As expected, using 7 hours as an interval for the CLV produced very poor results:

Optimization - part 2

What to optimize?

The reason for the poor results is quite obvious: we used the Interval, Stop Loss, buy and sell levels and other parameters, that were purely random - we just picked first that came in mind! What if we try few combinations?

First of all, by overoptimizing the buy and sell levels, we can ruin our future performance. However we still can tune them, especially, if the performance is close for close values of buy and sell limits. For example, if we have -10% profit at buy limit equal 0.3, and +1000% profit when it equals 0.35, then there is probably a lucky coincidence, and we should not use 0.35 for our trading system, as in future it will probably not happen again. If, instead, we have -10% and +10% (instead of +1000%), it may be safer to use.

Generally, our trading system should be built for WORSE possible scenario, as if during the "real" trading the performance will be better, then during the test, we will survive, but not the other way around.

We can vary the value for the indicator interval, provided we have enough trades, so that we can be confident, in terms of statistics, in the performance of a system.

We certainly can vary the number of neurons, I don't think it can be overoptimized easily.

We can vary number of inputs and lags for inputs. It is possible to overoptimize this, but it is not very likely to happen.

And, of course, we can try different indicators.

How to optimize?

As have already been mentioned, if we start trying all possible combinations, it will take forever. So we are going to cheat. We will create pre-defined sets of parameters, that we think are reasonable, and pass them to the program.

To make as few calculations as possible, note, that Clv-1 and Clv-2 are, probably, important, but what about Clv-128? And - if we already have Clv-128, do we need Clv-129? Probably, not. So we are going to have something like Clv-1, Clv-2, Clv-4, Clv-8, ... Clv-128 with just few variations, which will make our calculation time thousands times shorter.

Can it work at all?

What is it exactly we want to predict? Until this point we have used 1 hour chart for EURUSD, and we were predicting the next bar's CLV. Will the CLV+2 be better? What about CLV+3?

Also, especially considering the poor performance of our first trading system, it would be nice to know, that - at least in the "ideal" world, the goal (profitable trading) can be achieved.

To answer these questions, let's create a simple testing program. We assume, that our prediction is 100 % accurate, and, based on this assumption, we will use CLV+N, not the NN predicted one. That's right - we are going to take data from the future, and to use them instead of the NN prediction. This approach wouldn't work in the real life, of course, but at leats, it will give us some ideas of what to expect.

When looking at the results, please keep in mind, that we are not using any advanced money management, our lot size is set to a minimum $100. If you use variable lot sizes, results will be dramatically different. But even at a lot size set to 0.1 we can see (below) that getting the information from the future is an ultimate trader's "holly graal".

forex_nn_02.tsc, part 1

void main()
{
    OUT_CLEANUP();

    string strImagePath = 
        "c:\\S_Projects\\CortexPro\\data\\stocks_nn\\images\\";

    string strForexName = "EURUSD_H1";

    PRINT("%s\r\n", "Deleting image files...");

    array_s arrDirList = 
        GET_DIR(strImagePath, 0, "*.png");
    for(double n = 0; n < ARRAY_SIZE(arrDirList); n = n + 1)
    {
        F_UNLINK(arrDirList[n]);
    }

    // ***** Loading data
    string strDataFileName = 
        "c:\\S_Projects\\CortexPro\\data\\samples\\forex\\"
        + strForexName + ".TXT";
    double bIsPathRelative = 0;

    array     arrDate = CREATE_ARRAY(0);
    array arrTime = CREATE_ARRAY(0);
    array arrOpen = CREATE_ARRAY(0);
    array arrHigh = CREATE_ARRAY(0);
    array arrLow = CREATE_ARRAY(0);
    array arrClose = CREATE_ARRAY(0);

    TABLE_LOADER(strDataFileName, bIsPathRelative, 0, "", 
        0, "", 0, arrDate, 1, arrTime, 2, arrOpen, 3, 
        arrHigh, 4, arrLow, 5, arrClose);

You are already familiar with this code, it was used in FOREX_NN_01.TSC. It handles data loading. The only difference is in the part that obtains the list of files in the "images" directory and deletes all files with the .PNG extention. The reason for this code is simple: during our tests we are going to create many - may be, thousands - image files. We don't want them to hung around after we are done. So at the beginning of the script we are deleting images, created by other scripts.

forex_nn_02.tsc, part 2

    array arrClvIntervals = CREATE_ARRAY(0);
    arrClvIntervals[0] = 4;   
    arrClvIntervals[1] = 6;   
    arrClvIntervals[2] = 8;   
    arrClvIntervals[3] = 12;  
    arrClvIntervals[4] = 16;   
    arrClvIntervals[5] = 24;
    arrClvIntervals[6] = 32;   
    arrClvIntervals[7] = 48;

    array arrMa = CREATE_ARRAY(0);
    arrMa[0] = 1;    // No MA
    arrMa[1] = 4;
    arrMa[2] = 7;
    arrMa[3] = 14;

    // How far in future we want to predict
    array arrOutLag = CREATE_ARRAY(0);
    arrOutLag[0] = 0;    // Current
    arrOutLag[1] = 1;    // 1 bar
    arrOutLag[2] = 2;
    arrOutLag[3] = 3;
    arrOutLag[4] = 4;
    // Note: from this point coinsides with arrClvIntervals
    arrOutLag[5] = 6;    
    arrOutLag[6] = 8;    
    arrOutLag[7] = 12;  
    arrOutLag[8] = 16;   
    arrOutLag[9] = 24;
    arrOutLag[10] = 32;   
    arrOutLag[11] = 48;

    array arrStopLoss = CREATE_ARRAY(0);
    arrStopLoss[0] = 0.0025;
    arrStopLoss[1] = 0.005;
    arrStopLoss[2] = 0.0075;
    arrStopLoss[3] = 0.01;

    array arrClv = CREATE_ARRAY(0);
    array arrClvSmooth = CREATE_ARRAY(0);

    double nNetNum = 0;
    double nRemoveFirst = 128;

    string strXML = "<forex>\r\n";

    double nInterval;
    double nMaIdx;
    double nMa;
    double nOutLagIdx;
    double nOutLag;
    double dStopLoss;

    array arrBalance = CREATE_ARRAY(0);
    array arrBalanceBuy = CREATE_ARRAY(0);
    array arrBalanceSell = CREATE_ARRAY(0);
    
    double nTradeNumber;

    double dExpectedCycles = ARRAY_SIZE(arrClvIntervals) 
        * ARRAY_SIZE(arrMa) * ARRAY_SIZE(arrOutLag) * 
        ARRAY_SIZE(arrStopLoss) * 6 * 10 * 10;

Just a few comments. We do not want to try all possible values for, for example, CLV interval. Instead, we can create an array, that contains only values we want to test. Then (see below) we will walk through this array.

Stop losses are important part of any trading strategy, so I have decided to vary them as well. It is a dangerous idea, however, as it is easy to overoptimize the system.

I am planning to test different values for buy and sell levels, but it will be done in cycle, without using arrays.

Unlike in our previous example, we want to have a large XML file, containing many images. To do it, I have moved the code, that is forming the XML header and footer outside of the Chart function. Read the XML tutorial for details.

Note, that I am using 0 as the first lag, which means, that first I am testing the indicator (CLV) that was not "shifted" from the future. Just to get an idea, how good out "trading system" would be without NN (horrible, is the right word. It is loosing all the money).

Cortex uses the Internet Explorer control to display XML pages. When pages grow large, it takes a lot of memory. If your computer cannot handle it, consider creating multiple XML or HTML pages, instead. In the case of forex_nn_02, it should not be a problem, as the page is relatively short. Alternatively (that is what I am doing in scripts later in this text), create XML file, but do not open it from Cortex. Open them using Internet Explorer instead - unlike IE control, the Internet Explorer does not have the memory problem.

Now the code that is trying different combinations of parameters.

forex_nn_02.tsc, part 3

    double nWinners = 0;

    for(double nIntervalIdx = 0; 
        nIntervalIdx < ARRAY_SIZE(arrClvIntervals);
        nIntervalIdx = nIntervalIdx + 1)
    {
        nInterval = arrClvIntervals[nIntervalIdx];

        ARRAY_REMOVE(arrClv, -1);
        arrClv = CreateClv(nInterval);

        for(nMaIdx = 0; nMaIdx < ARRAY_SIZE(arrMa); 
            nMaIdx = nMaIdx + 1)
        {
            OUT_CLEANUP();
            nMa = arrMa[nMaIdx];

            ARRAY_REMOVE(arrClvSmooth, -1);
            arrClvSmooth = EXP_MVAVG(arrClv, nMa);

            for(nOutLagIdx = 0; nOutLagIdx < ARRAY_SIZE(arrOutLag); 
                nOutLagIdx = nOutLagIdx + 1)
            {
                nOutLag = arrOutLag[nOutLagIdx];

                for(double nStopIdx = 0; nStopIdx < 
                    ARRAY_SIZE(arrStopLoss);
                    nStopIdx = nStopIdx + 1)
                {
                    dStopLoss = arrStopLoss[nStopIdx];

                    for(double dTakeProfit = 0; dTakeProfit <= 0.05; 
                        dTakeProfit = dTakeProfit + 0.01)
                    {
                        for(double nBuyIdx = 0; nBuyIdx < 10; 
                            nBuyIdx = nBuyIdx + 1)
                        {
                            double dBuyLevel = 0.15 + 0.02 * nBuyIdx;

                            for(double nSellIdx = 0; nSellIdx < 10; 
                                nSellIdx = nSellIdx + 1)
                            {
                                double dSellLevel = 
                                    0.85 - 0.02 * nSellIdx;

                                PRINT("%.0f", nNetNum, " of %.0f", 
                                    dExpectedCycles);

                                Test();

                                PRINT(" (%.0f)\r\n", nWinners);

                                if(ARRAY_SIZE(arrBalance) <= 1 || 
                                    (ARRAY_SIZE(arrBalance) > 1 && 
                                    (arrBalance[ARRAY_SIZE(arrBalance)
                                        - 1] <= 5000 ||
                                        arrBalanceBuy[ARRAY_SIZE(
                                            arrBalanceSell) - 1] 
                                            <= 3000 ||
                                        arrBalanceSell[ARRAY_SIZE(
                                            arrBalanceSell) - 1] 
                                            <= 2000)) ||
                                        nTradeNumber < 50)
                                {
                                    continue nBuySellIdx;
                                }

                                nWinners = nWinners + 1;
                                            
                                Chart(strForexName);
                            }
                        }
                    }
                }
            }
        }
    }

    strXML = strXML + "</forex>\r\n";
    SAVE_XML(strImagePath, "chart_forex_nn", "chart_forex_nn", 
        "root", strXML);
    SHOW_XML(strImagePath + "chart_forex_nn.xml");

    PRINT("%s\r\n", "Done");
}

Here, we are using nested cycles. In every cycle, we are assidning some variable (for example, nInterval for the outer cycle). This way the cycle will assign values of all elements of a corresponding array, one in a time. Then WITHIN it, the inner cycle is used, and so on, so that all combinations of all array elements are tested.

In the innermost cycle, I am calling the Test() function, to "test trade", and Chart() to add a new picture to a list of images saved on disk. Note, that this Chart() does not show any images, until all cycles are completed.

The Test() and CreateClv() functions are almost the same as in the previous example. The only real difference is due to the fact that it is called more then once. To do it, I am calling ARRAY_REMOVE to cleanup arrays.

Also, notice, that we are only creating charts for the combinations of parameters, that produce trading system with positive profit. Otherwise, we call "continue", to skip the Chart() function.

Finally, we have Take Profit now, so our trading system can be a bit more flexible.

forex_nn_02.tsc, part 4

void Test()
{
    nNetNum = nNetNum + 1;
    
    ARRAY_REMOVE(arrBalance, -1);
    arrBalance[0] = 1000;

    ARRAY_REMOVE(arrBalanceBuy, -1);
    arrBalanceBuy[0] = 0;

    ARRAY_REMOVE(arrBalanceSell, -1);
    arrBalanceSell[0] = 0;

    array arrBars = CREATE_ARRAY(0);
    arrBars[0] = 0;

    // 0.1 lot
    double dLotSize = 100;
    // 1 buy, -1 sell, 0 - none
    double nType = 0;

    double dSpread = 0.0005;
    double bStop;
    
    // Max. Drawdown
    double dMaxDrawDown = 0;
    
    // Used to calculate drawdown
    double dCurrentMax = arrBalance[0];

    nTradeNumber = 0;
    double nTradeNumberBuy = 0;
    double nTradeNumberSell = 0;
    double dOpenPrice;
    double dStop;
    double dTp = 0;

    for(double nBar = nRemoveFirst + 1; 
        nBar < ARRAY_SIZE(arrClose) - nRemoveFirst; 
        nBar = nBar + 1)
    {
        if(nType != 0)
        {
            bStop = 0;
            double dClosedAt;
            
            // If BUY and stop loss or take profit reached
            if(nType == -1 && (arrLow[nBar] <= dStop || 
                (dTakeProfit > 0 
                && arrHigh[nBar] >= dTp - dSpread)))
            {
                arrBalance[ARRAY_SIZE(arrBalance)] = 
                    arrBalance[ARRAY_SIZE(arrBalance) - 1] 
                    + 100 * (arrLow[nBar] - dOpenPrice) * dLotSize;

                arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = 
                    arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1] 
                    + 100 * (arrLow[nBar] - dOpenPrice) * dLotSize;

                arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = 
                    arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1]; 

                bStop = 1;
                dClosedAt = arrLow[nBar];
            }
            else
            {
                if(nType == 1 && (arrHigh[nBar] >= dStop - dSpread ||
                    (dTakeProfit > 0 && arrLow[nBar] <= dTp)))
                {
                    arrBalance[ARRAY_SIZE(arrBalance)] = 
                        arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
                        100 * (dOpenPrice - arrHigh[nBar] - dSpread) 
                        * dLotSize;

                    arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = 
                        arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1];

                    arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = 
                        arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1] + 
                        100 * (dOpenPrice - arrHigh[nBar] - dSpread) 
                        * dLotSize;

                    bStop = 1;
                    dClosedAt = arrHigh[nBar];
                }
            }

            if(bStop == 1)
            {
                nType = 0;
                arrBars[ARRAY_SIZE(arrBars)] = nBar;
            }
        }

        double dDrawDown = (dCurrentMax - 
            arrBalance[ARRAY_SIZE(arrBalance) - 1]) / dCurrentMax;
        dMaxDrawDown = MAX(dMaxDrawDown, dDrawDown);
        dCurrentMax = MAX(dCurrentMax, arrBalance[
            ARRAY_SIZE(arrBalance) - 1]);

        if(arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1] < -500 ||
            arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1] < -500 ||
            dMaxDrawDown > 0.2)
        {
            break nBar;
        }

        if(nType != -1 && 
            arrClvSmooth[nBar - 1 + nOutLag] <= dBuyLevel && 
            arrClvSmooth[nBar + nOutLag] >= dBuyLevel)
        {
            if(nType == 1)
            {
                arrBalance[ARRAY_SIZE(arrBalance)] = 
                    arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
                    100 * (dOpenPrice - arrHigh[nBar] - dSpread) 
                    * dLotSize;

                arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = 
                    arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1] + 
                    100 * (dOpenPrice - arrHigh[nBar] - dSpread) 
                    * dLotSize;

                arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = 
                    arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1];

                arrBars[ARRAY_SIZE(arrBars)] = nBar;
            }

            dOpenPrice = arrHigh[nBar];
            dStop = dOpenPrice - dStopLoss;
            dTp = dOpenPrice + dTakeProfit;
            nType = -1;
            
            nTradeNumber = nTradeNumber + 1;
            nTradeNumberBuy = nTradeNumberBuy + 1;
        }
        else
        {
            if(nType != 1 && 
                arrClvSmooth[nBar - 1 + nOutLag] >= dSellLevel && 
                arrClvSmooth[nBar + nOutLag] <= dSellLevel)
            {
                if(nType == -1)
                {
                    arrBalance[ARRAY_SIZE(arrBalance)] =
                        arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
                        100 * (arrLow[nBar] - dOpenPrice) * dLotSize;

                    arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] =
                        arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1] + 
                        100 * (arrLow[nBar] - dOpenPrice) * dLotSize;

                    arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] =
                        arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1];

                    arrBars[ARRAY_SIZE(arrBars)] = nBar;
                }

                dOpenPrice = arrLow[nBar];
                dStop = dOpenPrice + dStopLoss;
                dTp = dOpenPrice - dTakeProfit;
                nType = 1;
                
                nTradeNumber = nTradeNumber + 1;
                nTradeNumberSell = nTradeNumberSell + 1;
            }
        }
    }

    // If at the end we have open positions, close them

    if(nType == 1)
    {
        arrBalance[ARRAY_SIZE(arrBalance)] = 
            arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
            100 * (dOpenPrice - arrHigh[nBar - 1] - dSpread) 
            * dLotSize;

        arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = 
            arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1] + 
            100 * (dOpenPrice - arrHigh[nBar - 1] - dSpread) 
            * dLotSize;

        arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = 
            arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1];

        arrBars[ARRAY_SIZE(arrBars)] = nBar - 1;
    }
    else
    {
        if(nType == -1)
        {
            arrBalance[ARRAY_SIZE(arrBalance)] = 
                arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
                    100 * (arrLow[nBar - 1] - dOpenPrice) 
                    * dLotSize;
    
            arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = 
                arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1] + 
                100 * (arrLow[nBar - 1] - dOpenPrice) * dLotSize;
    
            arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = 
                arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1];
    
            arrBars[ARRAY_SIZE(arrBars)] = nBar - 1;
        }
    }
}
// -------------------
array CreateClv(double nInterval)
{    
    PRINT("%s\r\n", "Creating CLV indicator");

    array arrClv = CREATE_ARRAY(0);
    array arrPeriodLow = CREATE_ARRAY(0);
    array arrPeriodHigh = CREATE_ARRAY(0);

    double nArraySize = ARRAY_SIZE(arrClose);

    array arrPeriodLow = MVMIN(arrLow, nInterval);
    array arrPeriodHigh = MVMAX(arrHigh, nInterval);

    for(double i = 0; i < nInterval; i = i + 1) 
    {
        arrClv[i] = 0;
    }
    
    double dClose;
    double dLow;
    double dHigh;
    for(i = nInterval; i < nArraySize; i = i + 1)
    {
        dClose = arrClose[i];
        dLow = arrPeriodLow[i];
        dHigh = arrPeriodHigh[i];

        // / 2 + 1 to confine to 0...1 instead of -1...1
        arrClv[i] = (((dClose - dLow) - (dHigh - dClose))
            / (dHigh - dLow)) / 2 + 0.5; 
    }

    return arrClv;
}

The Chart() function was broken into two pieces. The header and the footer should be written to the XML file only once, so they were moved to the main part of the program.

Also, I am using the counter, to save files under the different names. The information about parameters is written to the header of an image, so we can easily see which one it is. Finally, images are only saved for winning configurations, meaning the balance at the end should be more, then at the beginning.

forex_nn_02.tsc, part 5

void Chart(strForexName)
{
    string strProfitPath = strImagePath + 
        strForexName + NUM2STR(nNetNum, "%.0f") + ".png";    
    
    strXML = strXML + "\t<symbol>\r\n";
    strXML = strXML + "\t\t<symbol>\r\n";
    strXML = strXML + "\t\t\t" + strForexName + 
        NUM2STR(arrBalance[ARRAY_SIZE(arrBalance) - 1], 
            ", Profit: %f") +  
        NUM2STR(nTradeNumber, ", Number of trades: %.0f") +
        NUM2STR(dMaxDrawDown, ", Max. Drawdown: %f") +
        NUM2STR(nInterval, ", CLV: %.0f") +
        NUM2STR(nMa, ", MA: %.0f") +
        NUM2STR(nOutLag, ", Out.Lag: %.0f") +
        NUM2STR(dStopLoss, ", Stop: %f") +
        NUM2STR(dTakeProfit, ", TakeProfit: %f") +
        NUM2STR(dBuyLevel, ", Buy: %f") + 
        NUM2STR(dSellLevel, ", Sell: %f\r\n");

    strXML = strXML + "\t\t</symbol>\r\n";

    strXML = strXML + "\t\t" + 
        SAVE_CHART(400, 500, 0, strProfitPath, arrBars, arrBalance);

    strXML = strXML + "\t</symbol>\r\n";
}

Run the program (it will take some time to complete). You will end up with a large XML page with images, one for each winning configuration.

Some of the results are great, however, as we used data "from the future", this system will not work in the real life. Actually, if you look at the Test() function, you will notice, that the cycle stops before we reach the last element of arrClose:

for(nBar = nRemoveFirst + 1; nBar <
    ARRAY_SIZE(arrClose) - nRemoveFirst; ...

The reason is, we don't have the "future" data for the last elements. However, we can use the NN to create a prediction.

Trading with Neural Networks

Now that we know, what could have been done, IF we had glimps of information from the future, we can use this information to build a trading system, that does not "cheat", using NN prediction, instead of the future quotes (that are not available in the real life).

Once again, note, that the purpose of this text is to show you, how to use Cortex, not how to create a perfect trading system.

In the code below, we are going to try different combinations of Interval, MA, stop loss / take profit, buy/sell levels and so on. Some of these parameters (CLV interval, MA and output lag) are taken from the "winners", that we have found in the forex_nn_03 script.

The code below has some comments, also, by this time you should be able to understand it without help.

forex_nn_03.tsc

void main()
{
    OUT_CLEANUP();

    string strImagePath = 
        "c:\\S_Projects\\CortexPro\\data\\stocks_nn\\images\\";

    PRINT("%s\r\n", "Deleting image files...");

    array_s arrDirList = 
        GET_DIR(strImagePath, 0, "*.png");
    for(double n = 0; n < ARRAY_SIZE(arrDirList); n = n + 1)
    {
        F_UNLINK(arrDirList[n]);
    }

    string strForexName = "EURUSD_H1";

    string strNnPath = 
        "c:\\S_Projects\\CortexPro\\data\\stocks_nn\\nn\\";
    string strNnFileName = strNnPath + strForexName + ".nn";

    PRINT("%s\r\n", "Deleting nn files...");

    array_s arrDirList = 
        GET_DIR(strNnPath, 0, "*.nn");
    for(double n = 0; n < ARRAY_SIZE(arrDirList); n = n + 1)
    {
        F_UNLINK(arrDirList[n]);
    }
    
    // ------------

    string strDataFileName = 
        "c:\\S_Projects\\CortexPro\\data\\samples\\forex\\"
        + strForexName + ".TXT";
    string strLagFileName = 
        "c:\\S_Projects\\CortexPro\\data\\stocks_nn\\forex\\"
        + strForexName + ".lgg";
    
    double bIsPathRelative = 0;

    array     arrDate = CREATE_ARRAY(0);
    array arrTime = CREATE_ARRAY(0);
    array arrOpen = CREATE_ARRAY(0);
    array arrHigh = CREATE_ARRAY(0);
    array arrLow = CREATE_ARRAY(0);
    array arrClose = CREATE_ARRAY(0);

    TABLE_LOADER(strDataFileName, bIsPathRelative, 0, "", 
        0, "", 0, arrDate, 1, arrTime, 2, arrOpen, 3, 
        arrHigh, 4, arrLow, 5, arrClose);

    // Interval, Ma, OutLag 
    array_s arrParameters = CREATE_ARRAY_S(0);
    arrParameters[0] = "24,4,2";
    arrParameters[1] = "24,4,3";
    arrParameters[2] = "24,7,2";
    arrParameters[3] = "24,7,3";
    arrParameters[4] = "24,7,4";
    arrParameters[5] = "32,4,2";
    arrParameters[6] = "32,4,3";
    arrParameters[7] = "32,7,4";
    arrParameters[8] = "48,7,8";

    array arrClv;
    array arrClvSmooth;

    double nNetNum = 1;
    double nRemoveFirst = 200;

    double dStopError = 0;
    double nStopEpoch; 

    array arrLags = CREATE_ARRAY(0);
    array_s arrStrLags = CREATE_ARRAY_S(0)

    // ATTN: These lags are based on nWinLag. 
    // Example: nWinLag = 1, then each number 
    // should be increased by 1
    arrStrLags[0] = 
        "18,0,1,2,3,4,6,8,12,14,16,20,24,28,32,40,48,56,64";
    arrStrLags[1] = 
        "20,0,1,2,3,4,6,8,12,14,16,20,24,28,32,50,70,90,110,130,150";

    array arrBalance = CREATE_ARRAY(0);
    array arrBalanceBuy = CREATE_ARRAY(0);
    array arrBalanceSell = CREATE_ARRAY(0);
    
    array arrWinInterval = CREATE_ARRAY(0);
    array arrWinMa = CREATE_ARRAY(0);

    array arrWinLag = CREATE_ARRAY(0);
    array arrWinStopLoss = CREATE_ARRAY(0);
    array arrWinTakeProfit = CREATE_ARRAY(0);

    array arrWinBuyLevel = CREATE_ARRAY(0);
    array arrWinSellLevel = CREATE_ARRAY(0);

    array arrWinNeurons = CREATE_ARRAY(0);
    array_s arrWinNnLags = CREATE_ARRAY_S(0);
    array arrWinProfit = CREATE_ARRAY(0);
    array arrWinProfitBuy = CREATE_ARRAY(0);
    array arrWinProfitSell = CREATE_ARRAY(0);

    array arrNn = CREATE_ARRAY(0);

    array arrStopLoss = CREATE_ARRAY(0);
    arrStopLoss[0] = 0.005;
    arrStopLoss[1] = 0.0075;
    arrStopLoss[2] = 0.01;
    arrStopLoss[3] = 0.015;
    arrStopLoss[4] = 0.02;

    string strParam;
    string strToken;
    double nMa;
    double nOutLag;
    double hNn;
    double dBuyLevel;
    double dSellLevel;
    double nInterval;
    double nExtractRecords;

    double nCounter = 0;
    double nSelectedCounter = 0;
    double dExpectedCycles = 
        ARRAY_SIZE(arrParameters) * ARRAY_SIZE(arrStrLags);

    for(double nParIdx = 0; nParIdx < ARRAY_SIZE(arrParameters); 
        nParIdx = nParIdx + 1)
    {
        strParam = arrParameters[nParIdx];
        strToken = GET_TOKEN(strParam, ",");
        nInterval = STR2NUM(strToken);

        arrClv = CreateClv(nInterval);

        strToken = GET_TOKEN(strParam, ",");
        nMa = STR2NUM(strToken);

        arrClvSmooth = EXP_MVAVG(arrClv, nMa);

        strToken = GET_TOKEN(strParam, ",");
        nOutLag = STR2NUM(strToken);

        for(double nLagIdx = 0; nLagIdx < ARRAY_SIZE(arrStrLags); 
            nLagIdx = nLagIdx + 1)
        {
            double nNumOfLags;
            string strLagBuf = arrStrLags[nLagIdx];
            CreateLagFile(strLagBuf, nRemoveFirst);

            double nNeurons = (nNumOfLags + 1) / 2 + 1;
            nStopEpoch = nNeurons * 1000;

            nCounter = nCounter + 1;
            
            NewNn(arrLags, dStopError, nStopEpoch, nNeurons);
            TeachNn();

            hNn = OPEN_NN(strNnFileName, bIsPathRelative);
            APPLY_NN(hNn, nExtractRecords, 1.0, 1, 
                arrClvSmooth, arrLags, 1, arrNn);
            CLOSE_NN(hNn);

            OUT_CLEANUP();
                
            for(double nStopIdx = 0; nStopIdx < ARRAY_SIZE(arrStopLoss);
                nStopIdx = nStopIdx + 1)
            {
                double dStopLoss = arrStopLoss[nStopIdx];

                for(double dTakeProfit = 0; dTakeProfit <= 0.05; 
                dTakeProfit = dTakeProfit + 0.01)
                {
                    for(double nBuySellIdx = 0; nBuySellIdx < 24; 
                        nBuySellIdx = nBuySellIdx + 1)
                    {
                        dBuyLevel = 0.1 + 0.01 * nBuySellIdx;
                        dSellLevel = 0.9 - 0.01 * nBuySellIdx;

                        PRINT("%.0f", nCounter, " of %.0f", 
                            dExpectedCycles, " (%.0f): ", 
                            nSelectedCounter, "%.0f\r\n", nNetNum);

                        Test();

                        if(arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1] 
                            > 1000 
                            && arrBalanceSell[ARRAY_SIZE(arrBalanceSell) 
                            - 1] > 1000)
                        {
                            nSelectedCounter = nSelectedCounter + 1;

                            SaveWin();

                            double dBuyLevelTmp = dBuyLevel;
                            double dSellLevelTmp = dSellLevel;
                                    
                            dBuyLevel = dBuyLevel - 0.003;
                            dSellLevel = dSellLevel + 0.003;

                            Test();
                            if(arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) 
                                - 1] > 1000 
                                && arrBalanceSell[ARRAY_SIZE(
                                    arrBalanceSell) - 1] > 1000)
                            {
                                SaveWin();
                            }

                            dBuyLevel = dBuyLevel + 0.006;
                            dSellLevel = dSellLevel - 0.006;

                            Test();
                            if(arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) 
                                - 1] > 1000 
                                && arrBalanceSell[ARRAY_SIZE(
                                    arrBalanceSell) - 1] > 1000)
                            {
                                SaveWin();
                            }

                            dBuyLevel = dBuyLevelTmp;
                            dSellLevel = dSellLevelTmp;
                        }
                    }
                }
            }
        }
    }

    Chart(strForexName);

    PRINT("%s\r\n", "Done");
}

// ----------------

void Test()
{
    nNetNum = nNetNum + 1;
    
    ARRAY_REMOVE(arrBalance, -1);
    arrBalance[0] = 1000;

    ARRAY_REMOVE(arrBalanceBuy, -1);
    arrBalanceBuy[0] = 1000;

    ARRAY_REMOVE(arrBalanceSell, -1);
    arrBalanceSell[0] = 1000;

    array arrBars = CREATE_ARRAY(0);
    arrBars[0] = 0;

    double dLotSize = 100;        // 0.1 lot
    double nType = 0;            // 1 buy, -1 sell, 0 - none

    double dSpread = 0.0005;
    double bStop;
    double dMaxDrawDown = 0;                // Max. Drawdown
    double dCurrentMax = arrBalance[0];        // To calculate drawdown

    double nTradeNumber = 0;
    double nTradeNumberBuy = 0;
    double nTradeNumberSell = 0;
    double dOpenPrice;
    double dStop;
    double dTp = 0;

    for(double nBar = nRemoveFirst + 1; 
        nBar < ARRAY_SIZE(arrClose) - nRemoveFirst; 
        nBar = nBar + 1)
    {
        if(nType != 0)
        {
            bStop = 0;
            double dClosedAt;

            // If BUY and stop loss or take profit reached
            if(nType == -1 && (arrLow[nBar] <= dStop 
                || (dTakeProfit > 0 
                && arrHigh[nBar] >= dTp - dSpread)))
            {
                arrBalance[ARRAY_SIZE(arrBalance)] = 
                    arrBalance[ARRAY_SIZE(arrBalance) - 1] 
                    + 100 * (arrLow[nBar] - dOpenPrice) * dLotSize;

                arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = 
                    arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1] 
                    + 100 * (arrLow[nBar] - dOpenPrice) * dLotSize;

                arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = 
                    arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1]; 

                bStop = 1;
                dClosedAt = arrLow[nBar];
            }
            else
            {
                if(nType == 1 && (arrHigh[nBar] >= dStop - dSpread ||
                    (dTakeProfit > 0 && arrLow[nBar] <= dTp)))
                {
                    arrBalance[ARRAY_SIZE(arrBalance)] = 
                        arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
                        100 * (dOpenPrice - arrHigh[nBar] - dSpread) 
                            * dLotSize;

                    arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = 
                        arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1];

                    arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = 
                        arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1] + 
                        100 * (dOpenPrice - arrHigh[nBar] - dSpread) 
                            * dLotSize;

                    bStop = 1;
                    dClosedAt = arrHigh[nBar];
                }
            }

            if(bStop == 1)
            {
                nType = 0;
                arrBars[ARRAY_SIZE(arrBars)] = nBar;
            }
        }

        if(arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1] < 500 ||
            arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1] < 500)
        {
            break nBar;
        }

        double dDrawDown = (dCurrentMax - 
            arrBalance[ARRAY_SIZE(arrBalance) - 1]) / 1000; 
        dMaxDrawDown = MAX(dMaxDrawDown, dDrawDown);
        dCurrentMax = MAX(dCurrentMax, 
            arrBalance[ARRAY_SIZE(arrBalance) - 1]);

        if(nType != -1 && arrNn[nBar - 1] <= dBuyLevel 
            && arrNn[nBar] >= dBuyLevel)
        {
            if(nType == 1)
            {
                arrBalance[ARRAY_SIZE(arrBalance)] = 
                    arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
                    100 * (dOpenPrice - arrHigh[nBar] - dSpread) 
                    * dLotSize;

                arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = 
                    arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1] + 
                    100 * (dOpenPrice - arrHigh[nBar] - dSpread) 
                    * dLotSize;

                arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = 
                    arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1];

                arrBars[ARRAY_SIZE(arrBars)] = nBar;
            }

            dOpenPrice = arrHigh[nBar];
            dStop = dOpenPrice - dStopLoss;
            dTp = dOpenPrice + dTakeProfit;
            nType = -1;
            
            nTradeNumber = nTradeNumber + 1;
            nTradeNumberBuy = nTradeNumberBuy + 1;
        }
        else
        {
            if(nType != 1 && arrNn[nBar - 1] >= dSellLevel 
                && arrNn[nBar] <= dSellLevel)
            {
                if(nType == -1)
                {
                    arrBalance[ARRAY_SIZE(arrBalance)] =
                        arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
                        100 * (arrLow[nBar] - dOpenPrice) * dLotSize;

                    arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] =
                        arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1] + 
                        100 * (arrLow[nBar] - dOpenPrice) * dLotSize;

                    arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] =
                        arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1];

                    arrBars[ARRAY_SIZE(arrBars)] = nBar;
                }

                dOpenPrice = arrLow[nBar];
                dStop = dOpenPrice + dStopLoss;
                dTp = dOpenPrice - dTakeProfit;
                nType = 1;
                
                nTradeNumber = nTradeNumber + 1;
                nTradeNumberSell = nTradeNumberSell + 1;
            }
        }
    }

    // If at the end we have open positions, close them

    if(nType == 1)
    {
        arrBalance[ARRAY_SIZE(arrBalance)] = 
        arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
            100 * (dOpenPrice - arrHigh[nBar - 1] - dSpread) 
            * dLotSize;

        arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = 
            arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1] + 
            100 * (dOpenPrice - arrHigh[nBar - 1] - dSpread) 
            * dLotSize;

        arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = 
            arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1];

        arrBars[ARRAY_SIZE(arrBars)] = nBar - 1;
    }
    else
    {
        if(nType == -1)
        {
            arrBalance[ARRAY_SIZE(arrBalance)] = 
                arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
                100 * (arrLow[nBar - 1] - dOpenPrice) * dLotSize;
    
            arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = 
                arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1] + 
                100 * (arrLow[nBar - 1] - dOpenPrice) * dLotSize;
    
            arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = 
                arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1];
    
            arrBars[ARRAY_SIZE(arrBars)] = nBar - 1;

        }
    }

    if(arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1] > 1000 &&
        arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1] > 1000)
    {
        PRINT("%.0f", nNetNum, ". Trades: %.0f", nTradeNumber, 
            " (B: %.0f", nTradeNumberBuy, 
            ", S: %.0f", nTradeNumberSell, 
            "), Balance: %f", arrBalance[ARRAY_SIZE(arrBalance) - 1], 
            " (%f", arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1], 
            ", %f)\r\n", arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1], 
            "\tI: %.0f", nInterval, ", Ma: %.0f", nMa, ", Out: %.0f", 
            nOutLag, 
            ", Stop: %.4f", dStopLoss, ", Tp: %.4f\r\n", dTakeProfit, 
            "\tBuy: %.3f", dBuyLevel, ", Sell: %.3f", dSellLevel, 
            ", Neurons: %.0f", nNeurons, ", Lags: %s\r\n", strLagBuf);
    }
}

// ---------------

array CreateClv(double nInterval)
{    
    PRINT("%s\r\n", "Creating CLV indicator");

    array arrClv = CREATE_ARRAY(0);
    array arrPeriodLow = CREATE_ARRAY(0);
    array arrPeriodHigh = CREATE_ARRAY(0);

    double nArraySize = ARRAY_SIZE(arrClose);

    array arrPeriodLow = MVMIN(arrLow, nInterval);
    array arrPeriodHigh = MVMAX(arrHigh, nInterval);

    for(double i = 0; i < nInterval; i = i + 1) 
    {
        arrClv[i] = 0;
    }
    
    double dClose;
    double dLow;
    double dHigh;
    for(i = nInterval; i < nArraySize; i = i + 1)
    {
        dClose = arrClose[i];
        dLow = arrPeriodLow[i];
        dHigh = arrPeriodHigh[i];

        // / 2 + 1 to confine to 0...1 instead of -1...1
        arrClv[i] = (((dClose - dLow) - (dHigh - dClose))
            / (dHigh - dLow)) / 2 + 0.5; 
    }

    return arrClv;
}

// ----------------

void SaveWin()
{
    if(arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1] > 1000 
        && arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1] > 1000)
    {
        double nWinIdx = ARRAY_SIZE(arrWinInterval);

        arrWinInterval[nWinIdx] = nInterval;
        arrWinMa[nWinIdx] = nMa;
        arrWinLag[nWinIdx] = nOutLag;         
        arrWinStopLoss[nWinIdx] = dStopLoss;
        arrWinTakeProfit[nWinIdx] = dTakeProfit;
        arrWinBuyLevel[nWinIdx] = dBuyLevel;
        arrWinSellLevel[nWinIdx] = dSellLevel;
        arrWinNeurons[nWinIdx] = nNeurons;
        arrWinNnLags[nWinIdx] = arrStrLags[nLagIdx];
        arrWinProfit[nWinIdx] = arrBalance[ARRAY_SIZE(arrBalance) - 1];
        arrWinProfitBuy[nWinIdx] = 
            arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1];
        arrWinProfitSell[nWinIdx] = 
            arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1];

        string strWinNnFileName = strNnPath + strForexName + "_" 
            + NUM2STR(nWinIdx, "%.0f") + ".nn";    
        F_COPY(strNnFileName, strWinNnFileName);
    }
}

// -----------------

void CreateLagFile(string strLags, double nRemoveFirst)
{
    double hFile = F_OPEN(strLagFileName, "wb");

    F_PRINT(hFile, "%s", "No,Clv");

    ARRAY_REMOVE(arrLags, -1);
    string strToken = GET_TOKEN(strLags, ",");
    nNumOfLags = STR2NUM(strToken);
    
    for(double i = 0; i < nNumOfLags; i = i + 1)
    {
        strToken = GET_TOKEN(strLags, ",");
        arrLags[i] = STR2NUM(strToken) + nOutLag;
    }

    double nFullLag;
    for(i = 0; i < ARRAY_SIZE(arrLags); i = i + 1)
    {
        nFullLag = arrLags[i];
        F_PRINT(hFile, ",ClvMa%.0f", nMa, "-%.0f", nFullLag); 
    }

    F_PRINT(hFile, "%s\r\n", "");

    double nNum;
    for(i = nRemoveFirst; i < ARRAY_SIZE(arrClose); i = i + 1)
    {
        nNum = i - nRemoveFirst + 1;
        F_PRINT(hFile, "%.0f", nNum, ",%f", arrClvSmooth[i]);     

        for(double j = 0; j < ARRAY_SIZE(arrLags); j = j + 1)
        {
            F_PRINT(hFile, ",%f", arrClvSmooth[i - arrLags[j]]);     
        }

        F_PRINT(hFile, "%s\r\n", "");
    }

    F_CLOSE(hFile);
}

// --------------

void NewNn(array arrLags, double dStopError, 
    double nStopEpoch, double nNeuronsLayer2)
{
    double nSkipBefore = 0;
    double nSkipAfter = 0;
    string strStartLine = "";
    string strEndLine = "";
    double bReverseArrays = 0;

    // Inputs
    array arrInputColumns = CREATE_ARRAY(0);
    array_s arrInputColumnNames = CREATE_ARRAY_S(0);
    
    // 0  - Number, 1 - Clv, our input begins at column No 2

    for(double nInputCol = 0; nInputCol < ARRAY_SIZE(arrLags); 
        nInputCol = nInputCol + 1) 
    {
        arrInputColumns[nInputCol] = nInputCol + 2;
        arrInputColumnNames[nInputCol] = 
            "Clv-" + NUM2STR(arrLags[nInputCol], "%.0f"); 
    }

    array arrOutputColumns = CREATE_ARRAY(0);
    arrOutputColumns[0] = 1;    // Clv

    array_s arrOutputColumnNames = CREATE_ARRAY_S(0);
    arrOutputColumnNames[0] = "Clv";

    nExtractRecords = 0.7 * ARRAY_SIZE(arrClose);

    // To do: Modify if more than one indicator is used
    double nNeuronsLayer1 = ARRAY_SIZE(arrLags);
//  double nNeuronsLayer2 = 7;
    double nNeuronsLayer3 = 1;
    double nNeuronsLayer4 = 0;
    double nLayers = 3; 
    double nActivation = 0;
    double nAdjustRange = 1.0;

    array arrOutTabInputColumns = CREATE_ARRAY(0);
    arrOutTabInputColumns[0] = 0;    // Number

    array_s arrOutTabInputColumnNames = CREATE_ARRAY_S(0);
    arrOutTabInputColumnNames[0] = "No";

    array arrOutTabOutputColumns = CREATE_ARRAY(0);
    // Desired output and NN output will be added to the 
    // same list, right after inputs
    arrOutTabOutputColumns[0] = ARRAY_SIZE(arrLags) + 2;    
    arrOutTabOutputColumns[1] = ARRAY_SIZE(arrLags) + 3;

    array_s arrOutTabOutputColumnNames = CREATE_ARRAY_S(0);
    arrOutTabOutputColumnNames[0] = "Clv"; 
    arrOutTabOutputColumnNames[1] = "NN: Clv" ;

    CREATE_NN(strNnFileName, bIsPathRelative, strLagFileName, 
        bIsPathRelative, nSkipBefore, nSkipAfter, strStartLine, 
        strEndLine, bReverseArrays, arrInputColumns, 
        arrInputColumnNames, arrOutputColumns, arrOutputColumnNames, 
        nExtractRecords, dStopError, nStopEpoch, 
        nNeuronsLayer1, nNeuronsLayer2, nNeuronsLayer3, nNeuronsLayer4, 
        nLayers, nActivation, nAdjustRange, arrOutTabInputColumns, 
        arrOutTabInputColumnNames,
        arrOutTabOutputColumns, arrOutTabOutputColumnNames);
}

// ----------------

void TeachNn()
{
    PRINT("%s\r\n", "Opening NN dialog, teaching the NN");

    double bStartLearning = 1; 
    double bResumeScript = 1;
    double bReset = 1;

    OPEN_NN_FILE(strNnFileName, bIsPathRelative, 
        bStartLearning, bResumeScript, bReset);
}

// -------------

void Chart(strForexName)
{
    PRINT("%s\r\n", "Processing winning configurations...");

    string strXML = "<forex>\r\n";
    string strWinNnFileName;
        
    for(double nWin = 0; nWin < ARRAY_SIZE(arrWinInterval); 
        nWin = nWin + 1) 
    {
        PRINT("%.0f...", nWin);
        
        if(arrWinProfit[nWin] > 1000)
        {
            PRINT(" %s\r\n", "accepted");
            nInterval = arrWinInterval[nWin];
            nMa = arrWinMa[nWin];
            nOutLag = arrWinLag[nWin];
            dStopLoss = arrWinStopLoss[nWin];
            dTakeProfit = arrWinTakeProfit[nWin];
            dBuyLevel = arrWinBuyLevel[nWin];
            dSellLevel = arrWinSellLevel[nWin];
            strLagBuf = arrWinNnLags[nWin];

            arrClv = CreateClv(nInterval);
            arrClvSmooth = EXP_MVAVG(arrClv, nMa);

            CreateLagFile(strLagBuf, nRemoveFirst);

            strWinNnFileName = strNnPath + strForexName + 
                "_" + NUM2STR(nWin, "%.0f") + ".nn";

            hNn = OPEN_NN(strWinNnFileName, bIsPathRelative);
            APPLY_NN(hNn, nExtractRecords, 1.0, 1, arrClvSmooth, 
                arrLags, 1, arrNn);
            CLOSE_NN(hNn);

            nNetNum = nWin - 1;
            Test();
            
            string strImageFileName = strImagePath + 
                strForexName + "_" + 
                NUM2STR(nWin, "%.0f") + ".png";    
            
            strXML = strXML + 
                "\t<symbol>\r\n\t\t<symbol>\r\n";

            strXML = strXML + "Trades:" + NUM2STR(nTradeNumber, "%.0f")
                + "(Buy:" + NUM2STR(nTradeNumberBuy, "%.0f")
                + ",Sell:" + NUM2STR(nTradeNumberSell, "%.0f")
                + "),I:" + NUM2STR(nInterval, "%.0f")
                + ",Ma:" + NUM2STR(nMa, "%.0f")
                + ",Lag:" + NUM2STR(nOutLag, "%.0f")
                + ",Stop:" + NUM2STR(dStopLoss, "%.4f")
                + ",Tp:" + NUM2STR(dTakeProfit, "%.4f")
                + ",Buy:" + NUM2STR(dBuyLevel, "%.3f")
                + ",Sell:" + NUM2STR(dSellLevel, "%.3f")
                + ",Neurons:" + NUM2STR(arrWinNeurons[nWin], "%.0f")
                + ",Nn Lags:" + strLags
                + ",Balance:" + NUM2STR(arrWinProfit[nWin], "%f")
                + ",Balance long:" + 
                    NUM2STR(arrWinProfitBuy[nWin], "%f")
                + ",Balance short:" + 
                    NUM2STR(arrWinProfitSell[nWin], "%f\r\n");

            strXML = strXML + "\t\t</symbol>\r\n";

            strXML = strXML + "\t\t" + SAVE_CHART(400, 300, 0, 
                strImageFileName, 
                arrBars, arrBalance, arrBalanceBuy, arrBalanceSell);
            strXML = strXML + "\t</symbol>\r\n";
        }
        else
        {
            PRINT(" %s\r\n", "rejected");
        }
    }

    strXML = strXML + "</forex>\r\n";
    SAVE_XML(strImagePath, "chart_forex_nn", 
        "chart_forex_nn", "root", strXML);
    SHOW_XML(strImagePath + "chart_forex_nn.xml");
}

The program will take a very long time to work. The result will be a long list of charts, from which we can choose the one we "trust" in terms of drawdowns, profits and other criteria. Here is an example of a chart:

As you can see, the system is still loosing money. In the following chapters we will focus on making it profitable.


NOC indicator as an input

Why our trading system fails to produce profit?

To answer this question, lets take a closer look at the input we use. The CLV indicator is one of the closest things to the price, except it is normalized to the 0 - 1 range. By normalizing the price, we prevent the Neural Net from doing its own normalization.

The difference is, that NN would normalize the data to the range from smallest value of a Learning Set, to the largest value, while we normalize to the max - min of a fixed length "sliding" window (nClvInterval).

An advantage of our approach is that the price does not go too far during nClvInterval of bars, and therefore, the chart has "more resolution".

There is, however, one disadvantage, too. Lets say, the price is moving up, from 90 to 100 (just an example). Then the range is 10. If we have buy level of our trading system at 0.1, it will correspond to a price move equal 1 (10 * 0.1), same with sell level equal 0.9 ((1 - 0.9) * 10).

Now, lets say our market is moving sideways, and the price moves between 90 and 91 (range is equal 1, 10 times smaller than in a previous example). Then for the same buy level equal 0.1 and sell level equal 0.9, we will get trading signals, when the price moves 0.1, not 1!

In other words, the smaller the range is, the more sensitive our trading system becomes. Which, obviously, can be a problem.

Note, that it is a good illustration to the following statement: Neural Networks can do a lot, but the actual thinking MUST be done by a human.

Often, people provide all indicators they have to the Neural Network, without thinking, and expect it to come up with a good prediction. Our little (well, not that little :) example shows why it usually does not work.

In this chapter a solution to this problem will be provided. Just keep in mind that it is a partial solution because a) as have already been mentioned, this text is not about creating a perfect trading system, but rather about showing the possible problems on that path, and some ways of avoiding them and b) it certainly can be improved, but these improvements are beyond the scope of the current book.

The NOC indicator

I have invented it for the purpose of this article. However, as it is very simple, I cannot be sure that someone haven't invented it before me. If so, I apologise, and will add a proper reference, when get one.

"NOC" means "Normalize On Condition". The idea is very simple: when the CLV range becomes too small, so that even smallest price moves result in the wide oscillation of the indicator, we should stop using this range. Instead, we use the fixed, "minimum" value, and perform the normalization with it.

Lets take a look at the code:

forex_nn_04.tsc, fragment

array CreateNoc(double nInterval, double dMinRange)
{    
    PRINT("%s\r\n", "Creating NOC indicator");

    array arrNormOnCondition = CREATE_ARRAY(0);
    array arrPeriodLow = CREATE_ARRAY(0);
    array arrPeriodHigh = CREATE_ARRAY(0);

    double nArraySize = ARRAY_SIZE(arrClose);

    array arrPeriodLow = MVMIN(arrLow, nInterval);
    array arrPeriodHigh = MVMAX(arrHigh, nInterval);

    for(double i = 0; i < nInterval; i = i + 1) 
    {
        arrNormOnCondition[i] = 0;
    }
    
    double dClose;
    double dLow;
    double dHigh;
    for(i = nInterval; i < nArraySize; i = i + 1)
    {
        dClose = arrClose[i];
        dLow = arrPeriodLow[i];
        dHigh = arrPeriodHigh[i];

        if(dHigh - dLow > dMinRange)
        {
            // / 2 + 1 to confine to 0...1 instead of -1...1
            arrNormOnCondition[i] = (((dClose - dLow) - 
                (dHigh - dClose)) / (dHigh - dLow)) / 2 + 0.5; 
        }
        else
        {
            arrNormOnCondition[i] = (((dClose - dLow) - 
                (dHigh - dClose)) / dMinRange) / 2 + 0.5; 
        }
    }

    return arrNormOnCondition;
}

As you can see, it is similar to the CLV code, with an additional condition, that works when the range becomes too narrow.

As the result, we still have the chart, that is more or less similar to the price, it still is normalized, and it does not have the "whipsaw" effect at the flat parts of the charts.

This indicator can, of course, be improved, but we are not going to do it in this text. Lets mention one thing: instead of using the treshold, at which the behaviour of the indicator changes sharply, we can use more sophisticated approach, to make indicator less sensitive at narrow parts of the chart, but smoothly. There are some other possible improvements.

Also, the overall logic can still be criticized. The 0-1 range will now not produce trading signals, which is good, however the 90-100 and 100-200 will be normalized to the same 0-1 range, therefore, the sensitivity of our indicator will be less when the price moves fast. It is not necessarily bad, if we use this fact in our trading system, but it is something to keep in mind, especially if the results are poor and we need to figure out why.

Here is a simple script, that creates charts for both CLV and NOC indicators.

forex_nn_04.tsc

void main()
{
    OUT_CLEANUP();

    string strImagePath = 
        "c:\\S_Projects\\CortexPro\\data\\stocks_nn\\images\\";

    PRINT("%s\r\n", "Deleting image files...");

    array_s arrDirList = 
        GET_DIR(strImagePath, 0, "*.png");
    for(double n = 0; n < ARRAY_SIZE(arrDirList); n = n + 1)
    {
        F_UNLINK(arrDirList[n]);
    }

    // ------------

    string strForexName = "EURUSD_H1";
    string strDataFileName = 
        "c:\\S_Projects\\CortexPro\\data\\samples\\forex\\"
        + strForexName + ".TXT";
    
    double bIsPathRelative = 0;

    array     arrDate = CREATE_ARRAY(0);
    array arrTime = CREATE_ARRAY(0);
    array arrOpen = CREATE_ARRAY(0);
    array arrHigh = CREATE_ARRAY(0);
    array arrLow = CREATE_ARRAY(0);
    array arrClose = CREATE_ARRAY(0);

    TABLE_LOADER(strDataFileName, bIsPathRelative, 0, "", 0, "", 0, 
        arrDate, 1, arrTime, 2, arrOpen, 3, arrHigh, 4, 
        arrLow, 5, arrClose);

    array arrClv;
    array arrNormOnCondition;

    double nInterval = 64;
    double dMinRange = 0.03;

    arrClv = CreateClv(nInterval);
    arrNormOnCondition = CreateNoc(nInterval, dMinRange);

    Chart(strForexName);

    PRINT("%s\r\n", "Done");
}

// ---------------

array CreateClv(double nInterval)
{    
    PRINT("%s\r\n", "Creating CLV indicator");

    array arrClv = CREATE_ARRAY(0);
    array arrPeriodLow = CREATE_ARRAY(0);
    array arrPeriodHigh = CREATE_ARRAY(0);

    double nArraySize = ARRAY_SIZE(arrClose);

    array arrPeriodLow = MVMIN(arrLow, nInterval);
    array arrPeriodHigh = MVMAX(arrHigh, nInterval);

    for(double i = 0; i < nInterval; i = i + 1) 
    {
        arrClv[i] = 0;
    }
    
    double dClose;
    double dLow;
    double dHigh;
    for(i = nInterval; i < nArraySize; i = i + 1)
    {
        dClose = arrClose[i];
        dLow = arrPeriodLow[i];
        dHigh = arrPeriodHigh[i];

        // / 2 + 1 to confine to 0...1 instead of -1...1
        arrClv[i] = (((dClose - dLow) - (dHigh - dClose))
            / (dHigh - dLow)) / 2 + 0.5; 
    }

    return arrClv;
}

// ---------------

array CreateNoc(double nInterval, double dMinRange)
{    
    PRINT("%s\r\n", "Creating NOC indicator");

    array arrNormOnCondition = CREATE_ARRAY(0);
    array arrPeriodLow = CREATE_ARRAY(0);
    array arrPeriodHigh = CREATE_ARRAY(0);

    double nArraySize = ARRAY_SIZE(arrClose);

    array arrPeriodLow = MVMIN(arrLow, nInterval);
    array arrPeriodHigh = MVMAX(arrHigh, nInterval);

    for(double i = 0; i < nInterval; i = i + 1) 
    {
        arrNormOnCondition[i] = 0;
    }
    
    double dClose;
    double dLow;
    double dHigh;
    for(i = nInterval; i < nArraySize; i = i + 1)
    {
        dClose = arrClose[i];
        dLow = arrPeriodLow[i];
        dHigh = arrPeriodHigh[i];

        if(dHigh - dLow > dMinRange)
        {
            // / 2 + 1 to confine to 0...1 instead of -1...1
            arrNormOnCondition[i] = 
                (((dClose - dLow) - (dHigh - dClose))
                    / (dHigh - dLow)) / 2 + 0.5; 
        }
        else
        {
            arrNormOnCondition[i] = 
                (((dClose - dLow) - (dHigh - dClose))
                    / dMinRange) / 2 + 0.5; 
        }
    }

    return arrNormOnCondition;
}

// -------------

void Chart(string strForexName)
{
    string strXML = "<forex>\r\n";

    strXML = strXML + 
        "\t<symbol>\r\n\t\t<symbol>\r\n\t\t\tClose\r\n";
    strXML = strXML + "\t\t</symbol>\r\n";
    strXML = strXML + "\t\t" + SAVE_CHART(400, 300, 1, 
        strImagePath + strForexName + ".png", arrDate, arrClose);
    strXML = strXML + "\t</symbol>\r\n";

    strXML = strXML + 
        "\t<symbol>\r\n\t\t<symbol>\r\n\t\t\tClv, ";
    strXML = strXML + "Interval = " + NUM2STR(nInterval, "%.0f\r\n");
    strXML = strXML + "\t\t</symbol>\r\n";
    strXML = strXML + "\t\t" + SAVE_CHART(400, 150, 1, 
        strImagePath + strForexName + "_clv.png", arrDate, arrClv);
    strXML = strXML + "\t</symbol>\r\n";

    strXML = strXML + 
        "\t<symbol>\r\n\t\t<symbol>\r\n\t\t\tNoc, ";
    strXML = strXML + "Interval = " + NUM2STR(nInterval, "%.0f\r\n");
    strXML = strXML + "\t\t</symbol>\r\n";
    strXML = strXML + "\t\t" + SAVE_CHART(400, 150, 1, 
        strImagePath + 
        strForexName + "_noc.png", arrDate, arrNormOnCondition);
    strXML = strXML + "\t</symbol>\r\n";

    strXML = strXML + "</forex>\r\n";
    SAVE_XML(strImagePath, "chart_forex_nn", "chart_forex_nn", 
        "root", strXML);
    SHOW_XML(strImagePath + "chart_forex_nn.xml");
}

CLV
NOC


Using NN with NOC indicator

Using NOC with Neural Networks is similar to using CLV. The difference between the script we already saw above and this one is in the fact, that here we only use BUY signals. The reason is simple: it seems, that to get good trading results, we need to use different parameters of the NOC indicator for buy and sell operations. So lets limit out study to the BUY only, as is is "for educational purposes only". The code, responsible for SALE (short) operations is still there, it is just commented.

Also note, that some code is commented, just to make the code to run faster. For example, I have found, that the following set of lags ("10,0,1,2,3,4,6,8,12,14,16") does not produce good results. So I have commented it. You can remove the comments, of couse, but it will slow the program down.

forex_nn_04b.tsc

void main()
{
    OUT_CLEANUP();

    string strImagePath = 
        "c:\\S_Projects\\CortexPro\\data\\stocks_nn\\forex_nn_04b\\";

    string strForexName = "EURUSD_H1";

    string strNnPath = 
        "c:\\S_Projects\\CortexPro\\data\\stocks_nn\\forex_nn_04b\\";
    string strNnFileName = strNnPath + strForexName + "_04b.nn";

    // ------------

    string strDataFileName = 
        "c:\\S_Projects\\CortexPro\\data\\samples\\forex\\" 
        + strForexName + ".TXT";
    string strLagFileName = 
        "c:\\S_Projects\\CortexPro\\data\\stocks_nn\\forex_nn_04b\\"
            + strForexName + "_04b.lgg";
    
    double bIsPathRelative = 0;

    array arrDate = CREATE_ARRAY(0);
    array arrTime = CREATE_ARRAY(0);
    array arrOpen = CREATE_ARRAY(0);
    array arrHigh = CREATE_ARRAY(0);
    array arrLow = CREATE_ARRAY(0);
    array arrClose = CREATE_ARRAY(0);

    TABLE_LOADER(strDataFileName, bIsPathRelative, 0, "", 0, "", 0, 
        arrDate, 1, arrTime, 2, arrOpen, 3, arrHigh, 
        4, arrLow, 5, arrClose);

    double nArraySize = ARRAY_SIZE(arrClose);

    // -------
    
    // Interval, Range, Ma, OutLag 
    array_s arrParameters = CREATE_ARRAY_S(0);
    arrParameters[0] = "12,0,3,2";
    arrParameters[1] = "12,0.004,5,2";
    arrParameters[2] = "12,0.008,5,2";
    arrParameters[3] = "12,0.012,5,2";
    arrParameters[4] = "24,0,3,2";
    arrParameters[5] = "24,0.004,3,2";
    arrParameters[6] = "24,0.008,3,2";
    arrParameters[7] = "24,0.012,3,2";

    array arrNeurons = CREATE_ARRAY(0);
    arrNeurons[0] = 5;
//  arrNeurons[1] = 7;
//  arrNeurons[2] = 10;
//  arrNeurons[3] = 16;

    array arrLags = CREATE_ARRAY(0);
    array_s arrStrLags = CREATE_ARRAY_S(0);
    
    // ATTN: These lags are based on nWinLag. 
    // Example: nWinLag = 1, then each number should be increased by 1
    arrStrLags[0] = "17,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16";
//  arrStrLags[1] = "10,0,1,2,3,4,6,8,12,14,16";
//  arrStrLags[2] = "12,0,1,2,3,4,6,8,12,14,16,18,20";
//  arrStrLags[3] = "9,0,1,2,3,4,8,12,16,20";
//  arrStrLags[4] = "13,0,1,2,3,4,6,8,10,12,16,20,24,32";

    array arrStopLoss = CREATE_ARRAY(0);
    arrStopLoss[0] = 0.002;
    arrStopLoss[1] = 0.005;
    arrStopLoss[2] = 0.0075;
    arrStopLoss[3] = 0.01;
    arrStopLoss[4] = 0.015;
    arrStopLoss[5] = 0.02;

    array arrNoc;
    array arrNocSmooth;

    double nRemoveFirst = 200;

    double dStopError = 0;
    double nStopEpoch = 25000; 

    array arrNn = CREATE_ARRAY(0);

    string strParam;
    string strToken;
    double nMa;
    double hNn;
    double nExtractRecords;

    // ------

    double nNetNum = 0;
    double nCounter = 0;
    double dExpectedCycles = ARRAY_SIZE(arrParameters)
        * ARRAY_SIZE(arrStrLags) * ARRAY_SIZE(arrNeurons);

    string strResult = "";
    
    double dErrorMin = -1;

    double bSuccess;
    double nWinners = 0;
    double nTradeNumber;

    array arrBalance = CREATE_ARRAY(0);
    array arrBalanceBuy = CREATE_ARRAY(0);
    array arrBalanceSell = CREATE_ARRAY(0);

    string strXML = "<forex>\r\n";

    for(double nParIdx = 0; nParIdx < ARRAY_SIZE(arrParameters); 
        nParIdx = nParIdx + 1)
    {
        strParam = arrParameters[nParIdx];
        strToken = GET_TOKEN(strParam, ",");
        double nInterval = STR2NUM(strToken);

        strToken = GET_TOKEN(strParam, ",");

        double dRange = STR2NUM(strToken);

        arrNoc = CreateNoc(nInterval, dRange);

        strToken = GET_TOKEN(strParam, ",");
        nMa = STR2NUM(strToken);

        arrNocSmooth = EXP_MVAVG(arrNoc, nMa);

        strToken = GET_TOKEN(strParam, ",");
        double nOutLag = STR2NUM(strToken);
        
        for(double nLagIdx = 0; nLagIdx < ARRAY_SIZE(arrStrLags); 
            nLagIdx = nLagIdx + 1)
        {
            double nNumOfLags;
            string strLagBuf = arrStrLags[nLagIdx];
            CreateLagFile(strLagBuf, nRemoveFirst);

            for(double nNeuronsIdx = 0; nNeuronsIdx < 
                ARRAY_SIZE(arrNeurons); 
                nNeuronsIdx = nNeuronsIdx + 1)
            {
                nCounter = nCounter + 1;

                double nNeurons = arrNeurons[nNeuronsIdx];
                        
                PRINT("%.0f", nCounter, " of %.0f\r\n", dExpectedCycles);

                NewNn(arrLags, dStopError, nStopEpoch, nNeurons);
                TeachNn();

                hNn = OPEN_NN(strNnFileName, bIsPathRelative);

                APPLY_NN(hNn, nExtractRecords, 1.0, 1, 
                    arrNocSmooth, arrLags, 1, arrNn);

                for(double nStopIdx = 0; 
                    nStopIdx < ARRAY_SIZE(arrStopLoss); 
                    nStopIdx = nStopIdx + 1)
                {
                    double dStopLoss = arrStopLoss[nStopIdx];
                    double dTakeProfit = 0;
                                
                    for(double dStopIncrease = 0; 
                        dStopIncrease < 0.7; 
                        dStopIncrease = dStopIncrease + 0.1)
                    {
                        OUT_CLEANUP();

                        for(double nBuyIdx = 0; nBuyIdx < 30; 
                            nBuyIdx = nBuyIdx + 1)
                        {
                            double dBuyLevel = 0.1 + 0.01 * nBuyIdx;

                            for(double nSellIdx = 0; nSellIdx < 30; 
                                nSellIdx = nSellIdx + 1)
                            {
                                double dSellLevel = 0.9 - 
                                    0.01 * nSellIdx;

                                PRINT("%.0f", nCounter, " of %.0f", 
                                    dExpectedCycles);

                                Test();
                                
                                        
                                if(bSuccess == 0)
                                {
                                    PRINT(" (%.0f)\r\n", nWinners);
                                    continue nSellIdx;
                                }
    
                                nWinners = nWinners + 1;
                                PRINT(" (%.0f)\r\n", nWinners);    
                                                
                                Chart(strForexName);
                            }
                        }
                    }
                }
            }
        }
    }

    strXML = strXML + "</forex>\r\n";
    SAVE_XML(strImagePath, "chart_forex_nn", 
        "chart_forex_nn", "root", strXML);
//  SHOW_XML(strImagePath + "chart_forex_nn.xml");

    PRINT("%s\r\nDone\r\n", strResult);
}

// ---------------

void Test()
{
    bSuccess = 1;
    
    ARRAY_REMOVE(arrBalance, -1);
    arrBalance[0] = 1000;

    ARRAY_REMOVE(arrBalanceBuy, -1);
    arrBalanceBuy[0] = 0;

    ARRAY_REMOVE(arrBalanceSell, -1);
    arrBalanceSell[0] = 0;

    array arrBars = CREATE_ARRAY(0);
    arrBars[0] = 0;

    double dLotSize = 100;        // 0.1 lot
    double nType = 0;            // 1 buy, -1 sell, 0 - none

    double dSpread = 0.0005;
    double bStop;

    double dMaxDrawDown = 0;            // Max. Drawdown
    double dCurrentMax = 1000;        // Used to calculate drawdown

    nTradeNumber = 0;
    double nTradeNumberBuy = 0;
    double nTradeNumberSell = 0;
    double dOpenPrice;
    double dStop;
    double dTp = 0;

    for(double nBar = nRemoveFirst + 1; 
        nBar < ARRAY_SIZE(arrClose) - 1; nBar = nBar + 1)
    {
        if(nType != 0)
        {
            bStop = 0;
            double dClosedAt;

            // If BUY and stop loss or take profit reached
            if(nType == -1 && (arrLow[nBar] <= dStop || 
                (dTakeProfit > 0 && arrHigh[nBar] >= dTp - dSpread)))
            {
                arrBalance[ARRAY_SIZE(arrBalance)] = 
                    arrBalance[ARRAY_SIZE(arrBalance) - 1] 
                    + 100 * (arrLow[nBar + 1] - dOpenPrice) * dLotSize;

                arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = 
                    arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1] 
                    + 100 * (arrLow[nBar + 1] - dOpenPrice) * dLotSize;

                arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = 
                    arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1]; 

                bStop = 1;
                dClosedAt = arrLow[nBar + 1];
            }
            else
            {
                if(nType == 1 && (arrHigh[nBar] >= dStop - dSpread ||
                    (dTakeProfit > 0 && arrLow[nBar] <= dTp)))
                {
                    arrBalance[ARRAY_SIZE(arrBalance)] = 
                        arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
                        100 * (dOpenPrice - arrHigh[nBar + 1] - 
                            dSpread) * dLotSize;

                    arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = 
                        arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1];

                    arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = 
                        arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1] 
                            + 100 * (dOpenPrice - arrHigh[nBar + 1] - 
                            dSpread) * dLotSize;

                    bStop = 1;
                    dClosedAt = arrHigh[nBar + 1];
                }
            }

            if(bStop == 1)
            {
                nType = 0;
                arrBars[ARRAY_SIZE(arrBars)] = nBar;
            }
        }

        double dDrawDown = (dCurrentMax - 
            arrBalance[ARRAY_SIZE(arrBalance) - 1]) / 1000; 
        dMaxDrawDown = MAX(dMaxDrawDown, dDrawDown);
        dCurrentMax = 
            MAX(dCurrentMax, arrBalance[ARRAY_SIZE(arrBalance) - 1]);

        if(dMaxDrawDown > 1)
        {
            bSuccess = 0;
            break nBar;
        }

        if(nType != -1 && arrNn[nBar - 1] <= dBuyLevel && 
            arrNn[nBar] >= dBuyLevel)
        {
            if(nType == 1)
            {
                arrBalance[ARRAY_SIZE(arrBalance)] = 
                    arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
                        100 * (dOpenPrice - arrHigh[nBar + 1] - 
                            dSpread) * dLotSize;

                arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = 
                    arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1] + 
                        100 * (dOpenPrice - arrHigh[nBar + 1] - 
                            dSpread) * dLotSize;

                arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = 
                    arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1];

                arrBars[ARRAY_SIZE(arrBars)] = nBar;

                nType = 0;
            }

            dOpenPrice = arrHigh[nBar + 1];
            dStop = dOpenPrice - dStopLoss;
            dTp = dOpenPrice + dTakeProfit;
            nType = -1;
            
            nTradeNumber = nTradeNumber + 1;
            nTradeNumberBuy = nTradeNumberBuy + 1;
        }
        else
        {
            if(nType != 1 && arrNn[nBar - 1] >= dSellLevel && 
                arrNn[nBar] <= dSellLevel)
            {
                if(nType == -1)
                {
                    arrBalance[ARRAY_SIZE(arrBalance)] =
                        arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
                        100 * (arrLow[nBar + 1] - dOpenPrice) * dLotSize;

                    arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] =
                        arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1] + 
                        100 * (arrLow[nBar + 1] - dOpenPrice) * dLotSize;

                    arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] =
                        arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1];

                    arrBars[ARRAY_SIZE(arrBars)] = nBar;
                
                    nType = 0;
                }

/*              dOpenPrice = arrLow[nBar + 1];
                dStop = dOpenPrice + dStopLoss;
                dTp = dOpenPrice - dTakeProfit;
                nType = 1;
                
                nTradeNumber = nTradeNumber + 1;
                nTradeNumberSell = nTradeNumberSell + 1;
*/          }
        }

        if(dStopIncrease != 0)
        {
            if(nType == -1)
            {
                if(arrLow[nBar] - dStop >= 
                    dStopLoss * (1 + dStopIncrease))
                {
                    dStop = arrLow[nBar] - dStopLoss;
                }
            }
            else
            {
                if(nType == 1)
                {
                    if(dStop - arrHigh[nBar] >= 
                        dStopLoss * (1 + dStopIncrease))
                    {
                        dStop = arrHigh[nBar] + dStopLoss;
                    }
                }
            }
        }
    }

    // If at the end we have open positions, close them

    if(nType == 1)
    {
        arrBalance[ARRAY_SIZE(arrBalance)] = 
            arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
            100 * (dOpenPrice - arrHigh[nBar] - dSpread) * dLotSize;

        arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = 
            arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1] + 
            100 * (dOpenPrice - arrHigh[nBar] - dSpread) * dLotSize;

        arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = 
            arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1];

        arrBars[ARRAY_SIZE(arrBars)] = nBar - 1;
    }
    else
    {
        if(nType == -1)
        {
            arrBalance[ARRAY_SIZE(arrBalance)] = 
                arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
                100 * (arrLow[nBar] - dOpenPrice) * dLotSize;
    
            arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = 
                arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1] + 
                100 * (arrLow[nBar] - dOpenPrice) * dLotSize;
    
            arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = 
                arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1];
    
            arrBars[ARRAY_SIZE(arrBars)] = nBar - 1;
        }
    }

    if(ARRAY_SIZE(arrBalance) <= 1 || 
        (arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1] <= 500 
            && arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1] <= 200) 
            || nTradeNumber < 20)
    {
        bSuccess = 0;
    }
}

// ---------------

array CreateNoc(double nInterval, double dMinRange)
{    
    PRINT("%s\r\n", "Creating NOC indicator");

    array arrNormOnCondition = CREATE_ARRAY(0);
    array arrPeriodLow = CREATE_ARRAY(0);
    array arrPeriodHigh = CREATE_ARRAY(0);

    double nArraySize = ARRAY_SIZE(arrClose);

    array arrPeriodLow = MVMIN(arrLow, nInterval);
    array arrPeriodHigh = MVMAX(arrHigh, nInterval);

    for(double i = 0; i < nInterval; i = i + 1) 
    {
        arrNormOnCondition[i] = 0;
    }
    
    double dClose;
    double dLow;
    double dHigh;
    for(i = nInterval; i < nArraySize; i = i + 1)
    {
        dClose = arrClose[i];
        dLow = arrPeriodLow[i];
        dHigh = arrPeriodHigh[i];

        if(dHigh - dLow > dMinRange)
        {
            // / 2 + 1 to confine to 0...1 instead of -1...1
            arrNormOnCondition[i] = 
                (((dClose - dLow) - (dHigh - dClose))
                    / (dHigh - dLow)) / 2 + 0.5; 
        }
        else
        {
            arrNormOnCondition[i] = 
                (((dClose - dLow) - (dHigh - dClose))
                    / dMinRange) / 2 + 0.5; 
        }
    }

    return arrNormOnCondition;
}

// ----------------

void CreateLagFile(string strLags, double nRemoveFirst)
{
    double hFile = F_OPEN(strLagFileName, "wb");

    F_PRINT(hFile, "%s", "No,Clv");

    ARRAY_REMOVE(arrLags, -1);

    string strToken = GET_TOKEN(strLags, ",");
    double nNumOfLags = STR2NUM(strToken);

    for(double i = 0; i < nNumOfLags; i = i + 1)
    {
        strToken = GET_TOKEN(strLags, ",");
        arrLags[i] = STR2NUM(strToken) + nOutLag;
    }

    double nFullLag;
    for(i = 0; i < ARRAY_SIZE(arrLags); i = i + 1)
    {
        nFullLag = arrLags[i];
        F_PRINT(hFile, ",NocMa%.0f", nMa, "-%.0f", nFullLag); 
    }

    F_PRINT(hFile, "%s\r\n", "");

    double nNum;
    for(i = nRemoveFirst; i < ARRAY_SIZE(arrClose); i = i + 1)
    {
        nNum = i - nRemoveFirst + 1;
        F_PRINT(hFile, "%.0f", nNum, ",%f", arrNocSmooth[i]);     

        for(double j = 0; j < ARRAY_SIZE(arrLags); j = j + 1)
        {
            F_PRINT(hFile, ",%f", arrNocSmooth[i - arrLags[j]]);     
        }

        F_PRINT(hFile, "%s\r\n", "");
    }

    F_CLOSE(hFile);
}

// --------------

void NewNn(array arrLags, double dStopError, 
    double nStopEpoch, double nNeuronsLayer2)
{
    double nSkipBefore = 0;
    double nSkipAfter = 0;
    string strStartLine = "";
    string strEndLine = "";
    double bReverseArrays = 0;

    // Inputs
    array arrInputColumns = CREATE_ARRAY(0);
    array_s arrInputColumnNames = CREATE_ARRAY_S(0);
    
    // 0  - Number, 1 - Clv, our input begins at column No 2
    for(double nInputCol = 0; nInputCol < ARRAY_SIZE(arrLags); 
        nInputCol = nInputCol + 1) 
    {
        arrInputColumns[nInputCol] = nInputCol + 2;
        arrInputColumnNames[nInputCol] = 
            "Clv-" + NUM2STR(arrLags[nInputCol], "%.0f"); 
    }

    array arrOutputColumns = CREATE_ARRAY(0);
    arrOutputColumns[0] = 1;    // Clv

    array_s arrOutputColumnNames = CREATE_ARRAY_S(0);
    arrOutputColumnNames[0] = "Clv";

    nExtractRecords = 0.7 * ARRAY_SIZE(arrClose);

    double nNeuronsLayer1 = ARRAY_SIZE(arrLags);
//  double nNeuronsLayer2 = 7;
    double nNeuronsLayer3 = 1;
    double nNeuronsLayer4 = 0;
    double nLayers = 3; 
    double nActivation = 0;
    double nAdjustRange = 1.0;

    array arrOutTabInputColumns = CREATE_ARRAY(0);
    arrOutTabInputColumns[0] = 0;    // Number

    array_s arrOutTabInputColumnNames = CREATE_ARRAY_S(0);
    arrOutTabInputColumnNames[0] = "No";

    array arrOutTabOutputColumns = CREATE_ARRAY(0);
    // Desired output and NN output will be added to the 
    // same list, right after inputs
    arrOutTabOutputColumns[0] = ARRAY_SIZE(arrLags) + 2;    
    arrOutTabOutputColumns[1] = ARRAY_SIZE(arrLags) + 3;

    array_s arrOutTabOutputColumnNames = CREATE_ARRAY_S(0);
    arrOutTabOutputColumnNames[0] = "Clv"; 
    arrOutTabOutputColumnNames[1] = "NN: Clv" ;

    CREATE_NN(strNnFileName, bIsPathRelative, strLagFileName, 
        bIsPathRelative, nSkipBefore, nSkipAfter, strStartLine, 
        strEndLine, bReverseArrays, arrInputColumns, 
        arrInputColumnNames, arrOutputColumns, arrOutputColumnNames, 
        nExtractRecords, dStopError, nStopEpoch, 
        nNeuronsLayer1, nNeuronsLayer2, nNeuronsLayer3, 
        nNeuronsLayer4, nLayers, nActivation, nAdjustRange, 
        arrOutTabInputColumns, arrOutTabInputColumnNames,
        arrOutTabOutputColumns, arrOutTabOutputColumnNames);
}

// ----------------

void TeachNn()
{
    PRINT("%s\r\n", "Opening NN dialog, teaching the NN");

    double bStartLearning = 1; 
    double bResumeScript = 1;
    double bReset = 1;

    OPEN_NN_FILE(strNnFileName, bIsPathRelative, 
        bStartLearning, bResumeScript, bReset);
}


// -------------

void Chart(string strForexName)
{
    string strWinNnFileName;
        
    string strImageFileName = strImagePath + strForexName + 
        NUM2STR(nWinners, "_%.0f") + "_04b.png";    
            
    strXML = strXML + "\t<symbol>\r\n\t\t<symbol>\r\n";

    strXML = strXML + "Trades: " + NUM2STR(nTradeNumber, "%.0f")
        + "(Buy: " + NUM2STR(nTradeNumberBuy, "%.0f")
        + ", Sell :" + NUM2STR(nTradeNumberSell, "%.0f)\r\n")
        + "NocInterval: " + NUM2STR(nInterval, "%.0f")
        + ", Range: " + NUM2STR(dRange, "%.3f")
        + ", Ma: " + NUM2STR(nMa, "%.0f\r\n")

        + "Lag: " + NUM2STR(nOutLag, "%.0f")
        + ", Neurons: " + NUM2STR(nNeurons, "%.0f\r\n")
        + "Stop: " + NUM2STR(dStopLoss, "%.4f")
        + ", Tp: " + NUM2STR(dTakeProfit, "%.4f")
        + NUM2STR(dStopIncrease, ", Stop increase: %f")
        + ", Buy: " + NUM2STR(dBuyLevel, "%.3f")
        + ", Sell: " + NUM2STR(dSellLevel, "%.3f\r\n")

        + "Drawdown: " + NUM2STR(dMaxDrawDown, "%.3f\r\n")

        + "Profit: " 
        + NUM2STR(arrBalance[ARRAY_SIZE(arrBalance) - 1] - 1000, "%f")
        + " (long: " 
        + NUM2STR(arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1], "%f")
        + ", short: " 
        + NUM2STR(arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1], 
            "%f)\r\n");

    strXML = strXML + "\t\t</symbol>\r\n";

    strXML = strXML + "\t\t" + SAVE_CHART(400, 300, 0, strImageFileName, 
        arrBars, arrBalance, arrBalanceBuy, arrBalanceSell);
    strXML = strXML + "\t</symbol>\r\n";
}

Lets take a look at few profit charts, produced by this script.


Trades: 21 (Buy: 21, Sell :0)
NocInterval: 12, Range: 0.008, Ma: 5, Lag: 2,
Neurons: 5, Stop: 0.02, Stop increase: 0.5,
Buy: 0.14, Sell: 0.9, Drawdown: 0.257
Profit: 1172.00 (long: 1172.00, short: 0.00)


Trades: 38 (Buy: 38, Sell :0)
NocInterval: 12, Range: 0.012, Ma: 5, Lag: 2,
Neurons: 5, Stop: 0.02, Stop increase: 0.3,
Buy: 0.21, Sell: 0.9, Drawdown: 0.97
Profit: 1774.00 (long: 1774.00, short: 0.00)


Trades: 30 (Buy: 30, Sell :0),
NocInterval: 24, Range: 0, Ma: 3, Lag: 2,
Neurons: 5, Stop: 0.015, Stop increase: 0.3,
Buy: 0.11, Sell: 0.9, Drawdown: 0.68
Profit: 871.00 (long: 871.00, short: 0.00)


Trades: 28 (Buy: 28, Sell :0)
NocInterval: 24, Range: 0, Ma: 3, Lag: 2,
Neurons: 5, Stop: 0.02, Stop increase: 0,
Buy: 0.19, Sell: 0.9, Drawdown: 0.661,
Profit: 1608.00 (long: 1608.00, short: 0.00)


Trades: 45 (Buy: 45, Sell: 0)
NocInterval: 24, Range: 0, Ma: 3, Lag: 2,
Neurons: 5, Stop: 0.02, Stop increase: 0.3,
Buy: 0.18, Sell: 0.9, Drawdown: 0.712
Profit: 789.00 (long: 789.00, short: 0.00)


Trades: 27 (Buy: 27, Sell :0)
NocInterval: 24, Range: 0.012, Ma: 3, Lag: 2,
Neurons: 5, Stop: 0.02, Stop increase: 0.6,
Buy: 0.16, Sell: 0.9, Drawdown: 0.953
Profit: 1193.00 (long: 1193.00, short: 0.00)

The first thing that you will notice, is the fact, that this time, by more carefully choosing the parameters of our trading system, we were able to find profitable combinations even for CLV (range equals 0). Does it mean that all our discussion about advantages of NOC over CLV is wrong?

Probably, not. What SHOULD be concluded from the charts above, is that BOTH methods are rather clumsy, and shouldn't be used AS IS for trading with the real money.

The problem is in the drawdown of our trading systems, which is inacceptably large. Statistically, it is possible for more than one "unlucky strikes" to happen in a row, which means that we shouldn't use systems with the DD larger that, say, 10 - 30%. Also, the sahpe of the profit curve for any of our combinations of parameters is far from linear.

As we were not trying to create a perfect trading system, it is OK. We have now the basics, and we can try to figure out what else should be improved, but, as I have mentioned already, this is not the purpose of this book.

However, one thing should be mentioned. Lets take another look at one of the charts:


Trades: 38 (Buy: 38, Sell :0)
NocInterval: 12, Range: 0.012, Ma: 5, Lag: 2,
Neurons: 5, Stop: 0.02, Stop increase: 0.3,
Buy: 0.21, Sell: 0.9, Drawdown: 0.97
Profit: 1774.00 (long: 1774.00, short: 0.00)

We can see, that winning and loosing trades are going in series. This certainly can be used to improve out results in one of two ways.

First: we can use the profit curve to adjust the size of the lot. way we will use larger lots during winning periods, and smaller lots during times, when our system does not perform well.

Of course, this approach will not work, if we have series of loosing trades, with single winning trade between them, so it is necessary to do a careful study of the profit curve.

This approach is an example of money management strategy, and it can improve some trading systems dramatically.

Second. The fact that our system is sometimes working and sometimes does not, means that there is a factor (or few factors) unaccounted for. We need to look for these factors, and to adjust the system, for example, by adding an extra indicator, or by modifying the exixting one.

For example, if our system is profitable only when the price goes up, all we need to do is to add a condition: open trades only when the short moving average is above the long one. This approach can help a lot sometimes, however, it may require changing the code a bit. For example, the NN may give a "buy" signal BEFORE the two MA cross, so we need to use a variable, holding the first signal, and then to wait for the second to happen.

It is also possible, as I already mentioned, that the indicator is not good enough for a job. What if the system works fine while the range is between 100 and 200 points, and does not work when it is larger (it means, the system does not work well, when the price moves too fast). Then we need to change the indicator, to compensate for a speed of a price change, and MAYBE our results will improve.

Once again, this is beyond the scope of this text.


From testing to real trading

Getting weights of the NN

Now that we have a Neural Network, we may want to trade using it. As Cortex is just a simulator, it cannot be used for trading in a real time.

Of course, it is possible to use the "run by timer" feature, and to perform file input and output, so that Cortex gets new quotes from the file, and writes results to the file. The Trading Platform you use will read the results, use them to trade, and then, as new quotes become available, write them to file.

This is not a very good approach. It is clumsy, and it can be error prone.

Another approach is to use Cortex API. However a) not all trading platforms will work with it, so you might need to write an additional level of software to wrap API functions, and b) not all traders like low level C++ programming.

The third approach uses DDE and ActiveX, but as Cortex does not provide this kind of functionality, it cannot be used.

The solution we suggest is to use ONLY scripting language of the trading platform of your choice.

First of all, we need to create the NN and to teach it. We did it in the chapters above, the result is the file with .NN extention.

Then note, that while the teaching requires slow and complex code, using the NN AFTER the teaching is complete is quite easy. The code (C++ code) for that is much shorter, and it does not take a lot of time to run, as there is no need to do thousands passes through the data patterns.

Therefore, IF we had the original C++ code AND weights of the fully trained network, we could write an identical script, using either SLANG or the scripting language of your favorite trading platform (like TS, MetaQuotes, MetaTrader...). Then you will end up with the script ONLY, that does not use DLLs, or any other kind of program-to-program communications, but still doing our NN calculations.

In the following chapters we are going to:
a) create a SLANG script with the same functionality as our NN
b) create the MetaTrader script to perform the same task

Among advantages of the MetaTrader (metaquotes.net), are small size (it is not a "monster"), price (it is free) and overall clearness of the interface.

Please note: though I consider the MetaTrader one of the most convenient trading platforms, I am not, in any way, related to it. This is not a promotion, and if you prefer other platforms, use them. That's exactly why I a have created the SLANG script first, and only then "cloned" it using MetaTrader's MQL.

Creating the NN script

First of all, lets create the Cortex script, similar to forex_nn_04b.tsc, but without cycles. All we need here is to create a single NN, using "optimal" parameters, obtained in forex_nn_04b.tsc, to trade using it, and to produce the output chart.

The code below is the same as in forex_nn_04b.tsc, the only thing I did was commenting cycles and removing extras. It could be done in a more compact way, but I chose to keep the code as similar to the original forex_nn_04b.tsc as possible.

Additionally, the program obtains parameters of the NN, and prints it in the output window. We will need those parameters later.

The code, responsible for creating and teaching the NN is commented in the script below. When you run it for the first time, you need to uncomment it, of course. After that, you don't need to recreate the same NN over and over again, so if you want to run the script for the second time, you may want to set comments back again.

forex_nn_05a.tsc

/*
    The data, taken from forex_nn_04b. 
    This is an exact copy of forex_nn_04b, except we don't look for 
    optimal parameters, instead, we use ones from forex_nn_04b

    Trades: 38 (Buy: 38, Sell :0) 
    NocInterval: 12, Range: 0.012, Ma: 5, Lag: 2, 
    Neurons: 5, Stop: 0.02, Stop increase: 0.3, 
    Buy: 0.21, Sell: 0.9, Drawdown: 0.97 
    Profit: 1774.00 (long: 1774.00, short: 0.00) 
*/

void main()
{
    OUT_CLEANUP();

    string strImagePath = 
        "c:\\S_Projects\\CortexPro\\data\\stocks_nn\\forex_nn_05\\";

    string strForexName = "EURUSD_H1";

    string strNnPath = 
        "c:\\S_Projects\\CortexPro\\data\\stocks_nn\\forex_nn_05\\";
    string strNnFileName = strNnPath + strForexName + "_05.nn";

    // ------------

    string strDataFileName = 
        "c:\\S_Projects\\CortexPro\\data\\samples\\forex\\" 
            + strForexName + ".TXT";
    string strLagFileName = 
        "c:\\S_Projects\\CortexPro\\data\\stocks_nn\\forex_nn_05\\"
            + strForexName + "_05.lgg";
    
    double bIsPathRelative = 0;

    array arrDate = CREATE_ARRAY(0);
    array arrTime = CREATE_ARRAY(0);
    array arrOpen = CREATE_ARRAY(0);
    array arrHigh = CREATE_ARRAY(0);
    array arrLow = CREATE_ARRAY(0);
    array arrClose = CREATE_ARRAY(0);

    TABLE_LOADER(strDataFileName, bIsPathRelative, 0, "", 0, "", 0, 
        arrDate, 1, arrTime, 2, arrOpen, 3, arrHigh, 
        4, arrLow, 5, arrClose);

    double nArraySize = ARRAY_SIZE(arrClose);

    // -------
    
    // Interval, Range, Ma, OutLag 
    array_s arrParameters = CREATE_ARRAY_S(0);
    arrParameters[0] = "12,0.012,5,2";

    array arrNeurons = CREATE_ARRAY(0);
    arrNeurons[0] = 5;

    array arrLags = CREATE_ARRAY(0);
    array_s arrStrLags = CREATE_ARRAY_S(0);
    
    // ATTN: These lags are based on nWinLag. 
    // Example: nWinLag = 1, then each number should be increased by 1
    arrStrLags[0] = "17,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16";

    array arrStopLoss = CREATE_ARRAY(0);
    arrStopLoss[0] = 0.02;

    array arrNoc;
    array arrNocSmooth;

    double nRemoveFirst = 200;

    double dStopError = 0;
    double nStopEpoch = 25000; 

    array arrNn = CREATE_ARRAY(0);

    string strParam;
    string strToken;
    double nMa;
    double hNn;
    double nExtractRecords;

    // ------

    double nNetNum = 0;
    double nCounter = 0;
    double dExpectedCycles = ARRAY_SIZE(arrParameters)
        * ARRAY_SIZE(arrStrLags) * ARRAY_SIZE(arrNeurons);

    string strResult = "";
    
    double dErrorMin = -1;

    double bSuccess;
    double nWinners = 0;
    double nTradeNumber;

    array arrBalance = CREATE_ARRAY(0);
    array arrBalanceBuy = CREATE_ARRAY(0);
    array arrBalanceSell = CREATE_ARRAY(0);

    string strXML = "<forex>\r\n";

    // only one cycle
    for(double nParIdx = 0; nParIdx < ARRAY_SIZE(arrParameters); 
        nParIdx = nParIdx + 1)
    {
        strParam = arrParameters[nParIdx];
        strToken = GET_TOKEN(strParam, ",");
        double nInterval = STR2NUM(strToken);

        strToken = GET_TOKEN(strParam, ",");

        double dRange = STR2NUM(strToken);

        arrNoc = CreateNoc(nInterval, dRange);

        strToken = GET_TOKEN(strParam, ",");
        nMa = STR2NUM(strToken);

        arrNocSmooth = EXP_MVAVG(arrNoc, nMa);

        strToken = GET_TOKEN(strParam, ",");
        double nOutLag = STR2NUM(strToken);
        
        for(double nLagIdx = 0; nLagIdx < ARRAY_SIZE(arrStrLags); 
            nLagIdx = nLagIdx + 1)
        {
            double nNumOfLags;
            string strLagBuf = arrStrLags[nLagIdx];
            CreateLagFile(strLagBuf, nRemoveFirst);

            for(double nNeuronsIdx = 0; nNeuronsIdx < 
                ARRAY_SIZE(arrNeurons); 
                nNeuronsIdx = nNeuronsIdx + 1)
            {
                nCounter = nCounter + 1;

                double nNeurons = arrNeurons[nNeuronsIdx];
                        
                PRINT("%.0f", nCounter, " of %.0f\r\n", dExpectedCycles);

//                NewNn(arrLags, dStopError, nStopEpoch, nNeurons);
//                TeachNn();

                hNn = OPEN_NN(strNnFileName, bIsPathRelative);

                APPLY_NN(hNn, nExtractRecords, 1.0, 1, 
                    arrNocSmooth, arrLags, 1, arrNn);

                array arrDescription = GET_NN_DESCRIPTION(hNn);
                
                PRINT("\r\nActivation Type: %.0f\r\n", 
                    arrDescription[5]);

                double nLayers = arrDescription[0];

                array arrNumOfNeurons = CREATE_ARRAY(0);
                for(double nLayer = 0; nLayer < nLayers; 
                    nLayer = nLayer + 1)
                {
                    arrNumOfNeurons[nLayer] = arrDescription[1 + nLayer];
                }

                for(nLayer = 0; nLayer < nLayers; nLayer = nLayer + 1)
                {
                    PRINT("\r\n\t// Layer: %.0f\r\n", nLayer);

                    double nNumOfInputs = arrNumOfNeurons[nLayer] + 1;
                    if(nLayer != 0)
                    {
                        nNumOfInputs = arrNumOfNeurons[nLayer - 1] + 1;
                    }

                    array arrWeights = GET_NN_WEIGHTS(hNn, nLayer);

                    double nWeight = 0;
                    for(double nNeuron = 0; nNeuron < 
                        arrNumOfNeurons[nLayer]; 
                        nNeuron = nNeuron + 1)
                    {
                        for(double nNeuronWeight = 0; 
                            nNeuronWeight < nNumOfInputs; 
                            nNeuronWeight = nNeuronWeight + 1) 
                        {
                            if(nLayer == 0)
                            {
                                PRINT("\tarrWeights_0[%.0f] = ", nWeight,
                                    "%f;\r\n", arrWeights[nWeight]);
                            }
                            else 
                            {
                                if(nLayer == 1)
                                {
                                    PRINT("\tarrWeights_1[%.0f] = ", 
                                        nWeight,
                                        "%f;\r\n", arrWeights[nWeight]);
                                }
                                else
                                {
                                    PRINT("\tarrWeights_2[%.0f] = ", 
                                        nWeight,
                                        "%f;\r\n", arrWeights[nWeight]);
                                }
                            }
                            
                            nWeight = nWeight + 1;
                        }
                    }
                }

                for(double nStopIdx = 0; 
                    nStopIdx < ARRAY_SIZE(arrStopLoss); 
                    nStopIdx = nStopIdx + 1)
                {
                    double dStopLoss = arrStopLoss[nStopIdx];
                    double dTakeProfit = 0;
                                
                    double dStopIncrease = 0.3;

                    double dBuyLevel = 0.21;

                    double dSellLevel = 0.9;

                    Test();
                                
                    nWinners = nWinners + 1;
    
                    Chart(strForexName);
                }
            }
        }
    }

    strXML = strXML + "</forex>\r\n";
    SAVE_XML(strImagePath, "chart_forex_nn", 
        "chart_forex_nn", "root", strXML);
    SHOW_XML(strImagePath + "chart_forex_nn.xml");

    PRINT("%s\r\nDone\r\n", strResult);
}

// ---------------

void Test()
{
    bSuccess = 1;
    
    ARRAY_REMOVE(arrBalance, -1);
    arrBalance[0] = 1000;

    ARRAY_REMOVE(arrBalanceBuy, -1);
    arrBalanceBuy[0] = 0;

    ARRAY_REMOVE(arrBalanceSell, -1);
    arrBalanceSell[0] = 0;

    array arrBars = CREATE_ARRAY(0);
    arrBars[0] = 0;

    double dLotSize = 100;        // 0.1 lot
    double nType = 0;            // 1 buy, -1 sell, 0 - none

    double dSpread = 0.0005;
    double bStop;

    double dMaxDrawDown = 0;            // Max. Drawdown
    double dCurrentMax = 1000;        // Used to calculate drawdown

    nTradeNumber = 0;
    double nTradeNumberBuy = 0;
    double nTradeNumberSell = 0;
    double dOpenPrice;
    double dStop;
    double dTp = 0;

    for(double nBar = nRemoveFirst + 1; 
        nBar < ARRAY_SIZE(arrClose) - 1; nBar = nBar + 1)
    {
        if(nType != 0)
        {
            bStop = 0;
            double dClosedAt;

            // If BUY and stop loss or take profit reached
            if(nType == -1 && (arrLow[nBar] <= dStop || 
                (dTakeProfit > 0 && arrHigh[nBar] >= dTp - dSpread)))
            {
                arrBalance[ARRAY_SIZE(arrBalance)] = 
                    arrBalance[ARRAY_SIZE(arrBalance) - 1] 
                    + 100 * (arrLow[nBar + 1] - dOpenPrice) * dLotSize;

                arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = 
                    arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1] 
                    + 100 * (arrLow[nBar + 1] - dOpenPrice) * dLotSize;

                arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = 
                    arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1]; 

                bStop = 1;
                dClosedAt = arrLow[nBar + 1];
            }
            else
            {
                if(nType == 1 && (arrHigh[nBar] >= dStop - dSpread ||
                    (dTakeProfit > 0 && arrLow[nBar] <= dTp)))
                {
                    arrBalance[ARRAY_SIZE(arrBalance)] = 
                        arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
                        100 * (dOpenPrice - arrHigh[nBar + 1] - 
                            dSpread) * dLotSize;

                    arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = 
                        arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1];

                    arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = 
                        arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1] + 
                        100 * (dOpenPrice - arrHigh[nBar + 1] - 
                            dSpread) * dLotSize;

                    bStop = 1;
                    dClosedAt = arrHigh[nBar + 1];
                }
            }

            if(bStop == 1)
            {
                nType = 0;
                arrBars[ARRAY_SIZE(arrBars)] = nBar;
            }
        }

        double dDrawDown = (dCurrentMax - 
            arrBalance[ARRAY_SIZE(arrBalance) - 1]) / 1000; 
        dMaxDrawDown = MAX(dMaxDrawDown, dDrawDown);
        dCurrentMax = 
            MAX(dCurrentMax, arrBalance[ARRAY_SIZE(arrBalance) - 1]);

        if(dMaxDrawDown > 1)
        {
            bSuccess = 0;
            break nBar;
        }

        if(nType != -1 && arrNn[nBar - 1] <= dBuyLevel && 
            arrNn[nBar] >= dBuyLevel)
        {
            if(nType == 1)
            {
                arrBalance[ARRAY_SIZE(arrBalance)] = 
                    arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
                        100 * (dOpenPrice - arrHigh[nBar + 1] - 
                            dSpread) * dLotSize;

                arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = 
                    arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1] + 
                        100 * (dOpenPrice - arrHigh[nBar + 1] - 
                            dSpread) * dLotSize;

                arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = 
                    arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1];

                arrBars[ARRAY_SIZE(arrBars)] = nBar;

                nType = 0;
            }

            dOpenPrice = arrHigh[nBar + 1];
            dStop = dOpenPrice - dStopLoss;
            dTp = dOpenPrice + dTakeProfit;
            nType = -1;
            
            nTradeNumber = nTradeNumber + 1;
            nTradeNumberBuy = nTradeNumberBuy + 1;
        }
        else
        {
            if(nType != 1 && arrNn[nBar - 1] >= dSellLevel && 
                arrNn[nBar] <= dSellLevel)
            {
                if(nType == -1)
                {
                    arrBalance[ARRAY_SIZE(arrBalance)] =
                        arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
                        100 * (arrLow[nBar + 1] - dOpenPrice) * dLotSize;

                    arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] =
                        arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1] + 
                        100 * (arrLow[nBar + 1] - dOpenPrice) * dLotSize;

                    arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] =
                        arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1];

                    arrBars[ARRAY_SIZE(arrBars)] = nBar;
                
                    nType = 0;
                }

/*              dOpenPrice = arrLow[nBar + 1];
                dStop = dOpenPrice + dStopLoss;
                dTp = dOpenPrice - dTakeProfit;
                nType = 1;
                
                nTradeNumber = nTradeNumber + 1;
                nTradeNumberSell = nTradeNumberSell + 1;
*/          }
        }

        if(dStopIncrease != 0)
        {
            if(nType == -1)
            {
                if(arrLow[nBar] - dStop >= 
                    dStopLoss * (1 + dStopIncrease))
                {
                    dStop = arrLow[nBar] - dStopLoss;
                }
            }
            else
            {
                if(nType == 1)
                {
                    if(dStop - arrHigh[nBar] >= 
                        dStopLoss * (1 + dStopIncrease))
                    {
                        dStop = arrHigh[nBar] + dStopLoss;
                    }
                }
            }
        }
    }

    // If at the end we have open positions, close them

    if(nType == 1)
    {
        arrBalance[ARRAY_SIZE(arrBalance)] = 
            arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
            100 * (dOpenPrice - arrHigh[nBar] - dSpread) * dLotSize;

        arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = 
            arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1] + 
            100 * (dOpenPrice - arrHigh[nBar] - dSpread) * dLotSize;

        arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = 
            arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1];

        arrBars[ARRAY_SIZE(arrBars)] = nBar - 1;
    }
    else
    {
        if(nType == -1)
        {
            arrBalance[ARRAY_SIZE(arrBalance)] = 
                arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
                100 * (arrLow[nBar] - dOpenPrice) * dLotSize;
    
            arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = 
                arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1] + 
                100 * (arrLow[nBar] - dOpenPrice) * dLotSize;
    
            arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = 
                arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1];
    
            arrBars[ARRAY_SIZE(arrBars)] = nBar - 1;
        }
    }

    if(ARRAY_SIZE(arrBalance) <= 1 || 
        (arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1] <= 500 
            && arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1] <= 200) 
            || nTradeNumber < 20)
    {
        bSuccess = 0;
    }

}

// ---------------

array CreateNoc(double nInterval, double dMinRange)
{    
    PRINT("%s\r\n", "Creating NOC indicator");

    array arrNormOnCondition = CREATE_ARRAY(0);
    array arrPeriodLow = CREATE_ARRAY(0);
    array arrPeriodHigh = CREATE_ARRAY(0);

    double nArraySize = ARRAY_SIZE(arrClose);

    array arrPeriodLow = MVMIN(arrLow, nInterval);
    array arrPeriodHigh = MVMAX(arrHigh, nInterval);

    for(double i = 0; i < nInterval; i = i + 1) 
    {
        arrNormOnCondition[i] = 0;
    }
    
    double dClose;
    double dLow;
    double dHigh;
    for(i = nInterval; i < nArraySize; i = i + 1)
    {
        dClose = arrClose[i];
        dLow = arrPeriodLow[i];
        dHigh = arrPeriodHigh[i];

        if(dHigh - dLow > dMinRange)
        {
            // / 2 + 1 to confine to 0...1 instead of -1...1
            arrNormOnCondition[i] = 
                (((dClose - dLow) - (dHigh - dClose))
                    / (dHigh - dLow)) / 2 + 0.5; 
        }
        else
        {
            arrNormOnCondition[i] = 
                (((dClose - dLow) - (dHigh - dClose))
                    / dMinRange) / 2 + 0.5; 
        }
    }

    return arrNormOnCondition;
}

// ----------------

void CreateLagFile(string strLags, double nRemoveFirst)
{
    double hFile = F_OPEN(strLagFileName, "wb");

    F_PRINT(hFile, "%s", "No,Clv");

    ARRAY_REMOVE(arrLags, -1);

    string strToken = GET_TOKEN(strLags, ",");
    double nNumOfLags = STR2NUM(strToken);

    for(double i = 0; i < nNumOfLags; i = i + 1)
    {
        strToken = GET_TOKEN(strLags, ",");
        arrLags[i] = STR2NUM(strToken) + nOutLag;
    }

    double nFullLag;
    for(i = 0; i < ARRAY_SIZE(arrLags); i = i + 1)
    {
        nFullLag = arrLags[i];
        F_PRINT(hFile, ",NocMa%.0f", nMa, "-%.0f", nFullLag); 
    }

    F_PRINT(hFile, "%s\r\n", "");

    double nNum;
    for(i = nRemoveFirst; i < ARRAY_SIZE(arrClose); i = i + 1)
    {
        nNum = i - nRemoveFirst + 1;
        F_PRINT(hFile, "%.0f", nNum, ",%f", arrNocSmooth[i]);     

        for(double j = 0; j < ARRAY_SIZE(arrLags); j = j + 1)
        {
            F_PRINT(hFile, ",%f", arrNocSmooth[i - arrLags[j]]);     
        }

        F_PRINT(hFile, "%s\r\n", "");
    }

    F_CLOSE(hFile);
}

// --------------

void NewNn(array arrLags, double dStopError, 
    double nStopEpoch, double nNeuronsLayer2)
{
    double nSkipBefore = 0;
    double nSkipAfter = 0;
    string strStartLine = "";
    string strEndLine = "";
    double bReverseArrays = 0;

    // Inputs
    array arrInputColumns = CREATE_ARRAY(0);
    array_s arrInputColumnNames = CREATE_ARRAY_S(0);
    
    // 0  - Number, 1 - Clv, our input begins at column No 2
    for(double nInputCol = 0; nInputCol < ARRAY_SIZE(arrLags); 
        nInputCol = nInputCol + 1) 
    {
        arrInputColumns[nInputCol] = nInputCol + 2;
        arrInputColumnNames[nInputCol] = 
            "Clv-" + NUM2STR(arrLags[nInputCol], "%.0f"); 
    }

    array arrOutputColumns = CREATE_ARRAY(0);
    arrOutputColumns[0] = 1;    // Clv

    array_s arrOutputColumnNames = CREATE_ARRAY_S(0);
    arrOutputColumnNames[0] = "Clv";

    nExtractRecords = 0.7 * ARRAY_SIZE(arrClose);

    double nNeuronsLayer1 = ARRAY_SIZE(arrLags);
//  double nNeuronsLayer2 = 7;
    double nNeuronsLayer3 = 1;
    double nNeuronsLayer4 = 0;
    double nLayers = 3; 
    double nActivation = 0;
    double nAdjustRange = 1.0;

    array arrOutTabInputColumns = CREATE_ARRAY(0);
    arrOutTabInputColumns[0] = 0;    // Number

    array_s arrOutTabInputColumnNames = CREATE_ARRAY_S(0);
    arrOutTabInputColumnNames[0] = "No";

    array arrOutTabOutputColumns = CREATE_ARRAY(0);
    // Desired output and NN output will be added to the 
    // same list, right after inputs
    arrOutTabOutputColumns[0] = ARRAY_SIZE(arrLags) + 2;    
    arrOutTabOutputColumns[1] = ARRAY_SIZE(arrLags) + 3;

    array_s arrOutTabOutputColumnNames = CREATE_ARRAY_S(0);
    arrOutTabOutputColumnNames[0] = "Clv"; 
    arrOutTabOutputColumnNames[1] = "NN: Clv" ;

    CREATE_NN(strNnFileName, bIsPathRelative, strLagFileName, 
        bIsPathRelative, nSkipBefore, nSkipAfter, strStartLine, 
        strEndLine, bReverseArrays, arrInputColumns, 
        arrInputColumnNames, arrOutputColumns, arrOutputColumnNames, 
        nExtractRecords, dStopError, nStopEpoch, 
        nNeuronsLayer1, nNeuronsLayer2, nNeuronsLayer3, 
        nNeuronsLayer4, nLayers, nActivation, nAdjustRange, 
        arrOutTabInputColumns, arrOutTabInputColumnNames,
        arrOutTabOutputColumns, arrOutTabOutputColumnNames);
}

// ----------------

void TeachNn()
{
    PRINT("%s\r\n", "Opening NN dialog, teaching the NN");

    double bStartLearning = 1; 
    double bResumeScript = 1;
    double bReset = 1;

    OPEN_NN_FILE(strNnFileName, bIsPathRelative, 
        bStartLearning, bResumeScript, bReset);
}


// -------------

void Chart(string strForexName)
{
    string strWinNnFileName;
        
    string strImageFileName = strImagePath + strForexName + 
        NUM2STR(nWinners, "_%.0f") + "_05a.png";    
            
    strXML = strXML + "\t<symbol>\r\n\t\t<symbol>\r\n";

    strXML = strXML + "Trades: " + NUM2STR(nTradeNumber, "%.0f")
        + "(Buy: " + NUM2STR(nTradeNumberBuy, "%.0f")
        + ", Sell :" + NUM2STR(nTradeNumberSell, "%.0f)\r\n")
        + "NocInterval: " + NUM2STR(nInterval, "%.0f")
        + ", Range: " + NUM2STR(dRange, "%.3f")
        + ", Ma: " + NUM2STR(nMa, "%.0f\r\n")

        + "Lag: " + NUM2STR(nOutLag, "%.0f")
        + ", Neurons: " + NUM2STR(nNeurons, "%.0f\r\n")
        + "Stop: " + NUM2STR(dStopLoss, "%.4f")
        + ", Tp: " + NUM2STR(dTakeProfit, "%.4f")
        + NUM2STR(dStopIncrease, ", Stop increase: %f")
        + ", Buy: " + NUM2STR(dBuyLevel, "%.3f")
        + ", Sell: " + NUM2STR(dSellLevel, "%.3f\r\n")

        + "Drawdown: " + NUM2STR(dMaxDrawDown, "%.3f\r\n")

        + "Profit: " 
        + NUM2STR(arrBalance[ARRAY_SIZE(arrBalance) - 1] - 1000, "%f")
        + " (long: " 
        + NUM2STR(arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1], "%f")
        + ", short: " 
        + NUM2STR(arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1], 
            "%f)\r\n");

    strXML = strXML + "\t\t</symbol>\r\n";

    strXML = strXML + "\t\t" + SAVE_CHART(400, 300, 0, strImageFileName, 
        arrBars, arrBalance, arrBalanceBuy, arrBalanceSell);

    string strImageFileName_1 = strImagePath + strForexName + 
        "_nn_05a.png";    

    strXML = strXML + "\t\t" + SAVE_CHART(1000, 200, 1, 
        strImageFileName_1, arrDate, arrNn);

    strXML = strXML + "\t</symbol>\r\n";
}

Note the way program's output is formated. We can copy it from the output window and paste directly into our next SLANG program.

The program produces the chart, identical to the last chart in the forex_nn_04b.tsc section:


Trades: 38 (Buy: 38, Sell :0)
NocInterval: 12, Range: 0.012, Ma: 5, Lag: 2,
Neurons: 5, Stop: 0.02, Stop increase: 0.3,
Buy: 0.21, Sell: 0.9, Drawdown: 0.97
Profit: 1774.00 (long: 1774.00, short: 0.00)

Creating the NN script

Now that we have weights of the axons (of the neurons of our neural net), lets create the same network, using scripting language ONLY. It means, that we will not use functions of the scripting language, that are related with the NN functionality of Cortex, instead, we are going to emulate them.

This approach will allow us (in the next chapter) to move the code to almost ANY scripting language, even if it does not have NN functionality. For example, to the scripting language of your favorite trading platform.

First of all, lets take a look at the code that Cortex uses. This is a C++ code, and if you do not know C++, you can skip this part. This is just an illustration.

Keep in mind, that it is a "feedforward" part of the code. The complex one, that also takes a lot of resources to run, is the "backpropagation" one. But after the NN is created, we do not need it anymore.

int NeuralNetwork::Forward(double* pdInput)
{
    for(int i = 0; i < m_nLayers; i++)
        m_ppLayers[i]->Forward(pdInput);
    
    return 1;
}

// ------

int NeuralLayer::Forward(double* pdInput)
{
    for(int i = 0; i < m_nNeurons; i++)
        m_ppNeurons[i]->Forward(pdInput, m_nLayerType);
    return 1;
}

// ------

double Neuron::Forward(double* pnInput, int nLayerType)
{
    double dLinearCombiner = (-1) * m_pdWeights[m_nInputs - 1];
    
    if(nLayerType != SP_INPUT_LAYER)
        for(int i = 0; i < m_nInputs - 1; i++)
            dLinearCombiner += m_pdWeights[i] * m_ppFrom[i]->m_dOutput;
    else
        for(int i = 0; i < m_nInputs - 1; i++)
            dLinearCombiner += pnInput[i] * m_pdWeights[i];

    m_dOutput = Activation(dLinearCombiner);

    return m_dOutput;
}
///////////////////////////////////////////////////////
// Activation function
double Neuron::Activation(double u, int nType)    
{
    double dEx = tanh(u / 2);
    if(dEx == 1 || dEx == -1)
        dEx *= 0.999999999;
    return (nType == SP_SIGMOID) ? (dEx + 1) / 2 : dEx;
}

As you can see, the code is really simple. Now lets do the same using the SLANG script. As in examples before, we will keep the overall structure of the code, so that this example looks familiar. The only difference is that instead of using the built-in APPLY_NN function, we call the function of our own. The code that we do not use (such as cycles) is commented, but not removed.

The comments are placed inside the code (only in the part, that differs from the examples above).

void main()
{
    OUT_CLEANUP();

    string strImagePath = 
        "c:\\S_Projects\\CortexPro\\data\\stocks_nn\\forex_nn_05\\";

    string strForexName = "EURUSD_H1";

    string strNnPath = 
        "c:\\S_Projects\\CortexPro\\data\\stocks_nn\\forex_nn_05\\";
    string strNnFileName = strNnPath + strForexName + "_05.nn";

    // ------------

    string strDataFileName = 
        "c:\\S_Projects\\CortexPro\\data\\samples\\forex\\" 
            + strForexName + ".TXT";
    string strLagFileName = 
        "c:\\S_Projects\\CortexPro\\data\\stocks_nn\\forex_nn_05\\"
            + strForexName + "_05.lgg";
    
    double bIsPathRelative = 0;

    array arrDate = CREATE_ARRAY(0);
    array arrTime = CREATE_ARRAY(0);
    array arrOpen = CREATE_ARRAY(0);
    array arrHigh = CREATE_ARRAY(0);
    array arrLow = CREATE_ARRAY(0);
    array arrClose = CREATE_ARRAY(0);

    TABLE_LOADER(strDataFileName, bIsPathRelative, 0, "", 0, "", 0, 
        arrDate, 1, arrTime, 2, arrOpen, 3, arrHigh, 
        4, arrLow, 5, arrClose);

    double nArraySize = ARRAY_SIZE(arrClose);

    // -------
    
    // Interval, Range, Ma, OutLag 
    array_s arrParameters = CREATE_ARRAY_S(0);
    arrParameters[0] = "12,0.012,5,2";

    array arrNeurons = CREATE_ARRAY(0);
    arrNeurons[0] = 5;

    array arrLags = CREATE_ARRAY(0);
    array_s arrStrLags = CREATE_ARRAY_S(0);
    
    // ATTN: These lags are based on nWinLag. 
    // Example: nWinLag = 1, then each number should be increased by 1
    arrStrLags[0] = "17,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16";

    array arrStopLoss = CREATE_ARRAY(0);
    arrStopLoss[0] = 0.02;

    array arrNoc;
    array arrNocSmooth;

    double nRemoveFirst = 200;

    double dStopError = 0;
    double nStopEpoch = 25000; 

    array arrNn = CREATE_ARRAY(0);

    string strParam;
    string strToken;
    double nMa;
    double hNn;
    double nExtractRecords;

    // ------

    double nNetNum = 0;
    double nCounter = 0;
    double dExpectedCycles = ARRAY_SIZE(arrParameters)
        * ARRAY_SIZE(arrStrLags) * ARRAY_SIZE(arrNeurons);

    string strResult = "";
    
    double dErrorMin = -1;

    double bSuccess;
    double nWinners = 0;
    double nTradeNumber;

    array arrBalance = CREATE_ARRAY(0);
    array arrBalanceBuy = CREATE_ARRAY(0);
    array arrBalanceSell = CREATE_ARRAY(0);

    string strXML = "<forex>\r\n";

    // only one cycle
    for(double nParIdx = 0; nParIdx < ARRAY_SIZE(arrParameters); 
        nParIdx = nParIdx + 1)
    {
        strParam = arrParameters[nParIdx];
        strToken = GET_TOKEN(strParam, ",");
        double nInterval = STR2NUM(strToken);

        strToken = GET_TOKEN(strParam, ",");

        double dRange = STR2NUM(strToken);

        arrNoc = CreateNoc(nInterval, dRange);

        strToken = GET_TOKEN(strParam, ",");
        nMa = STR2NUM(strToken);

        arrNocSmooth = EXP_MVAVG(arrNoc, nMa);

        strToken = GET_TOKEN(strParam, ",");
        double nOutLag = STR2NUM(strToken);
        
        for(double nLagIdx = 0; nLagIdx < ARRAY_SIZE(arrStrLags); 
            nLagIdx = nLagIdx + 1)
        {
            double nNumOfLags;
            string strLagBuf = arrStrLags[nLagIdx];
            CreateLagFile(strLagBuf, nRemoveFirst);

            for(double nNeuronsIdx = 0; nNeuronsIdx < 
                ARRAY_SIZE(arrNeurons); 
                nNeuronsIdx = nNeuronsIdx + 1)
            {
                nCounter = nCounter + 1;

                double nNeurons = arrNeurons[nNeuronsIdx];
                        
//              NewNn(arrLags, dStopError, nStopEpoch, nNeurons);
//              TeachNn();

//              hNn = OPEN_NN(strNnFileName, bIsPathRelative);
//              APPLY_NN(hNn, nExtractRecords, 1.0, 1, 
//                  arrNocSmooth, arrLags, 1, arrNn);

                ApplyScriptNn();

                for(double nStopIdx = 0; 
                    nStopIdx < ARRAY_SIZE(arrStopLoss); 
                    nStopIdx = nStopIdx + 1)
                {
                    double dStopLoss = arrStopLoss[nStopIdx];
                    double dTakeProfit = 0;
                                
                    double dStopIncrease = 0.3;
//                  for(double dStopIncrease = 0; 
//                      dStopIncrease < 0.7; 
//                      dStopIncrease = dStopIncrease + 0.1)
//                  {
//                      OUT_CLEANUP();

//                      for(double nBuyIdx = 0; nBuyIdx < 30; 
//                          nBuyIdx = nBuyIdx + 1)
//                      {
                            double dBuyLevel = 0.21;
//                          double dBuyLevel = 0.1 + 0.01 * nBuyIdx;

//                          for(double nSellIdx = 0; nSellIdx < 30; 
//                              nSellIdx = nSellIdx + 1)
//                          {
                                double dSellLevel = 0.9;
//                              double dSellLevel = 0.9 - 
//                                    0.01 * nSellIdx;

                                Test();
                                        
                                nWinners = nWinners + 1;

                                Chart(strForexName);
//                          }
//                      }
//                  }
                }
            }
        }
    }

    strXML = strXML + "</forex>\r\n";
    SAVE_XML(strImagePath, "chart_forex_nn", 
        "chart_forex_nn", "root", strXML);
    SHOW_XML(strImagePath + "chart_forex_nn.xml");

    PRINT("%s\r\nDone\r\n", strResult);
}

// ---------------

void Test()
{
    bSuccess = 1;
    
    ARRAY_REMOVE(arrBalance, -1);
    arrBalance[0] = 1000;

    ARRAY_REMOVE(arrBalanceBuy, -1);
    arrBalanceBuy[0] = 0;

    ARRAY_REMOVE(arrBalanceSell, -1);
    arrBalanceSell[0] = 0;

    array arrBars = CREATE_ARRAY(0);
    arrBars[0] = 0;

    double dLotSize = 100;    // 0.1 lot
    double nType = 0;         // 1 buy, -1 sell, 0 - none

    double dSpread = 0.0005;
    double bStop;

    double dMaxDrawDown = 0;      // Max. Drawdown
    double dCurrentMax = 1000;    // Used to calculate drawdown

    nTradeNumber = 0;
    double nTradeNumberBuy = 0;
    double nTradeNumberSell = 0;
    double dOpenPrice;
    double dStop;
    double dTp = 0;

    for(double nBar = nRemoveFirst + 1; 
        nBar < ARRAY_SIZE(arrClose) - 1; nBar = nBar + 1)
    {
        if(nType != 0)
        {
            bStop = 0;
            double dClosedAt;

            // If BUY and stop loss or take profit reached
            if(nType == -1 && (arrLow[nBar] <= dStop || 
                (dTakeProfit > 0 && arrHigh[nBar] >= dTp - dSpread)))
            {
                arrBalance[ARRAY_SIZE(arrBalance)] = 
                    arrBalance[ARRAY_SIZE(arrBalance) - 1] 
                    + 100 * (arrLow[nBar + 1] - dOpenPrice) * dLotSize;

                arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = 
                    arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1] 
                    + 100 * (arrLow[nBar + 1] - dOpenPrice) * dLotSize;

                arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = 
                    arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1]; 

                bStop = 1;
                dClosedAt = arrLow[nBar + 1];
            }
            else
            {
                if(nType == 1 && (arrHigh[nBar] >= dStop - dSpread ||
                    (dTakeProfit > 0 && arrLow[nBar] <= dTp)))
                {
                    arrBalance[ARRAY_SIZE(arrBalance)] = 
                        arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
                        100 * (dOpenPrice - arrHigh[nBar + 1] - 
                            dSpread) * dLotSize;

                    arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = 
                        arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1];

                    arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = 
                        arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1] + 
                        100 * (dOpenPrice - arrHigh[nBar + 1] - 
                            dSpread) * dLotSize;

                    bStop = 1;
                    dClosedAt = arrHigh[nBar + 1];
                }
            }

            if(bStop == 1)
            {
                nType = 0;
                arrBars[ARRAY_SIZE(arrBars)] = nBar;
            }
        }

        double dDrawDown = (dCurrentMax - 
            arrBalance[ARRAY_SIZE(arrBalance) - 1]) / 1000; 
        dMaxDrawDown = MAX(dMaxDrawDown, dDrawDown);
        dCurrentMax = 
            MAX(dCurrentMax, arrBalance[ARRAY_SIZE(arrBalance) - 1]);

        if(nType != -1 && arrNn[nBar - 1] <= dBuyLevel && 
            arrNn[nBar] >= dBuyLevel)
        {
            if(nType == 1)
            {
                arrBalance[ARRAY_SIZE(arrBalance)] = 
                    arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
                        100 * (dOpenPrice - arrHigh[nBar + 1] - 
                            dSpread) * dLotSize;

                arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = 
                    arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1] + 
                        100 * (dOpenPrice - arrHigh[nBar + 1] - 
                            dSpread) * dLotSize;

                arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = 
                    arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1];

                arrBars[ARRAY_SIZE(arrBars)] = nBar;

                nType = 0;
            }

            dOpenPrice = arrHigh[nBar + 1];
            dStop = dOpenPrice - dStopLoss;
            dTp = dOpenPrice + dTakeProfit;
            nType = -1;
            
            nTradeNumber = nTradeNumber + 1;
            nTradeNumberBuy = nTradeNumberBuy + 1;
        }
        else
        {
            if(nType != 1 && arrNn[nBar - 1] >= dSellLevel && 
                arrNn[nBar] <= dSellLevel)
            {
                if(nType == -1)
                {
                    arrBalance[ARRAY_SIZE(arrBalance)] =
                        arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
                        100 * (arrLow[nBar + 1] - dOpenPrice) * dLotSize;

                    arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] =
                        arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1] + 
                        100 * (arrLow[nBar + 1] - dOpenPrice) * dLotSize;

                    arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] =
                        arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1];

                    arrBars[ARRAY_SIZE(arrBars)] = nBar;
                
                    nType = 0;
                }

//              dOpenPrice = arrLow[nBar + 1];
//              dStop = dOpenPrice + dStopLoss;
//              dTp = dOpenPrice - dTakeProfit;
//              nType = 1;
//        
//              nTradeNumber = nTradeNumber + 1;
//              nTradeNumberSell = nTradeNumberSell + 1;
            }
        }

        if(dStopIncrease != 0)
        {
            if(nType == -1)
            {
                if(arrLow[nBar] - dStop >= 
                    dStopLoss * (1 + dStopIncrease))
                {
                    dStop = arrLow[nBar] - dStopLoss;
                }
            }
            else
            {
                if(nType == 1)
                {
                    if(dStop - arrHigh[nBar] >= 
                        dStopLoss * (1 + dStopIncrease))
                    {
                        dStop = arrHigh[nBar] + dStopLoss;
                    }
                }
            }
        }
    }

    // If at the end we have open positions, close them

    if(nType == 1)
    {
        arrBalance[ARRAY_SIZE(arrBalance)] = 
            arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
            100 * (dOpenPrice - arrHigh[nBar] - dSpread) * dLotSize;

        arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = 
            arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1] + 
            100 * (dOpenPrice - arrHigh[nBar] - dSpread) * dLotSize;

        arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = 
            arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1];

        arrBars[ARRAY_SIZE(arrBars)] = nBar - 1;
    }
    else
    {
        if(nType == -1)
        {
            arrBalance[ARRAY_SIZE(arrBalance)] = 
                arrBalance[ARRAY_SIZE(arrBalance) - 1] + 
                100 * (arrLow[nBar] - dOpenPrice) * dLotSize;
    
            arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = 
                arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1] + 
                100 * (arrLow[nBar] - dOpenPrice) * dLotSize;
    
            arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = 
                arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1];
    
            arrBars[ARRAY_SIZE(arrBars)] = nBar - 1;
        }
    }
}

// ---------------

array CreateNoc(double nInterval, double dMinRange)
{    
    PRINT("%s\r\n", "Creating NOC indicator");

    array arrNormOnCondition = CREATE_ARRAY(0);
    array arrPeriodLow = CREATE_ARRAY(0);
    array arrPeriodHigh = CREATE_ARRAY(0);

    double nArraySize = ARRAY_SIZE(arrClose);

    array arrPeriodLow = MVMIN(arrLow, nInterval);
    array arrPeriodHigh = MVMAX(arrHigh, nInterval);

    for(double i = 0; i < nInterval; i = i + 1) 
    {
        arrNormOnCondition[i] = 0;
    }
    
    double dClose;
    double dLow;
    double dHigh;
    for(i = nInterval; i < nArraySize; i = i + 1)
    {
        dClose = arrClose[i];
        dLow = arrPeriodLow[i];
        dHigh = arrPeriodHigh[i];

        if(dHigh - dLow > dMinRange)
        {
            // / 2 + 1 to confine to 0...1 instead of -1...1
            arrNormOnCondition[i] = 
                (((dClose - dLow) - (dHigh - dClose))
                    / (dHigh - dLow)) / 2 + 0.5; 
        }
        else
        {
            arrNormOnCondition[i] = 
                (((dClose - dLow) - (dHigh - dClose))
                    / dMinRange) / 2 + 0.5; 
        }
    }

    return arrNormOnCondition;
}

// ----------------

void CreateLagFile(string strLags, double nRemoveFirst)
{
     PRINT("%s\r\n", "Creating Lag file");
    
    double hFile = F_OPEN(strLagFileName, "wb");

    F_PRINT(hFile, "%s", "No,Clv");

    ARRAY_REMOVE(arrLags, -1);

    string strToken = GET_TOKEN(strLags, ",");
    double nNumOfLags = STR2NUM(strToken);

    for(double i = 0; i < nNumOfLags; i = i + 1)
    {
        strToken = GET_TOKEN(strLags, ",");
        arrLags[i] = STR2NUM(strToken) + nOutLag;
    }

    double nFullLag;
    for(i = 0; i < ARRAY_SIZE(arrLags); i = i + 1)
    {
        nFullLag = arrLags[i];
        F_PRINT(hFile, ",NocMa%.0f", nMa, "-%.0f", nFullLag); 
    }

    F_PRINT(hFile, "%s\r\n", "");

    double nNum;
    for(i = nRemoveFirst; i < ARRAY_SIZE(arrClose); i = i + 1)
    {
        nNum = i - nRemoveFirst + 1;
        F_PRINT(hFile, "%.0f", nNum, ",%f", arrNocSmooth[i]);     

        for(double j = 0; j < ARRAY_SIZE(arrLags); j = j + 1)
        {
            F_PRINT(hFile, ",%f", arrNocSmooth[i - arrLags[j]]);     
        }

        F_PRINT(hFile, "%s\r\n", "");
    }

    F_CLOSE(hFile);
}

// --------------

void ApplyScriptNn()
{
    // We know the structure, as we created this NN

    double nActivationType = 0;

    double nLayers = 3;

    array arrNeuronsInLayer = CREATE_ARRAY(0);
    arrNeuronsInLayer[0] = 17;
    arrNeuronsInLayer[1] = 5;
    arrNeuronsInLayer[2] = 1;

    array arrWeights_0 = CREATE_ARRAY(0);
    array arrWeights_1 = CREATE_ARRAY(0);
    array arrWeights_2 = CREATE_ARRAY(0);

    // Below, is the output of the forex_nn_05a, pasted into
    // our script (weight, exported from the NN we created and teached). 

    // Layer: 0
    arrWeights_0[0] = 0.376436;
    arrWeights_0[1] = 0.690657;
    arrWeights_0[2] = 0.512335;
    arrWeights_0[3] = 0.786179;
    arrWeights_0[4] = 0.671377;
    arrWeights_0[5] = 0.614279;
    arrWeights_0[6] = 0.539750;
    arrWeights_0[7] = 0.820380;
    arrWeights_0[8] = 0.750566;
    arrWeights_0[9] = 0.707890;
    arrWeights_0[10] = 0.396094;
    arrWeights_0[11] = 0.725720;
    arrWeights_0[12] = 0.555349;
    arrWeights_0[13] = 0.395257;
    arrWeights_0[14] = 0.227280;
    arrWeights_0[15] = 0.128274;
    arrWeights_0[16] = 0.289072;
    arrWeights_0[17] = 0.387067;
    arrWeights_0[18] = 0.662450;
    arrWeights_0[19] = 1.019812;
    arrWeights_0[20] = 0.761206;
    arrWeights_0[21] = 0.984280;
    arrWeights_0[22] = 0.878235;
    arrWeights_0[23] = 0.829384;
    arrWeights_0[24] = 0.786234;
    arrWeights_0[25] = 1.189799;
    arrWeights_0[26] = 1.112219;
    arrWeights_0[27] = 1.069424;
    arrWeights_0[28] = 0.640762;
    arrWeights_0[29] = 0.730670;
    arrWeights_0[30] = 0.067006;
    arrWeights_0[31] = -0.320953;
    arrWeights_0[32] = -0.600880;
    arrWeights_0[33] = -0.371686;
    arrWeights_0[34] = 0.261142;
    arrWeights_0[35] = 0.063323;
    arrWeights_0[36] = 0.413053;
    arrWeights_0[37] = 0.737687;
    arrWeights_0[38] = 0.555759;
    arrWeights_0[39] = 0.815068;
    arrWeights_0[40] = 0.692078;
    arrWeights_0[41] = 0.630406;
    arrWeights_0[42] = 0.556191;
    arrWeights_0[43] = 0.846380;
    arrWeights_0[44] = 0.763236;
    arrWeights_0[45] = 0.715549;
    arrWeights_0[46] = 0.390297;
    arrWeights_0[47] = 0.705948;
    arrWeights_0[48] = 0.482014;
    arrWeights_0[49] = 0.291501;
    arrWeights_0[50] = 0.101741;
    arrWeights_0[51] = 0.048724;
    arrWeights_0[52] = 0.280508;
    arrWeights_0[53] = 0.428389;
    arrWeights_0[54] = -6.065296;
    arrWeights_0[55] = 0.559039;
    arrWeights_0[56] = 1.732407;
    arrWeights_0[57] = 2.446417;
    arrWeights_0[58] = 2.226664;
    arrWeights_0[59] = 1.043498;
    arrWeights_0[60] = -0.892194;
    arrWeights_0[61] = 0.538670;
    arrWeights_0[62] = 1.696599;
    arrWeights_0[63] = 2.895421;
    arrWeights_0[64] = 1.936742;
    arrWeights_0[65] = 0.857415;
    arrWeights_0[66] = 1.777173;
    arrWeights_0[67] = 1.489486;
    arrWeights_0[68] = 0.990452;
    arrWeights_0[69] = -0.312611;
    arrWeights_0[70] = -2.485575;
    arrWeights_0[71] = 5.784152;
    arrWeights_0[72] = 0.691233;
    arrWeights_0[73] = 1.003065;
    arrWeights_0[74] = 0.746760;
    arrWeights_0[75] = 0.973191;
    arrWeights_0[76] = 0.868018;
    arrWeights_0[77] = 0.821039;
    arrWeights_0[78] = 0.770604;
    arrWeights_0[79] = 1.139881;
    arrWeights_0[80] = 1.057454;
    arrWeights_0[81] = 1.004390;
    arrWeights_0[82] = 0.595049;
    arrWeights_0[83] = 0.733458;
    arrWeights_0[84] = 0.177670;
    arrWeights_0[85] = -0.170966;
    arrWeights_0[86] = -0.443584;
    arrWeights_0[87] = -0.345470;
    arrWeights_0[88] = 0.135814;
    arrWeights_0[89] = -0.010206;
    arrWeights_0[90] = 0.585687;
    arrWeights_0[91] = 1.040471;
    arrWeights_0[92] = 0.778059;
    arrWeights_0[93] = 0.994592;
    arrWeights_0[94] = 0.882501;
    arrWeights_0[95] = 0.819930;
    arrWeights_0[96] = 0.783313;
    arrWeights_0[97] = 1.225092;
    arrWeights_0[98] = 1.138965;
    arrWeights_0[99] = 1.112052;
    arrWeights_0[100] = 0.667895;
    arrWeights_0[101] = 0.725504;
    arrWeights_0[102] = -0.042205;
    arrWeights_0[103] = -0.470107;
    arrWeights_0[104] = -0.758346;
    arrWeights_0[105] = -0.356139;
    arrWeights_0[106] = 0.477461;
    arrWeights_0[107] = 0.159831;
    arrWeights_0[108] = 0.465873;
    arrWeights_0[109] = 0.827723;
    arrWeights_0[110] = 0.635881;
    arrWeights_0[111] = 0.876557;
    arrWeights_0[112] = 0.751904;
    arrWeights_0[113] = 0.689548;
    arrWeights_0[114] = 0.624036;
    arrWeights_0[115] = 0.943642;
    arrWeights_0[116] = 0.844483;
    arrWeights_0[117] = 0.793347;
    arrWeights_0[118] = 0.439269;
    arrWeights_0[119] = 0.710473;
    arrWeights_0[120] = 0.362673;
    arrWeights_0[121] = 0.107347;
    arrWeights_0[122] = -0.123129;
    arrWeights_0[123] = -0.079149;
    arrWeights_0[124] = 0.300953;
    arrWeights_0[125] = 0.333110;
    arrWeights_0[126] = 36.878339;
    arrWeights_0[127] = -34.157785;
    arrWeights_0[128] = 13.570499;
    arrWeights_0[129] = -9.445193;
    arrWeights_0[130] = 5.906855;
    arrWeights_0[131] = -2.631383;
    arrWeights_0[132] = -3.466850;
    arrWeights_0[133] = 1.387212;
    arrWeights_0[134] = 1.168750;
    arrWeights_0[135] = -1.252440;
    arrWeights_0[136] = -4.167107;
    arrWeights_0[137] = 7.462264;
    arrWeights_0[138] = 4.218665;
    arrWeights_0[139] = -10.616881;
    arrWeights_0[140] = 2.816792;
    arrWeights_0[141] = -0.854816;
    arrWeights_0[142] = 0.465124;
    arrWeights_0[143] = 3.805423;
    arrWeights_0[144] = 17.371809;
    arrWeights_0[145] = -10.758329;
    arrWeights_0[146] = 4.288758;
    arrWeights_0[147] = -3.300900;
    arrWeights_0[148] = -1.387066;
    arrWeights_0[149] = -0.545407;
    arrWeights_0[150] = -0.627846;
    arrWeights_0[151] = 0.359405;
    arrWeights_0[152] = -0.910465;
    arrWeights_0[153] = 1.672222;
    arrWeights_0[154] = -1.087077;
    arrWeights_0[155] = 1.711690;
    arrWeights_0[156] = 1.892743;
    arrWeights_0[157] = 1.370835;
    arrWeights_0[158] = -1.074922;
    arrWeights_0[159] = -2.069598;
    arrWeights_0[160] = 2.132245;
    arrWeights_0[161] = 3.323435;
    arrWeights_0[162] = 3.192245;
    arrWeights_0[163] = 0.722216;
    arrWeights_0[164] = 0.595036;
    arrWeights_0[165] = 2.562026;
    arrWeights_0[166] = 4.118245;
    arrWeights_0[167] = 2.533825;
    arrWeights_0[168] = 0.661547;
    arrWeights_0[169] = -0.522384;
    arrWeights_0[170] = -0.923482;
    arrWeights_0[171] = -0.744284;
    arrWeights_0[172] = -0.531758;
    arrWeights_0[173] = -1.737114;
    arrWeights_0[174] = -0.894963;
    arrWeights_0[175] = 0.335197;
    arrWeights_0[176] = 2.767129;
    arrWeights_0[177] = 0.169424;
    arrWeights_0[178] = -1.868230;
    arrWeights_0[179] = 2.057215;
    arrWeights_0[180] = 0.408584;
    arrWeights_0[181] = 0.730536;
    arrWeights_0[182] = 0.549013;
    arrWeights_0[183] = 0.810092;
    arrWeights_0[184] = 0.687577;
    arrWeights_0[185] = 0.626130;
    arrWeights_0[186] = 0.551439;
    arrWeights_0[187] = 0.839836;
    arrWeights_0[188] = 0.758454;
    arrWeights_0[189] = 0.711269;
    arrWeights_0[190] = 0.388133;
    arrWeights_0[191] = 0.706349;
    arrWeights_0[192] = 0.491137;
    arrWeights_0[193] = 0.305519;
    arrWeights_0[194] = 0.119262;
    arrWeights_0[195] = 0.059367;
    arrWeights_0[196] = 0.280111;
    arrWeights_0[197] = 0.432421;
    arrWeights_0[198] = 11.382900;
    arrWeights_0[199] = -2.355699;
    arrWeights_0[200] = 0.800231;
    arrWeights_0[201] = -0.812782;
    arrWeights_0[202] = -1.725548;
    arrWeights_0[203] = 0.506914;
    arrWeights_0[204] = 3.685745;
    arrWeights_0[205] = -1.570360;
    arrWeights_0[206] = -0.678070;
    arrWeights_0[207] = -1.385545;
    arrWeights_0[208] = 0.665419;
    arrWeights_0[209] = -2.403472;
    arrWeights_0[210] = 0.348713;
    arrWeights_0[211] = 0.948801;
    arrWeights_0[212] = 3.943966;
    arrWeights_0[213] = 0.609010;
    arrWeights_0[214] = -2.954932;
    arrWeights_0[215] = 6.570737;
    arrWeights_0[216] = 1.098680;
    arrWeights_0[217] = 0.457162;
    arrWeights_0[218] = 0.047123;
    arrWeights_0[219] = 0.314413;
    arrWeights_0[220] = 0.270546;
    arrWeights_0[221] = 0.271241;
    arrWeights_0[222] = 0.444340;
    arrWeights_0[223] = 0.685449;
    arrWeights_0[224] = 0.881795;
    arrWeights_0[225] = 0.904727;
    arrWeights_0[226] = 0.538997;
    arrWeights_0[227] = 0.274688;
    arrWeights_0[228] = -0.107260;
    arrWeights_0[229] = -0.182735;
    arrWeights_0[230] = -0.297917;
    arrWeights_0[231] = -0.722563;
    arrWeights_0[232] = -1.004438;
    arrWeights_0[233] = 2.114029;
    arrWeights_0[234] = 0.625319;
    arrWeights_0[235] = 1.073737;
    arrWeights_0[236] = 0.794572;
    arrWeights_0[237] = 1.012257;
    arrWeights_0[238] = 0.909403;
    arrWeights_0[239] = 0.851104;
    arrWeights_0[240] = 0.819041;
    arrWeights_0[241] = 1.284499;
    arrWeights_0[242] = 1.213157;
    arrWeights_0[243] = 1.195116;
    arrWeights_0[244] = 0.731964;
    arrWeights_0[245] = 0.733472;
    arrWeights_0[246] = -0.119051;
    arrWeights_0[247] = -0.574390;
    arrWeights_0[248] = -0.865588;
    arrWeights_0[249] = -0.405175;
    arrWeights_0[250] = 0.494158;
    arrWeights_0[251] = 0.142718;
    arrWeights_0[252] = 0.443620;
    arrWeights_0[253] = 0.790221;
    arrWeights_0[254] = 0.604280;
    arrWeights_0[255] = 0.852407;
    arrWeights_0[256] = 0.728354;
    arrWeights_0[257] = 0.666281;
    arrWeights_0[258] = 0.596990;
    arrWeights_0[259] = 0.903026;
    arrWeights_0[260] = 0.809120;
    arrWeights_0[261] = 0.758782;
    arrWeights_0[262] = 0.416953;
    arrWeights_0[263] = 0.708992;
    arrWeights_0[264] = 0.415040;
    arrWeights_0[265] = 0.186673;
    arrWeights_0[266] = -0.028387;
    arrWeights_0[267] = -0.027435;
    arrWeights_0[268] = 0.289143;
    arrWeights_0[269] = 0.376176;
    arrWeights_0[270] = 0.377669;
    arrWeights_0[271] = 0.691528;
    arrWeights_0[272] = 0.513124;
    arrWeights_0[273] = 0.786421;
    arrWeights_0[274] = 0.670892;
    arrWeights_0[275] = 0.613345;
    arrWeights_0[276] = 0.538605;
    arrWeights_0[277] = 0.819386;
    arrWeights_0[278] = 0.749119;
    arrWeights_0[279] = 0.706133;
    arrWeights_0[280] = 0.394004;
    arrWeights_0[281] = 0.723393;
    arrWeights_0[282] = 0.551766;
    arrWeights_0[283] = 0.390955;
    arrWeights_0[284] = 0.222564;
    arrWeights_0[285] = 0.125193;
    arrWeights_0[286] = 0.287931;
    arrWeights_0[287] = 0.397175;
    arrWeights_0[288] = 0.390177;
    arrWeights_0[289] = 0.704718;
    arrWeights_0[290] = 0.524964;
    arrWeights_0[291] = 0.793337;
    arrWeights_0[292] = 0.673926;
    arrWeights_0[293] = 0.614109;
    arrWeights_0[294] = 0.538623;
    arrWeights_0[295] = 0.821537;
    arrWeights_0[296] = 0.747044;
    arrWeights_0[297] = 0.702102;
    arrWeights_0[298] = 0.386249;
    arrWeights_0[299] = 0.712341;
    arrWeights_0[300] = 0.526445;
    arrWeights_0[301] = 0.357474;
    arrWeights_0[302] = 0.183363;
    arrWeights_0[303] = 0.099539;
    arrWeights_0[304] = 0.282065;
    arrWeights_0[305] = 0.431269;

    // Layer: 1
    arrWeights_1[0] = 0.313367;
    arrWeights_1[1] = 0.600233;
    arrWeights_1[2] = 0.413307;
    arrWeights_1[3] = 0.723853;
    arrWeights_1[4] = 0.604417;
    arrWeights_1[5] = 0.564724;
    arrWeights_1[6] = 0.494233;
    arrWeights_1[7] = 0.776964;
    arrWeights_1[8] = 0.713700;
    arrWeights_1[9] = 0.689037;
    arrWeights_1[10] = 0.403412;
    arrWeights_1[11] = 0.743660;
    arrWeights_1[12] = 0.681661;
    arrWeights_1[13] = 0.582135;
    arrWeights_1[14] = 0.470205;
    arrWeights_1[15] = 0.320594;
    arrWeights_1[16] = 0.360557;
    arrWeights_1[17] = 0.505358;
    arrWeights_1[18] = 0.207009;
    arrWeights_1[19] = -0.600315;
    arrWeights_1[20] = 0.292799;
    arrWeights_1[21] = -0.486397;
    arrWeights_1[22] = -0.698483;
    arrWeights_1[23] = -0.466675;
    arrWeights_1[24] = 0.161680;
    arrWeights_1[25] = 1.267072;
    arrWeights_1[26] = 0.942115;
    arrWeights_1[27] = 1.601429;
    arrWeights_1[28] = 0.294829;
    arrWeights_1[29] = 2.066898;
    arrWeights_1[30] = 1.149577;
    arrWeights_1[31] = -0.608109;
    arrWeights_1[32] = 0.236174;
    arrWeights_1[33] = 0.223691;
    arrWeights_1[34] = 0.281142;
    arrWeights_1[35] = 2.639872;
    arrWeights_1[36] = 0.298441;
    arrWeights_1[37] = 0.585156;
    arrWeights_1[38] = 0.397331;
    arrWeights_1[39] = 0.714002;
    arrWeights_1[40] = 0.591642;
    arrWeights_1[41] = 0.547087;
    arrWeights_1[42] = 0.477829;
    arrWeights_1[43] = 0.768086;
    arrWeights_1[44] = 0.709662;
    arrWeights_1[45] = 0.679375;
    arrWeights_1[46] = 0.387499;
    arrWeights_1[47] = 0.736123;
    arrWeights_1[48] = 0.665259;
    arrWeights_1[49] = 0.564510;
    arrWeights_1[50] = 0.453956;
    arrWeights_1[51] = 0.305558;
    arrWeights_1[52] = 0.345016;
    arrWeights_1[53] = 0.508310;
    arrWeights_1[54] = -0.677255;
    arrWeights_1[55] = -1.032676;
    arrWeights_1[56] = -0.744642;
    arrWeights_1[57] = -0.925155;
    arrWeights_1[58] = -1.197128;
    arrWeights_1[59] = -0.992372;
    arrWeights_1[60] = -0.855172;
    arrWeights_1[61] = 7.016197;
    arrWeights_1[62] = 5.226784;
    arrWeights_1[63] = 2.411577;
    arrWeights_1[64] = -0.733721;
    arrWeights_1[65] = 3.402664;
    arrWeights_1[66] = 1.729731;
    arrWeights_1[67] = -0.991801;
    arrWeights_1[68] = -0.815011;
    arrWeights_1[69] = -0.677321;
    arrWeights_1[70] = -0.693829;
    arrWeights_1[71] = 9.138765;
    arrWeights_1[72] = -0.246490;
    arrWeights_1[73] = -0.391846;
    arrWeights_1[74] = -0.347720;
    arrWeights_1[75] = -1.096698;
    arrWeights_1[76] = -0.001496;
    arrWeights_1[77] = -0.693606;
    arrWeights_1[78] = -0.468658;
    arrWeights_1[79] = 6.178259;
    arrWeights_1[80] = 4.306803;
    arrWeights_1[81] = 2.409460;
    arrWeights_1[82] = -0.336563;
    arrWeights_1[83] = 2.374341;
    arrWeights_1[84] = 1.504236;
    arrWeights_1[85] = -0.726152;
    arrWeights_1[86] = -0.426187;
    arrWeights_1[87] = -0.254840;
    arrWeights_1[88] = -0.293797;
    arrWeights_1[89] = -1.892730;

    // Layer: 2
    arrWeights_2[0] = -0.626160;
    arrWeights_2[1] = 1.717378;
    arrWeights_2[2] = -0.485501;
    arrWeights_2[3] = 2.751261;
    arrWeights_2[4] = 2.323994;
    arrWeights_2[5] = 2.047616;

    // First and second layers produce one outpur per neuron
    // Third layer has one neuron and produces one output

    array arrOutput_0 = CREATE_ARRAY(0);
    array arrOutput_1 = CREATE_ARRAY(0);
    double dNnOutput;

    // ------

    // We will read patterns from the lag file, one line is one pattern
    array arrPattern = CREATE_ARRAY(0);
    
    double hFile = F_OPEN(strLagFileName, "rb");
        
    // Skip the first line containing headers
    string strPattern = F_GETS(hFile); 

    // We do not need to normalize the data, as NOC is already 
    // normalized to 0-1 range

    string strPattern;
    string strToken;
    
    for(i = 0; i < 100000; i = i + 1)
    {
        if(F_EOF(hFile) == 1)
        {
            break i;
        }

        strPattern = F_GETS(hFile); 
        
        // The last CR-LF, empty string
        if(STR_LEN(strPattern) < 10)
        {
            break i;
        }
        
        if(i % 100 == 0)
        {
            PRINT("Pattern: %.0f\r\n", i);
        }

        // Skip first two columns containing column number 
        // and desirable output
        strToken = GET_TOKEN(strPattern, ",");
        strToken = GET_TOKEN(strPattern, ",");

        ARRAY_REMOVE(arrPattern, -1);

        for(double j = 0; j < nNumOfLags; j = j + 1)
        {
            strToken = GET_TOKEN(strPattern, ",");
            double dValue = STR2NUM(strToken);
            arrPattern[j] = dValue;
        }

        // Pattern loaded, now process

        ARRAY_REMOVE(arrOutput_0, -1);
        ARRAY_REMOVE(arrOutput_1, -1);
        
        // The following code implements 3 C++ functions (together),
        // quoted above

        for(double nLayer = 0; nLayer < nLayers; nLayer = nLayer + 1)
        {
            // Absolute index of the weight in an array
            double nWeightIdx = 0;

            for(double nNeuron = 0; nNeuron < arrNeuronsInLayer[nLayer]; 
                nNeuron = nNeuron + 1)
            {
                double dLinearCombiner = 0;
    
                double nInputs = arrNeuronsInLayer[nLayer];
                if(nLayer != 0)
                {
                    nInputs = arrNeuronsInLayer[nLayer - 1];
                }

                for(double nInput = 0; nInput < nInputs; 
                    nInput = nInput + 1)
                {
                    double dInput;
                    double dWeight;
                    if(nLayer == 0)
                    {
                        dInput = arrPattern[nInput];
                        dWeight = arrWeights_0[nWeightIdx];
                    }
                    else
                    {
                        if(nLayer == 1)
                        {
                            dInput = arrOutput_0[nInput];
                            dWeight = arrWeights_1[nWeightIdx];
                        }
                        else
                        {
                            dInput = arrOutput_1[nInput];
                            dWeight = arrWeights_2[nWeightIdx];
                        }
                    }

                    dLinearCombiner = dLinearCombiner + 
                        dWeight * dInput;

                    nWeightIdx = nWeightIdx + 1;
                }        

                if(nLayer == 0)
                {
                    dWeight = arrWeights_0[nWeightIdx];
                }
                else
                {
                    if(nLayer == 1)
                    {
                        dWeight = arrWeights_1[nWeightIdx];
                    }
                    else
                    {
                        dWeight = arrWeights_2[nWeightIdx];
                    }
                }

                dLinearCombiner = dLinearCombiner + (-1) * dWeight;
                nWeightIdx = nWeightIdx + 1;

                double dActivation = 
                    Activation(dLinearCombiner, nActivationType);

                if(nLayer == 0)
                {
                    arrOutput_0[nNeuron] = dActivation;
                }
                else 
                {
                    if(nLayer == 1)
                    {
                        arrOutput_1[nNeuron] = dActivation;
                    }
                    else
                    {
                        // 1 neuron in the last layer
                        dNnOutput = dActivation;    
                    }
                }
            
            }    // for all neurons

        }    // for all layers

        arrNn[i] = dNnOutput;
    
    }    // for all patterns

    double dInsert = arrNn[0];

    for(i = 0; i < nRemoveFirst; i = i + 1)
    {
        ARRAY_INSERT(arrNn, dInsert, 0);
    }
}

// -------------

double Activation(double u, double nType)
{
    double dEx = TANH(u / 2);
    if(dEx == 1 || dEx == -1)
    {
        dEx = dEx * 0.999999999;
    }
    
    // 0 - sigmoid, 1 - tangent
    if(nType == 0)
    {
        return (dEx + 1) / 2;
    }

    return dEx;
}

// ------

void Chart(string strForexName)
{
    string strWinNnFileName;
        
    string strImageFileName = strImagePath + strForexName + 
        NUM2STR(nWinners, "_%.0f") + "_05b.png";    
            
    strXML = strXML + "\t<symbol>\r\n\t\t<symbol>\r\n";

    strXML = strXML + "Trades: " + NUM2STR(nTradeNumber, "%.0f")
        + "(Buy: " + NUM2STR(nTradeNumberBuy, "%.0f")
        + ", Sell :" + NUM2STR(nTradeNumberSell, "%.0f)\r\n")
        + "NocInterval: " + NUM2STR(nInterval, "%.0f")
        + ", Range: " + NUM2STR(dRange, "%.3f")
        + ", Ma: " + NUM2STR(nMa, "%.0f\r\n")

        + "Lag: " + NUM2STR(nOutLag, "%.0f")
        + ", Neurons: " + NUM2STR(nNeurons, "%.0f\r\n")
        + "Stop: " + NUM2STR(dStopLoss, "%.4f")
        + ", Tp: " + NUM2STR(dTakeProfit, "%.4f")
        + NUM2STR(dStopIncrease, ", Stop increase: %f")
        + ", Buy: " + NUM2STR(dBuyLevel, "%.3f")
        + ", Sell: " + NUM2STR(dSellLevel, "%.3f\r\n")

        + "Drawdown: " + NUM2STR(dMaxDrawDown, "%.3f\r\n")

        + "Profit: " 
        + NUM2STR(arrBalance[ARRAY_SIZE(arrBalance) - 1] - 1000, "%f")
        + " (long: " 
        + NUM2STR(arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1], "%f")
        + ", short: " 
        + NUM2STR(arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1], 
            "%f)\r\n");

    strXML = strXML + "\t\t</symbol>\r\n";

    strXML = strXML + "\t\t" + SAVE_CHART(400, 300, 0, strImageFileName, 
        arrBars, arrBalance, arrBalanceBuy, arrBalanceSell);

    string strImageFileName_1 = strImagePath + strForexName + 
        "_nn_05b.png";    

    strXML = strXML + "\t\t" + SAVE_CHART(1000, 200, 1, 
        strImageFileName_1, arrDate, arrNn);

    strXML = strXML + "\t</symbol>\r\n";
}

After we run this function, we discover, that the result it produces is the same, as the forex_nn_05a produced, which means the code works fine. Here are charts for the arrNn:

Note, that there is a difference at the beginning of the charts, as "our" NN does not try to process the data at the beginning (where lag is incomplete), while the built-in NN does not "know" about this problem. Of course, it doesn't affect the result, as the beginning of the chart is ignored by using the nRemoveFirst parameter in our script (set to 200, which is guaranteed to be larger, then our lag).

Using third-party trading platform

We have the NN that (more or less) can be used. We have the script, implementing this NN without calls to the Cortex-specific NN functions. Now we are going to port it to the trading platform that can be used for the real trading, which means it can contact brocker, place orders and earn (or loose) money.

As a trading platform, I am going to use MetaTrader

Disclaimer: I am not related to MetaQuotes in any way. I do not work for them, I am not their affiliate and so on. I use MetaTrader, ONLY because I like it.

I find this program user-friendly, flexible and powerfull, and "not a monster". Also, it is free (compare to other packages of this class).

The only (minor) problem is that it is not always easy to find the dealer using MT in your area. Then, when you do a research, you may find couple of brockers, with screenshots on their web sites, that look suspiciously familiar. Yes, they use MetaTrader, but they don't call it MetaTrader!

I have asked for clarification at the company's forum, and they have told me, that they don't reveal brockers using their services. Very strange.

One of the brockers that is not hiding the fact they use MT, is Alpari. They will allow you to open a Demo account, so that you can trade in a real time, but without risking your money.

Warning!
I have ONLY used services of Alpari to trade on a Demo account. I do not know how reliable they are when it comes to money. You can start your own research at Internet forums.

Finally, if you do not like the MT, you can probably follow the example below using TS, MS or some other trading platform. This is just an example.

Our MT-based trading system will include two files, the indicator and an expert. This is the way they call it in MQL (scripting language of MT), and I am going to follow this naming convention.

The indicator implements the neural network and draws a chart. An expert takes these data and does trading. As MetaTrader has a "strategy tester", we will be able to test our strategy, to see how good it is.

I will assume, that you are familiar with MQL programming, it is quite close to SLANG and tutorials can be found both at MetaQuotes and Alpari.

Finally, I am using the code structure, that is borrowed from MetaQuotes forum, permission to use it the author of the corresponding posts had granted me permission to use fragments of his code.

Indicator file, Forex_Nn_Ind.mq4

#property copyright "S.Projects"
#property link      "cortex.snowcron.com"

#property indicator_separate_window
#property indicator_buffers 2
#property indicator_color1 Red
#property indicator_color2 Yellow
#property indicator_minimum 0
#property indicator_maximum 1

// indicator parameters
// To do: read parameters, including weights, from a description file

int nNocInterval = 12;
double dNocRange = 0.012;
int nNocMa = 5;
int nOutLag = 2;

int nLayers = 3;
int arrNeurons[3] = { 17, 5, 1 };
int nNumOfLags = 17;
int arrLags[17] = 
    { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };

double arrWeights_0[305];
double arrWeights_1[90];
double arrWeights_2[6];

// Layer: 0
arrWeights_0[0] = 0.376436;
arrWeights_0[1] = 0.690657;
arrWeights_0[2] = 0.512335;
arrWeights_0[3] = 0.786179;
arrWeights_0[4] = 0.671377;
arrWeights_0[5] = 0.614279;
arrWeights_0[6] = 0.539750;
arrWeights_0[7] = 0.820380;
arrWeights_0[8] = 0.750566;
arrWeights_0[9] = 0.707890;
arrWeights_0[10] = 0.396094;
arrWeights_0[11] = 0.725720;
arrWeights_0[12] = 0.555349;
arrWeights_0[13] = 0.395257;
arrWeights_0[14] = 0.227280;
arrWeights_0[15] = 0.128274;
arrWeights_0[16] = 0.289072;
arrWeights_0[17] = 0.387067;
arrWeights_0[18] = 0.662450;
arrWeights_0[19] = 1.019812;
arrWeights_0[20] = 0.761206;
arrWeights_0[21] = 0.984280;
arrWeights_0[22] = 0.878235;
arrWeights_0[23] = 0.829384;
arrWeights_0[24] = 0.786234;
arrWeights_0[25] = 1.189799;
arrWeights_0[26] = 1.112219;
arrWeights_0[27] = 1.069424;
arrWeights_0[28] = 0.640762;
arrWeights_0[29] = 0.730670;
arrWeights_0[30] = 0.067006;
arrWeights_0[31] = -0.320953;
arrWeights_0[32] = -0.600880;
arrWeights_0[33] = -0.371686;
arrWeights_0[34] = 0.261142;
arrWeights_0[35] = 0.063323;
arrWeights_0[36] = 0.413053;
arrWeights_0[37] = 0.737687;
arrWeights_0[38] = 0.555759;
arrWeights_0[39] = 0.815068;
arrWeights_0[40] = 0.692078;
arrWeights_0[41] = 0.630406;
arrWeights_0[42] = 0.556191;
arrWeights_0[43] = 0.846380;
arrWeights_0[44] = 0.763236;
arrWeights_0[45] = 0.715549;
arrWeights_0[46] = 0.390297;
arrWeights_0[47] = 0.705948;
arrWeights_0[48] = 0.482014;
arrWeights_0[49] = 0.291501;
arrWeights_0[50] = 0.101741;
arrWeights_0[51] = 0.048724;
arrWeights_0[52] = 0.280508;
arrWeights_0[53] = 0.428389;
arrWeights_0[54] = -6.065296;
arrWeights_0[55] = 0.559039;
arrWeights_0[56] = 1.732407;
arrWeights_0[57] = 2.446417;
arrWeights_0[58] = 2.226664;
arrWeights_0[59] = 1.043498;
arrWeights_0[60] = -0.892194;
arrWeights_0[61] = 0.538670;
arrWeights_0[62] = 1.696599;
arrWeights_0[63] = 2.895421;
arrWeights_0[64] = 1.936742;
arrWeights_0[65] = 0.857415;
arrWeights_0[66] = 1.777173;
arrWeights_0[67] = 1.489486;
arrWeights_0[68] = 0.990452;
arrWeights_0[69] = -0.312611;
arrWeights_0[70] = -2.485575;
arrWeights_0[71] = 5.784152;
arrWeights_0[72] = 0.691233;
arrWeights_0[73] = 1.003065;
arrWeights_0[74] = 0.746760;
arrWeights_0[75] = 0.973191;
arrWeights_0[76] = 0.868018;
arrWeights_0[77] = 0.821039;
arrWeights_0[78] = 0.770604;
arrWeights_0[79] = 1.139881;
arrWeights_0[80] = 1.057454;
arrWeights_0[81] = 1.004390;
arrWeights_0[82] = 0.595049;
arrWeights_0[83] = 0.733458;
arrWeights_0[84] = 0.177670;
arrWeights_0[85] = -0.170966;
arrWeights_0[86] = -0.443584;
arrWeights_0[87] = -0.345470;
arrWeights_0[88] = 0.135814;
arrWeights_0[89] = -0.010206;
arrWeights_0[90] = 0.585687;
arrWeights_0[91] = 1.040471;
arrWeights_0[92] = 0.778059;
arrWeights_0[93] = 0.994592;
arrWeights_0[94] = 0.882501;
arrWeights_0[95] = 0.819930;
arrWeights_0[96] = 0.783313;
arrWeights_0[97] = 1.225092;
arrWeights_0[98] = 1.138965;
arrWeights_0[99] = 1.112052;
arrWeights_0[100] = 0.667895;
arrWeights_0[101] = 0.725504;
arrWeights_0[102] = -0.042205;
arrWeights_0[103] = -0.470107;
arrWeights_0[104] = -0.758346;
arrWeights_0[105] = -0.356139;
arrWeights_0[106] = 0.477461;
arrWeights_0[107] = 0.159831;
arrWeights_0[108] = 0.465873;
arrWeights_0[109] = 0.827723;
arrWeights_0[110] = 0.635881;
arrWeights_0[111] = 0.876557;
arrWeights_0[112] = 0.751904;
arrWeights_0[113] = 0.689548;
arrWeights_0[114] = 0.624036;
arrWeights_0[115] = 0.943642;
arrWeights_0[116] = 0.844483;
arrWeights_0[117] = 0.793347;
arrWeights_0[118] = 0.439269;
arrWeights_0[119] = 0.710473;
arrWeights_0[120] = 0.362673;
arrWeights_0[121] = 0.107347;
arrWeights_0[122] = -0.123129;
arrWeights_0[123] = -0.079149;
arrWeights_0[124] = 0.300953;
arrWeights_0[125] = 0.333110;
arrWeights_0[126] = 36.878339;
arrWeights_0[127] = -34.157785;
arrWeights_0[128] = 13.570499;
arrWeights_0[129] = -9.445193;
arrWeights_0[130] = 5.906855;
arrWeights_0[131] = -2.631383;
arrWeights_0[132] = -3.466850;
arrWeights_0[133] = 1.387212;
arrWeights_0[134] = 1.168750;
arrWeights_0[135] = -1.252440;
arrWeights_0[136] = -4.167107;
arrWeights_0[137] = 7.462264;
arrWeights_0[138] = 4.218665;
arrWeights_0[139] = -10.616881;
arrWeights_0[140] = 2.816792;
arrWeights_0[141] = -0.854816;
arrWeights_0[142] = 0.465124;
arrWeights_0[143] = 3.805423;
arrWeights_0[144] = 17.371809;
arrWeights_0[145] = -10.758329;
arrWeights_0[146] = 4.288758;
arrWeights_0[147] = -3.300900;
arrWeights_0[148] = -1.387066;
arrWeights_0[149] = -0.545407;
arrWeights_0[150] = -0.627846;
arrWeights_0[151] = 0.359405;
arrWeights_0[152] = -0.910465;
arrWeights_0[153] = 1.672222;
arrWeights_0[154] = -1.087077;
arrWeights_0[155] = 1.711690;
arrWeights_0[156] = 1.892743;
arrWeights_0[157] = 1.370835;
arrWeights_0[158] = -1.074922;
arrWeights_0[159] = -2.069598;
arrWeights_0[160] = 2.132245;
arrWeights_0[161] = 3.323435;
arrWeights_0[162] = 3.192245;
arrWeights_0[163] = 0.722216;
arrWeights_0[164] = 0.595036;
arrWeights_0[165] = 2.562026;
arrWeights_0[166] = 4.118245;
arrWeights_0[167] = 2.533825;
arrWeights_0[168] = 0.661547;
arrWeights_0[169] = -0.522384;
arrWeights_0[170] = -0.923482;
arrWeights_0[171] = -0.744284;
arrWeights_0[172] = -0.531758;
arrWeights_0[173] = -1.737114;
arrWeights_0[174] = -0.894963;
arrWeights_0[175] = 0.335197;
arrWeights_0[176] = 2.767129;
arrWeights_0[177] = 0.169424;
arrWeights_0[178] = -1.868230;
arrWeights_0[179] = 2.057215;
arrWeights_0[180] = 0.408584;
arrWeights_0[181] = 0.730536;
arrWeights_0[182] = 0.549013;
arrWeights_0[183] = 0.810092;
arrWeights_0[184] = 0.687577;
arrWeights_0[185] = 0.626130;
arrWeights_0[186] = 0.551439;
arrWeights_0[187] = 0.839836;
arrWeights_0[188] = 0.758454;
arrWeights_0[189] = 0.711269;
arrWeights_0[190] = 0.388133;
arrWeights_0[191] = 0.706349;
arrWeights_0[192] = 0.491137;
arrWeights_0[193] = 0.305519;
arrWeights_0[194] = 0.119262;
arrWeights_0[195] = 0.059367;
arrWeights_0[196] = 0.280111;
arrWeights_0[197] = 0.432421;
arrWeights_0[198] = 11.382900;
arrWeights_0[199] = -2.355699;
arrWeights_0[200] = 0.800231;
arrWeights_0[201] = -0.812782;
arrWeights_0[202] = -1.725548;
arrWeights_0[203] = 0.506914;
arrWeights_0[204] = 3.685745;
arrWeights_0[205] = -1.570360;
arrWeights_0[206] = -0.678070;
arrWeights_0[207] = -1.385545;
arrWeights_0[208] = 0.665419;
arrWeights_0[209] = -2.403472;
arrWeights_0[210] = 0.348713;
arrWeights_0[211] = 0.948801;
arrWeights_0[212] = 3.943966;
arrWeights_0[213] = 0.609010;
arrWeights_0[214] = -2.954932;
arrWeights_0[215] = 6.570737;
arrWeights_0[216] = 1.098680;
arrWeights_0[217] = 0.457162;
arrWeights_0[218] = 0.047123;
arrWeights_0[219] = 0.314413;
arrWeights_0[220] = 0.270546;
arrWeights_0[221] = 0.271241;
arrWeights_0[222] = 0.444340;
arrWeights_0[223] = 0.685449;
arrWeights_0[224] = 0.881795;
arrWeights_0[225] = 0.904727;
arrWeights_0[226] = 0.538997;
arrWeights_0[227] = 0.274688;
arrWeights_0[228] = -0.107260;
arrWeights_0[229] = -0.182735;
arrWeights_0[230] = -0.297917;
arrWeights_0[231] = -0.722563;
arrWeights_0[232] = -1.004438;
arrWeights_0[233] = 2.114029;
arrWeights_0[234] = 0.625319;
arrWeights_0[235] = 1.073737;
arrWeights_0[236] = 0.794572;
arrWeights_0[237] = 1.012257;
arrWeights_0[238] = 0.909403;
arrWeights_0[239] = 0.851104;
arrWeights_0[240] = 0.819041;
arrWeights_0[241] = 1.284499;
arrWeights_0[242] = 1.213157;
arrWeights_0[243] = 1.195116;
arrWeights_0[244] = 0.731964;
arrWeights_0[245] = 0.733472;
arrWeights_0[246] = -0.119051;
arrWeights_0[247] = -0.574390;
arrWeights_0[248] = -0.865588;
arrWeights_0[249] = -0.405175;
arrWeights_0[250] = 0.494158;
arrWeights_0[251] = 0.142718;
arrWeights_0[252] = 0.443620;
arrWeights_0[253] = 0.790221;
arrWeights_0[254] = 0.604280;
arrWeights_0[255] = 0.852407;
arrWeights_0[256] = 0.728354;
arrWeights_0[257] = 0.666281;
arrWeights_0[258] = 0.596990;
arrWeights_0[259] = 0.903026;
arrWeights_0[260] = 0.809120;
arrWeights_0[261] = 0.758782;
arrWeights_0[262] = 0.416953;
arrWeights_0[263] = 0.708992;
arrWeights_0[264] = 0.415040;
arrWeights_0[265] = 0.186673;
arrWeights_0[266] = -0.028387;
arrWeights_0[267] = -0.027435;
arrWeights_0[268] = 0.289143;
arrWeights_0[269] = 0.376176;
arrWeights_0[270] = 0.377669;
arrWeights_0[271] = 0.691528;
arrWeights_0[272] = 0.513124;
arrWeights_0[273] = 0.786421;
arrWeights_0[274] = 0.670892;
arrWeights_0[275] = 0.613345;
arrWeights_0[276] = 0.538605;
arrWeights_0[277] = 0.819386;
arrWeights_0[278] = 0.749119;
arrWeights_0[279] = 0.706133;
arrWeights_0[280] = 0.394004;
arrWeights_0[281] = 0.723393;
arrWeights_0[282] = 0.551766;
arrWeights_0[283] = 0.390955;
arrWeights_0[284] = 0.222564;
arrWeights_0[285] = 0.125193;
arrWeights_0[286] = 0.287931;
arrWeights_0[287] = 0.397175;
arrWeights_0[288] = 0.390177;
arrWeights_0[289] = 0.704718;
arrWeights_0[290] = 0.524964;
arrWeights_0[291] = 0.793337;
arrWeights_0[292] = 0.673926;
arrWeights_0[293] = 0.614109;
arrWeights_0[294] = 0.538623;
arrWeights_0[295] = 0.821537;
arrWeights_0[296] = 0.747044;
arrWeights_0[297] = 0.702102;
arrWeights_0[298] = 0.386249;
arrWeights_0[299] = 0.712341;
arrWeights_0[300] = 0.526445;
arrWeights_0[301] = 0.357474;
arrWeights_0[302] = 0.183363;
arrWeights_0[303] = 0.099539;
arrWeights_0[304] = 0.282065;
arrWeights_0[305] = 0.431269;

// Layer: 1
arrWeights_1[0] = 0.313367;
arrWeights_1[1] = 0.600233;
arrWeights_1[2] = 0.413307;
arrWeights_1[3] = 0.723853;
arrWeights_1[4] = 0.604417;
arrWeights_1[5] = 0.564724;
arrWeights_1[6] = 0.494233;
arrWeights_1[7] = 0.776964;
arrWeights_1[8] = 0.713700;
arrWeights_1[9] = 0.689037;
arrWeights_1[10] = 0.403412;
arrWeights_1[11] = 0.743660;
arrWeights_1[12] = 0.681661;
arrWeights_1[13] = 0.582135;
arrWeights_1[14] = 0.470205;
arrWeights_1[15] = 0.320594;
arrWeights_1[16] = 0.360557;
arrWeights_1[17] = 0.505358;
arrWeights_1[18] = 0.207009;
arrWeights_1[19] = -0.600315;
arrWeights_1[20] = 0.292799;
arrWeights_1[21] = -0.486397;
arrWeights_1[22] = -0.698483;
arrWeights_1[23] = -0.466675;
arrWeights_1[24] = 0.161680;
arrWeights_1[25] = 1.267072;
arrWeights_1[26] = 0.942115;
arrWeights_1[27] = 1.601429;
arrWeights_1[28] = 0.294829;
arrWeights_1[29] = 2.066898;
arrWeights_1[30] = 1.149577;
arrWeights_1[31] = -0.608109;
arrWeights_1[32] = 0.236174;
arrWeights_1[33] = 0.223691;
arrWeights_1[34] = 0.281142;
arrWeights_1[35] = 2.639872;
arrWeights_1[36] = 0.298441;
arrWeights_1[37] = 0.585156;
arrWeights_1[38] = 0.397331;
arrWeights_1[39] = 0.714002;
arrWeights_1[40] = 0.591642;
arrWeights_1[41] = 0.547087;
arrWeights_1[42] = 0.477829;
arrWeights_1[43] = 0.768086;
arrWeights_1[44] = 0.709662;
arrWeights_1[45] = 0.679375;
arrWeights_1[46] = 0.387499;
arrWeights_1[47] = 0.736123;
arrWeights_1[48] = 0.665259;
arrWeights_1[49] = 0.564510;
arrWeights_1[50] = 0.453956;
arrWeights_1[51] = 0.305558;
arrWeights_1[52] = 0.345016;
arrWeights_1[53] = 0.508310;
arrWeights_1[54] = -0.677255;
arrWeights_1[55] = -1.032676;
arrWeights_1[56] = -0.744642;
arrWeights_1[57] = -0.925155;
arrWeights_1[58] = -1.197128;
arrWeights_1[59] = -0.992372;
arrWeights_1[60] = -0.855172;
arrWeights_1[61] = 7.016197;
arrWeights_1[62] = 5.226784;
arrWeights_1[63] = 2.411577;
arrWeights_1[64] = -0.733721;
arrWeights_1[65] = 3.402664;
arrWeights_1[66] = 1.729731;
arrWeights_1[67] = -0.991801;
arrWeights_1[68] = -0.815011;
arrWeights_1[69] = -0.677321;
arrWeights_1[70] = -0.693829;
arrWeights_1[71] = 9.138765;
arrWeights_1[72] = -0.246490;
arrWeights_1[73] = -0.391846;
arrWeights_1[74] = -0.347720;
arrWeights_1[75] = -1.096698;
arrWeights_1[76] = -0.001496;
arrWeights_1[77] = -0.693606;
arrWeights_1[78] = -0.468658;
arrWeights_1[79] = 6.178259;
arrWeights_1[80] = 4.306803;
arrWeights_1[81] = 2.409460;
arrWeights_1[82] = -0.336563;
arrWeights_1[83] = 2.374341;
arrWeights_1[84] = 1.504236;
arrWeights_1[85] = -0.726152;
arrWeights_1[86] = -0.426187;
arrWeights_1[87] = -0.254840;
arrWeights_1[88] = -0.293797;
arrWeights_1[89] = -1.892730;

// Layer: 2
arrWeights_2[0] = -0.626160;
arrWeights_2[1] = 1.717378;
arrWeights_2[2] = -0.485501;
arrWeights_2[3] = 2.751261;
arrWeights_2[4] = 2.323994;
arrWeights_2[5] = 2.047616;


double arrOutput_0[17];
double arrOutput_1[5];
double dNnOutput;

double arrPattern[18];

int nRemoveFirst = 200;
double dE = 2.7182818;

// indicator buffers
double arrNocBuffer[];
double arrNnBuffer[];

int nExtCountedBars = 0;

////////////////////////
int init()
{
    // drawing settings
    SetIndexStyle(0, DRAW_LINE);
    SetIndexShift(0, 0);
    SetIndexEmptyValue(0, 0.0);
        
    SetIndexStyle(1, DRAW_LINE);
    SetIndexShift(1, 0);
    SetIndexEmptyValue(1, 0.0);

    IndicatorDigits(4);
        
    string strIndicatorShortName = "forex_nn";
    IndicatorShortName(strIndicatorShortName);

    // indicator buffers mapping
    SetIndexBuffer(0, arrNocBuffer);
    SetIndexBuffer(1, arrNnBuffer);    

    return(0);
}
////////////////////
int start()
{
    nExtCountedBars = IndicatorCounted();
    if(nExtCountedBars < 0) 
        return(-1);

    // last counted bar will be recounted
    if(nExtCountedBars > 0) 
        nExtCountedBars--;
        
    Noc();        
    ApplyNn();  

    return(0);
}
///////////////////
// For the selected period:
// CLV = ((Close - Low) - (High - Close)) / (High - Low) 
void Noc()
{
    int nPos = Bars - nExtCountedBars;

    while(nPos > 0)
    {
        if(nPos > Bars - nRemoveFirst)
        {
            arrNocBuffer[nPos] = 0.5;
            nPos--;
            continue;
        }

        double dClose = Close[nPos];
        double dLow = Low[ArrayMinimum(Low, nNocInterval, nPos)]; 
        double dHigh = High[ArrayMaximum(High, nNocInterval, nPos)]; 

        arrNocBuffer[nPos] = 0.1;

        if(dHigh - dLow > dNocRange)
            arrNocBuffer[nPos] = (((dClose - dLow) - (dHigh - dClose)) 
                / (dHigh - dLow)) / 2 + 0.5; 
        else
            arrNocBuffer[nPos] = (((dClose - dLow) - (dHigh - dClose)) 
                / dNocRange) / 2 + 0.5; 

        nPos--;
    }
    
    if(nNocMa > 1)
        Ema(arrNocBuffer);
}
///////////////////
void Ema(double& arr[])
{
    double dPr = 2.0 / (nNocMa + 1);

    int nPos = Bars - nExtCountedBars;
    
    while(nPos > 0)
    {
        if(nPos == Bars - 2) 
            arrNocBuffer[nPos + 1] = arr[nPos + 1];

        arrNocBuffer[nPos] = arr[nPos] * dPr + arrNocBuffer[nPos + 1] 
            * (1 - dPr);
        nPos--;
    }
}
///////////////////
void ApplyNn()
{
    int nPos = Bars - nExtCountedBars - 2;

    while(nPos > 0)
    {
        if(nPos > Bars - nRemoveFirst)
        {
            arrNnBuffer[nPos] = 0.5;
            nPos--;
            continue;
        }

        arrNnBuffer[nPos] = 0.5;
        
        for(int nLagNo = 0; nLagNo < nNumOfLags; nLagNo++)
            arrPattern[nLagNo] 
                = arrNocBuffer[nPos + arrLags[nLagNo] + nOutLag];

        for(int nLayer = 0; nLayer < nLayers; nLayer++)
        {
            // Absolute index of the weight in an array
            int nWeightIdx = 0;

            for(int nNeuron = 0; nNeuron < arrNeurons[nLayer]; 
                nNeuron++)
            {
                double dLinearCombiner = 0;
    
                int nInputs = arrNeurons[nLayer];
                if(nLayer != 0)
                    nInputs = arrNeurons[nLayer - 1];
                
                for(int nInput = 0; nInput < nInputs; nInput++)
                {
                    double dInput;
                    double dWeight;
                    switch(nLayer)
                    { 
                        case 0:
                        {
                            dInput = arrPattern[nInput];
                            dWeight = arrWeights_0[nWeightIdx];
                        }
                        break;
                        
                        case 1:
                        {
                            dInput = arrOutput_0[nInput];
                            dWeight = arrWeights_1[nWeightIdx];
                        }
                        break;
                        
                        default:
                        {
                            dInput = arrOutput_1[nInput];
                            dWeight = arrWeights_2[nWeightIdx];
                        }
                        break;
                    }

                    dLinearCombiner += dWeight * dInput;
                
                    nWeightIdx++;
                }        

                switch(nLayer)
                {
                    case 0:
                        dWeight = arrWeights_0[nWeightIdx]; break;
                    case 1:
                        dWeight = arrWeights_1[nWeightIdx]; break;
                    default: 
                        dWeight = arrWeights_2[nWeightIdx]; break;
                }

                dLinearCombiner += (-1) * dWeight;
                nWeightIdx++;

                double dActivation = Activation(dLinearCombiner, 0);

                switch(nLayer)
                {
                    case 0:
                        arrOutput_0[nNeuron] = dActivation; break;
                    case 1:
                        arrOutput_1[nNeuron] = dActivation; break;
                    default: 
                        dNnOutput = dActivation; break;
                }

            }    // for all neurons

        }    // for all layers

        arrNnBuffer[nPos] = dNnOutput;

        nPos--;
            
    }    // for all patterns
}
///////////////////
double Activation(double u, int nType)
{
    //double dEx = TANH(u / 2);
    
    double dPow = MathPow(dE, 2 * u / 2);
    double dEx = (dPow - 1) / (dPow + 1);
    if(dEx == 1 || dEx == -1)
        dEx = dEx * 0.999999999;
    
    // 0 - sigmoid, 1 - tangent
    if(nType == 0)
        return((dEx + 1) / 2);

    return(dEx);
}

The code should look familiar, all I did was re-writing it, using slightly different language syntax of MQL.

This indicator has two buffers, and draws two lines, one for the original NOC, and one for the NN-predicted NOC. For trading, you don't have to draw both indicator lines, of course (see MQL tutorials to learn how to do it), but I have decided to show them together, so you can compare.

Expert file, Forex_Nn_Expert.mq4

#property copyright "S.Projects"
#property link "cortex.snowcron.com"

extern double dBuyLevel;
extern double dSellLevel;
extern double dStopLoss;
extern double dTrailingStop;

// ------

double dTakeProfit = 0;

datetime timePrev = 0;
int nBars;
int nSlip = 5;

double dLotSize = 0.1;

int nMagic = 0;

string strExpert = "forex_nn";

// ------

int init ()
{
    nBars = Bars;

    if(Symbol() == "EURUSD" && Period() == 60)
    {
        if(!IsTesting())                    
        {
            dBuyLevel = 0.21;        
            dSellLevel = 0.9;
                                    
            dStopLoss = 200 * Point;        
            dTrailingStop = 200 * Point;
        }
        else
        {
            dStopLoss = dStopLoss * Point;
            dTrailingStop = dTrailingStop * Point;
        }
        
        nMagic = 0;
    }

    return(0);
}
// ------
int deinit()
{
    return(0);
}

// ------

int start()
{
    if(Bars < 200)
        return(0);
    
    if(!IsBarEnd())
        return(0);
    
    // ------
    
    double dNoc = iCustom(NULL, 0, "_Forex_Nn_Ind", 1, 1);
    double dNocPrev = iCustom(NULL, 0, "_Forex_Nn_Ind", 1, 2);
        
    for(int nCnt = OrdersTotal() - 1; nCnt >= 0; nCnt--)
    {
        OrderSelect(nCnt, SELECT_BY_POS, MODE_TRADES);
        if(OrderMagicNumber() == nMagic)
        {
            if(OrderType() == OP_BUY)
            {         
                if(dNocPrev >= dSellLevel && dNoc <= dSellLevel)
                {
                    OrderClose(OrderTicket(), 
                        OrderLots(), Bid, nSlip, Aqua);
                    break;
                }
            }
            else if(OrderType() == OP_SELL)
            {
                if(dNocPrev <= dBuyLevel && dNoc >= dBuyLevel)
                {
                    OrderClose(OrderTicket(), 
                        OrderLots(), Ask, nSlip, OrangeRed);
                    break;
                }
            }
        }
    }

    int nNumOfOpenedOrders = 0;
    for(nCnt = OrdersTotal() - 1; nCnt >= 0; nCnt--)
    {
        OrderSelect(nCnt, SELECT_BY_POS, MODE_TRADES);
        if(OrderMagicNumber() == nMagic)
            nNumOfOpenedOrders++;
    }

    if(nNumOfOpenedOrders == 0)
    {
        if(dNocPrev <= dBuyLevel && dNoc >= dBuyLevel) 
        {
            OrderSend(Symbol(), OP_BUY, dLotSize, Ask, 
                nSlip, Ask - dStopLoss, 0, strExpert, 
                nMagic, 0, Aqua);
        }
        else if(dNocPrev >= dSellLevel && dNoc <= dSellLevel) 
        {
            OrderSend(Symbol(), OP_SELL, dLotSize, Bid, 
                nSlip, Bid + dStopLoss, 0, strExpert, 
                nMagic, 0, OrangeRed);
        }
    }
        
    // ------

    ModifyOrders();
    
    // ------
    
    return(0);
}


// ------

void ModifyOrders()
{
    for(int nCnt = 0; nCnt < OrdersTotal(); nCnt++)
    {
        OrderSelect(nCnt, SELECT_BY_POS, MODE_TRADES);
        if(OrderMagicNumber() == nMagic)
        {
            if(OrderType() == OP_BUY)
            {
                if(OrderStopLoss() < Bid - dTrailingStop - 5 * Point)
                {
                    OrderModify(OrderTicket(), OrderOpenPrice(), 
                        Bid - dTrailingStop, OrderTakeProfit(), 0, Aqua);
                    break;
                }
            }
            
            if(OrderType() == OP_SELL)
            {
                if(OrderStopLoss() > Ask + dTrailingStop + 5 * Point)
                {
                    OrderModify(OrderTicket(), OrderOpenPrice(), 
                        Ask + dTrailingStop, OrderTakeProfit(), 
                        0, OrangeRed);
                    break;
                }
            }
        }
    }
}

// ------

bool IsBarEnd()
{
    bool bIsBarEnd = false;
    if(nBars != Bars)
    {
        bIsBarEnd = true;
        nBars = Bars;
    }
    
    return(bIsBarEnd);
}


The logic of this expert is slightly different from the one I use in Cortex tests, particularly, the way it works with trailing stops in a different way. I simply took an expert from the MetaQuotes forum (with author's permission), and modified it to use my NN. The result only improved.

Another difference, that you should know about, is the way MT performs testing. It amy, in some cases, be more accurate, then one we did (we did the worse case scenario). Of course, you can always to change the SLANG script from the examples above, to implement any logic you want.

The result of our testing in MT is a bit better, then in Cortex, due to all these reasons: Maximal drawdown (%) 1042.95 (48.6%)

Keep in mind,that MT calculates the DD in a different way. I still think, that my way is better. If we calculate it "my" way, it will be 1.04 (1042.95 / 1000), which is very close to results of our test in SLANG script (0.9 - 1.2).

Here is a screenshot of a MT tester, the test is done for the date range between 30.01.2003 and 17.09.2004, same range was used to do SLANG testing. (Let me remind AGAIN, that this text is NOT about creating a perfect trading system, it is about using Cortex-generated NNs).

In should be especially noted, that no additional optimization had been performed using MetaTrader's optimizer. We have just plugged our MTS (mechanical trading system) in, and it worked as expected.


That is it. You can now create Cortex Neural Network, optimize it to do trading, and to port it to the trading platform of your choice.

To get fully enabled version of the Cortex Built-in Scripting engine, you need to register.

And do not forget about the navigation bar at the bottom of the page - it will take you to other pages, some of them using neural networks, and all of them facinating ;)



Free intros:

State of Power Tutorial

Hypnosis Tutorial

NLP Tutorial

Working with the Future Tutorial

Hypnotic Inductions

Working with Money

Working with Habits

Manipulation Tutorial


Karate online tutorial

Chi Gun online tutorial

Tai Chi 24 forms online tutorial

Tai Chi 40 forms online tutorial

Tai Chi 108 forms online tutorial

Tai Chi Chi Gun 18 forms online tutorial

Chi Gun (Dao In) Heart and Blood Vessels 8 forms online 
tutorial

Chi Gun (Dao In) Kidneys 8 forms online tutorial

Joints Gymnastics, Chi Gun warm-up online tutorial

Chi Gun tao of Dr. Shi online tutorial


Sore back treatment

Headache Relief Pressure Points Tutorial


Stock trading - technical analysis

Making a small profitable web site

Neural Networks

Flow Charts and decision trees for web sites and 
presentations

Another powerfull Flow Charts Designer

Site Downloader

Thumbnails Generator

Calendar Creator

Touch Typing


Photo gallery


Jewelry: Pearl. How to choose, price estimation,
					take care, history, legends, classification Pearl. How to choose, price estimation, take care, history, 
legends, classification Artifficial Pearl. Links to: How to choose pearl, estimate price, 
take care, history, legends, classification Buying Pearl. Links to: How to choose, estimate price, take care, 
history, legends, classification Jewelry: Pearl. Taking care. Links to: How to choose, estimate 
price, history, legends, classification Jewelry: Classification of Pearl. Links to: How to choose, 
estimate price, take care, history, legends Jewelry: Cultivated Pearl. Links to: How to choose, estimate 
price, take care, history, legends, classification Jewelry: Pearl. History and legends. Links to: How to choose, 
estimate price, take care, classification Jewelry: Pearl. Price estimation. Links to: How to choose, take 
care, history, legends, classification Jewelry: Pearl. What is it? Links to: How to choose, estimate 
price, take care, history, legends, classification


NLP, Hypnosis, Power, Manipulation Tai Chi, Chi Gun Neural Networks
Joints Gymnastics Photo album generator
Habit Management Karate tutorial Flow charts for Presentations and Web

Sore back treatment Calendar Creator
Building a small profitable site
Another powerfull Flow Charts Designer
Profitable web site in 9 days Web programming : Perl, XML Site Downloader
Web positioning Shareware Directory


Touch Typing
Stock and FOREX Trading Stock Photo Gallery

Links Home


Neural Networks
Neural networks are modelled after the brain (not necessarily a human brain) and provide the learning algorythm that does not require any knowkedges of the formulas for the process being researched, instead, it is based on the familiarity and pattern recognition.

data mining
The neural networks have unique ability to extract the relevant information from the noisy and incomplete data. In terms of machine learning, it is a valuable feature.

machine learning
The machine learning using the neural networks consists of two stages (repeated many times). First, we present the system with the input data and obtain the output. Second, we adjust the system, to make output closer to what it should be.
The first stage is refered as feedforward, the second - as a backpropagation.

neural net methodology
On this site you will find free tutorial, covering basics of the Feedforward Backpropagation neural networks.

knowledge discovery
Knowledge based algorythms are often considered as something opposite to the data based algorythms. However, the Neural Networks are both - they store the information, as a internal structure of a network, making possible to recognize the patterns, including those, that the system never seen before.

speech recognition neural networks
The recognition of speech is an example of the problem that neural networks can solve. The patterns are noisy, they are different from time to time and from person to person, and they are not presented in a form that can be used by algorythm based programs.

Stock market neurals network
There are endless discussions on stock market prediction using neural networks, and the argument are pretty much the same as in discussions about the technical analysis in general.
Neural networks can be used to predict the stock price, to a reasonable extent, they are valuab;e part of modern automated trading systems.

neural networks extrapolation
Neural Networks can work with the data they never seen before, and therefore, can be used for extrapolation. The problem appears when the data go outside the range for which the network was trained. There are tips, described in neural networks introduction to handle this problem.

neural networks stock prediction
The Cortex neural networks software that you can download from this site comes with the network trained to predict the price of a stock.
Usually, this kind of networks are working well only for limited time range, and have to re re-trained to handle new data.

neural networks download
Download the Cortex neural networks software from this site.

free neural networks software
The Cortex neural networks software is feature limited, however you can use even a free version to solve many practical tasks.

neural networks introduction
A free introduction to neural networks is available on this site. It explains the algorythm of the so called Feedforward Backpropagation networks, the most commonly used and probably the most powerfull.

tutorial on neural networks
On this site you will find both the tutorial on the neural nets and the user guide for the Cortex program, the easy to use and powerfull neural networks software.

neural networks book
Check out our list of recommended books of Neural Networks and related subjects of data mining.

neural network course
The Introduction to Neural Networks, available from this site, is a good (and free) start, if you want to learn the algorythms of Feedforward Backpropagation neural networks.

Feedforward
The word Feedforward stands for the first stage of the Neural Network learning process, when the system is presented with the input data.

Backpropagation
The word Backpropagation stands for the second stage of the Neural Network learning process, when the system is adjusted, to make its output closer to what it should be.

network neural type
There are few types of Neural Networks and many variations of the types. On this site you will find the information and software on Feedforward Backpropagation Neural Networks.

neural network free software
Download and try the Cortex program, the easy to use and powerfull neural networks software.

invest network neural
The neural networks are used in the investment analysis, particularly in the stock prediction. The Neural Networks tutorial, available from this site, has an example of such a network.


fuzzy network neural
Fuzzy logic deals with the rules taht are flexible or not constant. That is exactly how the Neural Network works, by recognizing "fuzzy", noisy or incomplete data.

stock market neural network
The Cortex neural networks software works with stock data as with any other data. The Trader stock trading simulator can use these neural networks to generate trading signals.

feed forward network neural
The Feedforward stage of Neural Network algorythm takes the input data and presents it to the first layer of the neurons. The output of the first layer is presented to the second layer, and so on.

trend data mining
Knowing the current trend provides us with some ability to predict the future price change. It is possible to use Neural Networks for this task. Alternatively, check out the Trader stock trading simulator.

basics data mining
Using some kind of tools to extract data, satisfying to some criteria, from the large amount of raw data.

stock data mining
Using the Neural Networks you can detect tendencies in a stock price, even if they are small or hidden. It requires some work on the side of data preparation.

data knowledge mining
The Neural Networks can be used to filter the data, in order to extract the relevant data.

advantage data mining
See the list of recommended books

neural network book
See the list of recommended books for a list of the most relevant books on Neural Networks and related totics.

neural network tool box
The Cortex neural networks software provides you with the easy to use interface, allowing to create, teach and use the Neural Networks with your data.

neural network financial
The algorythm can be used in any area, where the data can be presented in a tabular form.

data mining article
See the list of recommended books

data mining neural network
As the computer systems become faster, the value of the NN as the data mining tool will only increase.

future neural network
The ability ot the Neural Networks to predict the future is limeted, as one can expect, to the common sence. For example, if the way the market is treating the stock changes, the old network may become useless, and a new one will have to be trained, using new data.

data mining case study
See the list of recommended books for the data mining tutorials and case studies.

algorithm backpropagation
During the Backpropagation stage of the neural network training, the output layer of neurons is adjusted, to make the output closer to what it need to be. Then the layer before is adjusted, based on the adjusted last layer and so on.

application data mining
The Cortex neural networks software can be used to create the neural networks, that can become part of the custom data mining tools.

data mining example
See the list of recommended books for the data mining tutorials and examples.

neural network artificial intelligence
Neural Networks are considered important step in our understanding of the way human brain works.

data mining papers
As large amounts of data are usually processed during the data mining, the neural networks applications may become the bottle neck. The hardware is available to make it fast, as a mater of fact, hardware based NN are fast enough for almost any task.

network neural programming
The software that you find on this site allows seamless neural network programming, it includes both creating neural networks and teaching them using the feedforward backpropagation algorythm.

stock neural network
One of the most demanded areas of the neural networks prediction is predicting stock prices, as this task is one of the most difficult to formalize, and neural networks do not require the formal task description.

fuzzy network neural
To some extent, neural networks DO implement fuzzy logic, as they can produce results, that are close to correct ones, based on samples, that are close to familiar ones.

boosting data mining
Data mining can be boosted, in some cases, by applying neural network algorythms. It may involve predicting optimal ways to a place, where the information is located, rather than brutal search, however, this approach shouls be used with caution, as with neural networks, there is always a risk of finding a local minimum solution, instead of a global one.

neural network free software
On this site, you will find a feature-limited free trial version of the software.

stock market neural network
Stock market predictions are not as easy, as people usually think. One of the most important reasons for this to be true is the way data are organized. Generally, we need to know exactly what we use as an input and what we are expecting as an output, rather than feeding the network the raw data, hoping to get meaningfull results.

invest network neural
Before investing your time in the solution, based on neural networks, do a homework to figure out, what the exact nature of a task is. The nn can do anything, but WHAT to do - this is a human's decision.

data decision mining tree
As in any decision making process, there may be a way to speed up the data decision tree crawling, by applying the neural network algorythms.

bots data mining
As for the data mining bots, the use of neural networks is limited, if any. There is, of course, always a chance of adopting a new technology in a new way.

data mining benefit
The main benefit of data mining approach is in reducing the dimension of a task, making it more manageable, and less complex. Often, most important moving forces are hidden behind the scene, and neural networks are very good in finding them.

data mining resume
What is he data mining? Looking up for the relevant information in a large multidimentional data continuum, with little, if any, advance knowlege of the dependencies the data have.

neural network financial
Financial area is one where neural computations are used heavily. There are many areas where they provide signifficant help, and some areas, where they are the only solution.

neural network book
There are many books on the general neural networks theory available at Amazon, as well as few articles on this site, both general-purpose basic introductions, and specialized ones.

data mining article
There are many data mining articles and books, available in the Internet.

data mining neural network
The importance of neural networks in data mining is in their ability to find non-evident dependencies, thereby reducing dimension and increasing the quality of an answer.

data mining storage
As data mining storage, the object databases of hypercubes are often used.

data mining job
Neural networks can be used as a part of a data mining software. Their job in this tast may differ from identifiying binary or stored in tables data patterns, to suggesting the most promicing approach to a search.

data data mining mining
As a data mining algorythm per se, neural computations are just a tool. Using it depends on the person, specifying the mining algorythms.

data mining case study
To get a better idea of possible scenarios of data mining usage, we suggest performing the search in the Internet.

data mining warehouse
Data mining warehouses often use their own software, optimized, depending of the task it performs. For example, in face recognition, neural networks can be used, as a low level algorythm, while the generic database is used to store and maintain records.

algorithm backpropagation
Backpropagation algorythm is one of the most promicing. It uses the data pattern (so called supervised learning) to compute the signal (during the feedforward part of the learning), and then uses the error, to adjust the weights of the neurons (during the backpropagation part).

data mining concept
The overall concept of the data mining is based on the assumption, that the multi dimensional data continuum can be cross-cut by the (again - multi dimensional) surface, and that the intersection will contain a result we are after.

neural stock
As a demonstration of the complexity of the stock trading using neural networks, we provided, on this site, a simple article, that is attempting to do a small case study, examining the pitfalls of the task, as they arrive.

application data mining
"Cortex", the neural networks package, may, to some extent, be considered as a data mining tool.

data mining definition
Data mining requires the search (mining) algorythm to be provided, without such definition, it will not be usefull. Here, you will find a small article about stock price prediction. We start with a simple (and incorrect) task, and arrive to a more or less correct one.

data mining example
In that example (see the previous paragraph), it is interesting to mention, that in order to solve the problem, we had to re-state it. Indeed, we do not care about the stock price, we need trading signals instead!

neural network artificial intelligence
Some most promicing solutions in the area of artifficial intelligence use neural networks. This is not surprising, as the very idea of neural networks is copied from the structure of brain.

data mining papers, data mining sas
Data mining information is available online, both as whitepapers, articles and reviews.

fuzzy neural system
Neural networks do implement some kind of the fuzzy logic, though it is not the fuzzy logic as it is formally defined.

classification data mining
Classification is another area, where neural networks shine. It is possible to write both supervised classification software, for example, using feedforward backpropagation algorythms, and not-superwised automatic classification, where the network will break the data into the cetegories.

neural network for pattern recognition
Pattern recognition is one of the most important areas of neural networks application. The pattern may be provided as a text or binary (sound, image) sequence, and similarity between it and previously seen patterns used for classification.

data mining tutorial
This site is not intended as the data mining tutorial, except for a small subset, related to neural networks. You can find all the necessary information in the Internet.

neural network tutorial
On this site, you will find few tutorials, covering different aspects of the neural computations. First, these are introductions, and second, in-depth stock and FOREX trading.

data mining training
The most important - and the most confusing - part of the data mining using neural networks, is not in thaining itself, but in defining the algorythms, inputs and outputs, as well as finding the inner logic in data.

introduction to neural network
The articles on this site may be used as a good basic introduction to neural network computations.

data mining multimedia
When data mining is performed on a database, using the criteria that are not part of the index, neural networks can be used to do a recognition task. It is particularly true with analog, multimedia records.

introduction data mining
For an introductory data mining information, the best source is probably the Amazon book collection.

data mining on the web
To perform data mining on the web, we can use so called bots. Collected information can then be treated as any database, and data mining applies, as always. Also, the neural networks may be used to figure out ways of accelerating the web crawling.

neural network computer
To perform neural network computation faster, high degree of parallelism is required, ideally, every neuron should have its own thread. To implement it, special hardware exists, called neuristors.

data mining internet
The Internet is a very promicing media for data mining. As the mater of fact, the very nature of the Internet, created by people, and therefore, structured according to some intelligent logic, allows us to look for hidden important dependencies in it. For example, Google uses this approach to find "true meaning" of words and even phrases.

data mining conference
There are many, happening online and offline.

data mining course
There are many, happening online and offline.

neural network hardware
As was mentioned above, some very fast chips are available for neural computations.

matlab neural network
Matlab is one of the general purpose applications, that inclused neural networks block.

data mining data warehouse
Data warehouse software (used for data mining against large data arrays) is available off-the-shelf (do an online research).

business intelligence data mining
The term "business intelligence" is not strict enough. The data mininng can be performed to improve business, and yes, neural networks can be part of it.