Files
xMQL5/BKPS/14041018/Documents/BKP/XKI_MTF_EA.mq5
T
2026-01-08 11:39:42 +03:30

548 lines
16 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//+------------------------------------------------------------------+
//| XKI_MTF_EA.mq5
//| SaherElm IT Center - Strategy using XKI
//| Author: Hadi Khazaee Asl
//+------------------------------------------------------------------+
#property strict
//
// Imports ...
#include "../Libraries/x-saherelm.common.lib.mq5"
// Inputs
input string InpSymbol = "XAUUSDb";
input ENUM_TIMEFRAMES InpSignalTF = PERIOD_H1; // Signal timeframe (H1)
input int InpKiLength = 26; // KI length (XKI input)
input bool InpShowKI = true; // (XKI input)
input int InpCalcLastBars = 1500; // (XKI input)
input bool InpUseRSI = true; // Use RSI confirmation
input int InpRSIPeriod = 14;
input int InpRSIPrice = PRICE_CLOSE;
input bool InpUseH4Filter = true; // Confirm with H4
input bool InpUseD1Filter = false; // Confirm with D1
input bool InpRequireBothFilters = false; // If true, require BOTH H4 and D1 agreement; else either
input double InpFixedLot = 0.10; // Fixed lot size
input double InpRiskATRMult = 0.0; // Add ATR padding to stop (0=disabled)
input int InpATRPeriod = 14;
input double InpRR = 2.0; // Reward:Risk target
input bool InpTrailByKI = true; // Trail stop to KI
input int InpSlippagePoints = 20; // Max slippage in points
input int InpMaxSpreadPoints = 200; // Max allowed spread in points
input bool InpAvoidAsianSession = true; // Avoid 22:00–06:00 server time
input int InpAsianStartHour = 22;
input int InpAsianEndHour = 6;
input bool InpAvoidNewsWindow = false; // Placeholder flag
input int InpNewsWindowMinutes = 60; // Skip trades within +/- minutes
input int InpBarsLookbackSignal = 2; // Lookback bars for signal (e.g., last closed bar)
// Magic number and comments
input int InpMagic = 260126;
input string InpOrderComment = "XKI_MTF_EA";
// iCustom indicator path (relative to Indicators folder if compiled there)
string g_xkiIndicatorPath = "x-saherelm.x121.xki"; // the compiled name; adjust if different
// Buffers indices as per the indicator
#define KI_BUFFER_INDEX 0
#define KI_COLOR_IDX_BUFFER_INDEX 1
#define KI_STATE_BUFFER_INDEX 2
// States from indicator
#define BULLISH_STATE 1
#define NEUTURAL_STATE 0
#define BEARISH_STATE -1
// Globals
int g_digits;
double g_point;
MqlTick g_tick;
int g_atrHandleH1 = INVALID_HANDLE;
int g_atrHandleH4 = INVALID_HANDLE;
int g_atrHandleD1 = INVALID_HANDLE;
int g_rsiHandleH1 = INVALID_HANDLE;
int g_rsiHandleH4 = INVALID_HANDLE;
int g_rsiHandleD1 = INVALID_HANDLE;
//+------------------------------------------------------------------+
// OnInit
//+------------------------------------------------------------------+
int OnInit()
{
//
g_digits = (int)SymbolInfoInteger(InpSymbol, SYMBOL_DIGITS);
g_point = SymbolInfoDouble(InpSymbol, SYMBOL_POINT);
if (!SymbolInfoTick(InpSymbol, g_tick))
{
Print("Failed to get tick for symbol: ", InpSymbol);
return INIT_FAILED;
}
// Quick validation
if (InpKiLength < 5)
{
Print("Invalid KI length: ", InpKiLength);
return INIT_PARAMETERS_INCORRECT;
}
//
GetOrCreateATRHandle(InpSymbol, InpSignalTF, InpATRPeriod);
if (InpUseH4Filter)
GetOrCreateATRHandle(InpSymbol, PERIOD_H4, InpATRPeriod);
if (InpUseD1Filter)
GetOrCreateATRHandle(InpSymbol, PERIOD_D1, InpATRPeriod);
//
GetOrCreateRSIHandle(InpSymbol, InpSignalTF, InpRSIPeriod, InpRSIPrice);
if (InpUseH4Filter)
GetOrCreateRSIHandle(InpSymbol, PERIOD_H4, InpRSIPeriod, InpRSIPrice);
if (InpUseD1Filter)
GetOrCreateRSIHandle(InpSymbol, PERIOD_D1, InpRSIPeriod, InpRSIPrice);
//
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
// OnDeinit
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//
if (g_atrHandleH1 != INVALID_HANDLE)
{
IndicatorRelease(g_atrHandleH1);
g_atrHandleH1 = INVALID_HANDLE;
}
if (g_atrHandleH4 != INVALID_HANDLE)
{
IndicatorRelease(g_atrHandleH4);
g_atrHandleH4 = INVALID_HANDLE;
}
if (g_atrHandleD1 != INVALID_HANDLE)
{
IndicatorRelease(g_atrHandleD1);
g_atrHandleD1 = INVALID_HANDLE;
}
//
if (g_rsiHandleH1 != INVALID_HANDLE)
{
IndicatorRelease(g_rsiHandleH1);
g_rsiHandleH1 = INVALID_HANDLE;
}
if (g_rsiHandleH4 != INVALID_HANDLE)
{
IndicatorRelease(g_rsiHandleH4);
g_rsiHandleH4 = INVALID_HANDLE;
}
if (g_rsiHandleD1 != INVALID_HANDLE)
{
IndicatorRelease(g_rsiHandleD1);
g_rsiHandleD1 = INVALID_HANDLE;
}
}
//+------------------------------------------------------------------+
// OnTick
//+------------------------------------------------------------------+
void OnTick()
{
if (_Symbol != InpSymbol)
return;
// Basic trading safety
if (!SymbolInfoTick(InpSymbol, g_tick))
return;
double spreadPoints = GetSpread(InpSymbol);
if (spreadPoints > InpMaxSpreadPoints)
return;
if (InpAvoidAsianSession)
{
//
MqlDateTime now = GetCurrentTime();
int hr = now.hour;
if (IsHourInRange(hr, InpAsianStartHour, InpAsianEndHour))
return;
}
if (InpAvoidNewsWindow)
{
// Placeholder: implement your own calendar integration
// Skip trading in a window near news. Here we simply return.
return;
}
// Only process on new bar of signal timeframe
static datetime lastSignalBarTime = 0;
datetime currentSignalBarTime = GetLastClosedBarTime(InpSymbol, InpSignalTF);
if (currentSignalBarTime == 0 || currentSignalBarTime == lastSignalBarTime)
return;
lastSignalBarTime = currentSignalBarTime;
// Get signal state and KI on signal TF (last closed bar)
int signalBarShift = InpBarsLookbackSignal; // 1=last closed, 2=previous
int stateSignal;
double kiSignal, closeSignal;
if (!GetXKIStateAndKI(InpSymbol, InpSignalTF, signalBarShift, stateSignal, kiSignal))
return;
closeSignal = iClose(InpSymbol, InpSignalTF, signalBarShift);
// RSI confirmation
if (InpUseRSI)
{
double rsi = GetRSIValue(InpSymbol, InpSignalTF, InpRSIPeriod, InpRSIPrice, signalBarShift);
if (rsi == EMPTY_VALUE)
return;
if (stateSignal == BULLISH_STATE && rsi <= 50.0)
return;
if (stateSignal == BEARISH_STATE && rsi >= 50.0)
return;
}
// Higher timeframe agreement
bool agreesH4 = true, agreesD1 = true;
if (InpUseH4Filter)
agreesH4 = HigherTFAgrees(InpSymbol, PERIOD_H4, signalBarShift, stateSignal);
if (InpUseD1Filter)
agreesD1 = HigherTFAgrees(InpSymbol, PERIOD_D1, signalBarShift, stateSignal);
bool filterOK = true;
if (InpUseH4Filter || InpUseD1Filter)
{
if (InpRequireBothFilters)
filterOK = (agreesH4 && agreesD1);
else
filterOK = ((InpUseH4Filter && agreesH4) || (InpUseD1Filter && agreesD1));
}
if (!filterOK)
return;
// Neutral or invalid skip
if (stateSignal == NEUTURAL_STATE)
return;
// Ensure no open positions for this symbol/magic
if (HasOpenPosition(InpSymbol, InpMagic))
return;
// Prepare trade params
double atrPad = 0.0;
if (InpRiskATRMult > 0.0)
{
double atr = GetATRValue(InpSymbol, InpSignalTF, InpATRPeriod, signalBarShift);
if (atr != EMPTY_VALUE && atr > 0)
atrPad = atr * InpRiskATRMult;
}
double sl, tp;
bool isBuy = (stateSignal == BULLISH_STATE);
if (isBuy)
{
sl = kiSignal - atrPad;
tp = closeSignal + (closeSignal - sl) * InpRR;
PlaceOrder(InpSymbol, ORDER_TYPE_BUY, InpFixedLot, sl, tp);
}
else if (stateSignal == BEARISH_STATE)
{
sl = kiSignal + atrPad;
tp = closeSignal - (sl - closeSignal) * InpRR;
PlaceOrder(InpSymbol, ORDER_TYPE_SELL, InpFixedLot, sl, tp);
}
// After placement, trailing handled in OnTimer or OnTick below
if (InpTrailByKI)
TrailStopsByKI();
}
//+------------------------------------------------------------------+
// Utilities
//+------------------------------------------------------------------+
//
// Create or reuse an ATR handle for the given TF ...
int GetOrCreateATRHandle(
string symbol,
ENUM_TIMEFRAMES tf,
int period //
)
{
int h = g_atrHandleH1;
if (tf == PERIOD_H4)
h = g_atrHandleH4;
else if (tf == PERIOD_D1)
h = g_atrHandleD1;
if (h == INVALID_HANDLE)
{
h = iATR(symbol, tf, period);
if (h == INVALID_HANDLE)
Print("Failed to create ATR handle: ", symbol, " ", EnumToString(tf), " period=", period);
}
return h;
}
//
// Read ATR value for a specific bar shift (1=last closed bar) ...
double GetATRValue(
string symbol,
ENUM_TIMEFRAMES tf,
int period,
int shift //
)
{
int h = GetOrCreateATRHandle(symbol, tf, period);
if (h == INVALID_HANDLE)
return EMPTY_VALUE;
//
double buff[];
//
// Copy exactly one value at 'shift' ...
int copied = CopyBuffer(h, 0, shift, 1, buff);
if (copied != 1 || !MathIsValidNumber(buff[0]))
return EMPTY_VALUE;
//
return buff[0];
}
//
// Create or reuse an RSI handle for the given TF ...
int GetOrCreateRSIHandle(
string symbol,
ENUM_TIMEFRAMES tf,
int period,
int appliedPrice //
)
{
//
int h = g_rsiHandleH1;
if (tf == PERIOD_H4)
h = g_rsiHandleH4;
else if (tf == PERIOD_D1)
h = g_rsiHandleD1;
if (h == INVALID_HANDLE)
{
h = iRSI(symbol, tf, period, appliedPrice);
if (h == INVALID_HANDLE)
Print("Failed to create RSI handle: ", symbol, " ", EnumToString(tf), " period=", period, " price=", appliedPrice);
}
return h;
}
//
// Read RSI value for a specific bar shift (1=last closed bar) ..
double GetRSIValue(
string symbol,
ENUM_TIMEFRAMES tf,
int period,
int appliedPrice,
int shift //
)
{
int h = GetOrCreateRSIHandle(symbol, tf, period, appliedPrice);
if (h == INVALID_HANDLE)
return EMPTY_VALUE;
double buff[];
int copied = CopyBuffer(h, 0, shift, 1, buff);
if (copied != 1 || !MathIsValidNumber(buff[0]))
return EMPTY_VALUE;
return buff[0];
}
datetime GetLastClosedBarTime(string symbol, ENUM_TIMEFRAMES tf)
{
datetime times[];
if (CopyTime(symbol, tf, 0, 3, times) <= 1)
return 0;
// times[0] is current open bar time; last closed is times[1]
return times[1];
}
bool IsHourInRange(int hr, int startHr, int endHr)
{
// Handles wrap-around (e.g., 22 -> 6)
if (startHr <= endHr)
return (hr >= startHr && hr < endHr);
else
return (hr >= startHr || hr < endHr);
}
bool GetXKIStateAndKI(string symbol, ENUM_TIMEFRAMES tf, int shift, int &stateOut, double &kiOut)
{
// Prepare indicator handle for this TF
static int handleH1 = INVALID_HANDLE;
static int handleH4 = INVALID_HANDLE;
static int handleD1 = INVALID_HANDLE;
int handleRef = handleH1;
if (tf == PERIOD_H4)
handleRef = handleH4;
else if (tf == PERIOD_D1)
handleRef = handleD1;
if (handleRef == INVALID_HANDLE)
{
handleRef = iCustom(symbol, tf, g_xkiIndicatorPath,
InpKiLength, InpShowKI, InpCalcLastBars);
if (handleRef == INVALID_HANDLE)
{
Print("Failed to create XKI handle for TF: ", EnumToString(tf));
return false;
}
}
double kiBuff[];
double stateBuff[];
if (CopyBuffer(handleRef, KI_BUFFER_INDEX, shift, 1, kiBuff) != 1)
return false;
if (CopyBuffer(handleRef, KI_STATE_BUFFER_INDEX, shift, 1, stateBuff) != 1)
return false;
kiOut = kiBuff[0];
stateOut = (int)stateBuff[0];
if (!MathIsValidNumber(kiOut))
return false;
return true;
}
bool HigherTFAgrees(string symbol, ENUM_TIMEFRAMES tf, int shift, int signalState)
{
int stateHTF;
double kiHTF;
if (!GetXKIStateAndKI(symbol, tf, shift, stateHTF, kiHTF))
return false;
if (signalState == BULLISH_STATE)
return (stateHTF == BULLISH_STATE);
if (signalState == BEARISH_STATE)
return (stateHTF == BEARISH_STATE);
return false;
}
bool HasOpenPosition(string symbol, int magic)
{
for (int i = 0; i < PositionsTotal(); i++)
{
ulong ticket = PositionGetTicket(i);
if (ticket == 0)
continue;
if (PositionSelectByTicket(ticket))
{
string sym = PositionGetString(POSITION_SYMBOL);
long mg = (long)PositionGetInteger(POSITION_MAGIC);
if (sym == symbol && mg == magic)
return true;
}
}
return false;
}
void PlaceOrder(string symbol, ENUM_ORDER_TYPE type, double lots, double sl, double tp)
{
MqlTradeRequest req;
MqlTradeResult res;
ZeroMemory(req);
ZeroMemory(res);
double price = 0.0;
if (!SymbolInfoTick(symbol, g_tick))
return;
if (type == ORDER_TYPE_BUY)
price = g_tick.ask;
else if (type == ORDER_TYPE_SELL)
price = g_tick.bid;
// Normalize SL/TP
sl = NormalizeDouble(sl, g_digits);
tp = NormalizeDouble(tp, g_digits);
price = NormalizeDouble(price, g_digits);
req.action = TRADE_ACTION_DEAL;
req.symbol = symbol;
req.volume = lots;
req.type = type;
req.price = price;
req.sl = sl;
req.tp = tp;
req.deviation = InpSlippagePoints;
req.magic = InpMagic;
req.comment = InpOrderComment;
if (!OrderSend(req, res))
Print("OrderSend failed: ", res.retcode);
}
void TrailStopsByKI()
{
// Trail per position using current signal TF KI
for (int i = 0; i < PositionsTotal(); i++)
{
ulong ticket = PositionGetTicket(i);
if (ticket == 0)
continue;
if (!PositionSelectByTicket(ticket))
continue;
string sym = PositionGetString(POSITION_SYMBOL);
long mg = (long)PositionGetInteger(POSITION_MAGIC);
if (sym != InpSymbol || mg != InpMagic)
continue;
ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double sl = PositionGetDouble(POSITION_SL);
double priceOpen = PositionGetDouble(POSITION_PRICE_OPEN);
// Use last closed bar KI for stability
int shift = 1;
int state;
double ki;
if (!GetXKIStateAndKI(sym, InpSignalTF, shift, state, ki))
continue;
MqlTradeRequest req;
MqlTradeResult res;
ZeroMemory(req);
ZeroMemory(res);
double newSL = sl;
if (ptype == POSITION_TYPE_BUY)
{
double proposedSL = NormalizeDouble(ki, g_digits);
if (proposedSL > sl)
newSL = proposedSL; // only move up
}
else if (ptype == POSITION_TYPE_SELL)
{
double proposedSL = NormalizeDouble(ki, g_digits);
if (proposedSL < sl || sl == 0.0)
newSL = proposedSL; // only move down (or set if none)
}
if (newSL != sl && newSL != 0.0)
{
req.action = TRADE_ACTION_SLTP;
req.symbol = sym;
req.sl = newSL;
req.tp = PositionGetDouble(POSITION_TP);
req.magic = InpMagic;
if (!OrderSend(req, res))
Print("Trail SLTP failed: ", res.retcode);
}
}
}
//+------------------------------------------------------------------+