206 lines
6.5 KiB
MQL5
206 lines
6.5 KiB
MQL5
|
//+------------------------------------------------------------------+
|
||
|
//| CustomSymbolRandomRates.mq5 |
|
||
|
//| Copyright 2022, MetaQuotes Ltd. |
|
||
|
//| https://www.mql5.com |
|
||
|
//+------------------------------------------------------------------+
|
||
|
#property copyright "Copyright 2022, MetaQuotes Ltd."
|
||
|
#property link "https://www.mql5.com"
|
||
|
#property description "Create custom symbol with randomized quotes based on current chart's symbol."
|
||
|
#property script_show_inputs
|
||
|
|
||
|
#include "..\..\Include\PRTF.mqh"
|
||
|
|
||
|
enum RANDOMIZATION
|
||
|
{
|
||
|
ORIGINAL,
|
||
|
RANDOM_WALK,
|
||
|
FUZZY_WEAK,
|
||
|
FUZZY_STRONG,
|
||
|
};
|
||
|
|
||
|
input string CustomPath = "MQL5Book\\Part7"; // Custom Symbol Folder
|
||
|
input RANDOMIZATION RandomFactor = RANDOM_WALK;
|
||
|
input datetime _From; // From (default: 120 days back)
|
||
|
input datetime _To; // To (default: up to now)
|
||
|
input uint RandomSeed = 0; // Random Seed (0 means random)
|
||
|
|
||
|
datetime From;
|
||
|
datetime To;
|
||
|
const string CustomSymbol = _Symbol + "." + EnumToString(RandomFactor)
|
||
|
+ (RandomSeed ? "_" + (string)RandomSeed : "");
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Script program start function |
|
||
|
//+------------------------------------------------------------------+
|
||
|
void OnStart()
|
||
|
{
|
||
|
From = _From == 0 ? TimeCurrent() - 60 * 60 * 24 * 120 : _From; // 120 days by default
|
||
|
To = _To == 0 ? TimeCurrent() / 60 * 60 : _To; // till current time
|
||
|
if(From > To)
|
||
|
{
|
||
|
Alert("Date range must include From <= To");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(RandomSeed != 0) MathSrand(RandomSeed);
|
||
|
|
||
|
bool custom = false;
|
||
|
if(PRTF(SymbolExist(CustomSymbol, custom)) && custom)
|
||
|
{
|
||
|
if(IDYES == MessageBox(StringFormat("Delete custom symbol '%s'?", CustomSymbol),
|
||
|
"Please, confirm", MB_YESNO))
|
||
|
{
|
||
|
if(CloseChartsForSymbol(CustomSymbol))
|
||
|
{
|
||
|
Sleep(500); // wait changes to take effect (unreliable)
|
||
|
// we need to wipe out rates because otherwise the rates
|
||
|
// remain in the base file on the disk for some time
|
||
|
// and can be picked up by the following creation procedure
|
||
|
PRTF(CustomRatesDelete(CustomSymbol, 0, LONG_MAX));
|
||
|
PRTF(SymbolSelect(CustomSymbol, false));
|
||
|
PRTF(CustomSymbolDelete(CustomSymbol));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(IDYES == MessageBox(StringFormat("Create new custom symbol '%s'?", CustomSymbol),
|
||
|
"Please, confirm", MB_YESNO))
|
||
|
{
|
||
|
if(PRTF(CustomSymbolCreate(CustomSymbol, CustomPath, _Symbol)))
|
||
|
{
|
||
|
if(RandomFactor == RANDOM_WALK)
|
||
|
{
|
||
|
CustomSymbolSetInteger(CustomSymbol, SYMBOL_DIGITS, 8);
|
||
|
}
|
||
|
|
||
|
CustomSymbolSetString(CustomSymbol, SYMBOL_DESCRIPTION, "Randomized quotes");
|
||
|
|
||
|
const int n = GenerateQuotes();
|
||
|
Print("Bars M1 generated: ", n);
|
||
|
if(n > 0)
|
||
|
{
|
||
|
SymbolSelect(CustomSymbol, true);
|
||
|
ChartOpen(CustomSymbol, PERIOD_M1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Chart management function |
|
||
|
//+------------------------------------------------------------------+
|
||
|
bool CloseChartsForSymbol(const string symbol)
|
||
|
{
|
||
|
long id = ChartFirst();
|
||
|
long match = -1;
|
||
|
while(id != -1)
|
||
|
{
|
||
|
if(ChartSymbol(id) == symbol)
|
||
|
{
|
||
|
if(id == ChartID())
|
||
|
{
|
||
|
Alert("Can't close itself: start this script on another chart");
|
||
|
return false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
match = id;
|
||
|
}
|
||
|
}
|
||
|
id = ChartNext(id);
|
||
|
if(match != -1)
|
||
|
{
|
||
|
ChartClose(match);
|
||
|
match = -1;
|
||
|
}
|
||
|
}
|
||
|
ResetLastError(); // clear CHART_NOT_FOUND (4103)
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Get value mangled with nonuniform random noice |
|
||
|
//+------------------------------------------------------------------+
|
||
|
double RandomWalk(const double p)
|
||
|
{
|
||
|
const static double factor[] = {0.0, 0.1, 0.01, 0.05};
|
||
|
const static double f = factor[RandomFactor] / 100;
|
||
|
const double r = (rand() - 16383.0) / 16384.0; // [-1,+1]
|
||
|
const int sign = r >= 0 ? +1 : -1;
|
||
|
|
||
|
if(r != 0)
|
||
|
{
|
||
|
return p + p * sign * f * sqrt(-log(sqrt(fabs(r))));
|
||
|
}
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Request, modify, commit MqlRates for custom symbol |
|
||
|
//+------------------------------------------------------------------+
|
||
|
int GenerateQuotes()
|
||
|
{
|
||
|
MqlRates rates[];
|
||
|
MqlRates zero = {};
|
||
|
datetime start;
|
||
|
double price;
|
||
|
|
||
|
if(RandomFactor != RANDOM_WALK)
|
||
|
{
|
||
|
// NB: terminal settings are in effect here, including bar limit
|
||
|
if(PRTF(CopyRates(_Symbol, PERIOD_M1, From, To, rates)) <= 0)
|
||
|
{
|
||
|
return 0; // error
|
||
|
}
|
||
|
if(RandomFactor == ORIGINAL)
|
||
|
{
|
||
|
return PRTF(CustomRatesReplace(CustomSymbol, From, To, rates));
|
||
|
}
|
||
|
price = rates[0].open;
|
||
|
start = rates[0].time;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ArrayResize(rates, (int)((To - From) / 60) + 1);
|
||
|
price = 1.0;
|
||
|
start = From - 60;
|
||
|
}
|
||
|
|
||
|
const int size = ArraySize(rates);
|
||
|
|
||
|
double hlc[3];
|
||
|
for(int i = 0; i < size; ++i)
|
||
|
{
|
||
|
if(RandomFactor == RANDOM_WALK)
|
||
|
{
|
||
|
rates[i] = zero;
|
||
|
rates[i].time = start += 60;
|
||
|
rates[i].open = price;
|
||
|
hlc[0] = RandomWalk(price);
|
||
|
hlc[1] = RandomWalk(price);
|
||
|
hlc[2] = RandomWalk(price);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
double delta = 0;
|
||
|
if(i > 0)
|
||
|
{
|
||
|
delta = rates[i].open - price; // cumulative correction
|
||
|
}
|
||
|
rates[i].open = price;
|
||
|
hlc[0] = RandomWalk(rates[i].high - delta);
|
||
|
hlc[1] = RandomWalk(rates[i].low - delta);
|
||
|
hlc[2] = RandomWalk(rates[i].close - delta);
|
||
|
}
|
||
|
ArraySort(hlc);
|
||
|
|
||
|
rates[i].high = fmax(hlc[2], rates[i].open);
|
||
|
rates[i].low = fmin(hlc[0], rates[i].open);
|
||
|
rates[i].close = price = hlc[1];
|
||
|
rates[i].tick_volume = 4;
|
||
|
}
|
||
|
|
||
|
return PRTF(CustomRatesReplace(CustomSymbol, From, To, rates));
|
||
|
}
|
||
|
//+------------------------------------------------------------------+
|