Page 1 of 2

🛢️ High-Volume Oil (WTI) Scalping Strategy – 18 Years of Price Action Distilled

Posted: Mon Aug 31, 2026 9:26 pm
by PTScalper
Hey everyone,

While we spend a lot of time analyzing forex and silver pairs here on forex-scalping.com, I wanted to pivot and share a robust approach for scalping oil (WTI/USOIL). Oil offers incredible intraday volatility, but because of its fast-moving nature, it requires strict risk management and algorithmic discipline.

Over my 18 years in the markets, I’ve found that keeping the charts clean and focusing on price action combined with momentum filters is the most consistent way to scalp commodities.

The Strategy Setup
Instrument: USOIL / WTI / XTIUSD

Timeframe: M1 or M5

Indicators: 20 EMA, 50 EMA, and 14-period RSI

Session: London-New York overlap (peak liquidity and tightest spreads)

The Entry Rules
Long Setup:

Trend Alignment: Wait for the 20 EMA to cleanly cross above the 50 EMA.

Momentum Filter: The 14-period RSI must be above 50, confirming buying pressure.

Execution: Enter at market on the close of the crossover candle.

Short Setup:

Trend Alignment: Wait for the 20 EMA to cross below the 50 EMA.

Momentum Filter: The 14-period RSI must be below 50.

Execution: Enter at market on the close of the crossover candle.

Re: 🛢️ High-Volume Oil (WTI) Scalping Strategy – 18 Years of Price Action Distilled

Posted: Mon Aug 31, 2026 9:26 pm
by PTScalper
Risk Management

For scalping oil, stop losses need to be tight but respect the asset's noise. I typically aim for a strict 1:1.5 or 1:2 Risk-to-Reward ratio. Because oil spreads can fluctuate, ensure your average net profit per trade heavily outweighs the commission and slippage costs.

The MT4 Automation Script (MQL4)

To take the emotion out of the execution, I've written a lightweight MT4 Expert Advisor that automatically trades this EMA/RSI crossover logic. You can compile this in MetaEditor and run it directly on your oil charts.

Code: Select all

//+------------------------------------------------------------------+
//|                                              Oil_Scalper_EA.mq4 |
//|                                      forex-scalping.com          |
//+------------------------------------------------------------------+
#property copyright "forex-scalping.com"
#property strict

//--- Input Parameters
input double LotSize = 0.5;
input int StopLoss = 200;    // SL in points (adjust based on broker's oil decimals)
input int TakeProfit = 400;  // TP in points 
input int MagicNumber = 888999;
input int FastEMA = 20;
input int SlowEMA = 50;
input int RSIPeriod = 14;

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   // Prevent opening multiple positions at once
   if(OrdersTotal() > 0) return;

   // Retrieve Indicator Values
   double fastEma0 = iMA(NULL, 0, FastEMA, 0, MODE_EMA, PRICE_CLOSE, 0);
   double fastEma1 = iMA(NULL, 0, FastEMA, 0, MODE_EMA, PRICE_CLOSE, 1);
   
   double slowEma0 = iMA(NULL, 0, SlowEMA, 0, MODE_EMA, PRICE_CLOSE, 0);
   double slowEma1 = iMA(NULL, 0, SlowEMA, 0, MODE_EMA, PRICE_CLOSE, 1);
   
   double rsi = iRSI(NULL, 0, RSIPeriod, PRICE_CLOSE, 0);

   // Buy Condition: Fast EMA crosses above Slow EMA and RSI > 50
   if(fastEma1 <= slowEma1 && fastEma0 > slowEma0 && rsi > 50)
     {
      double sl = Ask - StopLoss * Point;
      double tp = Ask + TakeProfit * Point;
      int ticket = OrderSend(Symbol(), OP_BUY, LotSize, Ask, 3, sl, tp, "Oil Scalp Buy", MagicNumber, 0, clrBlue);
     }
     
   // Sell Condition: Fast EMA crosses below Slow EMA and RSI < 50
   if(fastEma1 >= slowEma1 && fastEma0 < slowEma0 && rsi < 50)
     {
      double sl = Bid + StopLoss * Point;
      double tp = Bid - TakeProfit * Point;
      int ticket = OrderSend(Symbol(), OP_SELL, LotSize, Bid, 3, sl, tp, "Oil Scalp Sell", MagicNumber, 0, clrRed);
     }
  }
