2026-04-26 23:38:40 +03:00
|
|
|
//buy below the open
|
|
|
|
|
//sell above the open
|
|
|
|
|
|
|
|
|
|
#include <Trade/Trade.mqh>
|
|
|
|
|
#include <Trade/SymbolInfo.mqh>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//struct tfCoord
|
|
|
|
|
// {
|
|
|
|
|
// ENUM_TIMEFRAMES xtf;
|
|
|
|
|
// ENUM_TIMEFRAMES ytf;
|
|
|
|
|
// ENUM_TIMEFRAMES ztf;
|
|
|
|
|
// };
|
|
|
|
|
//
|
|
|
|
|
//tfCoord tf;
|
|
|
|
|
|
|
|
|
|
input double BEFactor; //multiple of rr, that will acticate lock-in
|
|
|
|
|
|
|
|
|
|
//Timeframe Coordination
|
|
|
|
|
input ENUM_TIMEFRAMES InpXtf; //main starting tf
|
|
|
|
|
input ENUM_TIMEFRAMES inpYtf; // middle linking tf
|
|
|
|
|
input ENUM_TIMEFRAMES inpZtf; //execution tf
|
|
|
|
|
|
|
|
|
|
input double inpLot; //preferred position size
|
|
|
|
|
double FixedSizeRisk;
|
|
|
|
|
input int inpTickClearance; // number of ticks allowed to go beyond pre-defined sl
|
|
|
|
|
input int inpRR; //Risk to reward ratio;
|
|
|
|
|
input bool inpTrigger1; //activate recieval of triggering signal from model1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extern bool trigger(); //other complex logic
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Globals
|
|
|
|
|
bool isAbove;
|
|
|
|
|
bool isBelow;
|
|
|
|
|
double lockIn; //this for every open position
|
|
|
|
|
|
|
|
|
|
//int beyond; //-1 for below, 1 for above
|
|
|
|
|
|
|
|
|
|
bool isNewCandle;
|
|
|
|
|
bool isTradeDay;
|
|
|
|
|
int signalFlag;
|
2026-04-30 01:08:03 +03:00
|
|
|
static bool hasFired = false;
|
|
|
|
|
int buyCounts;
|
|
|
|
|
int sellCounts;
|
2026-04-26 23:38:40 +03:00
|
|
|
|
|
|
|
|
//ENUM_TIMEFRAMES tf {PERIOD_H1, PERIOD_M5, PERIOD_M1};
|
|
|
|
|
|
|
|
|
|
struct rates{
|
|
|
|
|
ENUM_TIMEFRAMES tf;
|
|
|
|
|
int beyond;
|
|
|
|
|
MqlRates x[]; //
|
|
|
|
|
int triggerFlag; //potential buy/sell catalyst
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
rates r[3];
|
|
|
|
|
|
|
|
|
|
//can instanciate rates object to look for other potential signal from another TF
|
|
|
|
|
|
|
|
|
|
CTrade trade;
|
|
|
|
|
CSymbolInfo symInfo;
|
|
|
|
|
|
|
|
|
|
//Forward declaration
|
|
|
|
|
void breakEven();
|
|
|
|
|
bool trigger(int &flag, int tfIndex, int candleIndex); //a reason to act
|
|
|
|
|
|
2026-04-30 01:08:03 +03:00
|
|
|
bool IsNewCandle() {
|
|
|
|
|
static datetime lastTime = 0;
|
|
|
|
|
datetime currentTime = iTime(_Symbol, inpZtf, 0);
|
|
|
|
|
if(lastTime != currentTime) {
|
|
|
|
|
lastTime = currentTime;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-26 23:38:40 +03:00
|
|
|
int OnInit(void)
|
|
|
|
|
{
|
|
|
|
|
//initiate vars
|
|
|
|
|
signalFlag=0;
|
|
|
|
|
|
|
|
|
|
//Validating user input var
|
|
|
|
|
if((InpXtf<inpYtf) || (inpYtf<inpZtf)){
|
|
|
|
|
Alert(" Input Timeframe doesn't enable Top-Down Approach");
|
|
|
|
|
return (INIT_PARAMETERS_INCORRECT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//else
|
|
|
|
|
//assigning selected tf
|
|
|
|
|
r[0].tf=InpXtf;
|
|
|
|
|
r[1].tf=inpYtf;
|
|
|
|
|
r[2].tf=inpZtf;
|
|
|
|
|
|
|
|
|
|
for(int i=0;i<ArraySize(r);++i){
|
|
|
|
|
r[i].triggerFlag=0; //Neutral
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//lot
|
|
|
|
|
if(inpLot<SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN/*symbol's lowest lot*/)){
|
|
|
|
|
Alert("ERROR! could not accept lot be less than ",SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN));
|
|
|
|
|
return (INIT_PARAMETERS_INCORRECT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(inpRR<0){ /*negative r*/
|
|
|
|
|
Alert("RR cant be less than 0");
|
|
|
|
|
return(INIT_PARAMETERS_INCORRECT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//if(<)
|
|
|
|
|
|
|
|
|
|
riskManagement();
|
|
|
|
|
//if(FixedSizeRisk<SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)) return (INIT_PARAMETERS_INCORRECT);
|
|
|
|
|
Print(FixedSizeRisk);
|
|
|
|
|
|
|
|
|
|
return(INIT_SUCCEEDED);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2026-04-30 01:08:03 +03:00
|
|
|
void OnDeinit(const int32_t reason)
|
|
|
|
|
{
|
|
|
|
|
Print("sell signals:",sellCounts," buy signals:",buyCounts);
|
|
|
|
|
}
|
2026-04-26 23:38:40 +03:00
|
|
|
|
|
|
|
|
void updateRates(){
|
|
|
|
|
for(int i=0;i<ArraySize(r);i++){
|
|
|
|
|
//for(int j=0;j<enumtotal;j++)
|
|
|
|
|
CopyRates(Symbol(),r[i].tf,0,5,r[i].x);
|
|
|
|
|
ArraySetAsSeries(r[i].x,true);
|
|
|
|
|
|
|
|
|
|
if(r[i].x[0].close>r[i].x[0].open){ //current price trading above the opening
|
|
|
|
|
r[i].beyond=1;
|
|
|
|
|
}
|
|
|
|
|
else if(r[i].x[0].close<r[i].x[0].open){ //current price trading below the opening
|
|
|
|
|
r[i].beyond=-1;
|
|
|
|
|
}
|
|
|
|
|
else r[i].beyond=0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void OnTick(){
|
|
|
|
|
////
|
|
|
|
|
// CopyRates(Symbol(),PERIOD_CURRENT,0,5,r.x);
|
|
|
|
|
// ArraySetAsSeries(r.x,true);
|
|
|
|
|
//
|
|
|
|
|
// if(r.x[0].close>r.x[0].open){ //current price trading above the opening
|
|
|
|
|
// beyond=1;
|
|
|
|
|
// }
|
|
|
|
|
// else if(r.x[0].close<r.x[0].open){ //current price trading below the opening
|
|
|
|
|
// beyond=-1;
|
|
|
|
|
// }
|
|
|
|
|
// else beyond=0;
|
|
|
|
|
updateRates();
|
|
|
|
|
//whenever you add a position assign a struct to
|
|
|
|
|
breakEven(BEFactor);
|
|
|
|
|
|
2026-04-30 01:08:03 +03:00
|
|
|
if(IsNewCandle()) {
|
|
|
|
|
hasFired = false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-26 23:38:40 +03:00
|
|
|
//trigger monitoring
|
|
|
|
|
for(int i=0;i<ArraySize(r);i++){
|
2026-04-30 01:08:03 +03:00
|
|
|
//if(inpTrigger1)
|
2026-04-26 23:38:40 +03:00
|
|
|
trigger(r[i].triggerFlag,i);
|
2026-04-30 01:08:03 +03:00
|
|
|
//else
|
|
|
|
|
//trigger(r[i].triggerFlag,i);
|
2026-04-26 23:38:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Signal processing
|
|
|
|
|
signal();
|
|
|
|
|
string signalCall;
|
|
|
|
|
if(signalFlag==1){signalCall="BUY BUY BUY";}
|
|
|
|
|
else if(signalFlag==-1){signalCall="SELL SELL SELL";}
|
|
|
|
|
|
|
|
|
|
//entering a trade
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//STATE MONITORING
|
|
|
|
|
Comment("Beyond || Trigger :","\nXTF=",r[0].beyond," || ",r[0].triggerFlag,"\nYTF=",r[1].beyond," || ",r[1].triggerFlag,"\nZTF=",r[2].beyond," || ",r[2].triggerFlag,"\nBE factor: ",BEFactor,"\n",signalCall);
|
|
|
|
|
|
|
|
|
|
//SymbolSelect(_Symbol,true);
|
|
|
|
|
//Alert();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void breakEven(const double factor /*, double &lockIn*/){
|
|
|
|
|
//accept a factor as a multiple of RR (USER INPUT) suggest to lock-in the position into a risk free
|
|
|
|
|
//SAFE GUARD
|
|
|
|
|
if(PositionsTotal()<1){ //no open position to BE
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(factor<1){
|
|
|
|
|
Print(__FUNCTION__,"ERROR Factor for BE provided was less than 1");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-30 01:08:03 +03:00
|
|
|
double spread = (double)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * _Point;
|
|
|
|
|
|
2026-04-26 23:38:40 +03:00
|
|
|
for(int i=PositionsTotal()-1;i>=0;--i){
|
|
|
|
|
ulong ticket = PositionGetTicket(i);
|
|
|
|
|
if(ticket > 0 && PositionGetString(POSITION_SYMBOL) == _Symbol) {
|
2026-04-30 01:08:03 +03:00
|
|
|
double sl=PositionGetDouble(POSITION_SL);
|
|
|
|
|
double tp=PositionGetDouble(POSITION_TP);
|
|
|
|
|
double entryPrice=PositionGetDouble(POSITION_PRICE_OPEN);
|
|
|
|
|
double risk=MathAbs(entryPrice-sl);
|
|
|
|
|
int posType = (int)PositionGetInteger(POSITION_TYPE);
|
|
|
|
|
|
|
|
|
|
double BEPoint=factor*risk;
|
|
|
|
|
double newSL=0;
|
|
|
|
|
|
|
|
|
|
// Calculate lock-in price based on position type
|
|
|
|
|
if(posType==POSITION_TYPE_BUY){
|
|
|
|
|
lockIn=entryPrice+BEPoint;
|
|
|
|
|
}
|
|
|
|
|
else if(posType==POSITION_TYPE_SELL){
|
|
|
|
|
lockIn=entryPrice-BEPoint;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if price has crossed the lock-in threshold
|
|
|
|
|
if(r[0].x[0].high>lockIn && r[0].x[0].low<lockIn){
|
|
|
|
|
// Aggressively adjust SL to spread + entry price (always breakeven+spread)
|
|
|
|
|
if(posType==POSITION_TYPE_BUY){
|
|
|
|
|
newSL = entryPrice + spread; // Entry + spread = breakeven point
|
|
|
|
|
}
|
|
|
|
|
else if(posType==POSITION_TYPE_SELL){
|
|
|
|
|
newSL = entryPrice - spread; // Entry - spread = breakeven point
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Only modify if new SL is better than current SL
|
|
|
|
|
if((posType==POSITION_TYPE_BUY && newSL>sl) || (posType==POSITION_TYPE_SELL && newSL<sl)){
|
|
|
|
|
if(trade.PositionModify(ticket,newSL,tp)){
|
|
|
|
|
Alert("B.E successful - SL adjusted to ", newSL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-26 23:38:40 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-30 01:08:03 +03:00
|
|
|
//
|
|
|
|
|
//bool trigger(int &flag, int tfIndex=0, int candleIndex=1){
|
|
|
|
|
// if(r[tfIndex].x[candleIndex].close>r[tfIndex].x[candleIndex+1].high){
|
|
|
|
|
// //bullish catalyst
|
|
|
|
|
// flag=1;
|
|
|
|
|
// return true;
|
|
|
|
|
// }
|
|
|
|
|
// else if(r[tfIndex].x[candleIndex].close<r[tfIndex].x[candleIndex+1].low){
|
|
|
|
|
// //bearish catalyst
|
|
|
|
|
// flag=-1;
|
|
|
|
|
// return true;
|
|
|
|
|
// }
|
|
|
|
|
// else flag=0;//close inside range
|
|
|
|
|
//
|
|
|
|
|
//return false;
|
|
|
|
|
//}
|
|
|
|
|
//
|
2026-04-26 23:38:40 +03:00
|
|
|
|
|
|
|
|
|
2026-04-30 01:08:03 +03:00
|
|
|
bool trigger(int &flag, int tfIndex=0, int candleIndex=1){
|
2026-04-26 23:38:40 +03:00
|
|
|
double upper=r[tfIndex].x[candleIndex].high-MathMax(r[tfIndex].x[candleIndex].open,r[tfIndex].x[candleIndex].close);
|
|
|
|
|
double lower=MathMin(r[tfIndex].x[candleIndex].open,r[tfIndex].x[candleIndex].close)-r[tfIndex].x[candleIndex].low;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(upper>lower//bearish momentum candle
|
|
|
|
|
&&(r[tfIndex].x[candleIndex].low>r[tfIndex].x[candleIndex+1].low)){
|
|
|
|
|
//bearish catalyst
|
|
|
|
|
flag=-1;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(lower>upper//bullish momentum candle
|
|
|
|
|
&&(r[tfIndex].x[candleIndex].high<r[tfIndex].x[candleIndex+1].high)){
|
|
|
|
|
//bullish catalyst
|
|
|
|
|
flag=1;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-04-30 01:08:03 +03:00
|
|
|
bool signal() {
|
|
|
|
|
// 1. Define the Alignments
|
|
|
|
|
bool bullishAlignment = (r[0].triggerFlag==1 && r[0].beyond==-1 &&
|
|
|
|
|
r[1].triggerFlag==1 && r[1].beyond==-1 &&
|
|
|
|
|
r[2].triggerFlag==1 && r[2].beyond==-1);
|
|
|
|
|
|
|
|
|
|
bool bearishAlignment = (r[0].triggerFlag==-1 && r[0].beyond==1 &&
|
|
|
|
|
r[1].triggerFlag==-1 && r[1].beyond==1 &&
|
|
|
|
|
r[2].triggerFlag==-1 && r[2].beyond==1);
|
2026-04-26 23:38:40 +03:00
|
|
|
|
2026-04-30 01:08:03 +03:00
|
|
|
// 2. Handle the Buy
|
|
|
|
|
if(bullishAlignment) {
|
|
|
|
|
if(!hasFired) {
|
|
|
|
|
trade.Buy(inpLot, _Symbol, r[2].x[0].close, (r[2].x[0].close-FixedSizeRisk), (r[2].x[0].close+(FixedSizeRisk*inpRR)));
|
|
|
|
|
buyCounts++;
|
|
|
|
|
hasFired = true;
|
|
|
|
|
Alert("Buy Executed");
|
2026-04-26 23:38:40 +03:00
|
|
|
}
|
2026-04-30 01:08:03 +03:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3. Handle the Sell
|
|
|
|
|
if(bearishAlignment) {
|
|
|
|
|
if(!hasFired) {
|
|
|
|
|
sellCounts++;
|
|
|
|
|
trade.Sell(inpLot, _Symbol, r[2].x[0].close, (r[2].x[0].close+FixedSizeRisk), (r[2].x[0].close-(FixedSizeRisk*inpRR)));
|
|
|
|
|
hasFired = true;
|
|
|
|
|
Alert("Sell Executed");
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2026-04-26 23:38:40 +03:00
|
|
|
|
2026-04-30 01:08:03 +03:00
|
|
|
// 4. Reset if NO alignment exists
|
|
|
|
|
//else {
|
|
|
|
|
// hasFired = false;
|
|
|
|
|
//}
|
2026-04-26 23:38:40 +03:00
|
|
|
|
2026-04-30 01:08:03 +03:00
|
|
|
return false;
|
2026-04-26 23:38:40 +03:00
|
|
|
}
|
|
|
|
|
|
2026-04-30 01:08:03 +03:00
|
|
|
|
|
|
|
|
void riskManagement() {
|
|
|
|
|
double spread = (double)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * _Point;
|
|
|
|
|
double minStop = (double)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * _Point;
|
2026-04-26 23:38:40 +03:00
|
|
|
|
2026-04-30 01:08:03 +03:00
|
|
|
// Allow just above minimum required: minStop + spread + clearance
|
|
|
|
|
FixedSizeRisk = MathMax(minStop + spread + (inpTickClearance * _Point), spread * 1.5);
|
|
|
|
|
}
|