2026-09-29 00:57:33 +03:00
//+------------------------------------------------------------------+
2026-09-28 01:52:58 +03:00
//| Trajectory.mqh |
//| Copyright 2026, DNG |
//| https://www.mql5.com/ru/users/dng |
//+------------------------------------------------------------------+
#property link "https://www.mql5.com/ru/users/dng"
#property version "1.00"
#include "..\NeuroNet_DNG\NeuroNet.mqh"
#include <Trade\Trade.mqh>
#include <Trade\SymbolInfo.mqh>
#include <Indicators\Oscilators.mqh>
//+-------------------------------------------------------------------------------------------------------------------------------+
//| OMPB chain stage selector. Stage 02 was the original scaffold; the expert now runs the full chain: MarketEncoder..Inference |
//+-------------------------------------------------------------------------------------------------------------------------------+
enum ENUM_OMPB_STAGE
{
OMPB_STAGE_MARKET_ENCODER = 1 , //Chain stage 01 - forecast (market encoder)
OMPB_STAGE_CALIBRATION , //Chain stage 02 - OMPB calibration
OMPB_STAGE_BASE_POLICY , //Chain stages 03-05 - base policy study / online / test
OMPB_STAGE_SKILL , //Reserved: skill stage
OMPB_STAGE_OPTIMIZATION , //Reserved: optimization stage
OMPB_STAGE_INFERENCE //Reserved: inference stage
};
input group " ---- Indicators ---- "
input ENUM_TIMEFRAMES TimeFrame = PERIOD_H1 ; //Working timeframe
//---
input group " ---- RSI ---- "
input int RSIPeriod = 14 ; //Period
input ENUM_APPLIED_PRICE RSIPrice = PRICE_CLOSE ; //Applied price
//---
input group " ---- CCI ---- "
input int CCIPeriod = 14 ; //Period
input ENUM_APPLIED_PRICE CCIPrice = PRICE_TYPICAL ; //Applied price
//---
input group " ---- ATR ---- "
input int ATRPeriod = 14 ; //Period
//---
input group " ---- MACD ---- "
input int FastPeriod = 12 ; //Fast
input int SlowPeriod = 26 ; //Slow
input int SignalPeriod = 9 ; //Signal
input ENUM_APPLIED_PRICE MACDPrice = PRICE_CLOSE ; //Applied price
int iLatentLayer = - 1 ;
int iStateRawForecastLayer = 5 ;
int iStateTokenLayer = 6 ;
//---
#define HistoryBars 5
#define BarDescr 9 //Elements for 1 bar description
#define AccountDescr 13 //Account description
#define NActions 6 //Number of possible Actions
#define NRewards 1 //Number of rewards
#define NForecast 12 //Number of forecast
#define EtalonBalance 1e4
#define BatchSize 1e+5
#define EmbeddingSize 16
#define DiscFactor 0.5f
#define FileName "ACSRM"
#define ACSRM_LOG_PREFIX "ACSRM"
#define LatentCount 64
#define LatentLayer iLatentLayer
#define StateRawForecastLayer iStateRawForecastLayer
#define StateTokenLayer iStateTokenLayer
#define ForecastTokenDim (EmbeddingSize + 1)
#define MaxSL 1000
#define MaxTP 1000
#define ActorUpdate 5
#define TargetUpdate 24*5
#define tau 0.9f
#define NHeads 4
#define NExperts 5
#define NScenarios 21
#define TopK 5
#define ACSRMSamples 5
#define ACSRMReferenceSize 256
#define ACSRMCurrentWindow 64
#define Quantiles 8
#define StackSize 24*21
#define Blocks 24
CSymbolInfo Symb ;
CTrade Trade ;
MqlRates Rates [];
//---
CiRSI RSI ;
CiCCI CCI ;
CiATR ATR ;
CiMACD MACD ;
struct SState
{
float state [ HistoryBars * BarDescr ];
float account [ AccountDescr - 4 ];
float action [ NActions ];
float rewards [ NRewards ];
//---
SState ( void );
//---
bool Save ( int file_handle );
bool Load ( int file_handle );
//--- overloading
void operator = ( const SState & obj )
{
ArrayCopy ( state , obj . state );
ArrayCopy ( account , obj . account );
ArrayCopy ( action , obj . action );
ArrayCopy ( rewards , obj . rewards );
}
};
//+------------------------------------------------------------------+
//| Complete outcome of one independently simulated Skill episode. |
//+------------------------------------------------------------------+
struct SSkillEpisodeOutcome
{
private :
double dOutcome ;
double dBalance ;
double dEquity ;
double dDrawdown ;
double dCost ;
double dRisk ;
double dPeakEquity ;
uint uDuration ;
public :
bool Reset ( void );
bool Accumulate ( const double reward , const double balance ,
const double equity , const double drawdown ,
const double cost , const uint duration ,
const double risk = 0.0 );
double Outcome ( void ) const { return ( dOutcome ); }
double Balance ( void ) const { return ( dBalance ); }
double Equity ( void ) const { return ( dEquity ); }
double Drawdown ( void ) const { return ( dDrawdown ); }
double Cost ( void ) const { return ( dCost ); }
double Risk ( void ) const { return ( dRisk ); }
uint Duration ( void ) const { return ( uDuration ); }
};
//+------------------------------------------------------------------+
//| Resets the episode outcome block to its neutral state. |
//+------------------------------------------------------------------+
bool SSkillEpisodeOutcome :: Reset ( void )
{
dOutcome = 0.0 ;
dBalance = 0.0 ;
dEquity = 0.0 ;
dDrawdown = 0.0 ;
dCost = 0.0 ;
dRisk = 0.0 ;
dPeakEquity = 0.0 ;
uDuration = 0 ;
return ( true );
}
//+------------------------------------------------------------------+
//| Accumulates one step into the episode outcome block. |
//+------------------------------------------------------------------+
bool SSkillEpisodeOutcome :: Accumulate ( const double reward , const double balance ,
const double equity , const double drawdown ,
const double cost , const uint duration ,
const double risk = 0.0 )
{
if ( ! MathIsValidNumber ( reward ) || ! MathIsValidNumber ( balance ) ||
! MathIsValidNumber ( equity ) || ! MathIsValidNumber ( drawdown ) ||
! MathIsValidNumber ( cost ) || ! MathIsValidNumber ( risk ))
ReturnFalse ;
dOutcome += reward ;
dBalance = balance ;
dEquity = equity ;
dPeakEquity = MathMax ( dPeakEquity , equity );
const double peak_drawdown = ( dPeakEquity > 0.0 ?
( dPeakEquity - equity ) / dPeakEquity : 0.0 );
dDrawdown = MathMax ( dDrawdown , MathMax ( MathAbs ( drawdown ), peak_drawdown ));
dCost += MathAbs ( cost );
dRisk = MathMax ( dRisk , MathAbs ( risk ));
uDuration += duration ;
return ( true );
}
//+------------------------------------------------------------------+
//| Calculates terminal paired utility from complete episode state. |
//+------------------------------------------------------------------+
bool SkillComputePairedEpisodeDelta ( const SSkillEpisodeOutcome & base ,
const SSkillEpisodeOutcome & skill ,
double & delta_j )
{
const double base_outcome = base . Outcome ();
const double skill_outcome = skill . Outcome ();
if ( ! MathIsValidNumber ( base_outcome ) || ! MathIsValidNumber ( skill_outcome ))
ReturnFalse ;
delta_j = skill_outcome - base_outcome ;
return ( MathIsValidNumber ( delta_j ));
}
//+------------------------------------------------------------------+
//| Validates a complete, synchronous paired terminal outcome. |
//+------------------------------------------------------------------+
bool SkillValidatePairedEpisode ( const SSkillEpisodeOutcome & base ,
const SSkillEpisodeOutcome & skill )
{
if ( ! MathIsValidNumber ( base . Outcome ()) || ! MathIsValidNumber ( skill . Outcome ()) ||
base . Duration () == 0 || skill . Duration () == 0 ||
base . Duration () != skill . Duration ())
ReturnFalse ;
return ( true );
}
//+------------------------------------------------------------------+
//| Returns whether a checkpoint requests a later boundary save. |
//+------------------------------------------------------------------+
bool SkillCheckpointDue ( const ulong transitions , const int checkpoint_interval )
{
return ( checkpoint_interval > 0 && transitions > 0 &&
transitions % ( ulong ) checkpoint_interval == 0 );
}
//+------------------------------------------------------------------+
//| DeltaJ may close only at a terminal or configured pair horizon. |
//+------------------------------------------------------------------+
bool SkillOnlinePairBoundary ( const bool real_terminal , const bool virtual_terminal ,
const ulong pair_transitions , const int pair_horizon )
{
if ( real_terminal || virtual_terminal )
return ( true );
return ( pair_horizon > 0 && pair_transitions >= ( ulong ) pair_horizon );
}
//+------------------------------------------------------------------+
//| An independent pair ends as soon as either branch is terminal. |
//+------------------------------------------------------------------+
bool SkillPairReachedTerminal ( const bool base_terminal , const bool skill_terminal )
{
return ( base_terminal || skill_terminal );
}
//+------------------------------------------------------------------+
//| Implements SState. |
//+------------------------------------------------------------------+
SState :: SState ( void )
{
ArrayInitialize ( state , 0 );
ArrayInitialize ( account , 0 );
ArrayInitialize ( action , 0 );
ArrayInitialize ( rewards , 0 );
}
//+------------------------------------------------------------------+
//| Saves |
//+------------------------------------------------------------------+
bool SState :: Save ( int file_handle )
{
if ( file_handle == INVALID_HANDLE )
ReturnFalse ;
//---
int total = ArraySize ( state );
if ( FileWriteInteger ( file_handle , total ) < sizeof ( int ))
ReturnFalse ;
for ( int i = 0 ; i < total ; i ++ )
if ( FileWriteFloat ( file_handle , state [ i ]) < sizeof ( float ))
ReturnFalse ;
//---
total = ArraySize ( account );
if ( FileWriteInteger ( file_handle , total ) < sizeof ( int ))
ReturnFalse ;
for ( int i = 0 ; i < total ; i ++ )
if ( FileWriteFloat ( file_handle , account [ i ]) < sizeof ( float ))
ReturnFalse ;
//---
total = ArraySize ( action );
if ( FileWriteInteger ( file_handle , total ) < sizeof ( int ))
ReturnFalse ;
for ( int i = 0 ; i < total ; i ++ )
if ( FileWriteFloat ( file_handle , action [ i ]) < sizeof ( float ))
ReturnFalse ;
total = ArraySize ( rewards );
if ( FileWriteInteger ( file_handle , total ) < sizeof ( int ))
ReturnFalse ;
for ( int i = 0 ; i < total ; i ++ )
if ( FileWriteFloat ( file_handle , rewards [ i ]) < sizeof ( float ))
ReturnFalse ;
//---
return ( true );
}
//+------------------------------------------------------------------+
//| Loads. |
//+------------------------------------------------------------------+
bool SState :: Load ( int file_handle )
{
if ( file_handle == INVALID_HANDLE )
ReturnFalse ;
if ( FileIsEnding ( file_handle ))
ReturnFalse ;
//---
int total = FileReadInteger ( file_handle );
if ( total != ArraySize ( state ))
ReturnFalse ;
//---
for ( int i = 0 ; i < total ; i ++ )
{
if ( FileIsEnding ( file_handle ))
ReturnFalse ;
state [ i ] = FileReadFloat ( file_handle );
}
//---
total = FileReadInteger ( file_handle );
if ( total != ArraySize ( account ))
ReturnFalse ;
//---
for ( int i = 0 ; i < total ; i ++ )
{
if ( FileIsEnding ( file_handle ))
ReturnFalse ;
account [ i ] = FileReadFloat ( file_handle );
}
//---
total = FileReadInteger ( file_handle );
if ( total != ArraySize ( action ))
ReturnFalse ;
//---
for ( int i = 0 ; i < total ; i ++ )
{
if ( FileIsEnding ( file_handle ))
ReturnFalse ;
action [ i ] = MathMin ( MathMax ( FileReadFloat ( file_handle ), 0 ), 1 );
}
//---
total = FileReadInteger ( file_handle );
if ( total != ArraySize ( rewards ))
ReturnFalse ;
//---
for ( int i = 0 ; i < total ; i ++ )
{
if ( FileIsEnding ( file_handle ))
ReturnFalse ;
rewards [ i ] = FileReadFloat ( file_handle );
}
//---
return ( true );
}
//+------------------------------------------------------------------+
//| Builds the market state encoder/decoder description stack. |
//+------------------------------------------------------------------+
bool CreateStateDescriptions ( CArrayObj *& encoder ,
CArrayObj *& decoder
)
{
//---
CLayerDescription * descr ;
//---
if ( ! encoder )
{
encoder = new CArrayObj ();
if ( ! encoder )
ReturnFalse ;
}
if ( ! decoder )
{
decoder = new CArrayObj ();
if ( ! decoder )
ReturnFalse ;
}
//--- State Encoder
encoder . Clear ();
//--- Input layer
if ( ! ( descr = new CLayerDescription ()))
DeleteObjAndFalse ( descr );
descr . type = defNeuronBaseOCL ;
uint prev_count = descr . count = HistoryBars * BarDescr ;
descr . activation = None ;
descr . optimization = ADAM ;
if ( ! encoder . Add ( descr ))
DeleteObjAndFalse ( descr );
//--- layer 1
if ( ! ( descr = new CLayerDescription ()))
DeleteObjAndFalse ( descr );
descr . type = defNeuronBatchNormOCL ;
descr . count = prev_count ;
descr . batch = 1e4 ;
descr . activation = None ;
descr . optimization = ADAM ;
if ( ! encoder . Add ( descr ))
DeleteObjAndFalse ( descr );
//--- layer 2
if ( ! ( descr = new CLayerDescription ()))
DeleteObjAndFalse ( descr );
descr . type = defNeuronDropoutOCL ;
descr . count = prev_count ;
descr . probability = 0.1f ;
descr . batch = BatchSize ;
descr . optimization = ADAM ;
if ( ! encoder . Add ( descr ))
DeleteObjAndFalse ( descr );
//--- layer 3
if ( ! ( descr = new CLayerDescription ()))
DeleteObjAndFalse ( descr );
descr . type = defNeuronCogDriverData ;
descr . window = BarDescr ;
descr . count = HistoryBars ;
{
uint temp [] = { StackSize , StackSize , Quantiles };
if ( ArrayCopy ( descr . units , temp , 0 , 0 , temp . Size ()) < int ( temp . Size ()))
ReturnFalse ;
}
descr . probability = 1.0f ;
descr . activation = None ;
descr . batch = BatchSize ;
descr . optimization = ADAM ;
if ( ! encoder . Add ( descr ))
DeleteObjAndFalse ( descr );
//--- layer 4
if ( ! ( descr = new CLayerDescription ()))
DeleteObjAndFalse ( descr );
descr . type = defNeuronCogDriverRankTCM ;
descr . window = BarDescr * ( 2 * Quantiles + 1 );
descr . count = EmbeddingSize ;
descr . variables = HistoryBars ;
{
uint temp [] = { StackSize , NHeads };
if ( ArrayCopy ( descr . units , temp , 0 , 0 , temp . Size ()) < int ( temp . Size ()))
ReturnFalse ;
}
descr . activation = None ;
descr . batch = BatchSize ;
descr . optimization = ADAM ;
if ( ! encoder . Add ( descr ))
DeleteObjAndFalse ( descr );
//--- layer 5
if ( ! ( descr = new CLayerDescription ()))
DeleteObjAndFalse ( descr );
descr . type = defNeuronCogDriverForecastHead ;
descr . window = EmbeddingSize ;
descr . count = NForecast ;
descr . variables = HistoryBars ;
{
uint temp [] = { Blocks , NHeads };
if ( ArrayCopy ( descr . units , temp , 0 , 0 , temp . Size ()) < int ( temp . Size ()))
ReturnFalse ;
}
descr . activation = None ;
descr . batch = BatchSize ;
descr . optimization = ADAM ;
if ( ! encoder . Add ( descr ))
DeleteObjAndFalse ( descr );
iStateRawForecastLayer = 5 ;
//--- layer 6
if ( ! ( descr = new CLayerDescription ()))
DeleteObjAndFalse ( descr );
descr . type = defNeuronCogDriverForecastToken ;
descr . window = EmbeddingSize ;
descr . count = NForecast ;
descr . activation = None ;
descr . batch = BatchSize ;
descr . optimization = ADAM ;
if ( ! encoder . Add ( descr ))
DeleteObjAndFalse ( descr );
iStateTokenLayer = 6 ;
//--- Forecast Decoder
decoder . Clear ();
//--- Input layer
if ( ! ( descr = new CLayerDescription ()))
DeleteObjAndFalse ( descr );
descr . type = defNeuronBaseOCL ;
prev_count = descr . count = NForecast * EmbeddingSize ;
descr . activation = None ;
descr . optimization = ADAM ;
if ( ! decoder . Add ( descr ))
DeleteObjAndFalse ( descr );
//--- layer 1
if ( ! ( descr = new CLayerDescription ()))
DeleteObjAndFalse ( descr );
descr . type = defNeuronConvOCL ;
descr . count = NForecast ;
descr . window = EmbeddingSize ;
descr . step = EmbeddingSize ;
descr . window_out = 2 * EmbeddingSize ;
descr . activation = GELU ;
descr . batch = BatchSize ;
descr . optimization = ADAM ;
if ( ! decoder . Add ( descr ))
DeleteObjAndFalse ( descr );
//--- layer 2
if ( ! ( descr = new CLayerDescription ()))
DeleteObjAndFalse ( descr );
descr . type = defNeuronConvOCL ;
descr . count = NForecast ;
descr . window = 2 * EmbeddingSize ;
descr . step = 2 * EmbeddingSize ;
descr . window_out = EmbeddingSize ;
descr . activation = GELU ;
descr . batch = BatchSize ;
descr . optimization = ADAM ;
if ( ! decoder . Add ( descr ))
DeleteObjAndFalse ( descr );
//--- layer 3
if ( ! ( descr = new CLayerDescription ()))
DeleteObjAndFalse ( descr );
descr . type = defNeuronConvOCL ;
descr . count = NForecast ;
descr . window = EmbeddingSize ;
descr . step = EmbeddingSize ;
descr . window_out = BarDescr ;
descr . activation = None ;
descr . batch = BatchSize ;
descr . optimization = ADAM ;
if ( ! decoder . Add ( descr ))
DeleteObjAndFalse ( descr );
//--- layer 4
if ( ! ( descr = new CLayerDescription ()))
DeleteObjAndFalse ( descr );
descr . type = defNeuronBatchNormOCL ;
descr . count = NForecast * BarDescr ;
descr . batch = 1e4 ;
descr . activation = None ;
descr . optimization = ADAM ;
if ( ! decoder . Add ( descr ))
DeleteObjAndFalse ( descr );
//---
return ( true );
}
//+-------------------------------------------------------------------+
//| Builds the Actor/Critic description stacks for the AC-SRM graph. |
//+-------------------------------------------------------------------+
bool CreateDescriptions ( CArrayObj *& actor ,
CArrayObj *& critic
)
{
//---
CLayerDescription * descr ;
//---
if ( ! actor )
{
actor = new CArrayObj ();
if ( ! actor )
ReturnFalse ;
}
if ( ! critic )
{
critic = new CArrayObj ();
if ( ! critic )
ReturnFalse ;
}
//--- Actor
actor . Clear ();
//--- Input layer
if ( ! ( descr = new CLayerDescription ()))
DeleteObjAndFalse ( descr );
descr . type = defNeuronBaseOCL ;
uint prev_count = descr . count = AccountDescr ;
descr . activation = None ;
descr . optimization = ADAM ;
if ( ! actor . Add ( descr ))
DeleteObjAndFalse ( descr );
iLatentLayer = 0 ;
//--- layer 1
if ( ! ( descr = new CLayerDescription ()))
DeleteObjAndFalse ( descr );
descr . type = defNeuronBaseOCL ;
descr . count = NScenarios * EmbeddingSize ;
descr . activation = GELU ;
descr . batch = BatchSize ;
descr . optimization = ADAM ;
if ( ! actor . Add ( descr ))
DeleteObjAndFalse ( descr );
//--- layer 2
if ( ! ( descr = new CLayerDescription ()))
DeleteObjAndFalse ( descr );
descr . type = defNeuronMomADMPI ;
descr . window = EmbeddingSize ;
descr . count = StackSize ;
{
uint temp [] = { NScenarios , ForecastTokenDim , NForecast };
if ( ArrayCopy ( descr . units , temp , 0 , 0 , temp . Size ()) < int ( temp . Size ()))
ReturnFalse ;
}
descr . probability = TopK ;
descr . step = NHeads ;
descr . window_out = EmbeddingSize / NHeads ;
descr . activation = None ;
descr . batch = BatchSize ;
descr . optimization = ADAM ;
if ( ! actor . Add ( descr ))
DeleteObjAndFalse ( descr );
iLatentLayer = 2 ;
//--- layer 3
if ( ! ( descr = new CLayerDescription ()))
DeleteObjAndFalse ( descr );
descr . type = defNeuronSpikeConvBlock ;
descr . count = 1 ;
descr . window = EmbeddingSize ;
descr . step = EmbeddingSize ;
descr . window_out = EmbeddingSize ;
descr . variables = 1 ;
descr . batch = BatchSize ;
descr . optimization = ADAM ;
if ( ! actor . Add ( descr ))
DeleteObjAndFalse ( descr );
//--- layer 4
if ( ! ( descr = new CLayerDescription ()))
DeleteObjAndFalse ( descr );
descr . type = defNeuronBaseOCL ;
descr . count = NActions ;
descr . optimization = ADAM ;
if ( ! actor . Add ( descr ))
DeleteObjAndFalse ( descr );
//--- layer 5
if ( ! ( descr = new CLayerDescription ()))
DeleteObjAndFalse ( descr );
descr . type = defNeuronConvOCL ;
descr . count = NActions / 3 ;
descr . window = 3 ;
descr . step = 3 ;
descr . window_out = 3 ;
descr . activation = SIGMOID ;
descr . optimization = ADAM ;
if ( ! actor . Add ( descr ))
DeleteObjAndFalse ( descr );
//--- Critic
critic . Clear ();
//--- Input layer
if ( ! ( descr = new CLayerDescription ()))
DeleteObjAndFalse ( descr );
descr . type = defNeuronBaseOCL ;
descr . count = NActions ;
descr . activation = None ;
descr . optimization = ADAM ;
if ( ! critic . Add ( descr ))
DeleteObjAndFalse ( descr );
//--- layer 1
if ( ! ( descr = new CLayerDescription ()))
DeleteObjAndFalse ( descr );
descr . type = defNeuronMHCrossFAT ;
{
uint temp [] = { 3 , // Inputs window
EmbeddingSize , // Key Dimension
ForecastTokenDim , // Cross window
EmbeddingSize // Embedding size
};
if ( ArrayCopy ( descr . windows , temp ) < ( int ) temp . Size ())
ReturnFalse ;
}
{
uint temp [] = { NActions / 3 , // Query units
NForecast // Cross units
};
if ( ArrayCopy ( descr . units , temp ) < ( int ) temp . Size ())
ReturnFalse ;
}
descr . step = NHeads ; // Heads
descr . batch = 1e4 ;
descr . layers = NExperts ; // Candidates
descr . variables = TopK ; // Top-K
descr . activation = None ;
descr . optimization = ADAM ;
if ( ! critic . Add ( descr ))
DeleteObjAndFalse ( descr );
//--- layer 2
if ( ! ( descr = new CLayerDescription ()))
DeleteObjAndFalse ( descr );
descr . type = defNeuronSpikeConvBlock ;
descr . count = NActions / 3 ;
descr . window = 3 ;
descr . step = 3 ;
descr . window_out = EmbeddingSize ;
descr . variables = 1 ;
descr . batch = BatchSize ;
descr . optimization = ADAM ;
if ( ! critic . Add ( descr ))
DeleteObjAndFalse ( descr );
//--- layer 4
if ( ! ( descr = new CLayerDescription ()))
DeleteObjAndFalse ( descr );
descr . type = defNeuronSpikeConvBlock ;
descr . count = NActions / 3 ;
descr . window = EmbeddingSize ;
descr . step = EmbeddingSize ;
descr . window_out = EmbeddingSize ;
descr . variables = 1 ;
descr . batch = BatchSize ;
descr . optimization = ADAM ;
if ( ! critic . Add ( descr ))
DeleteObjAndFalse ( descr );
//--- layer 5
if ( ! ( descr = new CLayerDescription ()))
DeleteObjAndFalse ( descr );
descr . type = defNeuronBaseOCL ;
prev_count = descr . count = LatentCount ;
descr . activation = SIGMOID ;
descr . batch = BatchSize ;
descr . optimization = ADAM ;
if ( ! critic . Add ( descr ))
DeleteObjAndFalse ( descr );
//--- layer 6
if ( ! ( descr = new CLayerDescription ()))
DeleteObjAndFalse ( descr );
descr . type = defNeuronBaseOCL ;
prev_count = descr . count = NRewards ;
descr . activation = None ;
descr . batch = BatchSize ;
descr . optimization = ADAM ;
if ( ! critic . Add ( descr ))
DeleteObjAndFalse ( descr );
//---
return ( true );
}
#ifndef Study
//+------------------------------------------------------------------+
//| Checks NewBar. |
//+------------------------------------------------------------------+
bool IsNewBar ( void )
{
static datetime last_bar = 0 ;
if ( last_bar >= iTime ( Symb . Name (), TimeFrame , 0 ))
return ( false );
//---
last_bar = iTime ( Symb . Name (), TimeFrame , 0 );
return ( true );
}
//+------------------------------------------------------------------+
//| Creates and manages object lifecycle for CloseByDirection. |
//+------------------------------------------------------------------+
bool CloseByDirection ( ENUM_POSITION_TYPE type )
{
int total = PositionsTotal ();
bool result = true ;
for ( int i = total - 1 ; i >= 0 ; i -- )
{
if ( PositionGetSymbol ( i ) != Symb . Name ())
continue ;
if ( PositionGetInteger ( POSITION_TYPE ) != type )
continue ;
result = ( Trade . PositionClose ( PositionGetInteger ( POSITION_TICKET )) && result );
}
//---
return ( result );
}
//+------------------------------------------------------------------+
//| Implements TrailPosition. |
//+------------------------------------------------------------------+
bool TrailPosition ( ENUM_POSITION_TYPE type , double sl , double tp )
{
int total = PositionsTotal ();
bool result = true ;
datetime time = TimeCurrent () - 5 * PeriodSeconds ( TimeFrame );
//---
for ( int i = 0 ; i < total ; i ++ )
{
if ( PositionGetSymbol ( i ) != Symb . Name ())
continue ;
if ( PositionGetInteger ( POSITION_TYPE ) != type )
continue ;
if ( PositionGetInteger ( POSITION_TIME_UPDATE ) > time )
continue ;
bool modify = false ;
double psl = PositionGetDouble ( POSITION_SL );
double ptp = PositionGetDouble ( POSITION_TP );
switch ( type )
{
case POSITION_TYPE_BUY :
if (( sl - psl ) >= Symb . Point ())
{
psl = sl ;
modify = true ;
}
if ( MathAbs ( tp - ptp ) >= Symb . Point ())
{
ptp = tp ;
modify = true ;
}
break ;
case POSITION_TYPE_SELL :
if (( psl - sl ) >= Symb . Point ())
{
psl = sl ;
modify = true ;
}
if ( MathAbs ( tp - ptp ) >= Symb . Point ())
{
ptp = tp ;
modify = true ;
}
break ;
}
if ( modify )
result = ( Trade . PositionModify ( PositionGetInteger ( POSITION_TICKET ), psl , ptp ) && result );
}
//---
return ( result );
}
//+------------------------------------------------------------------+
//| Creates and manages object lifecycle for ClosePartial. |
//+------------------------------------------------------------------+
bool ClosePartial ( ENUM_POSITION_TYPE type , double value )
{
if ( value <= 0 )
return ( true );
//---
for ( int i = 0 ; ( i < PositionsTotal () && value > 0 ); i ++ )
{
if ( PositionGetSymbol ( i ) != Symb . Name ())
continue ;
if ( PositionGetInteger ( POSITION_TYPE ) != type )
continue ;
double pvalue = PositionGetDouble ( POSITION_VOLUME );
if ( pvalue <= value )
{
if ( Trade . PositionClose ( PositionGetInteger ( POSITION_TICKET )))
{
value -= pvalue ;
i -- ;
}
}
else
{
if ( Trade . PositionClosePartial ( PositionGetInteger ( POSITION_TICKET ), value ))
value = 0 ;
}
}
//---
return ( value <= 0 );
}
#endif
class CDeal : public CObject
{
public :
datetime OpenTime ;
datetime CloseTime ;
ENUM_POSITION_TYPE Type ;
double Volume ;
double OpenPrice ;
double StopLos ;
double TakeProfit ;
double point ;
//---
CDeal ( void );
~ CDeal ( void ) {};
//---
vector < float > Action ( datetime current , double ask , double bid , int period_seconds );
};
//+------------------------------------------------------------------+
//| Creates and manages object lifecycle for CDeal. |
//+------------------------------------------------------------------+
void CDeal :: CDeal ( void ) : OpenTime ( 0 ),
//--- Creates and manages object lifecycle for CloseTime.
CloseTime ( 0 ),
//--- Implements Type.
Type ( POSITION_TYPE_BUY ),
//--- Implements Volume.
Volume ( 0 ),
//--- Implements OpenPrice.
OpenPrice ( 0 ),
//--- Implements StopLos.
StopLos ( 0 ),
//--- Implements TakeProfit.
TakeProfit ( 0 ),
//--- Implements point.
point ( 1e-5 )
{
}
//+------------------------------------------------------------------+
//| Implements Action. |
//+------------------------------------------------------------------+
vector < float > CDeal :: Action ( datetime current , double ask , double bid , int period_seconds )
{
vector < float > result = vector < float >:: Zeros ( NActions );
if (( OpenTime - period_seconds ) > current || CloseTime <= current )
return ( result );
//---
switch ( Type )
{
case POSITION_TYPE_BUY :
result [ 0 ] = float ( Volume );
if ( TakeProfit > 0 )
result [ 1 ] = float (( TakeProfit - ask ) / ( MaxTP * point ));
if ( StopLos > 0 )
result [ 2 ] = float (( ask - StopLos ) / ( MaxSL * point ));
break ;
case POSITION_TYPE_SELL :
result [ 3 ] = float ( Volume );
if ( TakeProfit > 0 )
result [ 4 ] = float (( bid - TakeProfit ) / ( MaxTP * point ));
if ( StopLos > 0 )
result [ 5 ] = float (( StopLos - bid ) / ( MaxSL * point ));
break ;
}
//---
return ( result );
}
class CDeals
{
protected :
CArrayObj Deals ;
public :
CDeals ( void ) { Deals . Clear (); }
~ CDeals ( void ) { Deals . Clear (); }
//---
bool LoadDeals ( string file_name , string symbol , double point );
vector < float > Action ( datetime current , double ask , double bid , int period_seconds );
};
//+------------------------------------------------------------------+
//| Loads Deals. |
//+------------------------------------------------------------------+
bool CDeals :: LoadDeals ( string file_name , string symbol , double point )
{
if ( file_name == NULL || ! FileIsExist ( file_name , FILE_COMMON ))
{
PrintFormat ( " File %s not exist " , file_name );
ReturnFalse ;
}
if ( symbol == NULL )
{
symbol = _Symbol ;
point = _Point ;
}
//---
ResetLastError ();
int handle = FileOpen ( file_name , FILE_READ | FILE_ANSI | FILE_CSV | FILE_COMMON , short ( ' ; ' ), CP_ACP );
if ( handle == INVALID_HANDLE )
{
PrintFormat ( " Error of open file %s: %d " , file_name , GetLastError ());
ReturnFalse ;
}
FileSeek ( handle , 0 , SEEK_SET );
while ( ! FileIsEnding ( handle ))
{
string s = FileReadString ( handle );
datetime open_time = StringToTime ( s );
string type = FileReadString ( handle );
double volume = StringToDouble ( FileReadString ( handle ));
string deal_symbol = FileReadString ( handle );
double open_price = StringToDouble ( FileReadString ( handle ));
volume = MathMin ( volume , StringToDouble ( FileReadString ( handle )));
datetime close_time = StringToTime ( FileReadString ( handle ));
double close_price = StringToDouble ( FileReadString ( handle ));
s = FileReadString ( handle );
s = FileReadString ( handle );
s = FileReadString ( handle );
if ( StringFind ( deal_symbol , symbol , 0 ) < 0 )
continue ;
//---
ResetLastError ();
CDeal * deal = new CDeal ();
if ( ! deal )
{
PrintFormat ( " Error of create new deal object: %d " , GetLastError ());
ReturnFalse ;
}
deal . OpenTime = open_time ;
deal . CloseTime = close_time ;
deal . OpenPrice = open_price ;
deal . Volume = volume ;
deal . point = point ;
if ( type == " Sell " )
{
deal . Type = POSITION_TYPE_SELL ;
if ( close_price < open_price )
{
deal . TakeProfit = close_price ;
deal . StopLos = 0 ;
}
else
{
deal . TakeProfit = 0 ;
deal . StopLos = close_price ;
}
}
else
{
deal . Type = POSITION_TYPE_BUY ;
if ( close_price > open_price )
{
deal . TakeProfit = close_price ;
deal . StopLos = 0 ;
}
else
{
deal . TakeProfit = 0 ;
deal . StopLos = close_price ;
}
}
//---
ResetLastError ();
if ( ! Deals . Add ( deal ))
{
PrintFormat ( " Error of add new deal: %d " , GetLastError ());
ReturnFalse ;
}
}
//---
FileClose ( handle );
//---
return ( true );
}
//+------------------------------------------------------------------+
//| Implements Action (Action). |
//+------------------------------------------------------------------+
vector < float > CDeals :: Action ( datetime current , double ask , double bid , int period_seconds )
{
vector < float > result = vector < float >:: Zeros ( NActions );
for ( int i = 0 ; i < Deals . Total (); i ++ )
{
CDeal * deal = Deals . At ( i );
if ( ! deal )
continue ;
vector < float > action = deal . Action ( current , ask , bid , period_seconds );
result [ 0 ] += action [ 0 ];
result [ 3 ] += action [ 3 ];
result [ 1 ] = MathMax ( result [ 1 ], action [ 1 ]);
result [ 2 ] = MathMax ( result [ 2 ], action [ 2 ]);
result [ 4 ] = MathMax ( result [ 4 ], action [ 4 ]);
result [ 5 ] = MathMax ( result [ 5 ], action [ 5 ]);
}
//---
return ( result );
}
//+------------------------------------------------------------------+
//| Creates or initializes Buffers. |
//+------------------------------------------------------------------+
bool CreateBuffers ( const int start_bar , CBufferFloat * state , CBufferFloat * time , CBufferFloat * forecast )
{
int total_bars = ( start_bar + HistoryBars + ( !! forecast ? NForecast : 0 ));
if ( ! state || ! time || start_bar < 0 ||
total_bars > int ( Rates . Size ()))
ReturnFalse ;
//---
matrix < float > mState = matrix < float >:: Zeros ( BarDescr , HistoryBars );
vector < float > vForecast = vector < float >:: Zeros ( NForecast * BarDescr );
time . Clear ();
time . Reserve ( HistoryBars );
int bar = start_bar + ( !! forecast ? NForecast : 0 );
for ( int b = 0 ; b < ( int ) HistoryBars ; b ++ )
{
float open = ( float ) Rates [ b + bar ]. open ;
float rsi = ( float ) RSI . Main ( b + bar );
float cci = ( float ) CCI . Main ( b + bar );
float atr = ( float ) ATR . Main ( b + bar );
float macd = ( float ) MACD . Main ( b + bar );
float sign = ( float ) MACD . Signal ( b + bar );
if ( rsi == EMPTY_VALUE || cci == EMPTY_VALUE || atr == EMPTY_VALUE || macd == EMPTY_VALUE || sign == EMPTY_VALUE )
ReturnFalse ;
//---
mState [ 0 , b ] = ( float )( Rates [ b + bar ]. close - open );
mState [ 1 , b ] = ( float )( Rates [ b + bar ]. high - open );
mState [ 2 , b ] = ( float )( Rates [ b + bar ]. low - open );
mState [ 3 , b ] = ( float )( Rates [ b + bar ]. tick_volume / 1000.0f );
mState [ 4 , b ] = rsi ;
mState [ 5 , b ] = cci ;
mState [ 6 , b ] = atr ;
mState [ 7 , b ] = macd ;
mState [ 8 , b ] = sign ;
if ( ! time . Add ( float ( Rates [ b + bar ]. time )))
ReturnFalse ;
}
if ( ! state . AssignArray ( mState ))
ReturnFalse ;
if ( time . GetIndex () >= 0 )
if ( ! time . BufferWrite ())
ReturnFalse ;
if ( ! forecast )
return ( true );
//---
for ( int b = 1 ; b <= ( int ) NForecast ; b ++ )
{
float open = ( float ) Rates [ bar - b ]. open ;
float rsi = ( float ) RSI . Main ( bar - b );
float cci = ( float ) CCI . Main ( bar - b );
float atr = ( float ) ATR . Main ( bar - b );
float macd = ( float ) MACD . Main ( bar - b );
float sign = ( float ) MACD . Signal ( bar - b );
if ( rsi == EMPTY_VALUE || cci == EMPTY_VALUE || atr == EMPTY_VALUE || macd == EMPTY_VALUE || sign == EMPTY_VALUE )
ReturnFalse ;
//---
int shift = ( NForecast - b ) * BarDescr ;
vForecast [ shift ] = ( float )( Rates [ bar - b ]. close - open );
vForecast [ shift + 1 ] = ( float )( Rates [ bar - b ]. high - open );
vForecast [ shift + 2 ] = ( float )( Rates [ bar - b ]. low - open );
vForecast [ shift + 3 ] = ( float )( Rates [ bar - b ]. tick_volume / 1000.0f );
vForecast [ shift + 4 ] = rsi ;
vForecast [ shift + 5 ] = cci ;
vForecast [ shift + 6 ] = atr ;
vForecast [ shift + 7 ] = macd ;
vForecast [ shift + 8 ] = sign ;
}
//---
if ( ! forecast . AssignArray ( vForecast ))
ReturnFalse ;
//---
return ( true );
}
//+------------------------------------------------------------------+
//| Implements SampleAccount. |
//+------------------------------------------------------------------+
const vector < float > SampleAccount ( CBufferFloat * state , datetime time , double max_balance , double min_balance = 0 )
{
vector < float > result = vector < float >:: Zeros ( AccountDescr );
if ( ! state )
return ( result );
//---
double marg = 0 ;
if ( ! Symb . RefreshRates () || ! OrderCalcMargin ( ORDER_TYPE_BUY , Symb . Name (), 1 , Symb . Ask (), marg ))
return ( result );
double buy_lot = 0 , sell_lot = 0 , profit = 0 ;
double deal = 0 ; //MathRand() / (32767 * 0.5) - 1;
double multiplyer = 1.0 / ( 60.0 * 60.0 * 10.0 );
//---
double balance = ( max_balance - min_balance ) * MathRand () / 32767.0 + min_balance ;
double prev_balance = balance ;
double equity = balance ;
double prev_equity = balance ;
double position_discount = 0 ;
//---
if ( deal > 0 )
{
double lot = balance / ( 2.0 * marg ) * deal ;
if ( lot >= Symb . LotsMin ())
buy_lot = MathMin ( int (( lot - Symb . LotsMin ()) / Symb . LotsStep ()) * Symb . LotsStep () + Symb . LotsMin (), 1.0 );
}
else
if ( deal < 0 )
{
double lot = MathAbs ( balance / ( 2.0 * marg ) * deal );
if ( lot >= Symb . LotsMin ())
sell_lot = MathMin ( int (( lot - Symb . LotsMin ()) / Symb . LotsStep ()) * Symb . LotsStep () + Symb . LotsMin (), 1.0 );
}
else
prev_balance += ( MathRand () / ( 2.0 * 32767.0 ) - 0.25 ) * balance ;
//---
if ( sell_lot > 0 || buy_lot > 0 )
{
int pos_open = int ( MathRand () / 32767.0 * ( state . Total () / BarDescr - 1 ));
for ( int i = 0 ; i <= pos_open ; i ++ )
{
profit += state . At ( i * BarDescr ) / Symb . TickSize () * Symb . TickValue ();
if ((( buy_lot > 0 && profit < 0 ) ||
( sell_lot > 0 && profit > 0 ))
//--- Implements MathAbs.
&& MathAbs ( profit * ( buy_lot - sell_lot )) > balance / 2 )
{
pos_open = i ;
break ;
}
}
profit *= buy_lot - sell_lot ;
equity += profit ;
prev_equity = equity - state . At ( 0 ) / Symb . TickSize () * Symb . TickValue () * ( buy_lot - sell_lot );
position_discount = pos_open * PeriodSeconds ( TimeFrame ) * multiplyer * MathAbs ( profit );
}
//---
result [ 0 ] = float ( balance / EtalonBalance );
result [ 1 ] = float (( balance - prev_balance ) / prev_balance );
result [ 2 ] = float ( equity / prev_balance );
result [ 3 ] = float (( equity - prev_equity ) / prev_equity );
result [ 4 ] = float ( buy_lot );
result [ 5 ] = float ( sell_lot );
result [ 6 ] = float ( buy_lot * profit / prev_balance );
result [ 7 ] = float ( sell_lot * profit / prev_balance );
result [ 8 ] = float ( position_discount / prev_balance );
double x = time / ( double )( D ' 2024.01.01 ' - D ' 2023.01.01 ' );
result [ 9 ] = float ( MathSin ( x != 0 ? 2.0 * M_PI * x : 0 ));
x = time / ( double ) PeriodSeconds ( PERIOD_MN1 );
result [ 10 ] = float ( MathCos ( x != 0 ? 2.0 * M_PI * x : 0 ));
x = time / ( double ) PeriodSeconds ( PERIOD_W1 );
result [ 11 ] = float ( MathSin ( x != 0 ? 2.0 * M_PI * x : 0 ));
x = time / ( double ) PeriodSeconds ( PERIOD_D1 );
result [ 12 ] = float ( MathSin ( x != 0 ? 2.0 * M_PI * x : 0 ));
//---
return ( result );
}
//+------------------------------------------------------------------+
//| Creates and manages object lifecycle for CheckAction. |
//+------------------------------------------------------------------+
double CheckAction ( CBufferFloat * action , double balance , uint start_position , const int horizon_bars ,
int & outcome_tag )
{
//--- outcome_tag: 0 = no resolvable label (data boundary reached before the
//--- outcome, or unexecutable/penalty value), 1 = TP first, 2 = SL first,
//--- 3 = HORIZON (no level reached before H; includes account blow-out).
//--- Incomplete horizons are NEVER reported as full HORIZON outcomes.
outcome_tag = 0 ;
if ( ! action || start_position >= Rates . Size () || horizon_bars < 1 )
return ( 0 );
//---
double buy_lot = MathMax ( double ( action [ 0 ] - action [ 3 ]), 0 );
double sell_lot = MathMax ( double ( action [ 3 ] - action [ 0 ]), 0 );
double marg = 0 ;
if ( ! OrderCalcMargin ( ORDER_TYPE_BUY , Symb . Name (), 1 , Symb . Ask (), marg ))
return ( 0 );
double point_cost = Symb . TickValue () / Symb . TickSize ();
const double net_lot = MathMax ( buy_lot , sell_lot );
if ( net_lot <= 0 )
{
//--- True no-position action: explicit penalty, no resolvable label.
double loss = - MathMax ( Rates [ start_position ]. high - Rates [ start_position ]. open ,
Rates [ start_position ]. open - Rates [ start_position ]. low ) *
point_cost * balance / ( 2 * marg );
return ( loss );
}
//--- The deal result is evaluated at the STATED lot (money P&L is linear in
//--- the volume). No min-lot floor: the Actor keeps the correct gradient
//--- dSRM/dlot direction even below the broker minimum, and executability
//--- remains an order-level property (IsExecutableOrder), never a label one.
if (( marg * net_lot ) >= balance )
{
double loss = - MathMax ( Rates [ start_position ]. high - Rates [ start_position ]. open ,
Rates [ start_position ]. open - Rates [ start_position ]. low ) *
point_cost * net_lot ;
return ( loss );
}
point_cost *= net_lot ;
//--- The deal is single-leg (buy_lot * sell_lot == 0 by construction).
double tp = 0 , sl = 0 , profit = 0 ;
int stops = MathMax ( Symb . StopsLevel (), 10 );
int spread = Symb . Spread ();
const int steps = MathMax ( 1 , horizon_bars );
if ( buy_lot > 0 )
{
tp = action [ 1 ] * MaxTP ;
sl = action [ 2 ] * MaxSL ;
if ( int ( tp ) < stops || int ( sl ) < ( stops + spread ))
{
double loss = - MathMax ( Rates [ start_position ]. high - Rates [ start_position ]. open ,
Rates [ start_position ]. open - Rates [ start_position ]. low ) *
point_cost * buy_lot ;
return ( loss );
}
tp = ( tp + spread ) * Symb . Point () + Rates [ start_position ]. open ;
sl = Rates [ start_position ]. open - ( sl + spread ) * Symb . Point ();
//--- One-time cost at the entry; it is never charged again.
profit = - spread * Symb . Point () * point_cost ;
for ( int n = 0 ; n < steps ; n ++ )
{
const int i = int ( start_position ) - n ;
//--- The label must be resolvable INSIDE the loaded window: a missing bar
//--- means the outcome does not exist yet (tag 0, no label), it is NOT a
//--- HORIZON result.
if ( i < 0 )
{
outcome_tag = 0 ;
return ( 0.0 );
}
//--- Explicit within-bar rule: SL-before-TP (original methodology order).
//--- The last buy leg is (SL - O_i): a stop closes BELOW the open, so the
//--- realized amount reduces profit. (O_i - SL) would have ADDED a gain.
if ( sl >= Rates [ i ]. low )
{
outcome_tag = 2 ;
profit += ( sl - Rates [ i ]. open ) * point_cost ;
return ( MathPow ( DiscFactor , float ( n )) * profit );
}
if ( tp <= Rates [ i ]. high )
{
outcome_tag = 1 ;
profit += ( tp - Rates [ i ]. open ) * point_cost ;
return ( MathPow ( DiscFactor , float ( n )) * profit );
}
//--- The next open is required for the drift leg; without it the horizon
//--- cannot be completed and the example has no resolvable label.
if ( i < 1 )
{
outcome_tag = 0 ;
return ( 0.0 );
}
//--- Mark-to-market drift from the current bar open to the next bar open.
profit += ( Rates [ i - 1 ]. open - Rates [ i ]. open ) * point_cost ;
if ( - profit >= balance )
{
outcome_tag = 3 ;
return ( MathPow ( DiscFactor , float ( n )) * profit - 1000.0 );
}
}
//--- Neither level reached before H: discounted Equity change over the period.
outcome_tag = 3 ;
return ( MathPow ( DiscFactor , float ( steps )) * profit );
}
//---
if ( sell_lot > 0 )
{
tp = action [ 4 ] * MaxTP ;
sl = action [ 5 ] * MaxSL ;
if ( int ( tp ) < stops || int ( sl ) < ( stops + spread ))
{
double loss = - MathMax ( Rates [ start_position ]. high - Rates [ start_position ]. open ,
Rates [ start_position ]. open - Rates [ start_position ]. low ) *
point_cost * sell_lot ;
return ( loss );
}
tp = Rates [ start_position ]. open - ( tp + spread ) * Symb . Point ();
sl = Rates [ start_position ]. open + ( sl + spread ) * Symb . Point ();
profit = - spread * Symb . Point () * point_cost ;
for ( int n = 0 ; n < steps ; n ++ )
{
const int i = int ( start_position ) - n ;
if ( i < 0 )
{
outcome_tag = 0 ;
return ( 0.0 );
}
//--- The last sell leg is (O_i - SL): a stop closes ABOVE the open, so the
//--- realized amount reduces profit. (SL - O_i) had added a gain.
if ( sl <= Rates [ i ]. high )
{
outcome_tag = 2 ;
profit += ( Rates [ i ]. open - sl ) * point_cost ;
return ( MathPow ( DiscFactor , float ( n )) * profit );
}
if ( tp >= Rates [ i ]. low )
{
outcome_tag = 1 ;
profit += ( Rates [ i ]. open - tp ) * point_cost ;
return ( MathPow ( DiscFactor , float ( n )) * profit );
}
if ( i < 1 )
{
outcome_tag = 0 ;
return ( 0.0 );
}
profit += ( Rates [ i ]. open - Rates [ i - 1 ]. open ) * point_cost ;
if ( - profit >= balance )
{
outcome_tag = 3 ;
return ( MathPow ( DiscFactor , float ( n )) * profit - 1000.0 );
}
}
outcome_tag = 3 ;
return ( MathPow ( DiscFactor , float ( steps )) * profit );
}
//---
return ( 0 );
}
//+------------------------------------------------------------------+
//| Implements OraculAction. |
//+------------------------------------------------------------------+
vector < float > OraculAction ( const vector < float > & account , CBufferFloat * forecat )
{
//--- Look for target
vector < float > result = vector < float >:: Zeros ( NActions );
matrix < float > fstate = matrix < float >:: Zeros ( NForecast , BarDescr );
if ( ! forecat . GetData ( fstate ))
return ( result );
//---
vector < float > target = fstate . Col ( 0 ). CumSum ();
if ( account [ 4 ] > account [ 5 ])
{
float tp = 0 ;
float sl = 0 ;
float cur_sl = float ( MathMax ( MathRand () / 32767.0 , 0.01 ) * MaxSL * Point ());
int pos = 0 ;
for ( int j = 0 ; j < NForecast ; j ++ )
{
tp = MathMax ( tp , target [ j ] + fstate [ j , 1 ] - fstate [ j , 0 ]);
pos = j ;
if ( cur_sl >= - ( target [ j ] + fstate [ j , 2 ] - fstate [ j , 0 ]))
break ;
sl = MathMin ( sl , target [ j ] + fstate [ j , 2 ] - fstate [ j , 0 ]);
}
if ( pos > 0 && tp > 0 )
{
sl = float ( MathMax ( MathMin ( MathAbs ( sl ) / ( MaxSL * Point ()), 1 ), 0.01 ));
tp = float ( MathMax ( MathMin ( tp / ( MaxTP * Point ()), 1 ), 0.01 ));
result [ 0 ] = MathMax ( result [ 0 ] - result [ 3 ], 0.011f );
result [ 5 ] = result [ 1 ] = tp ;
result [ 4 ] = result [ 2 ] = sl ;
result [ 3 ] = 0 ;
}
}
else
{
if ( account [ 4 ] < account [ 5 ])
{
float tp = 0 ;
float sl = 0 ;
float cur_sl = float ( MathMax ( MathRand () / 32767.0 , 0.01 ) * MaxSL * Point ());
int pos = 0 ;
for ( int j = 0 ; j < NForecast ; j ++ )
{
tp = MathMin ( tp , target [ j ] + fstate [ j , 2 ] - fstate [ j , 0 ]);
pos = j ;
if ( cur_sl <= target [ j ] + fstate [ j , 1 ] - fstate [ j , 0 ])
break ;
sl = MathMax ( sl , target [ j ] + fstate [ j , 1 ] - fstate [ j , 0 ]);
}
if ( pos > 0 && tp < 0 )
{
sl = float ( MathMax ( MathMin ( MathAbs ( sl ) / ( MaxSL * Point ()), 1 ), 0.01 ));
tp = float ( MathMax ( MathMin ( - tp / ( MaxTP * Point ()), 1 ), 0.01 ));
result [ 3 ] = MathMax ( result [ 3 ] - result [ 0 ], 0.011f );
result [ 2 ] = result [ 4 ] = tp ;
result [ 1 ] = result [ 5 ] = sl ;
result [ 0 ] = 0 ;
}
}
else
{
ulong argmin = target . ArgMin ();
ulong argmax = target . ArgMax ();
float max_sl = float ( MaxSL * Point ());
double equity = account [ 2 ] * account [ 0 ] * EtalonBalance / ( 1 + account [ 1 ]);
while ( argmax > 0 && argmin > 0 )
{
if ( argmax < argmin && target [ argmax ] / 2 > MathAbs ( target [ argmin ]) && MathAbs ( target [ argmin ]) < max_sl )
break ;
if ( argmax > argmin && target [ argmax ] < MathAbs ( target [ argmin ] / 2 ) && target [ argmax ] < max_sl )
break ;
target . Resize ( MathMin ( argmax , argmin ));
argmin = target . ArgMin ();
argmax = target . ArgMax ();
}
if ( argmin == 0 || ( argmax < argmin && argmax > 0 ))
{
float tp = 0 ;
float sl = 0 ;
float cur_sl = - float ( MaxSL * Point ());
ulong pos = 0 ;
for ( ulong j = 0 ; j < argmax ; j ++ )
{
tp = MathMax ( tp , target [ j ] + fstate [ j , 1 ] - fstate [ j , 0 ]);
pos = j ;
if ( cur_sl >= - ( target [ j ] + fstate [ j , 2 ] - fstate [ j , 0 ]))
break ;
sl = MathMin ( sl , target [ j ] + fstate [ j , 2 ] - fstate [ j , 0 ]);
}
if ( pos > 0 && tp > 0 )
{
sl = ( float ) MathMax ( MathMin ( MathAbs ( sl ) / ( MaxSL * Point ()), 1 ), 0.01 );
tp = ( float ) MathMin ( tp / ( MaxTP * Point ()), 1 );
result [ 0 ] = float ( MathMax ( equity / 100 * 0.01 , 0.011 ));
result [ 5 ] = result [ 1 ] = tp ;
result [ 4 ] = result [ 2 ] = sl ;
result [ 3 ] = 0 ;
}
}
else
{
if ( argmax == 0 || argmax > argmin )
{
float tp = 0 ;
float sl = 0 ;
float cur_sl = float ( MaxSL * Point ());
ulong pos = 0 ;
for ( ulong j = 0 ; j < argmin ; j ++ )
{
tp = MathMin ( tp , target [ j ] + fstate [ j , 2 ] - fstate [ j , 0 ]);
pos = j ;
if ( cur_sl <= target [ j ] + fstate [ j , 1 ] - fstate [ j , 0 ])
break ;
sl = MathMax ( sl , target [ j ] + fstate [ j , 1 ] - fstate [ j , 0 ]);
}
if ( pos > 0 && tp < 0 )
{
sl = ( float ) MathMax ( MathMin ( MathAbs ( sl ) / ( MaxSL * Point ()), 1 ), 0.01 );
tp = ( float ) MathMin ( - tp / ( MaxTP * Point ()), 1 );
result [ 3 ] = float ( MathMax ( equity / 100 * 0.01 , 0.011 ));
result [ 2 ] = result [ 4 ] = tp ;
result [ 1 ] = result [ 5 ] = sl ;
result [ 0 ] = 0 ;
}
}
}
}
}
//---
return ( result );
}
//+------------------------------------------------------------------+
//| Skill ScenarioForecast and Actor/Critic runtime |
//+------------------------------------------------------------------+
bool SkillRecoverySmoke = false ;
uint SkillRecoverySmokeBatches = 2000 ;
uint SkillRecoverySmokeAge = 512 ;
const uint Skill_FORMAT_VERSION = 7 ;
const string Skill_MARKET_FILE = " ACSRMMarket.nnw " ;
const string Skill_TARGET_FILE = " ACSRMTarget.nnw " ;
const string Skill_MANIFEST_FILE = " ACSRMForecast.manifest " ;
const string Skill_ACTOR_FILE = " ACSRMActor.nnw " ;
const string Skill_Q1_FILE = " ACSRMQ1.nnw " ;
const string Skill_Q2_FILE = " ACSRMQ2.nnw " ;
const string Skill_AC_MANIFEST_FILE = " ACSRMActorCritic.manifest " ;
const string Skill_ACTOR_NEXT_FILE = " ACSRMActor.next.nnw " ;
const string Skill_Q1_NEXT_FILE = " ACSRMQ1.next.nnw " ;
const string Skill_Q2_NEXT_FILE = " ACSRMQ2.next.nnw " ;
const string Skill_AC_MANIFEST_NEXT_FILE = " ACSRMActorCritic.next.manifest " ;
const string Skill_ACTOR_PREVIOUS_FILE = " ACSRMActor.previous.nnw " ;
const string Skill_Q1_PREVIOUS_FILE = " ACSRMQ1.previous.nnw " ;
const string Skill_Q2_PREVIOUS_FILE = " ACSRMQ2.previous.nnw " ;
const string Skill_AC_MANIFEST_PREVIOUS_FILE = " ACSRMActorCritic.previous.manifest " ;
const string Skill_AC_TRANSACTION_FILE = " ACSRMActorCritic.transaction " ;
const uint Skill_AC_FORMAT_VERSION = 14 ;
const uint Skill_AC_TRANSACTION_VERSION = 1 ;
const uint Skill_AC_CONFIG_VERSION = 9 ;
const string Skill_OOS_FILE = " ACSRMOOS.csv " ;
const string Skill_OOS_MANIFEST_FILE = " ACSRMOOS.manifest " ;
//--- Stage 02 publishes only the canonical production tuple. A complete
//--- next tuple is validated first; previous is retained only until reload
//--- proof accepts the canonical manifest as the commit point.
const string ACSRM_STAGE02_MARKET_NEXT_FILE = " ACSRMMarket.next.nnw " ;
const string ACSRM_STAGE02_TARGET_NEXT_FILE = " ACSRMTarget.next.nnw " ;
const string ACSRM_STAGE02_MANIFEST_NEXT_FILE = " ACSRMForecast.next.manifest " ;
const string ACSRM_STAGE02_MARKET_PREVIOUS_FILE = " ACSRMMarket.previous.nnw " ;
const string ACSRM_STAGE02_TARGET_PREVIOUS_FILE = " ACSRMTarget.previous.nnw " ;
const string ACSRM_STAGE02_MANIFEST_PREVIOUS_FILE = " ACSRMForecast.previous.manifest " ;
//--- Stage 01 uses an independent transaction namespace. The short names
//--- remain the only normal load contract; manifest is its commit record.
const string ACSRM_STAGE01_MARKET_NEXT_FILE = " ACSRMMarket.stage01.next.nnw " ;
const string ACSRM_STAGE01_TARGET_NEXT_FILE = " ACSRMTarget.stage01.next.nnw " ;
const string ACSRM_STAGE01_MANIFEST_NEXT_FILE = " ACSRMForecast.stage01.next.manifest " ;
const string ACSRM_STAGE01_MARKET_PREVIOUS_FILE = " ACSRMMarket.stage01.previous.nnw " ;
const string ACSRM_STAGE01_TARGET_PREVIOUS_FILE = " ACSRMTarget.stage01.previous.nnw " ;
const string ACSRM_STAGE01_MANIFEST_PREVIOUS_FILE = " ACSRMForecast.stage01.previous.manifest " ;
const string ACSRM_STAGE01_TRANSACTION_FILE = " ACSRMForecast.stage01.transaction " ;
const uint ACSRM_STAGE01_TRANSACTION_VERSION = 1 ;
//--- Legacy generation selectors are migration markers only. They are never
//--- resolved by a normal loader, which fails closed until an explicit migration.
const string ACSRM_STAGE02_ACTIVE_SELECTOR = " ACSRMForecast.stage02.active " ;
const string ACSRM_STAGE02_SELECTOR_NEXT = " ACSRMForecast.stage02.active.next " ;
const string ACSRM_STAGE02_SELECTOR_PREVIOUS = " ACSRMForecast.stage02.active.previous " ;
const string ACSRM_STAGE02_SELECTOR_RESTORE = " ACSRMForecast.stage02.active.restore " ;
const string ACSRM_STAGE02_GENERATION_PREFIX = " .stage02. " ;
const string ACSRM_STAGE02_LEGACY_TRANSACTION_FILE = " ACSRMForecast.stage02.transaction " ;
const string ACSRM_STAGE02_LEGACY_TRANSACTION_NEXT_FILE = " ACSRMForecast.stage02.transaction.next " ;
const uint ACSRM_STAGE02_SELECTOR_VERSION = 1 ;
//--- Normal loading always uses these canonical physical production names.
string SkillActiveMarketFile = Skill_MARKET_FILE ;
string SkillActiveTargetFile = Skill_TARGET_FILE ;
string SkillActiveManifestFile = Skill_MANIFEST_FILE ;
//+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Skill builds transitions directly from historical windows Base CNet already provides Layer(int) and FeedForwardLayer(CNeuronBaseOCL*). A transient, non-owning layer view lets the library transpose |
//+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
class CD2SkillBufferView : public CNeuronBaseOCL
{
public :
bool Bind ( CBufferFloat * source );
//--- Implements Unbind.
void Unbind ( void )
{
Output = NULL ;
}
};
//+------------------------------------------------------------------+
//| Implements Bind. |
//+------------------------------------------------------------------+
bool CD2SkillBufferView :: Bind ( CBufferFloat * source )
{
if ( ! source || source . GetIndex () < 0 )
ReturnFalse ;
if ( Output != source )
DeleteObj ( Output );
Output = source ;
return ( true );
}
//+------------------------------------------------------------------+
//| Device-only composition adapter, all operations reuse existing |
//+------------------------------------------------------------------+
class CD2SkillDeviceOps : public CNeuronBaseOCL
{
public :
//--- Implements Bind.
bool Bind ( COpenCLMy * open_cl )
{
OpenCL = open_cl ;
return ( CheckPointer ( OpenCL ) != POINTER_INVALID );
}
bool Copy ( CBufferFloat * source , CBufferFloat * destination , const uint total );
bool Join2 ( CBufferFloat * first , const uint first_total , CBufferFloat * second ,
const uint second_total , CBufferFloat * dest );
bool Join4 ( CBufferFloat * first , CBufferFloat * second , CBufferFloat * third ,
CBufferFloat * fourth , CBufferFloat * destination , const uint block );
bool Subtract ( CBufferFloat * first , CBufferFloat * second , CBufferFloat * destination ,
const uint dimension )
{
return ( Different ( first , second , destination , dimension ));
}
bool BroadcastSum ( CBufferFloat * vector_in , CBufferFloat * matrix_in ,
CBufferFloat * destination , const uint dimension , const uint variables )
{
return ( SumVecMatrix ( vector_in , matrix_in , destination , dimension , variables ));
}
bool Add ( CBufferFloat * first , CBufferFloat * second , CBufferFloat * destination ,
const uint dimension )
{
return ( SumAndNormalize ( first , second , destination , dimension , false , 0 , 0 , 0 , 1.0f ));
}
bool Split2 ( CBufferFloat * first , CBufferFloat * second , CBufferFloat * source ,
const uint first_total , const uint second_total )
{
return ( DeConcat ( first , second , source , first_total , second_total , 1 ));
}
//--- Public device wrappers over the protected base element ops.
bool IdentDifferenceOnDevice ( CBufferFloat * tensor , CBufferFloat * out ,
const int dimension )
{
return ( IdentDifferent ( tensor , out , dimension ));
}
bool ElementMultiplyOnDevice ( CBufferFloat * first , CBufferFloat * second ,
CBufferFloat * out )
{
return ( ElementMult ( first , second , out ));
}
//--- Elementwise activation-derivative scaling: inputs_gr[i] = Deactivation(output_gr[i], inputs[i], activation). For SIGMOID every component gets its OWN (1 - a_i) factor (elementwise), not an identity-row subtraction like IdentDifferent.
bool DeActivationOnDevice ( CBufferFloat * inputs , CBufferFloat * inputs_gr ,
CBufferFloat * output_gr , const int activat )
{
return ( DeActivation ( inputs , inputs_gr , output_gr , activat ));
}
};
//+------------------------------------------------------------------+
//| Creates and manages the Copy device operation. |
//+------------------------------------------------------------------+
bool CD2SkillDeviceOps :: Copy ( CBufferFloat * source , CBufferFloat * destination , const uint total )
{
if ( ! source || ! destination || source . Total () != int ( total ) ||
destination . Total () != int ( total ) || source . GetIndex () < 0 ||
destination . GetIndex () < 0 || source . GetOpenCL () != OpenCL ||
destination . GetOpenCL () != OpenCL )
ReturnFalse ;
//---
return ( CopyBufferRaw ( source , destination , total ));
}
//+------------------------------------------------------------------+
//| Concatenates two device buffers into one destination. |
//+------------------------------------------------------------------+
bool CD2SkillDeviceOps :: Join2 ( CBufferFloat * first , const uint first_total , CBufferFloat * second ,
const uint second_total , CBufferFloat * dest )
{
if ( ! first || ! second || ! dest || first . Total () != int ( first_total ) ||
second . Total () != int ( second_total ) ||
dest . Total () != int ( first_total + second_total ) || first . GetIndex () < 0 ||
second . GetIndex () < 0 || dest . GetIndex () < 0 )
ReturnFalse ;
return ( Concat ( first , second , dest , first_total , second_total , 1 ));
}
//+------------------------------------------------------------------+
//| Concatenates four device buffers into one destination. |
//+------------------------------------------------------------------+
bool CD2SkillDeviceOps :: Join4 ( CBufferFloat * first , CBufferFloat * second , CBufferFloat * third ,
CBufferFloat * fourth , CBufferFloat * destination , const uint block )
{
if ( ! first || ! second || ! third || ! fourth || ! destination ||
first . Total () != int ( block ) || second . Total () != int ( block ) ||
third . Total () != int ( block ) || fourth . Total () != int ( block ) ||
destination . Total () != int ( 4 * block ) || first . GetIndex () < 0 || second . GetIndex () < 0 ||
third . GetIndex () < 0 || fourth . GetIndex () < 0 || destination . GetIndex () < 0 )
ReturnFalse ;
return ( Concat ( first , second , third , fourth , destination , block , block , block , block , 1 ));
}
//---
CNet SkillMarket ;
CNet SkillTarget ;
CNeuronScenarioForecast * SkillForecast = NULL ;
//+------------------------------------------------------------------+
//| Resolves RankTCM at its fixed ACSRM graph slot. |
//+------------------------------------------------------------------+
CNeuronBaseOCL * GetRankTCM ( void )
{
//--- Resolve RankTCM only when the fixed ACSRM graph slot has its declared type.
CNeuronBaseOCL * layer = SkillMarket . Layer ( 4 );
//--- Return the typed predecessor or NULL for a stale or incompatible graph.
return ( layer && layer . Type () == defNeuronCogDriverRankTCM ? layer : NULL );
}
//+------------------------------------------------------------------+
//| Resolves ACSRM at its fixed MarketEncoder graph slot. |
//+------------------------------------------------------------------+
CNeuronOMPBOCL * GetACSRM ( void )
{
//--- Resolve ACSRM only when the fixed MarketEncoder graph slot has its declared type.
CNeuronBaseOCL * layer = SkillMarket . Layer ( 5 );
//--- Return the typed layer or NULL for a stale or incompatible graph.
return ( layer && layer . Type () == defNeuronOMPBOCL ? ( CNeuronOMPBOCL * ) layer : NULL );
}
//+------------------------------------------------------------------+
//| Sets an ACSRM runtime mode after clearing source-anchor state. |
//+------------------------------------------------------------------+
bool SetACSRMMode ( const ENUM_OMPB_MODE mode )
{
//--- Clear source-anchor state before selecting the requested ACSRM runtime mode.
CNeuronOMPBOCL * layer = GetACSRM ();
//--- Finalize the mode change only for a validated ACSRM layer.
return ( layer && layer . SetSourceAnchor ( false ) && layer . SetMode ( mode ));
}
//+------------------------------------------------------------------+
//| Configures ACSRM mode and BYPASS training safeguards. |
//+------------------------------------------------------------------+
bool ConfigureACSRM ( const ENUM_OMPB_MODE mode )
{
//--- Resolve the layer and install the requested ACSRM runtime mode.
CNeuronOMPBOCL * layer = GetACSRM ();
if ( ! layer || ! SetACSRMMode ( mode ))
ReturnFalse ;
//--- BYPASS must not retain a trainable ACSRM layer during MarketEncoder training.
if ( mode == OMPB_BYPASS )
layer . TrainMode ( false );
//--- Finalize the ACSRM configuration after the runtime safeguards are applied.
return ( true );
}
//+------------------------------------------------------------------+
//| Resets only the ACSRM immutable Reference history. |
//+------------------------------------------------------------------+
bool ResetACSRMReference ( void )
{
//--- Resolve ACSRM before discarding its immutable Reference history explicitly.
CNeuronOMPBOCL * layer = GetACSRM ();
//--- Finalize only when the validated layer reset succeeds.
return ( layer && layer . ResetReference ());
}
//+------------------------------------------------------------------+
//| Reads ACSRM runtime diagnostic counters and regularizer values. |
//+------------------------------------------------------------------+
bool ReadACSRMDiagnostics ( uint & reference_count , uint & current_count ,
float & disagreement , float & kl , float & alpha_prior ,
uint & invalid_fallbacks , uint & kl_rejects )
{
//--- Resolve ACSRM before exporting its runtime-only diagnostic counters.
CNeuronOMPBOCL * layer = GetACSRM ();
if ( ! layer )
ReturnFalse ;
//--- Read the immutable Reference and rolling Current occupancy counters.
reference_count = layer . ReferenceCount ();
current_count = layer . CurrentCount ();
//--- Read the latest regularizer values and guarded-inference counters.
disagreement = layer . LastDisagreement ();
kl = layer . LastKL ();
alpha_prior = layer . LastAlphaPrior ();
invalid_fallbacks = layer . InvalidFallbacks ();
kl_rejects = layer . KLRejects ();
//--- Finalize the diagnostics snapshot after all values have been read.
return ( true );
}
//+------------------------------------------------------------------+
//| Checks that BYPASS leaves ACSRM diagnostics unchanged. |
//+------------------------------------------------------------------+
bool SkillCheckACSRMBypassInvariant ( const uint reference_count_before ,
const uint current_count_before ,
const float disagreement_before ,
const float kl_before ,
const float alpha_prior_before ,
const uint invalid_fallbacks_before ,
const uint kl_rejects_before ,
const string scope )
{
uint reference_count_after , current_count_after , invalid_fallbacks_after , kl_rejects_after ;
float disagreement_after , kl_after , alpha_prior_after ;
if ( ! ReadACSRMDiagnostics ( reference_count_after , current_count_after , disagreement_after ,
kl_after , alpha_prior_after , invalid_fallbacks_after , kl_rejects_after ))
{
PrintFormat ( " ACSRM_STAGE01_BYPASS_DIAGNOSTIC_FAIL scope=%s reason=read_after " , scope );
return ( false );
}
const bool unchanged = ( reference_count_after == reference_count_before &&
current_count_after == current_count_before &&
disagreement_after == disagreement_before &&
kl_after == kl_before &&
alpha_prior_after == alpha_prior_before &&
invalid_fallbacks_after == invalid_fallbacks_before &&
kl_rejects_after == kl_rejects_before );
if ( ! unchanged )
{
PrintFormat ( " ACSRM_STAGE01_BYPASS_DIAGNOSTIC_FAIL scope=%s " +
" before=(%u,%u,%.9g,%.9g,%.9g,%u,%u) " +
" after=(%u,%u,%.9g,%.9g,%.9g,%u,%u) " ,
scope , reference_count_before , current_count_before , disagreement_before ,
kl_before , alpha_prior_before , invalid_fallbacks_before , kl_rejects_before ,
reference_count_after , current_count_after , disagreement_after , kl_after ,
alpha_prior_after , invalid_fallbacks_after , kl_rejects_after );
return ( false );
}
PrintFormat ( " ACSRM_STAGE01_BYPASS_DIAGNOSTIC_PASS scope=%s state_unchanged=true " , scope );
return ( true );
}
//---
CBufferFloat SkillState ;
CBufferFloat SkillTime ;
CBufferFloat SkillFuture ;
CBufferFloat SkillLatentTarget ;
CBufferFloat SkillLatentDelta ;
CBufferFloat SkillProbeState ;
CBufferFloat SkillProbeTime ;
CD2SkillBufferView SkillFutureView ;
CNeuronTransposeRCDOCL SkillFutureTranspose ;
CBufferFloat SkillLatentZero ;
CBufferFloat SkillLatentNegativeMarket ;
//---
ulong SkillInvalidBatches = 0 ;
ulong SkillBatches = 0 ;
ulong SkillResponsibilityMicroseconds = 0 ;
uint SkillCompletedEpochs = 0 ;
ulong SkillLastSignature = 0 ;
ulong SkillProductionBaseFingerprint = 0 ;
ulong SkillProductionACSRMFingerprint = 0 ;
bool SkillProductionSignatureReady = false ;
bool SkillReady = false ;
CBufferFloat SkillFrozenGeneratorWeights ;
CBufferFloat SkillFrozenRouterWeights ;
CBufferFloat SkillFrozenConfidenceWeights ;
CBufferFloat SkillFrozenPrototypes ;
CBufferFloat SkillFrozenEMASums ;
CBufferFloat SkillFrozenEMACounts ;
CBufferFloat SkillFrozenUsage ;
CBufferFloat SkillFrozenInactive ;
CBufferFloat SkillFrozenInactivityAge ;
bool SkillFrozenBaselineReady = false ;
CD2SkillDeviceOps SkillDevice ;
//--- Deal-evaluation horizon (bars) pinned by each Stage 03 expert from its
//--- own InpDealHorizon input. The manifest writer and validator share this
//--- value so a later stage that rewrites the tuple preserves the fixed key.
int SkillManifestDealHorizon = 24 ;
double SkillManifestOnlineDiscount = 0.5 ;
double SkillManifestTargetTau = 0.0 ;
int SkillManifestTargetUpdatePeriod = 0 ;
//--- Manifest-quantiles symmetry (rank-sensitive SRM rearrangement, canonical N=32)
uint SkillManifestQuantiles = 32 ;
//--- Device scratch tensors for the final-SIGMOID policy gradient conversion.
CBufferFloat ActorDerivOneMinus ;
CBufferFloat ActorDeriv ;
CBufferFloat ActorGradScratch ;
//+------------------------------------------------------------------+
//| Configures optional forecast recovery diagnostics. |
//+------------------------------------------------------------------+
bool SkillConfigureForecastRun ( const bool recovery_smoke ,
const uint recovery_batches , const uint recovery_age )
{
if ( recovery_smoke && ( recovery_batches == 0 || recovery_age == 0 ))
ReturnFalse ;
SkillRecoverySmoke = recovery_smoke ;
SkillRecoverySmokeBatches = recovery_batches ;
SkillRecoverySmokeAge = recovery_age ;
return ( true );
}
//+------------------------------------------------------------------+
//| Implements ForecastRecoveryAge. |
//+------------------------------------------------------------------+
uint ForecastRecoveryAge ( void )
{
if ( SkillRecoverySmoke )
return ( MathMax ( 1 , int ( SkillRecoverySmokeAge )));
const int seconds = PeriodSeconds ( TimeFrame );
if ( seconds <= 0 )
return ( 6240 );
//--- 52 five-day trading weeks: one calendar-equivalent year on H1.
return ( uint ) MathMax ( 1.0 , MathRound ( 6240.0 * PeriodSeconds ( PERIOD_H1 ) / seconds ));
}
//+-------------------------------------------------------------------+
//| Creates and manages object lifecycle for ConfigureForecastRec... |
//+-------------------------------------------------------------------+
bool ConfigureForecastRecoveryAge ( void )
{
if ( ! SkillForecast || ! SkillForecast . SetRecoveryAge ( ForecastRecoveryAge ()))
ReturnFalse ;
PrintFormat ( " %s %s recovery_age=%u bars " , ACSRM_LOG_PREFIX , ( SkillRecoverySmoke ? " smoke " : " forecast " ),
SkillForecast . RecoveryAge ());
return ( true );
}
//+------------------------------------------------------------------+
//| Implements SkillInitTrainingBuffers. |
//+------------------------------------------------------------------+
bool SkillInitTrainingBuffers ( void )
{
COpenCLMy * open_cl = SkillMarket . GetOpenCL ();
if ( ! open_cl || ! SkillDevice . Bind ( open_cl ))
ReturnFalse ;
if ( ! SkillFutureTranspose . Init ( 0 , 0 , open_cl , NForecast , BarDescr , 1 , ADAM , BatchSize ) ||
! SkillFuture . BufferInit (( NForecast * BarDescr ), 0 ) ||
! SkillFuture . BufferCreate ( open_cl ) ||
! SkillLatentZero . BufferInit (( BarDescr * EmbeddingSize ), 0 ) ||
! SkillLatentZero . BufferCreate ( open_cl ) ||
! SkillLatentNegativeMarket . BufferInit (( BarDescr * EmbeddingSize ), 0 ) ||
! SkillLatentNegativeMarket . BufferCreate ( open_cl ) ||
! SkillLatentTarget . BufferInit (( BarDescr * NForecast * EmbeddingSize ), 0 ) ||
! SkillLatentTarget . BufferCreate ( open_cl ) ||
! SkillLatentDelta . BufferInit (( BarDescr * NForecast * EmbeddingSize ), 0 ) ||
! SkillLatentDelta . BufferCreate ( open_cl ))
ReturnFalse ;
return ( true );
}
//+------------------------------------------------------------------+
//| Implements SkillAddBase. |
//+------------------------------------------------------------------+
bool SkillAddBase ( CArrayObj * description , const uint count )
{
if ( ! description )
ReturnFalse ;
CLayerDescription * descr = new CLayerDescription ();
if ( ! descr )
ReturnFalse ;
descr . type = defNeuronBaseOCL ;
descr . count = count ;
descr . activation = None ;
descr . optimization = ADAM ;
descr . batch = BatchSize ;
if ( description . Add ( descr ))
return ( true );
DeleteObjAndFalse ( descr );
}
//+------------------------------------------------------------------+
//| Implements SkillNormalizeDescriptionBatch. |
//+------------------------------------------------------------------+
bool SkillNormalizeDescriptionBatch ( CArrayObj * description )
{
if ( ! description )
ReturnFalse ;
for ( int i = 0 ; i < description . Total (); i ++ )
{
CLayerDescription * layer = ( CLayerDescription * ) description . At ( i );
if ( ! layer )
ReturnFalse ;
layer . batch = BatchSize ;
}
//---
return ( true );
}
//+------------------------------------------------------------------+
//| Implements SkillCreateDescriptions. |
//+------------------------------------------------------------------+
bool SkillCreateDescriptions ( CArrayObj *& market , CArrayObj *& target )
{
CArrayObj * legacy_decoder = NULL ;
//--- Function if.
if ( ! CreateStateDescriptions ( market , legacy_decoder ))
{
DeleteObj ( legacy_decoder );
ReturnFalse ;
}
DeleteObj ( legacy_decoder );
//--- Keep exactly CogDriver layers 0..4 (RankTCM is the boundary).
int layer = market . Total () - 1 ;
//--- Function while.
while ( layer >= 0 )
{
CLayerDescription * descr = market . At ( layer );
//--- Function if.
if ( ! descr || descr . type != defNeuronCogDriverRankTCM )
{
if ( ! market . Delete ( layer ))
ReturnFalse ;
layer -- ;
}
else
break ;
}
if ( market . Total () <= 0 )
ReturnFalse ;
if ( ! SkillNormalizeDescriptionBatch ( market ))
ReturnFalse ;
//--- CreateBuffers already stores state feature-major as
//--- [BarDescr,HistoryBars], which is CogDriverData's required input layout.
if ( ! market . Delete ( layer ) || ! market . Delete ( layer - 1 ))
ReturnFalse ;
CLayerDescription * descr = new CLayerDescription ();
if ( ! descr )
ReturnFalse ;
descr . type = defNeuronCogDriverData ;
descr . window = HistoryBars ;
descr . count = BarDescr ;
{
uint units [] = { StackSize , StackSize , Quantiles };
if ( ArrayCopy ( descr . units , units , 0 , 0 , units . Size ()) < int ( units . Size ()))
DeleteObjAndFalse ( descr );
}
descr . probability = 1.0f ;
descr . activation = None ;
descr . optimization = ADAM ;
descr . batch = BatchSize ;
if ( ! market . Add ( descr ))
DeleteObjAndFalse ( descr );
//---
descr = new CLayerDescription ();
if ( ! descr )
ReturnFalse ;
descr . type = defNeuronCogDriverRankTCM ;
descr . window = HistoryBars * ( 2 * Quantiles + 1 );
descr . count = EmbeddingSize ;
descr . variables = BarDescr ;
{
uint units [] = { StackSize , NHeads };
if ( ArrayCopy ( descr . units , units , 0 , 0 , units . Size ()) < int ( units . Size ()))
DeleteObjAndFalse ( descr );
}
descr . activation = None ;
descr . optimization = ADAM ;
descr . batch = BatchSize ;
if ( ! market . Add ( descr ))
DeleteObjAndFalse ( descr );
//---
descr = new CLayerDescription ();
if ( ! descr )
ReturnFalse ;
descr . type = defNeuronOMPBOCL ;
descr . window = EmbeddingSize ;
descr . count = BarDescr ;
descr . layers = ACSRMSamples ;
{
uint units [] = { ACSRMReferenceSize , ACSRMCurrentWindow };
if ( ArrayCopy ( descr . units , units , 0 , 0 , units . Size ()) < int ( units . Size ()))
DeleteObjAndFalse ( descr );
}
descr . activation = None ;
descr . optimization = ADAM ;
descr . batch = BatchSize ;
if ( ! market . Add ( descr ))
DeleteObjAndFalse ( descr );
//---
descr = new CLayerDescription ();
if ( ! descr )
ReturnFalse ;
descr . type = defNeuronScenarioForecast ;
descr . count = NScenarios ;
descr . variables = TopK ;
descr . window_out = NForecast ;
descr . window = EmbeddingSize ;
descr . layers = BarDescr ;
descr . activation = None ;
descr . optimization = ADAM ;
descr . batch = BatchSize ;
if ( ! market . Add ( descr ))
DeleteObjAndFalse ( descr );
//---
if ( ! target )
target = new CArrayObj ();
else
target . Clear ();
if ( ! target )
ReturnFalse ;
target . FreeMode ( true );
//--- Detached Target Encoder: one normalized future trajectory per BarDescr
//--- variable. ConvOCL zero-fills the unavailable edge of each window, so
//--- window=3 preserves NForecast positions without an auxiliary pad buffer.
if ( ! SkillAddBase ( target , ( BarDescr * NForecast )))
ReturnFalse ;
descr = new CLayerDescription ();
if ( ! descr )
ReturnFalse ;
descr . type = defNeuronPeriodNorm ;
descr . count = 1 ;
descr . window = NForecast ;
descr . variables = BarDescr ;
descr . activation = None ;
descr . optimization = ADAM ;
descr . batch = BatchSize ;
if ( ! target . Add ( descr ))
DeleteObjAndFalse ( descr );
//---
descr = new CLayerDescription ();
if ( ! descr )
ReturnFalse ;
descr . type = defNeuronConvOCL ;
descr . count = NForecast ;
descr . window = 3 ;
descr . step = 1 ;
descr . window_out = EmbeddingSize ;
descr . layers = BarDescr ;
descr . activation = GELU ;
descr . optimization = ADAM ;
descr . batch = BatchSize ;
if ( ! target . Add ( descr ))
DeleteObjAndFalse ( descr );
//---
descr = new CLayerDescription ();
if ( ! descr )
ReturnFalse ;
descr . type = defNeuronConvOCL ;
descr . count = NForecast ;
descr . window = EmbeddingSize ;
descr . step = EmbeddingSize ;
descr . window_out = 2 * EmbeddingSize ;
descr . layers = BarDescr ;
descr . activation = GELU ;
descr . optimization = ADAM ;
descr . batch = BatchSize ;
if ( ! target . Add ( descr ))
DeleteObjAndFalse ( descr );
//---
descr = new CLayerDescription ();
if ( ! descr )
ReturnFalse ;
descr . type = defNeuronConvOCL ;
descr . count = NForecast ;
descr . window = 2 * EmbeddingSize ;
descr . step = 2 * EmbeddingSize ;
descr . window_out = EmbeddingSize ;
descr . layers = BarDescr ;
descr . activation = None ;
descr . optimization = ADAM ;
descr . batch = BatchSize ;
if ( ! target . Add ( descr ))
DeleteObjAndFalse ( descr );
//---
descr = new CLayerDescription ();
if ( ! descr )
ReturnFalse ;
descr . type = defNeuronPeriodNorm ;
descr . count = NForecast ;
descr . window = EmbeddingSize ;
descr . variables = BarDescr ;
descr . activation = None ;
descr . optimization = ADAM ;
descr . batch = BatchSize ;
if ( ! target . Add ( descr ))
DeleteObjAndFalse ( descr );
//---
return ( true );
}
//+------------------------------------------------------------------+
//| Implements SkillCreateNetworks. |
//+------------------------------------------------------------------+
bool SkillCreateNetworks ( void )
{
CArrayObj * market = NULL , * target = NULL ;
//--- Function if.
if ( ! SkillCreateDescriptions ( market , target ))
{
DeleteObj ( market );
DeleteObj ( target );
ReturnFalse ;
}
const bool market_created = SkillMarket . Create ( market );
PrintFormat ( " %s Create market=%s batch=%u " , ACSRM_LOG_PREFIX , ( market_created ? " OK " : " FAIL " ), uint ( BatchSize ));
const bool target_created = ( market_created && SkillTarget . Create ( target ));
PrintFormat ( " %s Create target=%s " , ACSRM_LOG_PREFIX , ( target_created ? " OK " : " FAIL " ));
const bool created = ( market_created && target_created );
DeleteObj ( market );
DeleteObj ( target );
if ( ! created )
ReturnFalse ;
//--- CNet::Create initializes its OpenCL program. Build each network first,
//--- then move its device buffers to the Market context; otherwise a later
//--- Create invalidates buffers belonging to an earlier network.
if ( ! SkillTarget . SetOpenCLChecked ( SkillMarket . GetOpenCL ()))
ReturnFalseEx ( " target OpenCL transfer failed " );
//--- Function if.
if ( ! SkillInitTrainingBuffers ())
{
PrintFormat ( " %s init: training buffers=FAIL " , ACSRM_LOG_PREFIX );
ReturnFalse ;
}
SkillForecast = ( CNeuronScenarioForecast * ) SkillMarket . Layer ( - 1 );
//--- Function if.
if ( ! SkillForecast || SkillForecast . Type () != defNeuronScenarioForecast )
{
PrintFormat ( " %s init: forecast layer=FAIL " , ACSRM_LOG_PREFIX );
ReturnFalse ;
}
if ( ! ConfigureForecastRecoveryAge ())
ReturnFalse ;
SkillTarget . TrainMode ( false );
SkillMarket . TrainMode ( true );
CNeuronOMPBOCL * ompb = GetACSRM ();
if ( ! ompb || ! ConfigureACSRM ( OMPB_BYPASS ))
ReturnFalse ;
ompb . TrainMode ( false );
return ( true );
}
//+------------------------------------------------------------------+
//| Implements SkillValidateShapes. |
//+------------------------------------------------------------------+
bool SkillValidateShapes ( const bool training = true )
{
CNeuronBaseOCL * layer = SkillMarket . Layer ( 0 );
CBufferFloat * buffer = ( layer ? layer . getOutput () : NULL );
//--- Function if.
if ( ! buffer || buffer . Total () != HistoryBars * BarDescr )
{
PrintFormat ( " %s shape: market input expected=%d actual=%d " , ACSRM_LOG_PREFIX , HistoryBars * BarDescr ,
( buffer ? buffer . Total () : - 1 ));
ReturnFalse ;
}
layer = SkillMarket . Layer ( 4 );
buffer = ( layer ? layer . getOutput () : NULL );
//--- Function if.
if ( ! buffer || buffer . Total () != ( BarDescr * EmbeddingSize ))
{
PrintFormat ( " %s shape: market RankTCM expected=%d actual=%d " , ACSRM_LOG_PREFIX , BarDescr * EmbeddingSize ,
( buffer ? buffer . Total () : - 1 ));
ReturnFalse ;
}
layer = GetACSRM ();
buffer = ( layer ? layer . getOutput () : NULL );
if ( ! buffer || buffer . Total () != ( BarDescr * EmbeddingSize ))
{
PrintFormat ( " ACSRM shape: bridge expected=%d actual=%d " , BarDescr * EmbeddingSize ,
( buffer ? buffer . Total () : - 1 ));
ReturnFalse ;
}
//--- Function if.
if ( training )
{
layer = SkillTarget . Layer ( 0 );
buffer = ( layer ? layer . getOutput () : NULL );
//--- Function if.
if ( ! buffer || buffer . Total () != ( BarDescr * NForecast ))
{
PrintFormat ( " %s shape: target input expected=%d actual=%d " , ACSRM_LOG_PREFIX , BarDescr * NForecast ,
( buffer ? buffer . Total () : - 1 ));
ReturnFalse ;
}
layer = SkillTarget . Layer ( 1 );
buffer = ( layer ? layer . getOutput () : NULL );
//--- Function if.
if ( ! buffer || buffer . Total () != ( BarDescr * NForecast ))
{
PrintFormat ( " %s shape: target PeriodNorm expected=%d actual=%d " , ACSRM_LOG_PREFIX , BarDescr * NForecast ,
( buffer ? buffer . Total () : - 1 ));
ReturnFalse ;
}
layer = SkillTarget . Layer ( 2 );
buffer = ( layer ? layer . getOutput () : NULL );
//--- Function if.
if ( ! buffer || buffer . Total () != ( BarDescr * NForecast * EmbeddingSize ))
{
PrintFormat ( " %s shape: target Conv3 expected=%d actual=%d " , ACSRM_LOG_PREFIX ,
BarDescr * NForecast * EmbeddingSize ,
( buffer ? buffer . Total () : - 1 ));
ReturnFalse ;
}
layer = SkillTarget . Layer ( 3 );
buffer = ( layer ? layer . getOutput () : NULL );
//--- Function if.
if ( ! buffer || buffer . Total () != ( BarDescr * NForecast * 2 * EmbeddingSize ))
{
PrintFormat ( " %s shape: target Conv2 expected=%d actual=%d " , ACSRM_LOG_PREFIX ,
BarDescr * NForecast * 2 * EmbeddingSize ,
( buffer ? buffer . Total () : - 1 ));
ReturnFalse ;
}
layer = SkillTarget . Layer ( 4 );
buffer = ( layer ? layer . getOutput () : NULL );
//--- Function if.
if ( ! buffer || buffer . Total () != ( BarDescr * NForecast * EmbeddingSize ))
{
PrintFormat ( " %s shape: target final Conv expected=%d actual=%d " , ACSRM_LOG_PREFIX ,
BarDescr * NForecast * EmbeddingSize ,
( buffer ? buffer . Total () : - 1 ));
ReturnFalse ;
}
layer = SkillTarget . Layer ( - 1 );
buffer = ( layer ? layer . getOutput () : NULL );
//--- Function if.
if ( ! buffer || buffer . Total () != ( BarDescr * NForecast * EmbeddingSize ))
{
PrintFormat ( " %s shape: target output PeriodNorm expected=%d actual=%d " , ACSRM_LOG_PREFIX ,
BarDescr * NForecast * EmbeddingSize ,
( buffer ? buffer . Total () : - 1 ));
ReturnFalse ;
}
buffer = SkillFutureTranspose . getOutput ();
//--- Function if.
if ( ! buffer || buffer . Total () != ( BarDescr * NForecast ))
{
PrintFormat ( " %s shape: future transpose expected=%d actual=%d " , ACSRM_LOG_PREFIX , BarDescr * NForecast ,
( buffer ? buffer . Total () : - 1 ));
ReturnFalse ;
}
}
layer = SkillMarket . Layer ( - 1 );
buffer = ( layer ? layer . getOutput () : NULL );
//--- Function if.
if ( ! buffer || buffer . Total () != NScenarios * BarDescr * NForecast * EmbeddingSize )
{
PrintFormat ( " %s shape: Scenario Z expected=%d actual=%d " , ACSRM_LOG_PREFIX ,
NScenarios * BarDescr * NForecast * EmbeddingSize ,
( buffer ? buffer . Total () : - 1 ));
ReturnFalse ;
}
buffer = SkillForecast . GetU ();
//--- Function if.
if ( ! buffer || buffer . Total () != ( NScenarios * BarDescr * NForecast ))
{
PrintFormat ( " %s shape: Scenario U expected=%d actual=%d " , ACSRM_LOG_PREFIX , NScenarios * BarDescr * NForecast ,
( buffer ? buffer . Total () : - 1 ));
ReturnFalse ;
}
buffer = SkillForecast . GetPi ();
//--- Function if.
if ( ! buffer || buffer . Total () != NScenarios )
{
PrintFormat ( " %s shape: Scenario Pi expected=%d actual=%d " , ACSRM_LOG_PREFIX , NScenarios ,
( buffer ? buffer . Total () : - 1 ));
ReturnFalse ;
}
if ( SkillForecast . Variables () != BarDescr || SkillForecast . Scenarios () != NScenarios ||
SkillForecast . Horizon () != NForecast || SkillForecast . Dimension () != EmbeddingSize ||
//--- Function ActiveTrajectories.
SkillForecast . ActiveTrajectories () > TopK )
{
PrintFormat ( " %s shape: Forecast V=%d/%d K=%d/%d H=%d/%d D=%d/%d active=%d/%d " , ACSRM_LOG_PREFIX ,
SkillForecast . Variables (), BarDescr , SkillForecast . Scenarios (), NScenarios ,
SkillForecast . Horizon (), NForecast , SkillForecast . Dimension (), EmbeddingSize ,
SkillForecast . ActiveTrajectories (), TopK );
ReturnFalse ;
}
return ( true );
}
//+------------------------------------------------------------------+
//| Implements SkillHashUInt. |
//+------------------------------------------------------------------+
ulong SkillHashUInt ( ulong hash , const ulong value )
{
hash ^= value ;
return ( hash * ulong ( 1099511628211 ));
}
//+------------------------------------------------------------------+
//| Implements SkillHashText. |
//+------------------------------------------------------------------+
ulong SkillHashText ( ulong hash , const string text )
{
for ( int i = 0 ; i < StringLen ( text ); i ++ )
hash = SkillHashUInt ( hash , ( ulong ) StringGetCharacter ( text , i ));
return ( hash );
}
//+------------------------------------------------------------------+
//| Implements SkillHashFile. |
//+------------------------------------------------------------------+
ulong SkillHashFile ( ulong hash , const string file_name )
{
int handle = FileOpen ( file_name , FILE_READ | FILE_BIN | FILE_COMMON | FILE_SHARE_READ );
if ( handle == INVALID_HANDLE )
return ( 0 );
const ulong length = FileSize ( handle );
if ( length == 0 || length > ulong ( INT_MAX ))
{ FileClose ( handle ); return 0 ; }
uchar bytes [];
if ( ArrayResize ( bytes , ( int ) length ) != ( int ) length ||
//--- Read file payload as bytes after successful allocation.
FileReadArray ( handle , bytes , 0 , ( int ) length ) != ( int ) length )
{ FileClose ( handle ); return 0 ; }
FileClose ( handle );
for ( int i = 0 ; i < ( int ) length ; i ++ )
hash = SkillHashUInt ( hash , ( ulong ) bytes [ i ]);
return ( hash );
}
//+-------------------------------------------------------------------+
//| Exact signature of the unified Market/Scenario inference grap... |
//+-------------------------------------------------------------------+
ulong SkillForecastSignature ( const string market_file = " " )
{
if ( ! SkillForecast )
return ( 0 );
ulong hash = ulong ( 1469598103934665603 );
const string checkpoint = ( market_file == " " ? SkillActiveMarketFile : market_file );
hash = SkillHashFile ( hash , checkpoint );
if ( hash == 0 )
return ( 0 );
hash = SkillHashUInt ( hash , Skill_FORMAT_VERSION );
hash = SkillHashUInt ( hash , BarDescr );
hash = SkillHashUInt ( hash , NScenarios );
hash = SkillHashUInt ( hash , TopK );
hash = SkillHashUInt ( hash , NForecast );
hash = SkillHashUInt ( hash , EmbeddingSize );
hash = SkillHashUInt ( hash , SkillForecast . ContractSignature ());
hash = SkillHashText ( hash , " z_layout=K,V,H,D;u_layout=K,V,H;pi_layout=K;codebook_layout=K,V,H,D " );
hash = SkillHashText ( hash ,
" market_layout=RankTCM_then_ACSRM_then_ScenarioForecast; " +
" variable_order=BarDescr_feature_series_0_to_8 " );
hash = SkillHashText ( hash , " OHLC_deltas_from_open;tick_volume_div_1000;RSI_CCI_ATR_MACD_raw " );
return ( hash );
}
//+------------------------------------------------------------------+
//| Implements SkillForecastTrainingSignature. |
//+------------------------------------------------------------------+
ulong SkillForecastTrainingSignature ( const ulong forecast_signature , const string target_file = " " )
{
if ( forecast_signature == 0 )
return ( 0 );
const string checkpoint = ( target_file == " " ? SkillActiveTargetFile : target_file );
return ( SkillHashFile ( forecast_signature , checkpoint ));
}
//+------------------------------------------------------------------+
//| Implements SkillWriteManifest. |
//+------------------------------------------------------------------+
bool SkillWriteManifest ( const uint completed_epochs )
{
if ( ! SkillForecast )
ReturnFalse ;
const ulong signature = SkillForecastSignature ( Skill_MARKET_FILE );
const ulong target_hash = SkillHashFile ( ulong ( 1469598103934665603 ), Skill_TARGET_FILE );
const ulong training_signature = SkillForecastTrainingSignature ( signature , Skill_TARGET_FILE );
if ( signature == 0 || target_hash == 0 || training_signature == 0 )
ReturnFalse ;
int handle = FileOpen ( Skill_MANIFEST_FILE , FILE_WRITE | FILE_TXT | FILE_ANSI | FILE_COMMON );
if ( handle == INVALID_HANDLE )
ReturnFalse ;
FileWrite ( handle , " format=ACSRM_FORECAST " );
FileWrite ( handle , StringFormat ( " version=%u " , Skill_FORMAT_VERSION ));
FileWrite ( handle , StringFormat ( " forecast_type=%d " , defNeuronScenarioForecast ));
FileWrite ( handle , StringFormat ( " variables=%u " , BarDescr ));
FileWrite ( handle , StringFormat ( " scenarios=%u " , NScenarios ));
FileWrite ( handle , StringFormat ( " top_k=%u " , TopK ));
FileWrite ( handle , StringFormat ( " horizon=%u " , NForecast ));
FileWrite ( handle , StringFormat ( " latent=%u " , EmbeddingSize ));
FileWrite ( handle , " z_layout=K,V,H,D " );
FileWrite ( handle , " u_layout=K,V,H " );
FileWrite ( handle , " pi_layout=K " );
FileWrite ( handle , " codebook_layout=K,V,H,D " );
FileWrite ( handle , " variable_order=BarDescr_feature_series_0_to_8 " );
FileWrite ( handle , StringFormat ( " contract_signature=%I64u " , SkillForecast . ContractSignature ()));
FileWrite ( handle , StringFormat ( " forecast_signature=%I64u " , signature ));
FileWrite ( handle , StringFormat ( " target_hash=%I64u " , target_hash ));
FileWrite ( handle , StringFormat ( " training_signature=%I64u " , training_signature ));
FileWrite ( handle , StringFormat ( " completed_epochs=%u " , completed_epochs ));
FileWrite ( handle , StringFormat ( " training_batches=%I64u " , SkillBatches ));
FileWrite ( handle , StringFormat ( " invalid_batches=%I64u " , SkillInvalidBatches ));
FileWrite ( handle , " normalization=OHLC_deltas_from_open;tick_volume_div_1000;RSI_CCI_ATR_MACD_raw " );
FileClose ( handle );
SkillLastSignature = signature ;
return ( true );
}
bool SkillStage01SaveCheckpoint ( const uint completed_epochs );
//+------------------------------------------------------------------+
//| Implements SkillSaveCheckpoint. |
//+------------------------------------------------------------------+
bool SkillSaveCheckpoint ( const uint completed_epochs )
{
return ( SkillStage01SaveCheckpoint ( completed_epochs ));
}
//+------------------------------------------------------------------+
//| Implements SkillInitIndicators. |
//+------------------------------------------------------------------+
bool SkillInitIndicators ( void )
{
return ( Symb . Name ( _Symbol ) && Symb . Refresh () &&
RSI . Create ( Symb . Name (), TimeFrame , RSIPeriod , RSIPrice ) &&
CCI . Create ( Symb . Name (), TimeFrame , CCIPeriod , CCIPrice ) &&
ATR . Create ( Symb . Name (), TimeFrame , ATRPeriod ) &&
MACD . Create ( Symb . Name (), TimeFrame , FastPeriod , SlowPeriod , SignalPeriod , MACDPrice ));
}
//+------------------------------------------------------------------+
//| Loads forecast training data into the Skill caches. |
//+------------------------------------------------------------------+
bool SkillLoadForecastTraining ( void );
//+------------------------------------------------------------------+
//| Captures a frozen forecast baseline for Stage 02 proofs. |
//+------------------------------------------------------------------+
bool SkillCaptureFrozenForecastBaseline ( CNeuronScenarioForecast * forecast );
//+------------------------------------------------------------------+
//| Verifies the frozen forecast baseline is bit-exact. |
//+------------------------------------------------------------------+
bool SkillVerifyFrozenForecastExact ( CNeuronScenarioForecast * forecast );
//+------------------------------------------------------------------+
//| Forwards the Skill forecast chain by one step. |
//+------------------------------------------------------------------+
bool SkillForwardForecast ( const int position , CBufferFloat * state , CBufferFloat * time ,
const int shift_bars = 1 );
//+------------------------------------------------------------------+
//| Reads one key value from a manifest file. |
//+------------------------------------------------------------------+
string SkillManifestValue ( const string file_name , const string key );
//+------------------------------------------------------------------+
//| Checks the explicit Stage 02 checkpoint file triplet. |
//+------------------------------------------------------------------+
bool ACSRMStage02ExplicitCheckpointFilesValid ( const string market_file , const string target_file ,
const string manifest_file );
//+------------------------------------------------------------------+
//| Loads the explicit Stage 02 checkpoint file triplet. |
//+------------------------------------------------------------------+
bool ACSRMStage02LoadExplicitCheckpoint ( const string market_file , const string target_file ,
const string manifest_file );
//+--------------------------------------------------------------------------+
//| Reloads an explicit Stage 02 checkpoint and proves deterministic mode. |
//+--------------------------------------------------------------------------+
bool ACSRMStage02ReloadProofDeterministic ( const string market_file , const string target_file ,
const string manifest_file , const int position ,
CBufferFloat * state , CBufferFloat * time );
//+-----------------------------------------------------------------------+
//| Reloads the selected checkpoint and proves deterministic inference. |
//+-----------------------------------------------------------------------+
bool ACSRMStage02ReloadCheckpointProof ( const string market_file , const string target_file ,
const string manifest_file , const ulong expected_market ,
const ulong expected_target , const ulong expected_posterior );
//+------------------------------------------------------------------+
//| Finalizes the steady selector after Stage 02 proofs pass. |
//+------------------------------------------------------------------+
bool ACSRMStage02FinalizeSteadySelector ( void );
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Stage 02 preflight and Reference collection only. This path is intentionally separate from CreateSkillForecastStudy(): Stage 02 must reject a missing/incompatible Stage 01 checkpoint rather than creating a new random Market graph. |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
datetime ACSRMStage02ReferenceStart = 0 ;
datetime ACSRMStage02ReferenceSplit = 0 ;
datetime ACSRMStage02ReferenceEnd = 0 ;
datetime ACSRMStage02CalibrationStart = 0 ;
datetime ACSRMStage02CalibrationSplit = 0 ;
datetime ACSRMStage02CalibrationEnd = 0 ;
int ACSRMStage02ReferenceFirst = - 1 ;
int ACSRMStage02ReferenceLast = - 1 ;
int ACSRMStage02SourceEvalFirst = - 1 ;
int ACSRMStage02SourceEvalLast = - 1 ;
int ACSRMStage02TargetCalibrationFirst = - 1 ;
int ACSRMStage02TargetCalibrationLast = - 1 ;
int ACSRMStage02TargetEvalFirst = - 1 ;
int ACSRMStage02TargetEvalLast = - 1 ;
bool ACSRMStage02Smoke = false ;
uint ACSRMStage02SmokeLimit = 0 ;
uint ACSRMStage02SourcePeriod = 0 ;
uint ExtACSRMCalibrationEpochs = 1 ;
uint ExtACSRMCalibrationEpoch = 0 ;
ulong ACSRMStage02BaseFingerprint = 0 ;
ulong ACSRMStage02ForecastFingerprint = 0 ;
ulong ACSRMStage02TargetFingerprint = 0 ;
ulong ACSRMStage02PosteriorFingerprint = 0 ;
bool ACSRMStage02SignaturesCaptured = false ;
bool ACSRMStage02StopReported = false ;
//--- The Stage 01 state remains the only production checkpoint until an
//--- accepted Stage 02 transaction has completed its reload smoke.
bool ACSRMStage02CheckpointPublished = false ;
//+------------------------------------------------------------------------+
//| Checks that every finite value survives the Stage 02 state encoding. |
//+------------------------------------------------------------------------+
bool ACSRMStage02Finite ( const double value )
{
return ( MathIsValidNumber ( value ));
}
//+------------------------------------------------------------------+
//| Returns whether Stage 02 must terminate after a manual stop. |
//+------------------------------------------------------------------+
bool ACSRMStage02StopHandled ( const string phase = " " )
{
if ( ! IsStopped ())
return ( false );
//--- Leave both graphs outside training mode without finalizing or saving.
SkillMarket . TrainMode ( false );
SkillTarget . TrainMode ( false );
if ( phase != " " && ! ACSRMStage02StopReported )
{
PrintFormat ( " ACSRM_STAGE02_STOP_REQUESTED phase=%s " , phase );
ACSRMStage02StopReported = true ;
}
return ( true );
}
//+------------------------------------------------------------------+
//| Configures Stage 02 runtime constants and mode selectors. |
//+------------------------------------------------------------------+
bool ACSRMStage02Configure ( const datetime reference_start , const datetime reference_end ,
const datetime calibration_start , const datetime calibration_end ,
const uint anchor_period , const float tau_value , const float lambda_dis ,
const float lambda_kl , const float lambda_alpha , const float alpha_prior ,
const float max_kl , const bool smoke , const uint smoke_limit )
{
if ( reference_start >= reference_end || reference_end > calibration_start ||
calibration_start >= calibration_end || tau_value < 0.0f || tau_value > 1.0f ||
lambda_dis < 0.0f || lambda_kl < 0.0f || lambda_alpha < 0.0f ||
max_kl < 0.0f ||
! ACSRMStage02Finite ( tau_value ) || ! ACSRMStage02Finite ( lambda_dis ) ||
! ACSRMStage02Finite ( lambda_kl ) || ! ACSRMStage02Finite ( lambda_alpha ) ||
! ACSRMStage02Finite ( alpha_prior ) || ! ACSRMStage02Finite ( max_kl ))
{
Print ( " ACSRM_STAGE02_PREFLIGHT_FAIL reason=input_contract " );
ReturnFalse ;
}
const long reference_span = long ( reference_end ) - long ( reference_start );
const long calibration_span = long ( calibration_end ) - long ( calibration_start );
const datetime reference_split = reference_start + int ( reference_span * 4 / 5 );
const datetime calibration_split = calibration_start + int ( calibration_span * 4 / 5 );
if ( reference_span <= 0 || calibration_span <= 0 || reference_split <= reference_start ||
reference_split >= reference_end || calibration_split <= calibration_start ||
calibration_split >= calibration_end )
{
Print ( " ACSRM_STAGE02_PREFLIGHT_FAIL reason=empty_80_20_partition " );
ReturnFalse ;
}
CNeuronOMPBOCL * ompb = GetACSRM ();
if ( ! ompb || ! ompb . SetTau ( tau_value ) || ! ompb . SetDisagreementMult ( lambda_dis ) ||
! ompb . SetKLDMult ( lambda_kl ) || ! ompb . SetAlphaMult ( lambda_alpha ) ||
! ompb . SetAlphaPrior ( alpha_prior ) || ! ompb . SetMaxKL ( max_kl ) ||
! ConfigureACSRM ( OMPB_BYPASS ))
{
Print ( " ACSRM_STAGE02_PREFLIGHT_FAIL reason=ompb_configuration " );
ReturnFalse ;
}
ompb . TrainMode ( false );
ACSRMStage02ReferenceStart = reference_start ;
ACSRMStage02ReferenceSplit = reference_split ;
ACSRMStage02ReferenceEnd = reference_end ;
ACSRMStage02CalibrationStart = calibration_start ;
ACSRMStage02CalibrationSplit = calibration_split ;
ACSRMStage02CalibrationEnd = calibration_end ;
ACSRMStage02Smoke = smoke ;
ACSRMStage02SmokeLimit = ( smoke ? MathMax ( 1 , int ( smoke_limit )) : 0 );
ACSRMStage02SourcePeriod = anchor_period ;
PrintFormat ( " ACSRM_STAGE02_PREFLIGHT_CONFIG reference=%s..%s split=%s calibration=%s..%s " ,
TimeToString ( reference_start , TIME_DATE ), TimeToString ( reference_end , TIME_DATE ),
TimeToString ( reference_split , TIME_DATE ), TimeToString ( calibration_start , TIME_DATE ),
TimeToString ( calibration_end , TIME_DATE ));
PrintFormat ( " ACSRM_STAGE02_PREFLIGHT_CONFIG split=%s source_period=%u smoke=%s " ,
TimeToString ( calibration_split , TIME_DATE ), anchor_period , ( smoke ? " true " : " false " ));
PrintFormat ( " ACSRM_STAGE02_OBJECTIVE tau=%.8g lambda_dis=%.8g lambda_kl=%.8g " +
" lambda_alpha=%.8g alpha_prior=%.8g max_kl=%.8g " ,
tau_value , lambda_dis , lambda_kl , lambda_alpha , alpha_prior , max_kl );
return ( true );
}
//+------------------------------------------------------------------+
//| Prepares Stage 02 input data and state descriptors. |
//+------------------------------------------------------------------+
bool ACSRMStage02PrepareData ( void )
{
const int reference_start = iBarShift ( Symb . Name (), TimeFrame , ACSRMStage02ReferenceStart );
const int reference_split = iBarShift ( Symb . Name (), TimeFrame , ACSRMStage02ReferenceSplit );
const int reference_end = iBarShift ( Symb . Name (), TimeFrame , ACSRMStage02ReferenceEnd );
const int calibration_start = iBarShift ( Symb . Name (), TimeFrame , ACSRMStage02CalibrationStart );
const int calibration_split = iBarShift ( Symb . Name (), TimeFrame , ACSRMStage02CalibrationSplit );
const int calibration_end = iBarShift ( Symb . Name (), TimeFrame , ACSRMStage02CalibrationEnd );
if ( reference_start <= 0 || reference_split <= 0 || reference_end <= 0 ||
calibration_start <= 0 || calibration_split <= 0 || calibration_end <= 0 ||
reference_start <= reference_split || reference_split <= reference_end ||
reference_end < calibration_start || calibration_start <= calibration_split ||
calibration_split <= calibration_end )
{
PrintFormat ( " ACSRM_STAGE02_PREFLIGHT_FAIL reason=bar_order shifts=(%d,%d,%d,%d,%d,%d) " ,
reference_start , reference_split , reference_end , calibration_start ,
calibration_split , calibration_end );
ReturnFalse ;
}
const int bars = CopyRates ( Symb . Name (), TimeFrame , 0 , reference_start , Rates );
if ( bars <= 0 || ! RSI . BufferResize ( bars ) || ! CCI . BufferResize ( bars ) ||
! ATR . BufferResize ( bars ) || ! MACD . BufferResize ( bars ))
{
PrintFormat ( " ACSRM_STAGE02_PREFLIGHT_FAIL reason=rates bars=%d error=%d " , bars , GetLastError ());
ReturnFalse ;
}
int wait = - 1 ;
bool calculated = false ;
do
{
calculated = ( RSI . BarsCalculated () >= bars && CCI . BarsCalculated () >= bars &&
ATR . BarsCalculated () >= bars && MACD . BarsCalculated () >= bars );
Sleep ( 100 );
wait ++ ;
}
while ( ! calculated && wait < 100 );
if ( ! calculated )
{
PrintFormat ( " ACSRM_STAGE02_PREFLIGHT_FAIL reason=indicators bars=%d error=%d " , bars , GetLastError ());
ReturnFalse ;
}
RSI . Refresh ();
CCI . Refresh ();
ATR . Refresh ();
MACD . Refresh ();
if ( ! ArraySetAsSeries ( Rates , true ))
{
PrintFormat ( " ACSRM_STAGE02_PREFLIGHT_FAIL reason=rates_series error=%d " , GetLastError ());
ReturnFalse ;
}
ACSRMStage02ReferenceFirst = reference_start - HistoryBars - NForecast - 1 ;
ACSRMStage02ReferenceLast = reference_split ;
ACSRMStage02SourceEvalFirst = reference_split - 1 ;
ACSRMStage02SourceEvalLast = reference_end ;
ACSRMStage02TargetCalibrationFirst = calibration_start - HistoryBars - NForecast - 1 ;
ACSRMStage02TargetCalibrationLast = calibration_split ;
ACSRMStage02TargetEvalFirst = calibration_split - 1 ;
ACSRMStage02TargetEvalLast = calibration_end ;
const int reference_rows = ACSRMStage02ReferenceFirst - ACSRMStage02ReferenceLast + 1 ;
const int source_eval_rows = ACSRMStage02SourceEvalFirst - ACSRMStage02SourceEvalLast + 1 ;
const int target_calibration_rows = ACSRMStage02TargetCalibrationFirst - ACSRMStage02TargetCalibrationLast + 1 ;
const int target_eval_rows = ACSRMStage02TargetEvalFirst - ACSRMStage02TargetEvalLast + 1 ;
if ( ACSRMStage02ReferenceFirst < ACSRMStage02ReferenceLast ||
ACSRMStage02SourceEvalFirst < ACSRMStage02SourceEvalLast ||
ACSRMStage02TargetCalibrationFirst < ACSRMStage02TargetCalibrationLast ||
ACSRMStage02TargetEvalFirst < ACSRMStage02TargetEvalLast ||
reference_rows < int ( ACSRMReferenceSize ) || source_eval_rows <= 0 ||
target_calibration_rows < int ( ACSRMCurrentWindow ) || target_eval_rows <= 0 )
{
PrintFormat ( " ACSRM_STAGE02_PREFLIGHT_FAIL reason=bars ref=%d source=%d calibration=%d target=%d " ,
reference_rows , source_eval_rows , target_calibration_rows , target_eval_rows );
PrintFormat ( " ACSRM_STAGE02_PREFLIGHT_REQUIRED reference=%u target_calibration=%u " ,
ACSRMReferenceSize , ACSRMCurrentWindow );
ReturnFalse ;
}
PrintFormat ( " ACSRM_STAGE02_PREFLIGHT_PASS bars=%d reference=%d source_eval=%d target_calibration=%d target_eval=%d " ,
bars , reference_rows , source_eval_rows , target_calibration_rows , target_eval_rows );
return ( true );
}
//+------------------------------------------------------------------+
//| Returns the valid-row quota for one Stage 02 progress phase. |
//+------------------------------------------------------------------+
uint ACSRMStage02ProgressQuota ( const uint rows , const uint cap , const bool smoke ,
const uint smoke_limit )
{
uint quota = ( cap == 0 ? rows : ( uint ) MathMin ( rows , cap ));
if ( smoke )
quota = ( uint ) MathMin ( quota , smoke_limit );
return ( quota );
}
//+------------------------------------------------------------------+
//| Converts valid progress into a bounded Stage 02 percentage. |
//+------------------------------------------------------------------+
double ACSRMStage02ProgressPercent ( const uint done , const uint quota )
{
if ( quota == 0 )
return ( 0.0 );
return ( 100.0 * double ( MathMin ( done , quota )) / double ( quota ));
}
//+------------------------------------------------------------------+
//| Shows a throttled Stage 02 chart status without training work. |
//+------------------------------------------------------------------+
void ACSRMStage02ShowProgress ( const string phase , const uint done , const uint total ,
const uint attempts , const uint invalid , const bool end ,
const bool successful , const bool show_epoch ,
const bool show_ompb , const uint source_done ,
const uint source_attempts , const uint source_invalid ,
const string detail )
{
static ulong last_tick = 0 ;
static string last_phase = " " ;
const ulong now = GetTickCount64 ();
const bool phase_changed = ( phase != last_phase );
const bool timer_elapsed = ( ! phase_changed && last_tick > 0 && now - last_tick >= 1000 );
const bool read_metrics = ( ! phase_changed && ( timer_elapsed || end ));
if ( ! phase_changed && ! end && ! timer_elapsed )
return ;
string lmix_text = " n/a " ;
string valid_text = " n/a " ;
string diagnostic_invalid_text = " n/a " ;
string disagreement_text = " n/a " ;
string kl_text = " n/a " ;
//--- A phase transition deliberately hides the preceding phase's epoch metrics.
if ( read_metrics && show_epoch )
{
double lmix , router , trajectory , confidence , latent , observation ;
double valid , diagnostic_invalid , entropy , distance , inactive , recovered ;
if ( SkillForecast && SkillForecast . ReadEpochDiagnostics (
lmix , router , trajectory , confidence , latent , observation ,
valid , diagnostic_invalid , entropy , distance , inactive , recovered ) &&
valid > 0.0 && ACSRMStage02Finite ( lmix ) && ACSRMStage02Finite ( valid ) &&
ACSRMStage02Finite ( diagnostic_invalid ))
{
lmix_text = StringFormat ( " %.8f " , lmix / valid );
valid_text = StringFormat ( " %.0f " , valid );
diagnostic_invalid_text = StringFormat ( " %.0f " , diagnostic_invalid );
}
if ( show_ompb )
{
uint reference_count , current_count , invalid_fallbacks , kl_rejects ;
float disagreement , kl , alpha_prior ;
if ( ReadACSRMDiagnostics ( reference_count , current_count , disagreement , kl , alpha_prior ,
invalid_fallbacks , kl_rejects ) && ACSRMStage02Finite ( disagreement ) &&
ACSRMStage02Finite ( kl ))
{
disagreement_text = StringFormat ( " %.8f " , disagreement );
kl_text = StringFormat ( " %.8f " , kl );
}
}
}
const double percent = ACSRMStage02ProgressPercent ( done , total );
string state = " running " ;
if ( end )
{
state = ( successful && total > 0 && done >= total ? " complete " : " ended " );
if ( IsStopped ())
state = " stopped " ;
}
const string phase_label = ( phase == " calibration " ?
StringFormat ( " %s epoch %u/%u " , phase ,
ExtACSRMCalibrationEpoch , ExtACSRMCalibrationEpochs ) : phase );
Comment ( StringFormat ( " %s Stage02 %s %6.2f%% state=%s \n " +
" processed %u/%u attempts %u invalid %u L_mix %s valid %s invalid %s \n " +
" KL %s disagreement %s source_updates %u source_attempts %u source_invalid %u \n %s " ,
ACSRM_LOG_PREFIX , phase_label , percent , state , done , total , attempts , invalid ,
lmix_text , valid_text , diagnostic_invalid_text , kl_text , disagreement_text ,
source_done , source_attempts , source_invalid , detail ));
if ( end && IsStopped ())
PrintFormat ( " ACSRM_STAGE02_PROGRESS_STOP phase=%s valid=%u total=%u attempts=%u invalid=%u " ,
phase , done , total , attempts , invalid );
last_phase = phase ;
last_tick = now ;
}
//+------------------------------------------------------------------+
//| Collects the immutable Stage 02 Reference history. |
//+------------------------------------------------------------------+
bool ACSRMStage02CollectReference ( void )
{
CNeuronOMPBOCL * ompb = GetACSRM ();
if ( ! ompb || ! ResetACSRMReference () || ! ConfigureACSRM ( OMPB_REFERENCE ))
{
Print ( " ACSRM_STAGE02_REFERENCE_FAIL reason=setup " );
ReturnFalse ;
}
ompb . TrainMode ( false );
uint collected = 0 ;
const uint rows = uint ( ACSRMStage02ReferenceFirst - ACSRMStage02ReferenceLast + 1 );
const uint quota = ACSRMStage02ProgressQuota ( rows , ACSRMReferenceSize , ACSRMStage02Smoke ,
ACSRMStage02SmokeLimit );
ACSRMStage02ShowProgress ( " reference " , collected , quota , collected , 0 , false , false , false ,
false ,
0 , 0 , 0 , " " );
for ( int position = ACSRMStage02ReferenceFirst ;
position >= ACSRMStage02ReferenceLast && ! IsStopped (); position -- )
{
if ( ! CreateBuffers ( position + NForecast , GetPointer ( SkillState ),
GetPointer ( SkillTime ), NULL ) ||
! SkillMarket . feedForward ( GetPointer ( SkillState ), 1 , false , ( CBufferFloat * ) NULL ))
{
PrintFormat ( " ACSRM_STAGE02_REFERENCE_FAIL reason=forward position=%d line=%d " , position , __LINE__ );
ReturnFalse ;
}
collected ++ ;
ACSRMStage02ShowProgress ( " reference " , collected , quota , collected , 0 , false , false , false ,
false ,
0 , 0 , 0 , " " );
if ( ACSRMStage02Smoke && collected >= ACSRMStage02SmokeLimit )
break ;
if ( ompb . ReferenceCount () >= ACSRMReferenceSize )
break ;
}
//--- A partial Reference is never a successful collection.
if ( IsStopped ())
{
Print ( " ACSRM_STAGE02_STOP_REQUESTED phase=reference " );
ACSRMStage02StopReported = true ;
return ( false );
}
if ( collected == 0 || ( ! ACSRMStage02Smoke && ompb . ReferenceCount () != ACSRMReferenceSize ))
{
PrintFormat ( " ACSRM_STAGE02_REFERENCE_FAIL reason=count collected=%u reference=%u required=%u " ,
collected , ompb . ReferenceCount (), ACSRMReferenceSize );
ACSRMStage02ShowProgress ( " reference " , collected , quota , collected , 0 , true , false , false ,
false ,
0 , 0 , 0 , " result=failed " );
ReturnFalse ;
}
ACSRMStage02ShowProgress ( " reference " , collected , quota , collected , 0 , true , true , false ,
false ,
0 , 0 , 0 , " " );
PrintFormat ( " ACSRM_STAGE02_REFERENCE_PASS collected=%u reference=%u capacity=%u smoke=%s " ,
collected , ompb . ReferenceCount (), ACSRMReferenceSize , ( ACSRMStage02Smoke ? " true " : " false " ));
return ( ConfigureACSRM ( OMPB_BYPASS ));
}
//+------------------------------------------------------------------+
//| Computes the frozen BYPASS baseline for one evaluation scope. |
//+------------------------------------------------------------------+
bool ACSRMStage02Baseline ( const int first , const int last , const string scope ,
double & forecast_loss , uint & valid_batches , uint & invalid_batches )
{
forecast_loss = 0.0 ;
valid_batches = 0 ;
invalid_batches = 0 ;
uint attempts = 0 ;
const uint rows = uint ( first - last + 1 );
const uint quota = ACSRMStage02ProgressQuota ( rows , 0 , ACSRMStage02Smoke ,
ACSRMStage02SmokeLimit );
const string progress_phase = ( StringFind ( scope , " source " ) >= 0 ?
" baseline source " : " baseline target " );
if ( first < last || ! SkillForecast || ! ConfigureACSRM ( OMPB_BYPASS ) ||
! SkillMarket . Clear () || ! SkillTarget . Clear () ||
! SkillForecast . ResetEpochDiagnostics ())
{
PrintFormat ( " ACSRM_STAGE02_BASELINE_FAIL scope=%s reason=setup " , scope );
ReturnFalse ;
}
ACSRMStage02ShowProgress ( progress_phase , ( ACSRMStage02Smoke ? valid_batches : attempts ),
quota , attempts , invalid_batches , false , false , true , false ,
0 , 0 , 0 , " " );
for ( int position = first ; position >= last && ! IsStopped (); position -- )
{
attempts ++ ;
if ( ! SkillTrainBatch ( position ))
{
invalid_batches ++ ;
ACSRMStage02ShowProgress ( progress_phase , ( ACSRMStage02Smoke ? valid_batches : attempts ),
quota , attempts , invalid_batches , false , false , true , false ,
0 , 0 , 0 , " " );
continue ;
}
valid_batches ++ ;
ACSRMStage02ShowProgress ( progress_phase , ( ACSRMStage02Smoke ? valid_batches : attempts ),
quota , attempts , invalid_batches , false , false , true , false ,
0 , 0 , 0 , " " );
if ( ACSRMStage02Smoke && valid_batches >= ACSRMStage02SmokeLimit )
break ;
}
//--- A stopped partial baseline cannot supply acceptance metrics.
if ( IsStopped ())
{
if ( scope == " source_eval " )
Print ( " ACSRM_STAGE02_STOP_REQUESTED phase=baseline_source " );
else
Print ( " ACSRM_STAGE02_STOP_REQUESTED phase=baseline_target " );
ACSRMStage02StopReported = true ;
return ( false );
}
double lmix , router , trajectory , confidence , latent , observation ;
double valid , invalid , entropy , distance , inactive , recovered ;
if ( valid_batches == 0 || ! SkillForecast . BuildEpochCodebookDiagnostics () ||
! SkillForecast . ReadEpochDiagnostics (
lmix , router , trajectory , confidence , latent , observation ,
valid , invalid , entropy , distance , inactive , recovered ) ||
valid < double ( valid_batches ) || ! ACSRMStage02Finite ( lmix ) ||
! ACSRMStage02Finite ( valid ) || ! ACSRMStage02Finite ( invalid ))
{
PrintFormat ( " ACSRM_STAGE02_BASELINE_FAIL scope=%s reason=metrics valid=%u invalid=%u " ,
scope , valid_batches , invalid_batches );
ACSRMStage02ShowProgress ( progress_phase , ( ACSRMStage02Smoke ? valid_batches : attempts ),
quota , attempts , invalid_batches , true , false , true , false ,
0 , 0 , 0 , " result=failed " );
ReturnFalse ;
}
forecast_loss = lmix / valid ;
if ( ! ACSRMStage02Finite ( forecast_loss ))
{
PrintFormat ( " ACSRM_STAGE02_BASELINE_FAIL scope=%s reason=nonfinite_loss " , scope );
ACSRMStage02ShowProgress ( progress_phase , ( ACSRMStage02Smoke ? valid_batches : attempts ),
quota , attempts , invalid_batches , true , false , true , false ,
0 , 0 , 0 , " result=failed " );
ReturnFalse ;
}
ACSRMStage02ShowProgress ( progress_phase , ( ACSRMStage02Smoke ? valid_batches : attempts ),
quota , attempts , invalid_batches , true , true , true , false ,
0 , 0 , 0 , " " );
PrintFormat ( " ACSRM_STAGE02_BASELINE_PASS scope=%s loss=%.9f valid=%u invalid=%u " ,
scope , forecast_loss , valid_batches , invalid_batches );
return ( true );
}
//+------------------------------------------------------------------+
//| Fingerprints the selected market layers for Stage 02. |
//+------------------------------------------------------------------+
bool ACSRMStage02MarketFingerprint ( const int first , const int last , ulong & fingerprint )
{
if ( first < 0 || first > last )
ReturnFalse ;
fingerprint = ulong ( 1469598103934665603 );
for ( int index = first ; index <= last ; index ++ )
{
CNeuronBaseOCL * layer = SkillMarket . Layer ( index );
if ( ! layer )
ReturnFalse ;
fingerprint = ( fingerprint ^ ulong ( index + 1 )) * ulong ( 1099511628211 );
if ( ! layer . AppendParameterFingerprint ( fingerprint ))
ReturnFalse ;
}
return ( fingerprint != 0 );
}
//+------------------------------------------------------------------+
//| Captures the states that must remain frozen during ACSRM. |
//+------------------------------------------------------------------+
bool ACSRMStage02CaptureSignatures ( void )
{
ACSRMStage02SignaturesCaptured = false ;
CNeuronOMPBOCL * ompb = GetACSRM ();
if ( ! ompb || ! ACSRMStage02MarketFingerprint ( 0 , 4 , ACSRMStage02BaseFingerprint ) ||
! ACSRMStage02MarketFingerprint ( 6 , 6 , ACSRMStage02ForecastFingerprint ) ||
! SkillTarget . ParameterFingerprint ( ACSRMStage02TargetFingerprint ))
ReturnFalse ;
ACSRMStage02PosteriorFingerprint = ulong ( 1469598103934665603 );
if ( ! ompb . AppendParameterFingerprint ( ACSRMStage02PosteriorFingerprint ) ||
ACSRMStage02PosteriorFingerprint == 0 )
ReturnFalse ;
PrintFormat ( " ACSRM_STAGE02_SIGNATURES_BEFORE base=%I64u forecast=%I64u target=%I64u posterior=%I64u " ,
ACSRMStage02BaseFingerprint , ACSRMStage02ForecastFingerprint ,
ACSRMStage02TargetFingerprint , ACSRMStage02PosteriorFingerprint );
ACSRMStage02SignaturesCaptured = true ;
return ( true );
}
//+-------------------------------------------------------------------+
//| Ensures Stage 02 changed only the posterior/alpha parameter set. |
//+-------------------------------------------------------------------+
bool ACSRMStage02VerifySignatures ( void )
{
if ( ! ACSRMStage02SignaturesCaptured )
{
Print ( " ACSRM_STAGE02_SIGNATURES_FAIL reason=not_captured " );
return ( false );
}
ulong base = 0 , forecast = 0 , target = 0 ;
ulong posterior = ulong ( 1469598103934665603 );
CNeuronOMPBOCL * ompb = GetACSRM ();
if ( ! ompb || ! ACSRMStage02MarketFingerprint ( 0 , 4 , base ) ||
! ACSRMStage02MarketFingerprint ( 6 , 6 , forecast ) ||
! SkillTarget . ParameterFingerprint ( target ) ||
! ompb . AppendParameterFingerprint ( posterior ))
ReturnFalse ;
const bool outer_unchanged = ( base == ACSRMStage02BaseFingerprint &&
forecast == ACSRMStage02ForecastFingerprint &&
target == ACSRMStage02TargetFingerprint );
const bool posterior_changed = ( posterior != ACSRMStage02PosteriorFingerprint );
PrintFormat ( " ACSRM_STAGE02_SIGNATURES_AFTER base=%I64u forecast=%I64u target=%I64u posterior=%I64u " ,
base , forecast , target , posterior );
if ( ! outer_unchanged || ! posterior_changed )
{
PrintFormat ( " ACSRM_STAGE02_SIGNATURES_FAIL outer_unchanged=%s posterior_changed=%s " ,
( outer_unchanged ? " true " : " false " ),
( posterior_changed ? " true " : " false " ));
ReturnFalse ;
}
return ( true );
}
//+------------------------------------------------------------------+
//| Verifies that pre-calibration inference preserved frozen state. |
//+------------------------------------------------------------------+
bool ACSRMStage02VerifyPreCalibrationSignatures ( void )
{
if ( ! ACSRMStage02SignaturesCaptured )
{
Print ( " ACSRM_STAGE02_PRECALIBRATION_SIGNATURES_FAIL reason=not_captured " );
return ( false );
}
ulong base = 0 , forecast = 0 , target = 0 ;
ulong ompb = ulong ( 1469598103934665603 );
CNeuronOMPBOCL * layer = GetACSRM ();
//--- Capture the ACSRM and frozen outer-network fingerprints before calibration.
if ( ! layer || ! ACSRMStage02MarketFingerprint ( 0 , 4 , base ) ||
! ACSRMStage02MarketFingerprint ( 6 , 6 , forecast ) ||
! SkillTarget . ParameterFingerprint ( target ) ||
! layer . AppendParameterFingerprint ( ompb ))
{
Print ( " ACSRM_STAGE02_PRECALIBRATION_SIGNATURES_FAIL reason=fingerprint_read " );
return ( false );
}
//--- Compare every protected fingerprint with the BYPASS/signature baseline.
const bool unchanged = ( base == ACSRMStage02BaseFingerprint &&
forecast == ACSRMStage02ForecastFingerprint &&
target == ACSRMStage02TargetFingerprint &&
ompb == ACSRMStage02PosteriorFingerprint );
PrintFormat ( " ACSRM_STAGE02_PRECALIBRATION_SIGNATURES base=%I64u forecast=%I64u " +
" target=%I64u ompb=%I64u unchanged=%s " , base , forecast , target , ompb ,
( unchanged ? " true " : " false " ));
if ( ! unchanged )
{
Print ( " ACSRM_STAGE02_PRECALIBRATION_SIGNATURES_FAIL reason=state_changed " );
return ( false );
}
return ( true );
}
//+------------------------------------------------------------------+
//| Computes an evaluation loss through posterior-mean inference. |
//+------------------------------------------------------------------+
bool ACSRMStage02InferenceEvaluation ( const int first , const int last , const string scope ,
double & forecast_loss , uint & valid_batches ,
uint & invalid_batches )
{
forecast_loss = 0.0 ;
valid_batches = 0 ;
invalid_batches = 0 ;
uint attempts = 0 ;
const uint rows = uint ( first - last + 1 );
const uint quota = ACSRMStage02ProgressQuota ( rows , 0 , ACSRMStage02Smoke ,
ACSRMStage02SmokeLimit );
const string progress_phase = ( StringFind ( scope , " source " ) >= 0 ?
" eval source " : " eval target " );
CNeuronOMPBOCL * ompb = GetACSRM ();
if ( first < last || ! ompb || ! SkillForecast || ! ConfigureACSRM ( OMPB_INFERENCE ) ||
! SkillMarket . Clear () || ! SkillTarget . Clear () ||
! SkillForecast . ResetEpochDiagnostics ())
{
PrintFormat ( " ACSRM_STAGE02_EVALUATION_FAIL scope=%s reason=setup " , scope );
ReturnFalse ;
}
SkillMarket . TrainMode ( true );
SkillTarget . TrainMode ( false );
ompb . TrainMode ( false );
ACSRMStage02ShowProgress ( progress_phase , ( ACSRMStage02Smoke ? valid_batches : attempts ),
quota , attempts , invalid_batches , false , false , true , false ,
0 , 0 , 0 , " " );
for ( int position = first ; position >= last && ! IsStopped (); position -- )
{
attempts ++ ;
if ( ! SkillTrainBatch ( position ))
{
invalid_batches ++ ;
ACSRMStage02ShowProgress ( progress_phase , ( ACSRMStage02Smoke ? valid_batches : attempts ),
quota , attempts , invalid_batches , false , false , true , false ,
0 , 0 , 0 , " " );
continue ;
}
valid_batches ++ ;
ACSRMStage02ShowProgress ( progress_phase , ( ACSRMStage02Smoke ? valid_batches : attempts ),
quota , attempts , invalid_batches , false , false , true , false ,
0 , 0 , 0 , " " );
if ( ACSRMStage02Smoke && valid_batches >= ACSRMStage02SmokeLimit )
break ;
}
//--- A stopped partial inference pass cannot supply evaluation metrics.
if ( IsStopped ())
{
if ( scope == " pre_source_inference " )
Print ( " ACSRM_STAGE02_STOP_REQUESTED phase=pre_inference_source " );
else
if ( scope == " pre_target_inference " )
Print ( " ACSRM_STAGE02_STOP_REQUESTED phase=pre_inference_target " );
else
if ( scope == " source_inference " )
Print ( " ACSRM_STAGE02_STOP_REQUESTED phase=post_inference_source " );
else
Print ( " ACSRM_STAGE02_STOP_REQUESTED phase=post_inference_target " );
ACSRMStage02StopReported = true ;
return ( false );
}
double lmix , router , trajectory , confidence , latent , observation ;
double valid , invalid , entropy , distance , inactive , recovered ;
if ( valid_batches == 0 || ! SkillForecast . BuildEpochCodebookDiagnostics () ||
! SkillForecast . ReadEpochDiagnostics ( lmix , router , trajectory , confidence , latent ,
observation , valid , invalid , entropy , distance ,
inactive , recovered ) ||
valid < double ( valid_batches ) || ! ACSRMStage02Finite ( lmix ) ||
! ACSRMStage02Finite ( valid ) || ! ACSRMStage02Finite ( invalid ))
{
PrintFormat ( " ACSRM_STAGE02_EVALUATION_FAIL scope=%s reason=metrics valid=%u invalid=%u " ,
scope , valid_batches , invalid_batches );
ACSRMStage02ShowProgress ( progress_phase , ( ACSRMStage02Smoke ? valid_batches : attempts ),
quota , attempts , invalid_batches , true , false , true , false ,
0 , 0 , 0 , " result=failed " );
ReturnFalse ;
}
forecast_loss = lmix / valid ;
if ( ! ACSRMStage02Finite ( forecast_loss ) || ! SkillVerifyFrozenForecastExact ())
{
PrintFormat ( " ACSRM_STAGE02_EVALUATION_FAIL scope=%s reason=finite_or_frozen " , scope );
ACSRMStage02ShowProgress ( progress_phase , ( ACSRMStage02Smoke ? valid_batches : attempts ),
quota , attempts , invalid_batches , true , false , true , false ,
0 , 0 , 0 , " result=failed " );
ReturnFalse ;
}
ACSRMStage02ShowProgress ( progress_phase , ( ACSRMStage02Smoke ? valid_batches : attempts ),
quota , attempts , invalid_batches , true , true , true , false ,
0 , 0 , 0 , " " );
PrintFormat ( " ACSRM_STAGE02_EVALUATION_PASS scope=%s loss=%.9f valid=%u invalid=%u " ,
scope , forecast_loss , valid_batches , invalid_batches );
return ( true );
}
//+------------------------------------------------------------------+
//| Checks the frozen acceptance gate without replacing any file. |
//+------------------------------------------------------------------+
bool ACSRMStage02Accept ( const double source_before , const double target_before ,
const double source_after , const double target_after ,
const uint source_valid , const uint target_valid )
{
const double denominator_source = MathMax ( MathAbs ( source_before ), 1.0e-12 );
const double denominator_target = MathMax ( MathAbs ( target_before ), 1.0e-12 );
const double target_improvement = ( target_before - target_after ) / denominator_target ;
const double source_degradation = ( source_after - source_before ) / denominator_source ;
const bool finite = ( ACSRMStage02Finite ( source_before ) && ACSRMStage02Finite ( target_before ) &&
ACSRMStage02Finite ( source_after ) && ACSRMStage02Finite ( target_after ));
const bool accepted = ( finite && source_valid > 0 && target_valid > 0 &&
target_improvement >= 0.01 && source_degradation <= 0.01 );
PrintFormat ( " ACSRM_STAGE02_ACCEPTANCE target_improvement=%.8f source_degradation=%.8f " +
" source_valid=%u target_valid=%u accepted=%s " , target_improvement ,
source_degradation , source_valid , target_valid ,
( accepted ? " true " : " false " ));
return ( accepted );
}
//+------------------------------------------------------------------+
//| The temporary graph must never validate a production file. |
//+------------------------------------------------------------------+
ulong ACSRMStage02ForecastSignature ( const string market_file ,
CNeuronScenarioForecast * forecast )
{
if ( ! forecast )
return ( 0 );
ulong hash = ulong ( 1469598103934665603 );
hash = SkillHashFile ( hash , market_file );
if ( hash == 0 )
return ( 0 );
hash = SkillHashUInt ( hash , Skill_FORMAT_VERSION );
hash = SkillHashUInt ( hash , BarDescr );
hash = SkillHashUInt ( hash , NScenarios );
hash = SkillHashUInt ( hash , TopK );
hash = SkillHashUInt ( hash , NForecast );
hash = SkillHashUInt ( hash , EmbeddingSize );
hash = SkillHashUInt ( hash , forecast . ContractSignature ());
hash = SkillHashText ( hash , " z_layout=K,V,H,D;u_layout=K,V,H;pi_layout=K;codebook_layout=K,V,H,D " );
hash = SkillHashText ( hash ,
" market_layout=RankTCM_then_ACSRM_then_ScenarioForecast; " +
" variable_order=BarDescr_feature_series_0_to_8 " );
return ( SkillHashText ( hash , " OHLC_deltas_from_open;tick_volume_div_1000;RSI_CCI_ATR_MACD_raw " ));
}
//+------------------------------------------------------------------+
//| Writes the candidate manifest for Stage 02 checkpoints. |
//+------------------------------------------------------------------+
bool ACSRMStage02WriteCandidateManifest ( const string market_file , const string target_file ,
const string manifest_file , const uint completed_epochs )
{
const ulong forecast_signature = ACSRMStage02ForecastSignature ( market_file , SkillForecast );
const ulong target_hash = SkillHashFile ( ulong ( 1469598103934665603 ), target_file );
const ulong training_signature = SkillHashFile ( forecast_signature , target_file );
if ( ! SkillForecast || forecast_signature == 0 || target_hash == 0 || training_signature == 0 )
ReturnFalse ;
int handle = FileOpen ( manifest_file , FILE_WRITE | FILE_TXT | FILE_ANSI | FILE_COMMON );
if ( handle == INVALID_HANDLE )
ReturnFalse ;
const bool written = ( FileWrite ( handle , " format=ACSRM_FORECAST " ) > 0 &&
FileWrite ( handle , StringFormat ( " version=%u " , Skill_FORMAT_VERSION )) > 0 &&
FileWrite ( handle , StringFormat ( " forecast_type=%d " , defNeuronScenarioForecast )) > 0 &&
FileWrite ( handle , StringFormat ( " variables=%u " , BarDescr )) > 0 &&
FileWrite ( handle , StringFormat ( " scenarios=%u " , NScenarios )) > 0 &&
FileWrite ( handle , StringFormat ( " top_k=%u " , TopK )) > 0 &&
FileWrite ( handle , StringFormat ( " horizon=%u " , NForecast )) > 0 &&
FileWrite ( handle , StringFormat ( " latent=%u " , EmbeddingSize )) > 0 &&
FileWrite ( handle , " z_layout=K,V,H,D " ) > 0 &&
FileWrite ( handle , " u_layout=K,V,H " ) > 0 &&
FileWrite ( handle , " pi_layout=K " ) > 0 &&
FileWrite ( handle , " codebook_layout=K,V,H,D " ) > 0 &&
FileWrite ( handle , " variable_order=BarDescr_feature_series_0_to_8 " ) > 0 &&
FileWrite ( handle , StringFormat ( " contract_signature=%I64u " ,
SkillForecast . ContractSignature ())) > 0 &&
FileWrite ( handle , StringFormat ( " forecast_signature=%I64u " , forecast_signature )) > 0 &&
FileWrite ( handle , StringFormat ( " target_hash=%I64u " , target_hash )) > 0 &&
FileWrite ( handle , StringFormat ( " training_signature=%I64u " , training_signature )) > 0 &&
FileWrite ( handle , StringFormat ( " completed_epochs=%u " , completed_epochs )) > 0 &&
FileWrite ( handle , StringFormat ( " training_batches=%I64u " , SkillBatches )) > 0 &&
FileWrite ( handle , StringFormat ( " invalid_batches=%I64u " , SkillInvalidBatches )) > 0 &&
FileWrite ( handle , " normalization=OHLC_deltas_from_open; " +
" tick_volume_div_1000;RSI_CCI_ATR_MACD_raw " ) > 0 );
if ( written )
FileFlush ( handle );
FileClose ( handle );
return ( written );
}
//+------------------------------------------------------------------+
//| Validates the candidate manifest structure and fields. |
//+------------------------------------------------------------------+
bool ACSRMStage02ValidateCandidateManifest ( const string market_file , const string target_file ,
const string manifest_file ,
CNeuronScenarioForecast * forecast )
{
const ulong signature = ACSRMStage02ForecastSignature ( market_file , forecast );
const ulong target_hash = SkillHashFile ( ulong ( 1469598103934665603 ), target_file );
const ulong training_signature = SkillHashFile ( signature , target_file );
if ( ! forecast || signature == 0 || target_hash == 0 || training_signature == 0 )
ReturnFalse ;
#define ACSRM_STAGE02_MANIFEST_EQ(KEY,VALUE) if(SkillManifestValue(manifest_file, KEY) != (VALUE)) ReturnFalse
ACSRM_STAGE02_MANIFEST_EQ ( " format " , " ACSRM_FORECAST " );
ACSRM_STAGE02_MANIFEST_EQ ( " version " , IntegerToString ( Skill_FORMAT_VERSION ));
ACSRM_STAGE02_MANIFEST_EQ ( " forecast_type " , IntegerToString ( defNeuronScenarioForecast ));
ACSRM_STAGE02_MANIFEST_EQ ( " variables " , IntegerToString ( BarDescr ));
ACSRM_STAGE02_MANIFEST_EQ ( " scenarios " , IntegerToString ( NScenarios ));
ACSRM_STAGE02_MANIFEST_EQ ( " top_k " , IntegerToString ( TopK ));
ACSRM_STAGE02_MANIFEST_EQ ( " horizon " , IntegerToString ( NForecast ));
ACSRM_STAGE02_MANIFEST_EQ ( " latent " , IntegerToString ( EmbeddingSize ));
ACSRM_STAGE02_MANIFEST_EQ ( " z_layout " , " K,V,H,D " );
ACSRM_STAGE02_MANIFEST_EQ ( " u_layout " , " K,V,H " );
ACSRM_STAGE02_MANIFEST_EQ ( " pi_layout " , " K " );
ACSRM_STAGE02_MANIFEST_EQ ( " codebook_layout " , " K,V,H,D " );
ACSRM_STAGE02_MANIFEST_EQ ( " variable_order " , " BarDescr_feature_series_0_to_8 " );
ACSRM_STAGE02_MANIFEST_EQ ( " normalization " , " OHLC_deltas_from_open;tick_volume_div_1000;RSI_CCI_ATR_MACD_raw " );
ACSRM_STAGE02_MANIFEST_EQ ( " contract_signature " , StringFormat ( " %I64u " , forecast . ContractSignature ()));
ACSRM_STAGE02_MANIFEST_EQ ( " forecast_signature " , StringFormat ( " %I64u " , signature ));
ACSRM_STAGE02_MANIFEST_EQ ( " target_hash " , StringFormat ( " %I64u " , target_hash ));
ACSRM_STAGE02_MANIFEST_EQ ( " training_signature " , StringFormat ( " %I64u " , training_signature ));
#undef ACSRM_STAGE02_MANIFEST_EQ
return ( SkillManifestValue ( manifest_file , " completed_epochs " ) != " " &&
SkillManifestValue ( manifest_file , " training_batches " ) != " " &&
SkillManifestValue ( manifest_file , " invalid_batches " ) != " " );
}
//+---------------------------------------------------------------------+
//| Reload-probes one immutable candidate before selector activation. |
//+---------------------------------------------------------------------+
bool ACSRMStage02ValidateCandidate ( const string market_file , const string target_file ,
const string manifest_file )
{
CNet market ;
CNet target ;
float error = 0.0f , undefine = 0.0f , forecast = 0.0f ;
datetime studied = 0 ;
if ( ! market . Load ( market_file , error , undefine , forecast , studied , true ) ||
! target . Load ( target_file , error , undefine , forecast , studied , true ))
ReturnFalse ;
if ( ! target . SetOpenCLChecked ( market . GetOpenCL ()))
ReturnFalseEx ( " target OpenCL transfer failed " );
CNeuronBaseOCL * rank_tcm = market . Layer ( 4 );
CNeuronBaseOCL * ompb_layer = market . Layer ( 5 );
CNeuronBaseOCL * forecast_layer = market . Layer ( 6 );
CNeuronScenarioForecast * staged_forecast =
( forecast_layer && forecast_layer . Type () == defNeuronScenarioForecast ?
( CNeuronScenarioForecast * ) forecast_layer : NULL );
CNeuronOMPBOCL * staged_ompb =
( ompb_layer && ompb_layer . Type () == defNeuronOMPBOCL ? ( CNeuronOMPBOCL * ) ompb_layer : NULL );
ulong staged_market = 0 , staged_target = 0 , staged_posterior = ulong ( 1469598103934665603 );
ulong live_market = 0 , live_target = 0 , live_posterior = ulong ( 1469598103934665603 );
CNeuronOMPBOCL * live_ompb = GetACSRM ();
if ( ! rank_tcm || rank_tcm . Type () != defNeuronCogDriverRankTCM || ! staged_ompb ||
! staged_forecast ||
! market . ParameterFingerprint ( staged_market ) || ! target . ParameterFingerprint ( staged_target ) ||
! staged_ompb . AppendParameterFingerprint ( staged_posterior ) || ! SkillMarket . ParameterFingerprint ( live_market ) ||
! SkillTarget . ParameterFingerprint ( live_target ) || ! live_ompb ||
! live_ompb . AppendParameterFingerprint ( live_posterior ) ||
! ACSRMStage02ValidateCandidateManifest ( market_file , target_file , manifest_file ,
staged_forecast ))
ReturnFalse ;
return ( staged_market == live_market && staged_target == live_target &&
staged_posterior == live_posterior );
}
//+------------------------------------------------------------------+
//--- A malformed active selector is fail-closed. Its absence deliberately
//--- retains the original Stage 01 fixed-name loading contract.
bool ACSRMStage02RecoveryFailed = false ;
bool ACSRMStage02SelectorActivated = false ;
bool ACSRMStage02ReloadProofPassed = false ;
bool ACSRMStage02PreviousProofReady = false ;
string ACSRMStage02StagedGeneration = " " ;
ulong ACSRMStage02PreviousMarketFingerprint = 0 ;
ulong ACSRMStage02PreviousTargetFingerprint = 0 ;
ulong ACSRMStage02PreviousPosteriorFingerprint = 0 ;
//+-------------------------------------------------------------------+
//| Validates a complete checkpoint set without touching live state. |
//+-------------------------------------------------------------------+
bool ACSRMStage02ValidateCheckpointSet ( const string market_file , const string target_file ,
const string manifest_file )
{
if ( ! FileIsExist ( market_file , FILE_COMMON ) || ! FileIsExist ( target_file , FILE_COMMON ) ||
! FileIsExist ( manifest_file , FILE_COMMON ))
ReturnFalse ;
CNet market ;
CNet target ;
float error = 0.0f , undefine = 0.0f , forecast = 0.0f ;
datetime studied = 0 ;
if ( ! market . Load ( market_file , error , undefine , forecast , studied , true ) ||
! target . Load ( target_file , error , undefine , forecast , studied , true ))
ReturnFalse ;
if ( ! target . SetOpenCLChecked ( market . GetOpenCL ()))
ReturnFalseEx ( " target OpenCL transfer failed " );
CNeuronBaseOCL * rank_tcm = market . Layer ( 4 );
CNeuronBaseOCL * ompb_layer = market . Layer ( 5 );
CNeuronBaseOCL * forecast_layer = market . Layer ( 6 );
CNeuronScenarioForecast * checkpoint_forecast =
( forecast_layer && forecast_layer . Type () == defNeuronScenarioForecast ?
( CNeuronScenarioForecast * ) forecast_layer : NULL );
return ( rank_tcm != NULL && rank_tcm . Type () == defNeuronCogDriverRankTCM &&
ompb_layer != NULL && ompb_layer . Type () == defNeuronOMPBOCL &&
checkpoint_forecast != NULL &&
ACSRMStage02ValidateCandidateManifest ( market_file , target_file , manifest_file ,
checkpoint_forecast ));
}
//+----------------------------------------------------------------------+
//| Identifies the clean initial state before the first Stage 01 save. |
//+----------------------------------------------------------------------+
bool ACSRMStage02CanonicalTupleMissing ( const string market_file , const string target_file ,
const string manifest_file )
{
//--- A Stage 01 restore miss is safe only when no tuple member exists.
return ( ! FileIsExist ( market_file , FILE_COMMON ) && ! FileIsExist ( target_file , FILE_COMMON ) &&
! FileIsExist ( manifest_file , FILE_COMMON ));
}
//+----------------------------------------------------------------------+
//| Captures the immutable checkpoint fingerprints for rollback proof. |
//+----------------------------------------------------------------------+
bool ACSRMStage02CheckpointFingerprints ( const string market_file , const string target_file ,
ulong & market_fingerprint , ulong & target_fingerprint ,
ulong & posterior_fingerprint )
{
market_fingerprint = 0 ;
target_fingerprint = 0 ;
posterior_fingerprint = ulong ( 1469598103934665603 );
CNet checkpoint_market ;
CNet checkpoint_target ;
float error = 0.0f , undefine = 0.0f , forecast = 0.0f ;
datetime studied = 0 ;
if ( ! checkpoint_market . Load ( market_file , error , undefine , forecast , studied , true ) ||
! checkpoint_target . Load ( target_file , error , undefine , forecast , studied , true ))
ReturnFalse ;
if ( ! checkpoint_target . SetOpenCLChecked ( checkpoint_market . GetOpenCL ()))
ReturnFalseEx ( " target OpenCL transfer failed " );
CNeuronBaseOCL * ompb_layer = checkpoint_market . Layer ( 5 );
CNeuronOMPBOCL * checkpoint_ompb =
( ompb_layer && ompb_layer . Type () == defNeuronOMPBOCL ? ( CNeuronOMPBOCL * ) ompb_layer : NULL );
return ( checkpoint_ompb != NULL && checkpoint_market . ParameterFingerprint ( market_fingerprint ) &&
checkpoint_target . ParameterFingerprint ( target_fingerprint ) &&
checkpoint_ompb . AppendParameterFingerprint ( posterior_fingerprint ));
}
//+-------------------------------------------------------------------+
//| Restores the Stage 01 fixed-name checkpoint as the default view. |
//+-------------------------------------------------------------------+
void ACSRMStage02ResetActiveCheckpointFiles ( void )
{
SkillActiveMarketFile = Skill_MARKET_FILE ;
SkillActiveTargetFile = Skill_TARGET_FILE ;
SkillActiveManifestFile = Skill_MANIFEST_FILE ;
}
//+------------------------------------------------------------------+
//| Accepts only generated identifiers, never arbitrary filenames. |
//+------------------------------------------------------------------+
bool ACSRMStage02GenerationIdValid ( const string generation )
{
const int length = StringLen ( generation );
int separator = - 1 ;
if ( length < 3 )
return ( false );
for ( int index = 0 ; index < length ; index ++ )
{
const ushort character = ( ushort ) StringGetCharacter ( generation , index );
if ( character == ( ushort ) ' _ ' )
{
if ( separator >= 0 )
return ( false );
separator = index ;
continue ;
}
if ( character < ( ushort ) ' 0 ' || character > ( ushort ) ' 9 ' )
return ( false );
}
return ( separator > 0 && separator < length - 1 );
}
//+------------------------------------------------------------------+
//| Maps a selector generation to its immutable checkpoint trio. |
//+------------------------------------------------------------------+
bool ACSRMStage02GenerationFiles ( const string generation , string & market_file ,
string & target_file , string & manifest_file )
{
if ( generation == " stage01 " )
{
market_file = Skill_MARKET_FILE ;
target_file = Skill_TARGET_FILE ;
manifest_file = Skill_MANIFEST_FILE ;
return ( true );
}
if ( ! ACSRMStage02GenerationIdValid ( generation ))
return ( false );
market_file = " ACSRMMarket " + ACSRM_STAGE02_GENERATION_PREFIX + generation + " .nnw " ;
target_file = " ACSRMTarget " + ACSRM_STAGE02_GENERATION_PREFIX + generation + " .nnw " ;
manifest_file = " ACSRMForecast " + ACSRM_STAGE02_GENERATION_PREFIX + generation + " .manifest " ;
return ( true );
}
//+------------------------------------------------------------------+
//| Accepts only the fixed Stage 01 trio or one exact generation. |
//+------------------------------------------------------------------+
bool ACSRMStage02ExplicitCheckpointFilesValid ( const string market_file , const string target_file ,
const string manifest_file )
{
string expected_market = " " , expected_target = " " , expected_manifest = " " ;
if ( ! ACSRMStage02GenerationFiles ( " stage01 " , expected_market , expected_target , expected_manifest ))
return ( false );
if ( market_file == expected_market && target_file == expected_target &&
manifest_file == expected_manifest )
return ( true );
const string market_prefix = " ACSRMMarket " + ACSRM_STAGE02_GENERATION_PREFIX ;
const string market_suffix = " .nnw " ;
const int prefix_length = StringLen ( market_prefix );
const int suffix_length = StringLen ( market_suffix );
const int market_length = StringLen ( market_file );
if ( market_length <= prefix_length + suffix_length ||
StringFind ( market_file , market_prefix ) != 0 ||
StringSubstr ( market_file , market_length - suffix_length , suffix_length ) != market_suffix )
return ( false );
const string generation = StringSubstr ( market_file , prefix_length ,
market_length - prefix_length - suffix_length );
if ( ! ACSRMStage02GenerationFiles ( generation , expected_market , expected_target , expected_manifest ))
return ( false );
return ( market_file == expected_market && target_file == expected_target &&
manifest_file == expected_manifest );
}
//+----------------------------------------------------------------------+
//| Rejects interrupted publish sidecars instead of guessing recovery. |
//+----------------------------------------------------------------------+
bool ACSRMStage02RecoverySidecarPresent ( string & sidecar_file )
{
sidecar_file = " " ;
if ( FileIsExist ( ACSRM_STAGE02_LEGACY_TRANSACTION_FILE , FILE_COMMON ))
{
sidecar_file = ACSRM_STAGE02_LEGACY_TRANSACTION_FILE ;
return ( true );
}
if ( FileIsExist ( ACSRM_STAGE02_LEGACY_TRANSACTION_NEXT_FILE , FILE_COMMON ))
{
sidecar_file = ACSRM_STAGE02_LEGACY_TRANSACTION_NEXT_FILE ;
return ( true );
}
if ( FileIsExist ( ACSRM_STAGE02_SELECTOR_NEXT , FILE_COMMON ))
{
sidecar_file = ACSRM_STAGE02_SELECTOR_NEXT ;
return ( true );
}
if ( FileIsExist ( ACSRM_STAGE02_SELECTOR_RESTORE , FILE_COMMON ))
{
sidecar_file = ACSRM_STAGE02_SELECTOR_RESTORE ;
return ( true );
}
//--- Previous is valid only in the process that created it and will remove it
//--- after deterministic reload proof. On restart it is an interrupted
//--- transaction marker and must fail closed rather than activate unproven data.
if ( FileIsExist ( ACSRM_STAGE02_SELECTOR_PREVIOUS , FILE_COMMON ) &&
! ACSRMStage02PreviousProofReady )
{
sidecar_file = ACSRM_STAGE02_SELECTOR_PREVIOUS ;
return ( true );
}
return ( false );
}
//+----------------------------------------------------------------------+
//| Reads exactly one strict selector without allowing path injection. |
//+----------------------------------------------------------------------+
bool ACSRMStage02ReadSelector ( const string selector_file , string & generation )
{
generation = " " ;
int handle = FileOpen ( selector_file , FILE_READ | FILE_TXT | FILE_ANSI | FILE_COMMON | FILE_SHARE_READ );
if ( handle == INVALID_HANDLE )
return ( false );
bool format_seen = false , version_seen = false , generation_seen = false , valid = true ;
string parsed_generation = " " ;
while ( ! FileIsEnding ( handle ))
{
const string line = FileReadString ( handle );
const int separator = StringFind ( line , " = " );
if ( separator <= 0 || StringFind ( line , " = " , separator + 1 ) >= 0 )
{
valid = false ;
break ;
}
const string key = StringSubstr ( line , 0 , separator );
const string value = StringSubstr ( line , separator + 1 );
if ( key == " format " )
{
if ( format_seen || value != " ACSRM_STAGE02_SELECTOR " )
valid = false ;
format_seen = true ;
}
else
if ( key == " version " )
{
if ( version_seen || value != IntegerToString ( ACSRM_STAGE02_SELECTOR_VERSION ))
valid = false ;
version_seen = true ;
}
else
if ( key == " generation " )
{
if ( generation_seen )
valid = false ;
generation_seen = true ;
parsed_generation = value ;
}
else
valid = false ;
if ( ! valid )
break ;
}
FileClose ( handle );
if ( ! valid || ! format_seen || ! version_seen || ! generation_seen ||
( parsed_generation != " stage01 " && ! ACSRMStage02GenerationIdValid ( parsed_generation )))
return ( false );
generation = parsed_generation ;
return ( true );
}
//+------------------------------------------------------------------+
//| Binds one selector to the exact expected checkpoint generation. |
//+------------------------------------------------------------------+
bool ACSRMStage02SelectorMatchesExpected ( const string selector_file ,
const string expected_generation ,
const ulong expected_market ,
const ulong expected_target ,
const ulong expected_posterior )
{
string generation = " " , market_file = " " , target_file = " " , manifest_file = " " ;
ulong market = 0 , target = 0 ;
ulong posterior = ulong ( 1469598103934665603 );
if ( ! FileIsExist ( selector_file , FILE_COMMON ) ||
! ACSRMStage02ReadSelector ( selector_file , generation ) || generation != expected_generation ||
! ACSRMStage02GenerationFiles ( generation , market_file , target_file , manifest_file ) ||
! ACSRMStage02ValidateCheckpointSet ( market_file , target_file , manifest_file ) ||
! ACSRMStage02CheckpointFingerprints ( market_file , target_file , market , target , posterior ) ||
market != expected_market || target != expected_target || posterior != expected_posterior )
return ( false );
return ( true );
}
//+-------------------------------------------------------------------+
//| Writes and rereads one non-active selector file before a switch. |
//+-------------------------------------------------------------------+
bool ACSRMStage02WriteSelectorFile ( const string selector_file , const string generation )
{
if ( generation != " stage01 " && ! ACSRMStage02GenerationIdValid ( generation ))
return ( false );
int handle = FileOpen ( selector_file , FILE_WRITE | FILE_TXT | FILE_ANSI | FILE_COMMON );
if ( handle == INVALID_HANDLE )
return ( false );
const bool written = ( FileWrite ( handle , " format=ACSRM_STAGE02_SELECTOR " ) > 0 &&
FileWrite ( handle , StringFormat ( " version=%u " ,
ACSRM_STAGE02_SELECTOR_VERSION )) > 0 &&
FileWrite ( handle , StringFormat ( " generation=%s " , generation )) > 0 );
if ( written )
FileFlush ( handle );
FileClose ( handle );
string reread = " " ;
return ( written && ACSRMStage02ReadSelector ( selector_file , reread ) && reread == generation );
}
//+---------------------------------------------------------------------+
//| Ensures three tuple names describe three distinct physical files. |
//+---------------------------------------------------------------------+
bool ACSRMStage02TupleNamesValid ( const string market_file , const string target_file ,
const string manifest_file )
{
return ( market_file != " " && target_file != " " && manifest_file != " " &&
market_file != target_file && market_file != manifest_file &&
target_file != manifest_file );
}
//+------------------------------------------------------------------+
//| Copies a complete checkpoint tuple with manifest last. |
//+------------------------------------------------------------------+
bool ACSRMStage02CopyTuple ( const string source_market , const string source_target ,
const string source_manifest , const string destination_market ,
const string destination_target , const string destination_manifest ,
const bool rewrite )
{
if ( ! ACSRMStage02TupleNamesValid ( source_market , source_target , source_manifest ) ||
! ACSRMStage02TupleNamesValid ( destination_market , destination_target ,
destination_manifest ))
return ( false );
const uint flags = ( rewrite ? FILE_COMMON | FILE_REWRITE : FILE_COMMON );
return ( FileCopy ( source_market , FILE_COMMON , destination_market , flags ) &&
FileCopy ( source_target , FILE_COMMON , destination_target , flags ) &&
FileCopy ( source_manifest , FILE_COMMON , destination_manifest , flags ));
}
//+-------------------------------------------------------------------------------------------------------------------------------------------+
//| Publishes a staged tuple through three canonical active filenames. The staged and previous tuples remain until reload proof completes. |
//+-------------------------------------------------------------------------------------------------------------------------------------------+
bool ACSRMStage02PublishCanonicalTuple ( const string market_file , const string target_file ,
const string manifest_file , const string next_market ,
const string next_target , const string next_manifest ,
const string previous_market ,
const string previous_target ,
const string previous_manifest )
{
if ( ! ACSRMStage02TupleNamesValid ( market_file , target_file , manifest_file ) ||
! ACSRMStage02TupleNamesValid ( next_market , next_target , next_manifest ) ||
! ACSRMStage02TupleNamesValid ( previous_market , previous_target , previous_manifest ) ||
! ACSRMStage02ValidateCheckpointSet ( market_file , target_file , manifest_file ) ||
! ACSRMStage02ValidateCheckpointSet ( next_market , next_target , next_manifest ) ||
FileIsExist ( previous_market , FILE_COMMON ) || FileIsExist ( previous_target , FILE_COMMON ) ||
FileIsExist ( previous_manifest , FILE_COMMON ))
return ( false );
if ( ! ACSRMStage02CopyTuple ( market_file , target_file , manifest_file , previous_market ,
previous_target , previous_manifest , false ) ||
! ACSRMStage02ValidateCheckpointSet ( previous_market , previous_target , previous_manifest ))
return ( false );
return ( ACSRMStage02CopyTuple ( next_market , next_target , next_manifest , market_file ,
target_file , manifest_file , true ) &&
ACSRMStage02ValidateCheckpointSet ( market_file , target_file , manifest_file ));
}
//+----------------------------------------------------------------------+
//| Restores the retained tuple using the manifest as the final write. |
//+----------------------------------------------------------------------+
bool ACSRMStage02RestoreCanonicalTuple ( const string market_file , const string target_file ,
const string manifest_file , const string previous_market ,
const string previous_target ,
const string previous_manifest )
{
if ( ! ACSRMStage02TupleNamesValid ( market_file , target_file , manifest_file ) ||
! ACSRMStage02TupleNamesValid ( previous_market , previous_target , previous_manifest ) ||
! ACSRMStage02ValidateCheckpointSet ( previous_market , previous_target , previous_manifest ))
return ( false );
return ( ACSRMStage02CopyTuple ( previous_market , previous_target , previous_manifest , market_file ,
target_file , manifest_file , true ) &&
ACSRMStage02ValidateCheckpointSet ( market_file , target_file , manifest_file ));
}
//+------------------------------------------------------------------+
//| Classifies the retained tuple required by one Stage 01 phase. |
//+------------------------------------------------------------------+
bool SkillStage01TupleStateValid ( const bool market_exists ,
const bool target_exists ,
const bool manifest_exists ,
const bool had_previous ,
const string phase )
{
//--- The first save has no prior tuple; every later phase retains all members.
const bool complete = ( market_exists && target_exists && manifest_exists );
if ( phase == " prepared " )
return ( had_previous ? complete : ! market_exists && ! target_exists && ! manifest_exists );
if ( phase == " previous_ready " )
return ( had_previous && complete );
return ( false );
}
//+------------------------------------------------------------------+
//| Removes only files owned by a completed Stage 01 transaction. |
//+------------------------------------------------------------------+
bool SkillStage01DeleteFile ( const string file_name )
{
return ( ! FileIsExist ( file_name , FILE_COMMON ) || FileDelete ( file_name , FILE_COMMON ));
}
//+------------------------------------------------------------------+
//| Releases Stage 01 resources and resets stage state. |
//+------------------------------------------------------------------+
bool SkillStage01Cleanup ( void )
{
//--- Remove the commit marker last; its presence must survive every partial cleanup.
return ( SkillStage01DeleteFile ( ACSRM_STAGE01_MANIFEST_NEXT_FILE ) &&
SkillStage01DeleteFile ( ACSRM_STAGE01_TARGET_NEXT_FILE ) &&
SkillStage01DeleteFile ( ACSRM_STAGE01_MARKET_NEXT_FILE ) &&
SkillStage01DeleteFile ( ACSRM_STAGE01_MANIFEST_PREVIOUS_FILE ) &&
SkillStage01DeleteFile ( ACSRM_STAGE01_TARGET_PREVIOUS_FILE ) &&
SkillStage01DeleteFile ( ACSRM_STAGE01_MARKET_PREVIOUS_FILE ) &&
SkillStage01DeleteFile ( ACSRM_STAGE01_TRANSACTION_FILE ));
}
//+-------------------------------------------------------------------+
//| Writes one durable marker before canonical Stage 01 publication. |
//+-------------------------------------------------------------------+
bool SkillStage01WriteTransaction ( const bool had_previous , const string phase ,
const ulong market , const ulong target ,
const ulong posterior )
{
if (( phase != " prepared " && phase != " previous_ready " ) ||
FileIsExist ( ACSRM_STAGE01_TRANSACTION_FILE , FILE_COMMON ))
return ( false );
int handle = FileOpen ( ACSRM_STAGE01_TRANSACTION_FILE ,
FILE_WRITE | FILE_TXT | FILE_ANSI | FILE_COMMON );
if ( handle == INVALID_HANDLE )
return ( false );
//--- The candidate fingerprints identify an already-published complete tuple.
const bool written = ( FileWrite ( handle , " format=ACSRM_STAGE01_TRANSACTION " ) > 0 &&
FileWrite ( handle , StringFormat ( " version=%u " , ACSRM_STAGE01_TRANSACTION_VERSION )) > 0 &&
FileWrite ( handle , StringFormat ( " phase=%s " , phase )) > 0 &&
FileWrite ( handle , StringFormat ( " had_previous=%s " ,
( had_previous ? " true " : " false " ))) > 0 &&
FileWrite ( handle , StringFormat ( " candidate_market=%I64u " , market )) > 0 &&
FileWrite ( handle , StringFormat ( " candidate_target=%I64u " , target )) > 0 &&
FileWrite ( handle , StringFormat ( " candidate_posterior=%I64u " , posterior )) > 0 );
if ( written )
FileFlush ( handle );
FileClose ( handle );
return ( written );
}
//+------------------------------------------------------------------+
//| Rewrites only a complete transaction marker state transition. |
//+------------------------------------------------------------------+
bool SkillStage01AdvanceTransaction ( const bool had_previous , const string phase ,
const ulong market , const ulong target ,
const ulong posterior )
{
if ( ! SkillStage01DeleteFile ( ACSRM_STAGE01_TRANSACTION_FILE ))
return ( false );
return ( SkillStage01WriteTransaction ( had_previous , phase , market , target , posterior ));
}
//+------------------------------------------------------------------+
//| Reads one strict Stage 01 transaction marker. |
//+------------------------------------------------------------------+
bool SkillStage01ReadTransaction ( bool & had_previous , string & phase ,
string & market , string & target , string & posterior )
{
had_previous = false ;
phase = " " ;
market = " " ;
target = " " ;
posterior = " " ;
if ( ! FileIsExist ( ACSRM_STAGE01_TRANSACTION_FILE , FILE_COMMON ))
return ( false );
const string format = SkillManifestValue ( ACSRM_STAGE01_TRANSACTION_FILE , " format " );
const string version = SkillManifestValue ( ACSRM_STAGE01_TRANSACTION_FILE , " version " );
const string previous = SkillManifestValue ( ACSRM_STAGE01_TRANSACTION_FILE , " had_previous " );
phase = SkillManifestValue ( ACSRM_STAGE01_TRANSACTION_FILE , " phase " );
market = SkillManifestValue ( ACSRM_STAGE01_TRANSACTION_FILE , " candidate_market " );
target = SkillManifestValue ( ACSRM_STAGE01_TRANSACTION_FILE , " candidate_target " );
posterior = SkillManifestValue ( ACSRM_STAGE01_TRANSACTION_FILE , " candidate_posterior " );
if ( format != " ACSRM_STAGE01_TRANSACTION " ||
version != IntegerToString ( ACSRM_STAGE01_TRANSACTION_VERSION ) ||
( previous != " true " && previous != " false " ) ||
( phase != " prepared " && phase != " previous_ready " ) ||
market == " " || target == " " || posterior == " " )
return ( false );
had_previous = ( previous == " true " );
return ( true );
}
//+------------------------------------------------------------------+
//| Compares a complete tuple against its transaction marker. |
//+------------------------------------------------------------------+
bool SkillStage01TupleMatches ( const string market_file , const string target_file ,
const string manifest_file , const string market ,
const string target , const string posterior )
{
ulong actual_market = 0 , actual_target = 0 ;
ulong actual_posterior = ulong ( 1469598103934665603 );
return ( ACSRMStage02ValidateCheckpointSet ( market_file , target_file , manifest_file ) &&
ACSRMStage02CheckpointFingerprints ( market_file , target_file , actual_market ,
actual_target , actual_posterior ) &&
StringFormat ( " %I64u " , actual_market ) == market &&
StringFormat ( " %I64u " , actual_target ) == target &&
StringFormat ( " %I64u " , actual_posterior ) == posterior );
}
//+------------------------------------------------------------------+
//| Recovers only an interrupted Stage 01-owned publication. |
//+------------------------------------------------------------------+
bool SkillStage01RecoverInterruptedCheckpoint ( void )
{
const bool marker_exists = FileIsExist ( ACSRM_STAGE01_TRANSACTION_FILE , FILE_COMMON );
const bool temporary_exists = ( FileIsExist ( ACSRM_STAGE01_MARKET_NEXT_FILE , FILE_COMMON ) ||
FileIsExist ( ACSRM_STAGE01_TARGET_NEXT_FILE , FILE_COMMON ) ||
FileIsExist ( ACSRM_STAGE01_MANIFEST_NEXT_FILE , FILE_COMMON ) ||
FileIsExist ( ACSRM_STAGE01_MARKET_PREVIOUS_FILE , FILE_COMMON ) ||
FileIsExist ( ACSRM_STAGE01_TARGET_PREVIOUS_FILE , FILE_COMMON ) ||
FileIsExist ( ACSRM_STAGE01_MANIFEST_PREVIOUS_FILE , FILE_COMMON ));
if ( ! marker_exists )
{
//--- Uncommitted staging never changed canonical names and is safe to discard.
if ( ! temporary_exists )
return ( true );
if ( ! ACSRMStage02ValidateCheckpointSet ( Skill_MARKET_FILE , Skill_TARGET_FILE ,
Skill_MANIFEST_FILE ) &&
! ACSRMStage02CanonicalTupleMissing ( Skill_MARKET_FILE , Skill_TARGET_FILE ,
Skill_MANIFEST_FILE ))
return ( false );
return ( SkillStage01Cleanup ());
}
bool had_previous = false ;
string phase = " " , market = " " , target = " " , posterior = " " ;
if ( ! SkillStage01ReadTransaction ( had_previous , phase , market , target , posterior ))
return ( false );
//--- A complete candidate published before cleanup is already the committed epoch.
if ( SkillStage01TupleMatches ( Skill_MARKET_FILE , Skill_TARGET_FILE ,
Skill_MANIFEST_FILE , market , target , posterior ))
return ( SkillStage01Cleanup ());
//--- Before previous-ready canonical names are still the earlier valid tuple.
if ( phase == " prepared " && ACSRMStage02ValidateCheckpointSet ( Skill_MARKET_FILE ,
Skill_TARGET_FILE , Skill_MANIFEST_FILE ))
return ( SkillStage01Cleanup ());
if ( had_previous )
{
if ( ! SkillStage01TupleStateValid ( FileIsExist ( ACSRM_STAGE01_MARKET_PREVIOUS_FILE , FILE_COMMON ),
FileIsExist ( ACSRM_STAGE01_TARGET_PREVIOUS_FILE , FILE_COMMON ),
FileIsExist ( ACSRM_STAGE01_MANIFEST_PREVIOUS_FILE , FILE_COMMON ), true , phase ) ||
! ACSRMStage02RestoreCanonicalTuple ( Skill_MARKET_FILE , Skill_TARGET_FILE ,
Skill_MANIFEST_FILE ,
ACSRM_STAGE01_MARKET_PREVIOUS_FILE ,
ACSRM_STAGE01_TARGET_PREVIOUS_FILE ,
ACSRM_STAGE01_MANIFEST_PREVIOUS_FILE ))
return ( false );
return ( SkillStage01Cleanup ());
}
//--- The first publication has no prior checkpoint; remove only marker-owned partial data.
if ( ! SkillStage01DeleteFile ( Skill_MANIFEST_FILE ) ||
! SkillStage01DeleteFile ( Skill_TARGET_FILE ) ||
! SkillStage01DeleteFile ( Skill_MARKET_FILE ))
return ( false );
return ( SkillStage01Cleanup ());
}
//+------------------------------------------------------------------+
//| Publishes one fully validated Stage 01 checkpoint transaction. |
//+------------------------------------------------------------------+
bool SkillStage01SaveCheckpoint ( const uint completed_epochs )
{
if ( FileIsExist ( ACSRM_STAGE01_TRANSACTION_FILE , FILE_COMMON ) ||
FileIsExist ( ACSRM_STAGE01_MARKET_NEXT_FILE , FILE_COMMON ) ||
FileIsExist ( ACSRM_STAGE01_TARGET_NEXT_FILE , FILE_COMMON ) ||
FileIsExist ( ACSRM_STAGE01_MANIFEST_NEXT_FILE , FILE_COMMON ) ||
FileIsExist ( ACSRM_STAGE01_MARKET_PREVIOUS_FILE , FILE_COMMON ) ||
FileIsExist ( ACSRM_STAGE01_TARGET_PREVIOUS_FILE , FILE_COMMON ) ||
FileIsExist ( ACSRM_STAGE01_MANIFEST_PREVIOUS_FILE , FILE_COMMON ))
return ( false );
//--- Classify the pre-save canonical state before staging any new model bytes.
const bool had_previous = ACSRMStage02ValidateCheckpointSet ( Skill_MARKET_FILE ,
Skill_TARGET_FILE ,
Skill_MANIFEST_FILE );
if ( ! had_previous && ! ACSRMStage02CanonicalTupleMissing ( Skill_MARKET_FILE ,
Skill_TARGET_FILE , Skill_MANIFEST_FILE ))
return ( false );
//--- The next tuple is self-contained and validated before a commit marker exists.
const datetime now = TimeCurrent ();
if ( ! SkillMarket . Save ( ACSRM_STAGE01_MARKET_NEXT_FILE , 0.0f , 0.0f , 0.0f , now , true ) ||
! SkillTarget . Save ( ACSRM_STAGE01_TARGET_NEXT_FILE , 0.0f , 0.0f , 0.0f , now , true ) ||
! ACSRMStage02WriteCandidateManifest ( ACSRM_STAGE01_MARKET_NEXT_FILE ,
ACSRM_STAGE01_TARGET_NEXT_FILE , ACSRM_STAGE01_MANIFEST_NEXT_FILE , completed_epochs ) ||
! ACSRMStage02ValidateCheckpointSet ( ACSRM_STAGE01_MARKET_NEXT_FILE ,
ACSRM_STAGE01_TARGET_NEXT_FILE ,
ACSRM_STAGE01_MANIFEST_NEXT_FILE ))
return ( false );
ulong market = 0 , target = 0 ;
ulong posterior = ulong ( 1469598103934665603 );
if ( ! ACSRMStage02CheckpointFingerprints ( ACSRM_STAGE01_MARKET_NEXT_FILE ,
ACSRM_STAGE01_TARGET_NEXT_FILE ,
market , target , posterior ) ||
! SkillStage01WriteTransaction ( had_previous , " prepared " , market , target , posterior ))
return ( false );
//--- Retain the old complete tuple before any short canonical file is replaced.
if ( had_previous &&
( ! ACSRMStage02CopyTuple ( Skill_MARKET_FILE , Skill_TARGET_FILE , Skill_MANIFEST_FILE ,
ACSRM_STAGE01_MARKET_PREVIOUS_FILE ,
ACSRM_STAGE01_TARGET_PREVIOUS_FILE ,
ACSRM_STAGE01_MANIFEST_PREVIOUS_FILE , false ) ||
! ACSRMStage02ValidateCheckpointSet ( ACSRM_STAGE01_MARKET_PREVIOUS_FILE ,
ACSRM_STAGE01_TARGET_PREVIOUS_FILE ,
ACSRM_STAGE01_MANIFEST_PREVIOUS_FILE )))
return ( false );
if ( ! SkillStage01AdvanceTransaction ( had_previous , " previous_ready " , market , target , posterior ) ||
! ACSRMStage02CopyTuple ( ACSRM_STAGE01_MARKET_NEXT_FILE , ACSRM_STAGE01_TARGET_NEXT_FILE ,
ACSRM_STAGE01_MANIFEST_NEXT_FILE , Skill_MARKET_FILE ,
Skill_TARGET_FILE , Skill_MANIFEST_FILE , true ) ||
! SkillStage01TupleMatches ( Skill_MARKET_FILE , Skill_TARGET_FILE ,
Skill_MANIFEST_FILE , StringFormat ( " %I64u " , market ),
StringFormat ( " %I64u " , target ), StringFormat ( " %I64u " , posterior )))
return ( false );
if ( ! SkillStage01Cleanup ())
return ( false );
SkillLastSignature = SkillForecastSignature ( Skill_MARKET_FILE );
PrintFormat ( " ACSRM_STAGE01_CHECKPOINT_PASS epoch=%u " , completed_epochs );
return ( true );
}
//+---------------------------------------------------------------------+
//| Proves deterministic serialized parameters without live mutation. |
//+---------------------------------------------------------------------+
bool ACSRMStage02ReloadProofDeterministicTuple ( const string market_file ,
const string target_file ,
const string manifest_file )
{
ulong first_market = 0 , first_target = 0 ;
ulong first_posterior = ulong ( 1469598103934665603 );
ulong second_market = 0 , second_target = 0 ;
ulong second_posterior = ulong ( 1469598103934665603 );
return ( ACSRMStage02ValidateCheckpointSet ( market_file , target_file , manifest_file ) &&
ACSRMStage02CheckpointFingerprints ( market_file , target_file , first_market ,
first_target , first_posterior ) &&
ACSRMStage02CheckpointFingerprints ( market_file , target_file , second_market ,
second_target , second_posterior ) &&
first_market != 0 && first_target != 0 && first_posterior != 0 &&
first_market == second_market && first_target == second_target &&
first_posterior == second_posterior );
}
//+------------------------------------------------------------------+
//| Detects any interrupted canonical or legacy publication state. |
//+------------------------------------------------------------------+
bool ACSRMStage02CanonicalSidecarPresent ( string & sidecar_file )
{
sidecar_file = " " ;
const string sidecars [] =
{
ACSRM_STAGE02_MARKET_NEXT_FILE ,
ACSRM_STAGE02_TARGET_NEXT_FILE ,
ACSRM_STAGE02_MANIFEST_NEXT_FILE ,
ACSRM_STAGE02_MARKET_PREVIOUS_FILE ,
ACSRM_STAGE02_TARGET_PREVIOUS_FILE ,
ACSRM_STAGE02_MANIFEST_PREVIOUS_FILE ,
ACSRM_STAGE02_ACTIVE_SELECTOR ,
ACSRM_STAGE02_SELECTOR_NEXT ,
ACSRM_STAGE02_SELECTOR_PREVIOUS ,
ACSRM_STAGE02_SELECTOR_RESTORE ,
ACSRM_STAGE02_LEGACY_TRANSACTION_FILE ,
ACSRM_STAGE02_LEGACY_TRANSACTION_NEXT_FILE
};
for ( int index = 0 ; index < ArraySize ( sidecars ); ++ index )
if ( FileIsExist ( sidecars [ index ], FILE_COMMON ))
{
sidecar_file = sidecars [ index ];
return ( true );
}
return ( false );
}
//+--------------------------------------------------------------------------------------------------------------------------------------------------+
//| Resolves only the canonical production checkpoint or fails closed. Lightweight mode is valid only when the caller validates the loaded graph. |
//+--------------------------------------------------------------------------------------------------------------------------------------------------+
bool ACSRMStage02ResolveActiveCheckpoint ( const bool validate_tuple = true )
{
ACSRMStage02RecoveryFailed = false ;
ACSRMStage02ResetActiveCheckpointFiles ();
//--- Complete a Stage 01-owned recovery before interpreting canonical files.
if ( ! SkillStage01RecoverInterruptedCheckpoint ())
{
ACSRMStage02RecoveryFailed = true ;
Print ( " ACSRM_STAGE01_CHECKPOINT_FAIL reason=recovery " );
return ( false );
}
string sidecar_file = " " ;
if ( ACSRMStage02CanonicalSidecarPresent ( sidecar_file ))
{
ACSRMStage02RecoveryFailed = true ;
Print ( " ACSRM_STAGE02_CANONICAL_FAIL reason=incomplete_or_legacy_transaction " );
return ( false );
}
//--- No tuple exists before the first Stage 01 run; its caller creates a new graph.
if ( ACSRMStage02CanonicalTupleMissing ( Skill_MARKET_FILE , Skill_TARGET_FILE ,
Skill_MANIFEST_FILE ))
{
Print ( " ACSRM_STAGE02_CANONICAL_MISS reason=initial_checkpoint_absent " );
return ( false );
}
//--- A partial or unreadable tuple is not a new-model state and remains fail-closed.
if ( ! validate_tuple )
{
return ( true );
}
if ( ! ACSRMStage02ValidateCheckpointSet ( Skill_MARKET_FILE , Skill_TARGET_FILE ,
Skill_MANIFEST_FILE ))
{
ACSRMStage02RecoveryFailed = true ;
Print ( " ACSRM_STAGE02_CANONICAL_FAIL reason=invalid_active_tuple " );
return ( false );
}
Print ( " ACSRM_STAGE02_CANONICAL_PASS checkpoint=active " );
return ( true );
}
//+------------------------------------------------------------------+
//| Retains the previous selection before the one-file activation. |
//+------------------------------------------------------------------+
bool ACSRMStage02PreparePreviousSelector ( void )
{
string generation = " " , market_file = " " , target_file = " " , manifest_file = " " ;
string sidecar_file = " " ;
ACSRMStage02PreviousProofReady = false ;
if ( ACSRMStage02RecoverySidecarPresent ( sidecar_file ) ||
FileIsExist ( ACSRM_STAGE02_SELECTOR_PREVIOUS , FILE_COMMON ))
return ( false );
if ( ! FileIsExist ( ACSRM_STAGE02_ACTIVE_SELECTOR , FILE_COMMON ))
{
generation = " stage01 " ;
market_file = Skill_MARKET_FILE ;
target_file = Skill_TARGET_FILE ;
manifest_file = Skill_MANIFEST_FILE ;
}
else
if ( ! ACSRMStage02ReadSelector ( ACSRM_STAGE02_ACTIVE_SELECTOR , generation ) ||
! ACSRMStage02GenerationFiles ( generation , market_file , target_file , manifest_file ))
return ( false );
if ( ! ACSRMStage02ValidateCheckpointSet ( market_file , target_file , manifest_file ) ||
! ACSRMStage02CheckpointFingerprints ( market_file , target_file ,
ACSRMStage02PreviousMarketFingerprint ,
ACSRMStage02PreviousTargetFingerprint ,
ACSRMStage02PreviousPosteriorFingerprint ))
return ( false );
if ( generation == " stage01 " )
{
if ( ! ACSRMStage02WriteSelectorFile ( ACSRM_STAGE02_SELECTOR_PREVIOUS , generation ))
return ( false );
}
else
if ( ! FileCopy ( ACSRM_STAGE02_ACTIVE_SELECTOR , FILE_COMMON , ACSRM_STAGE02_SELECTOR_PREVIOUS ,
FILE_COMMON | FILE_REWRITE ))
return ( false );
string copied = " " ;
if ( ! ACSRMStage02ReadSelector ( ACSRM_STAGE02_SELECTOR_PREVIOUS , copied ) || copied != generation )
return ( false );
ACSRMStage02PreviousProofReady = true ;
return ( true );
}
//+------------------------------------------------------------------+
//| Switches exactly one active file after full generation proof. |
//+------------------------------------------------------------------+
bool ACSRMStage02ActivateGeneration ( const string generation )
{
if ( FileIsExist ( ACSRM_STAGE02_SELECTOR_NEXT , FILE_COMMON ) ||
! ACSRMStage02WriteSelectorFile ( ACSRM_STAGE02_SELECTOR_NEXT , generation ))
return ( false );
if ( ! FileMove ( ACSRM_STAGE02_SELECTOR_NEXT , FILE_COMMON , ACSRM_STAGE02_ACTIVE_SELECTOR ,
FILE_COMMON | FILE_REWRITE ))
{
Print ( " ACSRM_STAGE02_SELECTOR_FAIL reason=activation_move next_retained=true " );
return ( false );
}
ACSRMStage02SelectorActivated = true ;
PrintFormat ( " ACSRM_STAGE02_SELECTOR_SWITCH_PASS generation=%s " , generation );
return ( true );
}
//+------------------------------------------------------------------+
//| Restores a retained canonical tuple after failed reload proof. |
//+------------------------------------------------------------------+
bool ACSRMStage02RestorePreviousSelector ( void )
{
ulong previous_market = 0 , previous_target = 0 ;
ulong previous_posterior = ulong ( 1469598103934665603 );
if ( ! ACSRMStage02PreviousProofReady ||
! ACSRMStage02CheckpointFingerprints ( ACSRM_STAGE02_MARKET_PREVIOUS_FILE ,
ACSRM_STAGE02_TARGET_PREVIOUS_FILE ,
previous_market , previous_target , previous_posterior ) ||
previous_market != ACSRMStage02PreviousMarketFingerprint ||
previous_target != ACSRMStage02PreviousTargetFingerprint ||
previous_posterior != ACSRMStage02PreviousPosteriorFingerprint )
return ( false );
if ( ! ACSRMStage02RestoreCanonicalTuple ( Skill_MARKET_FILE , Skill_TARGET_FILE ,
Skill_MANIFEST_FILE ,
ACSRM_STAGE02_MARKET_PREVIOUS_FILE ,
ACSRM_STAGE02_TARGET_PREVIOUS_FILE ,
ACSRM_STAGE02_MANIFEST_PREVIOUS_FILE ))
return ( false );
ACSRMStage02SelectorActivated = false ;
ACSRMStage02ReloadProofPassed = false ;
if ( ! ACSRMStage02ReloadCheckpointProof ( Skill_MARKET_FILE , Skill_TARGET_FILE ,
Skill_MANIFEST_FILE ,
ACSRMStage02PreviousMarketFingerprint ,
ACSRMStage02PreviousTargetFingerprint ,
ACSRMStage02PreviousPosteriorFingerprint ) ||
! ACSRMStage02FinalizeSteadySelector ())
return ( false );
return ( true );
}
//+-------------------------------------------------------------------+
//| Stages and reload-probes the complete temporary canonical tuple. |
//+-------------------------------------------------------------------+
bool ACSRMStage02StageCandidate ( void )
{
ulong posterior = ulong ( 1469598103934665603 );
CNeuronOMPBOCL * ompb = GetACSRM ();
if ( ! ompb || ! ompb . AppendParameterFingerprint ( posterior ) ||
! ACSRMStage02ResolveActiveCheckpoint ())
return ( false );
if ( FileIsExist ( ACSRM_STAGE02_MARKET_NEXT_FILE , FILE_COMMON ) ||
FileIsExist ( ACSRM_STAGE02_TARGET_NEXT_FILE , FILE_COMMON ) ||
FileIsExist ( ACSRM_STAGE02_MANIFEST_NEXT_FILE , FILE_COMMON ) ||
FileIsExist ( ACSRM_STAGE02_MARKET_PREVIOUS_FILE , FILE_COMMON ) ||
FileIsExist ( ACSRM_STAGE02_TARGET_PREVIOUS_FILE , FILE_COMMON ) ||
FileIsExist ( ACSRM_STAGE02_MANIFEST_PREVIOUS_FILE , FILE_COMMON ))
return ( false );
const datetime now = TimeCurrent ();
if ( ! SkillMarket . Save ( ACSRM_STAGE02_MARKET_NEXT_FILE , 0.0f , 0.0f , 0.0f , now , true ) ||
! SkillTarget . Save ( ACSRM_STAGE02_TARGET_NEXT_FILE , 0.0f , 0.0f , 0.0f , now , true ) ||
! ACSRMStage02WriteCandidateManifest ( ACSRM_STAGE02_MARKET_NEXT_FILE ,
ACSRM_STAGE02_TARGET_NEXT_FILE ,
ACSRM_STAGE02_MANIFEST_NEXT_FILE ,
SkillCompletedEpochs ) ||
! ACSRMStage02ValidateCandidate ( ACSRM_STAGE02_MARKET_NEXT_FILE ,
ACSRM_STAGE02_TARGET_NEXT_FILE ,
ACSRM_STAGE02_MANIFEST_NEXT_FILE ))
{
Print ( " ACSRM_STAGE02_CANONICAL_FAIL reason=stage_or_reload_probe " );
return ( false );
}
ACSRMStage02StagedGeneration = " canonical " ;
PrintFormat ( " ACSRM_STAGE02_CANONICAL_STAGE_PASS posterior=%I64u " , posterior );
return ( true );
}
//+------------------------------------------------------------------+
//| Publishes only a fully validated temporary canonical tuple. |
//+------------------------------------------------------------------+
bool ACSRMStage02PublishCandidate ( void )
{
if ( ! ACSRMStage02CheckpointFingerprints ( Skill_MARKET_FILE , Skill_TARGET_FILE ,
ACSRMStage02PreviousMarketFingerprint ,
ACSRMStage02PreviousTargetFingerprint ,
ACSRMStage02PreviousPosteriorFingerprint ) ||
! ACSRMStage02PublishCanonicalTuple ( Skill_MARKET_FILE , Skill_TARGET_FILE ,
Skill_MANIFEST_FILE ,
ACSRM_STAGE02_MARKET_NEXT_FILE ,
ACSRM_STAGE02_TARGET_NEXT_FILE ,
ACSRM_STAGE02_MANIFEST_NEXT_FILE ,
ACSRM_STAGE02_MARKET_PREVIOUS_FILE ,
ACSRM_STAGE02_TARGET_PREVIOUS_FILE ,
ACSRM_STAGE02_MANIFEST_PREVIOUS_FILE ))
return ( false );
ACSRMStage02PreviousProofReady = true ;
ACSRMStage02SelectorActivated = true ;
return ( true );
}
//+-----------------------------------------------------------------------+
//| Loads exactly the supplied checkpoint without resolving a selector. |
//+-----------------------------------------------------------------------+
bool ACSRMStage02LoadExplicitCheckpoint ( const string market_file , const string target_file ,
const string manifest_file )
{
if ( ! ACSRMStage02ExplicitCheckpointFilesValid ( market_file , target_file , manifest_file ))
return ( false );
SkillFrozenBaselineReady = false ;
SkillForecast = NULL ;
if ( ! FileIsExist ( market_file , FILE_COMMON ) || ! FileIsExist ( target_file , FILE_COMMON ) ||
! FileIsExist ( manifest_file , FILE_COMMON ))
return ( false );
//--- The explicit loader retains this validated tuple as its current in-memory
//--- view for its caller's fingerprint/finalize work; it never resolves selectors.
SkillActiveMarketFile = market_file ;
SkillActiveTargetFile = target_file ;
SkillActiveManifestFile = manifest_file ;
if ( ! SkillValidateForecastManifestHeader ())
return ( false );
float error = 0.0f , undefine = 0.0f , forecast = 0.0f ;
datetime studied = 0 ;
if ( ! SkillMarket . Load ( market_file , error , undefine , forecast , studied , true ) ||
! SkillTarget . Load ( target_file , error , undefine , forecast , studied , true ))
return ( false );
if ( ! SkillTarget . SetOpenCLChecked ( SkillMarket . GetOpenCL ()))
ReturnFalseEx ( " target OpenCL transfer failed " );
SkillForecast = ( CNeuronScenarioForecast * ) SkillMarket . Layer ( - 1 );
if ( ! SkillForecast || SkillForecast . Type () != defNeuronScenarioForecast ||
! ConfigureForecastRecoveryAge () || ! SkillInitTrainingBuffers () ||
! SkillValidateShapes ())
return ( false );
const ulong signature = SkillForecastSignature ();
const string completed = SkillManifestValue ( manifest_file , " completed_epochs " );
const string batches = SkillManifestValue ( manifest_file , " training_batches " );
const string invalid = SkillManifestValue ( manifest_file , " invalid_batches " );
if ( ! SkillValidateForecastManifest ( signature , true ) || completed == " " || batches == " " ||
invalid == " " )
return ( false );
CNeuronOMPBOCL * ompb = GetACSRM ();
if ( ! ompb || ! SkillMarket . SetWeightsUpdate ( false ) || ! SkillTarget . SetWeightsUpdate ( false ) ||
! SkillForecast . SetCodebookUpdate ( false ) || ! ConfigureACSRM ( OMPB_INFERENCE ) || ! ompb . Clear () ||
! SkillCaptureFrozenForecastBaseline ( SkillForecast ))
return ( false );
SkillCompletedEpochs = ( uint ) StringToInteger ( completed );
SkillBatches = ( ulong ) StringToInteger ( batches );
SkillInvalidBatches = ( ulong ) StringToInteger ( invalid );
SkillMarket . TrainMode ( false );
SkillTarget . TrainMode ( false );
ompb . TrainMode ( false );
SkillLastSignature = signature ;
return ( true );
}
//+---------------------------------------------------------------------+
//| Compares one immutable reload output and names the failed tensor. |
//+---------------------------------------------------------------------+
bool ACSRMStage02ReloadTensorEqual ( const string tensor , CBufferFloat * actual ,
CBufferFloat & expected )
{
if ( ! actual || ! actual . BufferRead () || actual . Total () != expected . Total ())
{
PrintFormat ( " ACSRM_STAGE02_RELOAD_TENSOR_FAIL tensor=%s reason=shape actual=%d expected=%d " ,
tensor , ( actual ? actual . Total () : - 1 ), expected . Total ());
return ( false );
}
for ( int index = 0 ; index < actual . Total (); index ++ )
{
if ( ! MathIsValidNumber ( actual [ index ]) || ! MathIsValidNumber ( expected [ index ]) ||
actual [ index ] != expected [ index ])
{
PrintFormat ( " ACSRM_STAGE02_RELOAD_TENSOR_FAIL tensor=%s index=%d first=%.9g second=%.9g " ,
tensor , index , expected [ index ], actual [ index ]);
return ( false );
}
}
return ( true );
}
//+---------------------------------------------------------------------+
//| Proves the first inference result after two clean explicit loads. |
//+---------------------------------------------------------------------+
bool ACSRMStage02ReloadProofDeterministic ( const string market_file , const string target_file ,
const string manifest_file , const int position ,
CBufferFloat * state , CBufferFloat * time )
{
CBufferFloat z , u , pi ;
if ( position < 0 || ! state || ! time ||
! ACSRMStage02LoadExplicitCheckpoint ( market_file , target_file , manifest_file ) ||
! SkillForwardForecast ( position , state , time ) ||
! SkillCaptureFrozenBuffer ( SkillForecast . GetZ (), z ) ||
! SkillCaptureFrozenBuffer ( SkillForecast . GetU (), u ) ||
! SkillCaptureFrozenBuffer ( SkillForecast . GetPi (), pi ) ||
! ACSRMStage02LoadExplicitCheckpoint ( market_file , target_file , manifest_file ) ||
! SkillForwardForecast ( position , state , time ) ||
! ACSRMStage02ReloadTensorEqual ( " Z " , SkillForecast . GetZ (), z ) ||
! ACSRMStage02ReloadTensorEqual ( " U " , SkillForecast . GetU (), u ) ||
! ACSRMStage02ReloadTensorEqual ( " Pi " , SkillForecast . GetPi (), pi ) ||
! SkillVerifyFrozenForecastExact ( SkillForecast ))
return ( false );
return ( true );
}
//+----------------------------------------------------------------------------+
//| Reloads one selected checkpoint and proves deterministic mean inference. |
//+----------------------------------------------------------------------------+
bool ACSRMStage02ReloadCheckpointProof ( const string market_file , const string target_file ,
const string manifest_file , const ulong expected_market ,
const ulong expected_target , const ulong expected_posterior )
{
ACSRMStage02ReloadProofPassed = false ;
if ( ! ACSRMStage02ReloadProofDeterministic ( market_file , target_file , manifest_file ,
ACSRMStage02TargetEvalFirst , GetPointer ( SkillState ),
GetPointer ( SkillTime )))
return ( false );
CNeuronOMPBOCL * ompb = GetACSRM ();
ulong market = 0 , target = 0 , posterior = ulong ( 1469598103934665603 );
if ( ! ompb || ! SkillMarket . ParameterFingerprint ( market ) ||
! SkillTarget . ParameterFingerprint ( target ) || ! ompb . AppendParameterFingerprint ( posterior ) ||
market != expected_market || target != expected_target || posterior != expected_posterior )
return ( false );
ACSRMStage02ReloadProofPassed = true ;
Print ( " ACSRM_STAGE02_RELOAD_SMOKE_PASS mode=inference deterministic=true " );
return ( true );
}
//+------------------------------------------------------------------------+
//| Reloads the canonical tuple and proves deterministic mean inference. |
//+------------------------------------------------------------------------+
bool ACSRMStage02ReloadSmoke ( const ulong expected_market , const ulong expected_target ,
const ulong expected_posterior )
{
if ( ! ACSRMStage02SelectorActivated )
return ( false );
return ( ACSRMStage02ReloadCheckpointProof ( Skill_MARKET_FILE , Skill_TARGET_FILE ,
Skill_MANIFEST_FILE ,
expected_market , expected_target , expected_posterior ));
}
//+----------------------------------------------------------------------+
//| Cleans temporary tuples only after deterministic canonical reload. |
//+----------------------------------------------------------------------+
bool ACSRMStage02FinalizeSteadySelector ( void )
{
ulong market = 0 , target = 0 ;
ulong posterior = ulong ( 1469598103934665603 );
CNeuronOMPBOCL * ompb = GetACSRM ();
if ( ! ACSRMStage02ReloadProofPassed ||
! ACSRMStage02ValidateCheckpointSet ( Skill_MARKET_FILE , Skill_TARGET_FILE ,
Skill_MANIFEST_FILE ) ||
! ACSRMStage02ValidateCheckpointSet ( ACSRM_STAGE02_MARKET_NEXT_FILE ,
ACSRM_STAGE02_TARGET_NEXT_FILE ,
ACSRM_STAGE02_MANIFEST_NEXT_FILE ) ||
! ACSRMStage02ValidateCheckpointSet ( ACSRM_STAGE02_MARKET_PREVIOUS_FILE ,
ACSRM_STAGE02_TARGET_PREVIOUS_FILE ,
ACSRM_STAGE02_MANIFEST_PREVIOUS_FILE ))
return ( false );
if ( ! ompb || ! SkillMarket . ParameterFingerprint ( market ) ||
! SkillTarget . ParameterFingerprint ( target ) || ! ompb . AppendParameterFingerprint ( posterior ) ||
market == 0 || target == 0 || posterior == 0 )
return ( false );
if ( ! FileDelete ( ACSRM_STAGE02_MANIFEST_NEXT_FILE , FILE_COMMON ) ||
! FileDelete ( ACSRM_STAGE02_TARGET_NEXT_FILE , FILE_COMMON ) ||
! FileDelete ( ACSRM_STAGE02_MARKET_NEXT_FILE , FILE_COMMON ) ||
! FileDelete ( ACSRM_STAGE02_MANIFEST_PREVIOUS_FILE , FILE_COMMON ) ||
! FileDelete ( ACSRM_STAGE02_TARGET_PREVIOUS_FILE , FILE_COMMON ) ||
! FileDelete ( ACSRM_STAGE02_MARKET_PREVIOUS_FILE , FILE_COMMON ) ||
! ACSRMStage02ResolveActiveCheckpoint ())
return ( false );
ACSRMStage02PreviousProofReady = false ;
return ( true );
}
//+-------------------------------------------------------------------+
//| Publishes only after canonical staging and deterministic reload. |
//+-------------------------------------------------------------------+
bool ACSRMStage02SaveAcceptedCheckpoint ( void )
{
ulong market = 0 , target = 0 , posterior = ulong ( 1469598103934665603 );
CNeuronOMPBOCL * ompb = GetACSRM ();
ACSRMStage02ReloadProofPassed = false ;
ACSRMStage02PreviousProofReady = false ;
ACSRMStage02SelectorActivated = false ;
if ( ! ompb || ! SkillMarket . ParameterFingerprint ( market ) ||
! SkillTarget . ParameterFingerprint ( target ) || ! ompb . AppendParameterFingerprint ( posterior ) ||
! ACSRMStage02StageCandidate () || ! ACSRMStage02PublishCandidate ())
return ( false );
if ( ! ACSRMStage02ReloadSmoke ( market , target , posterior ) || ! ACSRMStage02FinalizeSteadySelector ())
{
const bool restored = ACSRMStage02RestorePreviousSelector ();
PrintFormat ( " ACSRM_STAGE02_CANONICAL_FAIL reason=reload_or_finalize restored=%s " ,
( restored ? " true " : " false " ));
return ( false );
}
ACSRMStage02CheckpointPublished = true ;
Print ( " ACSRM_STAGE02_CANONICAL_PASS checkpoint=published " );
return ( true );
}
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Runs one frozen-graph ACSRM update. CNet owns full gradient propagation but has parameter writes disabled; the explicit ACSRM call below is therefore the only update in Stage 02. |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
bool ACSRMStage02RunBatch ( const int position , const bool source_anchor )
{
CNeuronOMPBOCL * ompb = GetACSRM ();
CNeuronBaseOCL * rank_tcm = GetRankTCM ();
if ( ! ompb || ! rank_tcm )
ReturnFalseEx ( " Stage 02 batch layers are null " );
if ( ! ConfigureACSRM ( OMPB_CALIBRATE ))
ReturnFalseEx ( " Stage 02 batch configuration " );
if ( ! ompb . SetSourceAnchor ( source_anchor ))
ReturnFalseEx ( " Stage 02 source-anchor setup " );
ompb . TrainMode ( true );
//--- Target batches publish Current only after the complete update succeeds.
if ( ! source_anchor && ! ompb . BeginCurrentTransaction ())
ReturnFalseEx ( " Stage 02 Current transaction begin " );
if ( ! SkillTrainBatch ( position ))
{
if ( source_anchor )
{
if ( ! ompb . SetSourceAnchor ( false ))
ReturnFalseEx ( " Stage 02 source-anchor cleanup after training failure " );
}
else
{
if ( ! ompb . RollbackCurrentTransaction ())
ReturnFalseEx ( " Stage 02 Current rollback after training failure " );
}
ReturnFalseEx ( " Stage 02 batch training " );
}
//--- A source anchor has already created its mean Forecast gradient. Its
//--- forward/backward phase must retain the anchor flag so Current and both
//--- regularizers remain untouched. Clear only the update guard afterwards
//--- to apply that already-computed mean posterior gradient.
if ( source_anchor )
{
const bool accepted = ompb . ForwardAccepted ();
if ( ! ompb . SetSourceAnchor ( false ))
ReturnFalseEx ( " Stage 02 source-anchor cleanup " );
if ( ! accepted )
ReturnFalseEx ( " Stage 02 source-anchor forward rejected " );
if ( ! ompb . UpdateInputWeights ( rank_tcm ))
ReturnFalseEx ( " Stage 02 source-anchor update " );
return ( true );
}
//--- A fallback output cannot update weights or advance Current accounting.
if ( ! ompb . ForwardAccepted ())
{
if ( ! ompb . RollbackCurrentTransaction ())
ReturnFalseEx ( " Stage 02 Current rollback after rejected forward " );
ReturnFalseEx ( " Stage 02 target forward rejected " );
}
if ( ! ompb . UpdateInputWeights ( rank_tcm ))
{
if ( ! ompb . RollbackCurrentTransaction ())
ReturnFalseEx ( " Stage 02 Current rollback after update failure " );
ReturnFalseEx ( " Stage 02 target update " );
}
if ( ! ompb . CommitCurrentTransaction ())
{
if ( ! ompb . RollbackCurrentTransaction ())
ReturnFalseEx ( " Stage 02 Current rollback after commit failure " );
ReturnFalseEx ( " Stage 02 Current transaction commit " );
}
return ( true );
}
//+-----------------------------------------------------------------------------------------------------------------------------+
//| Stage 02 online calibration. The outer network stays frozen; only the ACSRM posterior and alpha receive explicit updates. |
//+-----------------------------------------------------------------------------------------------------------------------------+
bool ACSRMStage02CalibrateEpoch ( void )
{
CNeuronOMPBOCL * ompb = GetACSRM ();
if ( ! ompb || ! SkillForecast ||
! SkillMarket . SetWeightsUpdate ( false ) || ! SkillTarget . SetWeightsUpdate ( false ) ||
! SkillForecast . SetCodebookUpdate ( false ) || ! SkillMarket . Clear () ||
! SkillTarget . Clear () || ! SkillForecast . ResetEpochDiagnostics () ||
! ConfigureACSRM ( OMPB_CALIBRATE ))
{
Print ( " ACSRM_STAGE02_CALIBRATION_FAIL reason=setup " );
ReturnFalse ;
}
SkillMarket . TrainMode ( true );
SkillTarget . TrainMode ( false );
ompb . TrainMode ( true );
uint target_attempts = 0 ;
uint target_updates = 0 ;
uint target_invalid = 0 ;
uint target_fallbacks_epoch = 0 ;
uint target_kl_rejects_epoch = 0 ;
uint source_attempts = 0 ;
uint source_updates = 0 ;
uint source_invalid = 0 ;
uint source_fallbacks_epoch = 0 ;
uint source_kl_rejects_epoch = 0 ;
uint target_metric_count = 0 ;
double target_kl_sum = 0.0 ;
double target_disagreement_sum = 0.0 ;
double target_alpha_prior_sum = 0.0 ;
double target_kl_max = 0.0 ;
double target_disagreement_max = 0.0 ;
double target_alpha_prior_max = 0.0 ;
const uint source_rows = uint ( ACSRMStage02ReferenceFirst - ACSRMStage02ReferenceLast + 1 );
const uint target_rows = uint ( ACSRMStage02TargetCalibrationFirst -
ACSRMStage02TargetCalibrationLast + 1 );
const uint target_quota = ACSRMStage02ProgressQuota ( target_rows , 0 , ACSRMStage02Smoke ,
ACSRMStage02SmokeLimit );
if ( source_rows == 0 )
{
Print ( " ACSRM_STAGE02_CALIBRATION_FAIL reason=source_rows " );
ReturnFalse ;
}
ACSRMStage02ShowProgress ( " calibration " , target_attempts , target_quota , target_attempts ,
target_invalid , false , false , true , true , source_updates , source_attempts ,
source_invalid , StringFormat ( " target_updates=%u " , target_updates ));
for ( int position = ACSRMStage02TargetCalibrationFirst ;
position >= ACSRMStage02TargetCalibrationLast && ! IsStopped (); position -- )
{
target_attempts ++ ;
uint target_reference_before , target_current_before ;
uint target_invalid_fallbacks_before , target_kl_rejects_before ;
float target_disagreement_before , target_kl_before , target_alpha_prior_before ;
if ( ! ReadACSRMDiagnostics ( target_reference_before , target_current_before ,
target_disagreement_before , target_kl_before ,
target_alpha_prior_before , target_invalid_fallbacks_before ,
target_kl_rejects_before ))
{
Print ( " ACSRM_STAGE02_CALIBRATION_FAIL reason=target_diagnostics_before " );
ReturnFalse ;
}
const bool target_committed = ACSRMStage02RunBatch ( position , false );
uint target_reference_after , target_current_after ;
uint target_invalid_fallbacks_after , target_kl_rejects_after ;
float target_disagreement_preupdate , target_kl_preupdate , target_alpha_prior_preupdate ;
if ( ! ReadACSRMDiagnostics ( target_reference_after , target_current_after ,
target_disagreement_preupdate , target_kl_preupdate ,
target_alpha_prior_preupdate , target_invalid_fallbacks_after ,
target_kl_rejects_after ) ||
target_invalid_fallbacks_after < target_invalid_fallbacks_before ||
target_kl_rejects_after < target_kl_rejects_before )
{
PrintFormat ( " ACSRM_STAGE02_CALIBRATION_FAIL reason=target_diagnostics_after position=%d " , position );
ReturnFalse ;
}
target_fallbacks_epoch += target_invalid_fallbacks_after - target_invalid_fallbacks_before ;
target_kl_rejects_epoch += target_kl_rejects_after - target_kl_rejects_before ;
if ( ! target_committed )
{
target_invalid ++ ;
}
else
{
if ( ! ACSRMStage02Finite ( target_disagreement_preupdate ) ||
! ACSRMStage02Finite ( target_kl_preupdate ) ||
! ACSRMStage02Finite ( target_alpha_prior_preupdate ))
{
PrintFormat ( " ACSRM_STAGE02_CALIBRATION_FAIL reason=target_diagnostics position=%d " , position );
ReturnFalse ;
}
if ( target_metric_count == 0 )
{
target_kl_max = double ( target_kl_preupdate );
target_disagreement_max = double ( target_disagreement_preupdate );
target_alpha_prior_max = double ( target_alpha_prior_preupdate );
PrintFormat ( " ACSRM_STAGE02_INITIAL_KL epoch=%u value=%.9g " , ExtACSRMCalibrationEpoch ,
double ( target_kl_preupdate ));
}
target_kl_sum += double ( target_kl_preupdate );
target_disagreement_sum += double ( target_disagreement_preupdate );
target_alpha_prior_sum += double ( target_alpha_prior_preupdate );
target_kl_max = MathMax ( target_kl_max , double ( target_kl_preupdate ));
target_disagreement_max = MathMax ( target_disagreement_max , double ( target_disagreement_preupdate ));
target_alpha_prior_max = MathMax ( target_alpha_prior_max , double ( target_alpha_prior_preupdate ));
target_metric_count ++ ;
target_updates ++ ;
if ( ACSRMStage02SourcePeriod > 0 && target_updates % ACSRMStage02SourcePeriod == 0 )
{
const int source_position = ACSRMStage02ReferenceFirst - int ( source_updates % source_rows );
uint source_reference_before , source_current_before ;
uint source_invalid_fallbacks_before , source_kl_rejects_before ;
float source_disagreement_before , source_kl_before , source_alpha_prior_before ;
if ( ! ReadACSRMDiagnostics ( source_reference_before , source_current_before ,
source_disagreement_before , source_kl_before ,
source_alpha_prior_before , source_invalid_fallbacks_before ,
source_kl_rejects_before ))
{
Print ( " ACSRM_STAGE02_CALIBRATION_FAIL reason=anchor_diagnostics_before " );
ReturnFalse ;
}
source_attempts ++ ;
const bool source_committed = ACSRMStage02RunBatch ( source_position , true );
uint source_reference_after , source_current_after ;
uint source_invalid_fallbacks_after , source_kl_rejects_after ;
float source_disagreement_after , source_kl_after , source_alpha_prior_after ;
if ( ! ReadACSRMDiagnostics ( source_reference_after , source_current_after ,
source_disagreement_after , source_kl_after ,
source_alpha_prior_after , source_invalid_fallbacks_after ,
source_kl_rejects_after ) ||
source_invalid_fallbacks_after < source_invalid_fallbacks_before ||
source_kl_rejects_after < source_kl_rejects_before )
{
PrintFormat ( " ACSRM_STAGE02_CALIBRATION_FAIL reason=anchor_diagnostics_after position=%d " , source_position );
ReturnFalse ;
}
source_fallbacks_epoch += source_invalid_fallbacks_after - source_invalid_fallbacks_before ;
source_kl_rejects_epoch += source_kl_rejects_after - source_kl_rejects_before ;
if ( ! source_committed )
{
source_invalid ++ ;
}
else
{
if ( source_current_after != source_current_before ||
source_reference_after != source_reference_before ||
! ACSRMStage02Finite ( source_disagreement_after ) ||
! ACSRMStage02Finite ( source_kl_after ) ||
! ACSRMStage02Finite ( source_alpha_prior_after ))
{
PrintFormat ( " ACSRM_STAGE02_CALIBRATION_FAIL reason=anchor_contract position=%d " , source_position );
ReturnFalse ;
}
source_updates ++ ;
}
}
}
ACSRMStage02ShowProgress ( " calibration " , target_attempts , target_quota , target_attempts ,
target_invalid , false , false , true , true , source_updates , source_attempts ,
source_invalid , StringFormat ( " target_updates=%u " , target_updates ));
if ( ACSRMStage02Smoke && target_attempts >= ACSRMStage02SmokeLimit )
break ;
}
//--- A stopped epoch is terminal and cannot be reported as calibration failure.
if ( IsStopped ())
{
ACSRMStage02ShowProgress ( " calibration " , target_attempts , target_quota , target_attempts ,
target_invalid , true , false , true , true , source_updates ,
source_attempts , source_invalid ,
StringFormat ( " target_updates=%u result=stopped " , target_updates ));
return ( false );
}
double lmix , router , trajectory , confidence , latent , observation ;
double valid , invalid , entropy , distance , inactive , recovered ;
if ( target_attempts < target_quota || target_updates == 0 || target_metric_count == 0 ||
! SkillForecast . BuildEpochCodebookDiagnostics () ||
! SkillForecast . ReadEpochDiagnostics ( lmix , router , trajectory , confidence , latent ,
observation , valid , invalid , entropy , distance ,
inactive , recovered ) ||
valid < double ( target_updates ) || ! ACSRMStage02Finite ( lmix ) ||
! ACSRMStage02Finite ( valid ) || ! ACSRMStage02Finite ( invalid ) ||
! SkillVerifyFrozenForecastExact ())
{
PrintFormat ( " ACSRM_STAGE02_CALIBRATION_FAIL reason=final_metrics target=%u source=%u " ,
target_updates , source_updates );
ACSRMStage02ShowProgress ( " calibration " , target_attempts , target_quota , target_attempts ,
target_invalid , true , false , true , true , source_updates , source_attempts ,
source_invalid , StringFormat ( " target_updates=%u result=failed " , target_updates ));
ReturnFalse ;
}
ACSRMStage02ShowProgress ( " calibration " , target_attempts , target_quota , target_attempts ,
target_invalid , true , true , true , true , source_updates , source_attempts ,
source_invalid , StringFormat ( " target_updates=%u " , target_updates ));
PrintFormat ( " ACSRM_STAGE02_CALIBRATION_PASS target_attempts=%u target_updates=%u target_invalid=%u " +
" source_attempts=%u source_updates=%u source_invalid=%u loss=%.9f reference=%u current=%u smoke=%s " ,
target_attempts , target_updates , target_invalid , source_attempts , source_updates ,
source_invalid , lmix / valid , ompb . ReferenceCount (), ompb . CurrentCount (),
( ACSRMStage02Smoke ? " true " : " false " ));
PrintFormat ( " ACSRM_STAGE02_EPOCH_PASS epoch=%u epochs=%u loss_all_calls=%.9f " +
" target_kl_preupdate_mean=%.9g target_kl_preupdate_max=%.9g " +
" target_disagreement_preupdate_mean=%.9g target_disagreement_preupdate_max=%.9g " +
" target_alpha_prior_preupdate_mean=%.9g target_alpha_prior_preupdate_max=%.9g " +
" target_failed_batches=%u source_failed_batches=%u " +
" target_fallbacks_epoch=%u source_fallbacks_epoch=%u " +
" target_kl_rejects_epoch=%u source_kl_rejects_epoch=%u source_updates=%u " ,
ExtACSRMCalibrationEpoch , ExtACSRMCalibrationEpochs , lmix / valid ,
target_kl_sum / double ( target_metric_count ), target_kl_max ,
target_disagreement_sum / double ( target_metric_count ), target_disagreement_max ,
target_alpha_prior_sum / double ( target_metric_count ), target_alpha_prior_max ,
target_invalid , source_invalid , target_fallbacks_epoch , source_fallbacks_epoch ,
target_kl_rejects_epoch , source_kl_rejects_epoch , source_updates );
return ( true );
}
//+------------------------------------------------------------------+
//| Runs chronological calibration epochs with retained parameters. |
//+------------------------------------------------------------------+
bool ACSRMStage02Calibrate ( void )
{
//--- Clear in the epoch setup retains posterior, alpha and optimizer state.
for ( uint epoch = 0 ; epoch < ExtACSRMCalibrationEpochs ; epoch ++ )
{
if ( IsStopped ())
return ( false );
ExtACSRMCalibrationEpoch = epoch + 1 ;
PrintFormat ( " ACSRM_STAGE02_EPOCH_BEGIN epoch=%u epochs=%u " ,
ExtACSRMCalibrationEpoch , ExtACSRMCalibrationEpochs );
ACSRMStage02ShowProgress ( " epoch setup " , 0 , 1 , 0 , 0 , false , false , false , false ,
0 , 0 , 0 , StringFormat ( " epoch=%u/%u " , ExtACSRMCalibrationEpoch ,
ExtACSRMCalibrationEpochs ));
if ( ! ACSRMStage02CalibrateEpoch ())
return ( false );
}
//--- Only a complete run may proceed to held-out evaluation and publication.
return ( ! IsStopped () && ExtACSRMCalibrationEpoch == ExtACSRMCalibrationEpochs );
}
//+------------------------------------------------------------------+
//| Creates the Stage 02 reference study fixtures. |
//+------------------------------------------------------------------+
bool CreateACSRMStage02ReferenceStudy ( const datetime reference_start , const datetime reference_end ,
const datetime calibration_start , const datetime calibration_end ,
const uint anchor_period , const float tau_value ,
const float lambda_dis , const float lambda_kl ,
const float lambda_alpha , const float alpha_prior ,
const float max_kl , const bool smoke , const uint smoke_limit ,
const uint calibration_epochs = 1 )
{
if ( calibration_epochs == 0 )
ReturnFalseEx ( " calibration epochs is zero " );
ExtACSRMCalibrationEpochs = ( smoke ? 1 : calibration_epochs );
ExtACSRMCalibrationEpoch = 0 ;
ACSRMStage02SignaturesCaptured = false ;
ACSRMStage02StopReported = false ;
PrintFormat ( " ACSRM_STAGE02_EPOCH_CONFIG requested=%u effective=%u smoke=%s " ,
calibration_epochs , ExtACSRMCalibrationEpochs , ( smoke ? " true " : " false " ));
ResetLastError ();
SkillReady = false ;
SkillForecast = NULL ;
SkillCompletedEpochs = 0 ;
SkillBatches = 0 ;
SkillInvalidBatches = 0 ;
if ( ! SkillLoadForecastTraining ())
{
PrintFormat ( " ACSRM_STAGE02_PREFLIGHT_FAIL reason=stage01_checkpoint error=%d " , GetLastError ());
ReturnFalse ;
}
CNeuronBaseOCL * rank_tcm = SkillMarket . Layer ( 4 );
CNeuronBaseOCL * ompb_layer = SkillMarket . Layer ( 5 );
CNeuronBaseOCL * forecast_layer = SkillMarket . Layer ( 6 );
if ( ! SkillForecast || ! rank_tcm || ! ompb_layer || ! forecast_layer ||
rank_tcm . Type () != defNeuronCogDriverRankTCM || ompb_layer . Type () != defNeuronOMPBOCL ||
forecast_layer . Type () != defNeuronScenarioForecast || ! GetACSRM () ||
! SkillValidateShapes () || ! SkillInitIndicators () ||
! SkillMarket . SetWeightsUpdate ( false ) || ! SkillTarget . SetWeightsUpdate ( false ) ||
! SkillForecast . SetGradientNormalization ( true ))
{
Print ( " ACSRM_STAGE02_PREFLIGHT_FAIL reason=loaded_contract " );
ReturnFalse ;
}
if ( ! ACSRMStage02Configure ( reference_start , reference_end , calibration_start , calibration_end ,
anchor_period , tau_value , lambda_dis , lambda_kl , lambda_alpha ,
alpha_prior , max_kl , smoke , smoke_limit ))
ReturnFalse ;
SkillTarget . TrainMode ( false );
SkillMarket . TrainMode ( true );
GetACSRM (). TrainMode ( false );
SkillReady = true ;
if ( ! EventChartCustom ( ChartID (), 1 , 0 , 0 , " ACSRM_STAGE02_INIT " ))
{
PrintFormat ( " ACSRM_STAGE02_PREFLIGHT_FAIL reason=chart_event error=%d " , GetLastError ());
SkillReady = false ;
ReturnFalse ;
}
Print ( " ACSRM_STAGE02_CHECKPOINT_PASS mode=load_only production_save=forbidden " );
return ( true );
}
//+------------------------------------------------------------------+
//| Runs the Stage 02 reference study end to end. |
//+------------------------------------------------------------------+
void RunACSRMStage02ReferenceStudy ( void )
{
ACSRMStage02ShowProgress ( " preparing " , 0 , 1 , 0 , 0 , false , false , false , false ,
0 , 0 , 0 , " " );
if ( ! SkillReady || ! ACSRMStage02PrepareData () || ! ACSRMStage02CollectReference ())
{
if ( ACSRMStage02StopHandled ( " reference " ))
return ;
ACSRMStage02ShowProgress ( " preparing " , 0 , 1 , 0 , 0 , true , false , false , false ,
0 , 0 , 0 ,
" result=failed " );
ExpertRemove ();
return ;
}
if ( ! SkillForecast . SetCodebookUpdate ( false ))
{
Print ( " ACSRM_STAGE02_REFERENCE_FAIL reason=codebook_freeze " );
ExpertRemove ();
return ;
}
if ( ! SkillCaptureFrozenForecastBaseline ())
{
Print ( " ACSRM_STAGE02_REFERENCE_FAIL reason=frozen_baseline " );
ExpertRemove ();
return ;
}
double source_loss = 0.0 , target_loss = 0.0 ;
uint source_valid , source_invalid , target_valid , target_invalid ;
const bool source_ok = ACSRMStage02Baseline ( ACSRMStage02SourceEvalFirst ,
ACSRMStage02SourceEvalLast , " source_eval " ,
source_loss , source_valid , source_invalid );
if ( ACSRMStage02StopHandled ( " baseline_source " ))
return ;
const bool target_ok = ( source_ok &&
ACSRMStage02Baseline ( ACSRMStage02TargetEvalFirst ,
ACSRMStage02TargetEvalLast , " target_eval " ,
target_loss , target_valid , target_invalid ));
if ( ACSRMStage02StopHandled ( " baseline_target " ))
return ;
bool calibration_ok = false ;
bool evaluation_ok = false ;
bool acceptance_ok = false ;
bool checkpoint_saved = false ;
double pre_source_inference_loss = 0.0 , pre_target_inference_loss = 0.0 ;
uint pre_source_inference_valid = 0 , pre_source_inference_invalid = 0 ;
uint pre_target_inference_valid = 0 , pre_target_inference_invalid = 0 ;
double post_source_inference_loss = 0.0 , post_target_inference_loss = 0.0 ;
uint post_source_inference_valid = 0 , post_source_inference_invalid = 0 ;
uint post_target_inference_valid = 0 , post_target_inference_invalid = 0 ;
if ( target_ok && SkillVerifyFrozenForecastExact () && ACSRMStage02CaptureSignatures ())
{
PrintFormat ( " ACSRM_STAGE02_REFERENCE_READY source_loss=%.9f target_loss=%.9f source_valid=%u target_valid=%u " ,
source_loss , target_loss , source_valid , target_valid );
const bool pre_source_ok =
( ConfigureACSRM ( OMPB_INFERENCE ) &&
ACSRMStage02InferenceEvaluation ( ACSRMStage02SourceEvalFirst , ACSRMStage02SourceEvalLast ,
" pre_source_inference " , pre_source_inference_loss ,
pre_source_inference_valid , pre_source_inference_invalid ));
if ( ACSRMStage02StopHandled ( " pre_inference_source " ))
return ;
const bool pre_target_ok =
( pre_source_ok && ConfigureACSRM ( OMPB_INFERENCE ) &&
ACSRMStage02InferenceEvaluation ( ACSRMStage02TargetEvalFirst , ACSRMStage02TargetEvalLast ,
" pre_target_inference " , pre_target_inference_loss ,
pre_target_inference_valid , pre_target_inference_invalid ));
if ( ACSRMStage02StopHandled ( " pre_inference_target " ))
return ;
const bool pre_signatures_ok = ACSRMStage02VerifyPreCalibrationSignatures ();
const bool pre_inference_ok = ( pre_source_ok && pre_target_ok && pre_signatures_ok );
PrintFormat ( " ACSRM_STAGE02_PREUPDATE_INFERENCE mode=inference source_loss=%.9f " +
" target_loss=%.9f source_valid=%u source_invalid=%u target_valid=%u " +
" target_invalid=%u signatures_unchanged=%s status=%s " ,
pre_source_inference_loss , pre_target_inference_loss , pre_source_inference_valid ,
pre_source_inference_invalid , pre_target_inference_valid , pre_target_inference_invalid ,
( pre_signatures_ok ? " true " : " false " ),
( pre_inference_ok ? " pass " : " fail " ));
if ( pre_inference_ok && SkillForecast . SetGradientNormalization ( false ))
{
Print ( " ACSRM_STAGE02_GRADIENT_MODE mode=raw_forecast " );
calibration_ok = ACSRMStage02Calibrate ();
if ( ACSRMStage02StopHandled ( " calibration " ))
return ;
}
else
{
if ( ! pre_inference_ok )
Print ( " ACSRM_STAGE02_CALIBRATION_FAIL reason=preupdate_inference " );
else
Print ( " ACSRM_STAGE02_CALIBRATION_FAIL reason=gradient_mode " );
}
if ( ! SkillForecast . SetGradientNormalization ( true ))
{
Print ( " ACSRM_STAGE02_CALIBRATION_FAIL reason=gradient_mode_restore " );
calibration_ok = false ;
}
}
else
Print ( " ACSRM_STAGE02_REFERENCE_FAIL reason=baseline_frozen_or_signatures " );
//--- Evaluation is intentionally distinct from the frozen BYPASS baselines.
//--- It uses posterior-mean ACSRM inference and must be complete before any
//--- candidate checkpoint can be staged.
if ( calibration_ok && ConfigureACSRM ( OMPB_INFERENCE ))
{
const bool source_inference_ok =
ACSRMStage02InferenceEvaluation ( ACSRMStage02SourceEvalFirst , ACSRMStage02SourceEvalLast ,
" source_inference " , post_source_inference_loss ,
post_source_inference_valid , post_source_inference_invalid );
if ( ACSRMStage02StopHandled ( " post_inference_source " ))
return ;
const bool target_inference_ok =
( source_inference_ok &&
ACSRMStage02InferenceEvaluation ( ACSRMStage02TargetEvalFirst , ACSRMStage02TargetEvalLast ,
" target_inference " , post_target_inference_loss ,
post_target_inference_valid , post_target_inference_invalid ));
if ( ACSRMStage02StopHandled ( " post_inference_target " ))
return ;
evaluation_ok = ( target_inference_ok && ACSRMStage02VerifySignatures ());
if ( evaluation_ok )
{
const double continuation_source_denominator = MathAbs ( pre_source_inference_loss );
const double continuation_target_denominator = MathAbs ( pre_target_inference_loss );
const bool continuation_finite =
( ACSRMStage02Finite ( pre_source_inference_loss ) &&
ACSRMStage02Finite ( pre_target_inference_loss ) &&
ACSRMStage02Finite ( post_source_inference_loss ) &&
ACSRMStage02Finite ( post_target_inference_loss ) &&
continuation_source_denominator > 0.0 && continuation_target_denominator > 0.0 );
const double continuation_target_improvement =
( continuation_finite ?
( pre_target_inference_loss - post_target_inference_loss ) /
continuation_target_denominator : 0.0 );
const double continuation_source_degradation =
( continuation_finite ?
( post_source_inference_loss - pre_source_inference_loss ) /
continuation_source_denominator : 0.0 );
const bool continuation_status =
( continuation_finite && pre_source_inference_valid > 0 &&
pre_target_inference_valid > 0 && post_source_inference_valid > 0 &&
post_target_inference_valid > 0 );
PrintFormat ( " ACSRM_STAGE02_CONTINUATION target_improvement=%.8f " +
" source_degradation=%.8f pre_source_loss=%.9f " +
" post_source_loss=%.9f pre_target_loss=%.9f " +
" post_target_loss=%.9f pre_source_valid=%u " +
" pre_source_invalid=%u post_source_valid=%u " +
" post_source_invalid=%u pre_target_valid=%u " +
" pre_target_invalid=%u post_target_valid=%u " +
" post_target_invalid=%u finite=%s status=%s " ,
continuation_target_improvement , continuation_source_degradation ,
pre_source_inference_loss , post_source_inference_loss ,
pre_target_inference_loss , post_target_inference_loss ,
pre_source_inference_valid , pre_source_inference_invalid ,
post_source_inference_valid , post_source_inference_invalid ,
pre_target_inference_valid , pre_target_inference_invalid ,
post_target_inference_valid , post_target_inference_invalid ,
( continuation_finite ? " true " : " false " ),
( continuation_status ? " pass " : " fail " ));
acceptance_ok = ACSRMStage02Accept ( source_loss , target_loss ,
post_source_inference_loss ,
post_target_inference_loss ,
post_source_inference_valid ,
post_target_inference_valid );
}
else
Print ( " ACSRM_STAGE02_ACCEPTANCE_FAIL reason=evaluation_or_signatures " );
}
else
Print ( " ACSRM_STAGE02_ACCEPTANCE_FAIL reason=calibration " );
//--- Switching to deterministic inference and clearing ACSRM drops only Current
//--- and transient buffers. Clear deliberately preserves posterior parameters.
CNeuronOMPBOCL * ompb = GetACSRM ();
const bool finalized = ( ompb && ConfigureACSRM ( OMPB_INFERENCE ) && ompb . Clear ());
if ( ! finalized )
Print ( " ACSRM_STAGE02_CALIBRATION_FAIL reason=finalize " );
else
{
ompb . TrainMode ( false );
//--- Quality acceptance is diagnostic; unchanged frozen parameters remain mandatory.
const bool checkpoint_integrity = ACSRMStage02VerifySignatures ();
ACSRMStage02ShowProgress ( " saving " , 0 , 1 , 0 , 0 , false , false , false , false ,
0 , 0 , 0 ,
StringFormat ( " accepted=%s smoke=%s " ,
( acceptance_ok ? " true " : " false " ),
( ACSRMStage02Smoke ? " true " : " false " )));
if ( calibration_ok && checkpoint_integrity && ! ACSRMStage02Smoke && ! IsStopped ())
checkpoint_saved = ACSRMStage02SaveAcceptedCheckpoint ();
else
if ( ACSRMStage02Smoke )
PrintFormat ( " ACSRM_STAGE02_TRANSACTION_SKIPPED reason=smoke accepted=%s " ,
( acceptance_ok ? " true " : " false " ));
else
Print ( " ACSRM_STAGE02_TRANSACTION_SKIPPED reason=integrity " );
ACSRMStage02ShowProgress ( " saving " , ( checkpoint_saved ? 1 : 0 ), 1 , 1 , 0 , true ,
checkpoint_saved || ACSRMStage02Smoke , false , false ,
0 , 0 , 0 ,
StringFormat ( " accepted=%s saved=%s smoke=%s " ,
( acceptance_ok ? " true " : " false " ),
( checkpoint_saved ? " true " : " false " ),
( ACSRMStage02Smoke ? " true " : " false " )));
CNeuronOMPBOCL * final_ompb = GetACSRM ();
if ( ! final_ompb )
Print ( " ACSRM_STAGE02_INFERENCE_READY_FAIL reason=ompb_after_checkpoint " );
else
PrintFormat ( " ACSRM_STAGE02_INFERENCE_READY calibration=%s evaluation=%s accepted=%s saved=%s " +
" reference=%u current=%u " , ( calibration_ok ? " true " : " false " ),
( evaluation_ok ? " true " : " false " ), ( acceptance_ok ? " true " : " false " ),
( checkpoint_saved ? " true " : " false " ), final_ompb . ReferenceCount (),
final_ompb . CurrentCount ());
}
SkillMarket . TrainMode ( false );
SkillTarget . TrainMode ( false );
const uint final_rows = uint ( ACSRMStage02TargetEvalFirst - ACSRMStage02TargetEvalLast + 1 );
const uint final_quota = ACSRMStage02ProgressQuota ( final_rows , 0 , ACSRMStage02Smoke ,
ACSRMStage02SmokeLimit );
string final_detail = StringFormat ( " source_degradation=n/a target_improvement=n/a accepted=%s saved=%s " ,
( acceptance_ok ? " true " : " false " ),
( checkpoint_saved ? " true " : " false " ));
if ( target_ok && evaluation_ok )
{
const double source_denominator = MathMax ( MathAbs ( source_loss ), 1.0e-12 );
const double target_denominator = MathMax ( MathAbs ( target_loss ), 1.0e-12 );
const double source_degradation = ( post_source_inference_loss - source_loss ) /
source_denominator ;
const double target_improvement = ( target_loss - post_target_inference_loss ) /
target_denominator ;
final_detail = StringFormat ( " source_degradation=%.2f%% target_improvement=%.2f%% accepted=%s saved=%s " ,
100.0 * source_degradation , 100.0 * target_improvement ,
( acceptance_ok ? " true " : " false " ),
( checkpoint_saved ? " true " : " false " ));
}
const uint final_done = ( ACSRMStage02Smoke ? post_target_inference_valid :
post_target_inference_valid + post_target_inference_invalid );
ACSRMStage02ShowProgress ( " final " , final_done , final_quota ,
post_target_inference_valid + post_target_inference_invalid ,
post_target_inference_invalid , true , finalized && calibration_ok && evaluation_ok ,
false , false , post_source_inference_valid ,
post_source_inference_valid + post_source_inference_invalid ,
post_source_inference_invalid ,
final_detail );
ExpertRemove ();
}
//+------------------------------------------------------------------+
//| Creates or initializes SkillForecastStudy. |
//+------------------------------------------------------------------+
bool CreateSkillForecastStudy ( void )
{
ResetLastError ();
SkillCompletedEpochs = 0 ;
SkillBatches = 0 ;
SkillInvalidBatches = 0 ;
//--- Always prefer an existing compatible checkpoint for continued training.
//--- Only a clean initial checkpoint miss falls back to a new randomized graph.
//--- Partial, incompatible and transaction states remain fail-closed.
bool resumed = SkillLoadForecastTraining ();
//--- Function if.
if ( ! resumed )
{
if ( ACSRMStage02RecoveryFailed )
{
Print ( " ACSRM checkpoint recovery=FAIL before=training_create fail_closed=true " );
ReturnFalse ;
}
const int load_error = GetLastError ();
SkillForecast = NULL ;
PrintFormat ( " %s init: forecast restore=FAIL error=%d; creating new random model " , ACSRM_LOG_PREFIX , load_error );
ResetLastError ();
//--- Function if.
if ( ! SkillCreateNetworks ())
{
PrintFormat ( " %s init: forecast create=FAIL error=%d " , ACSRM_LOG_PREFIX , GetLastError ());
ReturnFalse ;
}
}
//--- Function if.
if ( ! SkillValidateShapes ())
{
PrintFormat ( " %s init: shapes=FAIL " , ACSRM_LOG_PREFIX );
ReturnFalse ;
}
//--- Stage 01 keeps ACSRM a non-trainable identity bridge. Its posterior,
//--- alpha and both histories remain untouched while ScenarioForecast trains.
CNeuronOMPBOCL * ompb = GetACSRM ();
if ( ! ompb || ! ConfigureACSRM ( OMPB_BYPASS ))
ReturnFalse ;
ompb . TrainMode ( false );
if ( ! SkillInitIndicators ())
{
PrintFormat ( " %s init: indicators=FAIL error=%d " , ACSRM_LOG_PREFIX , GetLastError ());
ReturnFalse ;
}
PrintFormat ( " %s init: forecast=%s completed_epochs=%u batches=%I64u invalid=%I64u " , ACSRM_LOG_PREFIX ,
( resumed ? " RESUMED " : " NEW " ), SkillCompletedEpochs , SkillBatches , SkillInvalidBatches );
SkillReady = true ;
//--- Function if.
if ( ! EventChartCustom ( ChartID (), 1 , 0 , 0 , " Init " ))
{
PrintFormat ( " %s init: chart event=FAIL error=%d " , ACSRM_LOG_PREFIX , GetLastError ());
ReturnFalse ;
}
return ( true );
}
//+------------------------------------------------------------------+
//| Implements ReleaseSkillForecastStudy. |
//+------------------------------------------------------------------+
void ReleaseSkillForecastStudy ( const int reason )
{
//--- Complete epochs save transactionally in TrainSkillForecast(). Never
//--- overwrite a valid checkpoint with a fresh, partial or failed run here.
SkillForecast = NULL ;
SkillReady = false ;
}
//+------------------------------------------------------------------+
//| Implements SkillPrepareLatentTarget. |
//+------------------------------------------------------------------+
bool SkillPrepareLatentTarget ( CBufferFloat * market_latent )
{
if ( ! market_latent || market_latent . Total () != ( BarDescr * EmbeddingSize ) || market_latent . GetIndex () < 0 ||
SkillFuture . Total () != ( NForecast * BarDescr ) || SkillFuture . GetIndex () < 0 ||
SkillLatentTarget . Total () != ( BarDescr * NForecast * EmbeddingSize ) || SkillLatentTarget . GetIndex () < 0 ||
SkillLatentDelta . Total () != ( BarDescr * NForecast * EmbeddingSize ) || SkillLatentDelta . GetIndex () < 0 )
ReturnFalse ;
//--- SkillFuture is decoder-native [H,B]. The existing transpose changes
//--- only this device layout to Target-native [B,H]; Market keeps its original
//--- [BarDescr,HistoryBars] representation and is never transposed.
if ( ! SkillFutureView . Bind ( GetPointer ( SkillFuture )))
ReturnFalse ;
const bool transposed = SkillFutureTranspose . FeedForward ( SkillFutureView . AsObject ());
SkillFutureView . Unbind ();
if ( ! transposed )
ReturnFalse ;
CNeuronBaseOCL * target_input = SkillTarget . Layer ( 0 );
CNeuronBaseOCL * future_latent = SkillTarget . Layer ( - 1 );
if ( ! target_input || target_input . getOutput (). Total () != ( BarDescr * NForecast ) ||
target_input . getOutputIndex () < 0 ||
! SkillDevice . Copy ( SkillFutureTranspose . getOutput (), target_input . getOutput (),
BarDescr * NForecast ) ||
! SkillTarget . feedForward ( GetPointer ( SkillTarget ), 0 , ( CBufferFloat * ) NULL ))
ReturnFalse ;
if ( ! future_latent || future_latent . getOutput (). Total () != ( BarDescr * NForecast * EmbeddingSize ) ||
future_latent . getOutputIndex () < 0 )
ReturnFalse ;
//--- Forecast loss compares full future Z against full generator Z. Codebook
//--- EMA alone receives the detached delta future-current.
if ( ! SkillDevice . Copy ( future_latent . getOutput (), GetPointer ( SkillLatentTarget ),
BarDescr * NForecast * EmbeddingSize ) ||
! SkillDevice . Subtract ( GetPointer ( SkillLatentZero ), market_latent ,
GetPointer ( SkillLatentNegativeMarket ), EmbeddingSize ))
ReturnFalse ;
if ( ! SkillDevice . BroadcastSum ( GetPointer ( SkillLatentNegativeMarket ),
future_latent . getOutput (), GetPointer ( SkillLatentDelta ),
EmbeddingSize , BarDescr ))
ReturnFalse ;
return ( true );
}
//+------------------------------------------------------------------+
//| Implements SkillProbeNet. |
//+------------------------------------------------------------------+
bool SkillProbeNet ( CNet & net , CBufferFloat * probe_state , double & latent [], const bool restore_training )
{
if ( ! probe_state || ArrayResize ( latent , ( BarDescr * EmbeddingSize )) != ( BarDescr * EmbeddingSize ))
ReturnFalse ;
//--- Measure both epoch boundaries in inference mode from an empty recurrent
//--- state. Clear again afterwards so the diagnostic forward cannot seed the
//--- first training batch or inherit the final training batch state.
if ( ! net . TrainMode ( false ))
ReturnFalse ;
bool result = net . Clear ();
if ( result )
result = net . feedForward ( probe_state , 1 , false , ( CBufferFloat * ) NULL );
//--- Get the live OpenCL output owned by RankTCM. GetLayerOutput() copies
//--- values into a new host CBufferFloat and therefore cannot be BufferRead().
CNeuronBaseOCL * latent_layer = net . Layer ( 4 );
CBufferFloat * output = ( latent_layer ? latent_layer . getOutput () : NULL );
CNeuronBaseOCL * bridge_layer = net . Layer ( 5 );
CNeuronOMPBOCL * bridge = ( bridge_layer && bridge_layer . Type () == defNeuronOMPBOCL ?
( CNeuronOMPBOCL * ) bridge_layer : NULL );
CBufferFloat * bridge_output = ( bridge ? bridge . getOutput () : NULL );
if ( result )
result = ( bridge && bridge . Mode () == OMPB_BYPASS && output != NULL && bridge_output != NULL &&
output . GetIndex () >= 0 && bridge_output . GetIndex () >= 0 && output . BufferRead () &&
bridge_output . BufferRead () && output . Total () == ( BarDescr * EmbeddingSize ) &&
bridge_output . Total () == ( BarDescr * EmbeddingSize ));
for ( uint d = 0 ; result && d < ( BarDescr * EmbeddingSize ); d ++ )
{
latent [ d ] = double ( output [ d ]);
if ( ! MathIsValidNumber ( latent [ d ]) || output [ d ] != bridge_output [ d ])
result = false ;
}
const bool cleared = net . Clear ();
const bool restored = net . TrainMode ( restore_training );
return ( result && cleared && restored );
}
//+------------------------------------------------------------------+
//| Implements SkillProbeDrift. |
//+------------------------------------------------------------------+
double SkillProbeDrift ( const double & before [], const double & after [])
{
if ( ArraySize ( before ) != ( BarDescr * EmbeddingSize ) || ArraySize ( after ) != ( BarDescr * EmbeddingSize ))
return ( DBL_MAX );
double square_sum = 0 ;
for ( uint d = 0 ; d < ( BarDescr * EmbeddingSize ); d ++ )
{
const double delta = after [ d ] - before [ d ];
square_sum += delta * delta ;
}
return ( MathSqrt ( square_sum / double (( BarDescr * EmbeddingSize ))));
}
//+------------------------------------------------------------------+
//| Implements FormatScenarioCounts. |
//+------------------------------------------------------------------+
string FormatScenarioCounts ( const uint & counts [])
{
string result = " [ " ;
for ( int i = 0 ; i < ArraySize ( counts ); i ++ )
result += ( i > 0 ? " , " : " " ) + IntegerToString (( int ) counts [ i ]);
return ( result + " ] " );
}
//+------------------------------------------------------------------+
//| Smoke-only responsibility audit; all tensor reads are host-side |
//+------------------------------------------------------------------+
bool SkillCollectRecoverySmoke ( ulong & hits [], double & responsibility_sum [], double & scale_sum [],
double & absolute_sum [], double & absolute_over_scale_sum [])
{
if ( ! SkillForecast || ArraySize ( hits ) != NScenarios || ArraySize ( responsibility_sum ) != NScenarios ||
ArraySize ( scale_sum ) != NScenarios || ArraySize ( absolute_sum ) != NScenarios ||
ArraySize ( absolute_over_scale_sum ) != NScenarios )
ReturnFalse ;
CBufferFloat * responsibilities = SkillForecast . GetResponsibilities ();
CBufferFloat * confidence = SkillForecast . GetConfidence ();
CBufferFloat * target = SkillForecast . GetTarget ();
CBufferFloat * forecast = SkillForecast . getOutput ();
const int target_total = BarDescr * NForecast * EmbeddingSize ;
const int confidence_total = NScenarios * BarDescr * NForecast ;
if ( ! responsibilities || ! confidence || ! target || ! forecast || responsibilities . Total () != NScenarios ||
confidence . Total () != confidence_total || target . Total () != target_total ||
forecast . Total () != NScenarios * target_total || ! responsibilities . BufferRead () || ! confidence . BufferRead () ||
! target . BufferRead () || ! forecast . BufferRead ())
ReturnFalse ;
for ( uint scenario = 0 ; scenario < NScenarios ; scenario ++ )
{
const double responsibility = responsibilities [ scenario ];
if ( ! MathIsValidNumber ( responsibility ) || responsibility < 0.0 )
ReturnFalse ;
if ( responsibility > 0.0 )
hits [ scenario ] ++ ;
responsibility_sum [ scenario ] += responsibility ;
if ( responsibility <= 0.0 )
continue ;
for ( int coordinate = 0 ; coordinate < target_total ; coordinate ++ )
{
const int token = coordinate / EmbeddingSize ;
const double scale = MathMax ( 0.001 , MathMin ( 10.0 , confidence [ int ( scenario ) * BarDescr * NForecast + token ]));
const double absolute_error = MathAbs ( target [ coordinate ] -
forecast [ int ( scenario ) * target_total + coordinate ]);
if ( ! MathIsValidNumber ( scale ) || ! MathIsValidNumber ( absolute_error ))
ReturnFalse ;
scale_sum [ scenario ] += responsibility * scale ;
absolute_sum [ scenario ] += responsibility * absolute_error ;
absolute_over_scale_sum [ scenario ] += responsibility * absolute_error / scale ;
}
}
return ( true );
}
//+------------------------------------------------------------------+
//| Logs the recovery-smoke responsibility and scale statistics. |
//+------------------------------------------------------------------+
bool SkillLogRecoverySmoke ( const ulong & hits [], const double & responsibility_sum [],
const double & scale_sum [], const double & absolute_sum [],
const double & absolute_over_scale_sum [],
const uint & recovered [], const uint & age_zero [])
{
if ( ! SkillForecast || ArraySize ( hits ) != NScenarios || ArraySize ( responsibility_sum ) != NScenarios ||
ArraySize ( scale_sum ) != NScenarios || ArraySize ( absolute_sum ) != NScenarios ||
ArraySize ( absolute_over_scale_sum ) != NScenarios ||
ArraySize ( recovered ) != NScenarios || ArraySize ( age_zero ) != NScenarios )
ReturnFalse ;
CScenarioCodebook * codebook = SkillForecast . GetCodebook ();
if ( ! codebook )
ReturnFalse ;
CBufferFloat * ages = codebook . GetInactivityAge ();
CBufferFloat * counts = codebook . GetEMACounts ();
CBufferFloat * usage = codebook . GetUsage ();
CBufferFloat * inactive = codebook . GetInactive ();
if ( ! ages || ! counts || ! usage || ! inactive || ages . Total () != NScenarios || counts . Total () != NScenarios ||
usage . Total () != NScenarios || inactive . Total () != NScenarios || ! ages . BufferRead () || ! counts . BufferRead () ||
! usage . BufferRead () || ! inactive . BufferRead ())
ReturnFalse ;
for ( uint scenario = 0 ; scenario < NScenarios ; scenario ++ )
{
if ( ! MathIsValidNumber ( ages [ scenario ]) || ! MathIsValidNumber ( counts [ scenario ]) ||
! MathIsValidNumber ( usage [ scenario ]) || ! MathIsValidNumber ( inactive [ scenario ]))
ReturnFalse ;
const double denominator = responsibility_sum [ scenario ] * double ( BarDescr * NForecast * EmbeddingSize );
const double mean_scale = ( denominator > 0.0 ? scale_sum [ scenario ] / denominator : 0.0 );
const double mean_absolute = ( denominator > 0.0 ? absolute_sum [ scenario ] / denominator : 0.0 );
const double mean_absolute_over_scale = ( denominator > 0.0 ?
absolute_over_scale_sum [ scenario ] / denominator : 0.0 );
PrintFormat ( " %s smoke state=%d hits=%I64u age_zero=%u responsibility_sum=%.8f mean_U=%.8f " +
" mean_abs_error=%.8f mean_abs_over_U=%.8f final_age=%.0f ema_count=%.8f " +
" usage=%.8f inactive=%d recovered=%u " ,
ACSRM_LOG_PREFIX , scenario , hits [ scenario ], age_zero [ scenario ],
responsibility_sum [ scenario ], mean_scale , mean_absolute ,
mean_absolute_over_scale , ages [ scenario ], counts [ scenario ], usage [ scenario ],
int ( MathRound ( inactive [ scenario ])), recovered [ scenario ]);
}
return ( true );
}
//+------------------------------------------------------------------+
//| Implements SkillTrainBatch. |
//+------------------------------------------------------------------+
bool SkillTrainBatch ( const int position )
{
//--- Build exactly the same X<=t state as CreateBuffers(position,...,future),
//--- but do not touch any future sample until Market and Scenario are complete.
if ( ! CreateBuffers ( position + NForecast , GetPointer ( SkillState ), GetPointer ( SkillTime ), NULL ))
ReturnFalse ;
if ( ! SkillMarket . feedForward ( GetPointer ( SkillState ), 1 , false , ( CBufferFloat * ) NULL ))
ReturnFalse ;
//--- Target supervision remains bound to raw RankTCM z_t. ACSRM sits between
//--- it and ScenarioForecast, so relative indexing would select the bridge.
CNeuronBaseOCL * market_layer = GetRankTCM ();
CBufferFloat * market_latent = ( market_layer ? market_layer . getOutput () : NULL );
if ( ! market_latent ||
market_latent . Total () != ( BarDescr * EmbeddingSize ) || market_latent . GetIndex () < 0 )
ReturnFalse ;
//--- Only now is any future window presented to the detached Target Encoder.
if ( ! CreateBuffers ( position , GetPointer ( SkillState ), GetPointer ( SkillTime ), GetPointer ( SkillFuture )))
ReturnFalse ;
if ( ! SkillPrepareLatentTarget ( market_latent ))
ReturnFalse ;
const ulong responsibility_started = GetMicrosecondCount ();
//--- Function if.
if ( ! SkillForecast . BuildResponsibilities ( GetPointer ( SkillLatentTarget ), GetPointer ( SkillLatentDelta )))
{
//--- Commit only a completed device validation. Infrastructure failures leave
//--- the transient control-valid flag false, so stale batch_control is ignored.
if ( SkillForecast . LastBatchControlValid () && ! SkillForecast . CommitBatchDiagnostics ())
PrintFormat ( " %s -> %d invalid diagnostic commit failed " , __FUNCTION__ , __LINE__ );
ReturnFalse ;
}
const ulong responsibility_elapsed = GetMicrosecondCount () - responsibility_started ;
if ( ! SkillForecast . CommitBatchDiagnostics ())
ReturnFalse ;
//--- The unified CNet updates ScenarioForecast and all preceding Market layers.
if ( ! SkillMarket . backPropGradient (( CBufferFloat * ) NULL , ( CBufferFloat * ) NULL , - 1 , true ))
ReturnFalse ;
SkillResponsibilityMicroseconds += responsibility_elapsed ;
return ( true );
}
//+-------------------------------------------------------------------+
//| Current-epoch progress uses the existing 12-float device summ... |
//+-------------------------------------------------------------------+
void SkillShowForecastProgress ( const double percent , const ulong failed_batches )
{
double lmix , router , trajectory , confidence , latent , observation ;
double valid , invalid , entropy , distance , inactive , recovered ;
if ( SkillForecast && SkillForecast . ReadEpochDiagnostics ( lmix , router , trajectory , confidence ,
latent , observation , valid , invalid , entropy , distance , inactive , recovered ) && valid > 0.0 )
{
Comment ( StringFormat ( " %s Forecast %6.2f%% L_mix %.8f latent %.8f invalid(current epoch) %I64u " ,
ACSRM_LOG_PREFIX , percent , lmix / valid , latent / valid , failed_batches ));
return ;
}
Comment ( StringFormat ( " %s Forecast %6.2f%% L_mix n/a latent n/a invalid(current epoch) %I64u " ,
ACSRM_LOG_PREFIX , percent , failed_batches ));
}
//+------------------------------------------------------------------+
//| Implements TrainSkillForecast. |
//+------------------------------------------------------------------+
2026-09-29 00:57:33 +03:00
#ifndef Online
2026-09-28 01:52:58 +03:00
void TrainSkillForecast ( void )
{
if ( ! SkillReady )
return ;
int start = iBarShift ( Symb . Name (), TimeFrame , Start );
int end = iBarShift ( Symb . Name (), TimeFrame , End );
int bars = CopyRates ( Symb . Name (), TimeFrame , 0 , start , Rates );
if ( bars <= 0 || ! RSI . BufferResize ( bars ) || ! CCI . BufferResize ( bars ) ||
! ATR . BufferResize ( bars ) || ! MACD . BufferResize ( bars ))
{ PrintFormat ( " %s -> %d " , __FUNCTION__ , __LINE__ ); return ; }
int wait = - 1 ;
bool calculated = false ;
do
{
calculated = ( RSI . BarsCalculated () >= bars && CCI . BarsCalculated () >= bars &&
ATR . BarsCalculated () >= bars && MACD . BarsCalculated () >= bars );
Sleep ( 100 );
wait ++ ;
}
while ( ! calculated && wait < 100 );
if ( ! calculated )
{ PrintFormat ( " %s -> %d data unavailable " , __FUNCTION__ , __LINE__ ); return ; }
RSI . Refresh ();
CCI . Refresh ();
ATR . Refresh ();
MACD . Refresh ();
if ( ! ArraySetAsSeries ( Rates , true ))
{ PrintFormat ( " %s -> %d data unavailable " , __FUNCTION__ , __LINE__ ); return ; }
bars -= end + HistoryBars + NForecast ;
if ( bars < 0 )
{ PrintFormat ( " %s -> %d insufficient history " , __FUNCTION__ , __LINE__ ); return ; }
//--- With a forecast buffer CreateBuffers(position,...) starts Market state at
//--- position+H. Reproduce that exact X<=t mapping with NULL, which also keeps
//--- every future target out of the diagnostic input.
const int probe_position = MathMax ( end - 1 , 0 );
if ( ! CreateBuffers ( probe_position + NForecast , GetPointer ( SkillProbeState ),
//--- Gets Pointer.
GetPointer ( SkillProbeTime ), NULL ))
{ PrintFormat ( " %s -> %d fixed probe unavailable " , __FUNCTION__ , __LINE__ ); return ; }
uint ticks = GetTickCount ();
bool stop = false ;
const uint passes = ( SkillRecoverySmoke ? 1 : uint ( MathMax ( Epochs , 0 )));
const ulong smoke_limit = ( SkillRecoverySmoke ? ulong ( MathMax ( 1 , int ( SkillRecoverySmokeBatches ))) : 0 );
for ( uint pass = 0 ; pass < passes && ! IsStopped () && ! stop ; pass ++ )
{
const uint epoch = SkillCompletedEpochs ;
ulong epoch_failures = 0 ;
ulong smoke_batches = 0 ;
ulong smoke_hits [];
double smoke_responsibility_sum [];
double smoke_scale_sum [];
double smoke_absolute_sum [];
double smoke_absolute_over_scale_sum [];
if ( SkillRecoverySmoke &&
( ArrayResize ( smoke_hits , NScenarios ) != NScenarios ||
ArrayResize ( smoke_responsibility_sum , NScenarios ) != NScenarios ||
ArrayResize ( smoke_scale_sum , NScenarios ) != NScenarios ||
ArrayResize ( smoke_absolute_sum , NScenarios ) != NScenarios ||
//--- All temporary smoke buffers must resize to NScenarios.
ArrayResize ( smoke_absolute_over_scale_sum , NScenarios ) != NScenarios ))
{ PrintFormat ( " %s -> %d smoke counter allocation failed " , __FUNCTION__ , __LINE__ ); break ; }
ArrayInitialize ( smoke_hits , 0 );
ArrayInitialize ( smoke_responsibility_sum , 0.0 );
ArrayInitialize ( smoke_scale_sum , 0.0 );
ArrayInitialize ( smoke_absolute_sum , 0.0 );
ArrayInitialize ( smoke_absolute_over_scale_sum , 0.0 );
if ( ! SkillMarket . Clear () || ! SkillTarget . Clear ())
{ PrintFormat ( " %s -> %d clear failed " , __FUNCTION__ , __LINE__ ); break ; }
SkillTarget . TrainMode ( false );
double latent_before [], latent_after [];
if ( ! SkillProbeNet ( SkillMarket , GetPointer ( SkillProbeState ), latent_before , true ) ||
! ConfigureACSRM ( OMPB_BYPASS ))
{ PrintFormat ( " %s -> %d fixed probe forward failed " , __FUNCTION__ , __LINE__ ); stop = true ; break ; }
if ( ! SkillForecast . ResetEpochDiagnostics ())
{ PrintFormat ( " %s -> %d diagnostic reset failed " , __FUNCTION__ , __LINE__ ); stop = true ; break ; }
uint ompb_reference_count_before , ompb_current_count_before ;
uint ompb_invalid_fallbacks_before , ompb_kl_rejects_before ;
float ompb_disagreement_before , ompb_kl_before , ompb_alpha_prior_before ;
if ( ! ReadACSRMDiagnostics ( ompb_reference_count_before , ompb_current_count_before ,
ompb_disagreement_before , ompb_kl_before ,
ompb_alpha_prior_before , ompb_invalid_fallbacks_before ,
ompb_kl_rejects_before ))
{
PrintFormat ( " ACSRM_STAGE01_BYPASS_DIAGNOSTIC_FAIL scope=epoch-%u reason=read_before " , epoch + 1 );
stop = true ;
break ;
}
SkillResponsibilityMicroseconds = 0 ;
for ( int position = start - HistoryBars - NForecast - 1 ; position >= end && ! IsStopped () && ! stop ; position -- )
{
//--- Function if.
if ( ! SkillTrainBatch ( position ))
{
epoch_failures ++ ;
PrintFormat ( " %s invalid batch epoch=%d position=%d line=%d " , ACSRM_LOG_PREFIX , epoch , position , __LINE__ );
//--- Function if.
if ( GetTickCount () - ticks > 500 )
{
const double percent = ( double ( pass ) + 1.0 -
double ( position - end ) /
MathMax ( start - end - HistoryBars - NForecast , 1 )) *
100.0 / Epochs ;
SkillShowForecastProgress ( percent , epoch_failures );
ticks = GetTickCount ();
}
continue ;
}
//--- Function if.
if ( SkillRecoverySmoke )
{
if ( ! SkillCollectRecoverySmoke ( smoke_hits , smoke_responsibility_sum , smoke_scale_sum ,
smoke_absolute_sum , smoke_absolute_over_scale_sum ))
{ PrintFormat ( " %s -> %d smoke responsibility read failed " , __FUNCTION__ , __LINE__ ); stop = true ; break ; }
smoke_batches ++ ;
if ( smoke_batches >= smoke_limit )
break ;
}
//--- Function if.
if ( GetTickCount () - ticks > 500 )
{
const double percent = ( double ( pass ) + 1.0 -
double ( position - end ) /
MathMax ( start - end - HistoryBars - NForecast , 1 )) *
100.0 / Epochs ;
SkillShowForecastProgress ( percent , epoch_failures );
ticks = GetTickCount ();
}
}
double lmix , lrouter , ltrajectory , lconfidence , latent , observation ;
double valid_value , invalid_value , entropy , distance , inactive_value , recovered_value ;
if ( ! SkillForecast . BuildEpochCodebookDiagnostics () ||
! SkillForecast . ReadEpochDiagnostics ( lmix , lrouter , ltrajectory , lconfidence , latent , observation ,
valid_value , invalid_value , entropy , distance , inactive_value , recovered_value ))
{ PrintFormat ( " %s -> %d diagnostic read failed " , __FUNCTION__ , __LINE__ ); stop = true ; break ; }
const ulong valid = ( ulong ) MathRound ( valid_value );
const ulong invalid = ( ulong ) MathRound ( invalid_value );
const uint inactive = ( uint ) MathRound ( inactive_value );
const uint recovered = ( uint ) MathRound ( recovered_value );
uint recovered_by_scenario [];
uint age_zero_by_scenario [];
if ( ! SkillForecast . ReadEpochRecoveryEvents ( recovered_by_scenario ))
{ PrintFormat ( " %s -> %d recovery-event read failed " , __FUNCTION__ , __LINE__ ); stop = true ; break ; }
if ( ! SkillForecast . ReadEpochAgeZeroEvents ( age_zero_by_scenario ))
{ PrintFormat ( " %s -> %d age-zero-event read failed " , __FUNCTION__ , __LINE__ ); stop = true ; break ; }
ulong recovery_sum = 0 ;
for ( int i = 0 ; i < ArraySize ( recovered_by_scenario ); i ++ )
recovery_sum += recovered_by_scenario [ i ];
if ( recovery_sum != ( ulong ) recovered )
{
PrintFormat ( " %s -> %d recovery-event mismatch total=%I64u diagnostics=%u " ,
__FUNCTION__ , __LINE__ , recovery_sum , recovered );
stop = true ;
break ;
}
if ( SkillRecoverySmoke && valid != smoke_batches )
{
PrintFormat ( " %s -> %d smoke valid mismatch valid=%I64u collected=%I64u " ,
__FUNCTION__ , __LINE__ , valid , smoke_batches );
stop = true ;
break ;
}
SkillBatches += valid ;
SkillInvalidBatches += invalid ;
if ( valid == 0 )
{ stop = true ; break ; }
if ( ! SkillCheckACSRMBypassInvariant ( ompb_reference_count_before , ompb_current_count_before ,
ompb_disagreement_before , ompb_kl_before , ompb_alpha_prior_before ,
ompb_invalid_fallbacks_before , ompb_kl_rejects_before ,
StringFormat ( " epoch-%u " , epoch + 1 )))
{ stop = true ; break ; }
//--- Target is an independently initialized inference-only encoder. It is
//--- never copied from Market and is absent from every backward/update path.
if ( ! SkillProbeNet ( SkillMarket , GetPointer ( SkillProbeState ), latent_after , true ) ||
! ConfigureACSRM ( OMPB_BYPASS ))
{ PrintFormat ( " %s -> %d Market epoch probe failed " , __FUNCTION__ , __LINE__ ); stop = true ; break ; }
const double latent_drift = SkillProbeDrift ( latent_before , latent_after );
PrintFormat ( " %s epoch=%d batches=%I64u L_mix=%.8f L_router=%.8f L_trajectory=%.8f " +
" L_confidence=%.8f NLL_confidence=%.8f usage_entropy=%.8f " +
" pairwise_codebook=%.8f invalid=%I64u latent_drift=%.8f inactive=%u " +
" recovered=%u recovered_by_scenario=%s responsibility_ms=%.3f " ,
ACSRM_LOG_PREFIX , epoch + 1 , valid , lmix / valid , lrouter / valid ,
ltrajectory / valid , latent / valid , lconfidence / valid , entropy ,
distance , SkillInvalidBatches , latent_drift , inactive , recovered ,
FormatScenarioCounts ( recovered_by_scenario ),
double ( SkillResponsibilityMicroseconds ) / ( 1000.0 * double ( valid )));
if ( SkillRecoverySmoke && ! SkillLogRecoverySmoke ( smoke_hits , smoke_responsibility_sum , smoke_scale_sum ,
smoke_absolute_sum , smoke_absolute_over_scale_sum ,
recovered_by_scenario , age_zero_by_scenario ))
{ PrintFormat ( " %s -> %d smoke state log failed " , __FUNCTION__ , __LINE__ ); stop = true ; break ; }
if ( ! SkillRecoverySmoke && ! SkillSaveCheckpoint ( epoch + 1 ))
{ PrintFormat ( " %s -> %d checkpoint failed " , __FUNCTION__ , __LINE__ ); stop = true ; break ; }
if ( SkillRecoverySmoke )
PrintFormat ( " %s smoke complete valid=%I64u recovery_age=%u checkpoint=SKIPPED " , ACSRM_LOG_PREFIX , valid ,
SkillForecast . RecoveryAge ());
SkillCompletedEpochs = epoch + 1 ;
}
Comment ( " " );
//--- Function if.
if ( ! stop )
{
//--- Successful forecast stage is sealed for the later Actor-Critic stage.
SkillMarket . TrainMode ( false );
SkillTarget . TrainMode ( false );
SkillLastSignature = SkillForecastSignature ();
PrintFormat ( " %s forecast inference-only signature=%I64u batches=%I64u invalid=%I64u " , ACSRM_LOG_PREFIX ,
SkillLastSignature , SkillBatches , SkillInvalidBatches );
}
ExpertRemove ();
}
2026-09-29 00:57:33 +03:00
#endif
2026-09-28 01:52:58 +03:00
//+-------------------------------------------------------------------+
//| Actor-Critic inference and composition helpers Implements ORI... |
//+-------------------------------------------------------------------+
string SkillManifestValue ( const string file_name , const string key )
{
int handle = FileOpen ( file_name , FILE_READ | FILE_TXT | FILE_ANSI | FILE_COMMON | FILE_SHARE_READ );
if ( handle == INVALID_HANDLE )
return ( " " );
const string prefix = key + " = " ;
string value = " " ;
//--- Function while.
while ( ! FileIsEnding ( handle ))
{
const string line = FileReadString ( handle );
//--- Function if.
if ( StringFind ( line , prefix ) == 0 )
{
value = StringSubstr ( line , StringLen ( prefix ));
break ;
}
}
FileClose ( handle );
return ( value );
}
//+-------------------------------------------------------------------+
//| Implements SkillValidateForecastManifestHeader. Static checkp... |
//+-------------------------------------------------------------------+
bool SkillValidateForecastManifestHeader ( void )
{
#define Skill_MANIFEST_HEADER_EQ(KEY,VALUE) if(SkillManifestValue(SkillActiveManifestFile,KEY)!=(VALUE)) ReturnFalse
Skill_MANIFEST_HEADER_EQ ( " format " , " ACSRM_FORECAST " );
Skill_MANIFEST_HEADER_EQ ( " version " , IntegerToString ( Skill_FORMAT_VERSION ));
Skill_MANIFEST_HEADER_EQ ( " forecast_type " , IntegerToString ( defNeuronScenarioForecast ));
Skill_MANIFEST_HEADER_EQ ( " variables " , IntegerToString ( BarDescr ));
Skill_MANIFEST_HEADER_EQ ( " scenarios " , IntegerToString ( NScenarios ));
Skill_MANIFEST_HEADER_EQ ( " top_k " , IntegerToString ( TopK ));
Skill_MANIFEST_HEADER_EQ ( " horizon " , IntegerToString ( NForecast ));
Skill_MANIFEST_HEADER_EQ ( " latent " , IntegerToString ( EmbeddingSize ));
Skill_MANIFEST_HEADER_EQ ( " z_layout " , " K,V,H,D " );
Skill_MANIFEST_HEADER_EQ ( " u_layout " , " K,V,H " );
Skill_MANIFEST_HEADER_EQ ( " pi_layout " , " K " );
Skill_MANIFEST_HEADER_EQ ( " codebook_layout " , " K,V,H,D " );
Skill_MANIFEST_HEADER_EQ ( " variable_order " , " BarDescr_feature_series_0_to_8 " );
Skill_MANIFEST_HEADER_EQ ( " normalization " , " OHLC_deltas_from_open;tick_volume_div_1000;RSI_CCI_ATR_MACD_raw " );
#undef Skill_MANIFEST_HEADER_EQ
return ( true );
}
//+------------------------------------------------------------------+
//| Implements SkillValidateForecastManifest. |
//+------------------------------------------------------------------+
bool SkillValidateForecastManifest ( const ulong signature , const bool training = false )
{
if ( signature == 0 )
ReturnFalse ;
if ( ! SkillValidateForecastManifestHeader ())
ReturnFalse ;
#define Skill_MANIFEST_EQ(KEY,VALUE) if(SkillManifestValue(SkillActiveManifestFile,KEY)!=(VALUE)) ReturnFalse
Skill_MANIFEST_EQ ( " contract_signature " , StringFormat ( " %I64u " , SkillForecast . ContractSignature ()));
Skill_MANIFEST_EQ ( " forecast_signature " , StringFormat ( " %I64u " , signature ));
//--- Function if.
if ( training )
{
const ulong target_hash = SkillHashFile ( ulong ( 1469598103934665603 ), SkillActiveTargetFile );
const ulong training_signature = SkillForecastTrainingSignature ( signature );
Skill_MANIFEST_EQ ( " target_hash " , StringFormat ( " %I64u " , target_hash ));
Skill_MANIFEST_EQ ( " training_signature " , StringFormat ( " %I64u " , training_signature ));
if ( target_hash == 0 || training_signature == 0 ||
SkillManifestValue ( SkillActiveManifestFile , " completed_epochs " ) == " " ||
SkillManifestValue ( SkillActiveManifestFile , " training_batches " ) == " " ||
SkillManifestValue ( SkillActiveManifestFile , " invalid_batches " ) == " " )
ReturnFalse ;
}
#undef Skill_MANIFEST_EQ
return ( true );
}
//+------------------------------------------------------------------+
//| Implements SkillCaptureFrozenBuffer. |
//+------------------------------------------------------------------+
bool SkillCaptureFrozenBuffer ( CBufferFloat * source , CBufferFloat & baseline )
{
return ( source && source . BufferRead () && source . Total () > 0 && baseline . AssignArray ( source ));
}
//+------------------------------------------------------------------+
//| Implements SkillFrozenBufferEqual. |
//+------------------------------------------------------------------+
bool SkillFrozenBufferEqual ( CBufferFloat * source , CBufferFloat & baseline )
{
if ( ! source || ! source . BufferRead () || source . Total () != baseline . Total ())
ReturnFalse ;
for ( int i = 0 ; i < source . Total (); i ++ )
if ( source [ i ] != baseline [ i ])
ReturnFalse ;
return ( true );
}
//+------------------------------------------------------------------+
//| Implements SkillCaptureFrozenForecastBaseline. |
//+------------------------------------------------------------------+
bool SkillCaptureFrozenForecastBaseline ( CNeuronScenarioForecast * forecast = NULL )
{
CNeuronScenarioForecast * current = ( forecast ? forecast : SkillForecast );
SkillFrozenBaselineReady = false ;
if ( ! current || ! current . GetCodebook ())
ReturnFalse ;
CBufferFloat * generator = current . GetGenerator (). GetWeightsConv ();
CBufferFloat * router = current . GetRouter (). GetWeightsConv ();
CBufferFloat * confidence = current . GetConfidenceHead (). GetWeightsConv ();
const int trainable = ( generator ? generator . Total () : 0 ) +
( router ? router . Total () : 0 ) +
( confidence ? confidence . Total () : 0 );
if ( trainable <= 0 || trainable != int ( current . TrainableWeights ()) ||
! SkillCaptureFrozenBuffer ( generator , SkillFrozenGeneratorWeights ) ||
! SkillCaptureFrozenBuffer ( router , SkillFrozenRouterWeights ) ||
! SkillCaptureFrozenBuffer ( confidence , SkillFrozenConfidenceWeights ) ||
! SkillCaptureFrozenBuffer ( current . GetCodebook (). GetPrototypes (), SkillFrozenPrototypes ) ||
! SkillCaptureFrozenBuffer ( current . GetCodebook (). GetEMASums (), SkillFrozenEMASums ) ||
! SkillCaptureFrozenBuffer ( current . GetCodebook (). GetEMACounts (), SkillFrozenEMACounts ) ||
! SkillCaptureFrozenBuffer ( current . GetCodebook (). GetUsage (), SkillFrozenUsage ) ||
! SkillCaptureFrozenBuffer ( current . GetCodebook (). GetInactive (), SkillFrozenInactive ) ||
! SkillCaptureFrozenBuffer ( current . GetCodebook (). GetInactivityAge (), SkillFrozenInactivityAge ))
ReturnFalse ;
SkillFrozenBaselineReady = true ;
return ( true );
}
//+------------------------------------------------------------------+
//| Implements SkillVerifyFrozenWeightsExact. |
//+------------------------------------------------------------------+
bool SkillVerifyFrozenWeightsExact ( CNeuronScenarioForecast * forecast = NULL )
{
CNeuronScenarioForecast * current = ( forecast ? forecast : SkillForecast );
if ( ! SkillFrozenBaselineReady || ! current )
ReturnFalse ;
CBufferFloat * generator = current . GetGenerator (). GetWeightsConv ();
CBufferFloat * router = current . GetRouter (). GetWeightsConv ();
CBufferFloat * confidence = current . GetConfidenceHead (). GetWeightsConv ();
const int trainable = ( generator ? generator . Total () : 0 ) +
( router ? router . Total () : 0 ) +
( confidence ? confidence . Total () : 0 );
return ( trainable > 0 && trainable == int ( current . TrainableWeights ()) &&
SkillFrozenBufferEqual ( generator , SkillFrozenGeneratorWeights ) &&
SkillFrozenBufferEqual ( router , SkillFrozenRouterWeights ) &&
SkillFrozenBufferEqual ( confidence , SkillFrozenConfidenceWeights ));
}
//+------------------------------------------------------------------+
//| Implements SkillVerifyFrozenCodebookExact. |
//+------------------------------------------------------------------+
bool SkillVerifyFrozenCodebookExact ( CNeuronScenarioForecast * forecast = NULL )
{
CNeuronScenarioForecast * current = ( forecast ? forecast : SkillForecast );
if ( ! SkillFrozenBaselineReady || ! current || ! current . GetCodebook ())
ReturnFalse ;
return ( SkillFrozenBufferEqual ( current . GetCodebook (). GetPrototypes (), SkillFrozenPrototypes ) &&
SkillFrozenBufferEqual ( current . GetCodebook (). GetEMASums (), SkillFrozenEMASums ) &&
SkillFrozenBufferEqual ( current . GetCodebook (). GetEMACounts (), SkillFrozenEMACounts ) &&
SkillFrozenBufferEqual ( current . GetCodebook (). GetUsage (), SkillFrozenUsage ) &&
SkillFrozenBufferEqual ( current . GetCodebook (). GetInactive (), SkillFrozenInactive ) &&
SkillFrozenBufferEqual ( current . GetCodebook (). GetInactivityAge (), SkillFrozenInactivityAge ));
}
//+------------------------------------------------------------------+
//| Implements SkillVerifyFrozenForecastExact. |
//+------------------------------------------------------------------+
bool SkillVerifyFrozenForecastExact ( CNeuronScenarioForecast * forecast = NULL )
{
return ( SkillVerifyFrozenWeightsExact ( forecast ) && SkillVerifyFrozenCodebookExact ( forecast ));
}
//+------------------------------------------------------------------+
//| Implements SkillLoadForecastInference. |
//+------------------------------------------------------------------+
bool SkillLoadForecastInference ( void )
{
SkillFrozenBaselineReady = false ;
SkillForecast = NULL ;
if ( ! ACSRMStage02ResolveActiveCheckpoint ( false ))
{
Print ( " ACSRM checkpoint selector=FAIL before=inference_load " );
ReturnFalse ;
}
float error = 0 , undefine = 0 , forecast = 0 ;
datetime studied = 0 ;
//--- Function if.
if ( ! SkillMarket . Load ( SkillActiveMarketFile , error , undefine , forecast , studied , true ))
{
PrintFormat ( " %s inference: model load=FAIL " , ACSRM_LOG_PREFIX );
ReturnFalse ;
}
SkillForecast = ( CNeuronScenarioForecast * ) SkillMarket . Layer ( - 1 );
//--- Function if.
if ( ! SkillForecast || SkillForecast . Type () != defNeuronScenarioForecast )
{
PrintFormat ( " %s inference: forecast layer=FAIL " , ACSRM_LOG_PREFIX );
ReturnFalse ;
}
if ( ! ConfigureForecastRecoveryAge ())
ReturnFalse ;
//--- Function if.
if ( SkillForecast . GetTopK () != TopK )
{
PrintFormat ( " %s inference: TopK expected=%d actual=%d " , ACSRM_LOG_PREFIX , TopK , SkillForecast . GetTopK ());
ReturnFalse ;
}
//--- only the Market/Forecast path. Target and its future-window b...
if ( ! SkillValidateShapes ( false ))
{
PrintFormat ( " %s inference: shape audit=FAIL " , ACSRM_LOG_PREFIX );
ReturnFalse ;
}
SkillMarket . TrainMode ( false );
const ulong signature = SkillForecastSignature ();
//--- Function if.
if ( ! SkillValidateForecastManifest ( signature ))
{
PrintFormat ( " %s inference: manifest=FAIL " , ACSRM_LOG_PREFIX );
ReturnFalse ;
}
//--- Function if.
if ( ! SkillCaptureFrozenForecastBaseline ())
{
PrintFormat ( " %s inference: frozen baseline=FAIL " , ACSRM_LOG_PREFIX );
ReturnFalse ;
}
SkillLastSignature = signature ;
return ( true );
}
//+-------------------------------------------------------------------+
//| Full forecast-training restore. Target is required here becau... |
//+-------------------------------------------------------------------+
bool SkillLoadForecastTraining ( void )
{
if ( ! ACSRMStage02ResolveActiveCheckpoint ())
{
Print ( " ACSRM checkpoint selector=FAIL before=training_load " );
ReturnFalse ;
}
if ( ! FileIsExist ( SkillActiveManifestFile , FILE_COMMON ) ||
! FileIsExist ( SkillActiveMarketFile , FILE_COMMON ) ||
! FileIsExist ( SkillActiveTargetFile , FILE_COMMON ))
ReturnFalse ;
//--- Avoid a partial CNet::Load before the fallback random graph i...
if ( ! SkillValidateForecastManifestHeader ())
{
PrintFormat ( " %s restore: manifest static contract=FAIL " , ACSRM_LOG_PREFIX );
ReturnFalse ;
}
float error = 0 , undefine = 0 , forecast = 0 ;
datetime studied = 0 ;
if ( ! SkillMarket . Load ( SkillActiveMarketFile , error , undefine , forecast , studied , true ) ||
! SkillTarget . Load ( SkillActiveTargetFile , error , undefine , forecast , studied , true ))
ReturnFalse ;
if ( ! SkillTarget . SetOpenCLChecked ( SkillMarket . GetOpenCL ()))
ReturnFalseEx ( " target OpenCL transfer failed " );
SkillForecast = ( CNeuronScenarioForecast * ) SkillMarket . Layer ( - 1 );
if ( ! SkillForecast || SkillForecast . Type () != defNeuronScenarioForecast )
ReturnFalse ;
if ( ! ConfigureForecastRecoveryAge ())
ReturnFalse ;
//--- These are transient training tensors and are intentionally absent from
//--- *.nnw. Recreate them before the common shape audit.
if ( ! SkillInitTrainingBuffers () || ! SkillValidateShapes ())
ReturnFalse ;
const ulong signature = SkillForecastSignature ();
if ( ! SkillValidateForecastManifest ( signature , true ))
ReturnFalse ;
const string completed = SkillManifestValue ( SkillActiveManifestFile , " completed_epochs " );
const string batches = SkillManifestValue ( SkillActiveManifestFile , " training_batches " );
const string invalid = SkillManifestValue ( SkillActiveManifestFile , " invalid_batches " );
if ( completed == " " || batches == " " || invalid == " " )
ReturnFalse ;
SkillCompletedEpochs = ( uint ) StringToInteger ( completed );
SkillBatches = ( ulong ) StringToInteger ( batches );
SkillInvalidBatches = ( ulong ) StringToInteger ( invalid );
SkillTarget . TrainMode ( false );
SkillMarket . TrainMode ( true );
SkillLastSignature = signature ;
return ( true );
}
//+------------------------------------------------------------------+
//| Configures the loaded Stage 02 graph for frozen Stage 03 use. |
//+------------------------------------------------------------------+
bool SkillConfigureProductionACSRMCheckpoint ( void )
{
CNeuronOMPBOCL * ompb = GetACSRM ();
CNeuronScenarioForecast * forecast = SkillForecast ;
if ( ! ompb || ! forecast || ! SetACSRMMode ( OMPB_INFERENCE ) ||
! SkillMarket . SetWeightsUpdate ( false ) ||
! forecast . SetCodebookUpdate ( false ))
ReturnFalse ;
SkillMarket . TrainMode ( false );
ompb . TrainMode ( false );
forecast . TrainMode ( false );
return ( true );
}
//+------------------------------------------------------------------+
//| Checks the strict published Stage 02 contract before Stage 03. |
//+------------------------------------------------------------------+
bool SkillValidateProductionACSRMCheckpoint ( void )
{
const string checkpoint = " canonical " ;
CNeuronBaseOCL * rank_tcm = SkillMarket . Layer ( 4 );
CNeuronOMPBOCL * ompb = GetACSRM ();
CNeuronBaseOCL * forecast_layer = SkillMarket . Layer ( 6 );
CNeuronBaseOCL * market_tail = SkillMarket . Layer ( - 1 );
CNeuronScenarioForecast * forecast = SkillForecast ;
CNeuronBaseOCL * ompb_base = ( CNeuronBaseOCL * ) ompb ;
CNeuronBaseOCL * forecast_base = ( CNeuronBaseOCL * ) forecast ;
CLayerDescription * ompb_info = ( ompb ? ompb . GetLayerInfo () : NULL );
bool layers_frozen = true ;
for ( int index = 0 ; index <= 6 ; ++ index )
{
CNeuronBaseOCL * layer = SkillMarket . Layer ( index );
if ( ! layer || layer . TrainMode ())
{
layers_frozen = false ;
break ;
}
}
const bool descriptor_valid = ( ompb_info &&
ompb_info . window == EmbeddingSize &&
ompb_info . count == BarDescr &&
ompb_info . layers == ACSRMSamples &&
ompb_info . units . Size () == 2 &&
ompb_info . units [ 0 ] == ACSRMReferenceSize &&
ompb_info . units [ 1 ] == ACSRMCurrentWindow &&
ompb_info . batch == BatchSize );
DeleteObj ( ompb_info );
const bool forecast_tail_valid = ( forecast_layer != NULL && market_tail != NULL &&
forecast_layer == market_tail && forecast_base != NULL &&
forecast_layer == forecast_base &&
forecast_layer . Type () == defNeuronScenarioForecast );
if ( ! forecast_tail_valid )
{
PrintFormat ( " %s_STAGE03_PRODUCTION_GATE_FORECAST_TAIL_FAIL checkpoint=%s " , FileName , checkpoint );
return ( false );
}
const ulong signature = SkillForecastSignature ();
const bool valid = ( ACSRMStage02ResolveActiveCheckpoint ( false ) &&
! ACSRMStage02RecoveryFailed &&
SkillActiveMarketFile == Skill_MARKET_FILE &&
SkillActiveTargetFile == Skill_TARGET_FILE &&
SkillActiveManifestFile == Skill_MANIFEST_FILE &&
SkillValidateForecastManifest ( signature ) &&
rank_tcm != NULL && rank_tcm . Type () == defNeuronCogDriverRankTCM &&
ompb != NULL && ompb . Mode () == OMPB_INFERENCE &&
ompb_base != NULL && ! ompb_base . TrainMode () &&
forecast != NULL && forecast_base != NULL && ! forecast_base . TrainMode () &&
! forecast . CodebookUpdate () &&
SkillMarket . WeightsUpdateEnabled () == false && layers_frozen &&
descriptor_valid );
if ( ! valid )
{
PrintFormat ( " %s_STAGE03_PRODUCTION_GATE_FAIL checkpoint=%s " , FileName , checkpoint );
return ( false );
}
PrintFormat ( " %s_STAGE03_PRODUCTION_GATE_PASS checkpoint=%s " , FileName , checkpoint );
return ( true );
}
//+-----------------------------------------------------------------------+
//| Captures Market-base and ACSRM parameter fingerprints for Stage 03. |
//+-----------------------------------------------------------------------+
bool SkillCaptureProductionACSRMFingerprints ( void )
{
CNeuronOMPBOCL * ompb = GetACSRM ();
SkillProductionSignatureReady = false ;
SkillProductionBaseFingerprint = 0 ;
SkillProductionACSRMFingerprint = ulong ( 1469598103934665603 );
if ( ! ompb || ! ACSRMStage02MarketFingerprint ( 0 , 4 , SkillProductionBaseFingerprint ) ||
! ompb . AppendParameterFingerprint ( SkillProductionACSRMFingerprint ) ||
SkillProductionBaseFingerprint == 0 || SkillProductionACSRMFingerprint == 0 )
ReturnFalse ;
SkillProductionSignatureReady = true ;
PrintFormat ( " %s_STAGE03_SIGNATURES_BEFORE base=%I64u posterior=%I64u " , FileName ,
SkillProductionBaseFingerprint , SkillProductionACSRMFingerprint );
return ( true );
}
//+------------------------------------------------------------------+
//| Verifies that Stage 03 preserved the frozen Market and ACSRM. |
//+------------------------------------------------------------------+
bool SkillVerifyProductionACSRMFingerprints ( void )
{
ulong base = 0 ;
ulong posterior = ulong ( 1469598103934665603 );
CNeuronOMPBOCL * ompb = GetACSRM ();
if ( ! SkillProductionSignatureReady || ! ompb ||
! ACSRMStage02MarketFingerprint ( 0 , 4 , base ) ||
! ompb . AppendParameterFingerprint ( posterior ) ||
! SkillValidateProductionACSRMCheckpoint ())
ReturnFalse ;
const bool unchanged = ( base == SkillProductionBaseFingerprint &&
posterior == SkillProductionACSRMFingerprint );
PrintFormat ( " %s_STAGE03_SIGNATURES_AFTER base=%I64u posterior=%I64u unchanged=%s " , FileName ,
base , posterior , ( unchanged ? " true " : " false " ));
return ( unchanged );
}
//+------------------------------------------------------------------+
//| Implements SkillAddCross. |
//+------------------------------------------------------------------+
bool SkillAddCross ( CArrayObj * description , const bool critic )
{
CLayerDescription * descr = new CLayerDescription ();
if ( ! description || ! descr )
{ DeleteObj ( descr ); ReturnFalse ; }
descr . type = defNeuronScenarioCrossAttention ;
descr . count = NScenarios ;
descr . variables = ( critic ? 5 : 3 );
descr . window_out = NForecast ;
descr . window = EmbeddingSize ;
descr . layers = BarDescr ;
descr . step = StackSize ;
descr . probability = TopK ;
descr . activation = None ;
descr . optimization = ADAM ;
descr . batch = BatchSize ;
if ( description . Add ( descr ))
return ( true );
DeleteObj ( descr );
ReturnFalse ;
}
//+------------------------------------------------------------------+
//| Appends one convolutional layer description to the graph. |
//+------------------------------------------------------------------+
bool SkillAddConv ( CArrayObj * description , const uint count , const uint window , const uint output ,
const uint variables , const ENUM_ACTIVATION activation )
{
CLayerDescription * descr = new CLayerDescription ();
if ( ! description || ! descr )
{ DeleteObj ( descr ); ReturnFalse ; }
descr . type = defNeuronConvOCL ;
descr . count = count ;
descr . window = window ;
descr . step = window ;
descr . window_out = output ;
descr . layers = variables ;
descr . activation = activation ;
descr . optimization = ADAM ;
descr . batch = BatchSize ;
if ( description . Add ( descr ))
return ( true );
DeleteObj ( descr );
ReturnFalse ;
}
//+------------------------------------------------------------------+
//| Implements SkillForwardForecast. |
//+------------------------------------------------------------------+
bool SkillForwardForecast ( const int position , CBufferFloat * state , CBufferFloat * time ,
const int shift_bars = 1 )
{
//--- Causality: the decision state window ends one bar BEFORE the execution
//--- bar, so close/high/low of the entry bar are realized when its open is
//--- chosen as the post-decision execution price.
if ( ! SkillForecast || ! CreateBuffers ( position + shift_bars , state , time , NULL ) ||
! SkillMarket . feedForward ( state , 1 , false , ( CBufferFloat * ) NULL ))
ReturnFalse ;
CBufferFloat * z = SkillForecast . GetZ (), * u = SkillForecast . GetU (), * pi = SkillForecast . GetPi ();
return ( z != NULL && u != NULL && pi != NULL && z . Total () == NScenarios * BarDescr * NForecast * EmbeddingSize &&
u . Total () == NScenarios * BarDescr * NForecast &&
pi . Total () == NScenarios && z . GetIndex () >= 0 && u . GetIndex () >= 0 && pi . GetIndex () >= 0 );
}
//+------------------------------------------------------------------+
//| Stable Owner index of one offline training example. |
//+------------------------------------------------------------------+
int SkillObservationOwner ( const int position , const int slot , const int seed )
{
//--- A deal (position, slot) is the example; the Owner is a stable function
//--- of the example id and the replay seed. Re-reading the same record or
//--- reordering episodes never transfers its label to the other Critic.
return ( int (( ulong ( MathMax ( position , 0 )) * ulong ( 2654435761 u ) +
ulong ( MathMax ( slot , 0 )) * ulong ( 40503 u ) +
ulong ( MathMax ( seed , 0 ))) & 1 ));
}
//+------------------------------------------------------------------------------------------------------------------+
//| Converts the actor gradient to the pre-activation gradient: dU/dz = dU/da * a * (1 - a) for the final SIGMOID. |
//+------------------------------------------------------------------------------------------------------------------+
bool ApplyActorSigmoidDerivative ( CNeuronBaseOCL * output_layer )
{
if ( ! output_layer || output_layer . getOutput (). Total () != ( int ) NActions ||
output_layer . getGradient (). Total () != ( int ) NActions ||
output_layer . getOutput (). GetIndex () < 0 || output_layer . getGradient (). GetIndex () < 0 )
ReturnFalse ;
if ( ! ActorDerivOneMinus . BufferInit ( NActions , 0 ) ||
! ActorDerivOneMinus . BufferCreate ( SkillMarket . GetOpenCL ()) ||
! ActorDerivOneMinus . BufferWrite () ||
! ActorDeriv . BufferInit ( NActions , 0 ) ||
! ActorDeriv . BufferCreate ( SkillMarket . GetOpenCL ()) ||
! ActorDeriv . BufferWrite () ||
! ActorGradScratch . BufferInit ( NActions , 0 ) ||
! ActorGradScratch . BufferCreate ( SkillMarket . GetOpenCL ()) ||
! ActorGradScratch . BufferWrite () ||
! SkillDevice . Bind ( SkillMarket . GetOpenCL ()))
ReturnFalse ;
//--- dU/dz = dU/da * a * (1 - a) through the genuine per-element
//--- DeActivation primitive: every component scales by its OWN (1 - a_i).
//--- IdentDifferent (identity-row subtraction) is NOT used: with
//--- dimension == NActions it produces (-a_1, -a_2, ..., 1 - a_k) instead
//--- of elementwise (1 - a_1, ..., 1 - a_n).
if ( ! SkillDevice . DeActivationOnDevice ( output_layer . getOutput (), GetPointer ( ActorGradScratch ),
output_layer . getGradient (), SIGMOID ) ||
! SkillDevice . Copy ( GetPointer ( ActorGradScratch ), output_layer . getGradient (), NActions ))
ReturnFalse ;
return ( true );
}
//+------------------------------------------------------------------+
//| Builds CriticInput. |
//+------------------------------------------------------------------+
bool BuildCriticInput ( CBufferFloat * account , CBufferFloat * action , CBufferFloat * combined )
{
if ( ! account || ! action || ! combined || account . Total () != AccountDescr || action . Total () != NActions ||
account . GetIndex () < 0 || action . GetIndex () < 0 )
ReturnFalse ;
//--- GPU Join/Copy transfer: both sources bind the Market context and the
//--- concatenation is assembled on device from the CURRENT tensors, so the
//--- target-action can never arrive from a stale host snapshot. The final
//--- read-back refreshes the host copy that the first CNet layer consumes.
if ( ! combined . BufferInit ( AccountDescr + NActions , 0 ))
ReturnFalse ;
if ( combined . GetIndex () < 0 && ! combined . BufferCreate ( SkillMarket . GetOpenCL ()))
ReturnFalse ;
if ( ! combined . BufferWrite () || ! account . BufferWrite ())
ReturnFalse ;
if ( ! SkillDevice . Bind ( SkillMarket . GetOpenCL ()))
ReturnFalse ;
if ( ! SkillDevice . Join2 ( account , AccountDescr , action , NActions , combined ))
ReturnFalse ;
return ( combined . BufferRead ());
}
//+-------------------------------------------------------------------+
//| Implements EvaluateAction. lot instead of the former balance-... |
//+-------------------------------------------------------------------+
double EvaluateAction ( CBufferFloat * action , const double balance , const uint position ,
const int horizon_bars , int & outcome_tag )
{
//--- CheckAction already floors sub-minimum lot fractions at LotsMin(), so
//--- the returned value carries a real outcome label (TP/SL/HORIZON) even
//--- for a cold-start Actor with tiny lots. Passthrough only.
const double reward = CheckAction ( action , balance , position , horizon_bars , outcome_tag );
if ( ! MathIsValidNumber ( reward ))
return ( reward );
return ( reward );
}
//+-------------------------------------------------------------------+
//| Builds TeacherAction. raw future window here; the live Market... |
//+-------------------------------------------------------------------+
bool BuildTeacherAction ( const int position , CBufferFloat * account , CBufferFloat * action ,
double & reward , const int horizon_bars , int & outcome_tag )
{
reward = 0 ;
if ( ! account || ! action || position < 0 || position + HistoryBars + NForecast > int ( Rates . Size ()) ||
//--- Function Total.
account . Total () != AccountDescr )
{
PrintFormat ( " BuildTeacherAction input position=%d rates=%d account=%d action=%d " ,
position , Rates . Size (), ( account ? account . Total () : - 1 ), ( action ? action . Total () : - 1 ));
ReturnFalse ;
}
CBufferFloat teacher_state , teacher_time ;
//--- Function if.
if ( ! CreateBuffers ( position , GetPointer ( teacher_state ), GetPointer ( teacher_time ), GetPointer ( SkillFuture )))
{
PrintFormat ( " BuildTeacherAction CreateBuffers position=%d future=%d index=%d " ,
position , SkillFuture . Total (), SkillFuture . GetIndex ());
ReturnFalse ;
}
vector < float > account_values ;
//--- Function if.
if ( account . GetData ( account_values ) != AccountDescr )
{
PrintFormat ( " BuildTeacherAction account data total=%d index=%d " , account . Total (), account . GetIndex ());
ReturnFalse ;
}
const vector < float > teacher = OraculAction ( account_values , GetPointer ( SkillFuture ));
//--- Function if.
if ( teacher . Size () != NActions )
{
PrintFormat ( " BuildTeacherAction oracle size=%d expected=%d " , teacher . Size (), NActions );
ReturnFalse ;
}
//--- Function if.
if ( ! action . AssignArray ( teacher ))
{
Print ( " BuildTeacherAction action AssignArray " );
ReturnFalse ;
}
//--- Function if.
if ( action . GetIndex () < 0 && ! action . BufferCreate ( SkillMarket . GetOpenCL ()))
{
PrintFormat ( " BuildTeacherAction action BufferCreate total=%d index=%d " , action . Total (), action . GetIndex ());
ReturnFalse ;
}
//--- Function if.
if ( action . GetIndex () >= 0 && ! action . BufferWrite ())
{
PrintFormat ( " BuildTeacherAction action BufferWrite total=%d index=%d " , action . Total (), action . GetIndex ());
ReturnFalse ;
}
reward = EvaluateAction ( action , MathMax ( 0.0 , double ( account_values [ 0 ]) * EtalonBalance ),
( uint ) position , horizon_bars , outcome_tag );
//--- Function if.
if ( ! MathIsValidNumber ( reward ))
{
PrintFormat ( " BuildTeacherAction reward invalid %.8f " , reward );
ReturnFalse ;
}
return ( true );
}
//+-------------------------------------------------------------------+
//| A valid random policy action expands Critic coverage only. Bu... |
//+-------------------------------------------------------------------+
bool BuildRandomAction ( const int position , CBufferFloat * account , CBufferFloat * action ,
double & reward , const int horizon_bars , int & outcome_tag )
{
reward = 0 ;
if ( ! account || ! action || position < 0 || position >= int ( Rates . Size ()) ||
account . Total () != AccountDescr )
ReturnFalse ;
vector < float > account_values ;
if ( account . GetData ( account_values ) != AccountDescr )
ReturnFalse ;
const double balance = MathMax ( 0.0 , double ( account_values [ 0 ]) * EtalonBalance );
double margin = 0 ;
if ( balance <= 0 || ! OrderCalcMargin ( ORDER_TYPE_BUY , Symb . Name (), 1 , Rates [ position ]. open , margin ) || margin <= 0 )
ReturnFalse ;
const double min_lot = Symb . LotsMin ();
const double max_lot = MathMin ( Symb . LotsMax (), balance / ( 2.0 * margin ));
if ( min_lot <= 0 || max_lot < min_lot )
ReturnFalse ;
const double stop_points = MathMax ( Symb . StopsLevel (), 10 );
const double min_tp = stop_points / MathMax ( MaxTP , 1 );
const double min_sl = ( stop_points + Symb . Spread ()) / MathMax ( MaxSL , 1 );
if ( min_tp >= 1.0 || min_sl >= 1.0 )
ReturnFalse ;
const double uniform = MathRand () / 32767.0 ;
const double lot = MathMin ( max_lot , NormalizeLot ( min_lot + ( max_lot - min_lot ) * uniform ));
const double tp = min_tp + ( 1.0 - min_tp ) * ( MathRand () / 32767.0 );
const double sl = min_sl + ( 1.0 - min_sl ) * ( MathRand () / 32767.0 );
vector < float > values = vector < float >:: Zeros ( NActions );
if (( MathRand () & 1 ) != 0 )
values [ 0 ] = float ( lot );
else
values [ 3 ] = float ( lot );
values [ 1 ] = values [ 4 ] = float ( tp );
values [ 2 ] = values [ 5 ] = float ( sl );
if ( ! action . AssignArray ( values ))
ReturnFalse ;
if ( action . GetIndex () < 0 && ! action . BufferCreate ( SkillMarket . GetOpenCL ()))
ReturnFalse ;
if ( action . GetIndex () >= 0 && ! action . BufferWrite ())
ReturnFalse ;
reward = EvaluateAction ( action , balance , ( uint ) position , horizon_bars , outcome_tag );
return ( MathIsValidNumber ( reward ));
}
//+------------------------------------------------------------------+
//| Implements SkillClampAction. |
//+------------------------------------------------------------------+
bool SkillClampAction ( CBufferFloat * source , CBufferFloat * target )
{
if ( ! source || ! target || ! source . BufferRead () || source . Total () != NActions ||
! target . BufferInit ( NActions , 0 ))
ReturnFalse ;
for ( uint i = 0 ; i < NActions ; i ++ )
{
if ( ! MathIsValidNumber ( source [ i ]) || ! target . Update ( i , float ( MathMax ( 0.0 , MathMin ( 1.0 , double ( source [ i ]))))))
ReturnFalse ;
}
return ( target . GetIndex () < 0 || target . BufferWrite ());
}
//+------------------------------------------------------------------+
//| Checks the canonical AC tuple file names. |
//+------------------------------------------------------------------+
bool SkillACTupleNamesValid ( const string actor_file , const string q1_file ,
const string q2_file , const string manifest_file )
{
return ( actor_file != " " && q1_file != " " && q2_file != " " && manifest_file != " " &&
actor_file != q1_file && actor_file != q2_file && actor_file != manifest_file &&
q1_file != q2_file && q1_file != manifest_file && q2_file != manifest_file );
}
//+------------------------------------------------------------------+
//| Deletes one canonical AC tuple file. |
//+------------------------------------------------------------------+
bool SkillACDeleteFile ( const string file_name )
{
return ( ! FileIsExist ( file_name , FILE_COMMON ) || FileDelete ( file_name , FILE_COMMON ));
}
//+------------------------------------------------------------------+
//| Copies the AC tuple files to a new canonical set. |
//+------------------------------------------------------------------+
bool SkillACCopyTuple ( const string source_actor , const string source_q1 ,
const string source_q2 , const string source_manifest ,
const string destination_actor , const string destination_q1 ,
const string destination_q2 , const string destination_manifest ,
const bool rewrite )
{
if ( ! SkillACTupleNamesValid ( source_actor , source_q1 , source_q2 , source_manifest ) ||
! SkillACTupleNamesValid ( destination_actor , destination_q1 , destination_q2 ,
destination_manifest ))
ReturnFalseEx ( " Actor-Critic tuple names " );
const uint flags = ( rewrite ? FILE_COMMON | FILE_REWRITE : FILE_COMMON );
if ( ! FileCopy ( source_actor , FILE_COMMON , destination_actor , flags ))
ReturnFalseEx ( " Actor-Critic Actor copy " );
if ( ! FileCopy ( source_q1 , FILE_COMMON , destination_q1 , flags ))
ReturnFalseEx ( " Actor-Critic Q1 copy " );
if ( ! FileCopy ( source_q2 , FILE_COMMON , destination_q2 , flags ))
ReturnFalseEx ( " Actor-Critic Q2 copy " );
//--- Write the manifest last so a complete tuple has an unambiguous commit point.
if ( ! FileCopy ( source_manifest , FILE_COMMON , destination_manifest , flags ))
ReturnFalseEx ( " Actor-Critic manifest copy " );
return ( true );
}
//+------------------------------------------------------------------+
//| Checks that an AC tuple file triplet is complete. |
//+------------------------------------------------------------------+
bool SkillACTupleComplete ( const string actor_file , const string q1_file ,
const string q2_file , const string manifest_file )
{
const ulong seed = ulong ( 1469598103934665603 );
return ( SkillACTupleNamesValid ( actor_file , q1_file , q2_file , manifest_file ) &&
SkillHashFile ( seed , actor_file ) != 0 && SkillHashFile ( seed , q1_file ) != 0 &&
SkillHashFile ( seed , q2_file ) != 0 && SkillHashFile ( seed , manifest_file ) != 0 );
}
//+------------------------------------------------------------------+
//| Compares two AC tuples byte-for-byte. |
//+------------------------------------------------------------------+
bool SkillACTuplesEqual ( const string first_actor , const string first_q1 ,
const string first_q2 , const string first_manifest ,
const string second_actor , const string second_q1 ,
const string second_q2 , const string second_manifest )
{
const ulong seed = ulong ( 1469598103934665603 );
return ( SkillACTupleComplete ( first_actor , first_q1 , first_q2 , first_manifest ) &&
SkillACTupleComplete ( second_actor , second_q1 , second_q2 , second_manifest ) &&
SkillHashFile ( seed , first_actor ) == SkillHashFile ( seed , second_actor ) &&
SkillHashFile ( seed , first_q1 ) == SkillHashFile ( seed , second_q1 ) &&
SkillHashFile ( seed , first_q2 ) == SkillHashFile ( seed , second_q2 ) &&
SkillHashFile ( seed , first_manifest ) == SkillHashFile ( seed , second_manifest ));
}
//+------------------------------------------------------------------+
//| Writes one transaction line to the AC transaction file. |
//+------------------------------------------------------------------+
bool SkillACWriteTransactionLine ( const int handle , const string value ,
const string context )
{
if ( FileWrite ( handle , value ) <= 0 )
{
PrintFormat ( " Skill policy transaction write failed field=%s error=%d " ,
context , GetLastError ());
return ( false );
}
return ( true );
}
//+------------------------------------------------------------------+
//| Writes the AC transaction record set. |
//+------------------------------------------------------------------+
bool SkillACWriteTransaction ( const string transaction_file , const bool had_previous ,
const string phase , const ulong actor_hash ,
const ulong q1_hash , const ulong q2_hash ,
const ulong manifest_hash )
{
const string staged_file = transaction_file + " .next " ;
if ( transaction_file == " " || ( phase != " prepared " && phase != " previous_ready " ) ||
actor_hash == 0 || q1_hash == 0 || q2_hash == 0 || manifest_hash == 0 ||
FileIsExist ( transaction_file , FILE_COMMON ) || FileIsExist ( staged_file , FILE_COMMON ))
ReturnFalseEx ( " Actor-Critic transaction arguments " );
int handle = FileOpen ( staged_file , FILE_WRITE | FILE_TXT | FILE_ANSI | FILE_COMMON );
if ( handle == INVALID_HANDLE )
ReturnFalseEx ( " Actor-Critic transaction open " );
if ( ! SkillACWriteTransactionLine ( handle , " format=ACSRM_ACTOR_CRITIC_TRANSACTION " , " format " ) ||
! SkillACWriteTransactionLine ( handle ,
StringFormat ( " version=%u " , Skill_AC_TRANSACTION_VERSION ),
" version " ) ||
! SkillACWriteTransactionLine ( handle , StringFormat ( " phase=%s " , phase ), " phase " ) ||
! SkillACWriteTransactionLine ( handle , StringFormat ( " had_previous=%s " ,
( had_previous ? " true " : " false " )), " had_previous " ) ||
! SkillACWriteTransactionLine ( handle , StringFormat ( " actor_hash=%I64u " , actor_hash ),
" actor_hash " ) ||
! SkillACWriteTransactionLine ( handle , StringFormat ( " q1_hash=%I64u " , q1_hash ), " q1_hash " ) ||
! SkillACWriteTransactionLine ( handle , StringFormat ( " q2_hash=%I64u " , q2_hash ), " q2_hash " ) ||
! SkillACWriteTransactionLine ( handle , StringFormat ( " manifest_hash=%I64u " , manifest_hash ),
" manifest_hash " ))
{
FileClose ( handle );
SkillACDeleteFile ( staged_file );
ReturnFalse ;
}
FileFlush ( handle );
FileClose ( handle );
//--- Publish the complete marker in one rename after all fields are flushed.
if ( ! FileMove ( staged_file , FILE_COMMON , transaction_file , FILE_COMMON | FILE_REWRITE ))
ReturnFalseEx ( " Actor-Critic transaction publish " );
return ( true );
}
//+------------------------------------------------------------------+
//| Reads the AC transaction record set. |
//+------------------------------------------------------------------+
bool SkillACReadTransaction ( const string transaction_file , bool & had_previous ,
string & phase , ulong & actor_hash , ulong & q1_hash ,
ulong & q2_hash , ulong & manifest_hash )
{
had_previous = false ;
phase = " " ;
actor_hash = 0 ;
q1_hash = 0 ;
q2_hash = 0 ;
manifest_hash = 0 ;
if ( ! FileIsExist ( transaction_file , FILE_COMMON ) ||
SkillManifestValue ( transaction_file , " format " ) != " ACSRM_ACTOR_CRITIC_TRANSACTION " ||
SkillManifestValue ( transaction_file , " version " ) != IntegerToString ( Skill_AC_TRANSACTION_VERSION ))
return ( false );
const string previous = SkillManifestValue ( transaction_file , " had_previous " );
phase = SkillManifestValue ( transaction_file , " phase " );
const string actor = SkillManifestValue ( transaction_file , " actor_hash " );
const string q1 = SkillManifestValue ( transaction_file , " q1_hash " );
const string q2 = SkillManifestValue ( transaction_file , " q2_hash " );
const string manifest = SkillManifestValue ( transaction_file , " manifest_hash " );
if (( previous != " true " && previous != " false " ) ||
( phase != " prepared " && phase != " previous_ready " ) || actor == " " || q1 == " " ||
q2 == " " || manifest == " " )
return ( false );
had_previous = ( previous == " true " );
actor_hash = ( ulong ) StringToInteger ( actor );
q1_hash = ( ulong ) StringToInteger ( q1 );
q2_hash = ( ulong ) StringToInteger ( q2 );
manifest_hash = ( ulong ) StringToInteger ( manifest );
return ( actor_hash != 0 && q1_hash != 0 && q2_hash != 0 && manifest_hash != 0 );
}
//+------------------------------------------------------------------+
//| Advances the AC transaction selector. |
//+------------------------------------------------------------------+
bool SkillACAdvanceTransaction ( const string transaction_file , const bool had_previous ,
const string phase , const ulong actor_hash ,
const ulong q1_hash , const ulong q2_hash ,
const ulong manifest_hash )
{
if ( ! SkillACDeleteFile ( transaction_file ))
ReturnFalse ;
return ( SkillACWriteTransaction ( transaction_file , had_previous , phase ,
actor_hash , q1_hash , q2_hash , manifest_hash ));
}
//+------------------------------------------------------------------+
//| Cleans up the AC transaction files. |
//+------------------------------------------------------------------+
bool SkillACCleanupTransaction ( const string transaction_file ,
const string next_actor , const string next_q1 ,
const string next_q2 , const string next_manifest ,
const string previous_actor , const string previous_q1 ,
const string previous_q2 , const string previous_manifest )
{
const string staged_transaction = transaction_file + " .next " ;
const string files [] = { next_manifest , next_q2 , next_q1 , next_actor ,
previous_manifest , previous_q2 , previous_q1 , previous_actor ,
staged_transaction , transaction_file
};
for ( int i = 0 ; i < ArraySize ( files ); i ++ )
if ( ! SkillACDeleteFile ( files [ i ]))
{
PrintFormat ( " Skill policy transaction cleanup failed file=%s error=%d " ,
files [ i ], GetLastError ());
return ( false );
}
return ( true );
}
//+------------------------------------------------------------------+
//| Recovers the AC transaction state after a failed write. |
//+------------------------------------------------------------------+
bool SkillACRecoverTransaction ( const string canonical_actor , const string canonical_q1 ,
const string canonical_q2 , const string canonical_manifest ,
const string next_actor , const string next_q1 ,
const string next_q2 , const string next_manifest ,
const string previous_actor , const string previous_q1 ,
const string previous_q2 , const string previous_manifest ,
const string transaction_file )
{
const bool marker_exists = FileIsExist ( transaction_file , FILE_COMMON );
const bool staged_marker_exists = FileIsExist ( transaction_file + " .next " , FILE_COMMON );
const bool sidecars_exist = ( FileIsExist ( next_actor , FILE_COMMON ) ||
FileIsExist ( next_q1 , FILE_COMMON ) ||
FileIsExist ( next_q2 , FILE_COMMON ) ||
FileIsExist ( next_manifest , FILE_COMMON ) ||
FileIsExist ( previous_actor , FILE_COMMON ) ||
FileIsExist ( previous_q1 , FILE_COMMON ) ||
FileIsExist ( previous_q2 , FILE_COMMON ) ||
FileIsExist ( previous_manifest , FILE_COMMON ) ||
staged_marker_exists );
if ( ! marker_exists )
{
if ( ! sidecars_exist )
return ( true );
if ( SkillACTupleComplete ( canonical_actor , canonical_q1 , canonical_q2 , canonical_manifest ))
return ( SkillACCleanupTransaction ( transaction_file , next_actor , next_q1 , next_q2 ,
next_manifest , previous_actor , previous_q1 ,
previous_q2 , previous_manifest ));
if ( SkillACTupleComplete ( previous_actor , previous_q1 , previous_q2 , previous_manifest ) &&
SkillACCopyTuple ( previous_actor , previous_q1 , previous_q2 , previous_manifest ,
canonical_actor , canonical_q1 , canonical_q2 , canonical_manifest , true ) &&
SkillACTuplesEqual ( previous_actor , previous_q1 , previous_q2 , previous_manifest ,
canonical_actor , canonical_q1 , canonical_q2 , canonical_manifest ))
return ( SkillACCleanupTransaction ( transaction_file , next_actor , next_q1 , next_q2 ,
next_manifest , previous_actor , previous_q1 ,
previous_q2 , previous_manifest ));
if ( ! FileIsExist ( canonical_actor , FILE_COMMON ) &&
! FileIsExist ( canonical_q1 , FILE_COMMON ) &&
! FileIsExist ( canonical_q2 , FILE_COMMON ) &&
! FileIsExist ( canonical_manifest , FILE_COMMON ))
return ( SkillACCleanupTransaction ( transaction_file , next_actor , next_q1 , next_q2 ,
next_manifest , previous_actor , previous_q1 ,
previous_q2 , previous_manifest ));
ReturnFalse ;
}
bool had_previous = false ;
string phase = " " ;
ulong actor_hash = 0 , q1_hash = 0 , q2_hash = 0 , manifest_hash = 0 ;
if ( ! SkillACReadTransaction ( transaction_file , had_previous , phase , actor_hash , q1_hash ,
q2_hash , manifest_hash ))
ReturnFalse ;
if ( SkillHashFile ( ulong ( 1469598103934665603 ), canonical_actor ) == actor_hash &&
SkillHashFile ( ulong ( 1469598103934665603 ), canonical_q1 ) == q1_hash &&
SkillHashFile ( ulong ( 1469598103934665603 ), canonical_q2 ) == q2_hash &&
SkillHashFile ( ulong ( 1469598103934665603 ), canonical_manifest ) == manifest_hash )
return ( SkillACCleanupTransaction ( transaction_file , next_actor , next_q1 , next_q2 ,
next_manifest , previous_actor , previous_q1 ,
previous_q2 , previous_manifest ));
if ( phase == " prepared " && had_previous &&
SkillACTupleComplete ( canonical_actor , canonical_q1 , canonical_q2 , canonical_manifest ))
return ( SkillACCleanupTransaction ( transaction_file , next_actor , next_q1 , next_q2 ,
next_manifest , previous_actor , previous_q1 ,
previous_q2 , previous_manifest ));
if ( had_previous && SkillACTupleComplete ( previous_actor , previous_q1 , previous_q2 , previous_manifest ) &&
SkillACCopyTuple ( previous_actor , previous_q1 , previous_q2 , previous_manifest ,
canonical_actor , canonical_q1 , canonical_q2 , canonical_manifest , true ) &&
SkillACTuplesEqual ( previous_actor , previous_q1 , previous_q2 , previous_manifest ,
canonical_actor , canonical_q1 , canonical_q2 , canonical_manifest ))
return ( SkillACCleanupTransaction ( transaction_file , next_actor , next_q1 , next_q2 ,
next_manifest , previous_actor , previous_q1 ,
previous_q2 , previous_manifest ));
if ( ! had_previous && phase == " prepared " &&
SkillACTupleComplete ( next_actor , next_q1 , next_q2 , next_manifest ) &&
SkillACCopyTuple ( next_actor , next_q1 , next_q2 , next_manifest ,
canonical_actor , canonical_q1 , canonical_q2 , canonical_manifest , true ) &&
SkillHashFile ( ulong ( 1469598103934665603 ), canonical_actor ) == actor_hash &&
SkillHashFile ( ulong ( 1469598103934665603 ), canonical_q1 ) == q1_hash &&
SkillHashFile ( ulong ( 1469598103934665603 ), canonical_q2 ) == q2_hash &&
SkillHashFile ( ulong ( 1469598103934665603 ), canonical_manifest ) == manifest_hash )
return ( SkillACCleanupTransaction ( transaction_file , next_actor , next_q1 , next_q2 ,
next_manifest , previous_actor , previous_q1 ,
previous_q2 , previous_manifest ));
ReturnFalse ;
}
//+------------------------------------------------------------------+
//| Writes the AC manifest file for a forecast signature. |
//+------------------------------------------------------------------+
bool SkillWriteACManifestFile ( const ulong forecast_signature , const string actor_file ,
const string q1_file , const string q2_file ,
const string manifest_file )
{
if ( forecast_signature == 0 || forecast_signature != SkillLastSignature ||
! SkillACTupleNamesValid ( actor_file , q1_file , q2_file , manifest_file ))
ReturnFalse ;
const ulong seed = ulong ( 1469598103934665603 );
const ulong actor_hash = SkillHashFile ( seed , actor_file );
const ulong q1_hash = SkillHashFile ( seed , q1_file );
const ulong q2_hash = SkillHashFile ( seed , q2_file );
if ( actor_hash == 0 || q1_hash == 0 || q2_hash == 0 )
ReturnFalse ;
int handle = FileOpen ( manifest_file , FILE_WRITE | FILE_TXT | FILE_ANSI | FILE_COMMON );
if ( handle == INVALID_HANDLE )
ReturnFalse ;
const bool written = ( FileWrite ( handle , " format=ACSRM_ACTOR_CRITIC " ) > 0 &&
FileWrite ( handle , StringFormat ( " version=%u " , Skill_AC_FORMAT_VERSION )) > 0 &&
FileWrite ( handle , StringFormat ( " forecast_signature=%I64u " , forecast_signature )) > 0 &&
FileWrite ( handle , StringFormat ( " actor_context=%u " , ( 3 * EmbeddingSize ))) > 0 &&
FileWrite ( handle , StringFormat ( " critic_context=%u " , ( 5 * EmbeddingSize ))) > 0 &&
FileWrite ( handle , " action_order=BuyLot,BuyTP,BuySL,SellLot,SellTP,SellSL " ) > 0 &&
FileWrite ( handle , " scenario_policy=preserve_KV_no_probability_aggregation " ) > 0 &&
FileWrite ( handle , " teacher_policy=profitable_realized_teacher_and_random_supervised " ) > 0 &&
FileWrite ( handle , " no_trade_penalty=zero_lot_only_sub_min_lot_evaluated_at_stated_lot " ) > 0 &&
FileWrite ( handle , " account_execution=target_position_tp_sl_bar_lifecycle " ) > 0 &&
FileWrite ( handle , " critic_target=deal_outcome_tp_sl_horizon " ) > 0 &&
FileWrite ( handle , " critic_pretrain_target=deal_outcome_tp_sl_horizon " ) > 0 &&
FileWrite ( handle , " offline_target=tp_sl_horizon_discounted_by_elapsed_bars " ) > 0 &&
FileWrite ( handle , " critic_online_target=cross_target_td " ) > 0 &&
FileWrite ( handle , " critic_final_semantics=discounted_live_return " ) > 0 &&
FileWrite ( handle , " online_bootstrap_state=next_closed_bar " ) > 0 &&
FileWrite ( handle , StringFormat ( " deal_horizon_bars=%d " , SkillManifestDealHorizon )) > 0 &&
FileWrite ( handle , " ambiguous_level_rule=sl_before_tp_within_same_bar " ) > 0 &&
FileWrite ( handle , " actor_policy_gradient=executable_single_critic_sigmoid_derivative " ) > 0 &&
FileWrite ( handle , " critic_coverage=owner_split_seed_stable_offline_separate_online " ) > 0 &&
FileWrite ( handle , " actor_supervision=policy_srm_single_owner_critic_plus_teacher_random " ) > 0 &&
FileWrite ( handle , " offline_units=account_currency_money " ) > 0 &&
FileWrite ( handle , " online_reward=account_currency_equity_delta " ) > 0 &&
FileWrite ( handle , " online_target=cross_target_td_y_r_plus_gamma_sample_targetq " ) > 0 &&
FileWrite ( handle , StringFormat ( " online_discount=%.4f " , SkillManifestOnlineDiscount )) > 0 &&
FileWrite ( handle , " actor_supervision_gradient=combined_single_update_before_weight_change " ) > 0 &&
FileWrite ( handle , " policy_critic_selector=actor_event_hash_seed_balanced " ) > 0 &&
FileWrite ( handle , " risk_variable=return " ) > 0 &&
FileWrite ( handle , " cvar_convention=lower_tail_return " ) > 0 &&
FileWrite ( handle , " srm_ordering=weighted_monotone_rearrangement " ) > 0 &&
FileWrite ( handle , " srm_ordering_key=return_ascending_index_tiebreak " ) > 0 &&
FileWrite ( handle , " sample_semantics=discrete_fqf_representative " ) > 0 &&
FileWrite ( handle , StringFormat ( " quantiles=%u " , SkillManifestQuantiles )) > 0 &&
FileWrite ( handle , " srm_invalid_policy=skip_actor_update " ) > 0 &&
FileWrite ( handle , " target_network_type=sgd_shadow " ) > 0 &&
FileWrite ( handle , " target_update=periodic_hard_copy " ) > 0 &&
FileWrite ( handle , " target_crossing=q1_from_q2_q2_from_q1 " ) > 0 &&
FileWrite ( handle , StringFormat ( " target_tau=%.4f " , SkillManifestTargetTau )) > 0 &&
FileWrite ( handle , StringFormat ( " target_update_period=%d " , SkillManifestTargetUpdatePeriod )) > 0 &&
FileWrite ( handle , StringFormat ( " config_version=%u " , Skill_AC_CONFIG_VERSION )) > 0 &&
FileWrite ( handle , StringFormat ( " actor_hash=%I64u " , actor_hash )) > 0 &&
FileWrite ( handle , StringFormat ( " q1_hash=%I64u " , q1_hash )) > 0 &&
FileWrite ( handle , StringFormat ( " q2_hash=%I64u " , q2_hash )) > 0 &&
FileWrite ( handle , StringFormat ( " generation=%I64u " , ( ulong ) TimeCurrent ())) > 0 );
if ( written )
FileFlush ( handle );
FileClose ( handle );
return ( written );
}
//+------------------------------------------------------------------+
//| Writes the full AC manifest record. |
//+------------------------------------------------------------------+
bool SkillWriteACManifest ( const ulong forecast_signature )
{
return ( SkillWriteACManifestFile ( forecast_signature , Skill_ACTOR_FILE ,
Skill_Q1_FILE , Skill_Q2_FILE ,
Skill_AC_MANIFEST_FILE ));
}
//+------------------------------------------------------------------+
//| Validates the AC manifest tuple presence and fields. |
//+------------------------------------------------------------------+
bool SkillValidateACManifestTuple ( const bool required , const string manifest_file ,
const string actor_file , const string q1_file ,
const string q2_file , const bool allow_bank_reset )
{
if ( ! SkillACTupleNamesValid ( actor_file , q1_file , q2_file , manifest_file ) ||
SkillManifestValue ( manifest_file , " format " ) == " " )
return ( ! required );
#define Skill_AC_MANIFEST_EQ(KEY,VALUE) \
{ const string actual = SkillManifestValue ( manifest_file , KEY ); const string expected = ( VALUE ); \
if ( actual != expected ) { PrintFormat ( " Skill policy manifest: %s expected=%s actual=%s " , KEY , expected , actual ); ReturnFalse ; } }
Skill_AC_MANIFEST_EQ ( " format " , " ACSRM_ACTOR_CRITIC " );
Skill_AC_MANIFEST_EQ ( " version " , IntegerToString ( Skill_AC_FORMAT_VERSION ));
Skill_AC_MANIFEST_EQ ( " forecast_signature " , StringFormat ( " %I64u " , SkillLastSignature ));
Skill_AC_MANIFEST_EQ ( " actor_context " , IntegerToString (( 3 * EmbeddingSize )));
Skill_AC_MANIFEST_EQ ( " critic_context " , IntegerToString (( 5 * EmbeddingSize )));
Skill_AC_MANIFEST_EQ ( " action_order " , " BuyLot,BuyTP,BuySL,SellLot,SellTP,SellSL " );
Skill_AC_MANIFEST_EQ ( " scenario_policy " , " preserve_KV_no_probability_aggregation " );
Skill_AC_MANIFEST_EQ ( " teacher_policy " , " profitable_realized_teacher_and_random_supervised " );
Skill_AC_MANIFEST_EQ ( " no_trade_penalty " , " zero_lot_only_sub_min_lot_evaluated_at_stated_lot " );
Skill_AC_MANIFEST_EQ ( " account_execution " , " target_position_tp_sl_bar_lifecycle " );
Skill_AC_MANIFEST_EQ ( " critic_target " , " deal_outcome_tp_sl_horizon " );
Skill_AC_MANIFEST_EQ ( " offline_target " , " tp_sl_horizon_discounted_by_elapsed_bars " );
{ const string bootstrap = SkillManifestValue ( manifest_file , " online_bootstrap_state " );
if ( bootstrap != " " && bootstrap != " next_closed_bar " )
{ PrintFormat ( " Skill policy manifest: online_bootstrap_state expected=next_closed_bar actual=%s " , bootstrap ); ReturnFalse ; } }
{ const string pretrain = SkillManifestValue ( manifest_file , " critic_pretrain_target " );
if ( pretrain != " " && pretrain != " deal_outcome_tp_sl_horizon " )
{ PrintFormat ( " Skill policy manifest: critic_pretrain_target expected=deal_outcome_tp_sl_horizon actual=%s " , pretrain ); ReturnFalse ; } }
{ const string online_target = SkillManifestValue ( manifest_file , " critic_online_target " );
if ( online_target != " " && online_target != " cross_target_td " )
{ PrintFormat ( " Skill policy manifest: critic_online_target expected=cross_target_td actual=%s " , online_target ); ReturnFalse ; } }
{ const string final_semantics = SkillManifestValue ( manifest_file , " critic_final_semantics " );
if ( final_semantics != " " && final_semantics != " discounted_live_return " )
{ PrintFormat ( " Skill policy manifest: critic_final_semantics expected=discounted_live_return actual=%s " , final_semantics ); ReturnFalse ; } }
Skill_AC_MANIFEST_EQ ( " deal_horizon_bars " , IntegerToString ( SkillManifestDealHorizon ));
Skill_AC_MANIFEST_EQ ( " ambiguous_level_rule " , " sl_before_tp_within_same_bar " );
Skill_AC_MANIFEST_EQ ( " actor_policy_gradient " , " executable_single_critic_sigmoid_derivative " );
Skill_AC_MANIFEST_EQ ( " critic_coverage " , " owner_split_seed_stable_offline_separate_online " );
Skill_AC_MANIFEST_EQ ( " actor_supervision " , " policy_srm_single_owner_critic_plus_teacher_random " );
Skill_AC_MANIFEST_EQ ( " offline_units " , " account_currency_money " );
Skill_AC_MANIFEST_EQ ( " online_reward " , " account_currency_equity_delta " );
Skill_AC_MANIFEST_EQ ( " online_target " , " cross_target_td_y_r_plus_gamma_sample_targetq " );
Skill_AC_MANIFEST_EQ ( " online_discount " , StringFormat ( " %.4f " , SkillManifestOnlineDiscount ));
Skill_AC_MANIFEST_EQ ( " actor_supervision_gradient " , " combined_single_update_before_weight_change " );
Skill_AC_MANIFEST_EQ ( " policy_critic_selector " , " actor_event_hash_seed_balanced " );
Skill_AC_MANIFEST_EQ ( " risk_variable " , " return " );
Skill_AC_MANIFEST_EQ ( " cvar_convention " , " lower_tail_return " );
Skill_AC_MANIFEST_EQ ( " srm_ordering " , " weighted_monotone_rearrangement " );
Skill_AC_MANIFEST_EQ ( " srm_ordering_key " , " return_ascending_index_tiebreak " );
Skill_AC_MANIFEST_EQ ( " sample_semantics " , " discrete_fqf_representative " );
Skill_AC_MANIFEST_EQ ( " quantiles " , IntegerToString ( SkillManifestQuantiles ));
Skill_AC_MANIFEST_EQ ( " srm_invalid_policy " , " skip_actor_update " );
{ const string version_actual = SkillManifestValue ( manifest_file , " config_version " );
if ( version_actual != IntegerToString ( Skill_AC_CONFIG_VERSION ) && version_actual != " 7 " && version_actual != " 8 " && version_actual != " 9 " )
{ PrintFormat ( " Skill policy manifest: config_version expected=%s or 7 actual=%s " ,
IntegerToString ( Skill_AC_CONFIG_VERSION ), version_actual ); ReturnFalse ; } }
Skill_AC_MANIFEST_EQ ( " actor_hash " ,
StringFormat ( " %I64u " ,
SkillHashFile ( ulong ( 1469598103934665603 ), actor_file )));
Skill_AC_MANIFEST_EQ ( " q1_hash " ,
StringFormat ( " %I64u " ,
SkillHashFile ( ulong ( 1469598103934665603 ), q1_file )));
Skill_AC_MANIFEST_EQ ( " q2_hash " ,
StringFormat ( " %I64u " ,
SkillHashFile ( ulong ( 1469598103934665603 ), q2_file )));
#undef Skill_AC_MANIFEST_EQ
//--- Function if.
if ( SkillManifestValue ( manifest_file , " generation " ) == " " )
{
Print ( " Skill policy manifest: generation is absent " );
ReturnFalse ;
}
return ( true );
}
//+------------------------------------------------------------------+
//| Validates one AC manifest file. |
//+------------------------------------------------------------------+
bool SkillValidateACManifestFile ( const bool required , const string manifest_file ,
const bool allow_bank_reset )
{
return ( SkillValidateACManifestTuple ( required , manifest_file , Skill_ACTOR_FILE ,
Skill_Q1_FILE , Skill_Q2_FILE , allow_bank_reset ));
}
//+------------------------------------------------------------------+
//| Validates the complete AC manifest state. |
//+------------------------------------------------------------------+
bool SkillValidateACManifest ( const bool required )
{
return ( SkillValidateACManifestFile ( required , Skill_AC_MANIFEST_FILE , false ));
}
//+------------------------------------------------------------------+
//| Binds Actor and both Critics to the Market OpenCL context. |
//+------------------------------------------------------------------+
bool SkillBindPolicyOpenCLChecked ( CNet & actor , CNet & q1 , CNet & q2 )
{
COpenCLMy * target = SkillMarket . GetOpenCL ();
if ( CheckPointer ( target ) == POINTER_INVALID ||
! actor . SupportsOpenCLChecked () || ! q1 . SupportsOpenCLChecked () ||
! q2 . SupportsOpenCLChecked ())
ReturnFalse ;
if ( ! actor . SetOpenCLChecked ( target ) || ! q1 . SetOpenCLChecked ( target ) ||
! q2 . SetOpenCLChecked ( target ))
ReturnFalse ;
return ( true );
}
//+------------------------------------------------------------------+
//| Loads a policy net checkpoint from file. |
//+------------------------------------------------------------------+
bool SkillLoadPolicyNet ( CNet & net , const string file_name )
{
float error = 0 , undefine = 0 , forecast = 0 ;
datetime studied = 0 ;
return ( net . Load ( file_name , error , undefine , forecast , studied , true ));
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Closes the offline episode through paired utility then reset. |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| SkillCanPublishACCheckpoint validates replacement permission |
//+------------------------------------------------------------------+
bool SkillCanPublishACCheckpoint ( const bool canonical_exists ,
const bool canonical_valid )
{
if ( ! canonical_exists || canonical_valid )
return ( true );
return ( false );
}
//+------------------------------------------------------------------+
//| Checks whether a policy checkpoint can be created. |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Loads or creates the policy set file. |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Loads or creates the full policy set. |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Implements SkillSavePolicySet. |
//+------------------------------------------------------------------+
bool SkillSavePolicySet ( CNet & actor , CNet & q1 , CNet & q2 )
{
if ( ! SkillVerifyFrozenWeightsExact () || ! SkillVerifyFrozenCodebookExact () ||
SkillForecastSignature () != SkillLastSignature )
ReturnFalse ;
if ( ! SkillACRecoverTransaction ( Skill_ACTOR_FILE , Skill_Q1_FILE , Skill_Q2_FILE ,
Skill_AC_MANIFEST_FILE , Skill_ACTOR_NEXT_FILE ,
Skill_Q1_NEXT_FILE , Skill_Q2_NEXT_FILE ,
Skill_AC_MANIFEST_NEXT_FILE , Skill_ACTOR_PREVIOUS_FILE ,
Skill_Q1_PREVIOUS_FILE , Skill_Q2_PREVIOUS_FILE ,
Skill_AC_MANIFEST_PREVIOUS_FILE ,
Skill_AC_TRANSACTION_FILE ))
ReturnFalse ;
const bool canonical_exists = ( FileIsExist ( Skill_ACTOR_FILE , FILE_COMMON ) ||
FileIsExist ( Skill_Q1_FILE , FILE_COMMON ) ||
FileIsExist ( Skill_Q2_FILE , FILE_COMMON ) ||
FileIsExist ( Skill_AC_MANIFEST_FILE , FILE_COMMON ));
const bool canonical_valid = SkillValidateACManifestFile ( true , Skill_AC_MANIFEST_FILE , false );
const bool had_previous = canonical_valid ;
if ( ! SkillCanPublishACCheckpoint ( canonical_exists , canonical_valid ))
{
Print ( " Skill policy checkpoint=FAIL reason=canonical_tuple_invalid " );
ReturnFalse ;
}
if ( canonical_exists && ! canonical_valid )
Print ( " Skill policy checkpoint=RECREATE reason=canonical_tuple_invalid " );
const datetime now = TimeCurrent ();
//--- Stage every model and write the candidate manifest without touching the active tuple.
if ( ! actor . Save ( Skill_ACTOR_NEXT_FILE , 0 , 0 , 0 , now , true ))
ReturnFalseEx ( " Actor-Critic Actor candidate " );
if ( ! q1 . Save ( Skill_Q1_NEXT_FILE , q1 . getRecentAverageError (), 0 , 0 , now , true ))
ReturnFalseEx ( " Actor-Critic Q1 candidate " );
if ( ! q2 . Save ( Skill_Q2_NEXT_FILE , q2 . getRecentAverageError (), 0 , 0 , now , true ))
ReturnFalseEx ( " Actor-Critic Q2 candidate " );
if ( ! SkillWriteACManifestFile ( SkillLastSignature , Skill_ACTOR_NEXT_FILE ,
Skill_Q1_NEXT_FILE , Skill_Q2_NEXT_FILE ,
Skill_AC_MANIFEST_NEXT_FILE ))
ReturnFalseEx ( " Actor-Critic candidate manifest " );
if ( ! SkillValidateACManifestTuple ( true , Skill_AC_MANIFEST_NEXT_FILE ,
Skill_ACTOR_NEXT_FILE , Skill_Q1_NEXT_FILE ,
Skill_Q2_NEXT_FILE , false ))
ReturnFalseEx ( " Actor-Critic candidate validation " );
const ulong seed = ulong ( 1469598103934665603 );
const ulong actor_hash = SkillHashFile ( seed , Skill_ACTOR_NEXT_FILE );
const ulong q1_hash = SkillHashFile ( seed , Skill_Q1_NEXT_FILE );
const ulong q2_hash = SkillHashFile ( seed , Skill_Q2_NEXT_FILE );
const ulong manifest_hash = SkillHashFile ( seed , Skill_AC_MANIFEST_NEXT_FILE );
if ( ! SkillACWriteTransaction ( Skill_AC_TRANSACTION_FILE , had_previous , " prepared " ,
actor_hash , q1_hash , q2_hash , manifest_hash ))
ReturnFalse ;
if ( had_previous )
{
if ( ! SkillACCopyTuple ( Skill_ACTOR_FILE , Skill_Q1_FILE , Skill_Q2_FILE ,
Skill_AC_MANIFEST_FILE , Skill_ACTOR_PREVIOUS_FILE ,
Skill_Q1_PREVIOUS_FILE , Skill_Q2_PREVIOUS_FILE ,
Skill_AC_MANIFEST_PREVIOUS_FILE , false ) ||
! SkillValidateACManifestTuple ( true , Skill_AC_MANIFEST_PREVIOUS_FILE ,
Skill_ACTOR_PREVIOUS_FILE , Skill_Q1_PREVIOUS_FILE ,
Skill_Q2_PREVIOUS_FILE , false ) ||
! SkillACAdvanceTransaction ( Skill_AC_TRANSACTION_FILE , true , " previous_ready " ,
actor_hash , q1_hash , q2_hash , manifest_hash ))
ReturnFalse ;
}
//--- Publish the candidate tuple and validate it before removing recovery sidecars.
if ( ! SkillACCopyTuple ( Skill_ACTOR_NEXT_FILE , Skill_Q1_NEXT_FILE , Skill_Q2_NEXT_FILE ,
Skill_AC_MANIFEST_NEXT_FILE , Skill_ACTOR_FILE ,
Skill_Q1_FILE , Skill_Q2_FILE , Skill_AC_MANIFEST_FILE , true ))
ReturnFalseEx ( " Actor-Critic canonical publication " );
if ( ! SkillValidateACManifestFile ( true , Skill_AC_MANIFEST_FILE , false ))
ReturnFalseEx ( " Actor-Critic canonical validation " );
if ( SkillHashFile ( seed , Skill_ACTOR_FILE ) != actor_hash ||
SkillHashFile ( seed , Skill_Q1_FILE ) != q1_hash ||
SkillHashFile ( seed , Skill_Q2_FILE ) != q2_hash ||
SkillHashFile ( seed , Skill_AC_MANIFEST_FILE ) != manifest_hash )
ReturnFalseEx ( " Actor-Critic canonical hash " );
if ( ! SkillACCleanupTransaction ( Skill_AC_TRANSACTION_FILE , Skill_ACTOR_NEXT_FILE ,
Skill_Q1_NEXT_FILE , Skill_Q2_NEXT_FILE ,
Skill_AC_MANIFEST_NEXT_FILE , Skill_ACTOR_PREVIOUS_FILE ,
Skill_Q1_PREVIOUS_FILE , Skill_Q2_PREVIOUS_FILE ,
Skill_AC_MANIFEST_PREVIOUS_FILE ))
ReturnFalseEx ( " Actor-Critic transaction cleanup " );
return ( true );
}
//+---------------------------------------------------------------------+
//| Saves a Stage 03 checkpoint only for the active production ACSRM. |
//+---------------------------------------------------------------------+
bool SkillSaveStage03StopCheckpoint ( CNet & actor , CNet & q1 , CNet & q2 )
{
if ( ! SkillVerifyProductionACSRMFingerprints ())
ReturnFalse ;
return ( SkillSavePolicySet ( actor , q1 , q2 ));
}
//+------------------------------------------------------------------+
//| Implements SkillLoadInferenceActor. |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Implements ReadAction. |
//+------------------------------------------------------------------+
bool ReadAction ( CNet & net , CBufferFloat * target )
{
//--- This is the explicit device-to-CPU boundary for trade execution.
//--- The final actor layer is allowed to be host-only, therefore its live
//--- output must not be read through CBufferFloat::BufferRead().
if ( ! target )
ReturnFalse ;
//--- CNet::getResults reuses a valid result object. Passing target directly
//--- avoids an allocation and the subsequent CPU-to-CPU AssignArray copy.
CBufferFloat * output = target ;
net . getResults ( output );
return ( output == target && target . Total () == NActions );
}
//+------------------------------------------------------------------+
//| Implements SkillAdvanceAccountTime. |
//+------------------------------------------------------------------+
bool SkillAdvanceAccountTime ( CBufferFloat * current , const datetime next_time , CBufferFloat * next )
{
if ( ! current || ! next || current . Total () != AccountDescr ||
( current . GetIndex () >= 0 && ! current . BufferRead ()) ||
! next . BufferInit ( AccountDescr , 0 ))
ReturnFalse ;
for ( uint i = 0 ; i < 9 ; i ++ )
if ( ! next . Update ( i , current [ i ]))
ReturnFalse ;
double x = next_time / ( double )( D ' 2024.01.01 ' - D ' 2023.01.01 ' );
if ( ! next . Update ( 9 , float ( MathSin ( x != 0 ? 2.0 * M_PI * x : 0 ))))
ReturnFalse ;
x = next_time / ( double ) PeriodSeconds ( PERIOD_MN1 );
if ( ! next . Update ( 10 , float ( MathCos ( x != 0 ? 2.0 * M_PI * x : 0 ))))
ReturnFalse ;
x = next_time / ( double ) PeriodSeconds ( PERIOD_W1 );
if ( ! next . Update ( 11 , float ( MathSin ( x != 0 ? 2.0 * M_PI * x : 0 ))))
ReturnFalse ;
x = next_time / ( double ) PeriodSeconds ( PERIOD_D1 );
if ( ! next . Update ( 12 , float ( MathSin ( x != 0 ? 2.0 * M_PI * x : 0 ))))
ReturnFalse ;
return ( next . GetIndex () < 0 || next . BufferWrite ());
}
//+------------------------------------------------------------------+
//| Implements SkillForwardForecastState. |
//+------------------------------------------------------------------+
bool SkillForwardForecastState ( CBufferFloat * state )
{
//--- CNet accepts a host-only input buffer and uploads it through its first
//--- layer. Do not require a device allocation from the caller here.
if ( ! SkillForecast || ! state || state . Total () != HistoryBars * BarDescr )
ReturnFalse ;
if ( ! SkillMarket . feedForward ( state , 1 , false , ( CBufferFloat * ) NULL ))
ReturnFalse ;
CBufferFloat * z = SkillForecast . GetZ (), * u = SkillForecast . GetU (), * pi = SkillForecast . GetPi ();
if ( ! z || ! u || ! pi || z . Total () != NScenarios * BarDescr * NForecast * EmbeddingSize ||
u . Total () != NScenarios * BarDescr * NForecast || pi . Total () != NScenarios ||
z . GetIndex () < 0 || u . GetIndex () < 0 || pi . GetIndex () < 0 )
ReturnFalse ;
return ( true );
}
//+-----------------------------------------------------------------------+
//| Builds the live-account feature vector from balance/equity changes. |
//+-----------------------------------------------------------------------+
bool SkillBuildLiveAccount ( const double previous_balance , const double previous_equity ,
const datetime state_time , CBufferFloat * account ,
double & buy_value , double & sell_value )
{
if ( ! account || ! MathIsValidNumber ( previous_balance ) ||
! MathIsValidNumber ( previous_equity ) || previous_balance <= 0 || previous_equity <= 0 )
ReturnFalse ;
double buy_profit = 0 , sell_profit = 0 , position_discount = 0 ;
buy_value = 0 ;
sell_value = 0 ;
const datetime current = TimeCurrent ();
for ( int i = 0 ; i < PositionsTotal (); i ++ )
{
if ( PositionGetSymbol ( i ) != Symb . Name ())
continue ;
const double profit = PositionGetDouble ( POSITION_PROFIT );
if (( int ) PositionGetInteger ( POSITION_TYPE ) == POSITION_TYPE_BUY )
{ buy_value += PositionGetDouble ( POSITION_VOLUME ); buy_profit += profit ; }
else
{ sell_value += PositionGetDouble ( POSITION_VOLUME ); sell_profit += profit ; }
position_discount += ( current - PositionGetInteger ( POSITION_TIME )) *
( 1.0 / ( 60.0 * 60.0 * 10.0 )) * MathAbs ( profit );
}
vector < float > values = vector < float >:: Zeros ( AccountDescr );
const double balance = AccountInfoDouble ( ACCOUNT_BALANCE );
const double equity = AccountInfoDouble ( ACCOUNT_EQUITY );
values [ 0 ] = float ( balance / EtalonBalance );
values [ 1 ] = float (( balance - previous_balance ) / previous_balance );
values [ 2 ] = float ( equity / previous_balance );
values [ 3 ] = float (( equity - previous_equity ) / previous_equity );
values [ 4 ] = float ( buy_value );
values [ 5 ] = float ( sell_value );
values [ 6 ] = float ( buy_profit / previous_balance );
values [ 7 ] = float ( sell_profit / previous_balance );
values [ 8 ] = float ( position_discount / previous_balance );
double x = state_time / ( double )( D ' 2024.01.01 ' - D ' 2023.01.01 ' );
values [ 9 ] = float ( MathSin ( x != 0 ? 2.0 * M_PI * x : 0 ));
x = state_time / ( double ) PeriodSeconds ( PERIOD_MN1 );
values [ 10 ] = float ( MathCos ( x != 0 ? 2.0 * M_PI * x : 0 ));
x = state_time / ( double ) PeriodSeconds ( PERIOD_W1 );
values [ 11 ] = float ( MathSin ( x != 0 ? 2.0 * M_PI * x : 0 ));
x = state_time / ( double ) PeriodSeconds ( PERIOD_D1 );
values [ 12 ] = float ( MathSin ( x != 0 ? 2.0 * M_PI * x : 0 ));
for ( uint i = 0 ; i < AccountDescr ; i ++ )
if ( ! MathIsValidNumber ( values [ i ]))
ReturnFalse ;
return ( account . AssignArray ( values ) && ( account . GetIndex () < 0 || account . BufferWrite ()));
}
//+------------------------------------------------------------------+
//| Implements SkillRefreshLiveMarket. |
//+------------------------------------------------------------------+
bool SkillRefreshLiveMarket ( CBufferFloat * state , CBufferFloat * time_state )
{
const int requested = StackSize + HistoryBars ;
//--- A new-bar event is evaluated from the last fully closed bar. The
//--- forming bar must not leak unfinished OHLC/indicator values into policy.
const int bars = CopyRates ( Symb . Name (), TimeFrame , 1 , requested , Rates );
if ( ! state || ! time_state || bars < HistoryBars || ! ArraySetAsSeries ( Rates , true ) ||
! RSI . BufferResize ( bars ) || ! CCI . BufferResize ( bars ) ||
! ATR . BufferResize ( bars ) || ! MACD . BufferResize ( bars ) ||
RSI . BarsCalculated () < bars || CCI . BarsCalculated () < bars ||
ATR . BarsCalculated () < bars || MACD . BarsCalculated () < bars )
ReturnFalse ;
RSI . Refresh ();
CCI . Refresh ();
ATR . Refresh ();
MACD . Refresh ();
Symb . Refresh ();
Symb . RefreshRates ();
return ( CreateBuffers ( 0 , state , time_state , ( CBufferFloat * ) NULL ));
}
//+-------------------------------------------------------------------+
//| Checks ExecutableOrder. One execution contract for historical... |
//+-------------------------------------------------------------------+
bool IsExecutableOrder ( const double lot , const double tp_fraction , const double sl_fraction )
{
const double stops = ( MathMax ( Symb . StopsLevel (), 1 ) + Symb . Spread ()) * Symb . Point ();
return ( lot >= Symb . LotsMin () && tp_fraction * MaxTP * Symb . Point () > 2.0 * stops &&
sl_fraction * MaxSL * Symb . Point () > stops );
}
//+------------------------------------------------------------------+
//| Implements NormalizeLot. |
//+------------------------------------------------------------------+
double NormalizeLot ( const double lot )
{
const double min_lot = Symb . LotsMin (), step_lot = Symb . LotsStep ();
return ( step_lot > 0 ? min_lot + MathRound (( lot - min_lot ) / step_lot ) * step_lot : lot );
}
#ifndef Study
//+------------------------------------------------------------------+
//| Implements SkillValidateAction. |
//+------------------------------------------------------------------+
bool SkillValidateAction ( CBufferFloat * action )
{
if ( ! action || action . Total () != NActions ||
( action . GetIndex () >= 0 && ! action . BufferRead ()))
ReturnFalse ;
for ( uint i = 0 ; i < NActions ; i ++ )
if ( ! MathIsValidNumber ( action [ i ]) || action [ i ] < 0 || action [ i ] > 1 )
ReturnFalse ;
return ( true );
}
//+------------------------------------------------------------------------+
//| Executes the actor action, places the real deal and returns margins. |
//+------------------------------------------------------------------------+
bool SkillExecuteAction ( CBufferFloat * action , double buy_value , double sell_value , double & margin_penalty ,
bool & market_closed )
{
margin_penalty = 0 ;
market_closed = false ;
if ( ! SkillValidateAction ( action ))
ReturnFalse ;
//--- Canonical CogDriver mutual exclusion and broker constraints.
if ( action [ 0 ] >= action [ 3 ])
{ action . Update ( 0 , ( action [ 0 ] - action [ 3 ])); action . Update ( 3 , 0 ); }
else
{ action . Update ( 3 , ( action [ 3 ] - action [ 0 ])); action . Update ( 0 , 0 ); }
//---
const double min_lot = Symb . LotsMin ();
if ( ! IsExecutableOrder ( action [ 0 ], action [ 1 ], action [ 2 ]))
{ if ( buy_value > 0 ) CloseByDirection ( POSITION_TYPE_BUY ); }
else
{
const double lot = NormalizeLot ( action [ 0 ]);
const double tp = NormalizeDouble ( Symb . Ask () + action [ 1 ] * MaxTP * Symb . Point (), Symb . Digits ());
const double sl = NormalizeDouble ( Symb . Ask () - action [ 2 ] * MaxSL * Symb . Point (), Symb . Digits ());
if ( buy_value > 0 )
TrailPosition ( POSITION_TYPE_BUY , sl , tp );
if (( buy_value - lot ) >= min_lot )
ClosePartial ( POSITION_TYPE_BUY , buy_value - lot );
else
//--- Function if.
if (( lot - buy_value ) >= min_lot && ! Trade . Buy ( lot - buy_value , Symb . Name (), Symb . Ask (), sl , tp ))
{
const uint retcode = Trade . ResultRetcode ();
//--- Function if.
if ( retcode == TRADE_RETCODE_MARKET_CLOSED )
{
market_closed = true ;
return ( true );
}
//--- Function if.
if ( retcode != 10019 )
{
PrintFormat ( " Skill buy execution failed: retcode=%u %s " , retcode , Trade . ResultRetcodeDescription ());
ReturnFalse ;
}
//--- Preserve CogDriver's insufficient-margin feedback, but keep the
//--- transition alive: the order was rejected, not the account.
margin_penalty -= 100.0 * ( lot - buy_value );
}
}
if ( ! IsExecutableOrder ( action [ 3 ], action [ 4 ], action [ 5 ]))
{ if ( sell_value > 0 ) CloseByDirection ( POSITION_TYPE_SELL ); }
else
{
const double lot = NormalizeLot ( action [ 3 ]);
const double tp = NormalizeDouble ( Symb . Bid () - action [ 4 ] * MaxTP * Symb . Point (), Symb . Digits ());
const double sl = NormalizeDouble ( Symb . Bid () + action [ 5 ] * MaxSL * Symb . Point (), Symb . Digits ());
if ( sell_value > 0 )
TrailPosition ( POSITION_TYPE_SELL , sl , tp );
if (( sell_value - lot ) >= min_lot )
ClosePartial ( POSITION_TYPE_SELL , sell_value - lot );
else
//--- Function if.
if (( lot - sell_value ) >= min_lot && ! Trade . Sell ( lot - sell_value , Symb . Name (), Symb . Bid (), sl , tp ))
{
const uint retcode = Trade . ResultRetcode ();
//--- Function if.
if ( retcode == TRADE_RETCODE_MARKET_CLOSED )
{
market_closed = true ;
return ( true );
}
//--- Function if.
if ( retcode != 10019 )
{
PrintFormat ( " Skill sell execution failed: retcode=%u %s " , retcode , Trade . ResultRetcodeDescription ());
ReturnFalse ;
}
margin_penalty -= 100.0 * ( lot - sell_value );
}
}
return ( true );
}
//+-------------------------------------------------------------------+
//| Implements SkillExecuteAction. Inference callers do not train... |
//+-------------------------------------------------------------------+
bool SkillExecuteAction ( CBufferFloat * action , double buy_value , double sell_value )
{
double margin_penalty = 0 ;
bool market_closed = false ;
return ( SkillExecuteAction ( action , buy_value , sell_value , margin_penalty , market_closed ));
}
#endif
//+----------------------------------------------------------------------+
//| Finalizes projected balance before writing terminal account state. |
//+----------------------------------------------------------------------+
bool SkillFinalizeAccountBalance ( const double balance , const double realized ,
const double min_balance , double & next_balance ,
bool & terminal )
{
if ( ! MathIsValidNumber ( balance ) || ! MathIsValidNumber ( realized ) ||
! MathIsValidNumber ( min_balance ) || min_balance < 0.0 )
ReturnFalse ;
const double projected_balance = balance + realized ;
if ( ! MathIsValidNumber ( projected_balance ))
ReturnFalse ;
terminal = ( projected_balance <= min_balance );
next_balance = ( terminal ? min_balance : projected_balance );
return ( true );
}
//+------------------------------------------------------------------+
//| One historical-bar account transition. CheckAction remains the |
//+------------------------------------------------------------------+
bool AdvanceAccount ( CBufferFloat * current , CBufferFloat * action , const int position ,
const double min_balance , CBufferFloat * next , double & reward , bool & terminal )
{
terminal = true ;
reward = 0 ;
if ( ! current || ! action || ! next || position <= 0 || position >= int ( Rates . Size ()) ||
current . Total () != AccountDescr || action . Total () != NActions ||
( current . GetIndex () >= 0 && ! current . BufferRead ()) ||
( action . GetIndex () >= 0 && ! action . BufferRead ()))
ReturnFalse ;
const double balance = MathMax ( 0.0 , double ( current [ 0 ]) * EtalonBalance );
const double buy = MathMax ( 0.0 , double ( action [ 0 ] - action [ 3 ]));
const double sell = MathMax ( 0.0 , double ( action [ 3 ] - action [ 0 ]));
double margin = 0 ;
if ( ! OrderCalcMargin ( ORDER_TYPE_BUY , Symb . Name (), 1 , Rates [ position ]. open , margin ))
ReturnFalse ;
const double min_lot = Symb . LotsMin ();
//--- Function if.
if ( balance <= min_balance || balance < margin * min_lot )
{
terminal = true ;
return ( SkillAdvanceAccountTime ( current , Rates [ position - 1 ]. time , next ));
}
//--- Action is the target position for the next bar. A valid same-direction
//--- action modifies its TP/SL; a valid opposite action closes then reopens;
//--- an invalid action closes both positions.
const bool open_buy = IsExecutableOrder ( buy , action [ 1 ], action [ 2 ]);
const bool open_sell = IsExecutableOrder ( sell , action [ 4 ], action [ 5 ]);
//--- Unified volume contract: the environment evaluates the STATED
//--- continuous volume (money P&L is linear in the lot), never rounded up
//--- to LotsMin. Broker executability stays the IsExecutableOrder gate and
//--- a separate Test-stage statistic, never a learning-label property.
const double target_buy = ( open_buy ? buy : 0.0 );
const double target_sell = ( open_sell ? sell : 0.0 );
const double point_cost = Symb . TickValue () / Symb . TickSize ();
const double entry = Rates [ position ]. open ;
const double spread = Symb . Spread () * Symb . Point ();
const double current_buy = MathMax ( 0.0 , double ( current [ 4 ]));
const double current_sell = MathMax ( 0.0 , double ( current [ 5 ]));
double current_buy_profit = double ( current [ 6 ]) * balance ;
double current_sell_profit = double ( current [ 7 ]) * balance ;
double next_buy = 0 , next_sell = 0 ;
double next_buy_profit = 0 , next_sell_profit = 0 ;
double realized = 0 ;
//--- An invalid target liquidates every open position at the curre...
if ( ! open_buy && ! open_sell )
{
realized = current_buy_profit + current_sell_profit ;
}
else
//--- Function if.
if ( open_buy )
{
//--- A reverse target closes the Sell; a same-side reduction realizes only its
//--- proportional carried P/L. The retained Buy keeps its marked-to-market P/L.
realized += current_sell_profit ;
//--- Function if.
if ( current_buy > target_buy && current_buy > 0 )
{
const double closed = current_buy - target_buy ;
realized += current_buy_profit * closed / current_buy ;
current_buy_profit -= current_buy_profit * closed / current_buy ;
}
const double added = MathMax ( 0.0 , target_buy - current_buy );
current_buy_profit -= spread * point_cost * added ;
const double tp = entry + ( action [ 1 ] * MaxTP + Symb . Spread ()) * Symb . Point ();
const double sl = entry - ( action [ 2 ] * MaxSL + Symb . Spread ()) * Symb . Point ();
const MqlRates bar = Rates [ position ];
if ( sl >= bar . low )
realized += current_buy_profit + ( sl - entry ) * point_cost * target_buy ;
else
if ( tp <= bar . high )
realized += current_buy_profit + ( tp - entry ) * point_cost * target_buy ;
else
{
next_buy = target_buy ;
next_buy_profit = current_buy_profit + ( Rates [ position - 1 ]. open - entry ) * point_cost * target_buy ;
}
}
else
{
//--- Symmetric Sell lifecycle. The SL-before-TP order matches CheckAction.
realized += current_buy_profit ;
//--- Function if.
if ( current_sell > target_sell && current_sell > 0 )
{
const double closed = current_sell - target_sell ;
realized += current_sell_profit * closed / current_sell ;
current_sell_profit -= current_sell_profit * closed / current_sell ;
}
const double added = MathMax ( 0.0 , target_sell - current_sell );
current_sell_profit -= spread * point_cost * added ;
const double tp = entry - ( action [ 4 ] * MaxTP + Symb . Spread ()) * Symb . Point ();
const double sl = entry + ( action [ 5 ] * MaxSL + Symb . Spread ()) * Symb . Point ();
const MqlRates bar = Rates [ position ];
if ( sl <= bar . high )
realized += current_sell_profit + ( entry - sl ) * point_cost * target_sell ;
else
if ( tp >= bar . low )
realized += current_sell_profit + ( entry - tp ) * point_cost * target_sell ;
else
{
next_sell = target_sell ;
next_sell_profit = current_sell_profit + ( entry - Rates [ position - 1 ]. open ) * point_cost * target_sell ;
}
}
double next_balance = 0.0 ;
bool balance_terminal = false ;
if ( ! SkillFinalizeAccountBalance ( balance , realized , min_balance , next_balance , balance_terminal ))
ReturnFalse ;
const double current_equity = balance + double ( current [ 6 ]) * balance + double ( current [ 7 ]) * balance ;
const double next_equity = next_balance + next_buy_profit + next_sell_profit ;
//--- Causal one-step equity change in ACCOUNT-CURRENCY units is the
//--- transition reward. The offline deal label is also account currency,
//--- so the online bootstrap y = r + gamma * Sample(TargetQ_i) keeps one
//--- explicit dimension base everywhere (an earlier relative reward was
//--- added to money-dimensioned distribution samples).
reward = ( next_equity - current_equity );
if ( ! MathIsValidNumber ( reward ))
ReturnFalse ;
if ( ! SkillAdvanceAccountTime ( current , Rates [ position - 1 ]. time , next ) ||
( next . GetIndex () >= 0 && ! next . BufferRead ()))
ReturnFalse ;
if ( ! next . Update ( 0 , float ( next_balance / EtalonBalance )) ||
! next . Update ( 1 , float (( next_balance - balance ) / MathMax ( balance , 1.0 ))) ||
! next . Update ( 2 , float ( next_equity / MathMax ( balance , 1.0 ))) ||
! next . Update ( 3 , float (( next_equity - current_equity ) / MathMax ( MathAbs ( current_equity ), 1.0 ))) ||
! next . Update ( 4 , float ( next_buy )) || ! next . Update ( 5 , float ( next_sell )) ||
! next . Update ( 6 , float ( next_buy_profit / MathMax ( next_balance , 1.0 ))) ||
! next . Update ( 7 , float ( next_sell_profit / MathMax ( next_balance , 1.0 ))) ||
! next . Update ( 8 , 0.0f ))
ReturnFalse ;
terminal = ( balance_terminal || next_equity < margin * min_lot );
return ( next . GetIndex () < 0 || next . BufferWrite ());
}
2026-09-29 18:12:11 +03:00
//+------------------------------------------------------------------+