IC Markets

Forex scalping indicator - Fibonacci + Volume-Weighted OTE

Optimize MetaTrader 4, MetaTrader 5, cTrader, and TradingView for speed. Discuss Level II Market Depth (DOM), custom hotkeys, and volume indicators.
Post Reply
PTScalper
Site Admin
Posts: 975
Joined: Mon Jul 20, 2026 1:28 pm

Forex scalping indicator - Fibonacci + Volume-Weighted OTE

Post by PTScalper »

Hi traders,

i would like to share with you one of my favorite setup.
I love Fibonacci and Fibonacci retracement, especially in longer term charts like H4 and D1.

Here is the complete, single-file MQL4 source code combining the Volume-Weighted OTE setup, the in-memory dynamic Fibonacci calculation, and the real-time ATR trailing stop.
You can copy and paste this directly into MT4's MetaEditor (F4), compile it, and attach it to your chart.

Code: Select all

//+------------------------------------------------------------------+
//|                                     VolumeWeightedOTE_EA.mq4     |
//|                                     Dynamic Fib OTE + MFI Scalper|
//+------------------------------------------------------------------+
#property copyright "Forex Scalping EA"
#property link      ""
#property version   "1.00"
#property strict

//--- General Inputs
input double LotSize          = 0.1;
input int    Slippage         = 3;
input int    MagicNumber      = 888888;

//--- Indicator Inputs
input int    SwingLookback    = 40;     // Bars scanned for dynamic impulse swing
input int    EmaPeriod        = 200;    // Macro trend filter
input int    MfiPeriod        = 3;      // Fast tick volume tracking
input double StopLossBuffer   = 2.0;    // Initial SL buffer in pips beyond swing high/low

//--- Trailing Stop Inputs
input bool   UseAtrTrailing   = true;   // Enable ATR Trailing Stop
input int    AtrPeriod        = 14;     // ATR calculation period
input double AtrMultiplier     = 2.0;    // Multiplier for trailing distance

//--- Global Variables
datetime lastBarTime = 0;
double   Pips;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    // Standardize pip values for 4-digit and 5-digit brokers
    Pips = Point;
    if(Digits == 3 || Digits == 5) Pips = Point * 10;

    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    // 1. Manage existing trades on EVERY tick (Dynamic ATR Trailing)
    ManageTrailingStop();

    // 2. Bar Close Lock: Pause entry execution until the current candle closes
    if(Time[0] == lastBarTime) return; 

    // 3. Fetch Indicator Data from the last completed bar (shift = 1)
    double ema200_1 = iMA(NULL, 0, EmaPeriod, 0, MODE_EMA, PRICE_CLOSE, 1);
    double mfi1     = iMFI(NULL, 0, MfiPeriod, 1);
    double mfi2     = iMFI(NULL, 0, MfiPeriod, 2);

    // 4. Locate Dynamic Swing High and Low over the Lookback Window
    int highestIndex = iHighest(NULL, 0, MODE_HIGH, SwingLookback, 1);
    int lowestIndex  = iLowest(NULL, 0, MODE_LOW, SwingLookback, 1);
    
    double swingHigh = High[highestIndex];
    double swingLow  = Low[lowestIndex];
    double swingRange= swingHigh - swingLow;

    if(swingRange == 0) return; // Prevent division by zero if market is dead flat

    // Setup Flags
    bool isBuySetup  = false;
    bool isSellSetup = false;
    
    double initialSL = 0;
    double initialTP = 0;

    // --- BUY SETUP EVALUATION ---
    // Rule A: Macro Uptrend (Price > 200 EMA)
    // Rule B: Valid upward impulse leg (Lowest low formed BEFORE Highest high in history)
    if(Close[1] > ema200_1 && lowestIndex > highestIndex)
    {
        double fib618 = swingHigh - (swingRange * 0.618);
        double fib786 = swingHigh - (swingRange * 0.786);
        
        // Rule C: Price retraced into the Optimal Trade Entry (OTE) Kill Zone
        bool inKillZone = (Close[1] <= fib618) && (Close[1] >= fib786);
        
        // Rule D: Volume Exhaustion (MFI dipped below 20 and turned back up)
        bool mfiTrigger = (mfi2 <= 20) && (mfi1 > 20);

        if(inKillZone && mfiTrigger)
        {
            isBuySetup = true;
            initialSL  = swingLow - (StopLossBuffer * Pips); 
            initialTP  = swingHigh; // Primary target at impulse high
        }
    }

    // --- SELL SETUP EVALUATION ---
    // Rule A: Macro Downtrend (Price < 200 EMA)
    // Rule B: Valid downward impulse leg (Highest high formed BEFORE Lowest low in history)
    if(Close[1] < ema200_1 && highestIndex > lowestIndex)
    {
        double fib618 = swingLow + (swingRange * 0.618);
        double fib786 = swingLow + (swingRange * 0.786);
        
        // Rule C: Price retraced into the Optimal Trade Entry (OTE) Kill Zone
        bool inKillZone = (Close[1] >= fib618) && (Close[1] <= fib786);
        
        // Rule D: Volume Exhaustion (MFI spiked above 80 and turned back down)
        bool mfiTrigger = (mfi2 >= 80) && (mfi1 < 80);

        if(inKillZone && mfiTrigger)
        {
            isSellSetup = true;
            initialSL   = swingHigh + (StopLossBuffer * Pips);
            initialTP   = swingLow; // Primary target at impulse low
        }
    }

    // 5. Order Execution Engine
    if(CountOpenPositions() == 0) 
    {
        if(isBuySetup)
        {
            int ticket = OrderSend(Symbol(), OP_BUY, LotSize, Ask, Slippage, initialSL, initialTP, "OTE-MFI-Buy", MagicNumber, 0, clrDodgerBlue);
            if(ticket > 0) lastBarTime = Time[0];
        }
        else if(isSellSetup)
        {
            int ticket = OrderSend(Symbol(), OP_SELL, LotSize, Bid, Slippage, initialSL, initialTP, "OTE-MFI-Sell", MagicNumber, 0, clrCrimson);
            if(ticket > 0) lastBarTime = Time[0];
        }
    }
}

