//+------------------------------------------------------------------+ //| 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); } } } //+------------------------------------------------------------------+