//+------------------------------------------------------------------+

Re: 🛢️ High-Volume Oil (WTI) Scalping Strategy – 18 Years of Price Action Distilled

Posted: Mon Aug 31, 2026 9:27 pm
by PTScalper
To add a time filter, you can introduce input variables for the start and end hours and use the built-in Hour() function to check the current broker server time before executing any trades.

The London and New York overlap typically runs from 13:00 to 17:00 GMT, but because MT4 runs on your broker's server time (not your local time in the Czech Republic), you need to make these hours adjustable so you can align them with your specific broker's timezone offset.

Here is the updated script with the time filter integrated:

Code: Select all

//+------------------------------------------------------------------+
//|                                              Oil_Scalper_EA.mq4  |
//|                                      forex-scalping.com          |
//+------------------------------------------------------------------+
#property copyright "forex-scalping.com"
#property strict

//--- Input Parameters
input double LotSize = 0.5;
input int StopLoss = 200;    // SL in points
input int TakeProfit = 400;  // TP in points 
input int MagicNumber = 888999;
input int FastEMA = 20;
input int SlowEMA = 50;
input int RSIPeriod = 14;

//--- Session Time Filter
input int TradeStartHour = 14; // Broker server hour for NY Open (Adjust to your broker)
input int TradeEndHour = 17;   // Broker server hour for London Close (Adjust to your broker)

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   // 1. Check if we are inside the allowed trading session
   int currentHour = Hour();
   bool isTradingTime = (currentHour >= TradeStartHour && currentHour < TradeEndHour);
   
   // If outside the overlap hours, exit the function and do not trade
   if(!isTradingTime) return;

   // 2. Prevent opening multiple positions at once
   if(OrdersTotal() > 0) return;

   // 3. Retrieve Indicator Values
   double fastEma0 = iMA(NULL, 0, FastEMA, 0, MODE_EMA, PRICE_CLOSE, 0);
   double fastEma1 = iMA(NULL, 0, FastEMA, 0, MODE_EMA, PRICE_CLOSE, 1);
   
   double slowEma0 = iMA(NULL, 0, SlowEMA, 0, MODE_EMA, PRICE_CLOSE, 0);
   double slowEma1 = iMA(NULL, 0, SlowEMA, 0, MODE_EMA, PRICE_CLOSE, 1);
   
   double rsi = iRSI(NULL, 0, RSIPeriod, PRICE_CLOSE, 0);

   // Buy Condition: Fast EMA crosses above Slow EMA and RSI > 50
   if(fastEma1 <= slowEma1 && fastEma0 > slowEma0 && rsi > 50)
     {
      double sl = Ask - StopLoss * Point;
      double tp = Ask + TakeProfit * Point;
      int ticket = OrderSend(Symbol(), OP_BUY, LotSize, Ask, 3, sl, tp, "Oil Scalp Buy", MagicNumber, 0, clrBlue);
     }
     
   // Sell Condition: Fast EMA crosses below Slow EMA and RSI < 50
   if(fastEma1 >= slowEma1 && fastEma0 < slowEma0 && rsi < 50)
     {
      double sl = Bid + StopLoss * Point;
      double tp = Bid - TakeProfit * Point;
      int ticket = OrderSend(Symbol(), OP_SELL, LotSize, Bid, 3, sl, tp, "Oil Scalp Sell", MagicNumber, 0, clrRed);
     }
  }
//+------------------------------------------------------------------+
What Changed:

Added Time Inputs: TradeStartHour and TradeEndHour allow you (or your forum members) to quickly tweak the active window directly from the EA properties box without recompiling the code.