//+------------------------------------------------------------------+
//| Helper: Dynamic ATR Trailing Stop Engine                         |
//+------------------------------------------------------------------+
void ManageTrailingStop()
{
    if(!UseAtrTrailing) return;
    
    // Calculate ATR distance from closed bar
    double atr = iATR(NULL, 0, AtrPeriod, 1); 
    double trailDistance = atr * AtrMultiplier;
    
    // Respect broker minimum stop level distance
    double stopLevel = MarketInfo(Symbol(), MODE_STOPLEVEL) * Point;

    for(int i = OrdersTotal() - 1; i >= 0; i--)
    {
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
            if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
            {
                if(OrderType() == OP_BUY)
                {
                    double newSL = NormalizeDouble(Bid - trailDistance, Digits);
                    
                    // Trail up only when new SL is higher than current SL
                    if(newSL > OrderStopLoss() || OrderStopLoss() == 0)
                    {
                        if((Bid - newSL) >= stopLevel)
                        {
                            bool modified = OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrBlue);
                        }
                    }
                }
                else if(OrderType() == OP_SELL)
                {
                    double newSL = NormalizeDouble(Ask + trailDistance, Digits);
                    
                    // Trail down only when new SL is lower than current SL
                    if(newSL < OrderStopLoss() || OrderStopLoss() == 0)
                    {
                        if((newSL - Ask) >= stopLevel)
                        {
                            bool modified = OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrRed);
                        }
                    }
                }
            }
        }
    }
}

//+------------------------------------------------------------------+
//| Helper: Count open positions belonging to this Magic Number      |
//+------------------------------------------------------------------+
int CountOpenPositions()
{
    int count = 0;
    for(int i = 0; i < OrdersTotal(); i++)
    {
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
            if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
            {
                count++;
            }
        }
    }
    return count;
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 975
Joined: Mon Jul 20, 2026 1:28 pm

Re: Forex scalping indicator - Fibonacci + Volume-Weighted OTE

Post by PTScalper »

Do you like it? Or do you use slightly different setup? You can share with us :-)
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 975
Joined: Mon Jul 20, 2026 1:28 pm

