781 lines
No EOL
50 KiB
MQL5
781 lines
No EOL
50 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| QuarterTheory_AGGRESSIVE_REENTRY.mq5 |
|
|
//| Aggressive MA 7 Retest System - Keep Re-entering on Trend |
|
|
//| 140 MA = Bullish Major | 500 MA = Bearish Major | 10-30 Trades |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Aggressive MA Retest System"
|
|
#property version "14.00"
|
|
#property strict
|
|
|
|
#include <Trade/Trade.mqh>
|
|
CTrade Trade;
|
|
|
|
//================ INPUT PARAMETERS ==================//
|
|
input group "=== GENERAL ==="
|
|
input int MagicNumber = 456789;
|
|
input double Risk_Per_Trade = 1.0;
|
|
input int Min_Active_Entries = 10; // Minimum 10 trades
|
|
input int Max_Simultaneous_Trades = 30; // Maximum 30 trades
|
|
|
|
input group "=== MA TREND SYSTEM ==="
|
|
input int MA_1 = 7; // PRIMARY - Retest this!
|
|
input int MA_2 = 14;
|
|
input int MA_3 = 21;
|
|
input int MA_4 = 50;
|
|
input int MA_5 = 140; // BULLISH MAJOR
|
|
input int MA_6 = 230;
|
|
input int MA_7 = 500; // BEARISH MAJOR
|
|
input int MA_8 = 1000;
|
|
input int MA_9 = 1100;
|
|
input int MA_10 = 1300;
|
|
|
|
input group "=== RE-ENTRY RULES ==="
|
|
input bool Reenter_On_MA7_Retest = true; // Re-enter EVERY MA 7 retest
|
|
input bool Reenter_On_Next_MA = true; // If MA 7 breaks, re-enter at next MA
|
|
input int MA_Retest_Buffer = 25; // Buffer for MA retest detection
|
|
input bool Bullish_Major_140 = true; // MA 140 = Bullish major pullback
|
|
input bool Bearish_Major_500 = true; // MA 500 = Bearish major pullback
|
|
|
|
input group "=== TREND REVERSAL ==="
|
|
input bool Close_Opposite_On_Reverse = true; // Close all opposite on trend change
|
|
input bool Reenter_On_New_Trend = true; // Immediate re-entry on new trend
|
|
input int Min_MA_For_Trend_Change = 3; // MAs needed to confirm reversal
|
|
|
|
input group "=== STOCHASTIC (SECONDARY) ==="
|
|
input int Stoch_K_Period = 5;
|
|
input int Stoch_D_Period = 3;
|
|
input int Stoch_Slowing = 3;
|
|
input bool Use_Stoch_Filter = false; // Optional - aggressive = off
|
|
|
|
input group "=== TAKE PROFIT & RISK ==="
|
|
input int Partial_TP_Points = 2300;
|
|
input double Partial_TP_Percent = 50.0;
|
|
input int BreakEven_Points = 500;
|
|
input int Trailing_SL_Points = 500;
|
|
input int Initial_SL_Points = 800;
|
|
|
|
input group "=== FIBONACCI LEVELS ==="
|
|
input int Lookback_Bars = 200;
|
|
input bool Show_Levels = true;
|
|
|
|
input group "=== DAILY LIMITS ==="
|
|
input double Max_Daily_Loss_Percent = 6.0;
|
|
input double Max_Daily_Profit_Percent = 40.0;
|
|
input int Max_Trades_Per_Day = 50;
|
|
|
|
//================ GLOBALS ==================//
|
|
int Stoch_Handle;
|
|
double Stoch_K_Current = 0;
|
|
|
|
double PriceLevels[];
|
|
string LevelTypes[];
|
|
int TotalLevels = 0;
|
|
|
|
int MA_Handles[10];
|
|
double MA_Current[10];
|
|
double MA_Previous[10];
|
|
|
|
bool Current_Trend_Bullish = false;
|
|
bool Current_Trend_Bearish = false;
|
|
bool Previous_Trend_Bullish = false;
|
|
bool Previous_Trend_Bearish = false;
|
|
|
|
int MA7_Last_Broken_Index = -1; // Track which MA the MA 7 last broke
|
|
|
|
struct Position
|
|
{
|
|
ulong ticket;
|
|
double entry;
|
|
double original_lot;
|
|
bool is_buy;
|
|
bool partial_tp_hit;
|
|
bool be_set;
|
|
datetime entry_time;
|
|
};
|
|
Position OpenPositions[];
|
|
|
|
double DailyStart = 0;
|
|
int TodayTrades = 0;
|
|
datetime LastDay = 0;
|
|
datetime Last_MA7_Retest_Time = 0;
|
|
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
Print("========================================");
|
|
Print("AGGRESSIVE RE-ENTRY v14.0");
|
|
Print("MA 7 Retest + Next MA Entry");
|
|
Print("140=Bull Major | 500=Bear Major");
|
|
Print("========================================");
|
|
|
|
Trade.SetExpertMagicNumber(MagicNumber);
|
|
Trade.SetDeviationInPoints(50);
|
|
|
|
Stoch_Handle = iStochastic(_Symbol, PERIOD_CURRENT, Stoch_K_Period, Stoch_D_Period,
|
|
Stoch_Slowing, MODE_SMA, STO_LOWHIGH);
|
|
|
|
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);
|
|
if(MA_Handles[i] == INVALID_HANDLE)
|
|
{
|
|
Print("ERROR: MA ", periods[i], " failed");
|
|
return INIT_FAILED;
|
|
}
|
|
}
|
|
|
|
CalculatePriceLevels();
|
|
if(Show_Levels) DrawLevels();
|
|
|
|
DailyStart = AccountInfoDouble(ACCOUNT_BALANCE);
|
|
|
|
Print("Min/Max Trades: ", Min_Active_Entries, " - ", Max_Simultaneous_Trades);
|
|
Print("Re-enter on MA 7 retest: ", Reenter_On_MA7_Retest);
|
|
Print("Bullish major: MA 140 | Bearish major: MA 500");
|
|
|
|
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);
|
|
ObjectsDeleteAll(0, "Level_");
|
|
ObjectsDeleteAll(0, "Arrow_");
|
|
ObjectsDeleteAll(0, "TrendLabel");
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void CalculatePriceLevels()
|
|
{
|
|
ArrayResize(PriceLevels, 0);
|
|
ArrayResize(LevelTypes, 0);
|
|
TotalLevels = 0;
|
|
|
|
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;
|
|
|
|
int size = 0;
|
|
ArrayResize(PriceLevels, 10);
|
|
ArrayResize(LevelTypes, 10);
|
|
|
|
PriceLevels[size] = low; LevelTypes[size] = "FIB_0.0"; size++;
|
|
PriceLevels[size] = low + range * 0.236; LevelTypes[size] = "FIB_0.236"; size++;
|
|
PriceLevels[size] = low + range * 0.382; LevelTypes[size] = "FIB_0.382"; size++;
|
|
PriceLevels[size] = low + range * 0.5; LevelTypes[size] = "FIB_0.5"; size++;
|
|
PriceLevels[size] = low + range * 0.618; LevelTypes[size] = "FIB_0.618"; size++;
|
|
PriceLevels[size] = low + range * 0.786; LevelTypes[size] = "FIB_0.786"; size++;
|
|
PriceLevels[size] = high; LevelTypes[size] = "FIB_1.0"; size++;
|
|
|
|
TotalLevels = size;
|
|
}
|
|
|
|
void DrawLevels()
|
|
{
|
|
ObjectsDeleteAll(0, "Level_");
|
|
for(int i=0; i<TotalLevels; i++)
|
|
{
|
|
string name = "Level_" + IntegerToString(i);
|
|
ObjectCreate(0, name, OBJ_HLINE, 0, 0, PriceLevels[i]);
|
|
ObjectSetInteger(0, name, OBJPROP_COLOR, clrDodgerBlue);
|
|
ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DOT);
|
|
ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
|
|
ObjectSetInteger(0, name, OBJPROP_BACK, true);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void UpdateMAs()
|
|
{
|
|
for(int i=0; i<10; i++)
|
|
{
|
|
double curr[1], prev[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];
|
|
}
|
|
}
|
|
|
|
void UpdateStochastic()
|
|
{
|
|
double k_curr[1];
|
|
if(CopyBuffer(Stoch_Handle, MAIN_LINE, 0, 1, k_curr) > 0)
|
|
Stoch_K_Current = k_curr[0];
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| DETERMINE TREND
|
|
//+------------------------------------------------------------------+
|
|
void DetermineTrend()
|
|
{
|
|
UpdateMAs();
|
|
|
|
// Save previous trend
|
|
Previous_Trend_Bullish = Current_Trend_Bullish;
|
|
Previous_Trend_Bearish = Current_Trend_Bearish;
|
|
|
|
int bullish_count = 0;
|
|
int bearish_count = 0;
|
|
|
|
// Count alignments
|
|
for(int i=0; i<5; i++)
|
|
{
|
|
if(MA_Current[i] > MA_Current[i+1])
|
|
bullish_count++;
|
|
if(MA_Current[i] < MA_Current[i+1])
|
|
bearish_count++;
|
|
}
|
|
|
|
// Determine trend
|
|
if(bullish_count >= Min_MA_For_Trend_Change && MA_Current[0] > MA_Current[3])
|
|
{
|
|
Current_Trend_Bullish = true;
|
|
Current_Trend_Bearish = false;
|
|
}
|
|
else if(bearish_count >= Min_MA_For_Trend_Change && MA_Current[0] < MA_Current[3])
|
|
{
|
|
Current_Trend_Bullish = false;
|
|
Current_Trend_Bearish = true;
|
|
}
|
|
|
|
// Update visual
|
|
ObjectDelete(0, "TrendLabel");
|
|
ObjectCreate(0, "TrendLabel", OBJ_LABEL, 0, 0, 0);
|
|
ObjectSetInteger(0, "TrendLabel", OBJPROP_CORNER, CORNER_LEFT_UPPER);
|
|
ObjectSetInteger(0, "TrendLabel", OBJPROP_XDISTANCE, 10);
|
|
ObjectSetInteger(0, "TrendLabel", OBJPROP_YDISTANCE, 30);
|
|
|
|
if(Current_Trend_Bullish)
|
|
{
|
|
ObjectSetString(0, "TrendLabel", OBJPROP_TEXT, "TREND: BULLISH ↑ | MA 140 Major");
|
|
ObjectSetInteger(0, "TrendLabel", OBJPROP_COLOR, clrLime);
|
|
}
|
|
else if(Current_Trend_Bearish)
|
|
{
|
|
ObjectSetString(0, "TrendLabel", OBJPROP_TEXT, "TREND: BEARISH ↓ | MA 500 Major");
|
|
ObjectSetInteger(0, "TrendLabel", OBJPROP_COLOR, clrRed);
|
|
}
|
|
else
|
|
{
|
|
ObjectSetString(0, "TrendLabel", OBJPROP_TEXT, "TREND: RANGING");
|
|
ObjectSetInteger(0, "TrendLabel", OBJPROP_COLOR, clrYellow);
|
|
}
|
|
|
|
ObjectSetInteger(0, "TrendLabel", OBJPROP_FONTSIZE, 12);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| DETECT TREND REVERSAL
|
|
//+------------------------------------------------------------------+
|
|
bool TrendReversed()
|
|
{
|
|
// Bullish to Bearish
|
|
if(Previous_Trend_Bullish && Current_Trend_Bearish)
|
|
{
|
|
Print("========================================");
|
|
Print("TREND REVERSAL: BULLISH → BEARISH");
|
|
Print("========================================");
|
|
return true;
|
|
}
|
|
|
|
// Bearish to Bullish
|
|
if(Previous_Trend_Bearish && Current_Trend_Bullish)
|
|
{
|
|
Print("========================================");
|
|
Print("TREND REVERSAL: BEARISH → BULLISH");
|
|
Print("========================================");
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| CLOSE ALL OPPOSITE TRADES
|
|
//+------------------------------------------------------------------+
|
|
void CloseOppositeTrades(bool close_buys, bool close_sells)
|
|
{
|
|
int closed = 0;
|
|
|
|
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;
|
|
|
|
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
|
|
|
if(close_buys && type == POSITION_TYPE_BUY)
|
|
{
|
|
Trade.PositionClose(ticket);
|
|
closed++;
|
|
Print("CLOSED BUY: Trend reversed to BEARISH");
|
|
}
|
|
|
|
if(close_sells && type == POSITION_TYPE_SELL)
|
|
{
|
|
Trade.PositionClose(ticket);
|
|
closed++;
|
|
Print("CLOSED SELL: Trend reversed to BULLISH");
|
|
}
|
|
}
|
|
|
|
if(closed > 0)
|
|
Print("Total opposite positions closed: ", closed);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| CHECK IF PRICE RETESTING MA 7
|
|
//+------------------------------------------------------------------+
|
|
bool IsPriceRetestingMA7(double price)
|
|
{
|
|
double buffer = MA_Retest_Buffer * _Point;
|
|
double ma7 = MA_Current[0];
|
|
|
|
if(MathAbs(price - ma7) <= buffer)
|
|
{
|
|
// Prevent multiple entries on same retest
|
|
if(TimeCurrent() - Last_MA7_Retest_Time < 60) // 1 minute cooldown
|
|
return false;
|
|
|
|
Last_MA7_Retest_Time = TimeCurrent();
|
|
Print("MA 7 RETEST @ ", ma7, " | Price: ", price);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| DETECT MA 7 BREAK & FIND NEXT MA
|
|
//+------------------------------------------------------------------+
|
|
bool MA7_Broke_Through(bool check_bullish, int &next_ma_index)
|
|
{
|
|
next_ma_index = -1;
|
|
|
|
// Check if MA 7 broke through any MA
|
|
for(int i=1; i<7; i++) // Check MAs 14, 21, 50, 140, 230, 500
|
|
{
|
|
if(check_bullish)
|
|
{
|
|
// MA 7 broke DOWN through MA
|
|
if(MA_Previous[0] > MA_Previous[i] && MA_Current[0] <= MA_Current[i])
|
|
{
|
|
next_ma_index = i;
|
|
MA7_Last_Broken_Index = i;
|
|
Print("MA 7 BROKE DOWN through MA ", GetMAPeriod(i));
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// MA 7 broke UP through MA
|
|
if(MA_Previous[0] < MA_Previous[i] && MA_Current[0] >= MA_Current[i])
|
|
{
|
|
next_ma_index = i;
|
|
MA7_Last_Broken_Index = i;
|
|
Print("MA 7 BROKE UP through MA ", GetMAPeriod(i));
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| CHECK IF PRICE AT NEXT MA (after break)
|
|
//+------------------------------------------------------------------+
|
|
bool IsPriceAtNextMA(double price, bool check_bullish, int &ma_idx, string &ma_name)
|
|
{
|
|
if(MA7_Last_Broken_Index < 0) return false;
|
|
|
|
double buffer = MA_Retest_Buffer * _Point;
|
|
|
|
// Check the MA that was broken
|
|
int idx = MA7_Last_Broken_Index;
|
|
if(MathAbs(price - MA_Current[idx]) <= buffer)
|
|
{
|
|
ma_idx = idx;
|
|
ma_name = "MA " + IntegerToString(GetMAPeriod(idx));
|
|
Print("NEXT MA RETEST: ", ma_name, " @ ", MA_Current[idx]);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| CHECK IF AT MAJOR MA (140 for bullish, 500 for bearish)
|
|
//+------------------------------------------------------------------+
|
|
bool IsPriceAtMajorMA(double price, bool check_bullish, string &major_name)
|
|
{
|
|
double buffer = MA_Retest_Buffer * _Point;
|
|
|
|
if(check_bullish && Bullish_Major_140)
|
|
{
|
|
// MA 140 for bullish
|
|
if(MathAbs(price - MA_Current[4]) <= buffer)
|
|
{
|
|
major_name = "MA 140 (BULL MAJOR)";
|
|
Print("MAJOR MA TOUCH: ", major_name);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if(!check_bullish && Bearish_Major_500)
|
|
{
|
|
// MA 500 for bearish
|
|
if(MathAbs(price - MA_Current[6]) <= buffer)
|
|
{
|
|
major_name = "MA 500 (BEAR MAJOR)";
|
|
Print("MAJOR MA TOUCH: ", major_name);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int GetMAPeriod(int index)
|
|
{
|
|
int periods[10] = {MA_1, MA_2, MA_3, MA_4, MA_5, MA_6, MA_7, MA_8, MA_9, MA_10};
|
|
return periods[index];
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
double GetLotSize(int sl_points)
|
|
{
|
|
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 / ((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;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
bool CheckLimits()
|
|
{
|
|
MqlDateTime dt;
|
|
TimeCurrent(dt);
|
|
dt.hour = 0; dt.min = 0; dt.sec = 0;
|
|
datetime today = StructToTime(dt);
|
|
|
|
if(today != LastDay)
|
|
{
|
|
DailyStart = AccountInfoDouble(ACCOUNT_BALANCE);
|
|
TodayTrades = 0;
|
|
LastDay = today;
|
|
}
|
|
|
|
if(TodayTrades >= Max_Trades_Per_Day) return false;
|
|
|
|
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
|
|
double pl = ((balance - DailyStart) / DailyStart) * 100.0;
|
|
|
|
if(pl <= -Max_Daily_Loss_Percent || pl >= Max_Daily_Profit_Percent)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void OpenTrade(bool buy, double price, string reason)
|
|
{
|
|
double lot = GetLotSize(Initial_SL_Points);
|
|
if(lot < SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN)) return;
|
|
|
|
double sl = buy ? price - Initial_SL_Points * _Point : price + Initial_SL_Points * _Point;
|
|
|
|
string comment = (buy ? "BULL" : "BEAR") + " | " + reason;
|
|
|
|
bool result = buy ? Trade.Buy(lot, _Symbol, price, sl, 0, comment)
|
|
: Trade.Sell(lot, _Symbol, price, sl, 0, comment);
|
|
|
|
if(result)
|
|
{
|
|
TodayTrades++;
|
|
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].entry_time = TimeCurrent();
|
|
|
|
Print("========== TRADE ", TodayTrades, " ==========");
|
|
Print(buy ? "BUY" : "SELL", " @ ", price);
|
|
Print("Reason: ", reason);
|
|
Print("===================================");
|
|
|
|
string arrow_name = "Arrow_" + IntegerToString(ticket);
|
|
ObjectCreate(0, arrow_name, OBJ_ARROW, 0, TimeCurrent(), price);
|
|
ObjectSetInteger(0, arrow_name, OBJPROP_COLOR, buy ? clrLime : clrRed);
|
|
ObjectSetInteger(0, arrow_name, OBJPROP_ARROWCODE, buy ? 233 : 234);
|
|
ObjectSetInteger(0, arrow_name, OBJPROP_WIDTH, 3);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
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 sl = PositionGetDouble(POSITION_SL);
|
|
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 lot = PositionGetDouble(POSITION_VOLUME);
|
|
double close_size = NormalizeDouble(OpenPositions[idx].original_lot * Partial_TP_Percent / 100.0, 2);
|
|
if(close_size > 0 && close_size <= lot)
|
|
{
|
|
Trade.PositionClosePartial(ticket, close_size);
|
|
OpenPositions[idx].partial_tp_hit = true;
|
|
}
|
|
}
|
|
|
|
// Breakeven
|
|
if(!OpenPositions[idx].be_set && profit_points >= BreakEven_Points)
|
|
{
|
|
if((is_buy && sl < entry) || (!is_buy && sl > entry))
|
|
{
|
|
Trade.PositionModify(ticket, entry, 0);
|
|
OpenPositions[idx].be_set = true;
|
|
}
|
|
}
|
|
|
|
// Trailing
|
|
if(profit_points >= Trailing_SL_Points + 100)
|
|
{
|
|
double newSL = is_buy ? current - Trailing_SL_Points * _Point
|
|
: current + Trailing_SL_Points * _Point;
|
|
if((is_buy && newSL > sl + 50 * _Point) ||
|
|
(!is_buy && newSL < sl - 50 * _Point))
|
|
Trade.PositionModify(ticket, newSL, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
void OnTick()
|
|
{
|
|
static datetime last_trend_check = 0;
|
|
if(TimeCurrent() - last_trend_check >= 5)
|
|
{
|
|
DetermineTrend();
|
|
|
|
// Check for trend reversal
|
|
if(TrendReversed() && Close_Opposite_On_Reverse)
|
|
{
|
|
if(Current_Trend_Bullish)
|
|
CloseOppositeTrades(false, true); // Close all sells
|
|
else if(Current_Trend_Bearish)
|
|
CloseOppositeTrades(true, false); // Close all buys
|
|
}
|
|
|
|
last_trend_check = TimeCurrent();
|
|
}
|
|
|
|
ManagePositions();
|
|
|
|
if(!CheckLimits()) return;
|
|
|
|
int buy_count = 0;
|
|
int sell_count = 0;
|
|
|
|
for(int i=0; i<PositionsTotal(); i++)
|
|
{
|
|
if(PositionGetTicket(i) == 0) continue;
|
|
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
|
|
PositionGetInteger(POSITION_MAGIC) == MagicNumber)
|
|
{
|
|
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
|
|
buy_count++;
|
|
else
|
|
sell_count++;
|
|
}
|
|
}
|
|
|
|
int total = buy_count + sell_count;
|
|
if(total >= Max_Simultaneous_Trades) return;
|
|
|
|
bool force_entry = (total < Min_Active_Entries);
|
|
|
|
static datetime last_calc = 0;
|
|
if(TimeCurrent() - last_calc > 1800)
|
|
{
|
|
CalculatePriceLevels();
|
|
if(Show_Levels) DrawLevels();
|
|
last_calc = TimeCurrent();
|
|
}
|
|
|
|
UpdateMAs();
|
|
UpdateStochastic();
|
|
|
|
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
|
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
|
double current = (bid + ask) / 2;
|
|
|
|
// Check re-entry triggers
|
|
bool ma7_retest = IsPriceRetestingMA7(current);
|
|
|
|
int next_ma_idx = -1;
|
|
bool ma7_broke_bull = MA7_Broke_Through(true, next_ma_idx);
|
|
bool ma7_broke_bear = MA7_Broke_Through(false, next_ma_idx);
|
|
|
|
int next_ma_check = -1;
|
|
string next_ma_name = "";
|
|
bool at_next_ma_bull = IsPriceAtNextMA(current, true, next_ma_check, next_ma_name);
|
|
bool at_next_ma_bear = IsPriceAtNextMA(current, false, next_ma_check, next_ma_name);
|
|
|
|
string major_name = "";
|
|
bool at_major_bull = IsPriceAtMajorMA(current, true, major_name);
|
|
bool at_major_bear = IsPriceAtMajorMA(current, false, major_name);
|
|
|
|
// === BUY ENTRIES (BULLISH TREND ONLY) ===
|
|
if(Current_Trend_Bullish)
|
|
{
|
|
bool buy_signal = false;
|
|
string buy_reason = "";
|
|
|
|
// 1. MA 7 Retest (PRIMARY!)
|
|
if(Reenter_On_MA7_Retest && ma7_retest)
|
|
{
|
|
buy_signal = true;
|
|
buy_reason = "MA 7 RETEST";
|
|
}
|
|
|
|
// 2. Next MA after MA 7 broke
|
|
if(Reenter_On_Next_MA && at_next_ma_bull)
|
|
{
|
|
buy_signal = true;
|
|
buy_reason = "NEXT MA: " + next_ma_name;
|
|
}
|
|
|
|
// 3. Major MA 140 (Bullish major pullback)
|
|
if(at_major_bull)
|
|
{
|
|
buy_signal = true;
|
|
buy_reason = major_name + " PULLBACK";
|
|
}
|
|
|
|
// 4. Force entry
|
|
if(force_entry && buy_count < Min_Active_Entries / 2)
|
|
{
|
|
buy_signal = true;
|
|
buy_reason = "FORCE (" + IntegerToString(total) + "/" + IntegerToString(Min_Active_Entries) + ")";
|
|
}
|
|
|
|
// 5. Immediate re-entry on new trend
|
|
if(Reenter_On_New_Trend && Previous_Trend_Bearish && buy_count == 0)
|
|
{
|
|
buy_signal = true;
|
|
buy_reason = "NEW BULL TREND";
|
|
}
|
|
|
|
if(buy_signal)
|
|
OpenTrade(true, ask, buy_reason);
|
|
}
|
|
|
|
// === SELL ENTRIES (BEARISH TREND ONLY) ===
|
|
if(Current_Trend_Bearish)
|
|
{
|
|
bool sell_signal = false;
|
|
string sell_reason = "";
|
|
|
|
// 1. MA 7 Retest (PRIMARY!)
|
|
if(Reenter_On_MA7_Retest && ma7_retest)
|
|
{
|
|
sell_signal = true;
|
|
sell_reason = "MA 7 RETEST";
|
|
}
|
|
|
|
// 2. Next MA after MA 7 broke
|
|
if(Reenter_On_Next_MA && at_next_ma_bear)
|
|
{
|
|
sell_signal = true;
|
|
sell_reason = "NEXT MA: " + next_ma_name;
|
|
}
|
|
|
|
// 3. Major MA 500 (Bearish major pullback)
|
|
if(at_major_bear)
|
|
{
|
|
sell_signal = true;
|
|
sell_reason = major_name + " PULLBACK";
|
|
}
|
|
|
|
// 4. Force entry
|
|
if(force_entry && sell_count < Min_Active_Entries / 2)
|
|
{
|
|
sell_signal = true;
|
|
sell_reason = "FORCE (" + IntegerToString(total) + "/" + IntegerToString(Min_Active_Entries) + ")";
|
|
}
|
|
|
|
// 5. Immediate re-entry on new trend
|
|
if(Reenter_On_New_Trend && Previous_Trend_Bullish && sell_count == 0)
|
|
{
|
|
sell_signal = true;
|
|
sell_reason = "NEW BEAR TREND";
|
|
}
|
|
|
|
if(sell_signal)
|
|
OpenTrade(false, bid, sell_reason);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+ |