The Session Check: The Hour() function grabs the current server hour (e.g., if it's 14:35, Hour() returns 14). The script verifies that the current hour is >= TradeStartHour and < TradeEndHour.

The Early Exit: If isTradingTime returns false, the script hits return; and stops reading the rest of the logic until the next tick, saving CPU resources when the markets are quieter.

Re: 🛢️ High-Volume Oil (WTI) Scalping Strategy – 18 Years of Price Action Distilled

Posted: Mon Aug 31, 2026 9:27 pm
by PTScalper
To capture sudden momentum spikes while protecting your floating profits, you can implement a step-based trailing stop.

A standard trailing stop updates with every single tick, which can spam the broker's server and lead to execution delays. A step-based approach uses a TrailingStep variable, ensuring the stop loss only modifies after the price has moved a predefined distance in your favor.

Here is the updated code incorporating a custom ManageTrailingStop() function:

Code: Select all

//+------------------------------------------------------------------+
//|                                              Oil_Scalper_EA.mq4  |
//|                                      forex-scalping.com          |
//+------------------------------------------------------------------+
#property copyright "forex-scalping.com"
#property strict

//--- Input Parameters
input double LotSize = 0.5;
input int StopLoss = 200;    
input int TakeProfit = 400;  
input int MagicNumber = 888999;
input int FastEMA = 20;
input int SlowEMA = 50;
input int RSIPeriod = 14;

//--- Session Time Filter
input int TradeStartHour = 14; 
input int TradeEndHour = 17;   

//--- Trailing Stop Settings
input int TrailingStop = 150; // Distance to trail the price (in points)
input int TrailingStep = 50;  // Minimum move before updating the SL (in points)

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   // 1. Manage open positions first (Trailing Stop)
   ManageTrailingStop();

   // 2. Check time filter
   int currentHour = Hour();
   bool isTradingTime = (currentHour >= TradeStartHour && currentHour < TradeEndHour);
   if(!isTradingTime) return;

   // 3. Prevent multiple positions
   if(OrdersTotal() > 0) return;

   // 4. Retrieve Indicator Values
   double fastEma0 = iMA(NULL, 0, FastEMA, 0, MODE_EMA, PRICE_CLOSE, 0);
   double fastEma1 = iMA(NULL, 0, FastEMA, 0, MODE_EMA, PRICE_CLOSE, 1);
   
   double slowEma0 = iMA(NULL, 0, SlowEMA, 0, MODE_EMA, PRICE_CLOSE, 0);
   double slowEma1 = iMA(NULL, 0, SlowEMA, 0, MODE_EMA, PRICE_CLOSE, 1);
   
   double rsi = iRSI(NULL, 0, RSIPeriod, PRICE_CLOSE, 0);

   // Buy Condition
   if(fastEma1 <= slowEma1 && fastEma0 > slowEma0 && rsi > 50)
     {
      double sl = Ask - StopLoss * Point;
      double tp = Ask + TakeProfit * Point;
      int ticket = OrderSend(Symbol(), OP_BUY, LotSize, Ask, 3, sl, tp, "Oil Scalp Buy", MagicNumber, 0, clrBlue);
     }
     
   // Sell Condition
   if(fastEma1 >= slowEma1 && fastEma0 < slowEma0 && rsi < 50)
     {
      double sl = Bid + StopLoss * Point;
      double tp = Bid - TakeProfit * Point;
      int ticket = OrderSend(Symbol(), OP_SELL, LotSize, Bid, 3, sl, tp, "Oil Scalp Sell", MagicNumber, 0, clrRed);
     }
  }

//+------------------------------------------------------------------+
//| Manage Trailing Stop Function                                    |
//+------------------------------------------------------------------+
void ManageTrailingStop()
  {
   for(int i = OrdersTotal() - 1; i >= 0; i--)
     {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
         if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
           {
            // Buy Order Trailing Logic
            if(OrderType() == OP_BUY)
              {
               if(Bid - OrderOpenPrice() > TrailingStop * Point)
                 {
                  if(OrderStopLoss() < Bid - (TrailingStop + TrailingStep) * Point || OrderStopLoss() == 0)
                    {
                     bool res = OrderModify(OrderTicket(), OrderOpenPrice(), Bid - TrailingStop * Point, OrderTakeProfit(), 0, clrBlue);
                    }
                 }
              }
            // Sell Order Trailing Logic
            else if(OrderType() == OP_SELL)
              {
               if(OrderOpenPrice() - Ask > TrailingStop * Point)
                 {
                  if(OrderStopLoss() > Ask + (TrailingStop + TrailingStep) * Point || OrderStopLoss() == 0)
                    {
                     bool res = OrderModify(OrderTicket(), OrderOpenPrice(), Ask + TrailingStop * Point, OrderTakeProfit(), 0, clrRed);
                    }
                 }
              }
           }
        }
     }
  }
//+------------------------------------------------------------------+
Key Additions:

The For Loop: The function scans backward (OrdersTotal() - 1) through the open trades. This is the safest way to iterate through orders in MQL4 without skipping indexes if an order closes mid-loop.

