NN_in_Trading/Experts/DPCC/Test.mq5
483 lines
21 KiB
MQL5
DNG
2026-10-22 14:57:41 +03:00
//+------------------------------------------------------------------+
//| Test.mq5 |
//| Copyright 2026, DNG |
//| https://www.mql5.com/ru/users/dng |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, DNG"
#property version "3.00"
#property description "Evaluates a frozen DPCC Action6 policy in Strategy Tester"
#property description "Uses the chart symbol, configured timeframe, and current account context"
#property description "Runs across the configured tester interval"
#property strict
#define DPCC_TRAJECTORY_ENTRY_STUDY_FORECAST
#include "Trajectory.mqh"
#undef DPCC_TRAJECTORY_ENTRY_STUDY_FORECAST
input group "---- Таймфрейм данных ----"
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_CURRENT; // Таймфрейм; PERIOD_CURRENT — таймфрейм графика/тестера.
input group "---- Режим оценки и исполнение ----"
input ENUM_DPCC_MODE InpMode = DPCC_P0; // Режим DPCC: P0, P1 или P2.
input long InpMagic = 33062; // Magic-номер заявок этого советника.
input group "---- Замер производительности ----"
input bool InpMeasureLatency = false; // Измерять задержку принятия решения.
const string DPCC_PACKAGE_ROOT = "DPCC/StudyForecast_M05_20261017";
const bool DPCC_COMMON_FILES = true;
//+------------------------------------------------------------------+
//| Frozen policy owners used throughout the Tester interval |
//+------------------------------------------------------------------+
CDPCCStudyForecastCore EXTMarket;
CDPCCAction6ChartContextOwner EXTDecisionContext;
CNet *EXTActor = NULL;
CNet *EXTQ1 = NULL;
CNet *EXTQ2 = NULL;
SDPCCExecutionControl EXTControl;
SDPCCRuntimeConfig EXTRuntime;
float EXTTradingLimits[];
CBufferFloat EXTCurrentZ;
CBufferFloat EXTActorAccount;
int EXTIndicators[4];
long EXTBarOpen = 0;
long EXTFirstDecision = 0;
double EXTInitialBalance = 0;
double EXTInitialEquity = 0;
uint EXTDecisions = 0;
ulong EXTNoiseSeed = 0;
double EXTTPCapPoints = 0;
double EXTSLCapPoints = 0;
bool EXTReady = false;
bool EXTStopped = false;
bool EXTWindowCompleted = false;
string EXTMarketSHA = "";
string EXTTradingSHA = "";
ulong EXTLatencySamples = 0;
ulong EXTMarketLatencyUs = 0;
ulong EXTActorLatencyUs = 0;
ulong EXTExecutionLatencyUs = 0;
ulong EXTDecisionLatencyUs = 0;
ulong EXTDecisionLatencyMaxUs = 0;
//+------------------------------------------------------------------+
//| Stop before another decision or execution request |
//+------------------------------------------------------------------+
void DPCCTestStop(const string phase, const string reason)
{
EXTStopped = true;
Print("DPCC_TEST_BLOCKED phase=", phase, " reason=", reason, " bar=", EXTBarOpen);
if(MQLInfoInteger(MQL_TESTER))
TesterStop();
}
//+------------------------------------------------------------------+
//| Shared D01 restore boundary for Market and Trading inference |
//| roles |
//+------------------------------------------------------------------+
bool DPCCTestRestoreFrozenTrading(string &reason)
{
CNet *target_actor = NULL;
CNet *target_q1 = NULL;
CNet *target_q2 = NULL;
SDPCCAction6TradingRestore restored;
if(!DPCCLoadAction6Trading(EXTControl, DPCC_COMMON_FILES, EXTMarket, EXTActor, EXTQ1, EXTQ2,
target_actor, target_q1, target_q2, restored, reason))
return(false);
//--- Test keeps only the frozen online policy and its ranking dependencies.
delete target_actor;
delete target_q1;
delete target_q2;
EXTRuntime = restored.runtime;
EXTMarketSHA = restored.market_sha256;
EXTTradingSHA = restored.trading_sha256;
EXTNoiseSeed = restored.diffusion_seed;
EXTTPCapPoints = MaxTP;
EXTSLCapPoints = MaxSL;
if(ArrayCopy(EXTTradingLimits, restored.limits) != 24)
{
reason = "test.restore.financial_limits_capacity";
return(false);
}
if(EXTNoiseSeed == 0)
{
reason = "BLOCKED:test.assigned_profile.diffusion.seed_must_be_nonzero_for_Action6";
return(false);
}
reason = "";
return(true);
}
//+------------------------------------------------------------------+
//| Shared physical-account and causal financial-context boundary |
//+------------------------------------------------------------------+
bool DPCCTestDecisionContext(const long decision_open, CBufferFloat *native_ohlc,
CBufferFloat *&context, float &account13[], string &reason)
{
return(EXTDecisionContext.Build(EXTRuntime, EXTTradingLimits, native_ohlc,
decision_open, context, account13, reason));
}
//+------------------------------------------------------------------+
//| Verify frozen Market, Actor and the two inference-only critics |
//+------------------------------------------------------------------+
bool DPCCTestFrozenPolicy(string &reason)
{
CNet *market = EXTMarket.ReadyMarketNetwork();
CNet *networks[] = {market, EXTActor, EXTQ1, EXTQ2};
const int layers[] = {8, 2, 5, 5};
COpenCLMy *context = EXTMarket.ReadyEncoderContext();
if(CheckPointer(context) == POINTER_INVALID)
{
reason = "test.frozen.market_context";
return(false);
}
for(int i = 0; i < ArraySize(networks); i++)
{
if(CheckPointer(networks[i]) == POINTER_INVALID || !networks[i].IsOpenCLTransferReady() ||
networks[i].GetOpenCL() != context || networks[i].WeightsUpdateEnabled() ||
networks[i].Layer(layers[i]) != NULL)
{
reason = StringFormat("test.frozen.graph_or_permission.%d", i);
return(false);
}
for(int j = 0; j < i; j++)
if(networks[i] == networks[j])
{
reason = "test.frozen.network_alias";
return(false);
}
for(int l = 0; l < layers[i]; l++)
{
CNeuronBaseOCL *layer = networks[i].Layer(l);
if(CheckPointer(layer) == POINTER_INVALID || layer.GetOpenCL() != context || layer.TrainMode())
{
reason = StringFormat("test.frozen.layer.%d.%d", i, l);
return(false);
}
}
}
CNeuronDPCCOCL *actor = dynamic_cast<CNeuronDPCCOCL*>(EXTActor.Layer(1));
if(!actor || actor.Type() != defNeuronDPCCOCL || actor.ActorABI() != 3 ||
!NNQCriticGraph(EXTQ1) || !NNQCriticGraph(EXTQ2))
{
reason = "test.frozen.Action6_graph";
return(false);
}
reason = "";
return(true);
}
//+------------------------------------------------------------------+
//| Allocate the live Market Z snapshot from its actual frozen axes |
//+------------------------------------------------------------------+
bool DPCCTestPrepareMarketSnapshot(string &reason)
{
CNet *market = EXTMarket.ReadyMarketNetwork();
COpenCLMy *context = EXTMarket.ReadyEncoderContext();
CNeuronScenarioForecastPriceOCL *forecast = NULL;
if(CheckPointer(market) != POINTER_INVALID)
forecast = dynamic_cast<CNeuronScenarioForecastPriceOCL*>(market.Layer(6));
ulong count = 0;
if(!forecast || CheckPointer(context) == POINTER_INVALID || forecast.GetOpenCL() != context ||
forecast.Type() != defNeuronScenarioForecastPriceOCL || forecast.CodebookUpdate() ||
!DPCCRolloutProduct(forecast.Scenarios(), forecast.Variables(), count) ||
!DPCCRolloutProduct(count, forecast.Horizon(), count) ||
!DPCCRolloutProduct(count, forecast.Dimension(), count) || count == 0 ||
count > (ulong)INT_MAX || count > (ulong)UINT_MAX / sizeof(float))
{
reason = "test.market.snapshot_axes_or_frozen_contract";
return(false);
}
if(!EXTCurrentZ.BufferInit((uint)count, 0) || !EXTCurrentZ.BufferCreate(context))
{
reason = "test.market.snapshot_allocation";
return(false);
}
reason = "";
return(true);
}
//+------------------------------------------------------------------+
//| Close the Actor frame before releasing its Market generation |
//+------------------------------------------------------------------+
bool DPCCTestReleaseGeneration(const ulong generation, string &reason)
{
if(generation == 0)
{
reason = "test.market.zero_generation";
return(false);
}
CNeuronDPCCOCL *actor = CheckPointer(EXTActor) == POINTER_INVALID ? NULL :
dynamic_cast<CNeuronDPCCOCL*>(EXTActor.Layer(1));
if(!actor || !actor.CancelGeneration())
{
reason = "test.Actor_frame_release";
return(false);
}
return(EXTMarket.ReleaseReadyEncoderZ(generation, reason));
}
//+------------------------------------------------------------------+
//| Initialize the assigned frozen policy for February 2026 |
//+------------------------------------------------------------------+
int OnInit(void)
{
for(int i = 0; i < 4; i++)
EXTIndicators[i] = INVALID_HANDLE;
string reason = "";
//--- Configure the package and load its embedded Market profile before restoration.
if(!DPCCConfigureExecutionControl(DPCC_PACKAGE_ROOT, InpTimeframe, DPCC_STAGE_TEST, "",
0, DPCC_RESUME_NEW, InpMode, DPCC_STAGE_TEST, EXTControl, reason) ||
!DPCCLoadMarketExecutionProfile(EXTControl, DPCC_COMMON_FILES, reason))
{
DPCCTestStop("execution_control", reason);
return(INIT_PARAMETERS_INCORRECT);
}
if(InpMagic < 1)
{
DPCCTestStop("profile_control", "H1_frozen_Test_without_update_budget_required");
return(INIT_PARAMETERS_INCORRECT);
}
if(!DPCCTesterOnly(reason) || !DPCCRequireTesterOneDirection(reason))
{
DPCCTestStop("environment", reason);
return(INIT_FAILED);
}
if(_Symbol == "" || AccountInfoString(ACCOUNT_CURRENCY) == "" ||
PositionsTotal() != 0 || OrdersTotal() != 0)
{
DPCCTestStop("environment", "flat_owned_Tester_start_required");
return(INIT_FAILED);
}
//--- D01 role validation and shared-context restoration belong to the shared loader.
if(!DPCCTestRestoreFrozenTrading(reason))
{
DPCCTestStop("D01_frozen_restore", reason);
return(INIT_FAILED);
}
if(!DPCCResolverLowerSha256(EXTMarketSHA) || !DPCCResolverLowerSha256(EXTTradingSHA) ||
EXTNoiseSeed == 0 || !MathIsValidNumber(EXTTPCapPoints) || EXTTPCapPoints <= 0 ||
!MathIsValidNumber(EXTSLCapPoints) || EXTSLCapPoints <= 0 ||
EXTRuntime.financial.mode != EXTControl.mode || !DPCCTestFrozenPolicy(reason))
{
DPCCTestStop("frozen_policy", reason == "" ? "authenticated_policy_contract_required" : reason);
return(INIT_FAILED);
}
CNeuronDPCCOCL *actor = dynamic_cast<CNeuronDPCCOCL*>(EXTActor.Layer(1));
if(!actor.BindPolicyNetworks(EXTMarket.ReadyMarketNetwork(), EXTQ1, EXTQ2))
{
DPCCTestStop("frozen_policy", "Actor_inference_dependencies");
return(INIT_FAILED);
}
if(!DPCCTestPrepareMarketSnapshot(reason))
{
DPCCTestStop("market_snapshot", reason);
return(INIT_FAILED);
}
if(!EXTActorAccount.BufferInit(13, 0) ||
!EXTActorAccount.BufferCreate(EXTMarket.ReadyEncoderContext()))
{
DPCCTestStop("account_input", "persistent_Account13_buffer");
return(INIT_FAILED);
}
EXTIndicators[0] = iRSI(_Symbol, EXTControl.timeframe, RSIPeriod, RSIPrice);
EXTIndicators[1] = iCCI(_Symbol, EXTControl.timeframe, CCIPeriod, CCIPrice);
EXTIndicators[2] = iATR(_Symbol, EXTControl.timeframe, ATRPeriod);
EXTIndicators[3] = iMACD(_Symbol, EXTControl.timeframe, FastPeriod, SlowPeriod, SignalPeriod, MACDPrice);
for(int i = 0; i < 4; i++)
if(EXTIndicators[i] == INVALID_HANDLE)
{
DPCCTestStop("indicators", (string)GetLastError());
return(INIT_FAILED);
}
EXTInitialBalance = AccountInfoDouble(ACCOUNT_BALANCE);
EXTInitialEquity = AccountInfoDouble(ACCOUNT_EQUITY);
if(!MathIsValidNumber(EXTInitialBalance) || !MathIsValidNumber(EXTInitialEquity) ||
EXTInitialBalance < 50 || EXTInitialEquity < 50)
{
DPCCTestStop("account", "actual_Tester_start_below_floor");
return(INIT_FAILED);
}
EXTReady = true;
Print("DPCC_TEST_READY policy=FROZEN_ACTION6 Q1_Q2=FROZEN_INFERENCE_ONLY tester_interval=FULL",
" market_sha256=", EXTMarketSHA,
" trading_sha256=", EXTTradingSHA, " profile_sha256=", EXTControl.profile_sha256);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Evaluate the first quote of each H1 bar in the Tester interval |
//+------------------------------------------------------------------+
void OnTick(void)
{
if(!EXTReady || EXTStopped || IsStopped())
return;
const long open = (long)iTime(_Symbol, EXTControl.timeframe, 0);
if(open <= 0 || open == EXTBarOpen)
return;
string reason = "";
if(!DPCCRequireTesterOneDirection(reason))
{
DPCCTestStop("environment", reason);
return;
}
if(iVolume(_Symbol, EXTControl.timeframe, 0) != 1 ||
(EXTBarOpen > 0 && (long)iTime(_Symbol, EXTControl.timeframe, 1) != EXTBarOpen))
{
DPCCTestStop("chronology", "first_H1_quote_required_no_hindsight_repair");
return;
}
const double balance = AccountInfoDouble(ACCOUNT_BALANCE);
const double equity = AccountInfoDouble(ACCOUNT_EQUITY);
if(!MathIsValidNumber(balance) || !MathIsValidNumber(equity) || balance < 50 || equity < 50)
{
DPCCTestStop("floor", "actual_balance_or_equity_below_50");
return;
}
//--- Capture the closed predecessor and only the observed first quote of its successor.
const ulong decision_started = GetMicrosecondCount();
SDPCCClosedHistoryRow closed;
MqlTick tick;
long quote_mode = -1;
double tick_size = 0;
if(!DPCCCaptureTesterClosedRow(_Symbol, EXTIndicators, 1, closed, reason) ||
closed.successor_open_server != open || !SymbolInfoTick(_Symbol, tick) ||
tick.time_msc < open * 1000 || tick.time_msc >= (open + 3600) * 1000 ||
!SymbolInfoInteger(_Symbol, SYMBOL_CHART_MODE, quote_mode) ||
!SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE, tick_size) ||
(quote_mode != SYMBOL_CHART_MODE_BID && quote_mode != SYMBOL_CHART_MODE_LAST))
{
DPCCTestStop("closed_history", reason == "" ? "causal_H1_quote_binding" : reason);
return;
}
//--- Replay pre-start chart history before the first policy decision.
if(EXTDecisions == 0 &&
!DPCCPrepareReadyMarketLiveStart(EXTMarket, _Symbol, EXTIndicators,
closed.bar.server_open, reason))
{
DPCCTestStop("market_warmup", reason);
return;
}
const double first_quote = (quote_mode == SYMBOL_CHART_MODE_LAST ? tick.last : tick.bid);
ulong generation = 0;
const ulong market_started = GetMicrosecondCount();
if(!EXTMarket.InferReadyMarketLive(closed, first_quote, tick.time_msc, tick_size,
GetPointer(EXTCurrentZ), generation, reason))
{
DPCCTestStop("market_inference", reason);
return;
}
const ulong market_us = GetMicrosecondCount() - market_started;
//--- Build the physical context without future bars and evaluate the frozen policy.
CBufferFloat *context = NULL;
float account[], action[];
bool prepared = DPCCTestDecisionContext(open, EXTMarket.ReadyMarketNativeOHLC(), context, account, reason);
if(prepared)
prepared = (ArraySize(account) == 13 && CheckPointer(context) != POINTER_INVALID);
if(prepared)
for(int i = 0; i < 13; i++)
if(!MathIsValidNumber(account[i]) || !EXTActorAccount.Update(i, account[i]))
{
reason = "test.Account13_input";
prepared = false;
break;
}
CNeuronDPCCOCL *actor = dynamic_cast<CNeuronDPCCOCL*>(EXTActor.Layer(1));
SDPCCDecisionMeta meta;
ZeroMemory(meta);
meta.decision_id = (ulong)EXTDecisions + 1;
meta.market_state_id = generation;
meta.q1_state_id = meta.decision_id;
meta.q2_state_id = meta.decision_id + 1;
meta.noise_seed = EXTNoiseSeed;
meta.decision_server_time = open;
meta.bar_index = (long)EXTDecisions;
meta.mode = (uint)EXTControl.mode;
meta.market_sha256 = EXTMarketSHA;
meta.profile_sha256 = EXTControl.profile_sha256;
const ulong actor_started = GetMicrosecondCount();
if(prepared)
prepared = EXTActorAccount.BufferWrite() && actor.BindDecisionContext(context, meta);
bool actor_forwarded = false;
if(prepared)
actor_forwarded = EXTActor.feedForward(GetPointer(EXTActorAccount), 1, false,
EXTMarket.ReadyMarketNetwork(), 6);
if(prepared)
{
string action_reason = "";
prepared = DPCCResolveAction6Output(actor, actor_forwarded, action, action_reason);
if(!prepared)
reason = "test." + action_reason;
}
if(!prepared && reason == "")
reason = "test.Action6_forward_or_output";
const ulong actor_us = GetMicrosecondCount() - actor_started;
string release_reason = "";
const bool released = DPCCTestReleaseGeneration(generation, release_reason);
if(!prepared || !released)
{
DPCCTestStop("policy_generation", !released ? "release:" + release_reason + ";" + reason : reason);
return;
}
//--- Execute only the published first Action6 through the existing Tester adapter.
const ulong execution_started = GetMicrosecondCount();
if(!DPCCExecuteTesterAction(_Symbol, InpMagic, action, EXTTPCapPoints, EXTSLCapPoints, reason))
{
DPCCTestStop("Tester_execution", reason);
return;
}
const ulong execution_us = GetMicrosecondCount() - execution_started;
const ulong decision_us = GetMicrosecondCount() - decision_started;
EXTBarOpen = open;
if(EXTFirstDecision == 0)
EXTFirstDecision = open;
EXTDecisions++;
if(InpMeasureLatency)
{
EXTLatencySamples++;
EXTMarketLatencyUs += market_us;
EXTActorLatencyUs += actor_us;
EXTExecutionLatencyUs += execution_us;
EXTDecisionLatencyUs += decision_us;
EXTDecisionLatencyMaxUs = MathMax(EXTDecisionLatencyMaxUs, decision_us);
}
}
//+------------------------------------------------------------------+
//| Mark normal completion of the Strategy Tester interval |
//+------------------------------------------------------------------+
double OnTester(void)
{
if(EXTReady && !EXTStopped)
EXTWindowCompleted = true;
return(0.0);
}
//+------------------------------------------------------------------+
//| Report observed results and release policy borrowers first |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(EXTReady)
Print("DPCC_TEST_RESULT decisions=", EXTDecisions, " first_server_open=", EXTFirstDecision,
" window_completed=", EXTWindowCompleted, " stopped=", EXTStopped,
" initial_balance=", DoubleToString(EXTInitialBalance, 2),
" final_balance=", DoubleToString(AccountInfoDouble(ACCOUNT_BALANCE), 2),
" initial_equity=", DoubleToString(EXTInitialEquity, 2),
" final_equity=", DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY), 2),
" market_sha256=", EXTMarketSHA, " trading_sha256=", EXTTradingSHA, " deinit_reason=", reason);
if(InpMeasureLatency && EXTLatencySamples > 0)
PrintFormat("DPCC_TEST_LATENCY samples=%I64u market_mean_us=%.2f " +
"actor_mean_us=%.2f execution_mean_us=%.2f " +
"total_mean_us=%.2f total_max_us=%I64u",
EXTLatencySamples, (double)EXTMarketLatencyUs / (double)EXTLatencySamples,
(double)EXTActorLatencyUs / (double)EXTLatencySamples,
(double)EXTExecutionLatencyUs / (double)EXTLatencySamples,
(double)EXTDecisionLatencyUs / (double)EXTLatencySamples, EXTDecisionLatencyMaxUs);
EXTDecisionContext.Reset();
if(CheckPointer(EXTActor) != POINTER_INVALID)
delete EXTActor;
EXTActor = NULL;
if(CheckPointer(EXTQ2) != POINTER_INVALID)
delete EXTQ2;
EXTQ2 = NULL;
if(CheckPointer(EXTQ1) != POINTER_INVALID)
delete EXTQ1;
EXTQ1 = NULL;
for(int i = 0; i < 4; i++)
if(EXTIndicators[i] != INVALID_HANDLE)
IndicatorRelease(EXTIndicators[i]);
}
//+------------------------------------------------------------------+