Re: Forex scalping indicator - Fibonacci + Volume-Weighted OTE

Post by PTScalper »

I rechecked that code, it is nice and clean.
But i decided to extend it to higher level.

Here is what I upgraded to make this professional-grade:

1.) Broker Server Protection (Trailing Stop Spam):

Your original trailing stop modified the order on every single tick if the ATR changed slightly. This will get you blocked by a broker (Error 1: ERR_NO_RESULT). I added a minimum step threshold so it only modifies when the stop moves a meaningful distance.

2.) Dynamic Money Management:

Hardcoded lots are fine for testing, but pros scale risk. I added an auto-lot sizing module based on your account balance and the dynamic stop-loss distance.

3.) Execution Safety & Error Logging:

Added GetLastError() checks. If a trade fails, you will now know exactly why in the journal.

4.) Slippage & StopLevel Standardization:

Handled 4-digit and 5-digit broker point conversions properly, and added logic to push the SL/TP out if they fall inside the broker's minimum STOPLEVEL.

5.) Modular Architecture:

Broke the logic into cleaner IsNewBar(), ManageEntry(), and CalculateLotSize() functions for easier maintenance.

Here is your upgraded, professional-level EA:

Code: Select all

//+------------------------------------------------------------------+
//|                                     VolumeWeightedOTE_EA_Pro.mq4 |
//|                                     Dynamic Fib OTE + MFI Scalper|
//+------------------------------------------------------------------+
#property copyright "Forex Scalping EA - Pro Version"
#property link      ""
#property version   "2.00"
#property strict

//--- Group: Money Management
input bool   UseDynamicRisk   = true;     // Use % Risk instead of Fixed Lots
input double RiskPercent      = 1.0;      // Risk % per trade
input double FixedLotSize     = 0.1;      // Fixed Lot Size (if Dynamic is false)

//--- Group: Trade Settings
input int    MaxSlippagePips  = 3;        // Maximum Slippage in Pips
input int    MagicNumber      = 888888;
input string TradeComment     = "OTE_MFI";

//--- Group: Indicator Inputs
input int    SwingLookback    = 40;     // Bars scanned for dynamic impulse swing
input int    EmaPeriod        = 200;    // Macro trend filter
input int    MfiPeriod        = 3;      // Fast tick volume tracking
input double StopLossBuffer   = 2.0;    // SL buffer in pips beyond swing high/low

//--- Group: Trailing Stop
input bool   UseAtrTrailing   = true;   // Enable ATR Trailing Stop
input int    AtrPeriod        = 14;     // ATR calculation period
input double AtrMultiplier    = 2.0;    // Multiplier for trailing distance
input double TrailStepPips    = 1.0;    // Min movement in pips before modifying SL

//--- Global Variables
double Pips;
int    SlippagePoints;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    // Standardize pip values for 4-digit and 5-digit brokers
    Pips = Point;
    SlippagePoints = MaxSlippagePips;
    
    if(Digits == 3 || Digits == 5) 
    {
        Pips = Point * 10;
        SlippagePoints = MaxSlippagePips * 10;
    }

    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    // 1. Manage existing trades on EVERY tick (Dynamic ATR Trailing)
    ManageTrailingStop();

    // 2. Bar Close Lock: Pause entry execution until the current candle closes
    if(!IsNewBar()) return; 

    // 3. Scan for Entries if we have no open positions
    if(CountOpenPositions() == 0)
    {
        ScanForEntries();
    }
}