Symbol and Magic Number Lock: OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber ensures the EA only manages its own oil trades, completely ignoring manual positions or trades executed by other scripts.

Step Logic Validation: The script actively measures the distance between the current price and the existing Stop Loss. It only triggers OrderModify() if the required TrailingStep threshold has been breached.

Re: 🛢️ High-Volume Oil (WTI) Scalping Strategy – 18 Years of Price Action Distilled

Posted: Mon Aug 31, 2026 9:28 pm
by PTScalper
To protect your entries from sudden liquidity drops and spread widening—which is especially important for scalping commodities—you can query the broker's current spread in real-time and block new trades if it exceeds your threshold.

The key is to place this check after the ManageTrailingStop() function. You want the EA to continue managing your open trades and locking in profits even if the spread is currently too wide to open a new one.

Here is the updated script with the maximum spread filter included:

Code: Select all

//+------------------------------------------------------------------+
//|                                              Oil_Scalper_EA.mq4  |
//|                                      forex-scalping.com          |
//+------------------------------------------------------------------+
#property copyright "forex-scalping.com"
#property strict

//--- Input Parameters
input double LotSize = 0.5;
input int StopLoss = 200;    
input int TakeProfit = 400;  
input int MagicNumber = 888999;
input int FastEMA = 20;
input int SlowEMA = 50;
input int RSIPeriod = 14;

//--- Session Time Filter
input int TradeStartHour = 14; 
input int TradeEndHour = 17;   

//--- Trailing Stop Settings
input int TrailingStop = 150; 
input int TrailingStep = 50;  

//--- Spread Filter
input int MaxSpread = 40;     // Maximum allowed spread in points

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   // 1. Manage open positions first (Trailing Stop must always run)
   ManageTrailingStop();

   // 2. Check time filter
   int currentHour = Hour();
   bool isTradingTime = (currentHour >= TradeStartHour && currentHour < TradeEndHour);
   if(!isTradingTime) return;

   // 3. Prevent multiple positions
   if(OrdersTotal() > 0) return;

   // 4. Check Spread Filter
   // MarketInfo returns the current spread in points
   int currentSpread = (int)MarketInfo(Symbol(), MODE_SPREAD);
   
   // If the spread is wider than allowed, exit before reading indicators
   if(currentSpread > MaxSpread) return;

   // 5. Retrieve Indicator Values
   double fastEma0 = iMA(NULL, 0, FastEMA, 0, MODE_EMA, PRICE_CLOSE, 0);
   double fastEma1 = iMA(NULL, 0, FastEMA, 0, MODE_EMA, PRICE_CLOSE, 1);
   
   double slowEma0 = iMA(NULL, 0, SlowEMA, 0, MODE_EMA, PRICE_CLOSE, 0);
   double slowEma1 = iMA(NULL, 0, SlowEMA, 0, MODE_EMA, PRICE_CLOSE, 1);
   
   double rsi = iRSI(NULL, 0, RSIPeriod, PRICE_CLOSE, 0);

   // Buy Condition
   if(fastEma1 <= slowEma1 && fastEma0 > slowEma0 && rsi > 50)
     {
      double sl = Ask - StopLoss * Point;
      double tp = Ask + TakeProfit * Point;
      int ticket = OrderSend(Symbol(), OP_BUY, LotSize, Ask, 3, sl, tp, "Oil Scalp Buy", MagicNumber, 0, clrBlue);
     }
     
   // Sell Condition
   if(fastEma1 >= slowEma1 && fastEma0 < slowEma0 && rsi < 50)
     {
      double sl = Bid + StopLoss * Point;
      double tp = Bid - TakeProfit * Point;
      int ticket = OrderSend(Symbol(), OP_SELL, LotSize, Bid, 3, sl, tp, "Oil Scalp Sell", MagicNumber, 0, clrRed);
     }
  }

