Writting my first script whose purpose is to gather historical ohlcv data on a specific period and calculate some indicators before persisting these time serie values into a json for further work.
So far, I though I should rely on the build-in MQL5 api function such as iMA, rSI and so on.
Nevertheless, I notice that the approach i’m using (declaring a handler of this built in) then using cpybuffer to pass it the MQL rates between my two dates give wrong results. I’m not understanding what i’m not doing correctly.
For the record I also tried with iCustom approach instead of built in parameters but it is not working as intended and I assume it must be a way to make that work with build ins (whose purpose seems to be precisely that).
//+------------------------------------------------------------------+
//| scrapper.mq5 |
//+------------------------------------------------------------------+
#property strict
#include "Libraries/IndicatorsUtils.mqh"
#include "Libraries/TimeUtils.mqh"
input int emaPeriod = 14;
input int rsiPeriod = 14;
input int adxPeriod = 14;
input int bBandsPeriod = 20;
input int bBandsShift = 0;
input double bBandsDev = 2;
input int stochFastK = 5;
input int stochSlowK = 3;
input int stochSlowD = 3;
// Nom du fichier
string fileName = "HistoricalDataWithIndicatorsAndSR.json";
int handle;
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
void OnStart()
{
handle = FileOpen(fileName, FILE_WRITE | FILE_TXT | FILE_ANSI); // Open the file in text mode for JSON output
if (handle == INVALID_HANDLE)
{
Print("Erreur d'ouverture de fichier : ", GetLastError());
return;
}
// Début du fichier JSON avec un tableau
FileWrite(handle, "[");
MqlRates rates[];
datetime preStartTime = D'2019.01.01 00:00';
datetime startTime = D'2019.02.01 00:00'; // Date de début spécifiée
datetime endTime = D'2019.06.01 00:00'; // Date de fin spécifiée
// Inclure des données historiques antérieures pour calculer les indicateurs et niveaux
// datetime preStartTime = startTime - PeriodSeconds(PERIOD_M1) * bBandsPeriod * 2; // ADX needs more historical data
int rates_total = CopyRates(_Symbol, PERIOD_M1, preStartTime, endTime, rates);
if (rates_total > 0)
{
double closePrices[];
ArrayResize(closePrices, rates_total);
for(int i = 0; i < rates_total; i++)
{
closePrices[i] = rates[i].close;
}
double emaValues[];
double rsiValues[];
double adxValues[];
double bbUpperValues[];
double bbMiddleValues[];
double bbLowerValues[];
double stochKValues[];
double stochDValues[];
ArrayResize(emaValues, rates_total);
ArrayResize(rsiValues, rates_total);
ArrayResize(adxValues, rates_total);
ArrayResize(bbUpperValues, rates_total);
ArrayResize(bbMiddleValues, rates_total);
ArrayResize(bbLowerValues, rates_total);
ArrayResize(stochKValues, rates_total);
ArrayResize(stochDValues, rates_total);
// Handles pour les indicateurs
int emaHandle = iMA(_Symbol, PERIOD_M1, emaPeriod, 0, MODE_EMA, PRICE_CLOSE);
int rsiHandle = iRSI(_Symbol, PERIOD_M1, rsiPeriod, PRICE_CLOSE);
int adxHandle = iADX(_Symbol, PERIOD_M1, adxPeriod);
int bbHandle = iBands(_Symbol, PERIOD_M1, bBandsPeriod, bBandsShift, bBandsDev, PRICE_CLOSE);
int stochHandle = iStochastic(_Symbol, PERIOD_M1, stochFastK, stochSlowK, stochSlowD, MODE_SMA, STO_LOWHIGH);
// Copie des valeurs des indicateurs
CopyBuffer(emaHandle, 0, 0, rates_total, emaValues);
CopyBuffer(rsiHandle, 0, 0, rates_total, rsiValues);
CopyBuffer(adxHandle, 0, 0, rates_total, adxValues);
CopyBuffer(bbHandle, 0, 0, rates_total, bbUpperValues); // Upper Band
CopyBuffer(bbHandle, 1, 0, rates_total, bbMiddleValues); // Middle Band
CopyBuffer(bbHandle, 2, 0, rates_total, bbLowerValues); // Lower Band
CopyBuffer(stochHandle, 0, 0, rates_total, stochKValues);
CopyBuffer(stochHandle, 1, 0, rates_total, stochDValues);
MqlRates hourlyRates[];
MqlRates dailyRates[];
MqlRates weeklyRates[];
MqlRates monthlyRates[];
// Calculer les niveaux de support et résistance pour chaque timeframe supérieure
CopyRates(_Symbol, PERIOD_H1, preStartTime, endTime, hourlyRates);
CopyRates(_Symbol, PERIOD_D1, preStartTime, endTime, dailyRates);
CopyRates(_Symbol, PERIOD_W1, preStartTime, endTime, weeklyRates);
CopyRates(_Symbol, PERIOD_MN1, preStartTime, endTime, monthlyRates);
double support1_hourly[], resistance1_hourly[];
double support2_hourly[], resistance2_hourly[];
double support1_daily[], resistance1_daily[];
double support2_daily[], resistance2_daily[];
double support1_weekly[], resistance1_weekly[];
double support2_weekly[], resistance2_weekly[];
double support1_monthly[], resistance1_monthly[];
double support2_monthly[], resistance2_monthly[];
GetSupportResistance(hourlyRates, ArraySize(hourlyRates), support1_hourly, resistance1_hourly, support2_hourly, resistance2_hourly);
GetSupportResistance(dailyRates, ArraySize(dailyRates), support1_daily, resistance1_daily, support2_daily, resistance2_daily);
GetSupportResistance(weeklyRates, ArraySize(weeklyRates), support1_weekly, resistance1_weekly, support2_weekly, resistance2_weekly);
GetSupportResistance(monthlyRates, ArraySize(monthlyRates), support1_monthly, resistance1_monthly, support2_monthly, resistance2_monthly);
int hourlyIndex = 0;
int dailyIndex = 0;
int weeklyIndex = 0;
int monthlyIndex = 0;
for (int i = 1; i < rates_total; i++)
{
if (rates[i].time < startTime) continue; // Ignore data before startTime
// Vérifier si une nouvelle bougie sur les timeframes supérieures est apparue
if (TimeHour(rates[i].time) != TimeHour(rates[i - 1].time)) hourlyIndex++;
if (TimeDay(rates[i].time) != TimeDay(rates[i - 1].time)) dailyIndex++;
if (TimeDayOfWeek(rates[i].time) != TimeDayOfWeek(rates[i - 1].time)) weeklyIndex++;
if (TimeMonth(rates[i].time) != TimeMonth(rates[i - 1].time)) monthlyIndex++;
// Ensure indices are within bounds
hourlyIndex = MathMin(hourlyIndex, ArraySize(support1_hourly) - 1);
dailyIndex = MathMin(dailyIndex, ArraySize(support1_daily) - 1);
weeklyIndex = MathMin(weeklyIndex, ArraySize(support1_weekly) - 1);
monthlyIndex = MathMin(monthlyIndex, ArraySize(support1_monthly) - 1);
// Vérifier si les indices sont valides avant de les utiliser
if (hourlyIndex >= ArraySize(support1_hourly) || dailyIndex >= ArraySize(support1_daily) || weeklyIndex >= ArraySize(support1_weekly) || monthlyIndex >= ArraySize(support1_monthly))
continue;
// Vérifier si les indices sont valides pour les indicateurs avant de les utiliser
if (i >= ArraySize(emaValues) || i >= ArraySize(rsiValues) || i >= ArraySize(adxValues) || i >= ArraySize(bbUpperValues) || i >= ArraySize(bbMiddleValues) || i >= ArraySize(bbLowerValues) || i >= ArraySize(stochKValues) || i >= ArraySize(stochDValues)) {
Print("Index out of bounds: i: ", i, ", hourlyIndex: ", hourlyIndex, ", dailyIndex: ", dailyIndex, ", weeklyIndex: ", weeklyIndex, ", monthlyIndex: ", monthlyIndex, ", ArraySize(emaValues): ", ArraySize(emaValues), ", ArraySize(rsiValues): ", ArraySize(rsiValues), ", ArraySize(adxValues): ", ArraySize(adxValues), ", ArraySize(bbUpperValues): ", ArraySize(bbUpperValues), ", ArraySize(bbMiddleValues): ", ArraySize(bbMiddleValues), ", ArraySize(bbLowerValues): ", ArraySize(bbLowerValues), ", ArraySize(stochKValues): ", ArraySize(stochKValues), ", ArraySize(stochDValues): ", ArraySize(stochDValues));
continue;
}
string jsonLine = StringFormat("{"Date":"%s", "Open":%.5f, "High":%.5f, "Low":%.5f, "Close":%.5f, "Volume":%d, "EMA":%.5f, "RSI":%.5f, "ADX":%.5f, "BBUpper":%.5f, "BBMiddle":%.5f, "BBLower":%.5f, "StochK":%.5f, "StochD":%.5f, "Support1_Hourly":%.5f, "Resistance1_Hourly":%.5f, "Support2_Hourly":%.5f, "Resistance2_Hourly":%.5f, "Support1_Daily":%.5f, "Resistance1_Daily":%.5f, "Support2_Daily":%.5f, "Resistance2_Daily":%.5f, "Support1_Weekly":%.5f, "Resistance1_Weekly":%.5f, "Support2_Weekly":%.5f, "Resistance2_Weekly":%.5f, "Support1_Monthly":%.5f, "Resistance1_Monthly":%.5f, "Support2_Monthly":%.5f, "Resistance2_Monthly":%.5f}",
TimeToString(rates[i].time, TIME_DATE | TIME_MINUTES),
rates[i].open, rates[i].high, rates[i].low, rates[i].close, rates[i].tick_volume,
emaValues[i], rsiValues[i], adxValues[i], bbUpperValues[i], bbMiddleValues[i], bbLowerValues[i], stochKValues[i], stochDValues[i],
support1_hourly[hourlyIndex], resistance1_hourly[hourlyIndex], support2_hourly[hourlyIndex], resistance2_hourly[hourlyIndex],
support1_daily[dailyIndex], resistance1_daily[dailyIndex], support2_daily[dailyIndex], resistance2_daily[dailyIndex],
support1_weekly[weeklyIndex], resistance1_weekly[weeklyIndex], support2_weekly[weeklyIndex], resistance2_weekly[weeklyIndex],
support1_monthly[monthlyIndex], resistance1_monthly[monthlyIndex], support2_monthly[monthlyIndex], resistance2_monthly[monthlyIndex]);
if (i < rates_total - 1)
jsonLine += ",";
FileWrite(handle, jsonLine);
}
}
else
{
Print("Aucune donnée historique disponible.");
}
// Fin du tableau JSON
FileWrite(handle, "]");
FileClose(handle);
Print("Fichier JSON écrit et fermé.");
}
For the record, here are the first 3 lines of generated output.
{"Date":"2019.02.01 00:00", "Open":0.95443, "High":0.95463, "Low":0.95428, "Close":0.95461, "Volume":9, "EMA":0.87805, "RSI":47.81784, "ADX":18.16786, "BBUpper":0.87802, "BBMiddle":0.87816, "BBLower":0.87789, "StochK":26.66667, "StochD":44.45652, "Support1_Hourly":0.95979, "Resistance1_Hourly":0.96075, "Support2_Hourly":0.95927, "Resistance2_Hourly":0.96119, "Support1_Daily":0.92800, "Resistance1_Daily":0.95543, "Support2_Daily":0.91128, "Resistance2_Daily":0.96614, "Support1_Weekly":0.94753, "Resistance1_Weekly":0.95568, "Support2_Weekly":0.94450, "Resistance2_Weekly":0.96080, "Support1_Monthly":0.93057, "Resistance1_Monthly":0.97015, "Support2_Monthly":0.90650, "Resistance2_Monthly":0.98566},
{"Date":"2019.02.01 00:01", "Open":0.95455, "High":0.95461, "Low":0.95455, "Close":0.95461, "Volume":2, "EMA":0.87804, "RSI":50.58763, "ADX":16.26163, "BBUpper":0.87803, "BBMiddle":0.87816, "BBLower":0.87790, "StochK":21.42857, "StochD":31.30952, "Support1_Hourly":0.95979, "Resistance1_Hourly":0.96075, "Support2_Hourly":0.95927, "Resistance2_Hourly":0.96119, "Support1_Daily":0.92800, "Resistance1_Daily":0.95543, "Support2_Daily":0.91128, "Resistance2_Daily":0.96614, "Support1_Weekly":0.94753, "Resistance1_Weekly":0.95568, "Support2_Weekly":0.94450, "Resistance2_Weekly":0.96080, "Support1_Monthly":0.93057, "Resistance1_Monthly":0.97015, "Support2_Monthly":0.90650, "Resistance2_Monthly":0.98566},
{"Date":"2019.02.01 00:02", "Open":0.95459, "High":0.95466, "Low":0.95459, "Close":0.95466, "Volume":4, "EMA":0.87803, "RSI":51.51154, "ADX":16.36762, "BBUpper":0.87803, "BBMiddle":0.87816, "BBLower":0.87791, "StochK":25.00000, "StochD":24.36508, "Support1_Hourly":0.95979, "Resistance1_Hourly":0.96075, "Support2_Hourly":0.95927, "Resistance2_Hourly":0.96119, "Support1_Daily":0.92800, "Resistance1_Daily":0.95543, "Support2_Daily":0.91128, "Resistance2_Daily":0.96614, "Support1_Weekly":0.94753, "Resistance1_Weekly":0.95568, "Support2_Weekly":0.94450, "Resistance2_Weekly":0.96080, "Support1_Monthly":0.93057, "Resistance1_Monthly":0.97015, "Support2_Monthly":0.90650, "Resistance2_Monthly":0.98566},
Concerning the approach from the dates, as you can see in the script i take a sliding period from the beginning date so i access mroe data to calculate indicators who need more data (adx/bbands/ema..) but in the loop where i collect the values of each array, i only start from the start date, excluding previous values used for calculations.
Any input appreciated