//+------------------------------------------------------------------+
//| Core Entry Logic                                                 |
//+------------------------------------------------------------------+
void ScanForEntries()
{
    double ema200_1 = iMA(Symbol(), 0, EmaPeriod, 0, MODE_EMA, PRICE_CLOSE, 1);
    double mfi1     = iMFI(Symbol(), 0, MfiPeriod, 1);
    double mfi2     = iMFI(Symbol(), 0, MfiPeriod, 2);

    int highestIndex = iHighest(Symbol(), 0, MODE_HIGH, SwingLookback, 1);
    int lowestIndex  = iLowest(Symbol(), 0, MODE_LOW, SwingLookback, 1);
    
    double swingHigh = High[highestIndex];
    double swingLow  = Low[lowestIndex];
    double swingRange = swingHigh - swingLow;

    if(swingRange == 0) return; // Prevent division by zero

    double initialSL = 0, initialTP = 0;
    int orderType = -1;

    // --- BUY SETUP ---
    if(Close[1] > ema200_1 && lowestIndex > highestIndex)
    {
        double fib618 = swingHigh - (swingRange * 0.618);
        double fib786 = swingHigh - (swingRange * 0.786);
        
        bool inKillZone = (Close[1] <= fib618) && (Close[1] >= fib786);
        bool mfiTrigger = (mfi2 <= 20) && (mfi1 > 20);

        if(inKillZone && mfiTrigger)
        {
            orderType = OP_BUY;
            initialSL = swingLow - (StopLossBuffer * Pips); 
            initialTP = swingHigh; 
        }
    }
    // --- SELL SETUP ---
    else if(Close[1] < ema200_1 && highestIndex > lowestIndex)
    {
        double fib618 = swingLow + (swingRange * 0.618);
        double fib786 = swingLow + (swingRange * 0.786);
        
        bool inKillZone = (Close[1] >= fib618) && (Close[1] <= fib786);
        bool mfiTrigger = (mfi2 >= 80) && (mfi1 < 80);

        if(inKillZone && mfiTrigger)
        {
            orderType = OP_SELL;
            initialSL = swingHigh + (StopLossBuffer * Pips);
            initialTP = swingLow; 
        }
    }

    // --- EXECUTION ENGINE ---
    if(orderType != -1)
    {
        double price = (orderType == OP_BUY) ? Ask : Bid;
        
        // Ensure SL is legal by broker standards
        initialSL = AdjustToStopLevel(orderType, price, initialSL);
        
        // Calculate dynamic lot size based on SL distance
        double slDistance = MathAbs(price - initialSL) / Pips;
        double lotSize = CalculateLotSize(slDistance);
        
        color arrowColor = (orderType == OP_BUY) ? clrDodgerBlue : clrCrimson;
        
        int ticket = OrderSend(Symbol(), orderType, lotSize, price, SlippagePoints, initialSL, initialTP, TradeComment, MagicNumber, 0, arrowColor);
        
        if(ticket < 0)
            Print("OrderSend Failed. Error: ", GetLastError());
        else
            Print("Successfully entered trade. Ticket: ", ticket);
    }
}

//+------------------------------------------------------------------+
//| Dynamic ATR Trailing Stop Engine (Server-Safe)                   |
//+------------------------------------------------------------------+
void ManageTrailingStop()
{
    if(!UseAtrTrailing) return;
    
    double atr = iATR(Symbol(), 0, AtrPeriod, 1); 
    double trailDistance = atr * AtrMultiplier;
    double stopLevel = MarketInfo(Symbol(), MODE_STOPLEVEL) * Point;
    double minStep = TrailStepPips * Pips; // Prevents server spam

    for(int i = OrdersTotal() - 1; i >= 0; i--)
    {
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
            if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
            {
                double currentSL = OrderStopLoss();
                
                if(OrderType() == OP_BUY)
                {
                    double newSL = NormalizeDouble(Bid - trailDistance, Digits);
                    
                    // Trail up only if new SL is higher AND passes the min step filter
                    if(newSL > currentSL + minStep || currentSL == 0)
                    {
                        if((Bid - newSL) >= stopLevel)
                        {
                            if(!OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrBlue))
                                Print("Trail Stop Modify Failed. Error: ", GetLastError());
                        }
                    }
                }
                else if(OrderType() == OP_SELL)
                {
                    double newSL = NormalizeDouble(Ask + trailDistance, Digits);
                    
                    // Trail down only if new SL is lower AND passes the min step filter
                    if(newSL < currentSL - minStep || currentSL == 0)
                    {
                        if((newSL - Ask) >= stopLevel)
                        {
                            if(!OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrRed))
                                Print("Trail Stop Modify Failed. Error: ", GetLastError());
                        }
                    }
                }
            }
        }
    }
}