//+------------------------------------------------------------------+
//| Manage Trailing Stop Function                                    |
//+------------------------------------------------------------------+
void ManageTrailingStop()
  {
   for(int i = OrdersTotal() - 1; i >= 0; i--)
     {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
         if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
           {
            // Buy Order Trailing Logic
            if(OrderType() == OP_BUY)
              {
               if(Bid - OrderOpenPrice() > TrailingStop * Point)
                 {
                  if(OrderStopLoss() < Bid - (TrailingStop + TrailingStep) * Point || OrderStopLoss() == 0)
                    {
                     bool res = OrderModify(OrderTicket(), OrderOpenPrice(), Bid - TrailingStop * Point, OrderTakeProfit(), 0, clrBlue);
                    }
                 }
              }
            // Sell Order Trailing Logic
            else if(OrderType() == OP_SELL)
              {
               if(OrderOpenPrice() - Ask > TrailingStop * Point)
                 {
                  if(OrderStopLoss() > Ask + (TrailingStop + TrailingStep) * Point || OrderStopLoss() == 0)
                    {
                     bool res = OrderModify(OrderTicket(), OrderOpenPrice(), Ask + TrailingStop * Point, OrderTakeProfit(), 0, clrRed);
                    }
                 }
              }
           }
        }
     }
  }
//+------------------------------------------------------------------+

Re: 🛢️ High-Volume Oil (WTI) Scalping Strategy – 18 Years of Price Action Distilled

Posted: Mon Aug 31, 2026 9:28 pm
by PTScalper
Key Additions:
MaxSpread Input: You can adjust the tolerance dynamically from the EA properties window without recompiling.

MarketInfo(Symbol(), MODE_SPREAD): This native MT4 function pulls the exact, real-time spread from the broker in points.

Early Exit Sequence: By placing if(currentSpread > MaxSpread) return; immediately after the time filter and open trades check, the EA saves processing power by not calculating the EMA and RSI values when the market conditions aren't safe for a scalp.

Re: 🛢️ High-Volume Oil (WTI) Scalping Strategy – 18 Years of Price Action Distilled

Posted: Mon Aug 31, 2026 9:29 pm
by PTScalper
Moving from MT4 to MT5 requires a complete rewrite because MQL5 handles indicators and trade execution very differently.

In MT5, you cannot simply call an indicator function like iMA() inside OnTick(). Instead, you have to create an "indicator handle" when the EA loads, and then pull data into an array (buffer) on every tick. Additionally, MT5 uses a "Position" system rather than an "Order" system for open trades.

To make execution clean and reliable, we use the built-in <Trade\Trade.mqh> standard library.

Here is the fully converted, MT5-ready version of the WTI Scalping strategy:

Code: Select all

//+------------------------------------------------------------------+
//|                                              Oil_Scalper_EA.mq5  |
//|                                      forex-scalping.com          |
//+------------------------------------------------------------------+
#property copyright "forex-scalping.com"
#property version   "1.00"

#include <Trade\Trade.mqh> // Include standard trade library
CTrade trade;              // Initialize the CTrade object

//--- Input Parameters
input double LotSize = 0.5;
input int StopLoss = 200;    
input int TakeProfit = 400;  
input ulong MagicNumber = 888999; // Note: MagicNumber is ulong in MT5
input int FastEMA = 20;
input int SlowEMA = 50;
input int RSIPeriod = 14;

//--- Session Time Filter
input int TradeStartHour = 14; 
input int TradeEndHour = 17;   

//--- Trailing Stop Settings
input int TrailingStop = 150; 
input int TrailingStep = 50;  

//--- Spread Filter
input int MaxSpread = 40;     

