594 lines
31 KiB
MQL5
594 lines
31 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| 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
|
|
input ED2SkillRepresentation InpD2SkillRepresentation = D2SkillDirectionMagnitude; //Bank representation
|
|
input bool InpD2SkillResetBanksOnRepresentationMismatch =
|
|
false; //Reset incompatible banks
|
|
input bool InpD2SkillRecreateIncompatibleCheckpoint =
|
|
false; //Recreate incompatible policy checkpoint
|
|
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());
|
|
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())
|
|
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());
|
|
//--- 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() ||
|
|
!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,
|
|
InpD2SkillMinUtility, InpD2SkillUtilityScale,
|
|
false, false, InpD2SkillRepresentation,
|
|
InpD2SkillResetBanksOnRepresentationMismatch,
|
|
InpD2SkillRecreateIncompatibleCheckpoint))
|
|
{ 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;
|
|
}
|
|
//+------------------------------------------------------------------+
|