//+------------------------------------------------------------------+
//| Helper: Calculate Dynamic Lot Size based on Risk %               |
//+------------------------------------------------------------------+
double CalculateLotSize(double slPips)
{
    if(!UseDynamicRisk || slPips <= 0) return FixedLotSize;

    double riskAmount = AccountBalance() * (RiskPercent / 100.0);
    double tickValue  = MarketInfo(Symbol(), MODE_TICKVALUE);
    double lotStep    = MarketInfo(Symbol(), MODE_LOTSTEP);
    double minLot     = MarketInfo(Symbol(), MODE_MINLOT);
    double maxLot     = MarketInfo(Symbol(), MODE_MAXLOT);

    if(tickValue == 0) return FixedLotSize; // Failsafe

    // Calculate Raw Lots (assumes ticksize == point, which is standard)
    double rawLot = riskAmount / (slPips * tickValue * (Pips / Point));
    
    // Normalize to broker lot step
    double finalLot = MathFloor(rawLot / lotStep) * lotStep;

    // Clamp to broker limits
    if(finalLot < minLot) finalLot = minLot;
    if(finalLot > maxLot) finalLot = maxLot;

    return NormalizeDouble(finalLot, 2);
}

//+------------------------------------------------------------------+
//| Helper: Adjust StopLoss to comply with Broker StopLevel          |
//+------------------------------------------------------------------+
double AdjustToStopLevel(int type, double openPrice, double slPrice)
{
    double stopLevel = MarketInfo(Symbol(), MODE_STOPLEVEL) * Point;
    
    if(type == OP_BUY)
    {
        if(openPrice - slPrice < stopLevel) 
            return NormalizeDouble(openPrice - stopLevel, Digits);
    }
    else if(type == OP_SELL)
    {
        if(slPrice - openPrice < stopLevel) 
            return NormalizeDouble(openPrice + stopLevel, Digits);
    }
    
    return NormalizeDouble(slPrice, Digits);
}

//+------------------------------------------------------------------+
//| Helper: Check for a new bar                                      |
//+------------------------------------------------------------------+
bool IsNewBar()
{
    static datetime lastBarTime = 0;
    datetime currentBarTime = iTime(Symbol(), 0, 0);
    
    if(lastBarTime != currentBarTime)
    {
        lastBarTime = currentBarTime;
        return true;
    }
    return false;
}

//+------------------------------------------------------------------+
//| Helper: Count open positions                                     |
//+------------------------------------------------------------------+
int CountOpenPositions()
{
    int count = 0;
    for(int i = 0; i < OrdersTotal(); i++)
    {
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
            if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
                count++;
        }
    }
    return count;
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 975
Joined: Mon Jul 20, 2026 1:28 pm

Re: Forex scalping indicator - Fibonacci + Volume-Weighted OTE

Post by PTScalper »

And if you make it even better, i prepared V3.

To take this from a "Pro-Level" EA to an Institutional-Grade algorithmic system, we need to address the environment and the psychology of the trade. Professional systems don't just manage risk on entry; they actively scale out of risk as the trade progresses, and they protect themselves from poor market conditions.

Here are the four advanced mechanisms I have added to this version (v3.00):

Partial Profit Taking (Scale-Out):

Automatically closes a percentage of your position (e.g., 50%) when it reaches a predefined profit target in pips. This secures capital while letting the "runner" catch the macro trend.

Auto Break-Even (Risk Free):

Once the trade reaches a profit threshold, the EA instantly moves the Stop Loss to your Entry Price + a small locked-in profit to cover commissions.

Spread Protection Filter:

Prevents the EA from executing trades during extreme market volatility or rollover periods when brokers artificially widen the spread.

Session Time Filters:

Confines trading to high-liquidity windows (e.g., London and New York sessions) to avoid false breakouts in dead Asian session hours.

Here is your ultimate v3.00 source code:

Code: Select all

//+------------------------------------------------------------------+
//|                                     VolumeWeightedOTE_EA_v3.mq4  |
//|                                     Institutional OTE Scalper    |
//+------------------------------------------------------------------+
#property copyright "Forex Scalping EA - Institutional Version"
#property link      ""
#property version   "3.00"
#property strict