//--- Indicator Handles
int handleFastEMA;
int handleSlowEMA;
int handleRSI;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   // Set the magic number for the CTrade object
   trade.SetExpertMagicNumber(MagicNumber);

   // Initialize indicator handles
   handleFastEMA = iMA(_Symbol, PERIOD_CURRENT, FastEMA, 0, MODE_EMA, PRICE_CLOSE);
   handleSlowEMA = iMA(_Symbol, PERIOD_CURRENT, SlowEMA, 0, MODE_EMA, PRICE_CLOSE);
   handleRSI = iRSI(_Symbol, PERIOD_CURRENT, RSIPeriod, PRICE_CLOSE);

   // Check if handles were created successfully
   if(handleFastEMA == INVALID_HANDLE || handleSlowEMA == INVALID_HANDLE || handleRSI == INVALID_HANDLE)
     {
      Print("Error: Failed to initialize indicators.");
      return(INIT_FAILED);
     }
     
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   // Free up memory by releasing indicator handles
   IndicatorRelease(handleFastEMA);
   IndicatorRelease(handleSlowEMA);
   IndicatorRelease(handleRSI);
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   // 1. Manage open positions (Trailing Stop)
   ManageTrailingStop();

   // 2. Check time filter
   MqlDateTime timeStruct;
   TimeCurrent(timeStruct);
   bool isTradingTime = (timeStruct.hour >= TradeStartHour && timeStruct.hour < TradeEndHour);
   if(!isTradingTime) return;

   // 3. Prevent multiple positions (Count only this EA's positions)
   if(PositionsTotalThisEA() > 0) return;

   // 4. Check Spread Filter
   long currentSpread = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);
   if(currentSpread > MaxSpread) return;

   // 5. Retrieve Indicator Values via CopyBuffer
   double fastEmaArray[], slowEmaArray[], rsiArray[];
   
   // Make arrays behave like MT4 (index 0 is current unclosed candle)
   ArraySetAsSeries(fastEmaArray, true);
   ArraySetAsSeries(slowEmaArray, true);
   ArraySetAsSeries(rsiArray, true);
   
   // Copy the last 2 candles for EMA, and 1 for RSI
   if(CopyBuffer(handleFastEMA, 0, 0, 2, fastEmaArray) <= 0) return;
   if(CopyBuffer(handleSlowEMA, 0, 0, 2, slowEmaArray) <= 0) return;
   if(CopyBuffer(handleRSI, 0, 0, 1, rsiArray) <= 0) return;

   double fastEma0 = fastEmaArray[0];
   double fastEma1 = fastEmaArray[1];
   double slowEma0 = slowEmaArray[0];
   double slowEma1 = slowEmaArray[1];
   double currentRsi = rsiArray[0];

   // Fetch current Bid/Ask and Point size for MT5
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);

   // Buy Condition
   if(fastEma1 <= slowEma1 && fastEma0 > slowEma0 && currentRsi > 50)
     {
      double sl = ask - StopLoss * point;
      double tp = ask + TakeProfit * point;
      trade.Buy(LotSize, _Symbol, ask, sl, tp, "Oil Scalp Buy");
     }
     
   // Sell Condition
   if(fastEma1 >= slowEma1 && fastEma0 < slowEma0 && currentRsi < 50)
     {
      double sl = bid + StopLoss * point;
      double tp = bid - TakeProfit * point;
      trade.Sell(LotSize, _Symbol, bid, sl, tp, "Oil Scalp Sell");
     }
  }

//+------------------------------------------------------------------+
//| Custom Function: Count Positions for this EA                     |
//+------------------------------------------------------------------+
int PositionsTotalThisEA()
  {
   int count = 0;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i);
      if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == MagicNumber)
        {
         count++;
        }
     }
   return count;
  }

//+------------------------------------------------------------------+
//| Manage Trailing Stop Function                                    |
//+------------------------------------------------------------------+
void ManageTrailingStop()
  {
   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i); // Selects position and gets ticket
      
      if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == MagicNumber)
        {
         double currentSL = PositionGetDouble(POSITION_SL);
         double currentTP = PositionGetDouble(POSITION_TP);
         double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
         long posType = PositionGetInteger(POSITION_TYPE);

         // Buy Position Trailing Logic
         if(posType == POSITION_TYPE_BUY)
           {
            if(bid - openPrice > TrailingStop * point)
              {
               double newSL = bid - TrailingStop * point;
               if(currentSL < bid - (TrailingStop + TrailingStep) * point || currentSL == 0)
                 {
                  trade.PositionModify(ticket, newSL, currentTP);
                 }
              }
           }
         // Sell Position Trailing Logic
         else if(posType == POSITION_TYPE_SELL)
           {
            if(openPrice - ask > TrailingStop * point)
              {
               double newSL = ask + TrailingStop * point;
               if(currentSL > ask + (TrailingStop + TrailingStep) * point || currentSL == 0)
                 {
                  trade.PositionModify(ticket, newSL, currentTP);
                 }
              }
           }
        }
     }
  }
//+------------------------------------------------------------------+
Important MT5 Architecture Changes:
CTrade Class: Replaces the clunky OrderSend() function with trade.Buy(), trade.Sell(), and trade.PositionModify(). It handles the complex trade requests in the background.

Handles & Arrays: Indicators now generate a handle in OnInit(). During OnTick(), we copy the exact number of candles we need into an array (fastEmaArray), using ArraySetAsSeries() to make sure index 0 is always the current live candle—mirroring how MT4 works.

Positions, Not Orders: We built a custom PositionsTotalThisEA() function. MT5 distinguishes between active trades ("Positions") and pending limits/stops ("Orders").

