//+------------------------------------------------------------------+ //| Runtime.mqh | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #include "Market/DataManager.mqh" #include "Configs/EAConfig.mqh" #include "Events/EventBus/EventBus.mqh" #include "Events/LoadingListener.mqh" #include "Analysers/IAnalyser.mqh" #include "Strategies/AbstractStrategy.mqh" #include "Trade/TradeExecutor.mqh" #include "Trade/TradeManager.mqh" #include "Trade/RiskManager.mqh" #include "Trade/Trackers/OrderTracker.mqh" #include "ProcessController.mqh" //+------------------------------------------------------------------+ //| Helper struct for registration storage | //+------------------------------------------------------------------+ class ListenerRegistration { public: EventType eventType; IEventListener* listener; }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class Re : public IEventListener { public: virtual void OnEvent(AbstractEvent* event) override { if(event.GetType() != RISK_REJECTED) return; RiskRejectedEvent* sig = (RiskRejectedEvent*)event; // Print("RiskRejected: ", sig.GetReason()); } }; //+------------------------------------------------------------------+ //| Main runtime class | //+------------------------------------------------------------------+ class Runtime final { private: DataManager m_dataManager; EventBus* m_eventBus; ProcessController m_controller; LoadingListener m_loader; TimeHelpers m_timeHelper; CTradeExecutor* executor; TradeManager* tradeMgr; RiskManager* riskMgr; OrderTracker* orderTracker; Re re; CArrayList m_registrations; void SubscribeAll(); void UnsubscribeAll(); void ClearRegistrations(); public: Runtime(); ~Runtime(); int Initialize(EAConfig &eaConfig); void Update(int count =3); // Registration helpers void AddListener(int eventType, IEventListener* listener); void AddAnalyser(IAnalyser* analyser) { // analyser.Init(m_eventBus); m_controller.AddChapter(analyser, m_eventBus); } void AddStrategy(AbstractStrategy* strategy); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ Runtime::Runtime() { m_eventBus = new EventBus(); tradeMgr = new TradeManager(); executor = new CTradeExecutor(); riskMgr = new RiskManager(); orderTracker = new OrderTracker(); } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ Runtime::~Runtime() { UnsubscribeAll(); // automatic bulk unsubscribe ClearRegistrations(); // free registration objects delete m_eventBus; delete tradeMgr; delete executor; delete orderTracker; delete riskMgr; } //+------------------------------------------------------------------+ //| One‑time initialisation | //+------------------------------------------------------------------+ int Runtime::Initialize(EAConfig &eaConfig) { if(!m_loader.Initialize()) return INIT_FAILED; // --- Register all event listeners (subscription happens later) --- AddListener(LOADING, &m_loader); AddListener(RISK_REJECTED, &re); AddListener(NEW_BAR, tradeMgr); AddListener(EXECUTE_MARKET_ORDER, tradeMgr); AddListener(EXECUTE_PENDING_ORDER, tradeMgr); AddListener(SIGNAL, riskMgr); AddListener(POSITION_OPENED, riskMgr); AddListener(PENDING_ORDER_PLACED, orderTracker); AddListener(ORDER_DELETED, orderTracker); // --- Loading sequence ------------------------------------------- m_eventBus.Push(new LoadingEvent(START, 6, "Starting...")); m_eventBus.Dispatch(); Sleep(500 * 4); m_eventBus.Push(new LoadingEvent(UPDATE, "Initializing Data Manager...")); m_eventBus.Dispatch(); m_dataManager.Initialize(m_eventBus, eaConfig.tfConfigs, eaConfig.indConfigs,eaConfig.asSeries); Sleep(500); m_eventBus.Push(new LoadingEvent(UPDATE, "Subscribing...")); m_eventBus.Dispatch(); SubscribeAll(); // bulk subscribe m_eventBus.Push(new LoadingEvent(UPDATE, "Initializing Trade Executor...")); m_eventBus.Dispatch(); executor.SetMagic(eaConfig.magicNumber); executor.SetDeviation(eaConfig.Slippage); Sleep(500); m_eventBus.Push(new LoadingEvent(UPDATE, "Initializing Systems...")); m_eventBus.Dispatch(); m_controller.Initialize(m_eventBus, m_dataManager); riskMgr.Initialize(m_eventBus, eaConfig); tradeMgr.Initialize(m_eventBus, executor, eaConfig); orderTracker.Initialize(m_eventBus); Sleep(500); m_eventBus.Push(new LoadingEvent(UPDATE, "Launching...")); m_eventBus.Dispatch(); m_controller.Launch(); Sleep(500); m_eventBus.Push(new LoadingEvent(UPDATE, "Finished")); m_eventBus.Dispatch(); Sleep(125); m_eventBus.Push(new LoadingEvent(CLOSE, "")); m_eventBus.Dispatch(); riskMgr.SetLaunching(false); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Add a listener to the registration list (does NOT subscribe yet) | //+------------------------------------------------------------------+ void Runtime::AddListener(int eventType, IEventListener* listener) { ListenerRegistration* reg = new ListenerRegistration(); reg.eventType = eventType; reg.listener = listener; m_registrations.Add(reg); } //+------------------------------------------------------------------+ //| Convenience: adds a strategy, sets its EventBus, subscribes it | //+------------------------------------------------------------------+ void Runtime::AddStrategy(AbstractStrategy* strategy) { strategy.SetEventBus(m_eventBus); // inject bus AddListener(ANALYSIS, strategy); // register for ANALYSIS events } //+------------------------------------------------------------------+ //| Subscribe all registered listeners to the event bus | //+------------------------------------------------------------------+ void Runtime::SubscribeAll() { for(int i = 0; i < m_registrations.Size(); i++) { ListenerRegistration* reg = m_registrations.At(i); m_eventBus.Subscribe(reg.eventType, reg.listener); } } //+------------------------------------------------------------------+ //| Unsubscribe all registered listeners | //+------------------------------------------------------------------+ void Runtime::UnsubscribeAll() { for(int i = 0; i < m_registrations.Size(); i++) { ListenerRegistration* reg = m_registrations.At(i); m_eventBus.Unsubscribe(reg.eventType, reg.listener); } } //+------------------------------------------------------------------+ //| Delete all registration objects | //+------------------------------------------------------------------+ void Runtime::ClearRegistrations() { for(int i = 0; i < m_registrations.Size(); i++) delete m_registrations.At(i); m_registrations.Clear(); } //+------------------------------------------------------------------+ //| Per‑tick update | //+------------------------------------------------------------------+ void Runtime::Update(int count =3) { m_dataManager.Update(count); CArrayList rawData = m_dataManager.GetRawData(); for(int i = 0; i < rawData.Size(); i++) if(m_dataManager.CheckAndRelaunch(i)) m_controller.RelaunchSeries(i); m_controller.Update(); m_eventBus.Dispatch(); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+