2026-08-20 19:42:54 +03:00 | | | //+------------------------------------------------------------------+
|
| | | //| Test.mq5 |
|
| | | //+------------------------------------------------------------------+
|
| | | #property copyright "Copyright DNG®"
|
| | | #property link "https://www.mql5.com/ru/users/dng"
|
| | | #property version "1.00"
|
| | | #property strict
|
| | | #include "Trajectory.mqh"
|
| | | //---
|
| | | input group "---- D2Skill OOS ----"
|
| | | input ENUM_D2SKILL_STAGE InpD2SkillStage = D2Skill_D2_STAGE_AUGMENTED; //Lifecycle stage
|
| | | input ENUM_D2SKILL_MODE InpD2SkillExecutionMode = D2_INFERENCE; //Bank execution mode
|
2026-08-22 20:08:02 +03:00 | | | input ED2SkillRepresentation InpD2SkillRepresentation = D2SkillDirectionMagnitude; //Bank representation
|
| | | input bool InpD2SkillResetBanksOnRepresentationMismatch =
|
| | | false; //Reset incompatible banks
|
| | | input bool InpD2SkillRecreateIncompatibleCheckpoint =
|
| | | false; //Recreate incompatible policy checkpoint
|
2026-08-20 19:42:54 +03:00 | | | datetime Start = 0;
|
| | | datetime End = 0;
|
| | | int Epochs = 0;
|
| | | CNet Actor;
|
| | | CBufferFloat State, TimeState, Account, Action;
|
| | | double PreviousBalance = 0, PreviousEquity = 0;
|
| | | bool D2SkillTestInitialized = false;
|
| | | ulong OOSTransitions = 0, OOSExecutableActions = 0;
|
| | | double OOSInitialBalance = 0;
|
| | | datetime OOSStartTime = 0;
|
| | | double OOSInitialEquity = 0, OOSPeakEquity = 0, OOSMaximumDrawdown = 0;
|
| | | double OOSReward = 0;
|
| | | ulong OOSTradeCount = 0;
|
| | | ulong OOSGpuBoundaryLatencyTotal = 0, OOSGpuBoundaryLatencyMaximum = 0;
|
| | | ulong OOSGpuBoundaryLatencySamples = 0;
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | bool D2SkillWriteD2OOSHeader(const int handle)
|
| | | {
|
| | | if(handle == INVALID_HANDLE)
|
| | | ReturnFalse;
|
| | | if(FileWrite(handle,
|
| | | "run_time", "symbol", "timeframe", "stage", "stage_name",
|
| | | "mode", "mode_name", "execution_mode", "execution_mode_name", "utility_aware", "min_utility", "utility_scale",
|
| | | "utility_source", "initial_balance", "final_balance", "initial_equity",
|
| | | "final_equity", "reward_equity_delta", "pnl_balance_delta", "max_drawdown",
|
| | | "max_drawdown_percent", "closed_trades", "transitions", "executable_actions",
|
| | | "comparison_eligible", "task_diagnostics_valid", "task_diagnostics_error",
|
| | | "task_retrievals", "task_no_skill", "task_retrieval_rate", "task_no_skill_rate",
|
| | | "task_promotions", "task_replacements", "step_diagnostics_valid",
|
| | | "step_diagnostics_error", "step_retrievals", "step_no_skill",
|
| | | "step_retrieval_rate", "step_no_skill_rate", "step_promotions",
|
| | | "step_replacements", "gpu_boundary_latency_us_avg", "gpu_boundary_latency_us_max",
|
| | | "paired_terminal_updates", "task_selection_valid", "selected_task_slot",
|
| | | "selected_task_score", "step_selection_valid", "selected_step_slot",
|
| | | "selected_step_score") <= 0)
|
| | | ReturnFalse;
|
| | | return(true);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | string D2SkillD2StageToString(const int stage)
|
| | | {
|
| | | if(stage == D2Skill_D2_STAGE_BASE)
|
| | | return("base");
|
| | | if(stage == D2Skill_D2_STAGE_FORMATION)
|
| | | return("formation");
|
| | | if(stage == D2Skill_D2_STAGE_HINDSIGHT)
|
| | | return("hindsight");
|
| | | if(stage == D2Skill_D2_STAGE_AUGMENTED)
|
| | | return("augmented");
|
| | | if(stage == D2Skill_D2_STAGE_ONLINE)
|
| | | return("online");
|
| | | return("unknown");
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | string D2SkillD2ModeToString(const ENUM_D2SKILL_BANK_MODE mode)
|
| | | {
|
| | | if(mode == D2Skill_D2_MODE_BASE)
|
| | | return("base");
|
| | | if(mode == D2Skill_D2_MODE_TASK)
|
| | | return("task");
|
| | | if(mode == D2Skill_D2_MODE_STEP)
|
| | | return("step");
|
| | | if(mode == D2Skill_D2_MODE_FULL)
|
| | | return("full");
|
| | | return("unknown");
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | string D2SkillD2ExecutionModeToString(const ENUM_D2SKILL_MODE mode)
|
| | | {
|
| | | switch(mode)
|
| | | {
|
| | | case D2_DISABLED:
|
| | | return("disabled");
|
| | | case D2_COLLECT:
|
| | | return("collect");
|
| | | case D2_EVALUATE:
|
| | | return("evaluate");
|
| | | case D2_INFERENCE:
|
| | | return("inference");
|
| | | case D2_ONLINE_CALIBRATION:
|
| | | return("online_calibration");
|
| | | }
|
| | | return("unknown");
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | string D2SkillD2UtilitySource(void)
|
| | | {
|
| | | if(D2SkillD2UtilityMode == D2Skill_D2_UTILITY_PAIRED_HINDSIGHT)
|
| | | return("paired_terminal");
|
| | | return("disabled");
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | bool D2SkillReadD2Selection(const bool task_bank, int &selected_slot, float &selected_score)
|
| | | {
|
| | | selected_slot = -1;
|
| | | selected_score = 0.0f;
|
| | | CNeuronBaseOCL *layer = Actor.Layer(1);
|
| | | if(!layer || layer.Type() != defNeuronD2Skill)
|
| | | ReturnFalse;
|
| | | CD2Skill *skill = (CD2Skill*)layer;
|
| | | if(!skill || !skill.Ready())
|
| | | ReturnFalse;
|
| | | CD2SkillBank *bank = (task_bank ? skill.TaskBank() : skill.StepBank());
|
2026-08-26 18:00:29 +03:00 | | | if(!bank)
|
| | | ReturnFalse;
|
| | | //--- BASE/disabled branches intentionally expose no device selection state.
|
| | | //--- OOS renders this normal control flow as selection_unavailable / NA.
|
| | | if(!bank.Enabled())
|
| | | return(false);
|
| | | if(!bank.SelectedSlot() || !bank.SelectedScore())
|
2026-08-20 19:42:54 +03:00 | | | ReturnFalse;
|
| | | if(!bank.SelectedSlot().BufferRead() || !bank.SelectedScore().BufferRead() ||
|
| | | bank.SelectedSlot().Total() != 1 || bank.SelectedScore().Total() != 1)
|
| | | ReturnFalse;
|
| | | selected_slot = (int)bank.SelectedSlot()[0];
|
| | | selected_score = bank.SelectedScore()[0];
|
| | | return(selected_slot >= 0 && (uint)selected_slot < bank.Slots() &&
|
| | | MathIsValidNumber(double(selected_score)) && selected_score > -1.0e30f);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Reads diagnostic counters only at the OOS reporting boundary. |
|
| | | //+------------------------------------------------------------------+
|
| | | bool D2SkillReadD2OOSBankStats(const bool task_bank, ulong &retrievals,
|
| | | ulong &no_skill, ulong &promotions,
|
| | | ulong &replacements)
|
| | | {
|
| | | retrievals = 0;
|
| | | no_skill = 0;
|
| | | promotions = 0;
|
| | | replacements = 0;
|
| | | CNeuronBaseOCL *layer = Actor.Layer(1);
|
| | | if(!layer || layer.Type() != defNeuronD2Skill)
|
| | | ReturnFalse;
|
| | | CD2Skill *skill = (CD2Skill*)layer;
|
| | | if(!skill || !skill.Ready())
|
| | | ReturnFalse;
|
| | | CD2SkillBank *bank = (task_bank ? skill.TaskBank() : skill.StepBank());
|
2026-08-26 18:00:29 +03:00 | | | //--- Base/disabled branches intentionally have no device diagnostics. Their
|
| | | //--- counters remain zero and OOS reporting must not allocate scratch to read
|
| | | //--- them.
|
| | | if(!bank)
|
| | | ReturnFalse;
|
| | | if(!bank.Enabled())
|
| | | return(true);
|
| | | if(!bank.RefreshDiagnostics() || !bank.Diagnostics() ||
|
2026-08-20 19:42:54 +03:00 | | | !bank.Diagnostics().BufferRead() || bank.Diagnostics().Total() != D2SKILL_DIAGNOSTICS)
|
| | | ReturnFalse;
|
| | | CBufferFloat *diagnostics = bank.Diagnostics();
|
| | | retrievals = (ulong)MathMax(0.0, double(diagnostics[0]));
|
| | | no_skill = (ulong)MathMax(0.0, double(diagnostics[1]));
|
| | | promotions = (ulong)MathMax(0.0, double(diagnostics[4]));
|
| | | replacements = (ulong)MathMax(0.0, double(diagnostics[7]));
|
| | | return(true);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | string D2SkillD2OOSCounterText(const bool valid, const ulong value)
|
| | | {
|
| | | return(valid ? StringFormat("%I64u", value) : "NA");
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | string D2SkillD2OOSRateText(const bool valid, const ulong numerator, const ulong denominator)
|
| | | {
|
| | | if(!valid)
|
| | | return("NA");
|
| | | return(StringFormat("%.8f", (denominator > 0 ? double(numerator) / denominator : 0.0)));
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | string D2SkillD2OOSReadError(const bool valid)
|
| | | {
|
| | | return(valid ? "none" : "bank_or_diagnostics_unavailable");
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | bool D2SkillWriteD2OOSBankConfiguration(const int handle, const string prefix,
|
| | | CD2SkillBank *bank)
|
| | | {
|
| | | if(handle == INVALID_HANDLE)
|
| | | ReturnFalse;
|
| | | if(!bank)
|
| | | {
|
| | | FileWrite(handle, prefix + "_configuration_valid=false");
|
| | | FileWrite(handle, prefix + "_configuration_error=bank_unavailable");
|
| | | ReturnFalse;
|
| | | }
|
| | | FileWrite(handle, prefix + "_configuration_valid=true");
|
| | | FileWrite(handle, StringFormat("%s_slots=%u", prefix, bank.Slots()));
|
| | | FileWrite(handle, StringFormat("%s_dimension=%u", prefix, bank.Dimension()));
|
| | | FileWrite(handle, StringFormat("%s_representation=%d", prefix, bank.Representation()));
|
| | | FileWrite(handle, StringFormat("%s_alpha=%f", prefix, bank.Alpha()));
|
| | | FileWrite(handle, StringFormat("%s_utility_aware=%s", prefix,
|
| | | (bank.UtilityAware() ? "true" : "false")));
|
| | | FileWrite(handle, StringFormat("%s_min_utility=%f", prefix, bank.MinUtility()));
|
| | | FileWrite(handle, StringFormat("%s_max_correction=%f", prefix, bank.MaxCorrection()));
|
| | | FileWrite(handle, StringFormat("%s_beta_correction=%f", prefix, bank.BetaCorrection()));
|
| | | FileWrite(handle, StringFormat("%s_beta_direction=%f", prefix, bank.BetaDirection()));
|
| | | FileWrite(handle, StringFormat("%s_beta_magnitude=%f", prefix, bank.BetaMagnitude()));
|
| | | //--- The following values are the fixed D2SkillEnableD2Banks reconstruction
|
| | | //--- contract; the Bank API intentionally has no duplicate read-only getters.
|
| | | FileWrite(handle, prefix + "_top_k=1");
|
| | | FileWrite(handle, prefix + "_similarity_threshold=0.65");
|
| | | FileWrite(handle, prefix + "_direction_threshold=0.00");
|
| | | FileWrite(handle, prefix + "_utility_weight=0.05");
|
| | | FileWrite(handle, prefix + "_beta_key=0.05");
|
| | | FileWrite(handle, prefix + "_beta_utility=0.05");
|
| | | FileWrite(handle, prefix + "_min_confirmations=3");
|
| | | FileWrite(handle, prefix + "_protection_age=32");
|
| | | FileWrite(handle, prefix + "_inactivity_age=256");
|
| | | return(true);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Counts closed position exits in the exact tested symbol window. |
|
| | | //+------------------------------------------------------------------+
|
| | | ulong D2SkillOOSClosedTrades(void)
|
| | | {
|
| | | if(OOSStartTime <= 0 || !HistorySelect(OOSStartTime, TimeCurrent()))
|
| | | return(0);
|
| | | ulong trades = 0;
|
| | | const uint total = HistoryDealsTotal();
|
| | | for(uint index = 0; index < total; index++)
|
| | | {
|
| | | const ulong deal = HistoryDealGetTicket(index);
|
| | | if(deal == 0 || HistoryDealGetString(deal, DEAL_SYMBOL) != _Symbol)
|
| | | continue;
|
| | | const ENUM_DEAL_ENTRY entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(deal, DEAL_ENTRY);
|
| | | if(entry == DEAL_ENTRY_OUT || entry == DEAL_ENTRY_OUT_BY || entry == DEAL_ENTRY_INOUT)
|
| | | trades++;
|
| | | }
|
| | | return(trades);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | bool D2SkillAppendOOSResultRow(const double final_balance, const double final_equity)
|
| | | {
|
| | | const int handle = FileOpen(D2Skill_OOS_FILE,
|
| | | FILE_WRITE | FILE_READ | FILE_CSV | FILE_ANSI | FILE_COMMON, ',');
|
| | | if(handle == INVALID_HANDLE)
|
| | | ReturnFalse;
|
| | | const bool need_header = (FileSize(handle) <= 0);
|
| | | FileSeek(handle, 0, SEEK_END);
|
| | | if(need_header && !D2SkillWriteD2OOSHeader(handle))
|
| | | {
|
| | | FileClose(handle);
|
| | | ReturnFalse;
|
| | | }
|
| | | int task_slot = -1, step_slot = -1;
|
| | | float task_score = 0.0f;
|
| | | float step_score = 0.0f;
|
| | | const bool task_selection_valid = D2SkillReadD2Selection(true, task_slot, task_score);
|
| | | const bool step_selection_valid = D2SkillReadD2Selection(false, step_slot, step_score);
|
| | | ulong task_retrievals = 0, task_no_skill = 0, task_promotions = 0, task_replacements = 0;
|
| | | ulong step_retrievals = 0, step_no_skill = 0, step_promotions = 0, step_replacements = 0;
|
| | | const bool task_diagnostics_valid = D2SkillReadD2OOSBankStats(true, task_retrievals, task_no_skill,
|
| | | task_promotions, task_replacements);
|
| | | const bool step_diagnostics_valid = D2SkillReadD2OOSBankStats(false, step_retrievals, step_no_skill,
|
| | | step_promotions, step_replacements);
|
| | | const ulong task_total = task_retrievals + task_no_skill;
|
| | | const ulong step_total = step_retrievals + step_no_skill;
|
| | | if(!task_diagnostics_valid || !step_diagnostics_valid)
|
| | | PrintFormat("D2Skill D2 OOS diagnostics unavailable: task=%s step=%s",
|
| | | D2SkillD2OOSReadError(task_diagnostics_valid),
|
| | | D2SkillD2OOSReadError(step_diagnostics_valid));
|
| | | if(!task_selection_valid || !step_selection_valid)
|
| | | PrintFormat("D2Skill D2 OOS selection unavailable: task=%s step=%s",
|
| | | (task_selection_valid ? "none" : "selection_unavailable"),
|
| | | (step_selection_valid ? "none" : "selection_unavailable"));
|
| | | const double drawdown_percent = (OOSPeakEquity > 0.0 ?
|
| | | 100.0 * OOSMaximumDrawdown / OOSPeakEquity : 0.0);
|
| | | const double average_latency = (OOSGpuBoundaryLatencySamples > 0 ?
|
| | | double(OOSGpuBoundaryLatencyTotal) / OOSGpuBoundaryLatencySamples : 0.0);
|
| | | FileWrite(handle,
|
| | | TimeToString(TimeCurrent(), TIME_DATE | TIME_MINUTES | TIME_SECONDS),
|
| | | _Symbol,
|
| | | EnumToString((ENUM_TIMEFRAMES)Period()),
|
| | | D2SkillD2Stage,
|
| | | D2SkillD2StageToString(D2SkillD2Stage),
|
| | | D2SkillD2Mode,
|
| | | D2SkillD2ModeToString(D2SkillD2Mode),
|
| | | D2SkillD2ExecutionMode,
|
| | | D2SkillD2ExecutionModeToString(D2SkillD2ExecutionMode),
|
| | | (D2SkillD2UtilityAware ? "true" : "false"),
|
| | | D2SkillD2MinUtility,
|
| | | D2SkillD2UtilityScale,
|
| | | D2SkillD2UtilitySource(),
|
| | | OOSInitialBalance,
|
| | | final_balance,
|
| | | OOSInitialEquity,
|
| | | final_equity,
|
| | | OOSReward,
|
| | | (final_balance - OOSInitialBalance),
|
| | | OOSMaximumDrawdown,
|
| | | drawdown_percent,
|
| | | OOSTradeCount,
|
| | | OOSTransitions,
|
| | | OOSExecutableActions,
|
| | | (OOSExecutableActions > 0 ? "true" : "false"),
|
| | | (task_diagnostics_valid ? "true" : "false"),
|
| | | D2SkillD2OOSReadError(task_diagnostics_valid),
|
| | | D2SkillD2OOSCounterText(task_diagnostics_valid, task_retrievals),
|
| | | D2SkillD2OOSCounterText(task_diagnostics_valid, task_no_skill),
|
| | | D2SkillD2OOSRateText(task_diagnostics_valid, task_retrievals, task_total),
|
| | | D2SkillD2OOSRateText(task_diagnostics_valid, task_no_skill, task_total),
|
| | | D2SkillD2OOSCounterText(task_diagnostics_valid, task_promotions),
|
| | | D2SkillD2OOSCounterText(task_diagnostics_valid, task_replacements),
|
| | | (step_diagnostics_valid ? "true" : "false"),
|
| | | D2SkillD2OOSReadError(step_diagnostics_valid),
|
| | | D2SkillD2OOSCounterText(step_diagnostics_valid, step_retrievals),
|
| | | D2SkillD2OOSCounterText(step_diagnostics_valid, step_no_skill),
|
| | | D2SkillD2OOSRateText(step_diagnostics_valid, step_retrievals, step_total),
|
| | | D2SkillD2OOSRateText(step_diagnostics_valid, step_no_skill, step_total),
|
| | | D2SkillD2OOSCounterText(step_diagnostics_valid, step_promotions),
|
| | | D2SkillD2OOSCounterText(step_diagnostics_valid, step_replacements),
|
| | | average_latency,
|
| | | OOSGpuBoundaryLatencyMaximum,
|
| | | D2SkillD2PairedUtilityUpdates,
|
| | | (task_selection_valid ? "true" : "false"),
|
| | | (task_selection_valid ? IntegerToString(task_slot) : "NA"),
|
| | | (task_selection_valid ? DoubleToString(task_score, 8) : "NA"),
|
| | | (step_selection_valid ? "true" : "false"),
|
| | | (step_selection_valid ? IntegerToString(step_slot) : "NA"),
|
| | | (step_selection_valid ? DoubleToString(step_score, 8) : "NA"));
|
| | | FileClose(handle);
|
| | | Print("D2Skill D2 OOS table: row appended to ", D2Skill_OOS_FILE);
|
| | | return(true);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | bool D2SkillWriteD2OOSManifest(const double final_balance, const double final_equity)
|
| | | {
|
| | | const int handle = FileOpen(D2Skill_OOS_MANIFEST_FILE, FILE_WRITE | FILE_TXT | FILE_ANSI | FILE_COMMON);
|
| | | if(handle == INVALID_HANDLE)
|
| | | ReturnFalse;
|
| | | FileWrite(handle, "format=D2Skill_D2_OOS");
|
| | | FileWrite(handle, StringFormat("run_time=%s", TimeToString(TimeCurrent(), TIME_DATE | TIME_MINUTES | TIME_SECONDS)));
|
| | | FileWrite(handle, StringFormat("symbol=%s", _Symbol));
|
| | | FileWrite(handle, StringFormat("timeframe=%s", EnumToString((ENUM_TIMEFRAMES)Period())));
|
| | | FileWrite(handle, StringFormat("stage=%d", D2SkillD2Stage));
|
| | | FileWrite(handle, StringFormat("stage_name=%s", D2SkillD2StageToString(D2SkillD2Stage)));
|
| | | FileWrite(handle, StringFormat("mode=%d", D2SkillD2Mode));
|
| | | FileWrite(handle, StringFormat("mode_name=%s", D2SkillD2ModeToString(D2SkillD2Mode)));
|
| | | FileWrite(handle, StringFormat("execution_mode=%d", D2SkillD2ExecutionMode));
|
| | | FileWrite(handle, StringFormat("execution_mode_name=%s",
|
| | | D2SkillD2ExecutionModeToString(D2SkillD2ExecutionMode)));
|
| | | FileWrite(handle, StringFormat("utility_aware=%s", (D2SkillD2UtilityAware ? "true" : "false")));
|
| | | FileWrite(handle, StringFormat("min_utility=%f", D2SkillD2MinUtility));
|
| | | FileWrite(handle, StringFormat("utility_scale=%f", D2SkillD2UtilityScale));
|
| | | FileWrite(handle, StringFormat("utility_source=%s", D2SkillD2UtilitySource()));
|
| | | FileWrite(handle, StringFormat("initial_balance=%f", OOSInitialBalance));
|
| | | FileWrite(handle, StringFormat("final_balance=%f", final_balance));
|
| | | FileWrite(handle, StringFormat("pnl=%f", final_balance - OOSInitialBalance));
|
| | | FileWrite(handle, "reward_definition=equity_delta_accumulated_after_each_executed_transition");
|
| | | FileWrite(handle, StringFormat("initial_equity=%f", OOSInitialEquity));
|
| | | FileWrite(handle, StringFormat("final_equity=%f", final_equity));
|
| | | FileWrite(handle, StringFormat("reward=%f", OOSReward));
|
| | | FileWrite(handle, StringFormat("max_drawdown=%f", OOSMaximumDrawdown));
|
| | | FileWrite(handle, StringFormat("max_drawdown_percent=%f",
|
| | | (OOSPeakEquity > 0.0 ? 100.0 * OOSMaximumDrawdown / OOSPeakEquity : 0.0)));
|
| | | FileWrite(handle, StringFormat("closed_trades=%I64u", OOSTradeCount));
|
| | | FileWrite(handle, StringFormat("oos_transitions=%I64u", OOSTransitions));
|
| | | FileWrite(handle, StringFormat("executable_actions=%I64u", OOSExecutableActions));
|
| | | FileWrite(handle, StringFormat("comparison_eligible=%s",
|
| | | (OOSExecutableActions > 0 ? "true" : "false")));
|
| | | ulong task_retrievals = 0, task_no_skill = 0, task_promotions = 0, task_replacements = 0;
|
| | | ulong step_retrievals = 0, step_no_skill = 0, step_promotions = 0, step_replacements = 0;
|
| | | const bool task_diagnostics_valid = D2SkillReadD2OOSBankStats(true, task_retrievals, task_no_skill,
|
| | | task_promotions, task_replacements);
|
| | | const bool step_diagnostics_valid = D2SkillReadD2OOSBankStats(false, step_retrievals, step_no_skill,
|
| | | step_promotions, step_replacements);
|
| | | const ulong task_total = task_retrievals + task_no_skill;
|
| | | const ulong step_total = step_retrievals + step_no_skill;
|
| | | FileWrite(handle, StringFormat("task_diagnostics_valid=%s", (task_diagnostics_valid ? "true" : "false")));
|
| | | FileWrite(handle, "task_diagnostics_error=" + D2SkillD2OOSReadError(task_diagnostics_valid));
|
| | | FileWrite(handle, "task_retrievals=" + D2SkillD2OOSCounterText(task_diagnostics_valid, task_retrievals));
|
| | | FileWrite(handle, "task_no_skill=" + D2SkillD2OOSCounterText(task_diagnostics_valid, task_no_skill));
|
| | | FileWrite(handle, "task_retrieval_rate=" + D2SkillD2OOSRateText(task_diagnostics_valid, task_retrievals, task_total));
|
| | | FileWrite(handle, "task_no_skill_rate=" + D2SkillD2OOSRateText(task_diagnostics_valid, task_no_skill, task_total));
|
| | | FileWrite(handle, "task_promotions=" + D2SkillD2OOSCounterText(task_diagnostics_valid, task_promotions));
|
| | | FileWrite(handle, "task_replacements=" + D2SkillD2OOSCounterText(task_diagnostics_valid, task_replacements));
|
| | | FileWrite(handle, StringFormat("step_diagnostics_valid=%s", (step_diagnostics_valid ? "true" : "false")));
|
| | | FileWrite(handle, "step_diagnostics_error=" + D2SkillD2OOSReadError(step_diagnostics_valid));
|
| | | FileWrite(handle, "step_retrievals=" + D2SkillD2OOSCounterText(step_diagnostics_valid, step_retrievals));
|
| | | FileWrite(handle, "step_no_skill=" + D2SkillD2OOSCounterText(step_diagnostics_valid, step_no_skill));
|
| | | FileWrite(handle, "step_retrieval_rate=" + D2SkillD2OOSRateText(step_diagnostics_valid, step_retrievals, step_total));
|
| | | FileWrite(handle, "step_no_skill_rate=" + D2SkillD2OOSRateText(step_diagnostics_valid, step_no_skill, step_total));
|
| | | FileWrite(handle, "step_promotions=" + D2SkillD2OOSCounterText(step_diagnostics_valid, step_promotions));
|
| | | FileWrite(handle, "step_replacements=" + D2SkillD2OOSCounterText(step_diagnostics_valid, step_replacements));
|
| | | FileWrite(handle, "gpu_latency_definition=host_elapsed_us_Actor_feedForward_plus_ReadAction");
|
| | | FileWrite(handle, StringFormat("gpu_boundary_latency_us_avg=%f",
|
| | | (OOSGpuBoundaryLatencySamples > 0 ?
|
| | | double(OOSGpuBoundaryLatencyTotal) / OOSGpuBoundaryLatencySamples : 0.0)));
|
| | | FileWrite(handle, StringFormat("gpu_boundary_latency_us_max=%I64u", OOSGpuBoundaryLatencyMaximum));
|
| | | FileWrite(handle, StringFormat("paired_utility_updates=%I64u", D2SkillD2PairedUtilityUpdates));
|
| | | FileWrite(handle, StringFormat("forecast_signature=%I64u", D2SkillLastSignature));
|
| | | FileWrite(handle, StringFormat("actor_file=%s", D2Skill_ACTOR_FILE));
|
| | | FileWrite(handle, StringFormat("actor_hash_fnv64=%I64u",
|
| | | D2SkillHashFile(ulong(1469598103934665603), D2Skill_ACTOR_FILE)));
|
| | | FileWrite(handle, StringFormat("actor_critic_manifest=%s", D2Skill_AC_MANIFEST_FILE));
|
| | | CNeuronBaseOCL *layer = Actor.Layer(1);
|
| | | CD2Skill *skill = (layer && layer.Type() == defNeuronD2Skill ? (CD2Skill*)layer : NULL);
|
| | | if(!skill || !skill.Ready())
|
| | | {
|
| | | D2SkillWriteD2OOSBankConfiguration(handle, "task", NULL);
|
| | | D2SkillWriteD2OOSBankConfiguration(handle, "step", NULL);
|
| | | }
|
| | | else
|
| | | {
|
| | | D2SkillWriteD2OOSBankConfiguration(handle, "task", skill.TaskBank());
|
| | | D2SkillWriteD2OOSBankConfiguration(handle, "step", skill.StepBank());
|
| | | }
|
| | | FileClose(handle);
|
| | | Print("D2Skill D2 OOS manifest: ", D2Skill_OOS_MANIFEST_FILE);
|
| | | return(true);
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Function OnInit. |
|
| | | //+------------------------------------------------------------------+
|
| | | int OnInit(void)
|
| | | {
|
| | | //--- Exact Forecast and six-artifact AC manifest/hash validation happens
|
| | | //--- before loading/binding the Actor and before any possible policy forward.
|
| | | D2SkillTestInitialized = false;
|
| | | if(!D2SkillConfigureRuntime(InpD2SkillStage, InpD2SkillMode,
|
| | | InpD2SkillExecutionMode, InpD2SkillUtilityAware,
|
2026-08-22 20:08:02 +03:00 | | | InpD2SkillMinUtility, InpD2SkillUtilityScale,
|
| | | false, false, InpD2SkillRepresentation,
|
| | | InpD2SkillResetBanksOnRepresentationMismatch,
|
| | | InpD2SkillRecreateIncompatibleCheckpoint))
|
2026-08-20 19:42:54 +03:00 | | | { Print("D2Skill test init: runtime configuration=FAIL"); return INIT_FAILED; }
|
| | | if(!D2SkillInitIndicators())
|
| | | { Print("D2Skill test init: indicators=FAIL"); return INIT_FAILED; }
|
| | | if(!Trade.SetTypeFillingBySymbol(Symb.Name()))
|
| | | { Print("D2Skill test init: trade filling=FAIL"); return INIT_FAILED; }
|
| | | if(!D2SkillLoadForecastInference())
|
| | | { Print("D2Skill test init: forecast=FAIL"); return INIT_FAILED; }
|
| | | if(!D2SkillLoadInferenceActor(Actor))
|
| | | { Print("D2Skill test init: actor checkpoint=FAIL"); return INIT_FAILED; }
|
| | | if(!D2SkillVerifyFrozenForecastExact())
|
| | | { Print("D2Skill test init: frozen forecast=FAIL"); return INIT_FAILED; }
|
| | | PreviousBalance = AccountInfoDouble(ACCOUNT_BALANCE);
|
| | | PreviousEquity = AccountInfoDouble(ACCOUNT_EQUITY);
|
| | | OOSInitialBalance = PreviousBalance;
|
| | | OOSStartTime = TimeCurrent();
|
| | | OOSInitialEquity = PreviousEquity;
|
| | | OOSPeakEquity = PreviousEquity;
|
| | | OOSMaximumDrawdown = 0;
|
| | | OOSReward = 0;
|
| | | OOSTradeCount = 0;
|
| | | OOSTransitions = 0;
|
| | | OOSExecutableActions = 0;
|
| | | OOSGpuBoundaryLatencyTotal = 0;
|
| | | OOSGpuBoundaryLatencyMaximum = 0;
|
| | | OOSGpuBoundaryLatencySamples = 0;
|
| | | D2SkillTestInitialized = true;
|
| | | return INIT_SUCCEEDED;
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | //| Function OnDeinit. |
|
| | | //+------------------------------------------------------------------+
|
| | | void OnDeinit(const int reason)
|
| | | {
|
| | | if(!D2SkillTestInitialized)
|
| | | {
|
| | | D2SkillForecast = NULL;
|
| | | return;
|
| | | }
|
| | | const double final_balance = AccountInfoDouble(ACCOUNT_BALANCE);
|
| | | const double final_equity = AccountInfoDouble(ACCOUNT_EQUITY);
|
| | | //--- Inference is pure: neither policy nor Forecast artifacts are saved.
|
| | | if(D2SkillTestInitialized && D2SkillForecast != NULL && !D2SkillVerifyFrozenForecastExact())
|
| | | PrintFormat("%s -> %d forecast mutation", __FUNCTION__, __LINE__);
|
| | | if(D2SkillTestInitialized)
|
| | | PrintFormat("D2Skill OOS summary: transitions=%I64u executable_actions=%I64u initial_balance=%.2f final_balance=%.2f",
|
| | | OOSTransitions, OOSExecutableActions, OOSInitialBalance,
|
| | | final_balance);
|
| | | OOSTradeCount = D2SkillOOSClosedTrades();
|
| | | const double drawdown_percent = (OOSPeakEquity > 0.0 ?
|
| | | 100.0 * OOSMaximumDrawdown / OOSPeakEquity : 0.0);
|
| | | const double average_latency = (OOSGpuBoundaryLatencySamples > 0 ?
|
| | | double(OOSGpuBoundaryLatencyTotal) / OOSGpuBoundaryLatencySamples : 0.0);
|
| | | Print("D2 OOS_TABLE stage mode execution_mode utility_aware min_utility utility_scale transitions executable_actions initial_balance final_balance pnl");
|
| | | PrintFormat("D2 OOS_TABLE_ROW stage=%d(%s) mode=%d(%s) execution=%d(%s) aware=%s min_utility=%.6f utility_scale=%.6f utility_source=%s reward=%.2f pnl=%.2f drawdown=%.2f drawdown_percent=%.6f closed_trades=%I64u gpu_boundary_latency_us_avg=%.3f gpu_boundary_latency_us_max=%I64u paired_terminal_updates=%I64u transitions=%I64u executable=%I64u initial_balance=%.2f final_balance=%.2f",
|
| | | D2SkillD2Stage, D2SkillD2StageToString(D2SkillD2Stage),
|
| | | D2SkillD2Mode, D2SkillD2ModeToString(D2SkillD2Mode),
|
| | | D2SkillD2ExecutionMode, D2SkillD2ExecutionModeToString(D2SkillD2ExecutionMode),
|
| | | (D2SkillD2UtilityAware ? "true" : "false"),
|
| | | D2SkillD2MinUtility,
|
| | | D2SkillD2UtilityScale, D2SkillD2UtilitySource(),
|
| | | OOSReward, final_balance - OOSInitialBalance, OOSMaximumDrawdown,
|
| | | drawdown_percent, OOSTradeCount, average_latency,
|
| | | OOSGpuBoundaryLatencyMaximum,
|
| | | D2SkillD2PairedUtilityUpdates,
|
| | | OOSTransitions, OOSExecutableActions, OOSInitialBalance,
|
| | | final_balance);
|
| | | if(OOSExecutableActions == 0)
|
| | | Print("D2Skill D2 OOS REJECT: executable_actions=0; Base/Task/Step/Full comparison is ineligible");
|
| | | if(!D2SkillAppendOOSResultRow(final_balance, final_equity))
|
| | | Print("D2Skill D2 OOS table: append failed");
|
| | | if(!D2SkillWriteD2OOSManifest(final_balance, final_equity))
|
| | | Print("D2Skill D2 OOS manifest: write failed");
|
| | | D2SkillForecast = NULL;
|
| | | }
|
| | | //+------------------------------------------------------------------+
|
| | | //| Function OnTick. |
|
| | | //+------------------------------------------------------------------+
|
| | | void OnTick(void)
|
| | | {
|
| | | if(!IsNewBar())
|
| | | return;
|
| | | if(!D2SkillRefreshLiveMarket(GetPointer(State), GetPointer(TimeState)) ||
|
| | | !D2SkillMarket.Clear() ||
|
| | | !D2SkillForwardForecastState(GetPointer(State)))
|
| | | { PrintFormat("%s -> %d market/forecast", __FUNCTION__, __LINE__); return; }
|
| | | double buy_value = 0, sell_value = 0;
|
| | | if(!D2SkillBuildLiveAccount(PreviousBalance, PreviousEquity, Rates[0].time,
|
| | | GetPointer(Account), buy_value, sell_value))
|
| | | { PrintFormat("%s -> %d account", __FUNCTION__, __LINE__); return; }
|
| | | const ulong forward_started = GetMicrosecondCount();
|
| | | if(!Actor.feedForward(GetPointer(Account), 1, false, GetPointer(D2SkillMarket), -1) ||
|
| | | !ReadAction(Actor, GetPointer(Action)))
|
| | | { PrintFormat("%s -> %d actor/read", __FUNCTION__, __LINE__); return; }
|
| | | const ulong forward_elapsed = GetMicrosecondCount() - forward_started;
|
| | | OOSGpuBoundaryLatencyTotal += forward_elapsed;
|
| | | if(forward_elapsed > OOSGpuBoundaryLatencyMaximum)
|
| | | OOSGpuBoundaryLatencyMaximum = forward_elapsed;
|
| | | OOSGpuBoundaryLatencySamples++;
|
| | | if(!D2SkillExecuteAction(GetPointer(Action), buy_value, sell_value))
|
| | | { PrintFormat("%s -> %d actor/execute", __FUNCTION__, __LINE__); return; }
|
| | | OOSTransitions++;
|
| | | if(IsExecutableOrder(double(Action[0]), double(Action[1]), double(Action[2])) ||
|
| | | IsExecutableOrder(double(Action[3]), double(Action[4]), double(Action[5])))
|
| | | OOSExecutableActions++;
|
| | | const double current_equity = AccountInfoDouble(ACCOUNT_EQUITY);
|
| | | OOSReward += current_equity - PreviousEquity;
|
| | | if(current_equity > OOSPeakEquity)
|
| | | OOSPeakEquity = current_equity;
|
| | | else
|
| | | if(OOSPeakEquity - current_equity > OOSMaximumDrawdown)
|
| | | OOSMaximumDrawdown = OOSPeakEquity - current_equity;
|
| | | PreviousBalance = AccountInfoDouble(ACCOUNT_BALANCE);
|
| | | PreviousEquity = current_equity;
|
| | | }
|
| | | //+------------------------------------------------------------------+
|