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;
}
}
}
// ------