This complete MQL5 Expert Advisor (EA) implements the classic Bill Williams Fractal and Alligator breakout strategy for MetaTrader 5.
The code handles two technical pitfalls that break most retail Fractal EAs:
Zero Repainting (Confirmed Bar Indexing): It evaluates fractals exclusively at Bar Index 3 on a newly opened bar (Bar 0), guaranteeing that Bar 1 and Bar 2 have fully closed to the right of the fractal peak before any order is placed.
True Alligator Shift Alignment: In MQL5, iAlligator applies the future shift (8, 5, 3) visually via PLOT_SHIFT, meaning CopyBuffer() returns unshifted SMMA values at index i. To read the actual Alligator Teeth value visually plotted at bar b, this EA correctly reads teethBuffer[b + InpTeethShift].
The Truth About Bill Williams Fractals
Re: The Truth About Bill Williams Fractals
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: The Truth About Bill Williams Fractals
MQL5 Source Code (WilliamsFractalChaosEA.mq5)
Save this file in your MetaTrader 5 directory under MQL5/Experts/WilliamsFractalChaosEA.mq5 and compile it in MetaEditor (F7).
Save this file in your MetaTrader 5 directory under MQL5/Experts/WilliamsFractalChaosEA.mq5 and compile it in MetaEditor (F7).
Code: Select all
//+------------------------------------------------------------------+
//| WilliamsFractalChaosEA.mq5 |
//| Bill Williams Fractal + Alligator Breakout |
//+------------------------------------------------------------------+
#property copyright "Forex Strategy Deep Dive"
#property version "1.00"
#property strict
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\OrderInfo.mqh>
//--- Input Parameters: Alligator Settings
input group "=== Alligator Settings ==="
input int InpJawPeriod = 13; // Jaw Period (Blue)
input int InpJawShift = 8; // Jaw Future Shift
input int InpTeethPeriod = 8; // Teeth Period (Red - Filter)
input int InpTeethShift = 5; // Teeth Future Shift
input int InpLipsPeriod = 5; // Lips Period (Green)
input int InpLipsShift = 3; // Lips Future Shift
input ENUM_MA_METHOD InpAlligatorMethod = MODE_SMMA; // Smoothing Method
input ENUM_APPLIED_PRICE InpAlligatorPrice = PRICE_MEDIAN;// Applied Price ((H+L)/2)
//--- Input Parameters: Fractal & Execution Rules
input group "=== Fractal Breakout & Stop Rules ==="
input double InpEntryBufferPips = 1.5; // Breakout Buffer Above/Below Fractal (Pips)
input double InpSLBufferPips = 2.0; // Stop-Loss Buffer Beyond Opposite Fractal (Pips)
input int InpTrailFractalRank = 2; // Trail Behind N-th Opposite Fractal (1 or 2)
input int InpFractalLookback = 100; // Max Bars to Search for Opposite Fractals
input int InpOrderExpirationHrs = 48; // Pending Order Expiration (Hours, 0 = GTC)
input double InpMinSLDistancePips = 10.0; // Minimum Stop-Loss Distance (Pips)
input double InpMaxSLDistancePips = 150.0; // Maximum Stop-Loss Distance (Pips)
//--- Input Parameters: Risk & Trade Management
input group "=== Risk & Money Management ==="
input double InpRiskPercent = 1.0; // Risk Per Trade (% of Balance, 0 = Fixed Lot)
input double InpFixedLot = 0.10; // Fixed Lot Size (if Risk % is 0)
input double InpRewardRiskRatio = 0.0; // Take Profit R:R Ratio (0 = Disable, Trail Only)
input ulong InpMagicNumber = 199505; // EA Magic Number
input ulong InpSlippagePoints = 10; // Max Slippage (Points)
//--- Global Variables & Handles
CTrade ExtTrade;
CPositionInfo ExtPosition;
COrderInfo ExtOrder;
int ExtFractalsHandle = INVALID_HANDLE;
int ExtAlligatorHandle = INVALID_HANDLE;
datetime ExtLastBarTime = 0;
double ExtPipValue = 0.0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- Configure CTrade execution wrapper
ExtTrade.SetExpertMagicNumber(InpMagicNumber);
ExtTrade.SetDeviationInPoints(InpSlippagePoints);
ExtTrade.SetTypeFillingBySymbol(_Symbol);
//--- Calculate 1 Standard Pip in price terms (handles 3/5 and 2/4 digit brokers)
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
ExtPipValue = (digits == 3 || digits == 5) ? _Point * 10.0 : _Point;
//--- Initialize Bill Williams Indicators
ExtFractalsHandle = iFractals(_Symbol, _Period);
if(ExtFractalsHandle == INVALID_HANDLE)
{
Print("Error creating iFractals handle: ", GetLastError());
return(INIT_FAILED);
}
ExtAlligatorHandle = iAlligator(_Symbol, _Period,
InpJawPeriod, InpJawShift,
InpTeethPeriod, InpTeethShift,
InpLipsPeriod, InpLipsShift,
InpAlligatorMethod, InpAlligatorPrice);
if(ExtAlligatorHandle == INVALID_HANDLE)
{
Print("Error creating iAlligator handle: ", GetLastError());
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(ExtFractalsHandle != INVALID_HANDLE) IndicatorRelease(ExtFractalsHandle);
if(ExtAlligatorHandle != INVALID_HANDLE) IndicatorRelease(ExtAlligatorHandle);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//--- Process strategy logic strictly once per newly closed candle
datetime currentBarTime = iTime(_Symbol, _Period, 0);
if(currentBarTime == ExtLastBarTime)
return;
//--- Ensure sufficient historical bars exist
if(Bars(_Symbol, _Period) < InpFractalLookback + InpJawShift + 10)
return;
ExtLastBarTime = currentBarTime;
//--- 1. Update mechanical fractal trailing stops on open positions
ManageOpenPositions();
//--- 2. Cancel invalidated pending orders if price crosses Alligator Teeth
ValidatePendingOrders();
//--- 3. Check for newly confirmed 5-bar Fractals at Bar Index 3
CheckForFractalSignals();
}
//+------------------------------------------------------------------+
//| Retrieve Visually Aligned Alligator Teeth Value at Bar 'barIdx' |
//+------------------------------------------------------------------+
double GetAlligatorTeethAtBar(int barIdx)
{
// Because PLOT_SHIFT shifts the line forward by InpTeethShift bars,
// the Teeth value visually plotted at 'barIdx' resides at buffer index (barIdx + InpTeethShift)
double teethBuf[];
ArraySetAsSeries(teethBuf, true);
int bufferIndex = barIdx + InpTeethShift;
if(CopyBuffer(ExtAlligatorHandle, 1, bufferIndex, 1, teethBuf) <= 0)
return(0.0);
return(teethBuf[0]);
}
//+------------------------------------------------------------------+
//| Find the N-th Most Recent Confirmed Fractal (Rank 1 = Newest) |
//+------------------------------------------------------------------+
double GetConfirmedFractalLevel(bool isUpFractal, int rank)
{
double fracBuf[];
ArraySetAsSeries(fracBuf, true);
int bufferNum = isUpFractal ? 0 : 1; // 0 = UPPER_LINE, 1 = LOWER_LINE
if(CopyBuffer(ExtFractalsHandle, bufferNum, 0, InpFractalLookback, fracBuf) <= 0)
return(0.0);
int foundCount = 0;
// Start at index 3 (first bar with 2 closed candles to its right: bars 2 and 1)
for(int i = 3; i < InpFractalLookback; i++)
{
if(fracBuf[i] != EMPTY_VALUE && fracBuf[i] > 0.0)
{
foundCount++;
if(foundCount >= rank)
return(fracBuf[i]);
}
}
// Fallback: if 2nd fractal not found in lookback window, return the 1st found
if(rank > 1 && foundCount == 1)
{
for(int i = 3; i < InpFractalLookback; i++)
if(fracBuf[i] != EMPTY_VALUE && fracBuf[i] > 0.0)
return(fracBuf[i]);
}
return(0.0);
}
//+------------------------------------------------------------------+
//| Evaluate Confirmed Fractals at Bar 3 and Place/Update Orders |
//+------------------------------------------------------------------+
void CheckForFractalSignals()
{
double upFracBuf[], dnFracBuf[];
ArraySetAsSeries(upFracBuf, true);
ArraySetAsSeries(dnFracBuf, true);
if(CopyBuffer(ExtFractalsHandle, 0, 3, 1, upFracBuf) <= 0) return;
if(CopyBuffer(ExtFractalsHandle, 1, 3, 1, dnFracBuf) <= 0) return;
double teethAtFractal = GetAlligatorTeethAtBar(3);
double teethAtCurrent = GetAlligatorTeethAtBar(1);
if(teethAtFractal <= 0.0 || teethAtCurrent <= 0.0) return;
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double spread = ask - bid;
//================================================================
// BULLISH SETUP: Confirmed Up Fractal at Bar 3 above Teeth
//================================================================
if(upFracBuf[0] != EMPTY_VALUE && upFracBuf[0] > teethAtFractal)
{
double fractalHigh = upFracBuf[0];
double entryPrice = NormalizeDouble(fractalHigh + (InpEntryBufferPips * ExtPipValue) + spread, _Digits);
// Ensure price hasn't already broken above the entry level and remains above Teeth
if(entryPrice > ask && fractalHigh > teethAtCurrent && !HasOpenPosition(POSITION_TYPE_BUY))
{
// Place initial SL below the N-th opposite Down Fractal (or Teeth if none in range)
double oppFractalLow = GetConfirmedFractalLevel(false, InpTrailFractalRank);
double slPrice = (oppFractalLow > 0.0 && oppFractalLow < entryPrice)
? oppFractalLow - (InpSLBufferPips * ExtPipValue)
: teethAtCurrent - (InpSLBufferPips * ExtPipValue);
slPrice = NormalizeDouble(slPrice, _Digits);
double slDistPips = (entryPrice - slPrice) / ExtPipValue;
if(slDistPips >= InpMinSLDistancePips && slDistPips <= InpMaxSLDistancePips)
{
double tpPrice = 0.0;
if(InpRewardRiskRatio > 0.0)
tpPrice = NormalizeDouble(entryPrice + (entryPrice - slPrice) * InpRewardRiskRatio, _Digits);
double lots = CalculateLotSize(entryPrice - slPrice);
PlaceOrReplaceStopOrder(ORDER_TYPE_BUY_STOP, lots, entryPrice, slPrice, tpPrice);
}
}
}
//================================================================
// BEARISH SETUP: Confirmed Down Fractal at Bar 3 below Teeth
//================================================================
if(dnFracBuf[0] != EMPTY_VALUE && dnFracBuf[0] < teethAtFractal)
{
double fractalLow = dnFracBuf[0];
double entryPrice = NormalizeDouble(fractalLow - (InpEntryBufferPips * ExtPipValue), _Digits);
// Ensure price hasn't already broken below the entry level and remains below Teeth
if(entryPrice < bid && fractalLow < teethAtCurrent && !HasOpenPosition(POSITION_TYPE_SELL))
{
// Place initial SL above the N-th opposite Up Fractal (or Teeth if none in range)
double oppFractalHigh = GetConfirmedFractalLevel(true, InpTrailFractalRank);
double slPrice = (oppFractalHigh > 0.0 && oppFractalHigh > entryPrice)
? oppFractalHigh + (InpSLBufferPips * ExtPipValue) + spread
: teethAtCurrent + (InpSLBufferPips * ExtPipValue) + spread;
slPrice = NormalizeDouble(slPrice, _Digits);
double slDistPips = (slPrice - entryPrice) / ExtPipValue;
if(slDistPips >= InpMinSLDistancePips && slDistPips <= InpMaxSLDistancePips)
{
double tpPrice = 0.0;
if(InpRewardRiskRatio > 0.0)
tpPrice = NormalizeDouble(entryPrice - (slPrice - entryPrice) * InpRewardRiskRatio, _Digits);
double lots = CalculateLotSize(slPrice - entryPrice);
PlaceOrReplaceStopOrder(ORDER_TYPE_SELL_STOP, lots, entryPrice, slPrice, tpPrice);
}
}
}
}
//+------------------------------------------------------------------+
//| Place a New Stop Order or Replace an Older Pending Stop Order |
//+------------------------------------------------------------------+
void PlaceOrReplaceStopOrder(ENUM_ORDER_TYPE orderType, double lots, double price, double sl, double tp)
{
//--- Per Bill Williams' rules: a newer valid fractal replaces any un-triggered stop order
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(ExtOrder.SelectByIndex(i))
{
if(ExtOrder.Symbol() == _Symbol && ExtOrder.Magic() == InpMagicNumber && ExtOrder.OrderType() == orderType)
{
ExtTrade.OrderDelete(ExtOrder.Ticket());
}
}
}
ENUM_ORDER_TYPE_TIME timeType = ORDER_TIME_GTC;
datetime expiration = 0;
if(InpOrderExpirationHrs > 0)
{
timeType = ORDER_TIME_SPECIFIED;
expiration = TimeCurrent() + (InpOrderExpirationHrs * 3600);
}
if(orderType == ORDER_TYPE_BUY_STOP)
ExtTrade.BuyStop(lots, price, _Symbol, sl, tp, timeType, expiration, "Williams Up Fractal Breakout");
else if(orderType == ORDER_TYPE_SELL_STOP)
ExtTrade.SellStop(lots, price, _Symbol, sl, tp, timeType, expiration, "Williams Down Fractal Breakout");
}
//+------------------------------------------------------------------+
//| Cancel Pending Orders if Price Closes Across Alligator Teeth |
//+------------------------------------------------------------------+
void ValidatePendingOrders()
{
double close1 = iClose(_Symbol, _Period, 1);
double teeth1 = GetAlligatorTeethAtBar(1);
if(teeth1 <= 0.0) return;
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(ExtOrder.SelectByIndex(i))
{
if(ExtOrder.Symbol() == _Symbol && ExtOrder.Magic() == InpMagicNumber)
{
// Cancel pending Buy Stop if market closes below Alligator Teeth
if(ExtOrder.OrderType() == ORDER_TYPE_BUY_STOP && close1 < teeth1)
ExtTrade.OrderDelete(ExtOrder.Ticket());
// Cancel pending Sell Stop if market closes above Alligator Teeth
if(ExtOrder.OrderType() == ORDER_TYPE_SELL_STOP && close1 > teeth1)
ExtTrade.OrderDelete(ExtOrder.Ticket());
}
}
}
}
//+------------------------------------------------------------------+
//| Trail Open Positions Behind Confirmed Opposite Fractals |
//+------------------------------------------------------------------+
void ManageOpenPositions()
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double spread = ask - bid;
double stopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * _Point;
double trailDownFractal = GetConfirmedFractalLevel(false, InpTrailFractalRank);
double trailUpFractal = GetConfirmedFractalLevel(true, InpTrailFractalRank);
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(ExtPosition.SelectByIndex(i))
{
if(ExtPosition.Symbol() != _Symbol || ExtPosition.Magic() != InpMagicNumber)
continue;
ulong ticket = ExtPosition.Ticket();
double currentSL = ExtPosition.StopLoss();
double currentTP = ExtPosition.TakeProfit();
//--- Trail Long Position behind N-th Down Fractal
if(ExtPosition.PositionType() == POSITION_TYPE_BUY && trailDownFractal > 0.0)
{
double newSL = NormalizeDouble(trailDownFractal - (InpSLBufferPips * ExtPipValue), _Digits);
if(newSL > currentSL + _Point && (bid - newSL) > stopLevel)
{
ExtTrade.PositionModify(ticket, newSL, currentTP);
}
}
//--- Trail Short Position behind N-th Up Fractal
else if(ExtPosition.PositionType() == POSITION_TYPE_SELL && trailUpFractal > 0.0)
{
double newSL = NormalizeDouble(trailUpFractal + (InpSLBufferPips * ExtPipValue) + spread, _Digits);
if((currentSL == 0.0 || newSL < currentSL - _Point) && (newSL - ask) > stopLevel)
{
ExtTrade.PositionModify(ticket, newSL, currentTP);
}
}
}
}
}
//+------------------------------------------------------------------+
//| Check if a Position of Given Direction is Already Open |
//+------------------------------------------------------------------+
bool HasOpenPosition(ENUM_POSITION_TYPE posType)
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(ExtPosition.SelectByIndex(i))
{
if(ExtPosition.Symbol() == _Symbol &&
ExtPosition.Magic() == InpMagicNumber &&
ExtPosition.PositionType() == posType)
return(true);
}
}
return(false);
}
//+------------------------------------------------------------------+
//| Dynamic Position Sizing Based on Account Risk % and SL Distance |
//+------------------------------------------------------------------+
double CalculateLotSize(double slPriceDistance)
{
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
if(InpRiskPercent <= 0.0 || slPriceDistance <= 0.0)
return(MathMax(minLot, MathMin(maxLot, InpFixedLot)));
double riskMoney = AccountInfoDouble(ACCOUNT_BALANCE) * (InpRiskPercent / 100.0);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
if(tickSize <= 0.0 || tickValue <= 0.0)
return(minLot);
double moneyRiskPerLot = (slPriceDistance / tickSize) * tickValue;
if(moneyRiskPerLot <= 0.0)
return(minLot);
double rawLots = riskMoney / moneyRiskPerLot;
double steppedLots = MathFloor(rawLots / lotStep) * lotStep;
return(NormalizeDouble(MathMax(minLot, MathMin(maxLot, steppedLots)), 2));
}
//+------------------------------------------------------------------+Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: The Truth About Bill Williams Fractals
How to Test It in MT5 Strategy Tester
Open MetaEditor (F4 in MetaTrader 5), create a new Expert Advisor named WilliamsFractalChaosEA.mq5, paste the code above, and press Compile (F7).
In MetaTrader 5, open the Strategy Tester (Ctrl+R) and select WilliamsFractalChaosEA.
Recommended Starting Settings:
Pairs: EURUSD, GBPUSD, USDJPY, or XAUUSD
Timeframe: H1 or H4 (avoids low-timeframe spread noise)
Modeling Mode: Every tick based on real ticks (important for accurate pending stop execution and spread simulation)
Take Profit vs. Pure Trend Riding: Leave InpRewardRiskRatio = 0.0 to let winners run purely on the mechanical Down/Up Fractal trailing stop, or set it to 2.0 to cap trades at a 2:1 Reward-to-Risk target.
Open MetaEditor (F4 in MetaTrader 5), create a new Expert Advisor named WilliamsFractalChaosEA.mq5, paste the code above, and press Compile (F7).
In MetaTrader 5, open the Strategy Tester (Ctrl+R) and select WilliamsFractalChaosEA.
Recommended Starting Settings:
Pairs: EURUSD, GBPUSD, USDJPY, or XAUUSD
Timeframe: H1 or H4 (avoids low-timeframe spread noise)
Modeling Mode: Every tick based on real ticks (important for accurate pending stop execution and spread simulation)
Take Profit vs. Pure Trend Riding: Leave InpRewardRiskRatio = 0.0 to let winners run purely on the mechanical Down/Up Fractal trailing stop, or set it to 2.0 to cap trades at a 2:1 Reward-to-Risk target.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: The Truth About Bill Williams Fractals
To prevent the Expert Advisor from placing breakout orders during tight sideways consolidation ("chop"), we can quantify whether the Alligator is Awake (Hungry) or Sleeping (Sated) at any bar index $b$ using four mathematical criteria across its three visually shifted lines—Jaw (Blue, 13/8), Teeth (Red, 8/5), and Lips (Green, 5/3):
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: The Truth About Bill Williams Fractals
Directional Fan Alignment:
Bullish Awake State: The Lips must sit above the Teeth, and the Teeth must sit above the Jaw.Bearish Awake State:
The Lips must sit below the Teeth, and the Teeth must sit below the Jaw.Minimum Total Mouth Width (Pips or ATR-Normalized):
The total vertical distance between the outer lines, $\left\vert{}\text{Lips}_b - \text{Jaw}_b\right\vert{}$, must exceed a minimum pip threshold (InpMinMouthWidthPips) and/or a dynamic multiple of the 14-period Average True Range (InpMinMouthATRMult). Using an ATR multiplier allows the filter to scale automatically between FX pairs and higher-volatility instruments like Gold (XAUUSD) across any timeframe.
Minimum Adjacent Line Separation: Both adjacent gaps—$\left\vert{}\text{Lips}_b - \text{Teeth}_b\right\vert{}$ and $\left\vert{}\text{Teeth}_b - \text{Jaw}_b\right\vert{}$—must exceed InpMinAdjacentGapPips so the EA rejects setups where two of the three lines are intertwined or touching.
Mouth Expansion Check (Optional): Compares the total spread $\left\vert{}\text{Lips}_1 - \text{Jaw}_1\right\vert{}$ on the just-closed bar against $\left\vert{}\text{Lips}_2 - \text{Jaw}_2\right\vert{}$ on the prior bar to verify that the Alligator's mouth is actively opening rather than closing.
Bullish Awake State: The Lips must sit above the Teeth, and the Teeth must sit above the Jaw.Bearish Awake State:
The Lips must sit below the Teeth, and the Teeth must sit below the Jaw.Minimum Total Mouth Width (Pips or ATR-Normalized):
The total vertical distance between the outer lines, $\left\vert{}\text{Lips}_b - \text{Jaw}_b\right\vert{}$, must exceed a minimum pip threshold (InpMinMouthWidthPips) and/or a dynamic multiple of the 14-period Average True Range (InpMinMouthATRMult). Using an ATR multiplier allows the filter to scale automatically between FX pairs and higher-volatility instruments like Gold (XAUUSD) across any timeframe.
Minimum Adjacent Line Separation: Both adjacent gaps—$\left\vert{}\text{Lips}_b - \text{Teeth}_b\right\vert{}$ and $\left\vert{}\text{Teeth}_b - \text{Jaw}_b\right\vert{}$—must exceed InpMinAdjacentGapPips so the EA rejects setups where two of the three lines are intertwined or touching.
Mouth Expansion Check (Optional): Compares the total spread $\left\vert{}\text{Lips}_1 - \text{Jaw}_1\right\vert{}$ on the just-closed bar against $\left\vert{}\text{Lips}_2 - \text{Jaw}_2\right\vert{}$ on the prior bar to verify that the Alligator's mouth is actively opening rather than closing.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: The Truth About Bill Williams Fractals
Updated MQL5 Source Code (WilliamsFractalChaosEA.mq5 v1.10)
Replace your existing code in MetaEditor with this updated version and press Compile (F7).
Replace your existing code in MetaEditor with this updated version and press Compile (F7).
Code: Select all
//+------------------------------------------------------------------+
//| WilliamsFractalChaosEA.mq5 |
//| Bill Williams Fractal + Alligator Awake Filter (v1.10) |
//+------------------------------------------------------------------+
#property copyright "Forex Strategy Deep Dive"
#property version "1.10"
#property strict
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\OrderInfo.mqh>
//--- Enum for Alligator Evaluation Bar
enum ENUM_AWAKE_EVAL_MODE
{
AWAKE_AT_SIGNAL_BAR = 0, // Check at Bar 1 (When Fractal Confirms)
AWAKE_AT_FRACTAL_BAR = 1, // Check at Bar 3 (At Fractal Peak/Trough)
AWAKE_AT_BOTH_BARS = 2 // Require Awake State at Both Bar 3 and Bar 1
};
//--- Input Parameters: Alligator Settings
input group "=== Alligator Core Settings ==="
input int InpJawPeriod = 13; // Jaw Period (Blue)
input int InpJawShift = 8; // Jaw Future Shift
input int InpTeethPeriod = 8; // Teeth Period (Red - Filter)
input int InpTeethShift = 5; // Teeth Future Shift
input int InpLipsPeriod = 5; // Lips Period (Green)
input int InpLipsShift = 3; // Lips Future Shift
input ENUM_MA_METHOD InpAlligatorMethod = MODE_SMMA; // Smoothing Method
input ENUM_APPLIED_PRICE InpAlligatorPrice = PRICE_MEDIAN; // Applied Price ((H+L)/2)
//--- Input Parameters: Alligator 'Awake' Mouth-Width Filter
input group "=== Alligator 'Awake' Filter Settings ==="
input bool InpUseAwakeFilter = true; // Enable Alligator Awake Filter
input ENUM_AWAKE_EVAL_MODE InpAwakeEvalMode = AWAKE_AT_SIGNAL_BAR;// Bar to Check Awake Condition
input bool InpRequireStrictFan = true; // Require Lips/Teeth/Jaw Fan Alignment
input double InpMinMouthWidthPips = 3.0; // Min Total Mouth Width |Lips - Jaw| (Pips)
input double InpMinAdjacentGapPips = 0.8; // Min Gap Between Adjacent Lines (Pips)
input int InpATRPeriod = 14; // ATR Period for Dynamic Width Filter
input double InpMinMouthATRMult = 0.25; // Min Mouth Width as ATR Multiple (0 = Off)
input bool InpRequireMouthExpanding= false; // Require Mouth Opening (Width[1] > Width[2])
input bool InpDeleteOrderIfSleeping= true; // Cancel Pending Orders if Alligator Sleeps
//--- Input Parameters: Fractal & Execution Rules
input group "=== Fractal Breakout & Stop Rules ==="
input double InpEntryBufferPips = 1.5; // Breakout Buffer Above/Below Fractal (Pips)
input double InpSLBufferPips = 2.0; // Stop-Loss Buffer Beyond Opposite Fractal (Pips)
input int InpTrailFractalRank = 2; // Trail Behind N-th Opposite Fractal (1 or 2)
input int InpFractalLookback = 100; // Max Bars to Search for Opposite Fractals
input int InpOrderExpirationHrs = 48; // Pending Order Expiration (Hours, 0 = GTC)
input double InpMinSLDistancePips = 10.0; // Minimum Stop-Loss Distance (Pips)
input double InpMaxSLDistancePips = 150.0; // Maximum Stop-Loss Distance (Pips)
//--- Input Parameters: Risk & Trade Management
input group "=== Risk & Money Management ==="
input double InpRiskPercent = 1.0; // Risk Per Trade (% of Balance, 0 = Fixed Lot)
input double InpFixedLot = 0.10; // Fixed Lot Size (if Risk % is 0)
input double InpRewardRiskRatio = 0.0; // Take Profit R:R Ratio (0 = Disable, Trail Only)
input ulong InpMagicNumber = 199505; // EA Magic Number
input ulong InpSlippagePoints = 10; // Max Slippage (Points)
//--- Global Variables & Handles
CTrade ExtTrade;
CPositionInfo ExtPosition;
COrderInfo ExtOrder;
int ExtFractalsHandle = INVALID_HANDLE;
int ExtAlligatorHandle = INVALID_HANDLE;
int ExtATRHandle = INVALID_HANDLE;
datetime ExtLastBarTime = 0;
double ExtPipValue = 0.0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- Configure CTrade execution wrapper
ExtTrade.SetExpertMagicNumber(InpMagicNumber);
ExtTrade.SetDeviationInPoints(InpSlippagePoints);
ExtTrade.SetTypeFillingBySymbol(_Symbol);
//--- Calculate 1 Standard Pip in price terms (handles 3/5 and 2/4 digit symbols)
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
ExtPipValue = (digits == 3 || digits == 5) ? _Point * 10.0 : _Point;
//--- Initialize Bill Williams Fractals
ExtFractalsHandle = iFractals(_Symbol, _Period);
if(ExtFractalsHandle == INVALID_HANDLE)
{
Print("Error creating iFractals handle: ", GetLastError());
return(INIT_FAILED);
}
//--- Initialize Bill Williams Alligator
ExtAlligatorHandle = iAlligator(_Symbol, _Period,
InpJawPeriod, InpJawShift,
InpTeethPeriod, InpTeethShift,
InpLipsPeriod, InpLipsShift,
InpAlligatorMethod, InpAlligatorPrice);
if(ExtAlligatorHandle == INVALID_HANDLE)
{
Print("Error creating iAlligator handle: ", GetLastError());
return(INIT_FAILED);
}
//--- Initialize ATR for dynamic mouth-width scaling
ExtATRHandle = iATR(_Symbol, _Period, InpATRPeriod);
if(ExtATRHandle == INVALID_HANDLE)
{
Print("Error creating iATR handle: ", GetLastError());
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(ExtFractalsHandle != INVALID_HANDLE) IndicatorRelease(ExtFractalsHandle);
if(ExtAlligatorHandle != INVALID_HANDLE) IndicatorRelease(ExtAlligatorHandle);
if(ExtATRHandle != INVALID_HANDLE) IndicatorRelease(ExtATRHandle);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//--- Process strategy logic strictly once per newly closed candle
datetime currentBarTime = iTime(_Symbol, _Period, 0);
if(currentBarTime == ExtLastBarTime)
return;
//--- Ensure sufficient historical bars exist
int maxShift = MathMax(InpJawShift, MathMax(InpTeethShift, InpLipsShift));
if(Bars(_Symbol, _Period) < InpFractalLookback + maxShift + 10)
return;
ExtLastBarTime = currentBarTime;
//--- 1. Update mechanical fractal trailing stops on open positions
ManageOpenPositions();
//--- 2. Cancel invalidated pending orders if price crosses Teeth or Alligator sleeps
ValidatePendingOrders();
//--- 3. Check for newly confirmed 5-bar Fractals at Bar Index 3
CheckForFractalSignals();
}
//+------------------------------------------------------------------+
//| Retrieve Visually Aligned Alligator Jaw, Teeth & Lips at 'barIdx'|
//+------------------------------------------------------------------+
bool GetAlligatorLinesAtBar(int barIdx, double &jaw, double &teeth, double &lips)
{
// Buffer 0 = Jaw (Blue), Buffer 1 = Teeth (Red), Buffer 2 = Lips (Green)
// Because PLOT_SHIFT shifts lines into the future on the chart, the value visually
// plotted at 'barIdx' resides at buffer index (barIdx + respective Shift).
double jawBuf[], teethBuf[], lipsBuf[];
ArraySetAsSeries(jawBuf, true);
ArraySetAsSeries(teethBuf, true);
ArraySetAsSeries(lipsBuf, true);
if(CopyBuffer(ExtAlligatorHandle, 0, barIdx + InpJawShift, 1, jawBuf) <= 0) return(false);
if(CopyBuffer(ExtAlligatorHandle, 1, barIdx + InpTeethShift, 1, teethBuf) <= 0) return(false);
if(CopyBuffer(ExtAlligatorHandle, 2, barIdx + InpLipsShift, 1, lipsBuf) <= 0) return(false);
jaw = jawBuf[0];
teeth = teethBuf[0];
lips = lipsBuf[0];
return(jaw > 0.0 && teeth > 0.0 && lips > 0.0);
}
//+------------------------------------------------------------------+
//| Check if Alligator is Awake at a Single Bar Index |
//+------------------------------------------------------------------+
bool IsAlligatorAwakeAtBar(int barIdx, bool isBullish)
{
double jaw = 0.0, teeth = 0.0, lips = 0.0;
if(!GetAlligatorLinesAtBar(barIdx, jaw, teeth, lips))
return(false);
//--- 1. Check strict directional fan ordering (Lips > Teeth > Jaw or Lips < Teeth < Jaw)
if(InpRequireStrictFan)
{
if(isBullish && !(lips > teeth && teeth > jaw))
return(false);
if(!isBullish && !(lips < teeth && teeth < jaw))
return(false);
}
//--- 2. Check minimum total mouth width (|Lips - Jaw|) in Pips
double totalMouthWidth = MathAbs(lips - jaw);
double minMouthPrice = InpMinMouthWidthPips * ExtPipValue;
if(totalMouthWidth < minMouthPrice)
return(false);
//--- 3. Check minimum gap between adjacent lines (|Lips - Teeth| and |Teeth - Jaw|)
double lipsTeethGap = MathAbs(lips - teeth);
double teethJawGap = MathAbs(teeth - jaw);
double minAdjPrice = InpMinAdjacentGapPips * ExtPipValue;
if(lipsTeethGap < minAdjPrice || teethJawGap < minAdjPrice)
return(false);
//--- 4. Check ATR-normalized mouth width threshold (if enabled)
if(InpMinMouthATRMult > 0.0)
{
double atrBuf[];
ArraySetAsSeries(atrBuf, true);
if(CopyBuffer(ExtATRHandle, 0, barIdx, 1, atrBuf) <= 0)
return(false);
if(totalMouthWidth < (atrBuf[0] * InpMinMouthATRMult))
return(false);
}
//--- 5. Check if mouth is actively expanding compared to prior bar (barIdx + 1)
if(InpRequireMouthExpanding)
{
double prevJaw = 0.0, prevTeeth = 0.0, prevLips = 0.0;
if(!GetAlligatorLinesAtBar(barIdx + 1, prevJaw, prevTeeth, prevLips))
return(false);
double prevMouthWidth = MathAbs(prevLips - prevJaw);
if(totalMouthWidth <= prevMouthWidth)
return(false);
}
return(true);
}
//+------------------------------------------------------------------+
//| Master Wrapper to Evaluate Alligator Awake State per Mode |
//+------------------------------------------------------------------+
bool CheckAlligatorAwakeFilter(bool isBullish)
{
if(!InpUseAwakeFilter)
return(true);
switch(InpAwakeEvalMode)
{
case AWAKE_AT_SIGNAL_BAR:
return IsAlligatorAwakeAtBar(1, isBullish);
case AWAKE_AT_FRACTAL_BAR:
return IsAlligatorAwakeAtBar(3, isBullish);
case AWAKE_AT_BOTH_BARS:
return (IsAlligatorAwakeAtBar(3, isBullish) && IsAlligatorAwakeAtBar(1, isBullish));
}
return(false);
}
//+------------------------------------------------------------------+
//| Find the N-th Most Recent Confirmed Fractal (Rank 1 = Newest) |
//+------------------------------------------------------------------+
double GetConfirmedFractalLevel(bool isUpFractal, int rank)
{
double fracBuf[];
ArraySetAsSeries(fracBuf, true);
int bufferNum = isUpFractal ? 0 : 1; // 0 = UPPER_LINE, 1 = LOWER_LINE
if(CopyBuffer(ExtFractalsHandle, bufferNum, 0, InpFractalLookback, fracBuf) <= 0)
return(0.0);
int foundCount = 0;
// Start at index 3 (first bar with 2 closed candles to its right: bars 2 and 1)
for(int i = 3; i < InpFractalLookback; i++)
{
if(fracBuf[i] != EMPTY_VALUE && fracBuf[i] > 0.0)
{
foundCount++;
if(foundCount >= rank)
return(fracBuf[i]);
}
}
// Fallback: if 2nd fractal not found in lookback window, return the 1st found
if(rank > 1 && foundCount == 1)
{
for(int i = 3; i < InpFractalLookback; i++)
if(fracBuf[i] != EMPTY_VALUE && fracBuf[i] > 0.0)
return(fracBuf[i]);
}
return(0.0);
}
//+------------------------------------------------------------------+
//| Evaluate Confirmed Fractals at Bar 3 and Place/Update Orders |
//+------------------------------------------------------------------+
void CheckForFractalSignals()
{
double upFracBuf[], dnFracBuf[];
ArraySetAsSeries(upFracBuf, true);
ArraySetAsSeries(dnFracBuf, true);
if(CopyBuffer(ExtFractalsHandle, 0, 3, 1, upFracBuf) <= 0) return;
if(CopyBuffer(ExtFractalsHandle, 1, 3, 1, dnFracBuf) <= 0) return;
double jaw3 = 0.0, teethAtFractal = 0.0, lips3 = 0.0;
double jaw1 = 0.0, teethAtCurrent = 0.0, lips1 = 0.0;
if(!GetAlligatorLinesAtBar(3, jaw3, teethAtFractal, lips3)) return;
if(!GetAlligatorLinesAtBar(1, jaw1, teethAtCurrent, lips1)) return;
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double spread = ask - bid;
//================================================================
// BULLISH SETUP: Confirmed Up Fractal at Bar 3 + Awake Alligator
//================================================================
if(upFracBuf[0] != EMPTY_VALUE && upFracBuf[0] > teethAtFractal)
{
// Verify Alligator is Awake for Bullish Breakout
if(CheckAlligatorAwakeFilter(true))
{
double fractalHigh = upFracBuf[0];
double entryPrice = NormalizeDouble(fractalHigh + (InpEntryBufferPips * ExtPipValue) + spread, _Digits);
// Ensure price hasn't already broken above the entry level and remains above Teeth
if(entryPrice > ask && fractalHigh > teethAtCurrent && !HasOpenPosition(POSITION_TYPE_BUY))
{
double oppFractalLow = GetConfirmedFractalLevel(false, InpTrailFractalRank);
double slPrice = (oppFractalLow > 0.0 && oppFractalLow < entryPrice)
? oppFractalLow - (InpSLBufferPips * ExtPipValue)
: teethAtCurrent - (InpSLBufferPips * ExtPipValue);
slPrice = NormalizeDouble(slPrice, _Digits);
double slDistPips = (entryPrice - slPrice) / ExtPipValue;
if(slDistPips >= InpMinSLDistancePips && slDistPips <= InpMaxSLDistancePips)
{
double tpPrice = 0.0;
if(InpRewardRiskRatio > 0.0)
tpPrice = NormalizeDouble(entryPrice + (entryPrice - slPrice) * InpRewardRiskRatio, _Digits);
double lots = CalculateLotSize(entryPrice - slPrice);
PlaceOrReplaceStopOrder(ORDER_TYPE_BUY_STOP, lots, entryPrice, slPrice, tpPrice);
}
}
}
}
//================================================================
// BEARISH SETUP: Confirmed Down Fractal at Bar 3 + Awake Alligator
//================================================================
if(dnFracBuf[0] != EMPTY_VALUE && dnFracBuf[0] < teethAtFractal)
{
// Verify Alligator is Awake for Bearish Breakout
if(CheckAlligatorAwakeFilter(false))
{
double fractalLow = dnFracBuf[0];
double entryPrice = NormalizeDouble(fractalLow - (InpEntryBufferPips * ExtPipValue), _Digits);
// Ensure price hasn't already broken below the entry level and remains below Teeth
if(entryPrice < bid && fractalLow < teethAtCurrent && !HasOpenPosition(POSITION_TYPE_SELL))
{
double oppFractalHigh = GetConfirmedFractalLevel(true, InpTrailFractalRank);
double slPrice = (oppFractalHigh > 0.0 && oppFractalHigh > entryPrice)
? oppFractalHigh + (InpSLBufferPips * ExtPipValue) + spread
: teethAtCurrent + (InpSLBufferPips * ExtPipValue) + spread;
slPrice = NormalizeDouble(slPrice, _Digits);
double slDistPips = (slPrice - entryPrice) / ExtPipValue;
if(slDistPips >= InpMinSLDistancePips && slDistPips <= InpMaxSLDistancePips)
{
double tpPrice = 0.0;
if(InpRewardRiskRatio > 0.0)
tpPrice = NormalizeDouble(entryPrice - (slPrice - entryPrice) * InpRewardRiskRatio, _Digits);
double lots = CalculateLotSize(slPrice - entryPrice);
PlaceOrReplaceStopOrder(ORDER_TYPE_SELL_STOP, lots, entryPrice, slPrice, tpPrice);
}
}
}
}
}
//+------------------------------------------------------------------+
//| Place a New Stop Order or Replace an Older Pending Stop Order |
//+------------------------------------------------------------------+
void PlaceOrReplaceStopOrder(ENUM_ORDER_TYPE orderType, double lots, double price, double sl, double tp)
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(ExtOrder.SelectByIndex(i))
{
if(ExtOrder.Symbol() == _Symbol && ExtOrder.Magic() == InpMagicNumber && ExtOrder.OrderType() == orderType)
{
ExtTrade.OrderDelete(ExtOrder.Ticket());
}
}
}
ENUM_ORDER_TYPE_TIME timeType = ORDER_TIME_GTC;
datetime expiration = 0;
if(InpOrderExpirationHrs > 0)
{
timeType = ORDER_TIME_SPECIFIED;
expiration = TimeCurrent() + (InpOrderExpirationHrs * 3600);
}
if(orderType == ORDER_TYPE_BUY_STOP)
ExtTrade.BuyStop(lots, price, _Symbol, sl, tp, timeType, expiration, "Williams Up Fractal [Awake]");
else if(orderType == ORDER_TYPE_SELL_STOP)
ExtTrade.SellStop(lots, price, _Symbol, sl, tp, timeType, expiration, "Williams Down Fractal [Awake]");
}
//+------------------------------------------------------------------+
//| Cancel Pending Orders if Price Crosses Teeth or Alligator Sleeps |
//+------------------------------------------------------------------+
void ValidatePendingOrders()
{
double close1 = iClose(_Symbol, _Period, 1);
double jaw1 = 0.0, teeth1 = 0.0, lips1 = 0.0;
if(!GetAlligatorLinesAtBar(1, jaw1, teeth1, lips1))
return;
bool awakeBull = IsAlligatorAwakeAtBar(1, true);
bool awakeBear = IsAlligatorAwakeAtBar(1, false);
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(ExtOrder.SelectByIndex(i))
{
if(ExtOrder.Symbol() == _Symbol && ExtOrder.Magic() == InpMagicNumber)
{
//--- Cancel pending Buy Stop if price closes below Teeth OR Alligator falls asleep
if(ExtOrder.OrderType() == ORDER_TYPE_BUY_STOP)
{
if(close1 < teeth1 || (InpUseAwakeFilter && InpDeleteOrderIfSleeping && !awakeBull))
ExtTrade.OrderDelete(ExtOrder.Ticket());
}
//--- Cancel pending Sell Stop if price closes above Teeth OR Alligator falls asleep
if(ExtOrder.OrderType() == ORDER_TYPE_SELL_STOP)
{
if(close1 > teeth1 || (InpUseAwakeFilter && InpDeleteOrderIfSleeping && !awakeBear))
ExtTrade.OrderDelete(ExtOrder.Ticket());
}
}
}
}
}
//+------------------------------------------------------------------+
//| Trail Open Positions Behind Confirmed Opposite Fractals |
//+------------------------------------------------------------------+
void ManageOpenPositions()
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double spread = ask - bid;
double stopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * _Point;
double trailDownFractal = GetConfirmedFractalLevel(false, InpTrailFractalRank);
double trailUpFractal = GetConfirmedFractalLevel(true, InpTrailFractalRank);
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(ExtPosition.SelectByIndex(i))
{
if(ExtPosition.Symbol() != _Symbol || ExtPosition.Magic() != InpMagicNumber)
continue;
ulong ticket = ExtPosition.Ticket();
double currentSL = ExtPosition.StopLoss();
double currentTP = ExtPosition.TakeProfit();
//--- Trail Long Position behind N-th Down Fractal
if(ExtPosition.PositionType() == POSITION_TYPE_BUY && trailDownFractal > 0.0)
{
double newSL = NormalizeDouble(trailDownFractal - (InpSLBufferPips * ExtPipValue), _Digits);
if(newSL > currentSL + _Point && (bid - newSL) > stopLevel)
{
ExtTrade.PositionModify(ticket, newSL, currentTP);
}
}
//--- Trail Short Position behind N-th Up Fractal
else if(ExtPosition.PositionType() == POSITION_TYPE_SELL && trailUpFractal > 0.0)
{
double newSL = NormalizeDouble(trailUpFractal + (InpSLBufferPips * ExtPipValue) + spread, _Digits);
if((currentSL == 0.0 || newSL < currentSL - _Point) && (newSL - ask) > stopLevel)
{
ExtTrade.PositionModify(ticket, newSL, currentTP);
}
}
}
}
}
//+------------------------------------------------------------------+
//| Check if a Position of Given Direction is Already Open |
//+------------------------------------------------------------------+
bool HasOpenPosition(ENUM_POSITION_TYPE posType)
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(ExtPosition.SelectByIndex(i))
{
if(ExtPosition.Symbol() == _Symbol &&
ExtPosition.Magic() == InpMagicNumber &&
ExtPosition.PositionType() == posType)
return(true);
}
}
return(false);
}
//+------------------------------------------------------------------+
//| Dynamic Position Sizing Based on Account Risk % and SL Distance |
//+------------------------------------------------------------------+
double CalculateLotSize(double slPriceDistance)
{
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
if(InpRiskPercent <= 0.0 || slPriceDistance <= 0.0)
return(MathMax(minLot, MathMin(maxLot, InpFixedLot)));
double riskMoney = AccountInfoDouble(ACCOUNT_BALANCE) * (InpRiskPercent / 100.0);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
if(tickSize <= 0.0 || tickValue <= 0.0)
return(minLot);
double moneyRiskPerLot = (slPriceDistance / tickSize) * tickValue;
if(moneyRiskPerLot <= 0.0)
return(minLot);
double rawLots = riskMoney / moneyRiskPerLot;
double steppedLots = MathFloor(rawLots / lotStep) * lotStep;
return(NormalizeDouble(MathMax(minLot, MathMin(maxLot, steppedLots)), 2));
}
//+------------------------------------------------------------------+Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: The Truth About Bill Williams Fractals
To transform the Expert Advisor into a full Trading Chaos Multi-Indicator Pyramid System, this update integrates Bill Williams' Awesome Oscillator (AO).
In Trading Chaos, the Awesome Oscillator (a 34-period Simple Moving Average of the median price subtracted from a 5-period Simple Moving Average of the median price) serves as the primary momentum engine. This version adds three distinct AO scaling and entry filters, allowing you to scale into established trends or catch early momentum surges:
AO Zero-Line Cross (Trend Initiation): Requires the AO value at the signal bar to be strictly positive for a Buy (above 0.0) or negative for a Sell (below 0.0), ensuring trades only fire in the direction of the macro momentum cycle.
AO Saucer Signal (Continuation / Pyramiding): Bill Williams' famous "Saucer" setup requires a three-bar sequence where the AO dips and turns back in the trend direction (e.g., Red bar followed by two Green bars with higher values for a bullish saucer).
AO Twin Peaks / Peak Crossing: Requires the AO momentum histogram to print a higher peak or cross a previous momentum threshold in the direction of the breakout.
In Trading Chaos, the Awesome Oscillator (a 34-period Simple Moving Average of the median price subtracted from a 5-period Simple Moving Average of the median price) serves as the primary momentum engine. This version adds three distinct AO scaling and entry filters, allowing you to scale into established trends or catch early momentum surges:
AO Zero-Line Cross (Trend Initiation): Requires the AO value at the signal bar to be strictly positive for a Buy (above 0.0) or negative for a Sell (below 0.0), ensuring trades only fire in the direction of the macro momentum cycle.
AO Saucer Signal (Continuation / Pyramiding): Bill Williams' famous "Saucer" setup requires a three-bar sequence where the AO dips and turns back in the trend direction (e.g., Red bar followed by two Green bars with higher values for a bullish saucer).
AO Twin Peaks / Peak Crossing: Requires the AO momentum histogram to print a higher peak or cross a previous momentum threshold in the direction of the breakout.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: The Truth About Bill Williams Fractals
Updated MQL5 Source Code (WilliamsFractalChaosEA.mq5 v1.20)
Replace your existing code in MetaEditor with this updated version and press Compile (F7).
Replace your existing code in MetaEditor with this updated version and press Compile (F7).
Code: Select all
//+------------------------------------------------------------------+
//| WilliamsFractalChaosEA.mq5 |
//| Bill Williams Fractal + Alligator Awake + AO Pyramiding (v1.20)|
//+------------------------------------------------------------------+
#property copyright "Forex Strategy Deep Dive"
#property version "1.20"
#property strict
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\OrderInfo.mqh>
//--- Enum for Alligator Evaluation Bar
enum ENUM_AWAKE_EVAL_MODE
{
AWAKE_AT_SIGNAL_BAR = 0, // Check at Bar 1 (When Fractal Confirms)
AWAKE_AT_FRACTAL_BAR = 1, // Check at Bar 3 (At Fractal Peak/Trough)
AWAKE_AT_BOTH_BARS = 2 // Require Awake State at Both Bar 3 and Bar 1
};
//--- Enum for Awesome Oscillator Filter Modes
enum ENUM_AO_FILTER_MODE
{
AO_FILTER_NONE = 0, // Disabled (Fractals + Alligator Only)
AO_FILTER_ZERO_LINE = 1, // Require AO > 0 (Buy) or AO < 0 (Sell)
AO_FILTER_SAUCER = 2, // Require AO Saucer Pattern (Bullish/Bearish)
AO_FILTER_ZERO_OR_SAUCER= 3 // Require either Zero-Line alignment OR Saucer pattern
};
//--- Input Parameters: Alligator Settings
input group "=== Alligator Core Settings ==="
input int InpJawPeriod = 13; // Jaw Period (Blue)
input int InpJawShift = 8; // Jaw Future Shift
input int InpTeethPeriod = 8; // Teeth Period (Red - Filter)
input int InpTeethShift = 5; // Teeth Future Shift
input int InpLipsPeriod = 5; // Lips Period (Green)
input int InpLipsShift = 3; // Lips Future Shift
input ENUM_MA_METHOD InpAlligatorMethod = MODE_SMMA; // Smoothing Method
input ENUM_APPLIED_PRICE InpAlligatorPrice = PRICE_MEDIAN; // Applied Price ((H+L)/2)
//--- Input Parameters: Alligator 'Awake' Mouth-Width Filter
input group "=== Alligator 'Awake' Filter Settings ==="
input bool InpUseAwakeFilter = true; // Enable Alligator Awake Filter
input ENUM_AWAKE_EVAL_MODE InpAwakeEvalMode = AWAKE_AT_SIGNAL_BAR;// Bar to Check Awake Condition
input bool InpRequireStrictFan = true; // Require Lips/Teeth/Jaw Fan Alignment
input double InpMinMouthWidthPips = 3.0; // Min Total Mouth Width |Lips - Jaw| (Pips)
input double InpMinAdjacentGapPips = 0.8; // Min Gap Between Adjacent Lines (Pips)
input int InpATRPeriod = 14; // ATR Period for Dynamic Width Filter
input double InpMinMouthATRMult = 0.25; // Min Mouth Width as ATR Multiple (0 = Off)
input bool InpDeleteOrderIfSleeping= true; // Cancel Pending Orders if Alligator Sleeps
//--- Input Parameters: Awesome Oscillator (AO) Settings
input group "=== Awesome Oscillator (AO) Settings ==="
input ENUM_AO_FILTER_MODE InpAOFilterMode = AO_FILTER_ZERO_LINE;// AO Filter Mode
input int InpAOFastPeriod = 5; // AO Fast MA Period
input int InpAOSlowPeriod = 34; // AO Slow MA Period
input bool InpAllowPyramiding = true; // Allow Multiple Positions (Pyramiding via New Fractals)
input int InpMaxPyramidTrades = 3; // Max Concurrent Pyramiding Positions
//--- Input Parameters: Fractal & Execution Rules
input group "=== Fractal Breakout & Stop Rules ==="
input double InpEntryBufferPips = 1.5; // Breakout Buffer Above/Below Fractal (Pips)
input double InpSLBufferPips = 2.0; // Stop-Loss Buffer Beyond Opposite Fractal (Pips)
input int InpTrailFractalRank = 2; // Trail Behind N-th Opposite Fractal (1 or 2)
input int InpFractalLookback = 100; // Max Bars to Search for Opposite Fractals
input int InpOrderExpirationHrs = 48; // Pending Order Expiration (Hours, 0 = GTC)
input double InpMinSLDistancePips = 10.0; // Minimum Stop-Loss Distance (Pips)
input double InpMaxSLDistancePips = 150.0; // Maximum Stop-Loss Distance (Pips)
//--- Input Parameters: Risk & Trade Management
input group "=== Risk & Money Management ==="
input double InpRiskPercent = 1.0; // Risk Per Trade (% of Balance, 0 = Fixed Lot)
input double InpFixedLot = 0.10; // Fixed Lot Size (if Risk % is 0)
input double InpRewardRiskRatio = 0.0; // Take Profit R:R Ratio (0 = Disable, Trail Only)
input ulong InpMagicNumber = 199505; // EA Magic Number
input ulong InpSlippagePoints = 10; // Max Slippage (Points)
//--- Global Variables & Handles
CTrade ExtTrade;
CPositionInfo ExtPosition;
COrderInfo ExtOrder;
int ExtFractalsHandle = INVALID_HANDLE;
int ExtAlligatorHandle = INVALID_HANDLE;
int ExtATRHandle = INVALID_HANDLE;
int ExtAOHandle = INVALID_HANDLE;
datetime ExtLastBarTime = 0;
double ExtPipValue = 0.0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
ExtTrade.SetExpertMagicNumber(InpMagicNumber);
ExtTrade.SetDeviationInPoints(InpSlippagePoints);
ExtTrade.SetTypeFillingBySymbol(_Symbol);
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
ExtPipValue = (digits == 3 || digits == 5) ? _Point * 10.0 : _Point;
//--- Initialize Indicators
ExtFractalsHandle = iFractals(_Symbol, _Period);
if(ExtFractalsHandle == INVALID_HANDLE) { Print("Error iFractals"); return(INIT_FAILED); }
ExtAlligatorHandle = iAlligator(_Symbol, _Period,
InpJawPeriod, InpJawShift,
InpTeethPeriod, InpTeethShift,
InpLipsPeriod, InpLipsShift,
InpAlligatorMethod, InpAlligatorPrice);
if(ExtAlligatorHandle == INVALID_HANDLE) { Print("Error iAlligator"); return(INIT_FAILED); }
ExtATRHandle = iATR(_Symbol, _Period, InpATRPeriod);
if(ExtATRHandle == INVALID_HANDLE) { Print("Error iATR"); return(INIT_FAILED); }
ExtAOHandle = iAO(_Symbol, _Period);
if(ExtAOHandle == INVALID_HANDLE) { Print("Error iAO"); return(INIT_FAILED); }
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(ExtFractalsHandle != INVALID_HANDLE) IndicatorRelease(ExtFractalsHandle);
if(ExtAlligatorHandle != INVALID_HANDLE) IndicatorRelease(ExtAlligatorHandle);
if(ExtATRHandle != INVALID_HANDLE) IndicatorRelease(ExtATRHandle);
if(ExtAOHandle != INVALID_HANDLE) IndicatorRelease(ExtAOHandle);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
datetime currentBarTime = iTime(_Symbol, _Period, 0);
if(currentBarTime == ExtLastBarTime)
return;
int maxShift = MathMax(InpJawShift, MathMax(InpTeethShift, InpLipsShift));
if(Bars(_Symbol, _Period) < InpFractalLookback + maxShift + 10)
return;
ExtLastBarTime = currentBarTime;
ManageOpenPositions();
ValidatePendingOrders();
CheckForFractalSignals();
}
//+------------------------------------------------------------------+
//| Retrieve Visually Aligned Alligator Lines |
//+------------------------------------------------------------------+
bool GetAlligatorLinesAtBar(int barIdx, double &jaw, double &teeth, double &lips)
{
double jawBuf[], teethBuf[], lipsBuf[];
ArraySetAsSeries(jawBuf, true);
ArraySetAsSeries(teethBuf, true);
ArraySetAsSeries(lipsBuf, true);
if(CopyBuffer(ExtAlligatorHandle, 0, barIdx + InpJawShift, 1, jawBuf) <= 0) return(false);
if(CopyBuffer(ExtAlligatorHandle, 1, barIdx + InpTeethShift, 1, teethBuf) <= 0) return(false);
if(CopyBuffer(ExtAlligatorHandle, 2, barIdx + InpLipsShift, 1, lipsBuf) <= 0) return(false);
jaw = jawBuf[0];
teeth = teethBuf[0];
lips = lipsBuf[0];
return(jaw > 0.0 && teeth > 0.0 && lips > 0.0);
}
//+------------------------------------------------------------------+
//| Check Alligator Awake Condition |
//+------------------------------------------------------------------+
bool IsAlligatorAwakeAtBar(int barIdx, bool isBullish)
{
double jaw = 0.0, teeth = 0.0, lips = 0.0;
if(!GetAlligatorLinesAtBar(barIdx, jaw, teeth, lips))
return(false);
if(InpRequireStrictFan)
{
if(isBullish && !(lips > teeth && teeth > jaw)) return(false);
if(!isBullish && !(lips < teeth && teeth < jaw)) return(false);
}
double totalMouthWidth = MathAbs(lips - jaw);
if(totalMouthWidth < (InpMinMouthWidthPips * ExtPipValue)) return(false);
if(MathAbs(lips - teeth) < (InpMinAdjacentGapPips * ExtPipValue) ||
MathAbs(teeth - jaw) < (InpMinAdjacentGapPips * ExtPipValue)) return(false);
if(InpMinMouthATRMult > 0.0)
{
double atrBuf[];
ArraySetAsSeries(atrBuf, true);
if(CopyBuffer(ExtATRHandle, 0, barIdx, 1, atrBuf) <= 0) return(false);
if(totalMouthWidth < (atrBuf[0] * InpMinMouthATRMult)) return(false);
}
return(true);
}
bool CheckAlligatorAwakeFilter(bool isBullish)
{
if(!InpUseAwakeFilter) return(true);
switch(InpAwakeEvalMode)
{
case AWAKE_AT_SIGNAL_BAR: return IsAlligatorAwakeAtBar(1, isBullish);
case AWAKE_AT_FRACTAL_BAR: return IsAlligatorAwakeAtBar(3, isBullish);
case AWAKE_AT_BOTH_BARS: return (IsAlligatorAwakeAtBar(3, isBullish) && IsAlligatorAwakeAtBar(1, isBullish));
}
return(false);
}
//+------------------------------------------------------------------+
//| Awesome Oscillator (AO) Filter Validation |
//+------------------------------------------------------------------+
bool CheckAwesomeOscillatorFilter(bool isBullish)
{
if(InpAOFilterMode == AO_FILTER_NONE)
return(true);
double aoBuf[];
ArraySetAsSeries(aoBuf, true);
// Copy 4 bars of AO (Index 1 = Bar 1 closed, Index 2, 3 for saucer detection)
if(CopyBuffer(ExtAOHandle, 0, 1, 4, aoBuf) < 4)
return(false);
double ao1 = aoBuf[0]; // Bar 1
double ao2 = aoBuf[1]; // Bar 2
double ao3 = aoBuf[2]; // Bar 3
bool zeroLinePass = false;
bool saucerPass = false;
//--- 1. Zero-Line Check
if(isBullish && ao1 > 0.0) zeroLinePass = true;
if(!isBullish && ao1 < 0.0) zeroLinePass = true;
//--- 2. Saucer Check (Bill Williams rules)
// Bullish Saucer: AO is above zero (or dipping), Bar 3 > Bar 2, and Bar 1 > Bar 2 (Red bar surrounded by two green bars)
if(isBullish)
{
if(ao2 < ao3 && ao1 > ao2 && ao1 > 0.0)
saucerPass = true;
}
// Bearish Saucer: AO is below zero, Bar 3 < Bar 2, and Bar 1 < Bar 2 (Green bar surrounded by two red bars)
else
{
if(ao2 > ao3 && ao1 < ao2 && ao1 < 0.0)
saucerPass = true;
}
//--- 3. Apply Filter Mode Evaluation
switch(InpAOFilterMode)
{
case AO_FILTER_ZERO_LINE: return zeroLinePass;
case AO_FILTER_SAUCER: return saucerPass;
case AO_FILTER_ZERO_OR_SAUCER: return (zeroLinePass || saucerPass);
}
return(true);
}
//+------------------------------------------------------------------+
//| Find N-th Most Recent Confirmed Fractal |
//+------------------------------------------------------------------+
double GetConfirmedFractalLevel(bool isUpFractal, int rank)
{
double fracBuf[];
ArraySetAsSeries(fracBuf, true);
int bufferNum = isUpFractal ? 0 : 1;
if(CopyBuffer(ExtFractalsHandle, bufferNum, 0, InpFractalLookback, fracBuf) <= 0)
return(0.0);
int foundCount = 0;
for(int i = 3; i < InpFractalLookback; i++)
{
if(fracBuf[i] != EMPTY_VALUE && fracBuf[i] > 0.0)
{
foundCount++;
if(foundCount >= rank)
return(fracBuf[i]);
}
}
if(rank > 1 && foundCount == 1)
{
for(int i = 3; i < InpFractalLookback; i++)
if(fracBuf[i] != EMPTY_VALUE && fracBuf[i] > 0.0)
return(fracBuf[i]);
}
return(0.0);
}
//+------------------------------------------------------------------+
//| Evaluate Fractals + Alligator + AO and Place Orders |
//+------------------------------------------------------------------+
void CheckForFractalSignals()
{
double upFracBuf[], dnFracBuf[];
ArraySetAsSeries(upFracBuf, true);
ArraySetAsSeries(dnFracBuf, true);
if(CopyBuffer(ExtFractalsHandle, 0, 3, 1, upFracBuf) <= 0) return;
if(CopyBuffer(ExtFractalsHandle, 1, 3, 1, dnFracBuf) <= 0) return;
double jaw3 = 0.0, teethAtFractal = 0.0, lips3 = 0.0;
double jaw1 = 0.0, teethAtCurrent = 0.0, lips1 = 0.0;
if(!GetAlligatorLinesAtBar(3, jaw3, teethAtFractal, lips3)) return;
if(!GetAlligatorLinesAtBar(1, jaw1, teethAtCurrent, lips1)) return;
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double spread = ask - bid;
int openCount = CountOpenPositions();
if(!InpAllowPyramiding && openCount > 0)
return;
if(openCount >= InpMaxPyramidTrades)
return;
//================================================================
// BULLISH SETUP: Up Fractal + Awake Alligator + AO Filter
//================================================================
if(upFracBuf[0] != EMPTY_VALUE && upFracBuf[0] > teethAtFractal)
{
if(CheckAlligatorAwakeFilter(true) && CheckAwesomeOscillatorFilter(true))
{
double fractalHigh = upFracBuf[0];
double entryPrice = NormalizeDouble(fractalHigh + (InpEntryBufferPips * ExtPipValue) + spread, _Digits);
if(entryPrice > ask && fractalHigh > teethAtCurrent && !HasOpenPositionDirection(POSITION_TYPE_BUY, fractalHigh))
{
double oppFractalLow = GetConfirmedFractalLevel(false, InpTrailFractalRank);
double slPrice = (oppFractalLow > 0.0 && oppFractalLow < entryPrice)
? oppFractalLow - (InpSLBufferPips * ExtPipValue)
: teethAtCurrent - (InpSLBufferPips * ExtPipValue);
slPrice = NormalizeDouble(slPrice, _Digits);
double slDistPips = (entryPrice - slPrice) / ExtPipValue;
if(slDistPips >= InpMinSLDistancePips && slDistPips <= InpMaxSLDistancePips)
{
double tpPrice = 0.0;
if(InpRewardRiskRatio > 0.0)
tpPrice = NormalizeDouble(entryPrice + (entryPrice - slPrice) * InpRewardRiskRatio, _Digits);
double lots = CalculateLotSize(entryPrice - slPrice);
PlaceOrReplaceStopOrder(ORDER_TYPE_BUY_STOP, lots, entryPrice, slPrice, tpPrice);
}
}
}
}
//================================================================
// BEARISH SETUP: Down Fractal + Awake Alligator + AO Filter
//================================================================
if(dnFracBuf[0] != EMPTY_VALUE && dnFracBuf[0] < teethAtFractal)
{
if(CheckAlligatorAwakeFilter(false) && CheckAwesomeOscillatorFilter(false))
{
double fractalLow = dnFracBuf[0];
double entryPrice = NormalizeDouble(fractalLow - (InpEntryBufferPips * ExtPipValue), _Digits);
if(entryPrice < bid && fractalLow < teethAtCurrent && !HasOpenPositionDirection(POSITION_TYPE_SELL, fractalLow))
{
double oppFractalHigh = GetConfirmedFractalLevel(true, InpTrailFractalRank);
double slPrice = (oppFractalHigh > 0.0 && oppFractalHigh > entryPrice)
? oppFractalHigh + (InpSLBufferPips * ExtPipValue) + spread
: teethAtCurrent + (InpSLBufferPips * ExtPipValue) + spread;
slPrice = NormalizeDouble(slPrice, _Digits);
double slDistPips = (slPrice - entryPrice) / ExtPipValue;
if(slDistPips >= InpMinSLDistancePips && slDistPips <= InpMaxSLDistancePips)
{
double tpPrice = 0.0;
if(InpRewardRiskRatio > 0.0)
tpPrice = NormalizeDouble(entryPrice - (slPrice - entryPrice) * InpRewardRiskRatio, _Digits);
double lots = CalculateLotSize(slPrice - entryPrice);
PlaceOrReplaceStopOrder(ORDER_TYPE_SELL_STOP, lots, entryPrice, slPrice, tpPrice);
}
}
}
}
}
//+------------------------------------------------------------------+
//| Place or Replace Stop Orders |
//+------------------------------------------------------------------+
void PlaceOrReplaceStopOrder(ENUM_ORDER_TYPE orderType, double lots, double price, double sl, double tp)
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(ExtOrder.SelectByIndex(i))
{
if(ExtOrder.Symbol() == _Symbol && ExtOrder.Magic() == InpMagicNumber && ExtOrder.OrderType() == orderType)
{
ExtTrade.OrderDelete(ExtOrder.Ticket());
}
}
}
ENUM_ORDER_TYPE_TIME timeType = ORDER_TIME_GTC;
datetime expiration = 0;
if(InpOrderExpirationHrs > 0)
{
timeType = ORDER_TIME_SPECIFIED;
expiration = TimeCurrent() + (InpOrderExpirationHrs * 3600);
}
if(orderType == ORDER_TYPE_BUY_STOP)
ExtTrade.BuyStop(lots, price, _Symbol, sl, tp, timeType, expiration, "Williams Up Pyramid");
else if(orderType == ORDER_TYPE_SELL_STOP)
ExtTrade.SellStop(lots, price, _Symbol, sl, tp, timeType, expiration, "Williams Down Pyramid");
}
//+------------------------------------------------------------------+
//| Validate Pending Orders |
//+------------------------------------------------------------------+
void ValidatePendingOrders()
{
double close1 = iClose(_Symbol, _Period, 1);
double jaw1 = 0.0, teeth1 = 0.0, lips1 = 0.0;
if(!GetAlligatorLinesAtBar(1, jaw1, teeth1, lips1)) return;
bool awakeBull = IsAlligatorAwakeAtBar(1, true);
bool awakeBear = IsAlligatorAwakeAtBar(1, false);
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(ExtOrder.SelectByIndex(i))
{
if(ExtOrder.Symbol() == _Symbol && ExtOrder.Magic() == InpMagicNumber)
{
if(ExtOrder.OrderType() == ORDER_TYPE_BUY_STOP)
{
if(close1 < teeth1 || (InpUseAwakeFilter && InpDeleteOrderIfSleeping && !awakeBull))
ExtTrade.OrderDelete(ExtOrder.Ticket());
}
if(ExtOrder.OrderType() == ORDER_TYPE_SELL_STOP)
{
if(close1 > teeth1 || (InpUseAwakeFilter && InpDeleteOrderIfSleeping && !awakeBear))
ExtTrade.OrderDelete(ExtOrder.Ticket());
}
}
}
}
}
//+------------------------------------------------------------------+
//| Manage Open Positions & Trailing Stops |
//+------------------------------------------------------------------+
void ManageOpenPositions()
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double spread = ask - bid;
double stopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * _Point;
double trailDownFractal = GetConfirmedFractalLevel(false, InpTrailFractalRank);
double trailUpFractal = GetConfirmedFractalLevel(true, InpTrailFractalRank);
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(ExtPosition.SelectByIndex(i))
{
if(ExtPosition.Symbol() != _Symbol || ExtPosition.Magic() != InpMagicNumber)
continue;
ulong ticket = ExtPosition.Ticket();
double currentSL = ExtPosition.StopLoss();
double currentTP = ExtPosition.TakeProfit();
if(ExtPosition.PositionType() == POSITION_TYPE_BUY && trailDownFractal > 0.0)
{
double newSL = NormalizeDouble(trailDownFractal - (InpSLBufferPips * ExtPipValue), _Digits);
if(newSL > currentSL + _Point && (bid - newSL) > stopLevel)
{
ExtTrade.PositionModify(ticket, newSL, currentTP);
}
}
else if(ExtPosition.PositionType() == POSITION_TYPE_SELL && trailUpFractal > 0.0)
{
double newSL = NormalizeDouble(trailUpFractal + (InpSLBufferPips * ExtPipValue) + spread, _Digits);
if((currentSL == 0.0 || newSL < currentSL - _Point) && (newSL - ask) > stopLevel)
{
ExtTrade.PositionModify(ticket, newSL, currentTP);
}
}
}
}
}
//+------------------------------------------------------------------+
//| Helper Counts & Position Checks |
//+------------------------------------------------------------------+
int CountOpenPositions()
{
int count = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(ExtPosition.SelectByIndex(i))
{
if(ExtPosition.Symbol() == _Symbol && ExtPosition.Magic() == InpMagicNumber)
count++;
}
}
return(count);
}
bool HasOpenPositionDirection(ENUM_POSITION_TYPE posType, double fractalPrice)
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(ExtPosition.SelectByIndex(i))
{
if(ExtPosition.Symbol() == _Symbol &&
ExtPosition.Magic() == InpMagicNumber &&
ExtPosition.PositionType() == posType)
{
// Prevent duplicate entries for the exact same fractal level
if(MathAbs(ExtPosition.PriceOpen() - fractalPrice) < (_Point * 2.0))
return(true);
}
}
}
return(false);
}
//+------------------------------------------------------------------+
//| Dynamic Position Sizing |
//+------------------------------------------------------------------+
double CalculateLotSize(double slPriceDistance)
{
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
if(InpRiskPercent <= 0.0 || slPriceDistance <= 0.0)
return(MathMax(minLot, MathMin(maxLot, InpFixedLot)));
double riskMoney = AccountInfoDouble(ACCOUNT_BALANCE) * (InpRiskPercent / 100.0);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
if(tickSize <= 0.0 || tickValue <= 0.0) return(minLot);
double moneyRiskPerLot = (slPriceDistance / tickSize) * tickValue;
if(moneyRiskPerLot <= 0.0) return(minLot);
double rawLots = riskMoney / moneyRiskPerLot;
double steppedLots = MathFloor(rawLots / lotStep) * lotStep;
return(NormalizeDouble(MathMax(minLot, MathMin(maxLot, steppedLots)), 2));
}
//+------------------------------------------------------------------+Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.