576 lines
21 KiB
Text
576 lines
21 KiB
Text
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| COMPLETE REVERSAL BOT - 127.8% + 61.8% STRATEGY |
|
||
|
|
//| Multi-Timeframe: 15M Setup | 5M Precision | 1H Trend Filter |
|
||
|
|
//| Indicators: Stoch RSI + CCI Dual Divergence |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
#property strict
|
||
|
|
|
||
|
|
// === INPUT PARAMETERS ===
|
||
|
|
input double FirstEntryLots = 0.03; // 127.8% entry size
|
||
|
|
input double SecondEntryLots = 0.02; // 61.8% add-on size
|
||
|
|
input int SLPips = 10; // Base SL in pips
|
||
|
|
input double MaxSpread = 3.0; // Max spread filter
|
||
|
|
input int MomentumThreshold = 70; // Min momentum score
|
||
|
|
input double Fib1278 = 1.278; // First entry fib level
|
||
|
|
input double Fib618 = 0.618; // Second entry fib level
|
||
|
|
|
||
|
|
enum EntryStyle {
|
||
|
|
STYLE_AGGRESSIVE, // Enter on touch/wick
|
||
|
|
STYLE_CONSERVATIVE // Enter on close confirmation
|
||
|
|
};
|
||
|
|
input EntryStyle EntryMode = STYLE_CONSERVATIVE;
|
||
|
|
|
||
|
|
enum TradePhase {
|
||
|
|
PHASE_NONE,
|
||
|
|
PHASE_1278_LONG,
|
||
|
|
PHASE_618_ADDON
|
||
|
|
};
|
||
|
|
|
||
|
|
// === GLOBAL VARIABLES ===
|
||
|
|
TradePhase currentPhase = PHASE_NONE;
|
||
|
|
|
||
|
|
struct SetupData {
|
||
|
|
double swingHigh;
|
||
|
|
double swingLow;
|
||
|
|
double fib1278Level;
|
||
|
|
double entry1278Price;
|
||
|
|
double newSwingHigh;
|
||
|
|
double pullbackLow;
|
||
|
|
bool swingEstablished;
|
||
|
|
};
|
||
|
|
SetupData activeSetup;
|
||
|
|
|
||
|
|
datetime last5MBar = 0;
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| EXPERT INITIALIZATION |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
int OnInit()
|
||
|
|
{
|
||
|
|
if(StringFind(Symbol(), "Volatility") == -1)
|
||
|
|
{
|
||
|
|
Alert("This EA is designed for Synthetic Indices!");
|
||
|
|
return(INIT_FAILED);
|
||
|
|
}
|
||
|
|
return(INIT_SUCCEEDED);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| MAIN TICK FUNCTION |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void OnTick()
|
||
|
|
{
|
||
|
|
// Spread filter
|
||
|
|
if(SymbolInfoInteger(Symbol(), SYMBOL_SPREAD) > MaxSpread * 10) return;
|
||
|
|
|
||
|
|
// Time filter
|
||
|
|
MqlDateTime dt;
|
||
|
|
TimeToStruct(TimeCurrent(), dt);
|
||
|
|
if(dt.min < 5 || dt.min > 55) return;
|
||
|
|
|
||
|
|
// Check for new 5M bar
|
||
|
|
datetime current5MBar = iTime(Symbol(), PERIOD_M5, 0);
|
||
|
|
if(current5MBar != last5MBar)
|
||
|
|
{
|
||
|
|
last5MBar = current5MBar;
|
||
|
|
|
||
|
|
// Entry logic
|
||
|
|
if(PositionsTotal() == 0 || !HasOpenPosition("1278"))
|
||
|
|
{
|
||
|
|
switch(currentPhase)
|
||
|
|
{
|
||
|
|
case PHASE_NONE:
|
||
|
|
Check1278LongEntry();
|
||
|
|
break;
|
||
|
|
case PHASE_1278_LONG:
|
||
|
|
CheckForPullbackSetup();
|
||
|
|
break;
|
||
|
|
case PHASE_618_ADDON:
|
||
|
|
Check618AddonEntry();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Manage positions
|
||
|
|
if(PositionsTotal() > 0)
|
||
|
|
ManagePositions();
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| 127.8% LONG ENTRY |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void Check1278LongEntry()
|
||
|
|
{
|
||
|
|
// 15M Setup
|
||
|
|
int highestBar15M = iHighest(Symbol(), PERIOD_M15, MODE_HIGH, 30, 1);
|
||
|
|
int lowestBar15M = iLowest(Symbol(), PERIOD_M15, MODE_LOW, 30, 1);
|
||
|
|
|
||
|
|
activeSetup.swingHigh = iHigh(Symbol(), PERIOD_M15, highestBar15M);
|
||
|
|
activeSetup.swingLow = iLow(Symbol(), PERIOD_M15, lowestBar15M);
|
||
|
|
|
||
|
|
double range15M = activeSetup.swingHigh - activeSetup.swingLow;
|
||
|
|
activeSetup.fib1278Level = activeSetup.swingHigh - (range15M * Fib1278);
|
||
|
|
|
||
|
|
double low15M_0 = iLow(Symbol(), PERIOD_M15, 0);
|
||
|
|
double tolerance15M = 5 * SymbolInfoDouble(Symbol(), SYMBOL_POINT) * 10;
|
||
|
|
|
||
|
|
if(!(low15M_0 <= activeSetup.fib1278Level + tolerance15M)) return;
|
||
|
|
|
||
|
|
// 1H Trend filter
|
||
|
|
if(GetTrendDirection(PERIOD_H1) == -1) return;
|
||
|
|
|
||
|
|
// Divergence check
|
||
|
|
if(!BullishDivergenceOnTimeframe(PERIOD_M15)) return;
|
||
|
|
|
||
|
|
// Momentum score
|
||
|
|
if(CalculateMomentumScore() < MomentumThreshold) return;
|
||
|
|
|
||
|
|
// 5M Precision entry
|
||
|
|
if(Check5MPrecisionEntry())
|
||
|
|
{
|
||
|
|
Print("127.8% entry executed!");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| 5M PRECISION ENTRY WITH STYLE SWITCH |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool Check5MPrecisionEntry()
|
||
|
|
{
|
||
|
|
double low5M_0 = iLow(Symbol(), PERIOD_M5, 0);
|
||
|
|
double low5M_1 = iLow(Symbol(), PERIOD_M5, 1);
|
||
|
|
double close5M_0 = iClose(Symbol(), PERIOD_M5, 0);
|
||
|
|
double close5M_1 = iClose(Symbol(), PERIOD_M5, 1);
|
||
|
|
double open5M_0 = iOpen(Symbol(), PERIOD_M5, 0);
|
||
|
|
double high5M_0 = iHigh(Symbol(), PERIOD_M5, 0);
|
||
|
|
double high5M_1 = iHigh(Symbol(), PERIOD_M5, 1);
|
||
|
|
|
||
|
|
double tolerance5M = 3 * SymbolInfoDouble(Symbol(), SYMBOL_POINT) * 10;
|
||
|
|
|
||
|
|
if(!(low5M_0 <= activeSetup.fib1278Level + tolerance5M ||
|
||
|
|
low5M_1 <= activeSetup.fib1278Level + tolerance5M)) return false;
|
||
|
|
|
||
|
|
// AGGRESSIVE MODE
|
||
|
|
if(EntryMode == STYLE_AGGRESSIVE)
|
||
|
|
{
|
||
|
|
bool quickBounce = (low5M_0 <= activeSetup.fib1278Level + tolerance5M && close5M_0 > open5M_0);
|
||
|
|
bool longWick = ((MathMin(close5M_0, open5M_0) - low5M_0) > MathAbs(close5M_0 - open5M_0) * 1.5);
|
||
|
|
|
||
|
|
double stochMain[];
|
||
|
|
ArraySetAsSeries(stochMain, true);
|
||
|
|
int stochHandle = iStochastic(Symbol(), PERIOD_M5, 14, 3, 3, MODE_SMA, STO_LOWHIGH);
|
||
|
|
CopyBuffer(stochHandle, 0, 0, 2, stochMain);
|
||
|
|
bool stochTurning = (stochMain[0] > stochMain[1] && stochMain[1] < 30);
|
||
|
|
|
||
|
|
int aggSignals = (quickBounce ? 1 : 0) + (longWick ? 1 : 0) + (stochTurning ? 1 : 0);
|
||
|
|
if(aggSignals >= 2)
|
||
|
|
return Execute5MEntry("1278_5M_Aggressive", 8);
|
||
|
|
}
|
||
|
|
|
||
|
|
// CONSERVATIVE MODE
|
||
|
|
else
|
||
|
|
{
|
||
|
|
bool bullishClose = (close5M_0 > open5M_0);
|
||
|
|
bool abovePrevious = (close5M_0 > high5M_1);
|
||
|
|
|
||
|
|
double microResistance = iHigh(Symbol(), PERIOD_M5, iHighest(Symbol(), PERIOD_M5, MODE_HIGH, 3, 1));
|
||
|
|
bool brokeResistance = (close5M_0 > microResistance);
|
||
|
|
|
||
|
|
double stochMain[], stochSignal[];
|
||
|
|
ArraySetAsSeries(stochMain, true);
|
||
|
|
ArraySetAsSeries(stochSignal, true);
|
||
|
|
int stochHandle = iStochastic(Symbol(), PERIOD_M5, 14, 3, 3, MODE_SMA, STO_LOWHIGH);
|
||
|
|
CopyBuffer(stochHandle, 0, 0, 2, stochMain);
|
||
|
|
CopyBuffer(stochHandle, 1, 0, 2, stochSignal);
|
||
|
|
bool stochCross = (stochMain[0] > stochSignal[0] && stochMain[1] <= stochSignal[1]);
|
||
|
|
|
||
|
|
double cci[];
|
||
|
|
ArraySetAsSeries(cci, true);
|
||
|
|
int cciHandle = iCCI(Symbol(), PERIOD_M5, 20, PRICE_TYPICAL);
|
||
|
|
CopyBuffer(cciHandle, 0, 0, 2, cci);
|
||
|
|
bool cciCross = (cci[0] > cci[1] && cci[1] < -100);
|
||
|
|
|
||
|
|
int confSignals = (bullishClose ? 1 : 0) + (abovePrevious ? 1 : 0) +
|
||
|
|
(brokeResistance ? 1 : 0) + (stochCross ? 1 : 0) + (cciCross ? 1 : 0);
|
||
|
|
|
||
|
|
if(confSignals >= 3)
|
||
|
|
return Execute5MEntry("1278_5M_Conservative", 6);
|
||
|
|
}
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| EXECUTE 5M ENTRY |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool Execute5MEntry(string comment, int slPips)
|
||
|
|
{
|
||
|
|
double entryPrice = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
|
||
|
|
double sl = NormalizeDouble(entryPrice - slPips * SymbolInfoDouble(Symbol(), SYMBOL_POINT) * 10, _Digits);
|
||
|
|
double tp = NormalizeDouble(activeSetup.swingHigh, _Digits);
|
||
|
|
double lots = (EntryMode == STYLE_AGGRESSIVE) ? FirstEntryLots * 0.9 : FirstEntryLots;
|
||
|
|
|
||
|
|
if(ExecuteTrade(ORDER_TYPE_BUY, lots, sl, tp, comment))
|
||
|
|
{
|
||
|
|
currentPhase = PHASE_1278_LONG;
|
||
|
|
activeSetup.entry1278Price = entryPrice;
|
||
|
|
activeSetup.swingEstablished = false;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| CHECK FOR PULLBACK SETUP (AFTER 127.8% MOVES UP) |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void CheckForPullbackSetup()
|
||
|
|
{
|
||
|
|
if(!HasOpenPosition("1278")) { ResetToPhaseNone(); return; }
|
||
|
|
|
||
|
|
double recentHigh = iHigh(Symbol(), PERIOD_M5, iHighest(Symbol(), PERIOD_M5, MODE_HIGH, 15, 0));
|
||
|
|
double moveUp = recentHigh - activeSetup.entry1278Price;
|
||
|
|
double minMove = 20 * SymbolInfoDouble(Symbol(), SYMBOL_POINT) * 10;
|
||
|
|
|
||
|
|
if(moveUp >= minMove)
|
||
|
|
{
|
||
|
|
activeSetup.newSwingHigh = recentHigh;
|
||
|
|
activeSetup.swingEstablished = true;
|
||
|
|
currentPhase = PHASE_618_ADDON;
|
||
|
|
Print("Swing established at ", recentHigh, " - waiting for 61.8% pullback");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| 61.8% ADD-ON ENTRY |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void Check618AddonEntry()
|
||
|
|
{
|
||
|
|
if(!activeSetup.swingEstablished) return;
|
||
|
|
|
||
|
|
double upMove = activeSetup.newSwingHigh - activeSetup.entry1278Price;
|
||
|
|
double fib618Level = activeSetup.newSwingHigh - (upMove * Fib618);
|
||
|
|
double fib786Level = activeSetup.newSwingHigh - (upMove * 0.786);
|
||
|
|
|
||
|
|
double close15M = iClose(Symbol(), PERIOD_M15, 0);
|
||
|
|
double low15M = iLow(Symbol(), PERIOD_M15, 0);
|
||
|
|
double tolerance = 5 * SymbolInfoDouble(Symbol(), SYMBOL_POINT) * 10;
|
||
|
|
|
||
|
|
if(!(low15M <= fib618Level + tolerance && low15M >= fib786Level - tolerance)) return;
|
||
|
|
|
||
|
|
if(!Check5M_618Entry(fib618Level, fib786Level)) return;
|
||
|
|
|
||
|
|
double entryPrice = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
|
||
|
|
double sl = NormalizeDouble(activeSetup.entry1278Price - 5 * SymbolInfoDouble(Symbol(), SYMBOL_POINT) * 10, _Digits);
|
||
|
|
double tp = NormalizeDouble(activeSetup.newSwingHigh + (upMove * 0.5), _Digits);
|
||
|
|
|
||
|
|
if(ExecuteTrade(ORDER_TYPE_BUY, SecondEntryLots, sl, tp, "618_Addon"))
|
||
|
|
{
|
||
|
|
activeSetup.pullbackLow = low15M;
|
||
|
|
Print("61.8% add-on executed at ", entryPrice);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| 5M 61.8% ENTRY WITH STYLE |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool Check5M_618Entry(double fib618, double fib786)
|
||
|
|
{
|
||
|
|
double close5M_0 = iClose(Symbol(), PERIOD_M5, 0);
|
||
|
|
double close5M_1 = iClose(Symbol(), PERIOD_M5, 1);
|
||
|
|
double low5M_0 = iLow(Symbol(), PERIOD_M5, 0);
|
||
|
|
double low5M_1 = iLow(Symbol(), PERIOD_M5, 1);
|
||
|
|
double open5M_0 = iOpen(Symbol(), PERIOD_M5, 0);
|
||
|
|
double high5M_1 = iHigh(Symbol(), PERIOD_M5, 1);
|
||
|
|
|
||
|
|
double tolerance = 3 * SymbolInfoDouble(Symbol(), SYMBOL_POINT) * 10;
|
||
|
|
|
||
|
|
bool inZone = (low5M_0 <= fib618 + tolerance && low5M_0 >= fib786 - tolerance);
|
||
|
|
if(!inZone) return false;
|
||
|
|
|
||
|
|
if(EntryMode == STYLE_AGGRESSIVE)
|
||
|
|
{
|
||
|
|
bool doubleBottom = (MathAbs(low5M_0 - low5M_1) < tolerance * 2);
|
||
|
|
bool quickBounce = (low5M_0 <= fib618 && close5M_0 > open5M_0);
|
||
|
|
bool hammer = ((MathMin(close5M_0, open5M_0) - low5M_0) > MathAbs(close5M_0 - open5M_0) * 2);
|
||
|
|
|
||
|
|
int aggCount = (doubleBottom ? 1 : 0) + (quickBounce ? 1 : 0) + (hammer ? 1 : 0);
|
||
|
|
return (aggCount >= 2);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
double low5M_2 = iLow(Symbol(), PERIOD_M5, 2);
|
||
|
|
bool higherLow = (low5M_0 > low5M_1 && low5M_1 > low5M_2);
|
||
|
|
bool closeStrong = (close5M_0 > high5M_1);
|
||
|
|
|
||
|
|
double microRes = iHigh(Symbol(), PERIOD_M5, iHighest(Symbol(), PERIOD_M5, MODE_HIGH, 5, 1));
|
||
|
|
bool brokeRes = (close5M_0 > microRes);
|
||
|
|
bool momentumOK = BullishDivergenceOnTimeframe(PERIOD_M5);
|
||
|
|
|
||
|
|
int confCount = (higherLow ? 1 : 0) + (closeStrong ? 1 : 0) + (brokeRes ? 1 : 0) + (momentumOK ? 1 : 0);
|
||
|
|
return (confCount >= 3);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| DIVERGENCE CHECKER (Stoch RSI + CCI) |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool BullishDivergenceOnTimeframe(ENUM_TIMEFRAMES tf)
|
||
|
|
{
|
||
|
|
double low1 = iLow(Symbol(), tf, 5);
|
||
|
|
double low2 = iLow(Symbol(), tf, 1);
|
||
|
|
if(low2 >= low1) return false;
|
||
|
|
|
||
|
|
double stochMain[];
|
||
|
|
ArraySetAsSeries(stochMain, true);
|
||
|
|
int stochHandle = iStochastic(Symbol(), tf, 14, 3, 3, MODE_SMA, STO_LOWHIGH);
|
||
|
|
if(CopyBuffer(stochHandle, 0, 0, 10, stochMain) < 10) return false;
|
||
|
|
|
||
|
|
double cci[];
|
||
|
|
ArraySetAsSeries(cci, true);
|
||
|
|
int cciHandle = iCCI(Symbol(), tf, 20, PRICE_TYPICAL);
|
||
|
|
if(CopyBuffer(cciHandle, 0, 0, 10, cci) < 10) return false;
|
||
|
|
|
||
|
|
return (stochMain[1] > stochMain[5] && cci[1] > cci[5]);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| MOMENTUM SCORE |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
int CalculateMomentumScore()
|
||
|
|
{
|
||
|
|
int score = 0;
|
||
|
|
|
||
|
|
double rsi = iRSI(Symbol(), PERIOD_M15, 14, PRICE_CLOSE, 0);
|
||
|
|
if(rsi < 30) score += (int)MathMin((30 - rsi) * 1.2, 25);
|
||
|
|
|
||
|
|
double open = iOpen(Symbol(), PERIOD_M15, 0);
|
||
|
|
double high = iHigh(Symbol(), PERIOD_M15, 0);
|
||
|
|
double low = iLow(Symbol(), PERIOD_M15, 0);
|
||
|
|
double close = iClose(Symbol(), PERIOD_M15, 0);
|
||
|
|
|
||
|
|
double body = MathAbs(close - open);
|
||
|
|
double lowerWick = MathMin(close, open) - low;
|
||
|
|
if(body > 0 && lowerWick / body > 2.0) score += 25;
|
||
|
|
|
||
|
|
double stochMain[];
|
||
|
|
ArraySetAsSeries(stochMain, true);
|
||
|
|
int stochHandle = iStochastic(Symbol(), PERIOD_M15, 14, 3, 3, MODE_SMA, STO_LOWHIGH);
|
||
|
|
CopyBuffer(stochHandle, 0, 0, 1, stochMain);
|
||
|
|
if(stochMain[0] < 15) score += 20;
|
||
|
|
|
||
|
|
double cci[];
|
||
|
|
ArraySetAsSeries(cci, true);
|
||
|
|
int cciHandle = iCCI(Symbol(), PERIOD_M15, 20, PRICE_TYPICAL);
|
||
|
|
CopyBuffer(cciHandle, 0, 0, 1, cci);
|
||
|
|
if(cci[0] < -150) score += 20;
|
||
|
|
else if(cci[0] < -100) score += 10;
|
||
|
|
|
||
|
|
double vwap = iMA(Symbol(), PERIOD_M15, 20, 0, MODE_SMA, PRICE_TYPICAL, 0);
|
||
|
|
double distance = MathAbs(close - vwap) / vwap * 100;
|
||
|
|
score += (int)MathMin(distance * 2, 10);
|
||
|
|
|
||
|
|
return score;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| TREND DIRECTION |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
int GetTrendDirection(ENUM_TIMEFRAMES tf)
|
||
|
|
{
|
||
|
|
double ma50 = iMA(Symbol(), tf, 50, 0, MODE_SMA, PRICE_CLOSE, 0);
|
||
|
|
double ma200 = iMA(Symbol(), tf, 200, 0, MODE_SMA, PRICE_CLOSE, 0);
|
||
|
|
double close = iClose(Symbol(), tf, 0);
|
||
|
|
|
||
|
|
if(close > ma50 && ma50 > ma200) return 1;
|
||
|
|
if(close < ma50 && ma50 < ma200) return -1;
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| POSITION MANAGEMENT |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void ManagePositions()
|
||
|
|
{
|
||
|
|
bool has1278 = false, has618 = false;
|
||
|
|
double avgEntry = 0, totalLots = 0;
|
||
|
|
|
||
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||
|
|
{
|
||
|
|
ulong ticket = PositionGetTicket(i);
|
||
|
|
if(ticket <= 0) continue;
|
||
|
|
if(PositionGetString(POSITION_SYMBOL) != Symbol()) continue;
|
||
|
|
|
||
|
|
string comment = PositionGetString(POSITION_COMMENT);
|
||
|
|
int posType = (int)PositionGetInteger(POSITION_TYPE);
|
||
|
|
double lots = PositionGetDouble(POSITION_VOLUME);
|
||
|
|
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
|
||
|
|
|
||
|
|
if(StringFind(comment, "1278") != -1) has1278 = true;
|
||
|
|
if(StringFind(comment, "618") != -1) has618 = true;
|
||
|
|
|
||
|
|
avgEntry += openPrice * lots;
|
||
|
|
totalLots += lots;
|
||
|
|
|
||
|
|
ManageSinglePosition(ticket, posType, openPrice);
|
||
|
|
}
|
||
|
|
|
||
|
|
if(totalLots > 0) avgEntry /= totalLots;
|
||
|
|
|
||
|
|
if(has1278 && has618)
|
||
|
|
ManageAveragedPosition(avgEntry, totalLots);
|
||
|
|
|
||
|
|
if(!has1278 && !has618 && currentPhase != PHASE_NONE)
|
||
|
|
ResetToPhaseNone();
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| SINGLE POSITION MANAGEMENT |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void ManageSinglePosition(ulong ticket, int posType, double openPrice)
|
||
|
|
{
|
||
|
|
double currentSL = PositionGetDouble(POSITION_SL);
|
||
|
|
double currentTP = PositionGetDouble(POSITION_TP);
|
||
|
|
|
||
|
|
double currentPrice = (posType == POSITION_TYPE_BUY) ?
|
||
|
|
SymbolInfoDouble(Symbol(), SYMBOL_BID) :
|
||
|
|
SymbolInfoDouble(Symbol(), SYMBOL_ASK);
|
||
|
|
|
||
|
|
double profitPips = MathAbs(currentPrice - openPrice) /
|
||
|
|
(SymbolInfoDouble(Symbol(), SYMBOL_POINT) * 10);
|
||
|
|
|
||
|
|
// Break even at 15 pips
|
||
|
|
if(profitPips >= 15 && currentSL == 0)
|
||
|
|
{
|
||
|
|
double newSL = openPrice + 2 * SymbolInfoDouble(Symbol(), SYMBOL_POINT) * 10;
|
||
|
|
ModifyPosition(ticket, NormalizeDouble(newSL, _Digits), currentTP);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| AVERAGED POSITION MANAGEMENT |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void ManageAveragedPosition(double avgPrice, double totalLots)
|
||
|
|
{
|
||
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||
|
|
{
|
||
|
|
ulong ticket = PositionGetTicket(i);
|
||
|
|
if(ticket <= 0) continue;
|
||
|
|
if(PositionGetString(POSITION_SYMBOL) != Symbol()) continue;
|
||
|
|
|
||
|
|
int posType = (int)PositionGetInteger(POSITION_TYPE);
|
||
|
|
double currentSL = PositionGetDouble(POSITION_SL);
|
||
|
|
double currentTP = PositionGetDouble(POSITION_TP);
|
||
|
|
|
||
|
|
double currentPrice = SymbolInfoDouble(Symbol(), SYMBOL_BID);
|
||
|
|
double profitPips = MathAbs(currentPrice - avgPrice) /
|
||
|
|
(SymbolInfoDouble(Symbol(), SYMBOL_POINT) * 10);
|
||
|
|
|
||
|
|
// Breakeven both at +10 pips averaged
|
||
|
|
if(profitPips >= 10 && currentSL < avgPrice)
|
||
|
|
{
|
||
|
|
double newSL = avgPrice + 2 * SymbolInfoDouble(Symbol(), SYMBOL_POINT) * 10;
|
||
|
|
ModifyPosition(ticket, NormalizeDouble(newSL, _Digits), currentTP);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Trail at structure when +40 pips
|
||
|
|
if(profitPips >= 40)
|
||
|
|
{
|
||
|
|
double newSL = GetStructureLevel(PERIOD_M15);
|
||
|
|
double buffer = 3 * SymbolInfoDouble(Symbol(), SYMBOL_POINT) * 10;
|
||
|
|
|
||
|
|
if(newSL > currentSL)
|
||
|
|
ModifyPosition(ticket, NormalizeDouble(newSL - buffer, _Digits), currentTP);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| GET STRUCTURE LEVEL |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
double GetStructureLevel(ENUM_TIMEFRAMES tf)
|
||
|
|
{
|
||
|
|
int lowest = iLowest(Symbol(), tf, MODE_LOW, 10, 1);
|
||
|
|
return iLow(Symbol(), tf, lowest);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| EXECUTE TRADE |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool ExecuteTrade(int direction, double lots, double sl, double tp, string comment)
|
||
|
|
{
|
||
|
|
MqlTradeRequest request = {};
|
||
|
|
MqlTradeResult result = {};
|
||
|
|
|
||
|
|
double price = (direction == ORDER_TYPE_SELL) ?
|
||
|
|
SymbolInfoDouble(Symbol(), SYMBOL_BID) :
|
||
|
|
SymbolInfoDouble(Symbol(), SYMBOL_ASK);
|
||
|
|
|
||
|
|
request.action = TRADE_ACTION_DEAL;
|
||
|
|
request.symbol = Symbol();
|
||
|
|
request.volume = lots;
|
||
|
|
request.type = direction;
|
||
|
|
request.price = price;
|
||
|
|
request.sl = sl;
|
||
|
|
request.tp = tp;
|
||
|
|
request.deviation = 10;
|
||
|
|
request.magic = 123456;
|
||
|
|
request.comment = comment;
|
||
|
|
|
||
|
|
if(!OrderSend(request, result))
|
||
|
|
{
|
||
|
|
Print("OrderSend failed: ", GetLastError());
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| MODIFY POSITION |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool ModifyPosition(ulong ticket, double sl, double tp)
|
||
|
|
{
|
||
|
|
MqlTradeRequest request = {};
|
||
|
|
MqlTradeResult result = {};
|
||
|
|
|
||
|
|
request.action = TRADE_ACTION_SLTP;
|
||
|
|
request.position = ticket;
|
||
|
|
request.symbol = Symbol();
|
||
|
|
request.sl = sl;
|
||
|
|
request.tp = tp;
|
||
|
|
|
||
|
|
return OrderSend(request, result);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| CHECK IF HAS OPEN POSITION |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool HasOpenPosition(string keyword)
|
||
|
|
{
|
||
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||
|
|
{
|
||
|
|
ulong ticket = PositionGetTicket(i);
|
||
|
|
if(ticket <= 0) continue;
|
||
|
|
if(PositionGetString(POSITION_SYMBOL) != Symbol()) continue;
|
||
|
|
|
||
|
|
string comment = PositionGetString(POSITION_COMMENT);
|
||
|
|
if(StringFind(comment, keyword) != -1) return true;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| RESET SETUP |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void ResetToPhaseNone()
|
||
|
|
{
|
||
|
|
currentPhase = PHASE_NONE;
|
||
|
|
activeSetup.swingHigh = 0;
|
||
|
|
activeSetup.swingLow = 0;
|
||
|
|
activeSetup.fib1278Level = 0;
|
||
|
|
activeSetup.entry1278Price = 0;
|
||
|
|
activeSetup.newSwingHigh = 0;
|
||
|
|
activeSetup.swingEstablished = false;
|
||
|
|
Print("Setup reset - hunting for new 127.8% opportunity");
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|