//--- Group: Money Management
input bool   UseDynamicRisk    = true;     // Use % Risk instead of Fixed Lots
input double RiskPercent       = 1.0;      // Risk % per trade
input double FixedLotSize      = 0.1;      // Fixed Lot Size (if Dynamic is false)

//--- Group: Advanced Risk Exits (New)
input bool   UsePartialClose   = true;     // Enable scaling out (Partial Close)
input double PartialTargetPips = 15.0;     // Pips in profit to trigger partial close
input double PartialClosePct   = 50.0;     // % of lots to close (e.g., 50 for half)
input bool   UseBreakEven      = true;     // Move SL to entry at target
input double BreakEvenPips     = 10.0;     // Pips in profit to trigger BE
input double LockInPips        = 1.0;      // Pips to lock in when moving to BE

//--- Group: Environment Filters (New)
input double MaxSpreadPips     = 2.0;      // Max allowable spread to enter trade
input int    StartTradingHour  = 8;        // Start trading (Broker Server Time)
input int    EndTradingHour    = 20;       // Stop trading (Broker Server Time)

//--- Group: Trade Settings
input int    MaxSlippagePips   = 3;        
input int    MagicNumber       = 888888;
input string TradeComment      = "OTE_MFI";

//--- Group: Indicator Inputs
input int    SwingLookback     = 40;     
input int    EmaPeriod         = 200;    
input int    MfiPeriod         = 3;      
input double StopLossBuffer    = 2.0;    

//--- Group: Trailing Stop
input bool   UseAtrTrailing    = true;   
input int    AtrPeriod         = 14;     
input double AtrMultiplier     = 2.0;    
input double TrailStepPips     = 1.0;    

//--- Global Variables
double Pips;
int    SlippagePoints;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    Pips = Point;
    SlippagePoints = MaxSlippagePips;
    
    if(Digits == 3 || Digits == 5) 
    {
        Pips = Point * 10;
        SlippagePoints = MaxSlippagePips * 10;
    }
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    // 1. Manage Active Trades (Trailing, Break-Even, Partial Closes)
    ManageAdvancedExits();
    ManageTrailingStop();

    // 2. Filter: Only run entry logic on a new bar
    if(!IsNewBar()) return; 

    // 3. Filter: Check Trading Hours and Max Spread before scanning
    if(!IsTradingHour()) return;
    if((MarketInfo(Symbol(), MODE_SPREAD) * Point) > (MaxSpreadPips * Pips)) return;

    // 4. Scan for Entries if we have no open positions
    if(CountOpenPositions() == 0)
    {
        ScanForEntries();
    }
}

//+------------------------------------------------------------------+
//| Advanced Exits: Partial Profit & Break Even                      |
//+------------------------------------------------------------------+
void ManageAdvancedExits()
{
    double stopLevel = MarketInfo(Symbol(), MODE_STOPLEVEL) * Point;

    for(int i = OrdersTotal() - 1; i >= 0; i--)
    {
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
            if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
            {
                double profitPips = 0;
                double openPrice  = OrderOpenPrice();
                double currentSL  = OrderStopLoss();
                int    type       = OrderType();
                
                if(type == OP_BUY)  profitPips = (Bid - openPrice) / Pips;
                if(type == OP_SELL) profitPips = (openPrice - Ask) / Pips;

                // --- BREAK EVEN LOGIC ---
                if(UseBreakEven && profitPips >= BreakEvenPips)
                {
                    double newBE = 0;
                    if(type == OP_BUY)
                    {
                        newBE = openPrice + (LockInPips * Pips);
                        if(currentSL < newBE && (Bid - newBE) >= stopLevel)
                            OrderModify(OrderTicket(), openPrice, NormalizeDouble(newBE, Digits), OrderTakeProfit(), 0, clrGreen);
                    }
                    else if(type == OP_SELL)
                    {
                        newBE = openPrice - (LockInPips * Pips);
                        if((currentSL > newBE || currentSL == 0) && (newBE - Ask) >= stopLevel)
                            OrderModify(OrderTicket(), openPrice, NormalizeDouble(newBE, Digits), OrderTakeProfit(), 0, clrGreen);
                    }
                }

                // --- PARTIAL CLOSE LOGIC ---
                if(UsePartialClose && profitPips >= PartialTargetPips)
                {
                    // MT4 adds "to #ticket" / "from #ticket" to comments on partial close. 
                    // We check this to ensure we only partially close the order ONCE.
                    if(StringFind(OrderComment(), "from #") == -1 && StringFind(OrderComment(), "to #") == -1)
                    {
                        double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
                        double lotsToClose = OrderLots() * (PartialClosePct / 100.0);
                        lotsToClose = MathFloor(lotsToClose / lotStep) * lotStep; // Normalize to broker steps
                        
                        double minLot = MarketInfo(Symbol(), MODE_MINLOT);
                        if(lotsToClose >= minLot && lotsToClose < OrderLots())
                        {
                            double closePrice = (type == OP_BUY) ? Bid : Ask;
                            color  closeColor = (type == OP_BUY) ? clrBlue : clrRed;
                            
                            if(OrderClose(OrderTicket(), lotsToClose, closePrice, SlippagePoints, closeColor))
                                Print("Partial profit taken successfully. Ticket: ", OrderTicket());
                        }
                    }
                }
            }
        }
    }
}

