void main() { // Remove all text from the text output window OUT_CLEANUP(); // Path for images string strImagePath = "h:\\S_Projects\\CortexPro\\data\\forex_nn\\images\\"; // Currency string strForexName = "EURUSD_H1"; // Save NN file here string strNnPath = "..\\CortexPro\\data\\forex_nn\\evolution\\"; string strNnFileName; // ------------ // Name of EURUSD quotes file, we use one in the // samples directory string strDataFileName = "..\\CortexPro\\data\\samples\\forex\\" + strForexName + ".TXT"; // Name of the generated indicator lag file // Note, that "real" TS should probably use // more than one indicator string strLagFileName = "..\\CortexPro\\data\\forex_nn\\evolution\\" + strForexName + "_evolution.lgg"; // c:\S_Projects\Cortex or ..\Cortex double bIsPathRelative = 1; // Input arrays, to be loaded from quotes file // Technically, we only use Close here 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); // Load time series from quotes file TABLE_LOADER(strDataFileName, bIsPathRelative, 0, "", 0, "", 0, arrDate, 1, arrTime, 2, arrOpen, 3, arrHigh, 4, arrLow, 5, arrClose); // Size of data arrays (they are all equal length) double nCloseArraySize = ARRAY_SIZE(arrClose); // Successful trades vs. failures, used to fine-tune the system // See below double dSuccessRatio; // Maximum drawdown, we add extra punishment to // systems with high volatility double dMaxDrawDown; // Interval, Range, Ma for NOC indicator // Note, that we still use array of parameters, this is // the legacy from earlier forex_nn examples. We can have more // than one set of parameters here, to test all and to find // the best. However, GA can do the same, if we program it // into NN, as we do with SL/TP. array_s arrNocParameters = CREATE_ARRAY_S(0); arrNocParameters[0] = "12,0.012,5"; // NN can produce any SL/TP, we need to make sure they // are within reasonable range double dMinStop = 0.0030; // 30 points double dMaxStop = 0.0250; // 250 points // Number of trades produced by current TS double nTradeNumberBuy = 0; double nTradeNumberSell = 0; double nTradeNumber = 0; // --- // Again, we can ask GA to find optimal number of neurons, // but we keep this approach to make the code as similar // to what was used in forex_nn examples, as possible // Number of neurons in hidden layer ([0] means 0th testing set, // not number of layer array arrNeurons = CREATE_ARRAY(0); arrNeurons[0] = 5; // Lags to be used to produce input. Again, GA can find optimal lags, // but not in this simple example array arrNocLags = CREATE_ARRAY(0); array_s arrStrNocLags = CREATE_ARRAY_S(0); // arrStrNocLags[0] = "8,1,3,6,8,10,12,16,24"; arrStrNocLags[0] = "22,1,2,3,4,6,8,10,12,16,20,24,28,32,36,42,48,54,60,68,76,84,92"; // NOC indicator, and MA-smoothed NOC array arrNoc; // array arrNocSmooth; // First few records cannot be used, because an indicator is not // yet defined there. We pick the number that is guaranteed to be // larger than any lag and any indicator / MA period, then // we do not use these records double nRemoveFirst = 100; // Arrays to keep NN output, created by ApplyNN, used in Test array arrNnBuy = CREATE_ARRAY(0); array arrNnSell = CREATE_ARRAY(0); array arrNnCloseBuy = CREATE_ARRAY(0); array arrNnCloseSell = CREATE_ARRAY(0); array arrNnHold = CREATE_ARRAY(0); array arrNnBuyStopLoss = CREATE_ARRAY(0); array arrNnBuyTakeProfit = CREATE_ARRAY(0); array arrNnSellStopLoss = CREATE_ARRAY(0); array arrNnSellTakeProfit = CREATE_ARRAY(0); // Arrays to keep profits by current NN array arrProfits = CREATE_ARRAY(1); // Sorted array array arrLearnProfits = CREATE_ARRAY(0); array arrTestProfits = CREATE_ARRAY(0); array arrNetworks = CREATE_ARRAY(0); // Temporary arrays, before we sorted the data by profit array arrProfitsTmp = CREATE_ARRAY(0); array arrCorrections = CREATE_ARRAY(0); array arrLearnProfitsTmp = CREATE_ARRAY(0); array arrTestProfitsTmp = CREATE_ARRAY(0); array arrNetworksTmp = CREATE_ARRAY(0); // ------ // Balance, total, by buy operations and by sell operations array arrBalance = CREATE_ARRAY(0); array arrBalanceBuy = CREATE_ARRAY(0); array arrBalanceSell = CREATE_ARRAY(0); // To speed up the process, as well as to avoid unnecessary // calculations, we can set the script to draw chatrs every n-th // cycle, not every time double nDrawChartEvery = 1; // We choose population size equal 15, mostly because // this is maximum number of different colors we can draw on a chart double nPopulationSize = 15; // String and temporary result of its parsing string strParam; string strToken; // We use first 0.7 of total number of records as a learning set, // the rest will be used as a testing set double nExtractRecords = 0.7 * nCloseArraySize; // For all sets of parameters for NOC indicator // Note, that in this example we only test one sed (size of an // array is 1 element, it is initialized above as // arrNocParameters[0] = "12,0.012,5";) for(double nNocParIdx = 0; nNocParIdx < ARRAY_SIZE(arrNocParameters); nNocParIdx = nNocParIdx + 1) { strParam = arrNocParameters[nNocParIdx]; strToken = GET_TOKEN(strParam, ","); double nNocInterval = STR2NUM(strToken); strToken = GET_TOKEN(strParam, ","); double dNocRange = STR2NUM(strToken); arrNoc = CreateNoc(nNocInterval, dNocRange); // For all sets of lags for(double nNocLagIdx = 0; nNocLagIdx < ARRAY_SIZE(arrStrNocLags); nNocLagIdx = nNocLagIdx + 1) { // Create lag file on disk string strNocLagBuf = arrStrNocLags[nNocLagIdx]; CreateLagFile(strNocLagBuf, nRemoveFirst); // For all different numbers of neurons in hidden layer for(double nNeuronsIdx = 0; nNeuronsIdx < ARRAY_SIZE(arrNeurons); nNeuronsIdx = nNeuronsIdx + 1) { double nNeurons = arrNeurons[nNeuronsIdx]; // -------------------- // Create initial population: we are going to create // 15 neural networks, by making mutated copies // of one of them PRINT("%s\r\n", "Creating initial population"); strNnFileName = strNnPath + strForexName + "_evolution_0.nn"; NewNn(arrNocLags, nNeurons); arrNetworks[0] = OPEN_NN(strNnFileName, bIsPathRelative); for(double nNnIdx = 1; nNnIdx < nPopulationSize; nNnIdx = nNnIdx + 1) { arrNetworks[nNnIdx] = MUTATION_NN(arrNetworks[nNnIdx - 1], 1, 0.1); } // Creating arrays to keep results of testing // We need 15 arrays for results of testing of a // learning set of records, and another 15 for results of // a testing set. PRINT("%s\r\n", "Creating wrapper for profit charts"); // 15 arrays, showing profit improvement array arrProfit_00 = CREATE_ARRAY(0); array arrProfit_01 = CREATE_ARRAY(0); array arrProfit_02 = CREATE_ARRAY(0); array arrProfit_03 = CREATE_ARRAY(0); array arrProfit_04 = CREATE_ARRAY(0); array arrProfit_05 = CREATE_ARRAY(0); array arrProfit_06 = CREATE_ARRAY(0); array arrProfit_07 = CREATE_ARRAY(0); array arrProfit_08 = CREATE_ARRAY(0); array arrProfit_09 = CREATE_ARRAY(0); array arrProfit_10 = CREATE_ARRAY(0); array arrProfit_11 = CREATE_ARRAY(0); array arrProfit_12 = CREATE_ARRAY(0); array arrProfit_13 = CREATE_ARRAY(0); array arrProfit_14 = CREATE_ARRAY(0); array arrTestProfit_00 = CREATE_ARRAY(0); array arrTestProfit_01 = CREATE_ARRAY(0); array arrTestProfit_02 = CREATE_ARRAY(0); array arrTestProfit_03 = CREATE_ARRAY(0); array arrTestProfit_04 = CREATE_ARRAY(0); array arrTestProfit_05 = CREATE_ARRAY(0); array arrTestProfit_06 = CREATE_ARRAY(0); array arrTestProfit_07 = CREATE_ARRAY(0); array arrTestProfit_08 = CREATE_ARRAY(0); array arrTestProfit_09 = CREATE_ARRAY(0); array arrTestProfit_10 = CREATE_ARRAY(0); array arrTestProfit_11 = CREATE_ARRAY(0); array arrTestProfit_12 = CREATE_ARRAY(0); array arrTestProfit_13 = CREATE_ARRAY(0); array arrTestProfit_14 = CREATE_ARRAY(0); // Teach NN on random 12000 records subsets of data double nLearn = 12000; for(double nGeneration = 1; nGeneration < 100000; nGeneration = nGeneration + 1) { // This string will grow with each iteration, // then saved to file to produce XML file, containing // profit charts string strXML = "\r\n\r\n"; string strHistoryXML = ""; // Clean output window OUT_CLEANUP(); PRINT("====== Generation %.0f ======\r\n", nGeneration); // Add one child for each network, with slightly // different weights. Note, that 0.1 for mutation tange // is not the only choice // Newly created NNs are added after 15 existing ones PRINT("%s\r\n", "Creating children"); for(nNnIdx = nPopulationSize; nNnIdx < 2 * nPopulationSize; nNnIdx = nNnIdx + 1) { arrNetworks[nNnIdx] = MUTATION_NN(arrNetworks[nNnIdx - nPopulationSize], 1, 0.1); } // Find best networks in both generations for(nNnIdx = 0; nNnIdx < 2 * nPopulationSize; nNnIdx = nNnIdx + 1) { PRINT("Testing NN %.0f(", nNnIdx, " %.0f), ", arrNetworks[nNnIdx]); // We apply NN to the entire range of data, // which probably should be optimized APPLY_NN(arrNetworks[nNnIdx], nExtractRecords, 1.0, 1, arrNoc, arrNocLags, 9, arrNnBuy, arrNnSell, arrNnCloseBuy, arrNnCloseSell, arrNnHold, arrNnBuyStopLoss, arrNnBuyTakeProfit, arrNnSellStopLoss, arrNnSellTakeProfit); // We use an interval of nLearn records, from nStart // to nStart + nLearn, where nStart is a random point // within learning set. double nStart = nRemoveFirst + RAND(nExtractRecords - nLearn - nRemoveFirst); // Use outputs produced by NN to simulate trading dMaxDrawDown = Test(nStart, nStart + nLearn); // This is a trick. It is possible, that our // trading system works very well on long trades, // and very poor on short, or vice versa. If, say, // longf trades are VERYT good, this TS may win, // even with large losses on short trades. // To avoid it, we assign more weight to long trades // in odd and to short trades in even cycles. // This is just an example, there is no guarantee, // that it will improve something. More about it // below, in discussion about corrections. double dBalance; if(nGeneration % 2 == 0) { dBalance = arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1] + 0.6 * arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1]; } else { dBalance = arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1] + 0.6 * arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1]; } // Correction // We are going to use corrections in our following // examples, for now, it is set to 1 double dCorrection = 1; // Store correction, calculated for current NN ARRAY_ADD(arrCorrections, dCorrection); // Store profit and NN handle ARRAY_ADD(arrProfitsTmp, dBalance); ARRAY_ADD(arrNetworksTmp, arrNetworks[nNnIdx]); // Now, do the same for entire testing set, and // store the results dMaxDrawDown = Test(nRemoveFirst, nExtractRecords); ARRAY_ADD(arrLearnProfitsTmp, arrBalance[ARRAY_SIZE(arrBalance) - 1]); // Every nDrawChartEvery generation, draw chart if(nGeneration % nDrawChartEvery == 0) { Chart(strForexName, nNnIdx); } // Print results for testing of current NN PRINT("\t\tBalance: %.4f; ", arrBalance[ARRAY_SIZE(arrBalance) - 1], "(%.4f, ", arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1], "%.4f), ", arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1], "trades: %.0f\r\n", ARRAY_SIZE(arrBalance)); // Test on entire set, and store the results Test(nExtractRecords, nCloseArraySize); ARRAY_ADD(arrTestProfitsTmp, arrBalance[ARRAY_SIZE(arrBalance) - 1]); // Every nDrawChartEvery generation, draw chart if(nGeneration % nDrawChartEvery == 0) { ChartTest(strForexName, nNnIdx); } } // for(nIdx = 0; nNnIdx < 2 * nPopulationSize // To assign weights to trading results properly, we want // these results to be zero-based double dMinProfit = ARRAY_MIN(arrProfitsTmp, -1, -1); arrProfitsTmp = arrProfitsTmp - dMinProfit; // Clean up an array that will hold NNs ARRAY_REMOVE(arrNetworks, -1); // Add profit to a sorted array. It returns an // insertion position, then we use this position to // add NN handle, learning and testing profits // to non-sorted arrays. Now we have data for current // NN at the same array index as its profit. double nPos; for(nNnIdx = 0; nNnIdx < 2 * nPopulationSize; nNnIdx = nNnIdx + 1) { nPos = ARRAY_ADD(arrProfits, arrProfitsTmp[nNnIdx] * arrCorrections[nNnIdx]); ARRAY_INSERT(arrNetworks, arrNetworksTmp[nNnIdx], nPos); ARRAY_INSERT(arrLearnProfits, arrLearnProfitsTmp[nNnIdx], nPos); ARRAY_INSERT(arrTestProfits, arrTestProfitsTmp[nNnIdx], nPos); } PRINT("%s\r\n", "Removing NNs with poor performance"); // Close NNs, that have poor performance // More fitness - more chances to be selected for(nNnIdx = nPopulationSize - 1; nNnIdx >= 0; nNnIdx = nNnIdx - 1) { CLOSE_NN(arrNetworks[nNnIdx]); ARRAY_REMOVE(arrNetworks, nNnIdx); } // Copy data from temporary arrays to permanent ones arrProfit_00[ARRAY_SIZE(arrProfit_00)] = arrLearnProfits[29]; arrProfit_01[ARRAY_SIZE(arrProfit_01)] = arrLearnProfits[28]; arrProfit_02[ARRAY_SIZE(arrProfit_02)] = arrLearnProfits[27]; arrProfit_03[ARRAY_SIZE(arrProfit_03)] = arrLearnProfits[26]; arrProfit_04[ARRAY_SIZE(arrProfit_04)] = arrLearnProfits[25]; arrProfit_05[ARRAY_SIZE(arrProfit_05)] = arrLearnProfits[24]; arrProfit_06[ARRAY_SIZE(arrProfit_06)] = arrLearnProfits[23]; arrProfit_07[ARRAY_SIZE(arrProfit_07)] = arrLearnProfits[22]; arrProfit_08[ARRAY_SIZE(arrProfit_08)] = arrLearnProfits[21]; arrProfit_09[ARRAY_SIZE(arrProfit_09)] = arrLearnProfits[20]; arrProfit_10[ARRAY_SIZE(arrProfit_10)] = arrLearnProfits[19]; arrProfit_11[ARRAY_SIZE(arrProfit_11)] = arrLearnProfits[18]; arrProfit_12[ARRAY_SIZE(arrProfit_12)] = arrLearnProfits[17]; arrProfit_13[ARRAY_SIZE(arrProfit_13)] = arrLearnProfits[16]; arrProfit_14[ARRAY_SIZE(arrProfit_14)] = arrLearnProfits[15]; arrTestProfit_00[ARRAY_SIZE(arrTestProfit_00)] = arrTestProfits[29]; arrTestProfit_01[ARRAY_SIZE(arrTestProfit_01)] = arrTestProfits[28]; arrTestProfit_02[ARRAY_SIZE(arrTestProfit_02)] = arrTestProfits[27]; arrTestProfit_03[ARRAY_SIZE(arrTestProfit_03)] = arrTestProfits[26]; arrTestProfit_04[ARRAY_SIZE(arrTestProfit_04)] = arrTestProfits[25]; arrTestProfit_05[ARRAY_SIZE(arrTestProfit_05)] = arrTestProfits[24]; arrTestProfit_06[ARRAY_SIZE(arrTestProfit_06)] = arrTestProfits[23]; arrTestProfit_07[ARRAY_SIZE(arrTestProfit_07)] = arrTestProfits[22]; arrTestProfit_08[ARRAY_SIZE(arrTestProfit_08)] = arrTestProfits[21]; arrTestProfit_09[ARRAY_SIZE(arrTestProfit_09)] = arrTestProfits[20]; arrTestProfit_10[ARRAY_SIZE(arrTestProfit_10)] = arrTestProfits[19]; arrTestProfit_11[ARRAY_SIZE(arrTestProfit_11)] = arrTestProfits[18]; arrTestProfit_12[ARRAY_SIZE(arrTestProfit_12)] = arrTestProfits[17]; arrTestProfit_13[ARRAY_SIZE(arrTestProfit_13)] = arrTestProfits[16]; arrTestProfit_14[ARRAY_SIZE(arrTestProfit_14)] = arrTestProfits[15]; PRINT("\r\n------------------%s\r\n", ""); // Clean up temporary arrays ARRAY_REMOVE(arrProfits, -1); ARRAY_REMOVE(arrProfitsTmp, -1); ARRAY_REMOVE(arrLearnProfits, -1); ARRAY_REMOVE(arrTestProfits, -1); ARRAY_REMOVE(arrLearnProfitsTmp, -1); ARRAY_REMOVE(arrTestProfitsTmp, -1); ARRAY_REMOVE(arrNetworksTmp, -1); // Every nDrawChartEvery generation, draw chart if(nGeneration % nDrawChartEvery == 0) { WrapChart(strForexName); WrapTestChart(strForexName); STR_REPLACE(strXML, "", strHistoryXML); strXML = strXML + "\r\n"; F_UNLINK(strImagePath + "chart_forex_nn.xml"); SAVE_XML(strImagePath, "chart_forex_nn", "chart_forex_nn", "root", strXML); } } // nGeneration } // for(double nNeuronsIdx = 0; nNeuronsIdx < ARRAY_SIZE(arrNeurons); } // for(double nNocLagIdx = 0; nNocLagIdx < ARRAY_SIZE(arrStrNocLags); } // for(double nNocParIdx = 0; nNocParIdx < ARRAY_SIZE(arrNocParameters); PRINT("%s\r\nDone\r\n", ""); } // --------------- // Testing function: takes array of NN outputs, and uses it to // simulate trading // Note, that it creates some variables that are not used in this // example, as we do not use corrections double Test(double nFirstRecordNum, double nLastRecordNum) { // Winning trades to loosing trades dSuccessRatio = 0.001; double nWinTrades = 0; double nLooseTrades = 0; // Cleanup and assign initial balance // We set balance to 1000 and balance buy / balance sell to 0 // simply to keep them at some distance on a chart ARRAY_REMOVE(arrBalance, -1); arrBalance[0] = 1000; ARRAY_REMOVE(arrBalanceBuy, -1); arrBalanceBuy[0] = 0; ARRAY_REMOVE(arrBalanceSell, -1); arrBalanceSell[0] = 0; // Bars are plotteg on horizontal axe array arrBars = CREATE_ARRAY(0); // Bar to start from arrBars[0] = nFirstRecordNum; double dLotSize = 100; // 0.1 lot, $100 double nTradeType = 0; // 1 buy, -1 sell, 0 - none double nCurrentTradeType = 0; // 1 buy, -1 sell, 0 - none double dSpread = 0.0005; // It TS is very bad from the very beginning, we use this flag // to stop, as there is no need to spend more time on it double bStop; // Drawdown and maximum value of chart by current bar dMaxDrawDown = 0; double dCurrentMax = 1000; // Used to calculate drawdown // Number of trades nTradeNumber = 0; nTradeNumberBuy = 0; nTradeNumberSell = 0; // We open trade with these parameters double dOpenPrice; double dStop = 0; double dTp = 0; double dBuyStopLoss; double dBuyTakeProfit; double dSellStopLoss; double dSellTakeProfit; // Price at which trade was closed double dClosedAt; // We are going to use these names instead of numbers, // just to make code more readable double BUY = -2; double CLOSE_BUY = -1; double HOLD = 0; double CLOSE_SELL = 1; double SELL = 2; // Used with corrections // For all records for(double nBar = nFirstRecordNum + 1; nBar < nLastRecordNum; nBar = nBar + 1) { nTradeType = 0; // Adjust dMaxNnAdvice double dMaxNnAdvice = MAX(arrNnBuy[nBar - 1], MAX(arrNnSell[nBar - 1], MAX(arrNnCloseSell[nBar - 1], MAX(arrNnCloseBuy[nBar - 1], arrNnHold[nBar - 1])))); // Make trading decision based on value of NN signal if(arrNnBuy[nBar - 1] == dMaxNnAdvice) { nTradeType = BUY; } else { if(arrNnSell[nBar - 1] == dMaxNnAdvice) { nTradeType = SELL; } else { if(arrNnCloseBuy[nBar - 1] == dMaxNnAdvice) { nTradeType = CLOSE_BUY; } else { if(arrNnCloseSell[nBar - 1] == dMaxNnAdvice) { nTradeType = CLOSE_SELL; } else { nTradeType = HOLD; } } } } // Calculate stops for the trade GetBuyStopLoss(); GetBuyTakeProfit(); GetSellStopLoss(); GetSellTakeProfit(); // If we already have an open trade, and NN's advice is // to close or to reverse a position, calculate profit // from current trade and close it. if(nCurrentTradeType != 0) // -1 or 1 { bStop = 0; double dProfit; // If BUY and stop loss or take profit reached, // or SELL signal received if(nCurrentTradeType == -1 && (arrLow[nBar - 1] <= dStop || arrHigh[nBar - 1] >= dTp - dSpread || nTradeType == CLOSE_BUY || nTradeType == SELL)) { dProfit = 100 * (arrOpen[nBar] - dOpenPrice) * dLotSize; arrBalance[ARRAY_SIZE(arrBalance)] = arrBalance[ARRAY_SIZE(arrBalance) - 1] + dProfit; arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1] + dProfit; arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1]; bStop = 1; dClosedAt = arrOpen[nBar]; } else { if(nCurrentTradeType == 1 && (arrHigh[nBar - 1] >= dStop - dSpread || arrLow[nBar - 1] <= dTp || nTradeType == CLOSE_SELL || nTradeType == BUY)) { dProfit = 100 * (dOpenPrice - arrOpen[nBar] - dSpread) * dLotSize; arrBalance[ARRAY_SIZE(arrBalance)] = arrBalance[ARRAY_SIZE(arrBalance) - 1] + dProfit; arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1]; arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1] + dProfit; bStop = 1; dClosedAt = arrOpen[nBar]; } } // If we set this flag to 1 in the code above, it means, // that there was a trade closed, so increase counter of // trades if(bStop == 1) { nCurrentTradeType = 0; arrBars[ARRAY_SIZE(arrBars)] = nBar; if(dProfit > 0) { nWinTrades = nWinTrades + 1; } else { nLooseTrades = nLooseTrades + 1; } } } // Calculate drawdown. In the current example, this code is not // used, it is used in following examples, to calculate // corrections double dDrawDown = (dCurrentMax - arrBalance[ARRAY_SIZE(arrBalance) - 1]) / 1000; //dCurrentMax; dMaxDrawDown = MAX(dMaxDrawDown, dDrawDown); dCurrentMax = MAX(dCurrentMax, arrBalance[ARRAY_SIZE(arrBalance) - 1]); // BUY signal. Remember open price // Same below for sell signal if(nCurrentTradeType != -1 && nTradeType == BUY) { dOpenPrice = arrOpen[nBar]; dStop = dOpenPrice - dBuyStopLoss; dTp = dOpenPrice + dBuyTakeProfit; nCurrentTradeType = -1; nTradeNumber = nTradeNumber + 1; nTradeNumberBuy = nTradeNumberBuy + 1; } else { // && dStopLoss >= dMinStop) if(nCurrentTradeType != 1 && nTradeType == SELL) { dOpenPrice = arrOpen[nBar]; dStop = dOpenPrice + dSellStopLoss; dTp = dOpenPrice - dSellTakeProfit; nCurrentTradeType = 1; nTradeNumber = nTradeNumber + 1; nTradeNumberSell = nTradeNumberSell + 1; } } } // We have cycled through all records. // If at the end we have open positions, close them bStop = 0; if(nCurrentTradeType == 1) { dProfit = 100 * (dOpenPrice - arrOpen[nBar - 1] - dSpread) * dLotSize; arrBalance[ARRAY_SIZE(arrBalance)] = arrBalance[ARRAY_SIZE(arrBalance) - 1] + dProfit; arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1] + dProfit; arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1]; arrBars[ARRAY_SIZE(arrBars)] = nBar - 1; bStop = 1; } else { if(nCurrentTradeType == -1) { dProfit = 100 * (arrOpen[nBar - 1] - dOpenPrice) * dLotSize; arrBalance[ARRAY_SIZE(arrBalance)] = arrBalance[ARRAY_SIZE(arrBalance) - 1] + dProfit; arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy)] = arrBalanceBuy[ARRAY_SIZE(arrBalanceBuy) - 1] + dProfit; arrBalanceSell[ARRAY_SIZE(arrBalanceSell)] = arrBalanceSell[ARRAY_SIZE(arrBalanceSell) - 1]; arrBars[ARRAY_SIZE(arrBars)] = nBar - 1; bStop = 1; } } if(bStop == 1) { if(dProfit > 0) { nWinTrades = nWinTrades + 1; } else { nLooseTrades = nLooseTrades + 1; } } // Calculate success ratio, to be used with corrections // (not in this simple example) if(nWinTrades + nLooseTrades > 0) { dSuccessRatio = (nWinTrades + 1) / (nWinTrades + nLooseTrades + 1); } return dMaxDrawDown; } // --------------- // This function creates NOC indicator, used as the input // for NN. For details on what NOC is, see previous article array CreateNoc(double nNocInterval, double dMinRange) { PRINT("%s\r\n", "Creating NOC indicator"); array arrNormOnCondition = CREATE_ARRAY(0); array arrPeriodLow = CREATE_ARRAY(0); array arrPeriodHigh = CREATE_ARRAY(0); array arrPeriodLow = MV_MIN(arrLow, nNocInterval); array arrPeriodHigh = MV_MAX(arrHigh, nNocInterval); for(double i = 0; i < nNocInterval; i = i + 1) { arrNormOnCondition[i] = 0; } double dClose; double dLow; double dHigh; for(i = nNocInterval; i < nCloseArraySize; 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; } // ---------------- // This function writes to the file lagged NOC (or something else), // that will be used as NN input. One line of this file is // one pattern to be fed to NN void CreateLagFile(string strNocLags, double nRemoveFirst) { PRINT("%s\r\n", "Creating Lag file"); double hFile = F_OPEN(strLagFileName, "wb", bIsPathRelative); F_PRINT(hFile, "%s", "No"); // --- ARRAY_REMOVE(arrNocLags, -1); strToken = GET_TOKEN(strNocLags, ","); double nNumOfLags = STR2NUM(strToken); for(double i = 0; i < nNumOfLags; i = i + 1) { strToken = GET_TOKEN(strNocLags, ","); arrNocLags[i] = STR2NUM(strToken); F_PRINT(hFile, ",Noc-%.0f", arrNocLags[i]); } // --- F_PRINT(hFile, "%s\r\n", ""); // --- double nNum; for(i = nRemoveFirst; i < nCloseArraySize; i = i + 1) { nNum = i - nRemoveFirst + 1; F_PRINT(hFile, "%.0f", nNum); for(double nLagIdx = 0; nLagIdx < nNumOfLags; nLagIdx = nLagIdx + 1) { F_PRINT(hFile, ",%f", arrNoc[i - arrNocLags[nLagIdx]]); } F_PRINT(hFile, "%s\r\n", ""); } F_CLOSE(hFile); } // -------------- // This function creates new NN // Note, that some values are hardcoded here. If you // change them, make sure the code remains logical. // For example, we use particular number of NN outputs, // so if you change it, the code will break. void NewNn(array arrNocLags, double nNeuronsLayer2) { double dStopError = 0; double nStopEpoch = 0; 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, our input begins at column No 1 for(double nInputCol = 0; nInputCol < ARRAY_SIZE(arrNocLags); nInputCol = nInputCol + 1) { arrInputColumns[nInputCol] = nInputCol + 1; arrInputColumnNames[nInputCol] = "Noc-" + NUM2STR(arrNocLags[nInputCol], "%.0f"); } array arrOutputColumns = CREATE_ARRAY(0); // Not used anywhere, however, should be 3 outputs, to match what we // pass to CREATE_NN arrOutputColumns[0] = 1; arrOutputColumns[1] = 2; arrOutputColumns[2] = 3; arrOutputColumns[3] = 4; arrOutputColumns[4] = 5; arrOutputColumns[5] = 6; arrOutputColumns[6] = 7; arrOutputColumns[7] = 8; arrOutputColumns[8] = 9; array_s arrOutputColumnNames = CREATE_ARRAY_S(0); arrOutputColumnNames[0] = "NnBuy"; arrOutputColumnNames[1] = "NnSell"; arrOutputColumnNames[2] = "NnCloseBuy"; arrOutputColumnNames[3] = "NnCloseSell"; arrOutputColumnNames[4] = "NnCloseHold"; arrOutputColumnNames[5] = "NnBuyStopLoss"; arrOutputColumnNames[6] = "NnBuyTakeProfit"; arrOutputColumnNames[7] = "NnSellStopLoss"; arrOutputColumnNames[8] = "NnSellTakeProfit"; double nNeuronsLayer1 = ARRAY_SIZE(arrNocLags); double nNeuronsLayer3 = 9; double nNeuronsLayer4 = 9; double nLayers = 4; double nActivation = 0; double nAdjustRange = 1.0; array arrOutTabInputColumns = CREATE_ARRAY(0); arrOutTabInputColumns[0] = 0; // Number arrOutTabInputColumns[1] = 1; arrOutTabInputColumns[2] = 2; arrOutTabInputColumns[3] = 3; arrOutTabInputColumns[4] = 4; arrOutTabInputColumns[5] = 5; arrOutTabInputColumns[6] = 6; arrOutTabInputColumns[7] = 7; arrOutTabInputColumns[8] = 8; array_s arrOutTabInputColumnNames = CREATE_ARRAY_S(0); arrOutTabInputColumnNames[0] = "NnBuy"; arrOutTabInputColumnNames[1] = "NnSell"; arrOutTabInputColumnNames[2] = "NnCloseBuy"; arrOutTabInputColumnNames[3] = "NnCloseSell"; arrOutTabInputColumnNames[4] = "NnCloseHold"; arrOutTabInputColumnNames[5] = "NnBuyStopLoss"; arrOutTabInputColumnNames[6] = "NnBuyTakeProfit"; arrOutTabInputColumnNames[7] = "NnSellStopLoss"; arrOutTabInputColumnNames[8] = "NnSellTakeProfit"; array arrOutTabOutputColumns = CREATE_ARRAY(0); // Desired output and NN output will be added to the // same list, right after inputs arrOutTabOutputColumns[0] = ARRAY_SIZE(arrNocLags) + 1; arrOutTabOutputColumns[1] = ARRAY_SIZE(arrNocLags) + 2; arrOutTabOutputColumns[2] = ARRAY_SIZE(arrNocLags) + 3; arrOutTabOutputColumns[3] = ARRAY_SIZE(arrNocLags) + 4; arrOutTabOutputColumns[4] = ARRAY_SIZE(arrNocLags) + 5; arrOutTabOutputColumns[5] = ARRAY_SIZE(arrNocLags) + 6; arrOutTabOutputColumns[6] = ARRAY_SIZE(arrNocLags) + 7; arrOutTabOutputColumns[7] = ARRAY_SIZE(arrNocLags) + 8; arrOutTabOutputColumns[8] = ARRAY_SIZE(arrNocLags) + 9; array_s arrOutTabOutputColumnNames = CREATE_ARRAY_S(0); arrOutTabOutputColumnNames[0] = "NnBuy"; arrOutTabOutputColumnNames[1] = "NnSell"; arrOutTabOutputColumnNames[2] = "NnCloseBuy"; arrOutTabOutputColumnNames[3] = "NnCloseSell"; arrOutTabOutputColumnNames[4] = "NnCloseHold"; arrOutTabOutputColumnNames[5] = "NnBuyStopLoss"; arrOutTabOutputColumnNames[6] = "NnBuyTakeProfit"; arrOutTabOutputColumnNames[7] = "NnSellStopLoss"; arrOutTabOutputColumnNames[8] = "NnSellTakeProfit"; 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); } // ---------------- // Simple wrapper for a chart function, it creates a chart // of profits for 15 winner NNs void WrapChart(string strForexName) { string strImageFileName = strImagePath + "evolution_" + strForexName + ".png"; strHistoryXML = ""; array arrX = CREATE_ARRAY(0); for(double i = 0; i < ARRAY_SIZE(arrProfit_00); i = i + 1) { arrX[i] = i; } strHistoryXML = strHistoryXML + "\t\r\n\t\t" + SAVE_CHART(400, 300, 0, strImageFileName, arrX, arrProfit_00, arrProfit_01, arrProfit_02, arrProfit_03, arrProfit_04, arrProfit_05, arrProfit_06, arrProfit_07, arrProfit_08, arrProfit_09, arrProfit_10, arrProfit_11, arrProfit_12, arrProfit_13, arrProfit_14); strHistoryXML = strHistoryXML + "\t\r\n"; } // ------ // Same as above, for testing set void WrapTestChart(string strForexName) { string strImageFileName = strImagePath + "evolution_test_" + strForexName + ".png"; array arrX = CREATE_ARRAY(0); for(double i = 0; i < ARRAY_SIZE(arrTestProfit_00); i = i + 1) { arrX[i] = i; } strHistoryXML = strHistoryXML + "\t\r\n\t\t" + SAVE_CHART(400, 300, 0, strImageFileName, arrX, arrTestProfit_00, arrTestProfit_01, arrTestProfit_02, arrTestProfit_03, arrTestProfit_04, arrTestProfit_05, arrTestProfit_06, arrTestProfit_07, arrTestProfit_08, arrTestProfit_09, arrTestProfit_10, arrTestProfit_11, arrTestProfit_12, arrTestProfit_13, arrTestProfit_14); strHistoryXML = strHistoryXML + "\t\r\n"; } // ------ // Profit chart for individual NN void Chart(string strForexName, double nNnIdx) { string strImageFileName = strImagePath + "evolution_" + strForexName + NUM2STR(nNnIdx, "_%.0f") + ".png"; strXML = strXML + "\t\r\n\t\t\r\n"; strXML = strXML + "Trades: " + NUM2STR(nTradeNumber, "%.0f") + "(Buy: " + NUM2STR(nTradeNumberBuy, "%.0f") + ", Sell :" + NUM2STR(nTradeNumberSell, "%.0f)\r\n") + "NocInterval: " + NUM2STR(nNocInterval, "%.0f") + ", Range: " + NUM2STR(dNocRange, "%.3f") + ", Ma: " + NUM2STR(nMa, "%.0f\r\n") + ", Neurons: " + NUM2STR(nNeurons, "%.0f\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\r\n"; strXML = strXML + "\t\t" + SAVE_CHART(400, 300, 0, strImageFileName, arrBars, arrBalance, arrBalanceBuy, arrBalanceSell); strXML = strXML + "\t\r\n"; } // ------ void ChartTest(string strForexName, double nNnIdx) { string strImageFileName = strImagePath + "evolution_test_" + strForexName + NUM2STR(nNnIdx, "_%.0f") + ".png"; strXML = strXML + "\t\r\n"; strXML = strXML + "\t\t" + SAVE_CHART(400, 300, 0, strImageFileName, arrBars, arrBalance, arrBalanceBuy, arrBalanceSell); strXML = strXML + "\t\r\n"; } // ------ void GetSellStopLoss() { double dRange = dMaxStop - dMinStop; if(arrNnSellStopLoss[nBar - 1] < dMinStop / dRange) { dSellStopLoss = dMinStop; } else { if(arrNnSellStopLoss[nBar - 1] > dMaxStop / dRange) { dSellStopLoss = dMaxStop; } else { dSellStopLoss = arrNnSellStopLoss[nBar - 1] * dRange; } } } // ------ void GetSellTakeProfit() { double dRange = dMaxStop - dMinStop; if(arrNnSellTakeProfit[nBar - 1] < dMinStop / dRange) { dSellTakeProfit = dMinStop; } else { if(arrNnSellTakeProfit[nBar - 1] > dMaxStop / dRange) { dSellTakeProfit = dMaxStop; } else { dSellTakeProfit = arrNnSellTakeProfit[nBar - 1] * dRange; } } } // ------ void GetBuyStopLoss() { double dRange = dMaxStop - dMinStop; if(arrNnBuyStopLoss[nBar - 1] < dMinStop / dRange) { dBuyStopLoss = dMinStop; } else { if(arrNnBuyStopLoss[nBar - 1] > dMaxStop / dRange) { dBuyStopLoss = dMaxStop; } else { dBuyStopLoss = arrNnBuyStopLoss[nBar - 1] * dRange; } } } // ------ void GetBuyTakeProfit() { double dRange = dMaxStop - dMinStop; if(arrNnBuyTakeProfit[nBar - 1] < dMinStop / dRange) { dBuyTakeProfit = dMinStop; } else { if(arrNnBuyTakeProfit[nBar - 1] > dMaxStop / dRange) { dBuyTakeProfit = dMaxStop; } else { dBuyTakeProfit = arrNnBuyTakeProfit[nBar - 1] * dRange; } } } // ------