Vizion-Trading-EA/Experts/cashcow.mq5

567 lines
39 KiB
MQL5
Raw Permalink Normal View History

<EFBFBD><EFBFBD>//+------------------------------------------------------------------+
//| cashcow.mq5 (VIZION MASTER EA - HUD + Aggressive Re-Entry) |
//| - No candle objects (no lag). One HUD label top-right. |
//| - Trend confirm: EMA7 cross EMA50 (lookback) + stack agree |
//| - Entry core: EMA14 reclaim after confirm |
//| - Aggressive: re-enter on NEW confirmations (pullbacks/retests) |
//| - Close rules: trend flip OR MFIB rejection |
//| - Risk: 300 points SL + trailing stop |
//+------------------------------------------------------------------+
#property strict
#property version "1.20"
#include <Trade/Trade.mqh>
CTrade trade;
//============================== INPUTS ==============================
input ENUM_TIMEFRAMES InpTF = PERIOD_H1;
// MA periods
input int InpEMA7 = 7;
input int InpEMA14 = 14;
input int InpEMA21 = 21;
input int InpEMA50 = 50;
input int InpEMA140 = 140;
input int InpEMA230 = 230;
input int InpEMA500 = 500;
input int InpEMA1400 = 1400;
// Aggression
input int InpBatchOrders = 4; // 4 trades per entry
input double InpLotsPerOrder = 0.01;
input int InpMaxBatchesPerTrendLeg = 6; // cap to avoid runaway stacking
input int InpMinBarsBetweenEntries = 1; // 1 = can enter each new bar
input int InpReentryCooldownBars = 0; // extra cooldown if needed (0 = off)
// Confirmation logic
input int InpTouchTolPoints = 35;
input int InpCrossLookbackBars = 18;
input bool InpRequireMicroStack = true; // 7>21>50 or 7<21<50
input bool InpEnterOn14Reclaim = true; // main entry preference
input bool InpAllowReentryOn50Reclaim = true;
input bool InpAllowReentryOn140Reclaim = true;
input bool InpAllowReentryOn230Reclaim = true;
// Risk / management
input int InpSL_Points = 300;
input bool InpUseTrailingStop = true;
input int InpTrailStartPoints = 250;
input int InpTrailDistancePoints = 200;
input bool InpCloseOppOnTrendFlip = true;
// MFIB
input bool InpUseMFIBBiasFilter = true;
input int InpMFIBLookbackBars = 2500;
input bool InpUseCloseForMFIBBreaks = true;
// HUD
input bool InpShowHUD = true;
//============================== COLORS ==============================
color ColHud = clrWhite;
//============================== HANDLES =============================
int h7=-1,h14=-1,h21=-1,h50=-1,h140=-1,h230=-1,h500=-1,h1400=-1;
//============================== BUFFERS =============================
double ema7[], ema14[], ema21[], ema50[], ema140[], ema230[], ema500[], ema1400[];
MqlRates rates[];
//============================== STATE ===============================
string HUD_NAME = "VIZION_HUD";
datetime g_lastBar = 0;
int g_lastEntryShift = 999999; // bars since last entry
datetime g_lastEntryBarTime = 0; // bar time of last entry
int g_batchesThisLeg = 0; // how many batches pressed in current trend leg
int g_lastSetupId = 0; // prevents duplicates (same setup repeated)
int g_lastDirection = 0; // +1 long, -1 short, 0 none
//============================== UTILS ===============================
bool SafeCopyBuffer(const int handle, const int buffer, const int start_pos, const int count, double &arr[])
{
if(handle <= 0) return false;
ArrayResize(arr, count);
ArraySetAsSeries(arr, true);
int copied = CopyBuffer(handle, buffer, start_pos, count, arr);
if(copied <= 0) return false;
if(copied < count) ArrayResize(arr, copied);
return true;
}
void EnsureHUD()
{
if(!InpShowHUD) return;
if(ObjectFind(0, HUD_NAME) < 0)
{
ObjectCreate(0, HUD_NAME, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, HUD_NAME, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
ObjectSetInteger(0, HUD_NAME, OBJPROP_XDISTANCE, 10);
ObjectSetInteger(0, HUD_NAME, OBJPROP_YDISTANCE, 12);
ObjectSetInteger(0, HUD_NAME, OBJPROP_FONTSIZE, 10);
ObjectSetString(0, HUD_NAME, OBJPROP_FONT, "Consolas");
ObjectSetInteger(0, HUD_NAME, OBJPROP_COLOR, ColHud);
}
}
void SetHUD(const string txt)
{
if(!InpShowHUD) return;
EnsureHUD();
ObjectSetString(0, HUD_NAME, OBJPROP_TEXT, txt);
}
int CountPositionsByType(const ENUM_POSITION_TYPE type)
{
int n = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0) continue;
if(!PositionSelectByTicket(ticket)) continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
ENUM_POSITION_TYPE t = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if(t == type) n++;
}
return n;
}
void CloseAllType(const ENUM_POSITION_TYPE type, const string reason)
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0) continue;
if(!PositionSelectByTicket(ticket)) continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
ENUM_POSITION_TYPE t = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if(t != type) continue;
trade.PositionClose(ticket);
Print("Closed ", (type == POSITION_TYPE_BUY ? "BUY" : "SELL"), " due to: ", reason);
}
}
void ApplyTrailing()
{
if(!InpUseTrailingStop) return;
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0) continue;
if(!PositionSelectByTicket(ticket)) continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double sl = PositionGetDouble(POSITION_SL);
double tp = PositionGetDouble(POSITION_TP);
double profitPts = 0;
if(type == POSITION_TYPE_BUY) profitPts = (bid - openPrice) / _Point;
if(type == POSITION_TYPE_SELL) profitPts = (openPrice - ask) / _Point;
if(profitPts < InpTrailStartPoints) continue;
if(type == POSITION_TYPE_BUY)
{
double newSL = bid - InpTrailDistancePoints * _Point;
if(sl == 0.0 || newSL > sl)
trade.PositionModify(_Symbol, newSL, tp);
}
else
{
double newSL = ask + InpTrailDistancePoints * _Point;
if(sl == 0.0 || newSL < sl)
trade.PositionModify(_Symbol, newSL, tp);
}
}
}
//============================== MFIB ================================
bool ComputeMFIB(const int shift, const int lookback, double &atl, double &ath)
{
int last = MathMin(shift + lookback, ArraySize(rates)-1);
if(last <= shift) return false;
atl = rates[shift].low;
ath = rates[shift].high;
for(int k=shift; k<=last; k++)
{
if(rates[k].low < atl) atl = rates[k].low;
if(rates[k].high > ath) ath = rates[k].high;
}
return (ath > atl);
}
double FibLevel(const double atl, const double ath, const double r)
{
return atl + (ath - atl) * r;
}
int MFIBBiasState(const int shift, int &stackCount)
{
stackCount = 0;
double atl, ath;
if(!ComputeMFIB(shift, InpMFIBLookbackBars, atl, ath)) return 0;
double f032 = FibLevel(atl,ath,0.32);
double f050 = FibLevel(atl,ath,0.50);
double f0618= FibLevel(atl,ath,0.618);
double f0786= FibLevel(atl,ath,0.786);
double bp = rates[shift].close; // close-based for stability
if(bp > f032)
{
if(bp > f050) stackCount = 1;
if(bp > f0618) stackCount = 2;
if(bp > f0786) stackCount = 3;
return (stackCount>=1 ? +1 : 0);
}
if(bp < f032)
{
if(bp < f050) stackCount = 1;
if(bp < f0618) stackCount = 2;
if(bp < f0786) stackCount = 3;
return (stackCount>=1 ? -1 : 0);
}
return 0;
}
bool MFIBRejectHigh(const int shift, double &whichLevel)
{
whichLevel = 0.0;
double atl, ath;
if(!ComputeMFIB(shift, InpMFIBLookbackBars, atl, ath)) return false;
double f0618= FibLevel(atl,ath,0.618);
double f0786= FibLevel(atl,ath,0.786);
if(rates[shift].high >= f0786 && rates[shift].close < f0786) { whichLevel = 0.786; return true; }
if(rates[shift].high >= f0618 && rates[shift].close < f0618) { whichLevel = 0.618; return true; }
return false;
}
bool MFIBRejectLow(const int shift, double &whichLevel)
{
whichLevel = 0.0;
double atl, ath;
if(!ComputeMFIB(shift, InpMFIBLookbackBars, atl, ath)) return false;
double f0618= FibLevel(atl,ath,0.618);
double f0786= FibLevel(atl,ath,0.786);
if(rates[shift].low <= f0786 && rates[shift].close > f0786) { whichLevel = 0.786; return true; }
if(rates[shift].low <= f0618 && rates[shift].close > f0618) { whichLevel = 0.618; return true; }
return false;
}
//============================== TREND ===============================
bool CrossUp7_50(const int shift)
{
if(shift+1 >= ArraySize(ema7) || shift+1 >= ArraySize(ema50)) return false;
double prevDiff = ema7[shift+1] - ema50[shift+1];
double currDiff = ema7[shift] - ema50[shift];
return (prevDiff <= 0.0 && currDiff > 0.0);
}
bool CrossDn7_50(const int shift)
{
if(shift+1 >= ArraySize(ema7) || shift+1 >= ArraySize(ema50)) return false;
double prevDiff = ema7[shift+1] - ema50[shift+1];
double currDiff = ema7[shift] - ema50[shift];
return (prevDiff >= 0.0 && currDiff < 0.0);
}
bool CrossedWithin(const int shift, const int lookback, const bool bull)
{
int maxShift = MathMin(shift + lookback, ArraySize(ema7) - 2);
for(int k=shift; k<=maxShift; k++)
{
if(bull && CrossUp7_50(k)) return true;
if(!bull && CrossDn7_50(k)) return true;
}
return false;
}
bool StackLong(const int shift) { return (ema7[shift] > ema21[shift] && ema21[shift] > ema50[shift]); }
bool StackShort(const int shift) { return (ema7[shift] < ema21[shift] && ema21[shift] < ema50[shift]); }
bool TouchReclaimMA_Long(const int shift, const double ma)
{
double tol = InpTouchTolPoints * _Point;
return (rates[shift].low <= ma + tol && rates[shift].close > ma);
}
bool TouchReclaimMA_Short(const int shift, const double ma)
{
double tol = InpTouchTolPoints * _Point;
return (rates[shift].high >= ma - tol && rates[shift].close < ma);
}
//============================== ORDERS ==============================
void PlaceBatch(const bool isBuy)
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double entry = isBuy ? ask : bid;
double sl = isBuy ? entry - InpSL_Points * _Point
: entry + InpSL_Points * _Point;
trade.SetDeviationInPoints(30);
for(int k=0; k<InpBatchOrders; k++)
{
bool ok = isBuy
? trade.Buy(InpLotsPerOrder, _Symbol, 0.0, sl, 0.0, "VIZION_BATCH")
: trade.Sell(InpLotsPerOrder, _Symbol, 0.0, sl, 0.0, "VIZION_BATCH");
if(!ok) Print("Order failed: ", GetLastError());
}
}
//============================== SETUP IDs ===========================
// Setup IDs prevent duplicate re-entries on the same confirmation.
enum SETUP_ID
{
SETUP_NONE = 0,
SETUP_ENTRY_14 = 1,
SETUP_REENTRY_50 = 2,
SETUP_REENTRY_140 = 3,
SETUP_REENTRY_230 = 4
};
//============================== INIT ================================
int OnInit()
{
h7 = iMA(_Symbol, InpTF, InpEMA7, 0, MODE_EMA, PRICE_CLOSE);
h14 = iMA(_Symbol, InpTF, InpEMA14, 0, MODE_EMA, PRICE_CLOSE);
h21 = iMA(_Symbol, InpTF, InpEMA21, 0, MODE_EMA, PRICE_CLOSE);
h50 = iMA(_Symbol, InpTF, InpEMA50, 0, MODE_EMA, PRICE_CLOSE);
h140 = iMA(_Symbol, InpTF, InpEMA140, 0, MODE_EMA, PRICE_CLOSE);
h230 = iMA(_Symbol, InpTF, InpEMA230, 0, MODE_EMA, PRICE_CLOSE);
h500 = iMA(_Symbol, InpTF, InpEMA500, 0, MODE_EMA, PRICE_CLOSE);
h1400 = iMA(_Symbol, InpTF, InpEMA1400, 0, MODE_EMA, PRICE_CLOSE);
if(h7<0 || h14<0 || h21<0 || h50<0 || h140<0 || h230<0 || h500<0 || h1400<0)
return(INIT_FAILED);
EnsureHUD();
return(INIT_SUCCEEDED);
}
//============================== TICK ================================
void OnTick()
{
// always manage trailing
ApplyTrailing();
// Run on new bar only (keeps it fast + consistent)
datetime curBar = iTime(_Symbol, InpTF, 0);
if(curBar == g_lastBar) return;
g_lastBar = curBar;
// Pull enough bars
int need = MathMax(InpEMA1400 + 50, InpMFIBLookbackBars + 50);
need = MathMin(need, 6000);
ArrayResize(rates, need);
ArraySetAsSeries(rates, true);
int gotRates = CopyRates(_Symbol, InpTF, 0, need, rates);
if(gotRates < 500) return;
int rates_total = gotRates;
// Copy buffers safely (no out-of-range)
if(!SafeCopyBuffer(h7,0,0,rates_total,ema7)) return;
if(!SafeCopyBuffer(h14,0,0,rates_total,ema14)) return;
if(!SafeCopyBuffer(h21,0,0,rates_total,ema21)) return;
if(!SafeCopyBuffer(h50,0,0,rates_total,ema50)) return;
if(!SafeCopyBuffer(h140,0,0,rates_total,ema140)) return;
if(!SafeCopyBuffer(h230,0,0,rates_total,ema230)) return;
if(!SafeCopyBuffer(h500,0,0,rates_total,ema500)) return;
if(!SafeCopyBuffer(h1400,0,0,rates_total,ema1400)) return;
// Use last closed bar
int i = 1;
//========================
// 1) Reversal management
//========================
bool flipBull = CrossUp7_50(i);
bool flipBear = CrossDn7_50(i);
if(InpCloseOppOnTrendFlip)
{
if(flipBull && CountPositionsByType(POSITION_TYPE_SELL) > 0)
{
CloseAllType(POSITION_TYPE_SELL, "TREND FLIP BULL (7>50)");
g_batchesThisLeg = 0;
g_lastSetupId = SETUP_NONE;
g_lastDirection = 0;
}
if(flipBear && CountPositionsByType(POSITION_TYPE_BUY) > 0)
{
CloseAllType(POSITION_TYPE_BUY, "TREND FLIP BEAR (7<50)");
g_batchesThisLeg = 0;
g_lastSetupId = SETUP_NONE;
g_lastDirection = 0;
}
}
// MFIB rejection closes
double rej=0.0;
if(CountPositionsByType(POSITION_TYPE_BUY) > 0 && MFIBRejectHigh(i, rej))
{
CloseAllType(POSITION_TYPE_BUY, "MFIB REJECT HIGH @ "+DoubleToString(rej,3));
g_batchesThisLeg = 0; g_lastSetupId = SETUP_NONE; g_lastDirection = 0;
}
if(CountPositionsByType(POSITION_TYPE_SELL) > 0 && MFIBRejectLow(i, rej))
{
CloseAllType(POSITION_TYPE_SELL, "MFIB REJECT LOW @ "+DoubleToString(rej,3));
g_batchesThisLeg = 0; g_lastSetupId = SETUP_NONE; g_lastDirection = 0;
}
//========================
// 2) Trend permission
//========================
bool longPerm = CrossedWithin(i, InpCrossLookbackBars, true);
bool shortPerm = CrossedWithin(i, InpCrossLookbackBars, false);
if(InpRequireMicroStack)
{
longPerm = longPerm && StackLong(i);
shortPerm = shortPerm && StackShort(i);
}
// MFIB bias permission
int stackCount=0;
int mfibBias = MFIBBiasState(i, stackCount);
if(InpUseMFIBBiasFilter)
{
if(longPerm && !(mfibBias > 0 && stackCount >= 1)) longPerm = false;
if(shortPerm && !(mfibBias < 0 && stackCount >= 1)) shortPerm = false;
}
//========================
// 3) Aggressive re-entry gating
//========================
// time-based gate
if(g_lastEntryBarTime != 0)
{
int barsSince = 0;
// compute bars since last entry using bar times (simple)
for(int k=1; k<MathMin(500, gotRates); k++)
{
if(rates[k].time == g_lastEntryBarTime) { barsSince = k-1; break; }
}
if(barsSince < InpMinBarsBetweenEntries) { SetHUD("Cooldown: waiting bars"); return; }
if(InpReentryCooldownBars > 0 && barsSince < InpReentryCooldownBars) { SetHUD("Reentry cooldown"); return; }
}
// batch cap per leg
if(g_batchesThisLeg >= InpMaxBatchesPerTrendLeg)
{
SetHUD("Max batches reached for this trend leg");
return;
}
//========================
// 4) Setup detection (pick ONE best setup per bar)
//========================
int setupId = SETUP_NONE;
int dir = 0;
string setupName = "NONE";
// Primary entry on EMA14 reclaim after confirm
if(longPerm && InpEnterOn14Reclaim && TouchReclaimMA_Long(i, ema14[i]))
{
setupId = SETUP_ENTRY_14; dir = +1; setupName = "ENTRY_14_RECLAIM";
}
else if(shortPerm && InpEnterOn14Reclaim && TouchReclaimMA_Short(i, ema14[i]))
{
setupId = SETUP_ENTRY_14; dir = -1; setupName = "ENTRY_14_RECLAIM";
}
// Aggressive continuation re-entries (only if trend confirmed)
else if(InpAllowReentryOn50Reclaim && longPerm && TouchReclaimMA_Long(i, ema50[i]))
{
setupId = SETUP_REENTRY_50; dir = +1; setupName = "REENTRY_50_RECLAIM";
}
else if(InpAllowReentryOn50Reclaim && shortPerm && TouchReclaimMA_Short(i, ema50[i]))
{
setupId = SETUP_REENTRY_50; dir = -1; setupName = "REENTRY_50_RECLAIM";
}
else if(InpAllowReentryOn140Reclaim && longPerm && TouchReclaimMA_Long(i, ema140[i]))
{
setupId = SETUP_REENTRY_140; dir = +1; setupName = "REENTRY_140_RECLAIM";
}
else if(InpAllowReentryOn140Reclaim && shortPerm && TouchReclaimMA_Short(i, ema140[i]))
{
setupId = SETUP_REENTRY_140; dir = -1; setupName = "REENTRY_140_RECLAIM";
}
else if(InpAllowReentryOn230Reclaim && longPerm && TouchReclaimMA_Long(i, ema230[i]))
{
setupId = SETUP_REENTRY_230; dir = +1; setupName = "REENTRY_230_RECLAIM";
}
else if(InpAllowReentryOn230Reclaim && shortPerm && TouchReclaimMA_Short(i, ema230[i]))
{
setupId = SETUP_REENTRY_230; dir = -1; setupName = "REENTRY_230_RECLAIM";
}
// Duplicate prevention: same setup twice in a row, same direction <EFBFBD>! skip
if(setupId != SETUP_NONE)
{
if(g_lastSetupId == setupId && g_lastDirection == dir)
{
SetHUD("Duplicate setup blocked: " + setupName);
return;
}
}
//========================
// 5) Execute
//========================
if(setupId != SETUP_NONE)
{
// If we already have opposite positions (should be rare due to flip close), clean them.
if(dir > 0 && CountPositionsByType(POSITION_TYPE_SELL) > 0 && InpCloseOppOnTrendFlip)
CloseAllType(POSITION_TYPE_SELL, "Opposite cleanup before LONG");
if(dir < 0 && CountPositionsByType(POSITION_TYPE_BUY) > 0 && InpCloseOppOnTrendFlip)
CloseAllType(POSITION_TYPE_BUY, "Opposite cleanup before SHORT");
PlaceBatch(dir > 0);
g_lastEntryBarTime = rates[i].time;
g_batchesThisLeg++;
g_lastSetupId = setupId;
g_lastDirection = dir;
}
//========================
// 6) HUD update
//========================
string biasTxt = (mfibBias>0 ? "MFIB_BULL" : (mfibBias<0 ? "MFIB_BEAR" : "MFIB_NEUTRAL"));
string permTxt = (longPerm ? "LONG_OK" : (shortPerm ? "SHORT_OK" : "NO_PERM"));
string hud =
"VIZION CASHCOW\n"
"TF: " + EnumToString(InpTF) + "\n"
"Perm: " + permTxt + "\n"
"MFIB: " + biasTxt + " | Stack:" + IntegerToString(stackCount) + "\n"
"Setup: " + setupName + "\n"
"BatchesThisLeg: " + IntegerToString(g_batchesThisLeg) + "/" + IntegerToString(InpMaxBatchesPerTrendLeg);
SetHUD(hud);
}
//+------------------------------------------------------------------+