
In our previous discussion, we built a custom, candlestick-pattern signal compatible with the MQL5 Wizard. The goal was to master custom signal development, enabling us to fully leverage the Wizard’s automation capabilities.
This raises a question: if the Wizard is so powerful, why is Expert Advisor development still challenging? The answer lies in market dynamics. While the MQL5 Wizard generates a professional and consistent EA structure, the core challenge remains the trading logic itself. Market conditions are dynamic and diverse, meaning a single set of static rules is often insufficient for long-term survival.
To create robust EAs, we must focus on the signals. By developing a diverse library of signals — and learning to modify existing ones — we can build systems that adapt. This approach allows for specialization; a developer can focus on perfecting a single signal module, contributing to a larger, collective toolkit.
Today, we will address the limitation of using only a few signals per EA. We are building an EA that can host and manage multiple trading signal modules. The advantage is resilience: in an evolving market, if one signal fails, others can continue to generate trading opportunities. This multi-strategy approach allows the EA to survive where others might fail.
As part of this project, I will create a new signal module based on Fibonacci analysis, and combine it with built-in signals from the MQL5 library.
It is also important to understand that in a multi-signal system, not all signals are used as primary entry triggers. Some act as filters, refining and enhancing the trades suggested by the primary signals, thereby creating more sophisticated strategies.
Below, I will explain the core concepts for this development in detail. Afterward, we will move to the implementation phase, where the focus will shift to the code to ensure a functional and effective multi-signal Expert Advisor.
Core Concepts for Multi-Signal Expert Advisor Development
Building an EA that can truly adapt to the markets requires a shift in thinking. Instead of a single, rigid strategy, we create a flexible system — a “team” of trading strategies working together. The MQL5 Standard Library provides the perfect framework to make this happen efficiently. Let’s break down the core ideas that make this so powerful.
The Modular Mindset
The foundation of this approach is modularity. Imagine each trading strategy — be it a Moving Average crossover, an RSI signal, or our custom Fibonacci pattern — as a self-contained module. Each module is an expert in its own domain, containing all the logic it needs to decide. The beauty of the MQL5 Standard Library is that it allows us to plug these modules into a central EA, which acts as the portfolio manager. This means we can develop, test, and improve strategies in isolation, then seamlessly add them to our trading system without rewriting its core.
How Decisions Are Made
Once you have multiple signal modules, how do they reach a consensus? This is where the elegant voting and weighting system comes in. Each module doesn’t just shout “buy” or “sell”; it calmly states its confidence level with a “weight” between 0 and 100.
A strong, clear signal might cast a heavy vote of 80, while a weaker one might only contribute 10. The EA then tallies all the votes. Only if the total confidence exceeds a predefined threshold does a trade execute. This system naturally prioritizes high-conviction signals and allows weaker, complementary signals to tip the scales without acting alone.
Assigning Roles: The Triggers and the Filters
Not all signals are created equal, and they shouldn’t be. In a sophisticated system, we assign them specific roles:
Primary Signals (The Triggers): These are your main strategies — the ones designed to find opportunities. They carry high weights and are responsible for initiating the bulk of the trading ideas.
Filter Signals: These act as a quality control team. A volatility filter, for example, might reduce the overall weight during chaotic market periods. A time filter can block trades outside specific hours. They rarely generate trades on their own, but they can veto bad ones, making the entire system more robust.
The Ultimate Benefit: Built-In Adaptability
This entire architecture leads to the holy grail of automated trading: adaptability. Markets evolve, and strategies that worked yesterday can fail today. In a single-strategy EA, this is catastrophic. But in a multi-signal system, it’s a managed risk.
When a trending market shifts to a range, your trend-following signals may grow quiet, but your mean-reversion or oscillator-based signals can pick up the slack. The EA doesn’t need to be rewritten; it naturally leans on the strategies that are currently working. This built-in resilience is what allows the EA to “survive” and thrive across different market environments.
Conceptual flow of the Multi-Signal EA
How the System Flows:
* Market data continuously feeds into our multi-signal EA system. Multiple signal modules process the data simultaneously:
Key Benefits Visualized:
This architecture creates what we might call “strategic diversity” — the EA isn’t reliant on any single market condition or pattern to succeed, making it much more likely to adapt and survive as markets evolve. By leveraging the MQL5 Wizard to handle the complex boilerplate code, we are free to focus on what matters most: developing and refining this diverse team of signals. In the next part, we’ll put this into practice, starting with coding our Fibonacci-based signal module and integrating it with the library’s built-in tools.
Implementation
Building a Professional Fibonacci Trading Signal for MQL5 Wizard
The Foundation: Setting Up Our Signal Class Structure
Before we dive into the complex logic, let’s elaborate on the “magic comments” that make our signal appear in the MQL5 Wizard. Notice the wizard description start/end block — this isn’t just documentation; it’s actually metadata that the MQL5 Wizard reads to display our signal in its dropdown menu. The title is what users see, ShortName becomes the internal identifier, and page links to documentation.
By inheriting from CExpertSignal, we’re tapping into the massive infrastructure of the MQL5 Standard Library. This is like building on the shoulders of giants — we get risk management, position sizing, and trade execution for free, while we focus purely on our Fibonacci trading logic. The protected member variables you see are our configuration parameters that users will be able to adjust in the MQL5 Wizard interface.
#ifndef SIGNAL_FIBONACCI_H #define SIGNAL_FIBONACCI_H #include class CSignalFibonacci : public CExpertSignal { protected: int m_depth; double m_min_retracement; double m_max_retracement; double m_weight_factor; bool m_use_as_filter; double m_filter_weight; bool m_combine_with_trend; double m_tolerance; int m_pattern_0; int m_pattern_1; int m_pattern_2; int m_pattern_3; double m_high_price; double m_low_price; datetime m_high_time; datetime m_low_time; bool m_uptrend; double m_current_strength; ENUM_TIMEFRAMES m_actual_period;
Smart Configuration: Giving Users Control Without Complexity
Here’s where we define the “control panel” for our Fibonacci signal. Each of these simple setter methods becomes a configurable parameter in the MQL5 Wizard interface. When users select our signal, they’ll see sliders and input fields for Depth, MinRetracement, and, most importantly, the pattern weights.
The pattern weights system (0-100) is what makes this signal intelligent. Think of it as telling the system, “When you see the price at the strong 0.618 Fibonacci level, I want you to be 80% confident, but at the weaker 0.236 level, only be 60% confident.” This granular control is what separates amateur indicators from professional trading systems. Users can backtest different weight combinations to find what works best for their trading style and market conditions.
public: void Depth(int value) { m_depth = value; } void MinRetracement(double value) { m_min_retracement = value; } void MaxRetracement(double value) { m_max_retracement = value; } void WeightFactor(double value) { m_weight_factor = value; } void Pattern_0(int value) { m_pattern_0 = value; } void Pattern_1(int value) { m_pattern_1 = value; }
The Brain: Finding Significant Market Swing Points
This is where the real detective work begins! FindSwingPoints() is our method for identifying significant market structure — the peaks and troughs that define trends. We use iHighest() and iLowest() to scan back through the specified number of bars (the m_depth parameter) to find the most recent significant high and low.
Here’s a pro tip: the m_depth parameter is crucial because markets have different “personalities” on different timeframes. On a 5-minute chart, looking back 50 bars might cover a few hours, while on a daily chart it could represent months of data. This parameter lets users adapt our signal to their preferred trading style without touching the code.
Notice we also determine trend direction by comparing the timestamps of the high and low — a simple yet effective way to understand market context. This trend awareness will later help us distinguish between Fibonacci support (in uptrends) and resistance (in downtrends).
bool CSignalFibonacci::FindSwingPoints(void) { if(m_depth open1 && open2 > close2 && close1 > open2 && open1 close1) ? (close1 – low1) : (open1 – low1); if(lower_wick >= (2 * body1)) { return true; } return false; }
The Decision Engine: Generating Long Trading Signals
This is where everything comes together — the LongCondition() method is the brain that makes final trading decisions. Notice it returns an integer between 0 and 100, representing the signal’s confidence level. This is the standardized interface that the MQL5 Wizard expects from all signals.
The IS_PATTERN_USAGE macro is a clever system that lets users enable or disable specific patterns in the Wizard interface. Maybe they only want to trade the strong 0.618 level but ignore the weaker levels — this system gives them that flexibility without code changes.
The real intelligence here is how we combine Fibonacci levels with price action confirmation. If we have both a Fibonacci level AND a bullish reversal pattern, we use the higher weight from pattern 3 (Fibonacci + price action). This creates a hierarchy of signal quality that makes our trading decisions much more sophisticated.
int CSignalFibonacci::LongCondition(void) { int result = 0; int pattern = -1; double current_price = m_symbol.Bid(); if(IsAtFibonacciLevel(current_price, pattern)) { if(pattern >= 0) { if(IS_PATTERN_USAGE(pattern)) { result = (int)GetPatternWeight(pattern); } if(IS_PATTERN_USAGE(3) && IsBullishReversal()) { int pa_weight = (int)GetPatternWeight(3); if(pa_weight > result) result = pa_weight; }
We have built a complete trading intelligence module that understands market structure, identifies key Fibonacci mathematical levels, waits for price confirmation, and generates confidence-weighted signals. The true power comes when you combine this with other signals in the MQL5 Wizard. Now, before we combined it with other signals to achieve our goal, we, created another test EA to see if the idea was working correctly. Below is a showcase of the strategy tester. As usual, to launch the MQL5 Wizard, you select New Document in Meta Editor or press the Ctrl+N keyboard combination and choose to generate an Expert Advisor.
Selecting the Fibonacci Signal Using the MQL5 Wizard
Testing 1
The image below shows the visual performance of the EA generated to test our custom Fibonacci signal on USDJPY, M5.
Testing the MQL5 Wizard-generated EA on USDJPY, M5
Now that we have successfully tested our Fibonacci signal, we’re ready to architect something truly powerful — a multi-signal Expert Advisor that behaves less like a simple robot and more like a team of specialized trading analysts working in perfect harmony. Think of this as building your own trading dream team where each signal brings unique expertise to the table.
The advantage of the MQL5 Wizard is that it handles the complex infrastructure, letting us focus on what really matters: crafting intelligent trading logic. Here’s how we’ll transform our single-signal test into a sophisticated trading system.
Using the same approach as before in MetaEditor, press Ctrl+N to open the MQL5 Wizard, then choose Expert Advisor (generate). In the Signals section, add the following modules:
* Fibonacci Signal (Precision Entry Expert)
* Accelerator Oscillator (Market Energy Reader)
* Moving Average (Trend Specialist)
* RSI (Momentum Analyst)
So here is the MQL5 Wizard generated expert advisor:
#property copyright “Copyright 2025, Clemence Benjamin.” #property link “https://www.mql5.com” #property version “1.00” #include #include #include #include #include #include #include input string Expert_Title =”Multi-Signal Expert”; ulong Expert_MagicNumber =-108721665; bool Expert_EveryTick =false; input int Signal_ThresholdOpen =10; input int Signal_ThresholdClose =10; input double Signal_PriceLevel =0.0; input double Signal_StopLevel =50.0; input double Signal_TakeLevel =50.0; input int Signal_Expiration =4; input double Signal_Fib_Weight =1.0; input double Signal_AC_Weight =1.0; input int Signal_MA_PeriodMA =12; input int Signal_MA_Shift =0; input ENUM_MA_METHOD Signal_MA_Method =MODE_SMA; input ENUM_APPLIED_PRICE Signal_MA_Applied =PRICE_CLOSE; input double Signal_MA_Weight =1.0; input int Signal_RSI_PeriodRSI =8; input ENUM_APPLIED_PRICE Signal_RSI_Applied =PRICE_CLOSE; input double Signal_RSI_Weight =1.0; input double Trailing_ParabolicSAR_Step =0.02; input double Trailing_ParabolicSAR_Maximum =0.2; input double Money_SizeOptimized_DecreaseFactor=3.0; input double Money_SizeOptimized_Percent =10.0; CExpert ExtExpert; int OnInit() { if(!ExtExpert.Init(Symbol(),Period(),Expert_EveryTick,Expert_MagicNumber)) { printf(__FUNCTION__+”: error initializing expert”); ExtExpert.Deinit(); return(INIT_FAILED); } CExpertSignal *signal=new CExpertSignal; if(signal==NULL) { printf(__FUNCTION__+”: error creating signal”); ExtExpert.Deinit(); return(INIT_FAILED); } ExtExpert.InitSignal(signal); signal.ThresholdOpen(Signal_ThresholdOpen); signal.ThresholdClose(Signal_ThresholdClose); signal.PriceLevel(Signal_PriceLevel); signal.StopLevel(Signal_StopLevel); signal.TakeLevel(Signal_TakeLevel); signal.Expiration(Signal_Expiration); CSignalFibonacci *filter0=new CSignalFibonacci; if(filter0==NULL) { printf(__FUNCTION__+”: error creating filter0″); ExtExpert.Deinit(); return(INIT_FAILED); } signal.AddFilter(filter0); filter0.Weight(Signal_Fib_Weight); CSignalAC *filter1=new CSignalAC; if(filter1==NULL) { printf(__FUNCTION__+”: error creating filter1″); ExtExpert.Deinit(); return(INIT_FAILED); } signal.AddFilter(filter1); filter1.Weight(Signal_AC_Weight); CSignalMA *filter2=new CSignalMA; if(filter2==NULL) { printf(__FUNCTION__+”: error creating filter2″); ExtExpert.Deinit(); return(INIT_FAILED); } signal.AddFilter(filter2); filter2.PeriodMA(Signal_MA_PeriodMA); filter2.Shift(Signal_MA_Shift); filter2.Method(Signal_MA_Method); filter2.Applied(Signal_MA_Applied); filter2.Weight(Signal_MA_Weight); CSignalRSI *filter3=new CSignalRSI; if(filter3==NULL) { printf(__FUNCTION__+”: error creating filter3″); ExtExpert.Deinit(); return(INIT_FAILED); } signal.AddFilter(filter3); filter3.PeriodRSI(Signal_RSI_PeriodRSI); filter3.Applied(Signal_RSI_Applied); filter3.Weight(Signal_RSI_Weight); CTrailingPSAR *trailing=new CTrailingPSAR; if(trailing==NULL) { printf(__FUNCTION__+”: error creating trailing”); ExtExpert.Deinit(); return(INIT_FAILED); } if(!ExtExpert.InitTrailing(trailing)) { printf(__FUNCTION__+”: error initializing trailing”); ExtExpert.Deinit(); return(INIT_FAILED); } trailing.Step(Trailing_ParabolicSAR_Step); trailing.Maximum(Trailing_ParabolicSAR_Maximum); CMoneySizeOptimized *money=new CMoneySizeOptimized; if(money==NULL) { printf(__FUNCTION__+”: error creating money”); ExtExpert.Deinit(); return(INIT_FAILED); } if(!ExtExpert.InitMoney(money)) { printf(__FUNCTION__+”: error initializing money”); ExtExpert.Deinit(); return(INIT_FAILED); } money.DecreaseFactor(Money_SizeOptimized_DecreaseFactor); money.Percent(Money_SizeOptimized_Percent); if(!ExtExpert.ValidationSettings()) { ExtExpert.Deinit(); return(INIT_FAILED); } if(!ExtExpert.InitIndicators()) { printf(__FUNCTION__+”: error initializing indicators”); ExtExpert.Deinit(); return(INIT_FAILED); } return(INIT_SUCCEEDED); } void OnDeinit(const int reason) { ExtExpert.Deinit(); } void OnTick() { ExtExpert.OnTick(); } void OnTrade() { ExtExpert.OnTrade(); } void OnTimer() { ExtExpert.OnTimer(); }
Testing 2
After generating the EA, I ran a test in the Strategy Tester to observe its performance. The GIF screencast below shows how it behaved on EURUSD, M5. Note that at this stage we haven’t added equity curve charts or other detailed testing metrics yet, as the focus is currently on visual performance only. Below the image, I share my thoughts and critique on the performance of the generated EA as we prepare to refine it and bring it closer to our intended goals.
Testing the generated Multi Signal Expert
Critical Analysis — Where the Wizard-Generated EA Misses Our Vision
After reviewing the code produced by the MQL5 Wizard, it becomes clear that there is a noticeable gap between what was generated and the multi-signal, confluence-driven framework we originally envisioned. The MQL5 Wizard gives us a useful starting point, but in its default form it remains a simple signal aggregator rather than a deliberate, hierarchy-aware decision engine. In this section we walk through the main shortcomings and outline the adjustments needed to bring the Expert Advisor closer to our design goals.
1. Restoring a Meaningful Signal Hierarchy
By default, the generated EA assigns a weight of 1.0 to all signals. That might be convenient for a quick test, but it entirely ignores the fact that our signals do not carry the same importance. In our concept, Fibonacci is the primary decision maker, while Moving Average, RSI, and Accelerator Oscillator play supporting roles.
Current auto-generated inputs:
input double Signal_Fib_Weight = 1.0; input double Signal_AC_Weight = 1.0; input double Signal_MA_Weight = 1.0; input double Signal_RSI_Weight = 1.0;
Equal weights flatten our hierarchy, dilute the influence of Fibonacci, and waste the opportunity to embed our trading experience directly into the model. A more strategic distribution is needed:
input double Signal_Fib_Weight = 0.8; input double Signal_MA_Weight = 0.6; input double Signal_RSI_Weight = 0.5; input double Signal_AC_Weight = 0.4;
This simple change already moves us away from a “flat democracy” of signals towards a structured, intention-driven system.
2. Opening Thresholds: From Overtrading to Selective Entries
The Wizard also initializes the opening threshold at a very low value. That almost guarantees that the EA will fire trades on weak combinations of signals, especially when all weights are set to 1.0.
input int Signal_ThresholdOpen = 10;
With such a threshold, the EA is biased towards overtrading: small flickers in the signal scores become enough to justify an entry. Transaction costs increase, confluence requirements vanish, and overall trade quality suffers.
For a multi-signal system built around confirmation, we need a much higher bar for opening trades, and a more flexible condition for closing them:
input int Signal_ThresholdOpen = 120; input int Signal_ThresholdClose = 80;
With this structure, one strong Fibonacci signal (80) plus at least one valid supporter (e.g., 40) is enough to justify a trade, while isolated minor readings no longer trigger entries on their own.
3. Missing Confluence Detection: Price and Time Clustering
Another key piece that the generated EA does not provide is a true confluence engine. Our design aims to recognize when several signals agree in both price and time, forming a higher-probability zone instead of isolated indicators firing independently.
Conceptually, we need a component that can answer questions like, are these signals clustering around the same price area? Are they appearing within the same time window? A minimal interface might look like this:
class CConfluenceEngine { public: bool HasPriceTimeConfluence(); int CalculateConfluenceBonus(); bool CheckSignalClustering(); };
This opens the door to adding extra “bonus” weight when multiple signals overlap in a tight range, scaling confidence when the market presents those rare aligned conditions that we care about.
4. Static Lot Size vs. Confidence-Based Position Sizing
The wizard-generated money management is essentially static. It applies the same risk percentage regardless of whether signals are weak or exceptionally strong:
input double Money_SizeOptimized_Percent = 10.0;
In a confluence-driven framework, this is a missed opportunity. Strong setups, backed by multiple aligned signals, should reasonably justify a larger risk allocation than borderline conditions. A better approach is to derive position size from total signal confidence:
double CalculateDynamicPositionSize(int total_confidence) { if(total_confidence >= 200) return BaseSize * 2.0; if(total_confidence >= 150) return BaseSize * 1.5; if(total_confidence >= 120) return BaseSize * 1.0; if(total_confidence >= 100) return BaseSize * 0.7; return 0.0; }
This turns our signal engine into a risk engine as well: better information leads to more informed capital deployment.
5. Lack of Market Regime Awareness
The generated EA also treats all market conditions as if they were identical. It does not distinguish between quiet vs. volatile periods, or strong trends vs. choppy ranges. In practice, our signals behave very differently across regimes, and the strategy should adapt accordingly.
A simple first step is to introduce basic regime checks using common indicators such as ATR and ADX:
bool IsHighVolatilityPeriod() { return (iATR(Symbol(), Period(), 14, 0) > high_vol_threshold); } bool IsStrongTrendingMarket() { return (iADX(Symbol(), Period(), 14, PRICE_CLOSE, MODE_MAIN, 0) > 25); } void AdjustStrategyForMarketConditions() { if(IsHighVolatilityPeriod()) { Signal_ThresholdOpen = 150; } }
Even these straightforward checks can prevent the EA from applying the same rules blindly in all environments.
6. From Raw Weight Summation to Meaningful Signal Interaction
In its basic form, the Wizard simply sums signal weights without considering how those signals interact. This is mathematically simple, but strategically naive:
Total_Weight = Fib_Weight + MA_Weight + RSI_Weight + AC_Weight;
Our aim is more structured: Fibonacci should be the anchor, while other modules either confirm or stay neutral. A more expressive interaction could look like this:
Here the structure of the logic tells a clear story: Fibonacci leads, the rest confirm, and confluence is rewarded.
double CalculateStrategicWeight() { double total = 0.0; total += Fib_Weight; if(MA_Confirms_Fib_Direction) total += MA_Weight; if(RSI_Confirms_Fib_Setup) total += RSI_Weight; if(AC_Shows_Momentum_Acceleration) total += AC_Weight; if(Active_Signals >= 3) total += Confluence_Bonus; return total; }
7. No Learning Loop: Weights Never Adapt
The current implementation is also static in time: weights, signals, and logic never evolve based on performance. In a long-running system, it is useful to at least track how each signal behaves and to leave room for future adaptive logic.
A small tracking structure is enough to start:
struct SignalPerformance { string name; int successful_trades; int total_trades; double success_rate; }; void UpdateSignalWeightsBasedOnPerformance() { }
Even if we begin with simple reporting in the first version, this layout makes it easier to introduce adaptive behavior later without redesigning the EA from scratch.
8. Risk Management Tied to Market and Confidence
Finally, the Wizard’s default risk management uses fixed stop and take-profit distances:
input double Signal_StopLevel = 50.0; input double Signal_TakeLevel = 50.0;
Such constants may be acceptable for a quick demo, but they do not reflect how volatility and signal quality should influence risk. A more robust approach links the stop level to ATR and adjusts it based on overall confidence:
double CalculateDynamicStopLoss(int signal_confidence,double volatility) { double base_stop = volatility * 2.0; if(signal_confidence >= 200) return base_stop * 0.7; if(signal_confidence <= 100) return base_stop * 1.5; return base_stop; }
In this way, the EA respects both the current state of the market and the strength of the underlying decision.
Taken together, these observations indicate that the Wizard gives us a convenient skeleton, but not the finished architecture we need. Our next steps are to gradually replace the generic components with the weighted hierarchy, confluence logic, dynamic sizing, and adaptive risk behavior described above, so that the generated EA truly reflects the design philosophy we started with.
Conclusion
In this discussion we have laid the groundwork for developing a multi-signal Expert Advisor. We explored one of the most popular technical frameworks — Fibonacci — and demonstrated how to translate that theory into a practical trading signal. Our initial test with the MQL5 Wizard produced a functioning EA that can open trades based on the custom Fibonacci signal. As always, this does not guarantee profitability; that part depends on going the extra mile with research, careful testing, and thoughtful optimization. At your own pace, you can experiment with the foundational source files provided and search for parameter combinations that align with your own trading objectives.
When we extended the idea to a multi-signal Expert Advisor, it became clear that the Wizard is only a starting point. The default generated code does not define how individual signals should work together as a coordinated team. There is no built-in logic that describes the relationships between Fibonacci, Moving Average, RSI, and Accelerator Oscillator — how they confirm, override, or reinforce one another. For this reason, further development in MetaEditor is essential after code generation. It is straightforward to generate an EA with multiple signals, but making those signals collaborate intelligently requires the developer to open the source and refine the logic manually.
Below, you will find the source code for the files used in this article. You are welcome to explore, modify, and share your thoughts in the comments section. Stay tuned for the next publication, where we will focus specifically on enhancing the Wizard-generated Expert Advisor so that it better reflects our original multi-signal design and trading goals.