Re: 🛢️ High-Volume Oil (WTI) Scalping Strategy – 18 Years of Price Action Distilled

Posted: Mon Aug 31, 2026 9:29 pm
by PTScalper
To calculate your position size dynamically based on a percentage of your account equity, we need to divide your maximum monetary risk by the financial cost of a 1-lot position hitting your specific Stop Loss.

Because oil (WTI) and other commodities often have non-standard contract sizes and tick values compared to forex, the calculation must query the broker's specific SYMBOL_TRADE_TICK_VALUE, SYMBOL_TRADE_TICK_SIZE, and SYMBOL_VOLUME_STEP to avoid "Invalid Volume" order rejections.

Here is how to update your MQL5 script.

Add Risk Management Inputs
Replace your single LotSize input at the top of the file with these variables to allow toggling between auto and fixed lot sizing:

Code: Select all

//--- Risk Management Inputs
input bool   UseAutoLot   = true;  // Enable dynamic lot sizing
input double RiskPercent  = 1.0;   // Risk per trade (% of Equity)
input double FixedLotSize = 0.5;   // Fallback fixed lot size
input int    StopLoss     = 200;   // SL in points
input int    TakeProfit   = 400;  
input ulong  MagicNumber  = 888999;

Re: 🛢️ High-Volume Oil (WTI) Scalping Strategy – 18 Years of Price Action Distilled

Posted: Mon Aug 31, 2026 9:30 pm
by PTScalper
Add the Lot Sizing Function

Place this custom function anywhere at the bottom of your script (outside of OnTick()). It handles the equity math and rounds the final volume to match your broker's allowed increments:

Code: Select all

//+------------------------------------------------------------------+
//| Custom Function: Calculate Lot Size based on Equity Risk         |
//+------------------------------------------------------------------+
double CalculateLotSize()
  {
   // Return fixed lot if auto-lot is disabled
   if(!UseAutoLot) return FixedLotSize;

   // 1. Calculate risk amount in account currency
   double equity = AccountInfoDouble(ACCOUNT_EQUITY);
   double riskAmount = equity * (RiskPercent / 100.0);
   
   // 2. Fetch symbol contract specifications
   double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
   double tickSize  = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   double point     = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   
   // 3. Calculate monetary loss for 1 standard lot hitting the Stop Loss
   double lossPerLot = (StopLoss * point) / tickSize * tickValue;
   
   // Prevent division by zero if broker data is missing
   if(lossPerLot <= 0) return FixedLotSize; 
   
   // 4. Calculate raw lot size
   double calculatedLot = riskAmount / lossPerLot;
   
   // 5. Normalize lot size to broker limits and step increments
   double minLot  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double maxLot  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
   double stepLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   
   // Round to the nearest allowed step
   calculatedLot = MathRound(calculatedLot / stepLot) * stepLot;
   
   // Clamp to min/max limits
   if(calculatedLot < minLot) calculatedLot = minLot;
   if(calculatedLot > maxLot) calculatedLot = maxLot;
   
   return calculatedLot;
  }

Re: 🛢️ High-Volume Oil (WTI) Scalping Strategy – 18 Years of Price Action Distilled

Posted: Mon Aug 31, 2026 9:30 pm
by PTScalper
Update the OnTick() Execution

Inside your OnTick() function, find the execution block at the very bottom and update the variables to call CalculateLotSize() right before sending the trade:

Code: Select all

// Buy Condition
   if(fastEma1 <= slowEma1 && fastEma0 > slowEma0 && currentRsi > 50)
     {
      double sl = ask - StopLoss * point;
      double tp = ask + TakeProfit * point;
      double tradeLot = CalculateLotSize(); // <-- Dynamic lot calculation
      
      trade.Buy(tradeLot, _Symbol, ask, sl, tp, "Oil Scalp Buy");
     }
     
   // Sell Condition
   if(fastEma1 >= slowEma1 && fastEma0 < slowEma0 && currentRsi < 50)
     {
      double sl = bid + StopLoss * point;
      double tp = bid - TakeProfit * point;
      double tradeLot = CalculateLotSize(); // <-- Dynamic lot calculation
      
      trade.Sell(tradeLot, _Symbol, bid, sl, tp, "Oil Scalp Sell");
     }
Now, as your account equity grows, the position volume on your scalps will automatically compound, and if you hit a drawdown, the lot size will smoothly decrease to protect your remaining capital.