//+------------------------------------------------------------------+
//| Core Entry Logic                                                 |
//+------------------------------------------------------------------+
void ScanForEntries()
{
    double ema200_1 = iMA(Symbol(), 0, EmaPeriod, 0, MODE_EMA, PRICE_CLOSE, 1);
    double mfi1     = iMFI(Symbol(), 0, MfiPeriod, 1);
    double mfi2     = iMFI(Symbol(), 0, MfiPeriod, 2);

    int highestIndex = iHighest(Symbol(), 0, MODE_HIGH, SwingLookback, 1);
    int lowestIndex  = iLowest(Symbol(), 0, MODE_LOW, SwingLookback, 1);
    
    double swingHigh = High[highestIndex];
    double swingLow  = Low[lowestIndex];
    double swingRange = swingHigh - swingLow;

    if(swingRange == 0) return; 

    double initialSL = 0, initialTP = 0;
    int orderType = -1;

    // BUY SETUP
    if(Close[1] > ema200_1 && lowestIndex > highestIndex)
    {
        double fib618 = swingHigh - (swingRange * 0.618);
        double fib786 = swingHigh - (swingRange * 0.786);
        
        if((Close[1] <= fib618) && (Close[1] >= fib786) && (mfi2 <= 20) && (mfi1 > 20))
        {
            orderType = OP_BUY;
            initialSL = swingLow - (StopLossBuffer * Pips); 
            initialTP = swingHigh; 
        }
    }
    // SELL SETUP
    else if(Close[1] < ema200_1 && highestIndex > lowestIndex)
    {
        double fib618 = swingLow + (swingRange * 0.618);
        double fib786 = swingLow + (swingRange * 0.786);
        
        if((Close[1] >= fib618) && (Close[1] <= fib786) && (mfi2 >= 80) && (mfi1 < 80))
        {
            orderType = OP_SELL;
            initialSL = swingHigh + (StopLossBuffer * Pips);
            initialTP = swingLow; 
        }
    }

    // EXECUTION
    if(orderType != -1)
    {
        double price = (orderType == OP_BUY) ? Ask : Bid;
        initialSL = AdjustToStopLevel(orderType, price, initialSL);
        
        double slDistance = MathAbs(price - initialSL) / Pips;
        double lotSize = CalculateLotSize(slDistance);
        
        color arrowColor = (orderType == OP_BUY) ? clrDodgerBlue : clrCrimson;
        
        int ticket = OrderSend(Symbol(), orderType, lotSize, price, SlippagePoints, initialSL, initialTP, TradeComment, MagicNumber, 0, arrowColor);
        if(ticket < 0) Print("OrderSend Failed. Error: ", GetLastError());
    }
}

