468 lines
30 KiB
MQL5
468 lines
30 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| AggressiveMA_Retest_TP4000.mq5 |
|
|
//| MA7 crosses >=2 MAs = trend; re-enter on MA retests; |
|
|
//| TP >= 4000 points + MA "magnet" partial TPs + Stochastic confirm |
|
|
//+------------------------------------------------------------------+
|
|
#property strict
|
|
|
|
#include <Trade/Trade.mqh>
|
|
CTrade Trade;
|
|
|
|
// -------------------- INPUTS --------------------
|
|
input ulong MagicNumber = 777001;
|
|
input double Lots = 0.01;
|
|
input int SlippagePoints = 50;
|
|
|
|
// MAs
|
|
input int MA_7 = 7;
|
|
input int MA_14 = 14;
|
|
input int MA_21 = 21;
|
|
input int MA_50 = 50;
|
|
input int MA_140 = 140;
|
|
input int MA_230 = 230;
|
|
input int MA_1000= 1000;
|
|
input int MA_1100= 1100;
|
|
input int MA_1300= 1300;
|
|
|
|
input ENUM_MA_METHOD MA_Method = MODE_EMA;
|
|
input ENUM_APPLIED_PRICE MA_Price = PRICE_CLOSE;
|
|
|
|
// Entry aggressiveness
|
|
input int RetestBufferPoints = 250; // how close price must get to MA to count as "retest"
|
|
input int CooldownBarsPerEntry = 1; // prevent same-bar spam entries
|
|
|
|
// Stochastic confirm (instead of TRIX)
|
|
input int StochK = 5;
|
|
input int StochD = 3;
|
|
input int StochSlowing = 3;
|
|
input ENUM_MA_METHOD StochMethod = MODE_SMA;
|
|
input ENUM_STO_PRICE StochPrice = STO_LOWHIGH;
|
|
|
|
// Stoch quarter theory levels
|
|
input double StochLvl_1 = 25.0;
|
|
input double StochLvl_2 = 50.0;
|
|
input double StochLvl_3 = 75.0;
|
|
|
|
// PROFIT TAKING (what you asked for)
|
|
input int TP_MinPoints = 4000; // hard TP distance from entry (server-side)
|
|
input int BE_TriggerPoints = 4000; // optional: move SL to BE after this (set 0 to disable)
|
|
input int BE_LockPoints = 200; // lock a little profit after BE trigger
|
|
|
|
// Partial TP at profit thresholds (in addition to MA magnets)
|
|
input bool UseProfitPartials = true;
|
|
input int PT1_Points = 4000;
|
|
input double PT1_ClosePct = 0.35; // close 35% at +4000 points
|
|
input int PT2_Points = 8000;
|
|
input double PT2_ClosePct = 0.35; // close 35% at +8000 points
|
|
input int PT3_Points = 12000;
|
|
input double PT3_ClosePct = 0.30; // close remaining at +12000 points (or let TP handle it)
|
|
|
|
// MA Magnet partials
|
|
input bool UseMAMagnets = true;
|
|
input int MagnetBufferPoints = 250; // distance to MA to count as "hit"
|
|
input double MagnetClosePct = 0.20; // close 20% each time a new MA magnet is hit
|
|
|
|
// -------------------- HANDLES --------------------
|
|
int hMA7=-1,hMA14=-1,hMA21=-1,hMA50=-1,hMA140=-1,hMA230=-1,hMA1000=-1,hMA1100=-1,hMA1300=-1;
|
|
int hStoch=-1;
|
|
|
|
// -------------------- STATE --------------------
|
|
datetime lastEntryBarTime=0;
|
|
|
|
// -------------------- HELPERS --------------------
|
|
double MidPrice()
|
|
{
|
|
double b=SymbolInfoDouble(_Symbol,SYMBOL_BID);
|
|
double a=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
|
|
if(b<=0 || a<=0) return 0;
|
|
return 0.5*(a+b);
|
|
}
|
|
|
|
bool Copy1(int handle, int shift, double &out)
|
|
{
|
|
double buf[];
|
|
ArraySetAsSeries(buf,true);
|
|
if(CopyBuffer(handle,0,shift,1,buf)!=1) return false;
|
|
out = buf[0];
|
|
return true;
|
|
}
|
|
|
|
bool CopyStoch(int shift, double &k, double &d)
|
|
{
|
|
double bk[], bd[];
|
|
ArraySetAsSeries(bk,true);
|
|
ArraySetAsSeries(bd,true);
|
|
// Stoch main is buffer 0, signal is buffer 1 in MQL5
|
|
if(CopyBuffer(hStoch,0,shift,1,bk)!=1) return false;
|
|
if(CopyBuffer(hStoch,1,shift,1,bd)!=1) return false;
|
|
k=bk[0]; d=bd[0];
|
|
return true;
|
|
}
|
|
|
|
bool GetMAValues(int shift,
|
|
double &ma7,double &ma14,double &ma21,double &ma50,double &ma140,double &ma230,
|
|
double &ma1000,double &ma1100,double &ma1300)
|
|
{
|
|
if(!Copy1(hMA7,shift,ma7)) return false;
|
|
if(!Copy1(hMA14,shift,ma14)) return false;
|
|
if(!Copy1(hMA21,shift,ma21)) return false;
|
|
if(!Copy1(hMA50,shift,ma50)) return false;
|
|
if(!Copy1(hMA140,shift,ma140)) return false;
|
|
if(!Copy1(hMA230,shift,ma230)) return false;
|
|
if(!Copy1(hMA1000,shift,ma1000)) return false;
|
|
if(!Copy1(hMA1100,shift,ma1100)) return false;
|
|
if(!Copy1(hMA1300,shift,ma1300)) return false;
|
|
return true;
|
|
}
|
|
|
|
// Trend direction:
|
|
// +1 bullish if MA7 above BOTH MA14 & MA21 (and preferably above others)
|
|
// -1 bearish if MA7 below BOTH MA14 & MA21
|
|
int TrendDir(int shift, int &strengthOut)
|
|
{
|
|
strengthOut=0;
|
|
|
|
double ma7,ma14,ma21,ma50,ma140,ma230,ma1000,ma1100,ma1300;
|
|
if(!GetMAValues(shift,ma7,ma14,ma21,ma50,ma140,ma230,ma1000,ma1100,ma1300))
|
|
return 0;
|
|
|
|
double others[7] = {ma14,ma21,ma50,ma140,ma230,ma1000,ma1100};
|
|
int above=0, below=0;
|
|
for(int i=0;i<7;i++)
|
|
{
|
|
if(ma7 > others[i]) above++;
|
|
if(ma7 < others[i]) below++;
|
|
}
|
|
|
|
bool bullConfirmed = (ma7 > ma14 && ma7 > ma21 && above>=2);
|
|
bool bearConfirmed = (ma7 < ma14 && ma7 < ma21 && below>=2);
|
|
|
|
if(bullConfirmed){ strengthOut=above; return +1; }
|
|
if(bearConfirmed){ strengthOut=below; return -1; }
|
|
return 0;
|
|
}
|
|
|
|
bool IsNewBar()
|
|
{
|
|
datetime t=iTime(_Symbol,PERIOD_CURRENT,0);
|
|
static datetime prev=0;
|
|
if(t!=prev){ prev=t; return true; }
|
|
return false;
|
|
}
|
|
|
|
bool InCooldown()
|
|
{
|
|
datetime t=iTime(_Symbol,PERIOD_CURRENT,0);
|
|
if(t==lastEntryBarTime) return true;
|
|
return false;
|
|
}
|
|
|
|
int CountMyPositions()
|
|
{
|
|
int c=0;
|
|
int total=(int)PositionsTotal();
|
|
for(int i=0;i<total;i++)
|
|
{
|
|
ulong ticket=PositionGetTicket(i);
|
|
if(ticket==0) continue;
|
|
if(!PositionSelectByTicket(ticket)) continue;
|
|
if((ulong)PositionGetInteger(POSITION_MAGIC)!=MagicNumber) continue;
|
|
if(PositionGetString(POSITION_SYMBOL)!=_Symbol) continue;
|
|
c++;
|
|
}
|
|
return c;
|
|
}
|
|
|
|
double ProfitPointsForPosition()
|
|
{
|
|
// assumes PositionSelectByTicket already done
|
|
long type = PositionGetInteger(POSITION_TYPE);
|
|
double open=PositionGetDouble(POSITION_PRICE_OPEN);
|
|
double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
|
|
double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
|
|
|
|
if(type==POSITION_TYPE_BUY)
|
|
return (bid-open)/_Point;
|
|
else
|
|
return (open-ask)/_Point;
|
|
}
|
|
|
|
bool PriceNear(double price, double level, int bufferPoints)
|
|
{
|
|
return (MathAbs(price-level) <= bufferPoints*_Point);
|
|
}
|
|
|
|
// One-time trigger key for magnet hits
|
|
string MagnetKey(ulong ticket, int maPeriod)
|
|
{
|
|
return "MAG_"+_Symbol+"_"+(string)MagicNumber+"_"+(string)ticket+"_"+(string)maPeriod;
|
|
}
|
|
|
|
// Close partial safely
|
|
bool ClosePartial(ulong ticket, double closePct)
|
|
{
|
|
if(closePct<=0) return false;
|
|
if(!PositionSelectByTicket(ticket)) return false;
|
|
|
|
double vol=PositionGetDouble(POSITION_VOLUME);
|
|
double minv=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
|
|
double step=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
|
|
|
|
double closeVol = vol * closePct;
|
|
// normalize to step
|
|
closeVol = MathFloor(closeVol/step)*step;
|
|
if(closeVol < minv) return false;
|
|
if(closeVol > vol) closeVol = vol;
|
|
|
|
Trade.SetDeviationInPoints(SlippagePoints);
|
|
return Trade.PositionClosePartial(ticket, closeVol);
|
|
}
|
|
|
|
void MoveSLToBE(ulong ticket, int lockPoints)
|
|
{
|
|
if(!PositionSelectByTicket(ticket)) return;
|
|
|
|
long type = PositionGetInteger(POSITION_TYPE);
|
|
double open=PositionGetDouble(POSITION_PRICE_OPEN);
|
|
double sl =PositionGetDouble(POSITION_SL);
|
|
double tp =PositionGetDouble(POSITION_TP);
|
|
|
|
double newSL=sl;
|
|
|
|
if(type==POSITION_TYPE_BUY)
|
|
newSL = open + lockPoints*_Point;
|
|
else
|
|
newSL = open - lockPoints*_Point;
|
|
|
|
// only improve SL
|
|
if(type==POSITION_TYPE_BUY && (sl<=0 || newSL>sl))
|
|
Trade.PositionModify(ticket,newSL,tp);
|
|
|
|
if(type==POSITION_TYPE_SELL && (sl<=0 || newSL<sl))
|
|
Trade.PositionModify(ticket,newSL,tp);
|
|
}
|
|
|
|
// Stochastic quarter confirmation:
|
|
// bullish bias if K > 50, stronger if > 75
|
|
// bearish bias if K < 50, stronger if < 25
|
|
bool StochConfirms(int dir, int shift)
|
|
{
|
|
double k,d;
|
|
if(!CopyStoch(shift,k,d)) return true; // if no data, don't block trades
|
|
|
|
if(dir>0)
|
|
return (k >= StochLvl_2); // >=50
|
|
if(dir<0)
|
|
return (k <= StochLvl_2); // <=50
|
|
return false;
|
|
}
|
|
|
|
// Entry trigger:
|
|
// 1) TrendDir says bull/bear (MA7 crossed 2+ MAs)
|
|
// 2) price retests MA7 or any crossed MA (we’ll prioritize MA7 retest)
|
|
// 3) stochastic confirms
|
|
bool EntrySignal(int &dirOut)
|
|
{
|
|
int strength=0;
|
|
int dir = TrendDir(1,strength);
|
|
if(dir==0) return false;
|
|
|
|
if(!StochConfirms(dir,1)) return false;
|
|
|
|
double ma7,ma14,ma21,ma50,ma140,ma230,ma1000,ma1100,ma1300;
|
|
if(!GetMAValues(0,ma7,ma14,ma21,ma50,ma140,ma230,ma1000,ma1100,ma1300))
|
|
return false;
|
|
|
|
double price = MidPrice();
|
|
if(price<=0) return false;
|
|
|
|
// aggressive: any retest near MA7 OR near any MA that MA7 is on the “trend side” of
|
|
bool near7 = PriceNear(price, ma7, RetestBufferPoints);
|
|
|
|
// “crossed” MAs are those that MA7 is above (bull) or below (bear)
|
|
bool nearOther=false;
|
|
if(dir>0)
|
|
{
|
|
if(ma7>ma14 && PriceNear(price,ma14,RetestBufferPoints)) nearOther=true;
|
|
if(ma7>ma21 && PriceNear(price,ma21,RetestBufferPoints)) nearOther=true;
|
|
if(ma7>ma50 && PriceNear(price,ma50,RetestBufferPoints)) nearOther=true;
|
|
if(ma7>ma140&& PriceNear(price,ma140,RetestBufferPoints)) nearOther=true;
|
|
if(ma7>ma230&& PriceNear(price,ma230,RetestBufferPoints)) nearOther=true;
|
|
}
|
|
else
|
|
{
|
|
if(ma7<ma14 && PriceNear(price,ma14,RetestBufferPoints)) nearOther=true;
|
|
if(ma7<ma21 && PriceNear(price,ma21,RetestBufferPoints)) nearOther=true;
|
|
if(ma7<ma50 && PriceNear(price,ma50,RetestBufferPoints)) nearOther=true;
|
|
if(ma7<ma140&& PriceNear(price,ma140,RetestBufferPoints)) nearOther=true;
|
|
if(ma7<ma230&& PriceNear(price,ma230,RetestBufferPoints)) nearOther=true;
|
|
}
|
|
|
|
if(!(near7 || nearOther)) return false;
|
|
|
|
dirOut=dir;
|
|
return true;
|
|
}
|
|
|
|
bool OpenTrade(int dir)
|
|
{
|
|
Trade.SetExpertMagicNumber((uint)MagicNumber);
|
|
Trade.SetDeviationInPoints(SlippagePoints);
|
|
|
|
double ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
|
|
double bid=SymbolInfoDouble(_Symbol,SYMBOL_BID);
|
|
if(ask<=0 || bid<=0) return false;
|
|
|
|
double tp=0.0;
|
|
// hard TP distance >= 4000 points
|
|
if(TP_MinPoints>0)
|
|
{
|
|
if(dir>0) tp = ask + TP_MinPoints*_Point;
|
|
else tp = bid - TP_MinPoints*_Point;
|
|
}
|
|
|
|
bool ok=false;
|
|
if(dir>0) ok = Trade.Buy(Lots,_Symbol,ask,0,tp,"MA7_retest_buy");
|
|
else ok = Trade.Sell(Lots,_Symbol,bid,0,tp,"MA7_retest_sell");
|
|
|
|
if(ok)
|
|
lastEntryBarTime = iTime(_Symbol,PERIOD_CURRENT,0);
|
|
|
|
return ok;
|
|
}
|
|
|
|
void ManagePositions()
|
|
{
|
|
int total=(int)PositionsTotal();
|
|
for(int i=0;i<total;i++)
|
|
{
|
|
ulong ticket=PositionGetTicket(i);
|
|
if(ticket==0) continue;
|
|
if(!PositionSelectByTicket(ticket)) continue;
|
|
|
|
if((ulong)PositionGetInteger(POSITION_MAGIC)!=MagicNumber) continue;
|
|
if(PositionGetString(POSITION_SYMBOL)!=_Symbol) continue;
|
|
|
|
long type = PositionGetInteger(POSITION_TYPE);
|
|
double price = (type==POSITION_TYPE_BUY) ? SymbolInfoDouble(_Symbol,SYMBOL_BID)
|
|
: SymbolInfoDouble(_Symbol,SYMBOL_ASK);
|
|
|
|
double profitPts = ProfitPointsForPosition();
|
|
if(profitPts <= 0) continue;
|
|
|
|
// ---- PROFIT PARTIALS ----
|
|
if(UseProfitPartials)
|
|
{
|
|
// Use GlobalVariables so each threshold triggers only once per ticket
|
|
string k1="PT1_"+(string)ticket, k2="PT2_"+(string)ticket, k3="PT3_"+(string)ticket;
|
|
|
|
if(PT1_Points>0 && profitPts>=PT1_Points && !GlobalVariableCheck(k1))
|
|
{
|
|
ClosePartial(ticket, PT1_ClosePct);
|
|
GlobalVariableSet(k1, TimeCurrent());
|
|
}
|
|
if(PT2_Points>0 && profitPts>=PT2_Points && !GlobalVariableCheck(k2))
|
|
{
|
|
ClosePartial(ticket, PT2_ClosePct);
|
|
GlobalVariableSet(k2, TimeCurrent());
|
|
}
|
|
if(PT3_Points>0 && profitPts>=PT3_Points && !GlobalVariableCheck(k3))
|
|
{
|
|
ClosePartial(ticket, PT3_ClosePct);
|
|
GlobalVariableSet(k3, TimeCurrent());
|
|
}
|
|
}
|
|
|
|
// ---- MOVE SL TO BE AFTER +4000 (optional) ----
|
|
if(BE_TriggerPoints>0 && profitPts>=BE_TriggerPoints)
|
|
{
|
|
string bek="BE_"+(string)ticket;
|
|
if(!GlobalVariableCheck(bek))
|
|
{
|
|
MoveSLToBE(ticket, BE_LockPoints);
|
|
GlobalVariableSet(bek, TimeCurrent());
|
|
}
|
|
}
|
|
|
|
// ---- MA MAGNET PARTIAL TPs ----
|
|
if(UseMAMagnets)
|
|
{
|
|
double ma7,ma14,ma21,ma50,ma140,ma230,ma1000,ma1100,ma1300;
|
|
if(!GetMAValues(0,ma7,ma14,ma21,ma50,ma140,ma230,ma1000,ma1100,ma1300))
|
|
continue;
|
|
|
|
// target the "big magnets" first
|
|
struct MMag { int p; double v; };
|
|
MMag mags[6] = {
|
|
{MA_50, ma50}, {MA_140, ma140}, {MA_230, ma230},
|
|
{MA_1000, ma1000}, {MA_1100, ma1100}, {MA_1300, ma1300}
|
|
};
|
|
|
|
for(int m=0;m<6;m++)
|
|
{
|
|
string key = MagnetKey(ticket, mags[m].p);
|
|
if(GlobalVariableCheck(key)) continue;
|
|
|
|
if(PriceNear(price, mags[m].v, MagnetBufferPoints))
|
|
{
|
|
// close a chunk once per MA per ticket
|
|
ClosePartial(ticket, MagnetClosePct);
|
|
GlobalVariableSet(key, TimeCurrent());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// -------------------- MQL5 EVENTS --------------------
|
|
int OnInit()
|
|
{
|
|
Trade.SetExpertMagicNumber((uint)MagicNumber);
|
|
|
|
hMA7 = iMA(_Symbol,PERIOD_CURRENT,MA_7,0,MA_Method,MA_Price);
|
|
hMA14 = iMA(_Symbol,PERIOD_CURRENT,MA_14,0,MA_Method,MA_Price);
|
|
hMA21 = iMA(_Symbol,PERIOD_CURRENT,MA_21,0,MA_Method,MA_Price);
|
|
hMA50 = iMA(_Symbol,PERIOD_CURRENT,MA_50,0,MA_Method,MA_Price);
|
|
hMA140 = iMA(_Symbol,PERIOD_CURRENT,MA_140,0,MA_Method,MA_Price);
|
|
hMA230 = iMA(_Symbol,PERIOD_CURRENT,MA_230,0,MA_Method,MA_Price);
|
|
hMA1000 = iMA(_Symbol,PERIOD_CURRENT,MA_1000,0,MA_Method,MA_Price);
|
|
hMA1100 = iMA(_Symbol,PERIOD_CURRENT,MA_1100,0,MA_Method,MA_Price);
|
|
hMA1300 = iMA(_Symbol,PERIOD_CURRENT,MA_1300,0,MA_Method,MA_Price);
|
|
|
|
hStoch = iStochastic(_Symbol,PERIOD_CURRENT,StochK,StochD,StochSlowing,StochMethod,StochPrice);
|
|
|
|
if(hMA7<0 || hMA14<0 || hMA21<0 || hMA50<0 || hMA140<0 || hMA230<0 || hMA1000<0 || hMA1100<0 || hMA1300<0 || hStoch<0)
|
|
return INIT_FAILED;
|
|
|
|
return INIT_SUCCEEDED;
|
|
}
|
|
|
|
void OnDeinit(const int reason)
|
|
{
|
|
if(hMA7>0) IndicatorRelease(hMA7);
|
|
if(hMA14>0) IndicatorRelease(hMA14);
|
|
if(hMA21>0) IndicatorRelease(hMA21);
|
|
if(hMA50>0) IndicatorRelease(hMA50);
|
|
if(hMA140>0) IndicatorRelease(hMA140);
|
|
if(hMA230>0) IndicatorRelease(hMA230);
|
|
if(hMA1000>0) IndicatorRelease(hMA1000);
|
|
if(hMA1100>0) IndicatorRelease(hMA1100);
|
|
if(hMA1300>0) IndicatorRelease(hMA1300);
|
|
if(hStoch>0) IndicatorRelease(hStoch);
|
|
}
|
|
|
|
void OnTick()
|
|
{
|
|
// manage first (locks profit fast)
|
|
ManagePositions();
|
|
|
|
// entry logic
|
|
if(InCooldown()) return;
|
|
|
|
int dir=0;
|
|
if(EntrySignal(dir))
|
|
{
|
|
// aggressive: allow multiple positions, but you can cap if needed
|
|
OpenTrade(dir);
|
|
}
|
|
}
|