1307 lines
94 KiB
MQL5
1307 lines
94 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| QuarterTheory_VIZION_FINAL_v5.1.mq5 |
|
|
//| Directional Re-Entry Filters + MFIB/Stoch Reject Pullbacks |
|
|
//| 150PT SL | 300PT TRAIL | 4000PT TP | RUNNERS PROTECTED |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "QuarterTheory x VIZION - Final v5.1"
|
|
#property version "5.10"
|
|
#property strict
|
|
|
|
#include <Trade/Trade.mqh>
|
|
CTrade Trade;
|
|
|
|
//================ INPUT PARAMETERS ==================//
|
|
input group "=== CORE SETTINGS ==="
|
|
input int MagicNumber = 456789;
|
|
input double Risk_Per_Trade = 1.2;
|
|
input int Max_Trades_Per_Setup = 10;
|
|
input int Max_Total_Trades = 100;
|
|
input int Min_Runners_To_Keep = 4;
|
|
|
|
input group "=== MARKET MODE SYSTEM ==="
|
|
input bool Use_Market_Mode_Filter = true;
|
|
input int Mode_Confirmation_Bars = 2;
|
|
input double Chop_ATR_Threshold = 0.5;
|
|
input double Range_Price_Threshold = 0.3;
|
|
|
|
input group "=== REVERSAL CONFIRMATION ==="
|
|
input bool Only_Close_On_Full_Reversal = true; // Protect runners from pullbacks
|
|
input int Reversal_Confirmation_Bars = 3; // Bars needed for full reversal
|
|
|
|
input group "=== MA SYSTEM ==="
|
|
input int MA_1 = 7;
|
|
input int MA_2 = 14;
|
|
input int MA_3 = 21;
|
|
input int MA_4 = 50;
|
|
input int MA_5 = 140;
|
|
input int MA_6 = 230;
|
|
input int MA_7 = 500;
|
|
input int MA_8 = 1000;
|
|
input int MA_9 = 1100;
|
|
input int MA_10 = 1300;
|
|
input int MA_Touch_Buffer = 100;
|
|
|
|
input group "=== STOCHASTIC ==="
|
|
input int Stoch_K_Period = 5;
|
|
input int Stoch_D_Period = 3;
|
|
input int Stoch_Slowing = 3;
|
|
input double Stoch_Extreme_High = 85.0; // Extreme overbought
|
|
input double Stoch_Extreme_Low = 15.0; // Extreme oversold
|
|
input double Stoch_High = 70.0;
|
|
input double Stoch_Low = 30.0;
|
|
|
|
input group "=== MFIB SYSTEM ==="
|
|
input int MFIB_Lookback = 500; // (kept for future expansion)
|
|
|
|
input group "=== FIBONACCI ==="
|
|
input int Lookback_Bars = 200;
|
|
input bool Show_Levels = true;
|
|
|
|
input group "=== TIGHT SL + AGGRESSIVE TRAIL SYSTEM ==="
|
|
input int Initial_SL_Points = 150; // Tight entry SL
|
|
input int BreakEven_Points = 300; // Move to BE at 300
|
|
input int Trailing_Distance_Points = 300; // Trail by 300
|
|
input int TP_Points = 4000; // Final TP at 4000
|
|
input int Partial_TP_Points = 900; // First partial at 900
|
|
input double Partial_TP_Percent = 33.0; // Close 33% at partial
|
|
|
|
//================ ENUMS ==================//
|
|
enum MODE_FAMILY
|
|
{
|
|
FAMILY_TRENDING = 0,
|
|
FAMILY_RANGING = 1,
|
|
FAMILY_CHOP = 2,
|
|
FAMILY_TRANSITIONAL = 3
|
|
};
|
|
|
|
enum TREND_BIAS
|
|
{
|
|
BIAS_NEUTRAL = 0,
|
|
BIAS_BULL = 1,
|
|
BIAS_BEAR = -1
|
|
};
|
|
|
|
enum TREND_STRENGTH
|
|
{
|
|
STRENGTH_WEAK = 0,
|
|
STRENGTH_CONFIRMED = 1,
|
|
STRENGTH_STRONG = 2
|
|
};
|
|
|
|
enum PRICE_STATE
|
|
{
|
|
STATE_CONTINUATION = 0, // Trend expansion / clean
|
|
STATE_PULLBACK = 1, // Counter-move but structure intact
|
|
STATE_DEEP_RETRACEMENT = 2, // Threatening structure / testing MA50+
|
|
STATE_REVERSAL_ATTEMPT = 3, // Structure break + failed reclaim attempt
|
|
STATE_REVERSAL_CONFIRMED = 4, // New bias confirmed
|
|
// Range states (mapped when FAMILY_RANGING)
|
|
STATE_RANGE_MID_DRIFT = 10,
|
|
STATE_RANGE_EDGE_TEST = 11,
|
|
STATE_RANGE_REJECTION = 12,
|
|
STATE_RANGE_BREAK_ATTEMPT = 13,
|
|
STATE_RANGE_BREAK_CONFIRMED = 14,
|
|
// Chop states (mapped when FAMILY_CHOP)
|
|
STATE_CHOP_NOISE = 20,
|
|
STATE_CHOP_VOL_SPIKE = 21,
|
|
STATE_CHOP_SQUEEZE = 22,
|
|
STATE_CHOP_FAKEOUT_LOOP = 23,
|
|
STATE_UNKNOWN = 99
|
|
};
|
|
|
|
enum SETUP_TYPE
|
|
{
|
|
SETUP_MA14_CROSS = 1,
|
|
SETUP_MA50_BOUNCE = 2,
|
|
SETUP_MA140_BOUNCE = 3,
|
|
SETUP_MA230_BOUNCE = 4,
|
|
SETUP_MA500_TOUCH = 5,
|
|
SETUP_FIB_BREAK = 6,
|
|
SETUP_FIB_RECLAIM = 7,
|
|
SETUP_FIB_REJECT = 8,
|
|
SETUP_MAGNET_WALK = 9,
|
|
SETUP_STAIRCASE_ADV = 10,
|
|
SETUP_CTRL_PULLBACK = 11,
|
|
SETUP_TF_RESET = 12,
|
|
SETUP_MFIB_LADDER = 13,
|
|
SETUP_MFIB_PRESS = 14,
|
|
SETUP_MEAN_REV = 15,
|
|
SETUP_RANGE_ENGINE = 16
|
|
};
|
|
|
|
//================ GLOBALS ==================//
|
|
int Stoch_Handle, ATR_Handle, ADX_Handle;
|
|
|
|
double Stoch_K_Current = 0, Stoch_K_Previous = 0, Stoch_K_Prev2 = 0;
|
|
double Stoch_D_Current = 0;
|
|
|
|
double Current_ATR = 0;
|
|
double Current_ADX = 0;
|
|
|
|
double PriceLevels[7];
|
|
|
|
int MA_Handles[10];
|
|
double MA_Current[10];
|
|
double MA_Previous[10];
|
|
double MA_Prev2[10];
|
|
|
|
double MFIB_High, MFIB_Low;
|
|
double MFIB_Level_236, MFIB_Level_382, MFIB_Level_050;
|
|
double MFIB_Level_618, MFIB_Level_786;
|
|
|
|
MODE_FAMILY Current_Family = FAMILY_TRANSITIONAL;
|
|
TREND_BIAS Current_Bias = BIAS_NEUTRAL;
|
|
TREND_STRENGTH Current_Strength = STRENGTH_WEAK;
|
|
PRICE_STATE Current_State = STATE_UNKNOWN;
|
|
|
|
MODE_FAMILY Prev_Family = FAMILY_TRANSITIONAL;
|
|
TREND_BIAS Prev_Bias = BIAS_NEUTRAL;
|
|
TREND_STRENGTH Prev_Strength = STRENGTH_WEAK;
|
|
|
|
int Mode_Confirmation_Count = 0;
|
|
int Reversal_Confirm_Counter = 0;
|
|
|
|
// Warning flags
|
|
bool MA7_Cross_14_Warning = false;
|
|
bool MA7_Cross_21_Warning = false;
|
|
bool MA50_Break_Warning = false;
|
|
|
|
bool Fib_Reject_Warning = false; // Classic fib reject
|
|
bool MFIB_Reject_Warning = false; // MFIB level reject (new)
|
|
|
|
bool MA_Reject_Warning = false;
|
|
bool Stoch_Extreme_Warning = false;
|
|
bool Stoch_Reject_Warning = false; // K/D reject at any level (new)
|
|
|
|
bool Pullback_Warning = false;
|
|
bool Retracement_Warning = false;
|
|
|
|
struct Position
|
|
{
|
|
ulong ticket;
|
|
double entry;
|
|
double original_lot;
|
|
bool is_buy;
|
|
bool partial_tp_hit;
|
|
bool be_set;
|
|
bool trailing_active;
|
|
bool is_runner;
|
|
SETUP_TYPE setup_type;
|
|
string setup_name;
|
|
MODE_FAMILY entry_family;
|
|
TREND_BIAS entry_bias;
|
|
TREND_STRENGTH entry_strength;
|
|
double distance_from_current;
|
|
};
|
|
Position OpenPositions[];
|
|
|
|
int TodayTrades = 0;
|
|
int BuyTrades = 0;
|
|
int SellTrades = 0;
|
|
int ClosedByReversal = 0;
|
|
|
|
int SetupCount[17];
|
|
datetime LastEntryTime[17];
|
|
|
|
//================ HELPERS ==================//
|
|
double MidPrice()
|
|
{
|
|
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
|
return (bid + ask) / 2.0;
|
|
}
|
|
|
|
bool NearLevel(const double price, const double level, const double tolPoints)
|
|
{
|
|
return (MathAbs(price - level) <= tolPoints * _Point);
|
|
}
|
|
|
|
bool StochCrossUp()
|
|
{
|
|
// K crosses above D on current bar vs previous bar (approx using K prev and current D current)
|
|
return (Stoch_K_Previous < Stoch_D_Current && Stoch_K_Current >= Stoch_D_Current);
|
|
}
|
|
|
|
bool StochCrossDown()
|
|
{
|
|
return (Stoch_K_Previous > Stoch_D_Current && Stoch_K_Current <= Stoch_D_Current);
|
|
}
|
|
|
|
bool IsBullBias() { return (Current_Bias == BIAS_BULL); }
|
|
bool IsBearBias() { return (Current_Bias == BIAS_BEAR); }
|
|
|
|
// Reclaim / reject MA50 logic for re-enabling trend-direction entries
|
|
bool Bull_MA50_Reclaimed()
|
|
{
|
|
// MA7 reclaims above MA50
|
|
bool reclaimed = (MA_Previous[0] <= MA_Previous[3]) && (MA_Current[0] > MA_Current[3]);
|
|
// or price rejects MA50 and closes above with stoch turning up
|
|
double low0 = iLow(_Symbol, PERIOD_CURRENT, 0);
|
|
double close0 = iClose(_Symbol, PERIOD_CURRENT, 0);
|
|
bool rejected = (low0 <= MA_Current[3] && close0 > MA_Current[3] && (StochCrossUp() || Stoch_K_Current > 50));
|
|
return (reclaimed || rejected);
|
|
}
|
|
|
|
bool Bear_MA50_Reclaimed()
|
|
{
|
|
bool reclaimed = (MA_Previous[0] >= MA_Previous[3]) && (MA_Current[0] < MA_Current[3]);
|
|
double high0 = iHigh(_Symbol, PERIOD_CURRENT, 0);
|
|
double close0 = iClose(_Symbol, PERIOD_CURRENT, 0);
|
|
bool rejected = (high0 >= MA_Current[3] && close0 < MA_Current[3] && (StochCrossDown() || Stoch_K_Current < 50));
|
|
return (reclaimed || rejected);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
Print("========================================");
|
|
Print("FINAL SYSTEM v5.1");
|
|
Print("Direction Filters: Trend entries paused during pullback/retracement");
|
|
Print("Pullback defs expanded: MA cross OR MFIB reject OR Stoch reject");
|
|
Print("========================================");
|
|
|
|
Trade.SetExpertMagicNumber(MagicNumber);
|
|
Trade.SetDeviationInPoints(50);
|
|
Trade.SetTypeFilling(ORDER_FILLING_FOK);
|
|
|
|
Stoch_Handle = iStochastic(_Symbol, PERIOD_CURRENT, Stoch_K_Period, Stoch_D_Period,
|
|
Stoch_Slowing, MODE_SMA, STO_LOWHIGH);
|
|
ATR_Handle = iATR(_Symbol, PERIOD_CURRENT, 14);
|
|
ADX_Handle = iADX(_Symbol, PERIOD_CURRENT, 14);
|
|
|
|
int periods[10] = {MA_1, MA_2, MA_3, MA_4, MA_5, MA_6, MA_7, MA_8, MA_9, MA_10};
|
|
for(int i=0; i<10; i++)
|
|
MA_Handles[i] = iMA(_Symbol, PERIOD_CURRENT, periods[i], 0, MODE_EMA, PRICE_CLOSE);
|
|
|
|
ArrayInitialize(SetupCount, 0);
|
|
ArrayInitialize(LastEntryTime, 0);
|
|
|
|
CalculateLevels();
|
|
if(Show_Levels) DrawLevels();
|
|
|
|
return INIT_SUCCEEDED;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
for(int i=0; i<10; i++)
|
|
if(MA_Handles[i] != INVALID_HANDLE)
|
|
IndicatorRelease(MA_Handles[i]);
|
|
|
|
if(Stoch_Handle != INVALID_HANDLE) IndicatorRelease(Stoch_Handle);
|
|
if(ATR_Handle != INVALID_HANDLE) IndicatorRelease(ATR_Handle);
|
|
if(ADX_Handle != INVALID_HANDLE) IndicatorRelease(ADX_Handle);
|
|
|
|
ObjectsDeleteAll(0, "Level_");
|
|
ObjectsDeleteAll(0, "ModeLabel");
|
|
ObjectsDeleteAll(0, "StateLabel");
|
|
ObjectsDeleteAll(0, "WarningLabel");
|
|
|
|
Print("Final: B:", BuyTrades, " | S:", SellTrades, " | Reversed:", ClosedByReversal);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void CalculateLevels()
|
|
{
|
|
double high = iHigh(_Symbol, PERIOD_CURRENT, 0);
|
|
double low = iLow(_Symbol, PERIOD_CURRENT, 0);
|
|
|
|
for(int i=1; i<=Lookback_Bars; i++)
|
|
{
|
|
double h = iHigh(_Symbol, PERIOD_CURRENT, i);
|
|
double l = iLow(_Symbol, PERIOD_CURRENT, i);
|
|
if(h > high) high = h;
|
|
if(l < low) low = l;
|
|
}
|
|
|
|
double range = high - low;
|
|
PriceLevels[0] = low;
|
|
PriceLevels[1] = low + range * 0.236;
|
|
PriceLevels[2] = low + range * 0.382;
|
|
PriceLevels[3] = low + range * 0.5;
|
|
PriceLevels[4] = low + range * 0.618;
|
|
PriceLevels[5] = low + range * 0.786;
|
|
PriceLevels[6] = high;
|
|
|
|
MFIB_High = high;
|
|
MFIB_Low = low;
|
|
MFIB_Level_236 = low + range * 0.236;
|
|
MFIB_Level_382 = low + range * 0.382;
|
|
MFIB_Level_050 = low + range * 0.5;
|
|
MFIB_Level_618 = low + range * 0.618;
|
|
MFIB_Level_786 = low + range * 0.786;
|
|
}
|
|
|
|
void DrawLevels()
|
|
{
|
|
ObjectsDeleteAll(0, "Level_");
|
|
color level_colors[7] = {clrRed, clrOrange, clrYellow, clrLime, clrCyan, clrBlue, clrMagenta};
|
|
|
|
for(int i=0; i<7; i++)
|
|
{
|
|
string name = "Level_" + IntegerToString(i);
|
|
ObjectCreate(0, name, OBJ_HLINE, 0, 0, PriceLevels[i]);
|
|
ObjectSetInteger(0, name, OBJPROP_COLOR, level_colors[i]);
|
|
ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
|
|
ObjectSetInteger(0, name, OBJPROP_WIDTH, 2);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void UpdateIndicators()
|
|
{
|
|
for(int i=0; i<10; i++)
|
|
{
|
|
double curr[1], prev[1], prev2[1];
|
|
if(CopyBuffer(MA_Handles[i], 0, 0, 1, curr) > 0) MA_Current[i] = curr[0];
|
|
if(CopyBuffer(MA_Handles[i], 0, 1, 1, prev) > 0) MA_Previous[i] = prev[0];
|
|
if(CopyBuffer(MA_Handles[i], 0, 2, 1, prev2) > 0) MA_Prev2[i] = prev2[0];
|
|
}
|
|
|
|
double k_curr[1], k_prev[1], k_prev2[1], d_curr[1];
|
|
if(CopyBuffer(Stoch_Handle, MAIN_LINE, 0, 1, k_curr) > 0) Stoch_K_Current = k_curr[0];
|
|
if(CopyBuffer(Stoch_Handle, MAIN_LINE, 1, 1, k_prev) > 0) Stoch_K_Previous = k_prev[0];
|
|
if(CopyBuffer(Stoch_Handle, MAIN_LINE, 2, 1, k_prev2) > 0) Stoch_K_Prev2 = k_prev2[0];
|
|
if(CopyBuffer(Stoch_Handle, SIGNAL_LINE, 0, 1, d_curr) > 0) Stoch_D_Current = d_curr[0];
|
|
|
|
double atr[1], adx[1];
|
|
if(CopyBuffer(ATR_Handle, 0, 0, 1, atr) > 0) Current_ATR = atr[0];
|
|
if(CopyBuffer(ADX_Handle, 0, 0, 1, adx) > 0) Current_ADX = adx[0];
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void DetectWarnings()
|
|
{
|
|
double current = MidPrice();
|
|
double bufferPts = (double)MA_Touch_Buffer;
|
|
|
|
// Reset warnings
|
|
MA7_Cross_14_Warning = false;
|
|
MA7_Cross_21_Warning = false;
|
|
MA50_Break_Warning = false;
|
|
|
|
Fib_Reject_Warning = false;
|
|
MFIB_Reject_Warning = false;
|
|
|
|
MA_Reject_Warning = false;
|
|
Stoch_Extreme_Warning = false;
|
|
Stoch_Reject_Warning = false;
|
|
|
|
Pullback_Warning = false;
|
|
Retracement_Warning = false;
|
|
|
|
// 1) STOCH extremes
|
|
if(Stoch_K_Current <= Stoch_Extreme_Low || Stoch_K_Current >= Stoch_Extreme_High)
|
|
Stoch_Extreme_Warning = true;
|
|
|
|
// 2) STOCH reject at ANY meaningful band (your request)
|
|
// - overbought reject: K crosses down while above 50 (stronger if above 70)
|
|
// - oversold reject: K crosses up while below 50 (stronger if below 30)
|
|
bool stoch_reject_down = (StochCrossDown() && Stoch_K_Current > 50);
|
|
bool stoch_reject_up = (StochCrossUp() && Stoch_K_Current < 50);
|
|
if(stoch_reject_down || stoch_reject_up)
|
|
Stoch_Reject_Warning = true;
|
|
|
|
// 3) MA7 x MA14 cross (early structure stress)
|
|
bool ma7_crossed_14_down = (MA_Previous[0] > MA_Previous[1]) && (MA_Current[0] <= MA_Current[1]);
|
|
bool ma7_crossed_14_up = (MA_Previous[0] < MA_Previous[1]) && (MA_Current[0] >= MA_Current[1]);
|
|
if(ma7_crossed_14_down || ma7_crossed_14_up)
|
|
{
|
|
MA7_Cross_14_Warning = true;
|
|
Retracement_Warning = true;
|
|
}
|
|
|
|
// 4) MA7 x MA21 cross (deeper)
|
|
bool ma7_crossed_21_down = (MA_Previous[0] > MA_Previous[2]) && (MA_Current[0] <= MA_Current[2]);
|
|
bool ma7_crossed_21_up = (MA_Previous[0] < MA_Previous[2]) && (MA_Current[0] >= MA_Current[2]);
|
|
if(ma7_crossed_21_down || ma7_crossed_21_up)
|
|
{
|
|
MA7_Cross_21_Warning = true;
|
|
Retracement_Warning = true;
|
|
}
|
|
|
|
// 5) MA50 break warning (reversal pressure)
|
|
bool ma7_broke_50_down = (MA_Previous[0] > MA_Previous[3]) && (MA_Current[0] <= MA_Current[3]);
|
|
bool ma7_broke_50_up = (MA_Previous[0] < MA_Previous[3]) && (MA_Current[0] >= MA_Current[3]);
|
|
if(ma7_broke_50_down || ma7_broke_50_up)
|
|
MA50_Break_Warning = true;
|
|
|
|
// 6) Classic fib reject if at fib + stoch extreme OR stoch reject
|
|
bool at_fib = false;
|
|
for(int i=1; i<6; i++)
|
|
{
|
|
if(NearLevel(current, PriceLevels[i], bufferPts))
|
|
{
|
|
at_fib = true;
|
|
break;
|
|
}
|
|
}
|
|
if(at_fib && (Stoch_Extreme_Warning || Stoch_Reject_Warning))
|
|
{
|
|
Fib_Reject_Warning = true;
|
|
Pullback_Warning = true;
|
|
}
|
|
|
|
// 7) MFIB reject (new): if at MFIB + stoch extreme OR stoch reject
|
|
bool at_mfib =
|
|
NearLevel(current, MFIB_Level_236, bufferPts) ||
|
|
NearLevel(current, MFIB_Level_382, bufferPts) ||
|
|
NearLevel(current, MFIB_Level_050, bufferPts) ||
|
|
NearLevel(current, MFIB_Level_618, bufferPts) ||
|
|
NearLevel(current, MFIB_Level_786, bufferPts);
|
|
|
|
if(at_mfib && (Stoch_Extreme_Warning || Stoch_Reject_Warning))
|
|
{
|
|
MFIB_Reject_Warning = true;
|
|
Pullback_Warning = true;
|
|
}
|
|
|
|
// 8) MA reject with stoch signal
|
|
bool at_ma50 = NearLevel(current, MA_Current[3], bufferPts);
|
|
bool at_ma140 = NearLevel(current, MA_Current[4], bufferPts);
|
|
bool at_ma230 = NearLevel(current, MA_Current[5], bufferPts);
|
|
if((at_ma50 || at_ma140 || at_ma230) && (Stoch_Extreme_Warning || Stoch_Reject_Warning))
|
|
{
|
|
MA_Reject_Warning = true;
|
|
Pullback_Warning = true;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
// Family/Bias/Strength detection (keeps your old logic but outputs
|
|
// clearer labels: Trending/Ranging/Chop + Weak/Confirmed/Strong + Bull/Bear)
|
|
void DetectFamilyBiasStrength(MODE_FAMILY &family, TREND_BIAS &bias, TREND_STRENGTH &strength)
|
|
{
|
|
double current = MidPrice();
|
|
|
|
bool mas_aligned_bull = (MA_Current[0] > MA_Current[1]) && (MA_Current[1] > MA_Current[2]) && (MA_Current[2] > MA_Current[3]);
|
|
bool mas_aligned_bear = (MA_Current[0] < MA_Current[1]) && (MA_Current[1] < MA_Current[2]) && (MA_Current[2] < MA_Current[3]);
|
|
|
|
// quick chop/range tests
|
|
double recent_high = iHigh(_Symbol, PERIOD_CURRENT, 0);
|
|
double recent_low = iLow(_Symbol, PERIOD_CURRENT, 0);
|
|
for(int i=1; i<20; i++)
|
|
{
|
|
recent_high = MathMax(recent_high, iHigh(_Symbol, PERIOD_CURRENT, i));
|
|
recent_low = MathMin(recent_low, iLow(_Symbol, PERIOD_CURRENT, i));
|
|
}
|
|
double range_size = (recent_high - recent_low) / current;
|
|
bool is_ranging = (range_size < Range_Price_Threshold);
|
|
|
|
bool ma_clustered = false;
|
|
if(Current_ATR > 0.0)
|
|
ma_clustered = ((MathAbs(MA_Current[0] - MA_Current[3]) / Current_ATR) < Chop_ATR_Threshold);
|
|
bool is_choppy = (ma_clustered || Current_ADX < 15);
|
|
|
|
// Decide bias first if trend exists
|
|
if(mas_aligned_bull)
|
|
bias = BIAS_BULL;
|
|
else if(mas_aligned_bear)
|
|
bias = BIAS_BEAR;
|
|
else
|
|
{
|
|
// softer bias using MA7 vs MA50 if not fully aligned
|
|
if(MA_Current[0] > MA_Current[3]) bias = BIAS_BULL;
|
|
else if(MA_Current[0] < MA_Current[3]) bias = BIAS_BEAR;
|
|
else bias = BIAS_NEUTRAL;
|
|
}
|
|
|
|
// Decide family
|
|
if(is_choppy && !(mas_aligned_bull || mas_aligned_bear))
|
|
family = FAMILY_CHOP;
|
|
else if(is_ranging && !(mas_aligned_bull || mas_aligned_bear))
|
|
family = FAMILY_RANGING;
|
|
else if(bias != BIAS_NEUTRAL)
|
|
family = FAMILY_TRENDING;
|
|
else
|
|
family = FAMILY_TRANSITIONAL;
|
|
|
|
// Decide strength (only meaningful in trending; still shown)
|
|
if(Current_ADX > 25 && (mas_aligned_bull || mas_aligned_bear))
|
|
strength = STRENGTH_STRONG;
|
|
else if(Current_ADX > 18 && (mas_aligned_bull || mas_aligned_bear))
|
|
strength = STRENGTH_CONFIRMED;
|
|
else
|
|
strength = STRENGTH_WEAK;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
// State logic: expanded pullback defs = MA stress OR MFIB reject OR Stoch reject.
|
|
// Direction rule you requested:
|
|
// - In bull trend pullback/retracement: ONLY SELL entries until MA50 reclaim/reject-up.
|
|
// - In bear trend pullback/retracement: ONLY BUY entries until MA50 reclaim/reject-down.
|
|
PRICE_STATE DetermineStateFromContext()
|
|
{
|
|
// If not trending, map to range/chop sub-states
|
|
if(Current_Family == FAMILY_RANGING)
|
|
{
|
|
double current = MidPrice();
|
|
double bufferPts = (double)MA_Touch_Buffer;
|
|
|
|
bool near_low = NearLevel(current, PriceLevels[1], bufferPts) || NearLevel(current, PriceLevels[0], bufferPts);
|
|
bool near_high = NearLevel(current, PriceLevels[5], bufferPts) || NearLevel(current, PriceLevels[6], bufferPts);
|
|
|
|
if(near_low || near_high) return STATE_RANGE_EDGE_TEST;
|
|
|
|
// mid drift default
|
|
if(Stoch_Reject_Warning || Stoch_Extreme_Warning) return STATE_RANGE_REJECTION;
|
|
|
|
// break attempt / confirmed using close beyond outer levels
|
|
double close0 = iClose(_Symbol, PERIOD_CURRENT, 0);
|
|
if(close0 > PriceLevels[6] || close0 < PriceLevels[0]) return STATE_RANGE_BREAK_CONFIRMED;
|
|
if(NearLevel(current, PriceLevels[6], bufferPts) || NearLevel(current, PriceLevels[0], bufferPts)) return STATE_RANGE_BREAK_ATTEMPT;
|
|
|
|
return STATE_RANGE_MID_DRIFT;
|
|
}
|
|
|
|
if(Current_Family == FAMILY_CHOP)
|
|
{
|
|
// simple chop phase mapping using ATR/ADX + stoch flips
|
|
if(Current_ADX < 12 && !Stoch_Reject_Warning) return STATE_CHOP_NOISE;
|
|
if(Current_ATR > 0 && (MathAbs(iClose(_Symbol, PERIOD_CURRENT, 0) - iOpen(_Symbol, PERIOD_CURRENT, 0)) / Current_ATR) > 1.2)
|
|
return STATE_CHOP_VOL_SPIKE;
|
|
if(Current_ADX < 15 && Current_ATR > 0 && (MathAbs(MA_Current[0] - MA_Current[3]) / Current_ATR) < 0.25)
|
|
return STATE_CHOP_SQUEEZE;
|
|
if(Stoch_Reject_Warning) return STATE_CHOP_FAKEOUT_LOOP;
|
|
return STATE_CHOP_NOISE;
|
|
}
|
|
|
|
// Trending / Transitional: use your structure ideas but cleaner naming
|
|
bool ma7_above_14 = (MA_Current[0] > MA_Current[1]);
|
|
bool ma7_above_21 = (MA_Current[0] > MA_Current[2]);
|
|
bool ma7_above_50 = (MA_Current[0] > MA_Current[3]);
|
|
bool ma14_above_50 = (MA_Current[1] > MA_Current[3]);
|
|
bool ma21_above_50 = (MA_Current[2] > MA_Current[3]);
|
|
|
|
bool ma7_below_14 = (MA_Current[0] < MA_Current[1]);
|
|
bool ma7_below_21 = (MA_Current[0] < MA_Current[2]);
|
|
bool ma7_below_50 = (MA_Current[0] < MA_Current[3]);
|
|
bool ma14_below_50 = (MA_Current[1] < MA_Current[3]);
|
|
bool ma21_below_50 = (MA_Current[2] < MA_Current[3]);
|
|
|
|
bool pullbackSignal = (Pullback_Warning || MFIB_Reject_Warning || Fib_Reject_Warning || Stoch_Reject_Warning);
|
|
bool retraceSignal = (MA7_Cross_14_Warning || MA7_Cross_21_Warning);
|
|
|
|
if(IsBullBias())
|
|
{
|
|
// confirmed reversal is when structure fully flips below 50s
|
|
if(ma7_below_50 && !ma14_above_50 && !ma21_above_50) return STATE_REVERSAL_CONFIRMED;
|
|
|
|
// reversal attempt: MA50 pressure/break warning + failed reclaim vibe
|
|
if(MA50_Break_Warning && (retraceSignal || Stoch_Extreme_Warning || Stoch_Reject_Warning))
|
|
return STATE_REVERSAL_ATTEMPT;
|
|
|
|
// deep retracement = MA7 not above 14/21 or testing MA50 and you have pullback signals
|
|
if((!ma7_above_14 || !ma7_above_21 || !ma7_above_50) && (pullbackSignal || retraceSignal))
|
|
return STATE_DEEP_RETRACEMENT;
|
|
|
|
// pullback = structure mostly intact (14/21 above 50) and we have *any* reject signal
|
|
if(ma14_above_50 && ma21_above_50 && pullbackSignal)
|
|
return STATE_PULLBACK;
|
|
|
|
return STATE_CONTINUATION;
|
|
}
|
|
|
|
if(IsBearBias())
|
|
{
|
|
if(ma7_above_50 && !ma14_below_50 && !ma21_below_50) return STATE_REVERSAL_CONFIRMED;
|
|
|
|
if(MA50_Break_Warning && (retraceSignal || Stoch_Extreme_Warning || Stoch_Reject_Warning))
|
|
return STATE_REVERSAL_ATTEMPT;
|
|
|
|
if((!ma7_below_14 || !ma7_below_21 || !ma7_below_50) && (pullbackSignal || retraceSignal))
|
|
return STATE_DEEP_RETRACEMENT;
|
|
|
|
if(ma14_below_50 && ma21_below_50 && pullbackSignal)
|
|
return STATE_PULLBACK;
|
|
|
|
return STATE_CONTINUATION;
|
|
}
|
|
|
|
return STATE_UNKNOWN;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void UpdateModeAndState()
|
|
{
|
|
// detect new family/bias/strength
|
|
MODE_FAMILY detected_family;
|
|
TREND_BIAS detected_bias;
|
|
TREND_STRENGTH detected_strength;
|
|
DetectFamilyBiasStrength(detected_family, detected_bias, detected_strength);
|
|
|
|
// set state using current detected context
|
|
MODE_FAMILY tmpFamily = detected_family;
|
|
TREND_BIAS tmpBias = detected_bias;
|
|
TREND_STRENGTH tmpStrength = detected_strength;
|
|
|
|
// temporarily apply to compute state consistently
|
|
Current_Family = tmpFamily;
|
|
Current_Bias = tmpBias;
|
|
Current_Strength = tmpStrength;
|
|
PRICE_STATE detected_state = DetermineStateFromContext();
|
|
|
|
// Confirmation logic (avoid flapping)
|
|
bool changed = (detected_family != Prev_Family) || (detected_bias != Prev_Bias) || (detected_strength != Prev_Strength);
|
|
|
|
if(changed)
|
|
{
|
|
// quick flip if reversal confirmed
|
|
int required = Mode_Confirmation_Bars;
|
|
if(detected_state == STATE_REVERSAL_CONFIRMED) required = 1;
|
|
|
|
if(detected_family == Prev_Family && detected_bias == Prev_Bias && detected_strength == Prev_Strength)
|
|
{
|
|
Mode_Confirmation_Count++;
|
|
if(Mode_Confirmation_Count >= required)
|
|
{
|
|
Prev_Family = Current_Family;
|
|
Prev_Bias = Current_Bias;
|
|
Prev_Strength = Current_Strength;
|
|
|
|
Current_Family = detected_family;
|
|
Current_Bias = detected_bias;
|
|
Current_Strength = detected_strength;
|
|
Current_State = detected_state;
|
|
|
|
Mode_Confirmation_Count = 0;
|
|
Reversal_Confirm_Counter = 0;
|
|
|
|
// Close on confirmed reversal only
|
|
if(Current_State == STATE_REVERSAL_CONFIRMED)
|
|
CloseOnFullReversal();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Prev_Family = detected_family;
|
|
Prev_Bias = detected_bias;
|
|
Prev_Strength = detected_strength;
|
|
Mode_Confirmation_Count = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Mode_Confirmation_Count = 0;
|
|
Current_Family = detected_family;
|
|
Current_Bias = detected_bias;
|
|
Current_Strength = detected_strength;
|
|
Current_State = detected_state;
|
|
}
|
|
|
|
UpdateLabels();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
string FamilyToText(MODE_FAMILY f)
|
|
{
|
|
switch(f)
|
|
{
|
|
case FAMILY_TRENDING: return "Trending";
|
|
case FAMILY_RANGING: return "Ranging";
|
|
case FAMILY_CHOP: return "Chop";
|
|
case FAMILY_TRANSITIONAL: return "Transitional";
|
|
}
|
|
return "Unknown";
|
|
}
|
|
|
|
string BiasToText(TREND_BIAS b)
|
|
{
|
|
if(b == BIAS_BULL) return "Bull";
|
|
if(b == BIAS_BEAR) return "Bear";
|
|
return "Neutral";
|
|
}
|
|
|
|
string StrengthToText(TREND_STRENGTH s)
|
|
{
|
|
switch(s)
|
|
{
|
|
case STRENGTH_WEAK: return "Weak";
|
|
case STRENGTH_CONFIRMED: return "Confirmed";
|
|
case STRENGTH_STRONG: return "Strong";
|
|
}
|
|
return "Weak";
|
|
}
|
|
|
|
string StateToText(PRICE_STATE s)
|
|
{
|
|
switch(s)
|
|
{
|
|
case STATE_CONTINUATION: return "Continuation";
|
|
case STATE_PULLBACK: return "Pullback";
|
|
case STATE_DEEP_RETRACEMENT: return "Deep Retracement";
|
|
case STATE_REVERSAL_ATTEMPT: return "Reversal Attempt";
|
|
case STATE_REVERSAL_CONFIRMED: return "Reversal Confirmed";
|
|
|
|
case STATE_RANGE_MID_DRIFT: return "Range: Mid Drift";
|
|
case STATE_RANGE_EDGE_TEST: return "Range: Edge Test";
|
|
case STATE_RANGE_REJECTION: return "Range: Rejection";
|
|
case STATE_RANGE_BREAK_ATTEMPT: return "Range: Break Attempt";
|
|
case STATE_RANGE_BREAK_CONFIRMED: return "Range: Break Confirmed";
|
|
|
|
case STATE_CHOP_NOISE: return "Chop: Noise";
|
|
case STATE_CHOP_VOL_SPIKE: return "Chop: Vol Spike";
|
|
case STATE_CHOP_SQUEEZE: return "Chop: Squeeze";
|
|
case STATE_CHOP_FAKEOUT_LOOP: return "Chop: Fakeout Loop";
|
|
|
|
default: return "Unknown";
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void UpdateLabels()
|
|
{
|
|
// MODE LABEL (top-left)
|
|
ObjectDelete(0, "ModeLabel");
|
|
ObjectCreate(0, "ModeLabel", OBJ_LABEL, 0, 0, 0);
|
|
ObjectSetInteger(0, "ModeLabel", OBJPROP_CORNER, CORNER_LEFT_UPPER);
|
|
ObjectSetInteger(0, "ModeLabel", OBJPROP_XDISTANCE, 10);
|
|
ObjectSetInteger(0, "ModeLabel", OBJPROP_YDISTANCE, 20);
|
|
|
|
string mode_text = "MODE: " + FamilyToText(Current_Family);
|
|
if(Current_Family == FAMILY_TRENDING)
|
|
mode_text += " | " + StrengthToText(Current_Strength) + " " + BiasToText(Current_Bias);
|
|
|
|
color mode_color = clrYellow;
|
|
if(Current_Family == FAMILY_TRENDING)
|
|
{
|
|
if(Current_Bias == BIAS_BULL) mode_color = (Current_Strength == STRENGTH_STRONG ? clrLime : (Current_Strength == STRENGTH_CONFIRMED ? clrGreenYellow : clrGreen));
|
|
if(Current_Bias == BIAS_BEAR) mode_color = (Current_Strength == STRENGTH_STRONG ? clrRed : (Current_Strength == STRENGTH_CONFIRMED ? clrOrangeRed : clrOrange));
|
|
}
|
|
else if(Current_Family == FAMILY_RANGING) mode_color = clrAqua;
|
|
else if(Current_Family == FAMILY_CHOP) mode_color = clrViolet;
|
|
|
|
ObjectSetString(0, "ModeLabel", OBJPROP_TEXT, mode_text);
|
|
ObjectSetInteger(0, "ModeLabel", OBJPROP_COLOR, mode_color);
|
|
ObjectSetInteger(0, "ModeLabel", OBJPROP_FONTSIZE, 12);
|
|
|
|
// STATE LABEL
|
|
ObjectDelete(0, "StateLabel");
|
|
ObjectCreate(0, "StateLabel", OBJ_LABEL, 0, 0, 0);
|
|
ObjectSetInteger(0, "StateLabel", OBJPROP_CORNER, CORNER_LEFT_UPPER);
|
|
ObjectSetInteger(0, "StateLabel", OBJPROP_XDISTANCE, 10);
|
|
ObjectSetInteger(0, "StateLabel", OBJPROP_YDISTANCE, 40);
|
|
|
|
string state_text = "STATE: " + StateToText(Current_State);
|
|
|
|
// add action guidance (buys-only/sells-only) for your key trend pullback logic
|
|
if(Current_Family == FAMILY_TRENDING && (Current_State == STATE_PULLBACK || Current_State == STATE_DEEP_RETRACEMENT))
|
|
{
|
|
if(IsBullBias() && !Bull_MA50_Reclaimed())
|
|
state_text += " | ACTION: SELLS-ONLY (until MA50 reclaim/reject-up)";
|
|
else if(IsBearBias() && !Bear_MA50_Reclaimed())
|
|
state_text += " | ACTION: BUYS-ONLY (until MA50 reclaim/reject-down)";
|
|
else
|
|
state_text += " | ACTION: TREND-OK";
|
|
}
|
|
|
|
color state_color = clrWhite;
|
|
if(Current_State == STATE_CONTINUATION) state_color = clrLime;
|
|
else if(Current_State == STATE_PULLBACK) state_color = clrYellow;
|
|
else if(Current_State == STATE_DEEP_RETRACEMENT) state_color = clrOrange;
|
|
else if(Current_State == STATE_REVERSAL_ATTEMPT) state_color = clrOrangeRed;
|
|
else if(Current_State == STATE_REVERSAL_CONFIRMED) state_color = clrRed;
|
|
else if(Current_Family == FAMILY_RANGING) state_color = clrAqua;
|
|
else if(Current_Family == FAMILY_CHOP) state_color = clrViolet;
|
|
|
|
ObjectSetString(0, "StateLabel", OBJPROP_TEXT, state_text);
|
|
ObjectSetInteger(0, "StateLabel", OBJPROP_COLOR, state_color);
|
|
ObjectSetInteger(0, "StateLabel", OBJPROP_FONTSIZE, 11);
|
|
|
|
// WARNING LABEL
|
|
bool anyWarn =
|
|
(Stoch_Extreme_Warning || Stoch_Reject_Warning ||
|
|
MA7_Cross_14_Warning || MA7_Cross_21_Warning ||
|
|
MA50_Break_Warning || Fib_Reject_Warning || MFIB_Reject_Warning ||
|
|
MA_Reject_Warning);
|
|
|
|
if(anyWarn)
|
|
{
|
|
ObjectDelete(0, "WarningLabel");
|
|
ObjectCreate(0, "WarningLabel", OBJ_LABEL, 0, 0, 0);
|
|
ObjectSetInteger(0, "WarningLabel", OBJPROP_CORNER, CORNER_LEFT_UPPER);
|
|
ObjectSetInteger(0, "WarningLabel", OBJPROP_XDISTANCE, 10);
|
|
ObjectSetInteger(0, "WarningLabel", OBJPROP_YDISTANCE, 60);
|
|
|
|
string warn_text = "WARN: ";
|
|
if(Stoch_Extreme_Warning) warn_text += "STOCH-EXT | ";
|
|
if(Stoch_Reject_Warning) warn_text += "STOCH-REJECT | ";
|
|
if(MA7_Cross_14_Warning) warn_text += "MA7x14 | ";
|
|
if(MA7_Cross_21_Warning) warn_text += "MA7x21 | ";
|
|
if(MA50_Break_Warning) warn_text += "MA50-BREAK | ";
|
|
if(Fib_Reject_Warning) warn_text += "FIB-REJECT | ";
|
|
if(MFIB_Reject_Warning) warn_text += "MFIB-REJECT | ";
|
|
if(MA_Reject_Warning) warn_text += "MA-REJECT";
|
|
|
|
ObjectSetString(0, "WarningLabel", OBJPROP_TEXT, warn_text);
|
|
ObjectSetInteger(0, "WarningLabel", OBJPROP_COLOR, clrRed);
|
|
ObjectSetInteger(0, "WarningLabel", OBJPROP_FONTSIZE, 10);
|
|
}
|
|
else
|
|
{
|
|
ObjectDelete(0, "WarningLabel");
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void MarkRunners()
|
|
{
|
|
double current = MidPrice();
|
|
|
|
for(int i=0; i<ArraySize(OpenPositions); i++)
|
|
{
|
|
if(PositionSelectByTicket(OpenPositions[i].ticket))
|
|
{
|
|
double entry = OpenPositions[i].entry;
|
|
OpenPositions[i].distance_from_current = MathAbs(current - entry) / _Point;
|
|
}
|
|
}
|
|
|
|
for(int i=0; i<ArraySize(OpenPositions)-1; i++)
|
|
{
|
|
for(int j=i+1; j<ArraySize(OpenPositions); j++)
|
|
{
|
|
if(OpenPositions[j].distance_from_current > OpenPositions[i].distance_from_current)
|
|
{
|
|
Position temp = OpenPositions[i];
|
|
OpenPositions[i] = OpenPositions[j];
|
|
OpenPositions[j] = temp;
|
|
}
|
|
}
|
|
}
|
|
|
|
for(int i=0; i<ArraySize(OpenPositions); i++)
|
|
OpenPositions[i].is_runner = (i < Min_Runners_To_Keep);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void CloseOnFullReversal()
|
|
{
|
|
if(!Only_Close_On_Full_Reversal) return;
|
|
|
|
MarkRunners();
|
|
Print("🔄 REVERSAL CONFIRMED - Closing opposing positions (protecting runners)");
|
|
|
|
for(int i=PositionsTotal()-1; i>=0; i--)
|
|
{
|
|
if(PositionGetTicket(i) == 0) continue;
|
|
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
|
|
if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
|
|
|
|
ulong ticket = PositionGetTicket(i);
|
|
bool is_buy = (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY);
|
|
|
|
int idx = -1;
|
|
for(int j=0; j<ArraySize(OpenPositions); j++)
|
|
{
|
|
if(OpenPositions[j].ticket == ticket) { idx = j; break; }
|
|
}
|
|
|
|
if(idx >= 0 && OpenPositions[idx].is_runner)
|
|
{
|
|
Print("🏃 RUNNER PROTECTED: ", OpenPositions[idx].setup_name, " | Dist: ", (int)OpenPositions[idx].distance_from_current);
|
|
continue;
|
|
}
|
|
|
|
bool should_close = false;
|
|
|
|
if(is_buy && IsBearBias()) should_close = true;
|
|
if(!is_buy && IsBullBias()) should_close = true;
|
|
|
|
if(should_close)
|
|
{
|
|
if(Trade.PositionClose(ticket))
|
|
{
|
|
ClosedByReversal++;
|
|
Print("✂ REVERSAL CLOSE: ", is_buy ? "BUY" : "SELL");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
// Direction filter core:
|
|
// - Strong/confirmed/weak trend: allow trend direction in continuation.
|
|
// - During pullback/deep retracement: ONLY countertrend until MA50 reclaim/reject.
|
|
// - Range/chop: allow both but keep range-engine preference via your setup logic.
|
|
bool IsDirectionAllowed(bool is_buy)
|
|
{
|
|
if(!Use_Market_Mode_Filter) return true;
|
|
|
|
// Trending family directional rules
|
|
if(Current_Family == FAMILY_TRENDING)
|
|
{
|
|
// Reversal confirmed: trade ONLY new bias direction
|
|
if(Current_State == STATE_REVERSAL_CONFIRMED)
|
|
{
|
|
if(IsBullBias() && !is_buy) return false;
|
|
if(IsBearBias() && is_buy) return false;
|
|
return true;
|
|
}
|
|
|
|
// Pullback / deep retracement: countertrend-only until MA50 reclaim/reject
|
|
if(Current_State == STATE_PULLBACK || Current_State == STATE_DEEP_RETRACEMENT)
|
|
{
|
|
if(IsBullBias())
|
|
{
|
|
if(!Bull_MA50_Reclaimed())
|
|
return (!is_buy); // SELLS ONLY
|
|
// once reclaimed/rejected-up, allow trend direction again
|
|
return is_buy;
|
|
}
|
|
|
|
if(IsBearBias())
|
|
{
|
|
if(!Bear_MA50_Reclaimed())
|
|
return (is_buy); // BUYS ONLY
|
|
return (!is_buy);
|
|
}
|
|
}
|
|
|
|
// Continuation: trend direction only (aggressive)
|
|
if(Current_State == STATE_CONTINUATION)
|
|
{
|
|
if(IsBullBias() && !is_buy) return false;
|
|
if(IsBearBias() && is_buy) return false;
|
|
return true;
|
|
}
|
|
|
|
// Reversal attempt: still allow both, but you can tighten here later
|
|
return true;
|
|
}
|
|
|
|
// Ranging / Chop / Transitional: allow both (your setup blocks decide)
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
int CountSetupTrades(SETUP_TYPE setup)
|
|
{
|
|
int count = 0;
|
|
for(int i=0; i<PositionsTotal(); i++)
|
|
{
|
|
if(PositionGetTicket(i) == 0) continue;
|
|
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
|
|
if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
|
|
|
|
ulong t = PositionGetTicket(i);
|
|
|
|
for(int j=0; j<ArraySize(OpenPositions); j++)
|
|
{
|
|
if(OpenPositions[j].ticket == t)
|
|
{
|
|
if(OpenPositions[j].setup_type == setup)
|
|
count++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
double GetLotSize()
|
|
{
|
|
double risk = AccountInfoDouble(ACCOUNT_BALANCE) * Risk_Per_Trade / 100.0;
|
|
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
|
|
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
|
|
|
|
double lot = risk / ((Initial_SL_Points * _Point / tickSize) * tickValue);
|
|
|
|
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
|
|
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
|
|
double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
|
|
|
|
lot = MathMax(lot, minLot);
|
|
lot = MathMin(lot, maxLot);
|
|
lot = NormalizeDouble(lot / step, 0) * step;
|
|
|
|
return lot;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void OpenTrade(bool buy, double price, string setup_name, SETUP_TYPE setup_type)
|
|
{
|
|
if(!IsDirectionAllowed(buy)) return;
|
|
|
|
if(CountSetupTrades(setup_type) >= Max_Trades_Per_Setup) return;
|
|
if(PositionsTotal() >= Max_Total_Trades) return;
|
|
|
|
datetime now = TimeCurrent();
|
|
if(now - LastEntryTime[setup_type] < 1) return;
|
|
LastEntryTime[setup_type] = now;
|
|
|
|
double lot = GetLotSize();
|
|
if(lot < SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN)) return;
|
|
|
|
double sl = buy ? price - Initial_SL_Points * _Point : price + Initial_SL_Points * _Point;
|
|
double tp = buy ? price + TP_Points * _Point : price - TP_Points * _Point;
|
|
|
|
string comment = setup_name + "|" + FamilyToText(Current_Family) + "|" + BiasToText(Current_Bias) + "|" + StrengthToText(Current_Strength) + "|" + StateToText(Current_State);
|
|
|
|
bool result = false;
|
|
if(buy) result = Trade.Buy(lot, _Symbol, 0, sl, tp, comment);
|
|
else result = Trade.Sell(lot, _Symbol, 0, sl, tp, comment);
|
|
|
|
if(result)
|
|
{
|
|
TodayTrades++;
|
|
if(buy) BuyTrades++; else SellTrades++;
|
|
SetupCount[setup_type]++;
|
|
|
|
ulong ticket = Trade.ResultOrder();
|
|
|
|
int size = ArraySize(OpenPositions);
|
|
ArrayResize(OpenPositions, size+1);
|
|
OpenPositions[size].ticket = ticket;
|
|
OpenPositions[size].entry = price;
|
|
OpenPositions[size].original_lot = lot;
|
|
OpenPositions[size].is_buy = buy;
|
|
OpenPositions[size].partial_tp_hit = false;
|
|
OpenPositions[size].be_set = false;
|
|
OpenPositions[size].trailing_active = false;
|
|
OpenPositions[size].is_runner = false;
|
|
OpenPositions[size].setup_type = setup_type;
|
|
OpenPositions[size].setup_name = setup_name;
|
|
OpenPositions[size].entry_family = Current_Family;
|
|
OpenPositions[size].entry_bias = Current_Bias;
|
|
OpenPositions[size].entry_strength = Current_Strength;
|
|
OpenPositions[size].distance_from_current = 0;
|
|
|
|
Print("✓ ", setup_name, " ", buy ? "BUY" : "SELL", " | ", BiasToText(Current_Bias), " | ", StateToText(Current_State));
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void ManagePositions()
|
|
{
|
|
for(int i=PositionsTotal()-1; i>=0; i--)
|
|
{
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(ticket == 0) continue;
|
|
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
|
|
if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
|
|
|
|
int idx = -1;
|
|
for(int j=0; j<ArraySize(OpenPositions); j++)
|
|
{
|
|
if(OpenPositions[j].ticket == ticket) { idx = j; break; }
|
|
}
|
|
if(idx == -1) continue;
|
|
|
|
double entry = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
double current_sl = PositionGetDouble(POSITION_SL);
|
|
double current_tp = PositionGetDouble(POSITION_TP);
|
|
bool is_buy = OpenPositions[idx].is_buy;
|
|
|
|
double current = is_buy ? SymbolInfoDouble(_Symbol, SYMBOL_BID) : SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
|
double profit_points = is_buy ? (current - entry) / _Point : (entry - current) / _Point;
|
|
|
|
// Partial TP
|
|
if(!OpenPositions[idx].partial_tp_hit && profit_points >= Partial_TP_Points)
|
|
{
|
|
double current_lot = PositionGetDouble(POSITION_VOLUME);
|
|
double close_size = NormalizeDouble(OpenPositions[idx].original_lot * Partial_TP_Percent / 100.0, 2);
|
|
|
|
if(close_size > 0 && close_size <= current_lot)
|
|
{
|
|
if(Trade.PositionClosePartial(ticket, close_size))
|
|
OpenPositions[idx].partial_tp_hit = true;
|
|
}
|
|
}
|
|
|
|
// Break Even
|
|
if(!OpenPositions[idx].be_set && profit_points >= BreakEven_Points)
|
|
{
|
|
if((is_buy && current_sl < entry) || (!is_buy && current_sl > entry))
|
|
{
|
|
if(Trade.PositionModify(ticket, entry, current_tp))
|
|
OpenPositions[idx].be_set = true;
|
|
}
|
|
}
|
|
|
|
// Trailing
|
|
if(profit_points >= BreakEven_Points + 100)
|
|
{
|
|
OpenPositions[idx].trailing_active = true;
|
|
|
|
double newSL = 0;
|
|
bool should_modify = false;
|
|
|
|
if(is_buy)
|
|
{
|
|
newSL = current - Trailing_Distance_Points * _Point;
|
|
if(newSL > current_sl + 30 * _Point) should_modify = true;
|
|
}
|
|
else
|
|
{
|
|
newSL = current + Trailing_Distance_Points * _Point;
|
|
if(newSL < current_sl - 30 * _Point) should_modify = true;
|
|
}
|
|
|
|
if(should_modify) Trade.PositionModify(ticket, newSL, current_tp);
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void OnTick()
|
|
{
|
|
UpdateIndicators();
|
|
DetectWarnings();
|
|
|
|
// periodic level refresh
|
|
static int tick_count = 0;
|
|
tick_count++;
|
|
if(tick_count >= 100)
|
|
{
|
|
CalculateLevels();
|
|
tick_count = 0;
|
|
}
|
|
|
|
// update mode/state/labels
|
|
UpdateModeAndState();
|
|
|
|
// manage open trades
|
|
ManagePositions();
|
|
|
|
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
|
double current = (bid + ask) / 2.0;
|
|
double previous = iClose(_Symbol, PERIOD_CURRENT, 1);
|
|
double buffer = MA_Touch_Buffer * _Point;
|
|
|
|
//=================================================================
|
|
// SETUPS (unchanged core triggers) — now filtered mainly by direction
|
|
//=================================================================
|
|
|
|
// 1) Pullback/Deep Retracement behavior (your new rule: opposite entries only)
|
|
if(Current_Family == FAMILY_TRENDING && (Current_State == STATE_PULLBACK || Current_State == STATE_DEEP_RETRACEMENT))
|
|
{
|
|
bool bull = IsBullBias();
|
|
bool bear = IsBearBias();
|
|
|
|
// while bull pullback: SELLS ONLY until MA50 reclaim/reject-up
|
|
// while bear pullback: BUYS ONLY until MA50 reclaim/reject-down
|
|
bool allowTrendAgain = (bull && Bull_MA50_Reclaimed()) || (bear && Bear_MA50_Reclaimed());
|
|
|
|
// Countertrend mean-reversion entries
|
|
if(!allowTrendAgain)
|
|
{
|
|
// Stoch-based reversion (any reject OR extremes)
|
|
if(Stoch_Extreme_Warning || Stoch_Reject_Warning)
|
|
{
|
|
if(bull)
|
|
{
|
|
// bull pullback => open sells (countertrend)
|
|
if(Stoch_K_Current >= Stoch_Low) // keep it aggressive; you can tighten later
|
|
OpenTrade(false, bid, "PB-COUNTER-SELL", SETUP_MEAN_REV);
|
|
}
|
|
if(bear)
|
|
{
|
|
// bear pullback => open buys (countertrend)
|
|
if(Stoch_K_Current <= Stoch_High)
|
|
OpenTrade(true, ask, "PB-COUNTER-BUY", SETUP_MEAN_REV);
|
|
}
|
|
}
|
|
|
|
// Fib/MFIB reject confirmations (new)
|
|
if(Fib_Reject_Warning || MFIB_Reject_Warning)
|
|
{
|
|
if(bull) OpenTrade(false, bid, (MFIB_Reject_Warning ? "MFIB-REJECT-SELL" : "FIB-REJECT-SELL"), SETUP_FIB_REJECT);
|
|
if(bear) OpenTrade(true, ask, (MFIB_Reject_Warning ? "MFIB-REJECT-BUY" : "FIB-REJECT-BUY"), SETUP_FIB_REJECT);
|
|
}
|
|
|
|
// MA reject at key levels with stoch reject/extreme
|
|
if(MA_Reject_Warning)
|
|
{
|
|
if(bull) OpenTrade(false, bid, "MA-REJECT-SELL", SETUP_CTRL_PULLBACK);
|
|
if(bear) OpenTrade(true, ask, "MA-REJECT-BUY", SETUP_CTRL_PULLBACK);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// MA50 reclaimed/rejected back in favor => allow trend direction entries again immediately
|
|
if(bull) OpenTrade(true, ask, "MA50-RECLAIM-BUY", SETUP_MA50_BOUNCE);
|
|
if(bear) OpenTrade(false, bid, "MA50-RECLAIM-SELL", SETUP_MA50_BOUNCE);
|
|
}
|
|
}
|
|
|
|
// 2) Normal trend continuation setups
|
|
if(Current_Family == FAMILY_TRENDING && Current_State == STATE_CONTINUATION)
|
|
{
|
|
// MA14 CROSS (kept)
|
|
int ma14_crosses = 0;
|
|
for(int i=0; i<10; i++) if(i != 1 && MA_Current[1] > MA_Current[i]) ma14_crosses++;
|
|
if(ma14_crosses >= 2) OpenTrade(true, ask, "MA14-CROSS↑", SETUP_MA14_CROSS);
|
|
|
|
ma14_crosses = 0;
|
|
for(int i=0; i<10; i++) if(i != 1 && MA_Current[1] < MA_Current[i]) ma14_crosses++;
|
|
if(ma14_crosses >= 2) OpenTrade(false, bid, "MA14-CROSS↓", SETUP_MA14_CROSS);
|
|
|
|
// MA BOUNCES (kept)
|
|
if(MathAbs(current - MA_Current[3]) <= buffer)
|
|
{
|
|
OpenTrade(true, ask, "MA50-BOUNCE↑", SETUP_MA50_BOUNCE);
|
|
OpenTrade(false, bid, "MA50-BOUNCE↓", SETUP_MA50_BOUNCE);
|
|
}
|
|
|
|
if(MathAbs(current - MA_Current[4]) <= buffer)
|
|
{
|
|
OpenTrade(true, ask, "MA140-BOUNCE↑", SETUP_MA140_BOUNCE);
|
|
OpenTrade(false, bid, "MA140-BOUNCE↓", SETUP_MA140_BOUNCE);
|
|
}
|
|
|
|
// FIB BREAKS (kept)
|
|
for(int i=1; i<6; i++)
|
|
{
|
|
if(MathAbs(current - PriceLevels[i]) <= buffer)
|
|
{
|
|
if(previous < PriceLevels[i])
|
|
OpenTrade(true, ask, "FIB-BREAK↑", SETUP_FIB_BREAK);
|
|
if(previous > PriceLevels[i])
|
|
OpenTrade(false, bid, "FIB-BREAK↓", SETUP_FIB_BREAK);
|
|
}
|
|
}
|
|
|
|
// MAGNET WALK (kept)
|
|
bool hugging = (Current_ATR > 0.0) ? (MathAbs(current - MA_Current[2]) / Current_ATR < 0.7) : false;
|
|
if(hugging && MA_Current[0] > MA_Current[3])
|
|
OpenTrade(true, ask, "MAGNET-WALK↑", SETUP_MAGNET_WALK);
|
|
if(hugging && MA_Current[0] < MA_Current[3])
|
|
OpenTrade(false, bid, "MAGNET-WALK↓", SETUP_MAGNET_WALK);
|
|
}
|
|
|
|
// 3) Range setups (kept, plus your new “don’t lose money” guard by family)
|
|
if(Current_Family == FAMILY_RANGING)
|
|
{
|
|
if(MathAbs(current - PriceLevels[1]) < buffer && Stoch_K_Current < 35)
|
|
OpenTrade(true, ask, "RANGE-BUY", SETUP_RANGE_ENGINE);
|
|
if(MathAbs(current - PriceLevels[5]) < buffer && Stoch_K_Current > 65)
|
|
OpenTrade(false, bid, "RANGE-SELL", SETUP_RANGE_ENGINE);
|
|
|
|
// Optional extra: MFIB reject inside range edges = higher confidence
|
|
if(MFIB_Reject_Warning)
|
|
{
|
|
if(Stoch_Reject_Warning || Stoch_Extreme_Warning)
|
|
{
|
|
// if near lower half -> buy; upper half -> sell
|
|
if(current <= MFIB_Level_050) OpenTrade(true, ask, "RANGE-MFIB-BUY", SETUP_RANGE_ENGINE);
|
|
if(current >= MFIB_Level_050) OpenTrade(false, bid, "RANGE-MFIB-SELL", SETUP_RANGE_ENGINE);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 4) Chop handling (light-touch; your direction filter won’t block)
|
|
if(Current_Family == FAMILY_CHOP)
|
|
{
|
|
// In chop: lean mean reversion + rejects
|
|
if(Stoch_Extreme_Warning || Stoch_Reject_Warning || Fib_Reject_Warning || MFIB_Reject_Warning)
|
|
{
|
|
if(Stoch_K_Current <= Stoch_Low) OpenTrade(true, ask, "CHOP-REV-BUY", SETUP_MEAN_REV);
|
|
if(Stoch_K_Current >= Stoch_High) OpenTrade(false, bid, "CHOP-REV-SELL", SETUP_MEAN_REV);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|