forked from rosh/MQL5Book
208 lines
6.9 KiB
MQL5
208 lines
6.9 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| FrameTransfer.mq5 |
| |||
//| Copyright (c) 2022, MetaQuotes Ltd. |
| |||
//| https://www.mql5.com |
| |||
//+------------------------------------------------------------------+
| |||
#property copyright "Copyright (c) 2022, MetaQuotes Ltd."
| |||
#property link "https://www.mql5.com"
| |||
#property description "Example of sending and receiving data frames during EA optimization."
| |||
| |||
#property tester_set "FrameTransfer.set"
| |||
#property tester_no_cache
| |||
| |||
#include "..\..\Include\MqlError.mqh"
| |||
| |||
input bool Parameter0;
| |||
input long Parameter1;
| |||
input double Parameter2;
| |||
input string Parameter3;
| |||
| |||
#define MY_FILE_ID 100
| |||
#define MY_TIME_ID 101
| |||
| |||
ulong startup; // single pass timing (just a demo data)
| |||
//+------------------------------------------------------------------+
| |||
//| Expert initialization function |
| |||
//+------------------------------------------------------------------+
| |||
int OnInit()
| |||
{
| |||
startup = GetMicrosecondCount();
| |||
MathSrand((uint)startup);
| |||
return INIT_SUCCEEDED;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Test completion event handler |
| |||
//+------------------------------------------------------------------+
| |||
double OnTester()
| |||
{
| |||
// send a file in one frame
| |||
const static string filename = "binfile";
| |||
int h = FileOpen(filename, FILE_WRITE | FILE_BIN | FILE_ANSI);
| |||
FileWriteString(h, StringFormat("Random: %d", MathRand()));
| |||
FileClose(h);
| |||
FrameAdd(filename, MY_FILE_ID, MathRand(), filename);
| |||
| |||
// send an array in another frame
| |||
ulong dummy[1];
| |||
dummy[0] = GetMicrosecondCount() - startup;
| |||
FrameAdd("timing", MY_TIME_ID, 0, dummy);
| |||
| |||
return (Parameter2 + 1) * (Parameter1 + 2);
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Adjust optimization range to the limit (to simplify the demo) |
| |||
//+------------------------------------------------------------------+
| |||
template<typename T>
| |||
void LimitParameterCount(const string name, const int limit)
| |||
{
| |||
bool enabled;
| |||
T value, start, step, stop;
| |||
if(ParameterGetRange(name, enabled, value, start, step, stop))
| |||
{
| |||
if(enabled && step > 0)
| |||
{
| |||
if((stop - start) / step > limit)
| |||
{
| |||
ParameterSetRange(name, enabled, value, start, step, start + step * limit);
| |||
}
| |||
}
| |||
}
| |||
}
| |||
| |||
int handle; // a file to collect all custom results
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Optimization start |
| |||
//+------------------------------------------------------------------+
| |||
void OnTesterInit()
| |||
{
| |||
handle = FileOpen("output.csv", FILE_WRITE | FILE_CSV | FILE_ANSI, ",");
| |||
LimitParameterCount<long>("Parameter1", 10);
| |||
LimitParameterCount<double>("Parameter2", 10);
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Receive data with MY_FILE_ID |
| |||
//+------------------------------------------------------------------+
| |||
void ProcessFileFrames()
| |||
{
| |||
static ulong framecount = 0; // our own counter of frame count
| |||
| |||
// frame fields
| |||
ulong pass;
| |||
string name;
| |||
long id;
| |||
double value;
| |||
// when the array is used to read a file from a frame,
| |||
// the datatype of the array must have a size that
| |||
// the file size is divisible by it without a remainder,
| |||
// so most universal types are:
| |||
// - uchar for binary files and ANSI text files
| |||
// - ushort for text files in Unicode
| |||
// otherwise you'll get INVALID_ARRAY(4006) error
| |||
uchar data[];
| |||
| |||
// input parameters for the pass where the frame belongs
| |||
string params[];
| |||
uint count;
| |||
| |||
ResetLastError();
| |||
| |||
while(FrameNext(pass, name, id, value, data))
| |||
{
| |||
PrintFormat("Pass: %lld Frame: %s Value:%f", pass, name, value);
| |||
if(id != MY_FILE_ID) continue;
| |||
if(FrameInputs(pass, params, count))
| |||
{
| |||
string header, record;
| |||
if(framecount == 0) // prepare CSV header
| |||
{
| |||
header = "Counter,Pass ID,";
| |||
}
| |||
record = (string)framecount + "," + (string)pass + ",";
| |||
// let collect values of optimized parameters
| |||
for(uint i = 0; i < count; i++)
| |||
{
| |||
string name2value[];
| |||
int n = StringSplit(params[i], '=', name2value);
| |||
if(n == 2)
| |||
{
| |||
long pvalue, pstart, pstep, pstop;
| |||
bool enabled = false;
| |||
if(ParameterGetRange(name2value[0], enabled, pvalue, pstart, pstep, pstop))
| |||
{
| |||
if(enabled)
| |||
{
| |||
if(framecount == 0) // prepare CSV header
| |||
{
| |||
header += name2value[0] + ",";
| |||
}
| |||
record += name2value[1] + ","; // data field
| |||
}
| |||
}
| |||
}
| |||
}
| |||
if(framecount == 0) // prepare CSV header
| |||
{
| |||
FileWriteString(handle, header + "Value,File Content\n");
| |||
}
| |||
// write data records into CSV
| |||
FileWriteString(handle, record + DoubleToString(value) + ","
| |||
+ CharArrayToString(data) + "\n");
| |||
}
| |||
framecount++;
| |||
}
| |||
| |||
if(_LastError != 4000 && _LastError != 0)
| |||
{
| |||
Print("Error: ", E2S(_LastError));
| |||
}
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Optimization pass (frame) |
| |||
//+------------------------------------------------------------------+
| |||
void OnTesterPass()
| |||
{
| |||
ProcessFileFrames(); // standard processing of frames
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| End of optimization |
| |||
//+------------------------------------------------------------------+
| |||
void OnTesterDeinit()
| |||
{
| |||
ProcessFileFrames(); // final clean-up: some frames may be late
| |||
FileClose(handle); // close CSV-file
| |||
| |||
ulong pass;
| |||
string name;
| |||
long id;
| |||
double value;
| |||
ulong data[]; // use the same datatype as it was at sending of the array
| |||
| |||
FrameFilter("timing", MY_TIME_ID); // rewind to first frames
| |||
| |||
ulong count = 0;
| |||
ulong total = 0;
| |||
// loop through 'timing' frames only
| |||
while(FrameNext(pass, name, id, value, data))
| |||
{
| |||
if(ArraySize(data) == 1)
| |||
{
| |||
total += data[0];
| |||
}
| |||
else
| |||
{
| |||
total += (ulong)value;
| |||
}
| |||
++count;
| |||
}
| |||
if(count > 0)
| |||
{
| |||
PrintFormat("Average timing: %lld", total / count);
| |||
}
| |||
}
| |||
//+------------------------------------------------------------------+
|