//+------------------------------------------------------------------+
//| Dynamic ATR Trailing Stop Engine                                 |
//+------------------------------------------------------------------+
void ManageTrailingStop()
{
    if(!UseAtrTrailing) return;
    
    double atr = iATR(Symbol(), 0, AtrPeriod, 1); 
    double trailDistance = atr * AtrMultiplier;
    double stopLevel = MarketInfo(Symbol(), MODE_STOPLEVEL) * Point;
    double minStep = TrailStepPips * Pips; 

    for(int i = OrdersTotal() - 1; i >= 0; i--)
    {
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
            if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
            {
                double currentSL = OrderStopLoss();
                
                if(OrderType() == OP_BUY)
                {
                    double newSL = NormalizeDouble(Bid - trailDistance, Digits);
                    if(newSL > currentSL + minStep || currentSL == 0)
                    {
                        if((Bid - newSL) >= stopLevel)
                            OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrBlue);
                    }
                }
                else if(OrderType() == OP_SELL)
                {
                    double newSL = NormalizeDouble(Ask + trailDistance, Digits);
                    if(newSL < currentSL - minStep || currentSL == 0)
                    {
                        if((newSL - Ask) >= stopLevel)
                            OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrRed);
                    }
                }
            }
        }
    }
}

//+------------------------------------------------------------------+
//| Helper: Check Trading Session                                    |
//+------------------------------------------------------------------+
bool IsTradingHour()
{
    int h = Hour();
    if(StartTradingHour < EndTradingHour) 
        return (h >= StartTradingHour && h < EndTradingHour);
    else if (StartTradingHour > EndTradingHour) // Handles overnight (e.g., 22 to 04)
        return (h >= StartTradingHour || h < EndTradingHour);
    
    return true; // If equal, trade 24 hours
}

//+------------------------------------------------------------------+
//| Helper: Calculate Dynamic Lot Size based on Risk %               |
//+------------------------------------------------------------------+
double CalculateLotSize(double slPips)
{
    if(!UseDynamicRisk || slPips <= 0) return FixedLotSize;

    double riskAmount = AccountBalance() * (RiskPercent / 100.0);
    double tickValue  = MarketInfo(Symbol(), MODE_TICKVALUE);
    double lotStep    = MarketInfo(Symbol(), MODE_LOTSTEP);
    
    if(tickValue == 0) return FixedLotSize; 

    double rawLot = riskAmount / (slPips * tickValue * (Pips / Point));
    double finalLot = MathFloor(rawLot / lotStep) * lotStep;

    if(finalLot < MarketInfo(Symbol(), MODE_MINLOT)) finalLot = MarketInfo(Symbol(), MODE_MINLOT);
    if(finalLot > MarketInfo(Symbol(), MODE_MAXLOT)) finalLot = MarketInfo(Symbol(), MODE_MAXLOT);

    return NormalizeDouble(finalLot, 2);
}

//+------------------------------------------------------------------+
//| Helper: Adjust StopLoss to comply with Broker StopLevel          |
//+------------------------------------------------------------------+
double AdjustToStopLevel(int type, double openPrice, double slPrice)
{
    double stopLevel = MarketInfo(Symbol(), MODE_STOPLEVEL) * Point;
    if(type == OP_BUY && openPrice - slPrice < stopLevel) return NormalizeDouble(openPrice - stopLevel, Digits);
    if(type == OP_SELL && slPrice - openPrice < stopLevel) return NormalizeDouble(openPrice + stopLevel, Digits);
    return NormalizeDouble(slPrice, Digits);
}

//+------------------------------------------------------------------+
//| Helper: Check for a new bar                                      |
//+------------------------------------------------------------------+
bool IsNewBar()
{
    static datetime lastBarTime = 0;
    datetime currentBarTime = iTime(Symbol(), 0, 0);
    if(lastBarTime != currentBarTime)
    {
        lastBarTime = currentBarTime;
        return true;
    }
    return false;
}

//+------------------------------------------------------------------+
//| Helper: Count open positions                                     |
//+------------------------------------------------------------------+
int CountOpenPositions()
{
    int count = 0;
    for(int i = 0; i < OrdersTotal(); i++)
    {
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
            if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber) count++;
        }
    }
    return count;
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Post Reply