MQL5Book/Experts/p6/FrameTransfer.mq5
super.admin 1c8e83ce31 convert
2025-05-30 16:09:41 +02:00

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