//+------------------------------------------------------------------+ //| StudyOnline.mq5 | //| Copyright 2026, DNG | //| https://www.mql5.com/ru/users/dng | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, DNG" #property version "3.00" #property description "DPCC online Actor and twin Critic learning in Strategy Tester" #property description "Updates the policy from one bar of reward and bootstrapped TD targets" #property description "Continues through the configured tester interval" #property strict #define DPCC_TRAJECTORY_ENTRY_STUDY_FORECAST #include "Trajectory.mqh" #undef DPCC_TRAJECTORY_ENTRY_STUDY_FORECAST input group "---- Общие параметры режима ----" //--- Таймфрейм; PERIOD_CURRENT — таймфрейм графика/тестера. input ENUM_TIMEFRAMES InpTimeframe = PERIOD_CURRENT; input ENUM_DPCC_RESUME_MODE InpResumeMode = DPCC_RESUME_UNASSIGNED; // Загрузить веса для продолжения обучения. input ENUM_DPCC_MODE InpMode = DPCC_P0; // Режим DPCC: P0, P1 или P2. input long InpMagic = 33062; // Magic-номер заявок этого советника. input bool InpMeasureLatency = false; // Измерять задержку принятия решения. input group "---- Частота обновлений и TD ----" input uint InpUpdatePolicy = 2; // Обновлять Actor через каждые N переходов. input uint InpUpdateTargets = 20; // Обновлять Target-сети через каждые N переходов. input double InpTauT = 1.0; // Коэффициент Polyak-сглаживания Target-сетей. input double InpGamma = 0.5; // Коэффициент дисконтирования награды по барам. input int InpSelector = 0; // Источник градиента Actor: 0 — по хешу события, 1 — Q1, 2 — Q2. input uint InpSeed = 20260920; // Начальное значение для воспроизводимой случайной выборки. input group "---- Распределительные Критики и мера риска ----" input int InpRiskType = 2; // Мера риска SRM, заданная значением defSRM_*. input double InpRiskParameter = 0.1; // Параметр выбранной меры SRM: alpha, lambda или nu. input double InpMeanWeight = 0.0; // Вес среднего результата в Mean-CVaR. input double InpProbWeight = 1.0; // Вес вероятностей квантильного распределения. input double InpValueWeight = 1.0; // Вес значений квантилей в распределении. input uint InpQuantiles = 32; // Число квантилей Critic; архитектура рассчитана на 32. input group "---- Нижняя граница счёта ----" input double InpMinBalance = 50.0; // Минимальный баланс или эквити для торговли. const string DPCC_PACKAGE_ROOT = "DPCC/StudyForecast_M05_20261017"; const bool DPCC_COMMON_FILES = true; //+------------------------------------------------------------------+ //| Shared Market and its six independent Trading roles | //+------------------------------------------------------------------+ CDPCCStudyForecastCore EXTMarket; CDPCCAction6ChartContextOwner EXTDecisionContext; CNet *EXTActor = NULL; CNet *EXTQ1 = NULL; CNet *EXTQ2 = NULL; CNet *EXTTargetActor = NULL; CNet *EXTTargetQ1 = NULL; CNet *EXTTargetQ2 = NULL; SDPCCExecutionControl EXTControl; SDPCCAction6TradingRestore EXTRestored; CBufferFloat EXTCurrentZ; CBufferFloat EXTActorAccount; CBufferFloat EXTTargetAccount; CBufferFloat EXTTargetQuery; CBufferFloat EXTSample; CBufferFloat EXTRandom; SLayerReadState EXTPendingMarketState; SLayerReadState EXTNextMarketState; float EXTPendingAccount[13]; float EXTPendingAction[6]; bool EXTPending = false; bool EXTPendingActorFrame = false; bool EXTMutationFailed = false; bool EXTFailed = false; ulong EXTTransitions = 0; ulong EXTPolicyEvents = 0; ulong EXTTargetUpdates = 0; ulong EXTActorNoPlan = 0; ulong EXTTargetActorNoPlan = 0; bool EXTTargetQ1First = true; string EXTDonorSHA = ""; uint EXTUpdatePolicy = 0; uint EXTUpdateTargets = 0; uint EXTDonorSeed = 0; int EXTSelector = -1; float EXTGammaTD = 0; float EXTTauTarget = 0; float EXTProbabilityWeight = 0; float EXTValueWeight = 0; double EXTMinBalance = 0; int EXTIndicators[4]; long EXTBarOpen = 0; ulong EXTDecisions = 0; ulong EXTPendingMarketGeneration = 0; SDPCCDecisionMeta EXTPendingDecision; double EXTMarkBalance = 0; double EXTMarkEquity = 0; bool EXTReady = false; bool EXTStopped = false; bool EXTTrainingComplete = false; bool EXTControlledFloorStop = false; bool EXTModelSaveAttempted = false; bool EXTModelSaved = false; string EXTModelSavePath = ""; string EXTModelSaveSHA = ""; string EXTModelSaveReason = ""; ulong EXTLatencySamples = 0; ulong EXTDecisionLatencyUs = 0; ulong EXTDecisionLatencyMaxUs = 0; //+------------------------------------------------------------------+ //| Stop before another model transaction or execution request | //+------------------------------------------------------------------+ void DPCCOnlineStop(const string phase, const string reason) { EXTStopped = true; EXTFailed = true; Print("DPCC_STUDYONLINE_BLOCKED phase=", phase, " reason=", reason, " bar=", EXTBarOpen); if(MQLInfoInteger(MQL_TESTER)) TesterStop(); } //+------------------------------------------------------------------+ //| Configure the direct AC-SRM online-learning inputs | //+------------------------------------------------------------------+ bool DPCCOnlineConfigureLearning(string &reason) { const SDPCCConfig financial = EXTRestored.runtime.financial; if(!MathIsValidNumber(InpTauT) || !MathIsValidNumber(InpGamma) || !MathIsValidNumber(InpRiskParameter) || !MathIsValidNumber(InpMeanWeight) || !MathIsValidNumber(InpProbWeight) || !MathIsValidNumber(InpValueWeight) || !MathIsValidNumber(InpMinBalance) || InpUpdatePolicy < 1 || InpUpdateTargets < 1 || InpTauT <= 0 || InpTauT > 1 || InpGamma < 0 || InpGamma > 1 || InpSelector < 0 || InpSelector > 2 || InpSeed > INT_MAX || InpRiskType != financial.risk_type || (float)InpRiskParameter != financial.risk_parameter || (float)InpMeanWeight != financial.mean_weight || InpProbWeight < 0 || InpValueWeight < 0 || InpQuantiles != 32 || InpMinBalance <= 0) { reason = "online.inputs.runtime_SRM_or_cadence_mismatch"; return(false); } EXTUpdatePolicy = InpUpdatePolicy; EXTUpdateTargets = InpUpdateTargets; EXTTauTarget = (float)InpTauT; EXTGammaTD = (float)InpGamma; EXTSelector = InpSelector; EXTDonorSeed = InpSeed; EXTProbabilityWeight = (float)InpProbWeight; EXTValueWeight = (float)InpValueWeight; EXTMinBalance = InpMinBalance; return(true); } //+------------------------------------------------------------------+ //| Allocate reused input and sampling buffers on the Market context | //+------------------------------------------------------------------+ bool DPCCOnlinePrepareBuffers(string &reason) { CNet *market = EXTMarket.ReadyMarketNetwork(); COpenCLMy *context = EXTMarket.ReadyEncoderContext(); CNeuronScenarioForecastPriceOCL *forecast = NULL; if(CheckPointer(market) != POINTER_INVALID) forecast = dynamic_cast(market.Layer(6)); ulong count = 0; if(!forecast || CheckPointer(context) == POINTER_INVALID || forecast.GetOpenCL() != context || 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 = "online.market.snapshot_axes_or_frozen_contract"; return(false); } CBufferFloat *buffers[] = {GetPointer(EXTCurrentZ), GetPointer(EXTActorAccount), GetPointer(EXTTargetAccount), GetPointer(EXTTargetQuery), GetPointer(EXTSample), GetPointer(EXTRandom) }; const uint sizes[] = {(uint)count, 13, 13, 19, 1, 1}; for(int i = 0; i < 6; i++) { if(!buffers[i].BufferInit(sizes[i], 0)) { reason = StringFormat("online.buffer_init.%d", i); return(false); } if(!buffers[i].BufferCreate(context)) { reason = StringFormat("online.buffer_create.%d", i); return(false); } } return(true); } //+------------------------------------------------------------------+ //| Save or restore only the frozen Market's three policy streams | //+------------------------------------------------------------------+ bool DPCCOnlineMarketState(SLayerReadState &state, const bool capture, const ulong decision, const ulong generation, string &reason) { CNet *market = EXTMarket.ReadyMarketNetwork(); CNeuronScenarioForecastPriceOCL *forecast = CheckPointer(market) == POINTER_INVALID ? NULL : dynamic_cast(market.Layer(6)); if(!forecast || (state.Context() != NULL && state.Context() != market.GetOpenCL())) { reason = "online.market_snapshot.context"; return(false); } CBufferFloat *buffers[] = {forecast.GetZ(), forecast.GetLogU(), forecast.GetPi()}; if(capture) { if(!state.Begin(market.GetOpenCL(), decision, generation)) { reason = "online.market_snapshot.begin"; return(false); } for(int i = 0; i < 3; i++) if(CheckPointer(buffers[i]) == POINTER_INVALID || !state.PutBuffer(buffers[i])) { reason = StringFormat("online.market_snapshot.capture.%d", i); return(false); } } else { state.Rewind(); for(int i = 0; i < 3; i++) { CBufferFloat *original = buffers[i]; if(!state.GetBuffer(buffers[i]) || buffers[i] != original) { reason = StringFormat("online.market_snapshot.restore.%d", i); return(false); } } if(!state.Complete()) { reason = "online.market_snapshot.incomplete"; return(false); } } return(true); } //+------------------------------------------------------------------+ //| Keep Market and targets frozen while enabling the online roles | //+------------------------------------------------------------------+ bool DPCCOnlineLearningPermissions(string &reason) { CNet *market = EXTMarket.ReadyMarketNetwork(); CNet *networks[] = {EXTActor, EXTQ1, EXTQ2, EXTTargetActor, EXTTargetQ1, EXTTargetQ2}; const int layers[] = {2, 5, 5, 2, 5, 5}; COpenCLMy *context = EXTMarket.ReadyEncoderContext(); if(CheckPointer(market) == POINTER_INVALID || CheckPointer(context) == POINTER_INVALID || market.GetOpenCL() != context || market.WeightsUpdateEnabled()) { reason = "online.market.frozen_context"; return(false); } for(int i = 0; i < 6; i++) { const bool trainable = (i < 3); if(CheckPointer(networks[i]) == POINTER_INVALID || networks[i].GetOpenCL() != context || !networks[i].IsOpenCLTransferReady() || networks[i].Layer(layers[i]) != NULL || !networks[i].TrainMode(trainable) || !networks[i].SetWeightsUpdate(trainable)) { reason = StringFormat("online.role.permission_or_context.%d", i); return(false); } } return(true); } //+------------------------------------------------------------------+ //| Restore the same Market and native Action6 bundle used by Study | //+------------------------------------------------------------------+ int OnInit(void) { for(int i = 0; i < 4; i++) EXTIndicators[i] = INVALID_HANDLE; string reason = ""; if(!DPCCConfigureExecutionControl(DPCC_PACKAGE_ROOT, InpTimeframe, DPCC_STAGE_STUDYONLINE, "", 0, InpResumeMode, InpMode, DPCC_STAGE_STUDYONLINE, EXTControl, reason) || !DPCCLoadMarketExecutionProfile(EXTControl, DPCC_COMMON_FILES, reason)) { DPCCOnlineStop("execution_control", reason); return(INIT_PARAMETERS_INCORRECT); } if(InpMagic < 1 || !DPCCTesterOnly(reason) || !DPCCRequireTesterOneDirection(reason)) { DPCCOnlineStop("environment", reason == "" ? "H1_Tester_required" : reason); return(INIT_PARAMETERS_INCORRECT); } if(_Symbol == "" || AccountInfoString(ACCOUNT_CURRENCY) == "" || PositionsTotal() != 0 || OrdersTotal() != 0) { DPCCOnlineStop("environment", "flat_Tester_start_required"); return(INIT_FAILED); } if(!DPCCLoadAction6Trading(EXTControl, DPCC_COMMON_FILES, EXTMarket, EXTActor, EXTQ1, EXTQ2, EXTTargetActor, EXTTargetQ1, EXTTargetQ2, EXTRestored, reason)) { DPCCOnlineStop("D01_restore", reason); return(INIT_FAILED); } if(EXTRestored.diffusion_seed == 0) { DPCCOnlineStop("profile", "assigned_profile.diffusion.seed_must_be_nonzero_for_Action6"); return(INIT_FAILED); } if(!DPCCOnlineConfigureLearning(reason) || !DPCCOnlinePrepareBuffers(reason) || !DPCCOnlineLearningPermissions(reason)) { DPCCOnlineStop("policy", reason); 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) { DPCCOnlineStop("indicators", (string)GetLastError()); return(INIT_FAILED); } EXTMarkBalance = AccountInfoDouble(ACCOUNT_BALANCE); EXTMarkEquity = AccountInfoDouble(ACCOUNT_EQUITY); if(!MathIsValidNumber(EXTMarkBalance) || !MathIsValidNumber(EXTMarkEquity) || EXTMarkBalance < EXTMinBalance || EXTMarkEquity < EXTMinBalance) { DPCCOnlineStop("account", "actual_Tester_start_below_floor"); return(INIT_FAILED); } //--- Study supplies initial targets; an Online generation already owns its target parameters. if(EXTRestored.source_stage == "Study") { if(!EXTTargetActor.WeightsUpdate(EXTActor, 1.0f) || !EXTTargetQ1.WeightsUpdate(EXTQ2, 1.0f) || !EXTTargetQ2.WeightsUpdate(EXTQ1, 1.0f)) { DPCCOnlineStop("targets", "cold_Study_crossed_initialization"); return(INIT_FAILED); } } else if(EXTRestored.source_stage != "StudyOnline") { DPCCOnlineStop("targets", "Trading_source_stage_required"); return(INIT_FAILED); } MathSrand((int)EXTDonorSeed); EXTReady = true; Print("DPCC_STUDYONLINE_READY source_stage=", EXTRestored.source_stage, " donor=", EXTDonorSHA, " model_export=ON_COMPLETION_ONCE"); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Encode the next closed bar and build one pre-action account mark | //+------------------------------------------------------------------+ bool DPCCOnlineObserve(const long decision_open, CBufferFloat *&context, float &account[], string &reason) { context = NULL; SDPCCClosedHistoryRow closed; MqlTick tick; long quote_mode = -1; double tick_size = 0; if(!DPCCCaptureTesterClosedRow(_Symbol, EXTIndicators, 1, closed, reason) || closed.successor_open_server != decision_open || !SymbolInfoTick(_Symbol, tick) || decision_open > LONG_MAX / 1000 - 3600 || tick.time_msc < decision_open * 1000 || tick.time_msc >= (decision_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)) { if(reason == "") reason = "online.causal_H1_quote_binding"; return(false); } if(EXTDecisions == 0 && !DPCCPrepareReadyMarketLiveStart(EXTMarket, _Symbol, EXTIndicators, closed.bar.server_open, reason)) return(false); const double first_quote = (quote_mode == SYMBOL_CHART_MODE_LAST ? tick.last : tick.bid); if(!EXTMarket.InferReadyMarketLive(closed, first_quote, tick.time_msc, tick_size, GetPointer(EXTCurrentZ), EXTPendingMarketGeneration, reason)) return(false); if(!EXTDecisionContext.Build(EXTRestored.runtime, EXTRestored.limits, EXTMarket.ReadyMarketNativeOHLC(), decision_open, context, account, reason)) return(false); if(ArraySize(account) != 13 || CheckPointer(context) == POINTER_INVALID) { reason = "online.Account13_context_shape"; return(false); } for(int i = 0; i < 13; i++) if(!MathIsValidNumber(account[i])) { reason = "online.Account13_nonfinite"; return(false); } return(true); } //+------------------------------------------------------------------+ //| Bind either Actor to this causal observation and its own input | //+------------------------------------------------------------------+ bool DPCCOnlineActorForward(CNet &network, CBufferFloat &account_buffer, CBufferFloat *context, const long decision_open, const float &account[], float &action[], SDPCCDecisionMeta &meta, bool &has_plan, string &reason) { has_plan = false; CNeuronDPCCOCL *actor = dynamic_cast(network.Layer(1)); if(!actor || ArraySize(account) != 13 || EXTPendingMarketGeneration == 0 || EXTDecisions >= (ulong)LONG_MAX - 2) { reason = "online.Actor_input_or_decision_id"; return(false); } for(int i = 0; i < 13; i++) if(!MathIsValidNumber(account[i]) || !account_buffer.Update(i, account[i])) { reason = "online.Actor_Account13_upload"; return(false); } ZeroMemory(meta); meta.decision_id = EXTDecisions + 1; meta.market_state_id = EXTPendingMarketGeneration; meta.q1_state_id = meta.decision_id; meta.q2_state_id = meta.decision_id + 1; meta.noise_seed = EXTRestored.diffusion_seed; meta.decision_server_time = decision_open; meta.bar_index = (long)EXTDecisions; meta.mode = (uint)EXTControl.mode; meta.market_sha256 = EXTRestored.market_sha256; meta.profile_sha256 = EXTControl.profile_sha256; if(!account_buffer.BufferWrite() || !actor.BindDecisionContext(context, meta)) { reason = "online.Action6_context"; return(false); } const bool forwarded = network.feedForward(GetPointer(account_buffer), 1, false, EXTMarket.ReadyMarketNetwork(), 6); if(!DPCCResolveAction6Output(actor, forwarded, action, reason)) { reason = "online." + reason; return(false); } has_plan = actor.HasValidDecision(); return(true); } //+------------------------------------------------------------------+ //| Release Market generation without consuming the Actor unroll | //+------------------------------------------------------------------+ bool DPCCOnlineReleaseMarket(string &reason) { if(EXTPendingMarketGeneration == 0) return(true); if(!EXTMarket.ReleaseReadyEncoderZ(EXTPendingMarketGeneration, reason)) { reason = "online.Market_release:" + reason; return(false); } EXTPendingMarketGeneration = 0; return(true); } //+------------------------------------------------------------------+ //| Close one retained Actor frame after update, skip, or shutdown | //+------------------------------------------------------------------+ bool DPCCOnlineCancelActor(CNet *network, string &reason) { CNeuronDPCCOCL *actor = CheckPointer(network) == POINTER_INVALID ? NULL : dynamic_cast(network.Layer(1)); if(!actor || !actor.CancelGeneration()) { reason = "online.Actor_frame_release"; return(false); } return(true); } //+------------------------------------------------------------------+ //| Sample head 4 using the donor's open-interval random variate | //+------------------------------------------------------------------+ bool DPCCOnlineSample(CNet &critic, float &value, string &reason) { CNeuronACSRM *head = dynamic_cast(critic.Layer(4)); if(!head) { reason = "online.target_Q.head4_required"; return(false); } const float u = (float)((double)MathRand() + 0.5) / 32768.0f; if(!EXTRandom.Update(0, u) || !EXTRandom.BufferWrite() || !head.Sample(GetPointer(EXTSample), GetPointer(EXTRandom), 1) || !EXTSample.BufferRead() || !MathIsValidNumber(EXTSample.At(0))) { reason = "online.target_Q.sample"; return(false); } value = EXTSample.At(0); return(true); } //+------------------------------------------------------------------+ //| Bootstrap the next causal state with the frozen target models | //+------------------------------------------------------------------+ bool DPCCOnlineBootstrap(CBufferFloat *context, const long decision_open, const float &account[], float &z1, float &z2, string &reason) { float action[]; SDPCCDecisionMeta meta; bool has_plan = false; if(!DPCCOnlineActorForward(*EXTTargetActor, EXTTargetAccount, context, decision_open, account, action, meta, has_plan, reason)) return(false); if(!DPCCOnlineCancelActor(EXTTargetActor, reason)) return(false); if(!has_plan) EXTTargetActorNoPlan++; for(int i = 0; i < 19; i++) if(!EXTTargetQuery.Update(i, i < 13 ? account[i] : action[i - 13])) { reason = "online.target_Q.Account13_Action6_upload"; return(false); } if(!EXTTargetQuery.BufferWrite()) { reason = "online.target_Q.query_write"; return(false); } CNet *market = EXTMarket.ReadyMarketNetwork(); if(!EXTTargetQ1.feedForward(GetPointer(EXTTargetQuery), 1, false, market, 6) || !DPCCOnlineSample(*EXTTargetQ1, z1, reason)) { reason = "online.target_Q1:" + reason; return(false); } if(!EXTTargetQ2.feedForward(GetPointer(EXTTargetQuery), 1, false, market, 6) || !DPCCOnlineSample(*EXTTargetQ2, z2, reason)) { reason = "online.target_Q2:" + reason; return(false); } return(true); } //+------------------------------------------------------------------+ //| Refresh Actor and one crossed Critic on the donor target cadence | //+------------------------------------------------------------------+ bool DPCCOnlineRefreshTargets(string &reason) { if(EXTTransitions == 0 || EXTTransitions % EXTUpdateTargets != 0) return(true); if(!EXTTargetActor.WeightsUpdate(EXTActor, EXTTauTarget)) { reason = "online.target_Actor.weights_update"; return(false); } if((EXTTargetUpdates & 1) == 0) { EXTTargetQ1First = ((MathRand() & 1) == 0); } const bool update_q1 = EXTTargetQ1First ? ((EXTTargetUpdates & 1) == 0) : ((EXTTargetUpdates & 1) != 0); if(update_q1 ? !EXTTargetQ1.WeightsUpdate(EXTQ2, EXTTauTarget) : !EXTTargetQ2.WeightsUpdate(EXTQ1, EXTTauTarget)) { reason = "online.crossed_target_Q.weights_update"; return(false); } EXTTargetUpdates++; return(true); } //+------------------------------------------------------------------+ //| Complete exactly one pending transition before a new action | //+------------------------------------------------------------------+ bool DPCCOnlineUpdatePending(CBufferFloat *context, const long decision_open, const float &next_account[], const double next_equity, string &reason) { if(!EXTPending) return(true); float reward = 0, z1 = 0, z2 = 0, y1 = 0, y2 = 0; if(!DPCCBuildOneBarReward(EXTMarkEquity, next_equity, reward, reason)) return(false); if(!DPCCOnlineBootstrap(context, decision_open, next_account, z1, z2, reason)) return(false); if(!DPCCBuildOneBarTDTargets(reward, EXTGammaTD, 1.0f, z1, z2, y1, y2, reason)) return(false); //--- Preserve s(t+1), train against s(t), then return Market to the new decision. if(!DPCCOnlineMarketState(EXTNextMarketState, true, EXTDecisions + 1, EXTPendingMarketGeneration, reason)) return(false); if(!DPCCOnlineMarketState(EXTPendingMarketState, false, 0, 0, reason)) { string restore_reason = ""; if(!DPCCOnlineMarketState(EXTNextMarketState, false, 0, 0, restore_reason)) reason += ";next_state_restore:" + restore_reason; return(false); } float query[19]; for(int i = 0; i < 13; i++) query[i] = EXTPendingAccount[i]; for(int i = 0; i < 6; i++) query[13 + i] = EXTPendingAction[i]; CNet *market = EXTMarket.ReadyMarketNetwork(); bool updated = DPCCUpdateAction6Critic(*EXTQ1, *market, query, y1, EXTProbabilityWeight, EXTValueWeight, reason); if(updated) updated = DPCCUpdateAction6Critic(*EXTQ2, *market, query, y2, EXTProbabilityWeight, EXTValueWeight, reason); if(updated && EXTPendingActorFrame) { const ulong transition = EXTTransitions + 1; if(transition % EXTUpdatePolicy == 0) { const int selected = DPCCOnlineActorCritic(EXTPolicyEvents, EXTSelector, EXTDonorSeed); updated = selected >= 0 && DPCCUpdateAction6Actor(*EXTActor, EXTPendingDecision.decision_id, (uint)selected, reason); if(updated) EXTPolicyEvents++; } else updated = DPCCOnlineCancelActor(EXTActor, reason); } if(updated) { EXTTransitions++; updated = DPCCOnlineRefreshTargets(reason); } string restore_reason = ""; const bool restored = DPCCOnlineMarketState(EXTNextMarketState, false, 0, 0, restore_reason); if(!restored) reason += ";next_state_restore:" + restore_reason; EXTNextMarketState.Clear(); if(!updated || !restored) { EXTMutationFailed = true; return(false); } EXTPendingMarketState.Clear(); EXTPending = false; EXTPendingActorFrame = false; ZeroMemory(EXTPendingDecision); return(true); } //+------------------------------------------------------------------+ //| Execute one causal operation and retain its Actor training frame | //+------------------------------------------------------------------+ void OnTick(void) { if(!EXTReady || EXTStopped || IsStopped()) return; const double balance = AccountInfoDouble(ACCOUNT_BALANCE); const double equity = AccountInfoDouble(ACCOUNT_EQUITY); string reason = ""; if(!MathIsValidNumber(balance) || !MathIsValidNumber(equity)) { DPCCOnlineStop("account", "actual_balance_or_equity_nonfinite"); return; } if(balance < EXTMinBalance || equity < EXTMinBalance) { EXTStopped = true; EXTControlledFloorStop = true; Print("DPCC_STUDYONLINE_CONTROLLED_STOP phase=account_floor reason=actual_Tester_balance_or_equity_below_floor", " transitions=", EXTTransitions, " actor_updates=", EXTPolicyEvents); TesterStop(); return; } const long open = (long)iTime(_Symbol, EXTControl.timeframe, 0); if(open <= 0 || open == EXTBarOpen) return; if(iVolume(_Symbol, EXTControl.timeframe, 0) != 1 || (EXTBarOpen > 0 && (long)iTime(_Symbol, EXTControl.timeframe, 1) != EXTBarOpen)) { DPCCOnlineStop("chronology", "first_H1_quote_required_no_hindsight_repair"); return; } if(!DPCCTesterOnly(reason) || !DPCCRequireTesterOneDirection(reason)) { DPCCOnlineStop("environment", reason); return; } const ulong started = GetMicrosecondCount(); CBufferFloat *context = NULL; float account[], action[]; if(!DPCCOnlineObserve(open, context, account, reason)) { DPCCOnlineStop("causal_observation", reason); return; } if(!DPCCOnlineUpdatePending(context, open, account, equity, reason)) { DPCCOnlineStop("one_bar_TD", reason); return; } SDPCCDecisionMeta meta; bool has_plan = false; if(!DPCCOnlineActorForward(*EXTActor, EXTActorAccount, context, open, account, action, meta, has_plan, reason)) { DPCCOnlineStop("Action6_forward", reason); return; } if(!has_plan) { if(!DPCCOnlineCancelActor(EXTActor, reason)) { DPCCOnlineStop("Actor_frame_release", reason); return; } EXTActorNoPlan++; } if(!DPCCOnlineMarketState(EXTPendingMarketState, true, meta.decision_id, EXTPendingMarketGeneration, reason)) { DPCCOnlineStop("pending_snapshot", reason); return; } for(int i = 0; i < 13; i++) EXTPendingAccount[i] = account[i]; for(int i = 0; i < 6; i++) EXTPendingAction[i] = action[i]; EXTPendingDecision = meta; EXTPendingActorFrame = has_plan; EXTMarkBalance = balance; EXTMarkEquity = equity; EXTPending = true; if(!DPCCExecuteTesterAction(_Symbol, InpMagic, action, (double)MaxTP, (double)MaxSL, reason)) { EXTMutationFailed = true; DPCCOnlineStop("Tester_execution", reason); return; } EXTBarOpen = open; EXTDecisions++; if(!DPCCOnlineReleaseMarket(reason)) { DPCCOnlineStop("Market_release", reason); return; } if(InpMeasureLatency) { const ulong elapsed = GetMicrosecondCount() - started; EXTLatencySamples++; EXTDecisionLatencyUs += elapsed; EXTDecisionLatencyMaxUs = MathMax(EXTDecisionLatencyMaxUs, elapsed); } } //+------------------------------------------------------------------+ //| Mark a normal completed Tester history pass for final export | //+------------------------------------------------------------------+ double OnTester(void) { if(EXTReady && !EXTStopped && !EXTFailed && !EXTMutationFailed && EXTTransitions > 0) EXTTrainingComplete = true; return(0.0); } //+------------------------------------------------------------------+ //| Release frames, save the last complete models once, then destroy | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { bool cleanup_ok = true; const bool pending_discarded = EXTPending; string release_reason = ""; if(CheckPointer(EXTActor) != POINTER_INVALID && !DPCCOnlineCancelActor(EXTActor, release_reason)) cleanup_ok = false; if(CheckPointer(EXTTargetActor) != POINTER_INVALID && !DPCCOnlineCancelActor(EXTTargetActor, release_reason)) cleanup_ok = false; if(!DPCCOnlineReleaseMarket(release_reason)) cleanup_ok = false; EXTPendingMarketState.Clear(); EXTNextMarketState.Clear(); EXTPending = false; EXTPendingActorFrame = false; ZeroMemory(EXTPendingDecision); if(EXTReady && cleanup_ok && !EXTFailed && !EXTMutationFailed && EXTTransitions > 0 && (EXTTrainingComplete || EXTControlledFloorStop)) { EXTModelSaved = DPCCSaveAction6OnlineTrading(EXTControl, DPCC_COMMON_FILES, EXTMarket, *EXTActor, *EXTQ1, *EXTQ2, *EXTTargetActor, *EXTTargetQ1, *EXTTargetQ2, EXTRestored, EXTControlledFloorStop, EXTModelSaveAttempted, EXTModelSavePath, EXTModelSaveSHA, EXTModelSaveReason); if(!EXTModelSaved && EXTModelSaveReason == "") EXTModelSaveReason = "online.model_export_failed_without_reason"; } else if(!cleanup_ok) EXTModelSaveReason = "online.cleanup_failed:" + release_reason; else if(EXTMutationFailed || EXTFailed) EXTModelSaveReason = "online.failed_or_partial_model_update"; else if(!EXTTrainingComplete && !EXTControlledFloorStop) EXTModelSaveReason = "online.training_not_complete"; else if(EXTTransitions == 0) EXTModelSaveReason = "online.no_complete_transition"; EXTDecisionContext.Reset(); DPCCReleaseAction6Trading(EXTActor, EXTQ1, EXTQ2, EXTTargetActor, EXTTargetQ1, EXTTargetQ2); for(int i = 0; i < 4; i++) if(EXTIndicators[i] != INVALID_HANDLE) IndicatorRelease(EXTIndicators[i]); if(EXTReady) Print("DPCC_STUDYONLINE_FINAL transitions=", EXTTransitions, " actor_updates=", EXTPolicyEvents, " target_updates=", EXTTargetUpdates, " actor_no_plan=", EXTActorNoPlan, " target_actor_no_plan=", EXTTargetActorNoPlan, " mutation_failed=", EXTMutationFailed, " controlled_floor=", EXTControlledFloorStop, " pending_transition_discarded=", pending_discarded, " model_export=", (EXTModelSaved ? "PASS" : (EXTModelSaveAttempted ? "FAIL" : "SKIPPED")), " path=", EXTModelSavePath, " sha256=", EXTModelSaveSHA, " reason=", EXTModelSaveReason, " deinit_reason=", reason); if(InpMeasureLatency && EXTLatencySamples > 0) PrintFormat("DPCC_STUDYONLINE_DECISION_LATENCY samples=%I64u mean_us=%.2f max_us=%I64u", EXTLatencySamples, (double)EXTDecisionLatencyUs / (double)EXTLatencySamples, EXTDecisionLatencyMaxUs); } //+------------------------------------------------------------------+