2026-07-14 22:36:27 -04:00
//+------------------------------------------------------------------+
//| Warrior_EA |
//| AnimateDread |
//| |
//+------------------------------------------------------------------+
//--- database classes
# include "Database\DatabaseManager.mqh"
//--- available custom classes
# include "Expert\ExpertCustom.mqh"
# include "System\PrintVerbose.mqh"
2026-07-17 21:28:59 -04:00
# include "System\StatusLabel.mqh"
2026-07-14 22:36:27 -04:00
//--- available signals
# include "Signals\Signals.mqh"
//--- available trailing
# include "Trailing\Trailing.mqh"
//--- available money management
# include "Money\Money.mqh"
//--- Inputs
# include "Variables\Inputs.mqh"
//--- Variables
# include "Variables\Variables.mqh"
//--- Control panel GUI (standard MQL5 Controls library)
# include "Panel\ControlPanel.mqh"
//+------------------------------------------------------------------+
//| The 5 CustomIndicators\*.mq5 files (ADCumulativeDelta, |
//| ADShorteningOfThrust, ADWyckoffEventStream, |
//| ADWyckoffFailedStructure, ADWyckoffSignificantBarInversion) are |
//| loaded via CiCustom/IND_CUSTOM (see ExpertSignalAIBase.mqh), which |
//| - like #import for DLLs - resolves them from |
//| <terminal data folder>\MQL5\Indicators\ at call time. MQL5 no |
//| longer allows a running program to write outside its own |
//| MQL5\Files\ sandbox (mirroring the WarriorCPU.dll/WarriorDML.dll |
//| restriction in AI\Network.mqh), so auto-extracting them there from |
//| an embedded #resource is no longer possible. Compile each |
//| CustomIndicators\*.mq5 once in MetaEditor (or copy the already- |
//| compiled .ex5) directly into MQL5\Indicators\ alongside this EA |
//| before enabling any EnableAD* input - InitADCumulativeDelta() etc. |
//| below will simply fail CiCustom::Create() with a clear log message |
//| if the matching .ex5 isn't there. |
//+------------------------------------------------------------------+
//
CExpertCustom Expert ;
CDatabaseManager dbm ( ) ;
//+------------------------------------------------------------------+
//| Pointers to whichever AI signal instances this run actually |
//| created (per AIType - PAI/CONV/LSTM, any subset), so the control |
//| panel can drive training/weight actions on exactly the signal(s) |
//| in play this run and never touch another config's files. |
//+------------------------------------------------------------------+
# define MAX_AI_SIGNALS 3
CExpertSignalAIBase * g_aiSignals [ MAX_AI_SIGNALS ] ;
int g_aiSignalCount = 0 ;
void RegisterAISignal ( CExpertSignalAIBase * sig )
{
if ( sig = = NULL | | g_aiSignalCount > = MAX_AI_SIGNALS )
return ;
g_aiSignals [ g_aiSignalCount + + ] = sig ;
}
//+------------------------------------------------------------------+
//| Control panel: a CAppDialog-based GUI (see Panel\ControlPanel.mqh) |
//| with show/hide signals, start/pause/stop training, and save/load/ |
//| delete-reset weights buttons for the currently-active AI |
//| signal(s) only. The dialog's own caption bar provides the show/ |
//| hide (minimize) control - no separate toggle button needed. |
//+------------------------------------------------------------------+
2026-07-17 21:28:59 -04:00
//--- default spawn position: top-right corner, clear of the status label text block (top-left) so the
2026-07-14 22:36:27 -04:00
//--- two don't overlap on first run - the panel is fully draggable afterwards via its caption bar,
//--- so this is only a starting point, not a constraint.
# define CP_Y0 10
# define CP_RIGHT_MARGIN 80
CControlPanel ExtPanel ;
bool g_signalsVisible = true ;
//--- tracks the last known AlgoTrading permission state (terminal "Algo Trading" toggle AND this
//--- EA's own "Allow Algo Trading" property) so a change is logged exactly once, not spammed every tick
bool g_lastAlgoTradingAllowed = true ;
//--- OnDeinit() is not guaranteed to run on a terminal crash/power loss/forced kill, so weights
//--- would otherwise only be as fresh as the last fully-completed training era. A periodic
//--- autosave closes that gap without depending on UseDatabaseRanking's timer.
# define AUTOSAVE_INTERVAL_SECONDS 300
datetime g_lastAutosave = 0 ;
//--- summarizes state across all currently-active AI signals for button labels;
//--- "paused"/"stopped" only report true if EVERY active signal agrees, so a mixed state
//--- (e.g. AIType=All with one signal paused and one running) still shows an actionable label
bool AllTrainingPaused ( void )
{
if ( g_aiSignalCount = = 0 )
return false ;
for ( int i = 0 ; i < g_aiSignalCount ; i + + )
if ( ! g_aiSignals [ i ] . IsTrainingPaused ( ) )
return false ;
return true ;
}
bool AllTrainingStopped ( void )
{
if ( g_aiSignalCount = = 0 )
return false ;
for ( int i = 0 ; i < g_aiSignalCount ; i + + )
if ( ! g_aiSignals [ i ] . IsTrainingStopped ( ) )
return false ;
return true ;
}
void ApplySignalsVisibility ( void )
{
for ( int i = ObjectsTotal ( 0 , 0 , OBJ_ARROW ) - 1 ; i > = 0 ; i - - )
{
string name = ObjectName ( 0 , i , 0 , OBJ_ARROW ) ;
ObjectSetInteger ( 0 , name , OBJPROP_TIMEFRAMES , g_signalsVisible ? OBJ_ALL_PERIODS : OBJ_NO_PERIODS ) ;
}
ChartRedraw ( 0 ) ;
}
void ToggleSignalsVisibility ( void )
{
g_signalsVisible = ! g_signalsVisible ;
ApplySignalsVisibility ( ) ;
}
//--- keeps every button's label in sync with live training/signal-visibility state; safe/cheap to
//--- call after every panel action
void RefreshControlPanelLabels ( void )
{
ExtPanel . SetSignalsText ( g_signalsVisible ? " Hide Signals " : " Show Signals " ) ;
bool noAI = ( g_aiSignalCount = = 0 ) ;
ExtPanel . SetPauseText ( noAI ? " Pause Training (n/a) " : ( AllTrainingPaused ( ) ? " Resume Training " : " Pause Training " ) ) ;
ExtPanel . SetStopText ( noAI ? " Stop Training (n/a) " : ( AllTrainingStopped ( ) ? " Start Training " : " Stop Training " ) ) ;
ChartRedraw ( 0 ) ;
}
//--- creates the control panel dialog once, from OnInit() - the standard CAppDialog usage pattern
//--- (create in OnInit, destroy in OnDeinit; see Controls\Dialog.mqh). CAppDialog::Destroy(REASON_PROGRAM)
//--- calls ExpertRemove() once the dialog has a valid PROGRAM_EXPERT type (i.e. on any call after the
//--- first successful Create()), so it must never be called speculatively/defensively before Create() -
//--- doing so would silently detach this EA from the chart the next time this function ran.
bool CreateControlPanel ( void )
{
ResetLastError ( ) ;
long chartWidth = ChartGetInteger ( 0 , CHART_WIDTH_IN_PIXELS ) ;
int panelX1 = ( chartWidth > CP_PANEL_W + CP_RIGHT_MARGIN + 20 ) ? ( int ) ( chartWidth - CP_PANEL_W - CP_RIGHT_MARGIN ) : 10 ;
if ( ! ExtPanel . Create ( 0 , " WarriorCP " , 0 , panelX1 , CP_Y0 , panelX1 + CP_PANEL_W , CP_Y0 + CP_PANEL_H ) )
{
Print ( __FUNCTION__ + " : failed to create control panel, error " + IntegerToString ( GetLastError ( ) ) ) ;
return false ;
}
if ( ! ExtPanel . Run ( ) )
{
Print ( __FUNCTION__ + " : failed to run control panel, error " + IntegerToString ( GetLastError ( ) ) ) ;
return false ;
}
ExtPanel . ForceMaximize ( ) ;
RefreshControlPanelLabels ( ) ;
return true ;
}
//--- performs whatever button action ExtPanel recorded (see ConsumeAction() in ControlPanel.mqh);
//--- a no-op when nothing was clicked since the last call
void HandleControlPanelAction ( ENUM_CP_ACTION action )
{
switch ( action )
{
case CP_ACTION_TOGGLE_SIGNALS :
ToggleSignalsVisibility ( ) ;
RefreshControlPanelLabels ( ) ;
break ;
case CP_ACTION_TOGGLE_PAUSE :
{
bool pause = ! AllTrainingPaused ( ) ;
for ( int i = 0 ; i < g_aiSignalCount ; i + + )
if ( pause )
g_aiSignals [ i ] . PauseTraining ( ) ;
else
g_aiSignals [ i ] . ResumeTraining ( ) ;
RefreshControlPanelLabels ( ) ;
break ;
}
case CP_ACTION_TOGGLE_STOP :
{
bool doStop = ! AllTrainingStopped ( ) ;
for ( int i = 0 ; i < g_aiSignalCount ; i + + )
if ( doStop )
g_aiSignals [ i ] . StopTraining ( ) ;
else
g_aiSignals [ i ] . StartTraining ( ) ;
RefreshControlPanelLabels ( ) ;
break ;
}
case CP_ACTION_SAVE :
for ( int i = 0 ; i < g_aiSignalCount ; i + + )
g_aiSignals [ i ] . SaveWeightsNow ( ) ;
break ;
case CP_ACTION_LOAD :
for ( int i = 0 ; i < g_aiSignalCount ; i + + )
g_aiSignals [ i ] . LoadWeightsNow ( ) ;
break ;
case CP_ACTION_RESET :
for ( int i = 0 ; i < g_aiSignalCount ; i + + )
g_aiSignals [ i ] . ResetWeights ( ) ;
RefreshControlPanelLabels ( ) ;
break ;
default :
break ;
}
}
// Helper function to pause execution for a random duration between 1 to 3 seconds
void RandomSleep ( )
{
Sleep ( MathRand ( ) % 2000 + 1000 ) ; // Sleeps between 1000ms (1s) and 3000ms (3s)
}
2026-07-17 23:21:12 -04:00
//+------------------------------------------------------------------+
//| Verifies real Depth of Market data is actually available for |
//| `symbol` before anything else (including neural network init) |
//| depends on it. MarketBookAdd() succeeding only means the |
//| SUBSCRIPTION was accepted - some brokers/symbols accept the |
//| subscription but never actually stream book data, so this also |
//| polls MarketBookGet() briefly for a genuinely non-empty snapshot |
//| before trusting it. Alerts the user (popup) and cleans up its own |
//| subscription on any failure so the caller can simply treat |
//| EnableMarketDepth as false for the rest of this run. |
//+------------------------------------------------------------------+
bool CheckMarketDepthAvailability ( string symbol )
{
if ( ! MarketBookAdd ( symbol ) )
{
Alert ( " Warrior EA: Market Depth (DOM) is not available for " + symbol + " - the order-book confirmation filter will be omitted for this run. Your broker/account may not provide Depth of Market for this symbol. " ) ;
Print ( " Warrior EA: MarketBookAdd( " + symbol + " ) failed, error " + IntegerToString ( GetLastError ( ) ) + " - EnableMarketDepth will be treated as false. " ) ;
return false ;
}
MqlBookInfo book [ ] ;
bool populated = false ;
for ( int attempt = 0 ; attempt < 10 & & ! populated ; attempt + + )
{
if ( MarketBookGet ( symbol , book ) & & ArraySize ( book ) > 0 )
populated = true ;
else
Sleep ( 200 ) ;
}
if ( ! populated )
{
MarketBookRelease ( symbol ) ;
Alert ( " Warrior EA: Market Depth (DOM) subscribed for " + symbol + " but returned no data - your broker likely does not provide real Depth of Market for this symbol. The order-book confirmation filter will be omitted for this run. " ) ;
Print ( " Warrior EA: MarketBookGet( " + symbol + " ) returned no levels after 2s - EnableMarketDepth will be treated as false. " ) ;
return false ;
}
Print ( " Warrior EA: Market Depth (DOM) available for " + symbol + " - order-book confirmation filter enabled. " ) ;
return true ;
}
2026-07-14 22:36:27 -04:00
// Helper function to retry signal creation with error handling
template < typename TSignal >
TSignal * CreateSignalWithRetry ( int maxRetries , bool enableFlag )
{
if ( ! enableFlag )
return NULL ;
TSignal * signal = NULL ;
for ( int tries = 0 ; tries < maxRetries ; + + tries )
{
signal = new TSignal ;
if ( signal = = NULL )
{
Print ( " Initialization of signal failed, retrying... " ) ;
RandomSleep ( ) ;
}
else
break ;
}
if ( signal = = NULL )
{
Print ( " Failed to create and initialize signal after retries " ) ;
}
return signal ;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-07-16 01:12:37 -04:00
//+------------------------------------------------------------------+
//| IMPORTANT: no failure branch below (nor in any helper it calls - |
//| AddFilterToSignal(), InitializeSignal(), InitializeTrailing(), |
//| InitializeMoneyManagement()) may call Expert.Deinit() before |
//| returning INIT_FAILED/false. MQL5 ALWAYS calls this EA's own |
//| OnDeinit(REASON_INITFAILED) automatically once OnInit() returns |
//| anything other than INIT_SUCCEEDED, and OnDeinit() already calls |
//| Expert.Deinit() itself. Expert.Deinit() tears down `signal` and, |
//| through it, every registered AI signal (PAI/CONV/LSTM - see |
//| g_aiSignals' declaration comment) - calling it a second time here |
//| would free those objects while g_aiSignals[] still points at them, |
//| and OnDeinit()'s own PersistOnShutdown() loop over g_aiSignals[] |
//| would then dereference already-freed pointers. This is exactly |
//| what "invalid pointer access" during OnDeinit() after a failed |
//| OnInit() means if it ever recurs - the fix is to remove whichever |
//| inline Expert.Deinit() call was re-added, not to guard the loop. |
//+------------------------------------------------------------------+
2026-07-14 22:36:27 -04:00
int OnInit ( )
{
2026-07-17 21:28:59 -04:00
//--- clears out whatever status label text was left over from before this OnInit() ran (stale text
//--- from a prior "warm" re-init - e.g. an input-parameter change, which reuses this same running
//--- instance rather than a fresh one - would otherwise sit unchanged and look like nothing is
//--- happening) so it's obvious the moment training/signal init actually resumes producing new status text
SetStatusLabel ( " Warrior EA: initializing... " ) ;
2026-07-14 22:36:27 -04:00
int maxRetryOnError = 5 ;
string functionName = __FUNCTION__ ;
// Initialize random seed based on the number of milliseconds since the system started
MathSrand ( GetTickCount ( ) ) ;
// Initialize expert
bool expertInitialized = false ;
for ( int tries = 0 ; ! expertInitialized & & tries < 5 ; + + tries )
{
if ( ! Expert .Init ( Symbol ( ) , Period ( ) , Expert_EveryTick , Expert_MagicNumber ) )
{
Print ( functionName + " : Failed initializing expert, retrying... " ) ;
RandomSleep ( ) ;
}
else
{
expertInitialized = true ;
break ;
}
}
if ( ! expertInitialized )
{
Print ( functionName + " : Failed to initialize expert after retries " ) ;
return INIT_FAILED ;
}
Expert . OnChartEventProcess ( true ) ;
2026-07-17 23:21:12 -04:00
//--- Market Depth availability check - deliberately BEFORE any signal (and therefore any neural
//--- network) is created below, per the same "before initializing neurons" requirement as the
//--- AI models themselves. g_marketDepthAvailable stays false (its declaration default) if
//--- EnableMarketDepth is off, so nothing below needs to special-case that.
if ( EnableMarketDepth )
g_marketDepthAvailable = CheckMarketDepthAvailability ( _Symbol ) ;
2026-07-14 22:36:27 -04:00
// Creating signal
PrintVerbose ( " Initializing Signal... " ) ;
CExpertSignalCustom * signal = NULL ;
for ( int tries = 0 ; signal = = NULL & & tries < 5 ; + + tries )
{
signal = new CExpertSignalCustom ;
if ( signal = = NULL )
{
Print ( functionName + " : Failed to initialize Signal, retrying... " ) ;
RandomSleep ( ) ;
}
else
{
break ;
}
}
if ( signal = = NULL )
{
Print ( functionName + " : Failed to initialize Signal after retries " ) ;
return INIT_FAILED ;
}
InitializeSignal ( signal ) ;
// Initializing Database
if ( UseDatabaseRanking )
{
bool dbInitialized = false ;
string databaseFolderStructure [ ] = { eaName , " Databases " , " Signals " } ;
const string dbName = Symbol ( ) + " _ " + IntegerToString ( Period ( ) ) + " .db " ;
const string dbVersion = " 2.0 " ;
PrintVerbose ( " Initializing Database... " ) ;
for ( int tries = 0 ; ! dbInitialized & & tries < 5 ; + + tries )
{
if ( ! dbm .Init ( dbVersion , databaseFolderStructure , dbName ) )
{
Print ( functionName + " : Failed to initialize Database, retrying... " ) ;
RandomSleep ( ) ;
}
else
{
dbInitialized = true ;
break ;
}
}
if ( ! dbInitialized )
{
Print ( functionName + " : Failed to initialize Database after retries " ) ;
return INIT_FAILED ;
}
}
2026-07-16 00:56:33 -04:00
//+------------------------------------------------------------------+
//| AIType selects which of the 3 AI signal models this run trades/ |
//| trains, or all 3 at once under HYBRID: |
//| - PAI (CSignalPAI): plain multi-layer Perceptron - input layer |
//| feeds straight into the tapering Dense hidden-layer stack, no |
//| Conv/Pool/LSTM stage. The baseline/cheapest model. |
//| - CONV (CSignalCONV): Conv+Pool front-end ahead of the same |
//| tapering Dense stack - looks for local price-action patterns |
//| (candlestick/short-range shapes) before the dense layers see |
//| them. |
//| - LSTM (CSignalLSTM): a single LSTM layer ahead of the same |
//| tapering Dense stack - genuine forget/input/output-gated |
//| recurrence (see AI\Network.mqh's CNeuronLSTM/CNeuronLSTMOCL), |
//| for sequential/regime-dependent structure the other two can't |
//| see across bars. |
//| All 3 share one CExpertSignalCustom "signal" (see |
//| InitializeSignal()/AddFilterToSignal() below) via the standard |
//| MQL5 wizard signal-aggregation pattern: each is added as a |
//| weighted filter, and the aggregate signal's Direction()/SLTP just |
//| blends whichever of them are active. Concurrency model: MQL5 is |
//| single-threaded per chart - OnTick()/OnTimer() never run |
//| re-entrantly, so PAI/CONV/LSTM never race each other inside this |
//| EA's own code; PollTraining() below just calls each in turn every |
//| timer tick. The one real concurrency-relevant boundary is the |
//| native compute backend (WarriorCPU.dll/WarriorDML.dll, see |
//| AI\Network.mqh) - each CNet (there can be up to 6 alive at once |
//| under HYBRID: 3 signals x live+shadow net each) gets its OWN |
//| opaque per-instance context handle with no shared/global DLL |
//| state, so a fault or watchdog-kill against one can never poison |
2026-07-16 13:51:28 -04:00
//| another's calls. Each CNet's WarriorCPU.dll worker pool is sized |
//| directly off the single global TargetCPULoad input, undivided, |
//| even though several pools can be alive at once - see |
//| AI\Network.mqh's TargetCPULoad declaration comment for why that's |
//| safe: MQL5's single execution thread per chart means only one |
//| pool is EVER actively computing at a time, so there is no real |
//| contention to divide the budget across. |
2026-07-16 00:56:33 -04:00
//| Memory ownership: PAI/CONV/LSTM are allocated here with `new` and |
//| registered into g_aiSignals[] for the control panel's benefit, but |
//| g_aiSignals[] does NOT own them - AddFilterToSignal() below adds |
//| each to `signal`'s own filter array (CExpertSignal::AddFilter()), |
//| which frees its elements on destruction; `signal` itself is owned |
//| by Expert (InitializeSignal() -> Expert.InitSignal()). So the |
//| actual free happens via Expert.Deinit() (see OnDeinit() below) |
//| tearing down signal -> its filter array -> PAI/CONV/LSTM, exactly |
//| once each - g_aiSignals[] is never delete'd directly (grep confirms|
//| this file has no `delete g_aiSignals` anywhere), so there is no |
//| double-free risk from the two arrays holding the same pointers. |
//+------------------------------------------------------------------+
2026-07-14 22:36:27 -04:00
EnablePAI = ( AIType = = 0 | | AIType = = 3 ) ;
EnableCONV = ( AIType = = 1 | | AIType = = 3 ) ;
EnableLSTM = ( AIType = = 2 | | AIType = = 3 ) ;
// Creating instances of signals
CSignalPAI * PAI = CreateSignalWithRetry < CSignalPAI > ( maxRetryOnError , EnablePAI ) ;
CSignalCONV * CONV = CreateSignalWithRetry < CSignalCONV > ( maxRetryOnError , EnableCONV ) ;
CSignalLSTM * LSTM = CreateSignalWithRetry < CSignalLSTM > ( maxRetryOnError , EnableLSTM ) ;
//--- register whichever AI signal instances this run created, so the control panel can drive
//--- training/weight actions on exactly this run's current config (never another AIType's files)
g_aiSignalCount = 0 ;
if ( EnablePAI & & PAI ! = NULL )
RegisterAISignal ( PAI ) ;
if ( EnableCONV & & CONV ! = NULL )
RegisterAISignal ( CONV ) ;
if ( EnableLSTM & & LSTM ! = NULL )
RegisterAISignal ( LSTM ) ;
CSignalNewsFilter * newsFilter = CreateSignalWithRetry < CSignalNewsFilter > ( maxRetryOnError , EnableNewsFilter ) ;
CSignalSessionFilter * sessionFilter = CreateSignalWithRetry < CSignalSessionFilter > ( maxRetryOnError , EnableSessionFilter ) ;
CSignalITF * ITF = CreateSignalWithRetry < CSignalITF > ( maxRetryOnError , EnableITF ) ;
2026-07-17 23:21:12 -04:00
//--- g_marketDepthAvailable is the OnInit-time verified result (see CheckMarketDepthAvailability()
//--- above), not the raw EnableMarketDepth input - only created when real DOM data was confirmed.
CSignalMarketDepth * marketDepth = CreateSignalWithRetry < CSignalMarketDepth > ( maxRetryOnError , g_marketDepthAvailable ) ;
2026-07-18 17:29:38 -04:00
CSignalRiskGuard * riskGuard = CreateSignalWithRetry < CSignalRiskGuard > ( maxRetryOnError , EnableRiskGuard ) ;
if ( ( EnableITF & & ITF = = NULL ) | | ( EnablePAI & & PAI = = NULL ) | | ( EnableCONV & & CONV = = NULL ) | | ( EnableLSTM & & LSTM = = NULL ) | | ( EnableNewsFilter & & newsFilter = = NULL ) | | ( EnableSessionFilter & & sessionFilter = = NULL ) | | ( g_marketDepthAvailable & & marketDepth = = NULL ) | | ( EnableRiskGuard & & riskGuard = = NULL ) )
2026-07-14 22:36:27 -04:00
{
Print ( " Critical signal initialization failed, cannot proceed " ) ;
return INIT_FAILED ;
}
// Set filter parameters
2026-07-18 17:29:38 -04:00
if ( EnableRiskGuard )
{
riskGuard . SetMaxDailyLossPct ( MaxDailyLossPct ) ;
riskGuard . SetMaxDrawdownPct ( MaxDrawdownPct ) ;
}
2026-07-14 22:36:27 -04:00
if ( EnableSessionFilter )
{
sessionFilter . TradeLondonSession ( SF_trade_LondonSession ) ;
sessionFilter . TradeNewYorkSession ( SF_trade_NewYorkSession ) ;
sessionFilter . TradeTokyoSession ( SF_trade_TokyoSession ) ;
}
if ( EnableITF )
{
ITF . GoodHourOfDay ( ITF_GoodHourOfDay ) ;
ITF . BadHoursOfDay ( ITF_BadHoursOfDay ) ;
ITF . GoodDayOfWeek ( ITF_GoodDayOfWeek ) ;
ITF . BadDaysOfWeek ( ITF_BadDaysOfWeek ) ;
}
if ( EnableNewsFilter )
{
newsFilter . SetMinImpact ( NF_MinImpact ) ;
newsFilter . SetLookbackMinutes ( NF_LookMinutes ) ;
}
2026-07-17 23:21:12 -04:00
if ( g_marketDepthAvailable )
{
marketDepth . DepthLevels ( DOM_DepthLevels ) ;
marketDepth . ImbalanceScale ( DOM_ImbalanceScale / 100.0 ) ;
marketDepth . MaxSpreadMultiple ( DOM_MaxSpreadMultiple ) ;
}
2026-07-14 22:36:27 -04:00
if ( AIType = = 0 | | AIType = = 3 )
{
PAI . InitialNeuronsCount ( InitialNeurons ) ;
PAI . OutputNeuronsCount ( OutputNeuronsCount ) ;
PAI . MinNeuronsCount ( MinNeuronsCount ) ;
PAI . HiddenLayersCount ( HiddenLayersCount ) ;
PAI . NeuronsReduction ( NeuronsReduction ) ;
PAI . HistoryBars ( ind_Periods ) ;
PAI . StudyPeriod ( StudyPeriods ) ;
PAI . StopTrainWR ( MinWR ) ;
PAI . MinDirectionalRecall ( MinRecall ) ;
2026-07-18 02:01:06 -04:00
PAI . ClassSampleWeight ( ClassSampleWeight / 10.0 ) ;
2026-07-18 10:03:21 -04:00
PAI . FocalLossGamma ( FocalLossGamma / 10.0 ) ;
2026-07-14 22:36:27 -04:00
PAI . SwingConfirmationBars ( SwingConfirmationBars ) ;
PAI . MaxErasPerRun ( MaxErasPerRun ) ;
PAI . TrainRetryCooldownSeconds ( TrainRetryCooldownSec ) ;
PAI . OOSSplit ( OOSSplit ) ;
if ( ! UseDatabaseRanking )
PAI . Weight ( 1 ) ;
PAI . UseVolumes ( EnableVolume ) ;
PAI . UseTime ( EnableTime ) ;
PAI . UseATR ( EnableATR ) ;
2026-07-19 11:04:38 -04:00
PAI . UseSwingContext ( EnableSwingContext ) ;
feat: add configurable news event proximity/impact as an NN input feature
Price, time, volume, and volatility were already trained-model input
features; the real economic calendar (already used for the live
NewsFilter veto) is now an optional one too, reusing
System/NewsRelevance.mqh's symbol-relevance logic from the prior fix.
New EnableNews/NewsFeatureWindowMinutes inputs gate two features per
bar: minutes-since and minutes-until the nearest symbol-relevant
calendar event, impact-weighted. Deliberately limited to proximity +
impact, not actual-vs-forecast deviation - release schedules are
public knowledge ahead of time (not lookahead bias to use for a
historical training bar), but a release's actual outcome is not.
Wired identically to the existing EnableVolume/EnableTime/EnableATR
toggles: InitIndicators() accounts for the +2 neuron count,
BufferTempDataCompute() appends the two feature values, PAI/CONV/LSTM
all wired in Warrior_EA.mq5. Compiled clean (MetaEditor, 0 errors/0
warnings).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 17:26:04 -04:00
PAI . UseNews ( EnableNews ) ;
PAI . NewsFeatureWindowMinutes ( NewsFeatureWindowMinutes ) ;
2026-07-14 22:36:27 -04:00
PAI . UseADCumulativeDelta ( EnableADCumulativeDelta ) ;
PAI . UseADShorteningOfThrust ( EnableADShorteningOfThrust ) ;
PAI . UseADWyckoffEventStream ( EnableADWyckoffEventStream ) ;
PAI . UseADWyckoffFailedStructure ( EnableADWyckoffFailedStructure ) ;
PAI . UseADWyckoffSignificantBarInversion ( EnableADWyckoffSignificantBarInversion ) ;
PAI . AutoTuneIndicators ( AutoTuneIndicators ) ;
PAI . IndicatorTuneTrials ( IndicatorTuneTrials ) ;
}
if ( AIType = = 1 | | AIType = = 3 )
{
CONV . InitialNeuronsCount ( InitialNeurons ) ;
CONV . OutputNeuronsCount ( OutputNeuronsCount ) ;
CONV . MinNeuronsCount ( MinNeuronsCount ) ;
CONV . HiddenLayersCount ( HiddenLayersCount ) ;
CONV . NeuronsReduction ( NeuronsReduction ) ;
CONV . HistoryBars ( ind_Periods ) ;
CONV . StudyPeriod ( StudyPeriods ) ;
CONV . StopTrainWR ( MinWR ) ;
CONV . MinDirectionalRecall ( MinRecall ) ;
2026-07-18 02:01:06 -04:00
CONV . ClassSampleWeight ( ClassSampleWeight / 10.0 ) ;
2026-07-18 10:03:21 -04:00
CONV . FocalLossGamma ( FocalLossGamma / 10.0 ) ;
2026-07-14 22:36:27 -04:00
CONV . SwingConfirmationBars ( SwingConfirmationBars ) ;
CONV . MaxErasPerRun ( MaxErasPerRun ) ;
CONV . TrainRetryCooldownSeconds ( TrainRetryCooldownSec ) ;
CONV . OOSSplit ( OOSSplit ) ;
if ( ! UseDatabaseRanking )
CONV . Weight ( 1 ) ;
if ( AIType = = 3 )
CONV . Pattern_0 ( 10 ) ;
CONV . UseVolumes ( EnableVolume ) ;
CONV . UseTime ( EnableTime ) ;
CONV . UseATR ( EnableATR ) ;
2026-07-19 11:04:38 -04:00
CONV . UseSwingContext ( EnableSwingContext ) ;
feat: add configurable news event proximity/impact as an NN input feature
Price, time, volume, and volatility were already trained-model input
features; the real economic calendar (already used for the live
NewsFilter veto) is now an optional one too, reusing
System/NewsRelevance.mqh's symbol-relevance logic from the prior fix.
New EnableNews/NewsFeatureWindowMinutes inputs gate two features per
bar: minutes-since and minutes-until the nearest symbol-relevant
calendar event, impact-weighted. Deliberately limited to proximity +
impact, not actual-vs-forecast deviation - release schedules are
public knowledge ahead of time (not lookahead bias to use for a
historical training bar), but a release's actual outcome is not.
Wired identically to the existing EnableVolume/EnableTime/EnableATR
toggles: InitIndicators() accounts for the +2 neuron count,
BufferTempDataCompute() appends the two feature values, PAI/CONV/LSTM
all wired in Warrior_EA.mq5. Compiled clean (MetaEditor, 0 errors/0
warnings).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 17:26:04 -04:00
CONV . UseNews ( EnableNews ) ;
CONV . NewsFeatureWindowMinutes ( NewsFeatureWindowMinutes ) ;
2026-07-14 22:36:27 -04:00
CONV . UseADCumulativeDelta ( EnableADCumulativeDelta ) ;
CONV . UseADShorteningOfThrust ( EnableADShorteningOfThrust ) ;
CONV . UseADWyckoffEventStream ( EnableADWyckoffEventStream ) ;
CONV . UseADWyckoffFailedStructure ( EnableADWyckoffFailedStructure ) ;
CONV . UseADWyckoffSignificantBarInversion ( EnableADWyckoffSignificantBarInversion ) ;
CONV . AutoTuneIndicators ( AutoTuneIndicators ) ;
CONV . IndicatorTuneTrials ( IndicatorTuneTrials ) ;
}
if ( AIType = = 2 | | AIType = = 3 )
{
LSTM . InitialNeuronsCount ( InitialNeurons ) ;
LSTM . OutputNeuronsCount ( OutputNeuronsCount ) ;
LSTM . MinNeuronsCount ( MinNeuronsCount ) ;
LSTM . HiddenLayersCount ( HiddenLayersCount ) ;
LSTM . NeuronsReduction ( NeuronsReduction ) ;
LSTM . HistoryBars ( ind_Periods ) ;
LSTM . StudyPeriod ( StudyPeriods ) ;
LSTM . StopTrainWR ( MinWR ) ;
LSTM . MinDirectionalRecall ( MinRecall ) ;
2026-07-18 02:01:06 -04:00
LSTM . ClassSampleWeight ( ClassSampleWeight / 10.0 ) ;
2026-07-18 10:03:21 -04:00
LSTM . FocalLossGamma ( FocalLossGamma / 10.0 ) ;
2026-07-14 22:36:27 -04:00
LSTM . SwingConfirmationBars ( SwingConfirmationBars ) ;
LSTM . MaxErasPerRun ( MaxErasPerRun ) ;
LSTM . TrainRetryCooldownSeconds ( TrainRetryCooldownSec ) ;
LSTM . OOSSplit ( OOSSplit ) ;
if ( ! UseDatabaseRanking )
LSTM . Weight ( 1 ) ;
if ( AIType = = 3 )
LSTM . Pattern_0 ( 10 ) ;
LSTM . UseVolumes ( EnableVolume ) ;
LSTM . UseTime ( EnableTime ) ;
LSTM . UseATR ( EnableATR ) ;
2026-07-19 11:04:38 -04:00
LSTM . UseSwingContext ( EnableSwingContext ) ;
feat: add configurable news event proximity/impact as an NN input feature
Price, time, volume, and volatility were already trained-model input
features; the real economic calendar (already used for the live
NewsFilter veto) is now an optional one too, reusing
System/NewsRelevance.mqh's symbol-relevance logic from the prior fix.
New EnableNews/NewsFeatureWindowMinutes inputs gate two features per
bar: minutes-since and minutes-until the nearest symbol-relevant
calendar event, impact-weighted. Deliberately limited to proximity +
impact, not actual-vs-forecast deviation - release schedules are
public knowledge ahead of time (not lookahead bias to use for a
historical training bar), but a release's actual outcome is not.
Wired identically to the existing EnableVolume/EnableTime/EnableATR
toggles: InitIndicators() accounts for the +2 neuron count,
BufferTempDataCompute() appends the two feature values, PAI/CONV/LSTM
all wired in Warrior_EA.mq5. Compiled clean (MetaEditor, 0 errors/0
warnings).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18 17:26:04 -04:00
LSTM . UseNews ( EnableNews ) ;
LSTM . NewsFeatureWindowMinutes ( NewsFeatureWindowMinutes ) ;
2026-07-14 22:36:27 -04:00
LSTM . UseADCumulativeDelta ( EnableADCumulativeDelta ) ;
LSTM . UseADShorteningOfThrust ( EnableADShorteningOfThrust ) ;
LSTM . UseADWyckoffEventStream ( EnableADWyckoffEventStream ) ;
LSTM . UseADWyckoffFailedStructure ( EnableADWyckoffFailedStructure ) ;
LSTM . UseADWyckoffSignificantBarInversion ( EnableADWyckoffSignificantBarInversion ) ;
LSTM . AutoTuneIndicators ( AutoTuneIndicators ) ;
LSTM . IndicatorTuneTrials ( IndicatorTuneTrials ) ;
}
// Add filters
PrintVerbose ( " Initializing Signal filters... " ) ;
//--- added exactly once, before the DB retry loop below - these calls don't depend on DB success at
//--- all (every pointer here was already validated non-NULL above), but living inside the loop body
//--- meant a DB open/transaction failure that triggered a retry would re-run AddFilterToSignal() and
//--- register the same filter pointer a second time in signal's CArrayObj; since that array frees its
//--- elements on destruction, a duplicate entry means the same pointer gets deleted twice on shutdown
//--- (heap corruption), which could easily explain instability across a later remove/re-add cycle.
bool filtersAdded = true ;
filtersAdded & = ( EnableITF ? AddFilterToSignal ( signal , ITF ) : true ) ;
filtersAdded & = ( EnableSessionFilter ? AddFilterToSignal ( signal , sessionFilter ) : true ) ;
2026-07-17 23:21:12 -04:00
filtersAdded & = ( g_marketDepthAvailable ? AddFilterToSignal ( signal , marketDepth ) : true ) ;
2026-07-14 22:36:27 -04:00
filtersAdded & = ( EnableNewsFilter ? AddFilterToSignal ( signal , newsFilter ) : true ) ;
2026-07-18 17:29:38 -04:00
filtersAdded & = ( EnableRiskGuard ? AddFilterToSignal ( signal , riskGuard ) : true ) ;
2026-07-14 22:36:27 -04:00
filtersAdded & = ( EnablePAI ? AddFilterToSignal ( signal , PAI ) : true ) ;
filtersAdded & = ( EnableCONV ? AddFilterToSignal ( signal , CONV ) : true ) ;
filtersAdded & = ( EnableLSTM ? AddFilterToSignal ( signal , LSTM ) : true ) ;
if ( ! filtersAdded )
{
Print ( functionName + " : Error loading filters " ) ;
return INIT_FAILED ;
}
bool filterSuccess = false ;
for ( int tries = 0 ; tries < maxRetryOnError ; + + tries )
{
if ( UseDatabaseRanking & & ! dbm . OpenDatabase ( ) )
{
Print ( functionName + " : Error opening database, retrying... " ) ;
RandomSleep ( ) ;
continue ;
}
if ( UseDatabaseRanking & & ! dbm . BeginTransaction ( ) )
{
Print ( functionName + " : Error starting transaction, retrying... " ) ;
dbm . CloseDatabase ( ) ; // Ensure the database is closed before retry
RandomSleep ( ) ;
continue ;
}
if ( UseDatabaseRanking & & ( ! dbm . CommitTransaction ( ) | | ! dbm . CloseDatabase ( ) ) )
{
Print ( functionName + " : Error committing transaction or closing database, retrying... " ) ;
RandomSleep ( ) ;
continue ;
}
filterSuccess = true ;
break ; // Success if all operations complete without error
}
if ( ! filterSuccess )
{
Print ( functionName + " : Failed after all retries " ) ;
return INIT_FAILED ; // Return failure if retries are exhausted
}
// Trailing logic
PrintVerbose ( " Initializing Trailing... " ) ;
bool trailingInitialized = false ;
for ( int tries = 0 ; ! trailingInitialized & & tries < maxRetryOnError ; + + tries )
{
if ( ! InitializeTrailing ( ) )
{
Print ( functionName + " : Failed to initialize Trailing, retrying... " ) ;
RandomSleep ( ) ;
}
else
{
trailingInitialized = true ;
break ;
}
}
if ( ! trailingInitialized )
{
Print ( functionName + " : Failed to initialize Trailing after retries " ) ;
return INIT_FAILED ;
}
// Creation of money object
bool moneyManagementInitialized = false ;
for ( int tries = 0 ; ! moneyManagementInitialized & & tries < maxRetryOnError ; + + tries )
{
if ( ! InitializeMoneyManagement ( ) )
{
Print ( functionName + " : Failed to initialize Money Management, retrying... " ) ;
RandomSleep ( ) ;
}
else
{
moneyManagementInitialized = true ;
break ;
}
}
if ( ! moneyManagementInitialized )
{
Print ( functionName + " : Failed to initialize Money Management after retries " ) ;
return INIT_FAILED ;
}
// Check all trading objects parameters
PrintVerbose ( " Validating settings... " ) ;
bool settingsValidated = false ;
for ( int tries = 0 ; ! settingsValidated & & tries < maxRetryOnError ; + + tries )
{
if ( ! Expert . ValidationSettings ( ) )
{
Print ( functionName + " : Failed to validate settings, retrying... " ) ;
RandomSleep ( ) ;
}
else
{
settingsValidated = true ;
break ;
}
}
if ( ! settingsValidated )
{
Print ( functionName + " : Failed to validate settings after retries " ) ;
return INIT_FAILED ;
}
// Tuning of all necessary indicators
PrintVerbose ( " Initializing Indicators... " ) ;
bool indicatorsInitialized = false ;
for ( int tries = 0 ; ! indicatorsInitialized & & tries < maxRetryOnError ; + + tries )
{
if ( ! Expert . InitIndicators ( ) )
{
Print ( functionName + " : Failed to initialize Indicators, retrying... " ) ;
RandomSleep ( ) ;
}
else
{
indicatorsInitialized = true ;
break ;
}
}
if ( ! indicatorsInitialized )
{
Print ( functionName + " : Failed to initialize Indicators after retries " ) ;
return INIT_FAILED ;
}
// setting timer: always on (short interval) so control-panel upkeep and other periodic checks run
// on a fixed schedule regardless of tick activity - a quiet/after-hours symbol can go long stretches
// without a single OnTick() call, and self-healing logic that only lives in OnTick() would never run
// during that stretch. The (much less frequent) DB-ranking work is throttled separately inside
// OnTimer() below rather than by EventSetTimer's own interval, since MQL5 only supports one timer
// interval per program.
if ( UseDatabaseRanking )
Expert . OnTimerProcess ( true ) ;
bool timerSet = false ;
2026-07-17 09:11:42 -04:00
// 250ms (was 5s, via EventSetTimer's whole-second granularity): Train() only does up to
// TRAIN_TIME_BUDGET_MS (80ms) of work per call, then yields back here - at a 5s interval that's
// ~80ms busy / 5000ms elapsed, i.e. training sat idle ~98% of the time whenever it was being
// driven by this timer (quiet symbol/no ticks) instead of real tick flow, which is why an era was
// taking ~2 minutes wall-clock for well under 2s of actual compute. EventSetMillisecondTimer is
// needed for sub-second resolution; EventSetTimer only accepts whole seconds. DB-ranking work
// below is paced by its own g_lastDbRankingRun/DB_RANKING_INTERVAL_SECONDS check, not by this
// interval, so it still only runs ~hourly regardless of this change.
int timerInterval_ms = 250 ;
2026-07-14 22:36:27 -04:00
for ( int tries = 0 ; ! timerSet & & tries < maxRetryOnError ; + + tries )
{
2026-07-17 09:11:42 -04:00
if ( ! EventSetMillisecondTimer ( timerInterval_ms ) )
2026-07-14 22:36:27 -04:00
{
Print ( functionName + " : Error creating timer, retrying... " ) ;
RandomSleep ( ) ;
}
else
{
timerSet = true ;
break ;
}
}
if ( ! timerSet )
{
Print ( functionName + " : Failed to set timer after retries " ) ;
return INIT_FAILED ;
}
// Initialization successful
PrintVerbose ( " Initialization successful " ) ;
if ( ! CreateControlPanel ( ) )
Print ( functionName + " : WARNING - control panel failed to initialize; trading/training continue normally, "
" but no GUI panel will be available for this run " ) ;
2026-07-17 21:28:59 -04:00
//--- one-time cleanup: an earlier build used Comment() plus a separate background rectangle object
//--- that turned out to render ON TOP of the text (Comment() has no built-in background/styling
//--- parameters at all) - delete any leftover from a prior run now that status text is a single
//--- self-contained OBJ_LABEL (see SetStatusLabel()) with its own BGCOLOR fill instead
2026-07-14 22:36:27 -04:00
if ( ObjectFind ( 0 , " WarriorCommentBG " ) > = 0 )
ObjectDelete ( 0 , " WarriorCommentBG " ) ;
//--- required for CAppDialog's caption-bar drag to work at all - without it, the chart never delivers
//--- CHARTEVENT_MOUSE_MOVE and the panel silently ignores drag attempts
ChartSetInteger ( 0 , CHART_EVENT_MOUSE_MOVE , true ) ;
g_lastAlgoTradingAllowed = ( bool ) TerminalInfoInteger ( TERMINAL_TRADE_ALLOWED ) & & ( bool ) MQLInfoInteger ( MQL_TRADE_ALLOWED ) ;
if ( ! g_lastAlgoTradingAllowed )
Print ( functionName + " : WARNING - AlgoTrading is currently disabled (terminal or EA); signals will still train but no orders will be sent until it is re-enabled " ) ;
return INIT_SUCCEEDED ;
}
// Called before optimization/backtesting in the strategy tester
int OnTesterInit ( )
{
IsBacktesting = true ;
return ( INIT_SUCCEEDED ) ;
}
// Called after EA optimization in the strategy tester
void OnTesterDeinit ( )
{
dbm . Deinit ( ) ;
IsBacktesting = false ;
OnDeinit ( 0 ) ;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Maps a terminal deinit reason code to a short label for logging, |
//| so operators can tell a routine recompile/parameter change apart |
//| from a terminal shutdown or the EA actually being removed. |
//+------------------------------------------------------------------+
string DeinitReasonToString ( const int reason )
{
switch ( reason )
{
case REASON_PROGRAM : return " EA stopped by ExpertRemove()/self " ;
case REASON_REMOVE : return " EA removed from chart " ;
case REASON_RECOMPILE : return " EA recompiled " ;
case REASON_CHARTCHANGE : return " chart symbol/period changed " ;
case REASON_CHARTCLOSE : return " chart closed " ;
case REASON_PARAMETERS : return " input parameters changed " ;
case REASON_ACCOUNT : return " account changed " ;
case REASON_TEMPLATE : return " template applied " ;
case REASON_INITFAILED : return " OnInit() failed " ;
case REASON_CLOSE : return " terminal closed " ;
default : return " unknown ( " + IntegerToString ( reason ) + " ) " ;
}
}
void OnDeinit ( const int reason )
{
string reasonStr = DeinitReasonToString ( reason ) ;
Print ( __FUNCTION__ + " : shutting down - reason: " + reasonStr ) ;
2026-07-17 23:21:12 -04:00
//--- matches the MarketBookAdd() in CheckMarketDepthAvailability() (OnInit) - only subscribed at
//--- all when g_marketDepthAvailable ended up true for this run.
if ( g_marketDepthAvailable )
MarketBookRelease ( _Symbol ) ;
2026-07-14 22:36:27 -04:00
//--- persist every active AI signal's current in-memory weights/state before anything is torn down,
//--- so a terminal restart, chart re-add, or template swap resumes from here rather than from the
//--- last fully-completed training era only. Best-effort: a save failure is logged but must not
//--- block the rest of shutdown.
for ( int i = 0 ; i < g_aiSignalCount ; i + + )
{
if ( ! g_aiSignals [ i ] . PersistOnShutdown ( ) )
Print ( __FUNCTION__ + " : WARNING - failed to persist weights for signal index " + IntegerToString ( i ) + " on shutdown (reason: " + reasonStr + " ) " ) ;
}
g_aiSignalCount = 0 ;
EventKillTimer ( ) ;
dbm . Deinit ( ) ;
//--- destroy the control panel's own UI BEFORE Expert.Deinit() below - each active AI signal's
//--- destructor (~CExpertSignalAIBase) calls PurgeChart(), which does ObjectsDeleteAll(0) and force-
//--- deletes every object on the chart, including the panel's buttons/labels. Tearing the panel down
//--- first lets CAppDialog remove its own objects cleanly instead of Destroy() running against
//--- objects that were already deleted out from under it.
ExtPanel . Destroy ( reason ) ;
Expert . Deinit ( ) ;
2026-07-17 21:28:59 -04:00
//--- ObjectsDeleteAll(0) above already deleted the status label along with every other chart object,
//--- but this explicit clear is kept as a belt-and-suspenders guard in case that call path changes -
//--- without it, whatever status line (e.g. "Warrior EA: initializing..." or the last training-
//--- progress line) was showing could stay on the chart forever after the EA is removed.
ClearStatusLabel ( ) ;
2026-07-14 22:36:27 -04:00
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
//--- Expert.OnTimer() does the (comparatively expensive) DB-ranking work, originally paced by its
//--- own 1-hour EventSetTimer() interval; now that the timer itself runs every 5s (see OnInit), pace
//--- that work manually instead so it still only actually runs about once an hour.
# define DB_RANKING_INTERVAL_SECONDS 3600
datetime g_lastDbRankingRun = 0 ;
void OnTimer ( )
{
//--- keeps training progressing on wall-clock time even with no ticks at all (market closed) -
//--- OnTickHandler's own scheduling only ever runs when a tick actually arrives
for ( int i = 0 ; i < g_aiSignalCount ; i + + )
g_aiSignals [ i ] . PollTraining ( ) ;
if ( ! UseDatabaseRanking )
return ;
datetime now = TimeCurrent ( ) ;
if ( g_lastDbRankingRun ! = 0 & & now - g_lastDbRankingRun < DB_RANKING_INTERVAL_SECONDS )
return ;
g_lastDbRankingRun = now ;
Expert . OnTimer ( ) ;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CheckAlgoTradingState ( void )
{
bool allowed = ( bool ) TerminalInfoInteger ( TERMINAL_TRADE_ALLOWED ) & & ( bool ) MQLInfoInteger ( MQL_TRADE_ALLOWED ) ;
if ( allowed ! = g_lastAlgoTradingAllowed )
{
if ( allowed )
Print ( __FUNCTION__ + " : AlgoTrading re-enabled - order placement resumed (training/signals were unaffected while disabled) " ) ;
else
Print ( __FUNCTION__ + " : AlgoTrading disabled (terminal toggle off, or EA's own permission revoked) - no new orders will be sent until re-enabled; training/signal generation continues unaffected " ) ;
g_lastAlgoTradingAllowed = allowed ;
}
}
void AutosaveWeightsIfDue ( void )
{
datetime now = TimeCurrent ( ) ;
if ( now - g_lastAutosave < AUTOSAVE_INTERVAL_SECONDS )
return ;
g_lastAutosave = now ;
for ( int i = 0 ; i < g_aiSignalCount ; i + + )
g_aiSignals [ i ] . SaveWeightsNow ( ) ;
}
void OnTick ( )
{
CheckAlgoTradingState ( ) ;
AutosaveWeightsIfDue ( ) ;
Expert . OnTick ( ) ;
//--- new arrows are always created visible; if signals are currently hidden, re-hide
//--- any that were drawn this tick (cheap - only runs while the toggle is in the "hidden" state)
if ( ! g_signalsVisible )
ApplySignalsVisibility ( ) ;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void OnChartEvent ( const int id ,
const long & lparam ,
const double & dparam ,
const string & sparam )
{
//--- canonical CAppDialog usage (Controls\Dialog.mqh): forward every event to the dialog first, since
//--- that's what drives its own click/drag hit-testing (via CHARTEVENT_MOUSE_MOVE) as well as our
//--- buttons' EVENT_MAP handlers (see ControlPanel.mqh) - then pick up whatever button action, if any,
//--- that just recorded.
ExtPanel . ChartEvent ( id , lparam , dparam , sparam ) ;
HandleControlPanelAction ( ExtPanel . ConsumeAction ( ) ) ;
Expert . OnChartEvent ( id , lparam , dparam , sparam ) ;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool AddFilterToSignal ( CExpertSignalCustom * signal , CExpertSignalCustom * filter )
{
if ( filter = = NULL )
{
Print ( __FUNCTION__ + " Error creating filters " ) ;
return false ;
}
return signal . AddFilter ( filter ) ;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool InitializeSignal ( CExpertSignalCustom * signal_obj )
{
if ( signal_obj = = NULL )
{
Print ( __FUNCTION__ + " : error creating signal " ) ;
return false ;
}
Expert . InitSignal ( signal_obj ) ;
signal_obj . Entry_Multiplier ( Entry_Multiplier ) ;
signal_obj . Expiration ( Signal_Expiration ) ;
signal_obj . Periods ( ind_Periods ) ;
signal_obj . SLAtrMultiplier ( SL_Atr_Multiplier ) ;
signal_obj . MinRiskRewardRatio ( Min_Risk_Reward_Ratio ) ;
signal_obj . UseAISLTP ( SLTP_Source = = SLTP_AI_CONFIDENCE ) ;
signal_obj . ConfidenceSource ( ( int ) Confidence_Source ) ;
signal_obj . UseAIExit ( Use_AI_Exit ) ;
signal_obj . AIExitThreshold ( ( double ) AI_Exit_Threshold / 100.0 ) ;
if ( AIType = = 3 )
{
signal_obj . ThresholdOpen ( 20 ) ;
signal_obj . ThresholdClose ( 20 ) ;
}
else
{
signal_obj . ThresholdOpen ( 10 ) ;
signal_obj . ThresholdClose ( 10 ) ;
}
return true ;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
// Initialize Trailing
bool InitializeTrailing ( )
{
if ( TrailingStrategy = = TRAILING_STRATEGY_NONE )
{
// No trailing strategy selected
return true ;
}
else
if ( TrailingStrategy = = TRAILING_STRATEGY_ATR_x1 | |
TrailingStrategy = = TRAILING_STRATEGY_ATR_x2 | |
TrailingStrategy = = TRAILING_STRATEGY_ATR_x3 )
{
// ATR Trailing Strategy
double multiplier = 0 ;
if ( TrailingStrategy = = TRAILING_STRATEGY_ATR_x1 )
multiplier = 1 ;
else
if ( TrailingStrategy = = TRAILING_STRATEGY_ATR_x2 )
multiplier = 2 ;
else
if ( TrailingStrategy = = TRAILING_STRATEGY_ATR_x3 )
multiplier = 3 ;
CTrailingATR * trailing = new CTrailingATR ;
if ( trailing = = NULL )
{
Print ( __FUNCTION__ + " : error creating trailing " ) ;
return false ;
}
// Set ATR Multiplier
trailing . Multiplier ( multiplier ) ;
if ( ! Expert . InitTrailing ( trailing ) )
{
Print ( __FUNCTION__ + " : error initializing trailing " ) ;
return false ;
}
}
// Add more trailing strategies if needed
return true ;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool InitializeMoneyManagement ( )
{
string functionName = __FUNCTION__ ;
if ( MM_STRATEGY = = FIXED_RISK )
{
CMoneyFixedRisk * money = new CMoneyFixedRisk ;
if ( money = = NULL )
{
Print ( functionName + " : error creating money " ) ;
return false ;
}
if ( ! Expert . InitMoney ( money ) )
{
Print ( functionName + " : error initializing money " ) ;
return false ;
}
money . Percent ( Money_Risk_Percent ) ;
}
else
if ( MM_STRATEGY = = FIXED_LOT )
{
CMoneyFixedLot * money = new CMoneyFixedLot ;
if ( money = = NULL )
{
Print ( functionName + " : error creating money " ) ;
return false ;
}
if ( ! Expert . InitMoney ( money ) )
{
Print ( functionName + " : error initializing money " ) ;
return false ;
}
money . Lots ( Money_FixLot_Lots ) ;
}
else
if ( MM_STRATEGY = = INTELLIGENT )
{
CMoneyIntelligent * money = new CMoneyIntelligent ;
if ( money = = NULL )
{
Print ( functionName + " : error creating money " ) ;
return false ;
}
if ( ! Expert . InitMoney ( money ) )
{
Print ( functionName + " : error initializing money " ) ;
return false ;
}
money . Percent ( Money_Risk_Percent ) ;
money . UseAIConfidenceLotSizing ( Use_AI_Lot_Sizing ) ;
money . ConfidenceSource ( ( int ) Confidence_Source ) ;
}
// Add more money management strategies if needed
return true ;
}
//+------------------------------------------------------------------+