//+------------------------------------------------------------------+ //| SignalEngine.mqh – indikátory a výpočet signálů | //+------------------------------------------------------------------+ #property copyright "TestRefactor" #property strict #ifndef __SIGNAL_ENGINE_MQH__ #define __SIGNAL_ENGINE_MQH__ #include "Context.mqh" #include "Logger.mqh" struct SSignalResult { bool ok; bool buySignal; bool sellSignal; double smaDistancePoints; bool trendUp; double volume; }; struct SLastBarValues { double slowMa0, slowMa1, fastMa0, fastMa1, rsi0; }; struct SH1TrendResult { bool ok; bool trendUp; }; //+------------------------------------------------------------------+ //| CSignalEngine – drží handly, počítá SMA distance, trend, signály| //+------------------------------------------------------------------+ class CSignalEngine { private: string m_symbol; SContext m_ctx; CLogger *m_log; int m_handleFastMa; int m_handleSlowMa; int m_handleRsi; int m_handleH1TrendMa; int m_handleTf1Ma; int m_handleTf2Ma; datetime m_lastBarTime; public: CSignalEngine() : m_symbol(""), m_handleFastMa(INVALID_HANDLE), m_handleSlowMa(INVALID_HANDLE), m_handleRsi(INVALID_HANDLE), m_handleH1TrendMa(INVALID_HANDLE), m_handleTf1Ma(INVALID_HANDLE), m_handleTf2Ma(INVALID_HANDLE), m_lastBarTime(0), m_log(NULL) {} void SetLogger(CLogger *log) { m_log = log; } void SetSymbol(string symbol) { m_symbol = symbol; } void SetContext(const SContext &ctx) { m_ctx = ctx; } bool Init() { m_handleFastMa = iMA(m_symbol, PERIOD_CURRENT, m_ctx.fastMaPeriod, 0, MODE_SMA, PRICE_CLOSE); m_handleSlowMa = iMA(m_symbol, PERIOD_CURRENT, m_ctx.slowMaPeriod, 0, MODE_SMA, PRICE_CLOSE); m_handleRsi = iRSI(m_symbol, PERIOD_CURRENT, m_ctx.rsiPeriod, PRICE_CLOSE); m_handleH1TrendMa = INVALID_HANDLE; if(m_ctx.h1TrendMaPeriod > 0) { m_handleH1TrendMa = iMA(m_symbol, PERIOD_H1, m_ctx.h1TrendMaPeriod, 0, MODE_SMA, PRICE_CLOSE); if(m_handleH1TrendMa == INVALID_HANDLE) { if(m_log) m_log.Error("Error creating H1 trend MA"); return false; } } if(m_ctx.timeframe1MaPeriod > 0) { m_handleTf1Ma = iMA(m_symbol, m_ctx.timeframe1, m_ctx.timeframe1MaPeriod, 0, MODE_SMA, PRICE_CLOSE); if(m_handleTf1Ma == INVALID_HANDLE) { if(m_log) m_log.Error("Error creating TF1 trend MA"); return false; } } if(m_ctx.timeframe2MaPeriod > 0) { m_handleTf2Ma = iMA(m_symbol, m_ctx.timeframe2, m_ctx.timeframe2MaPeriod, 0, MODE_SMA, PRICE_CLOSE); if(m_handleTf2Ma == INVALID_HANDLE) { if(m_log) m_log.Error("Error creating TF2 trend MA"); return false; } } if(m_handleFastMa == INVALID_HANDLE || m_handleSlowMa == INVALID_HANDLE || m_handleRsi == INVALID_HANDLE) { if(m_log) m_log.Error("Error creating indicators"); return false; } return true; } void Deinit() { if(m_handleFastMa != INVALID_HANDLE) IndicatorRelease(m_handleFastMa); if(m_handleSlowMa != INVALID_HANDLE) IndicatorRelease(m_handleSlowMa); if(m_handleRsi != INVALID_HANDLE) IndicatorRelease(m_handleRsi); if(m_handleH1TrendMa != INVALID_HANDLE) IndicatorRelease(m_handleH1TrendMa); if(m_handleTf1Ma != INVALID_HANDLE) IndicatorRelease(m_handleTf1Ma); if(m_handleTf2Ma != INVALID_HANDLE) IndicatorRelease(m_handleTf2Ma); m_handleFastMa = m_handleSlowMa = m_handleRsi = m_handleH1TrendMa = m_handleTf1Ma = m_handleTf2Ma = INVALID_HANDLE; } bool IsNewBar() { datetime time = iTime(m_symbol, PERIOD_CURRENT, 0); if(time != m_lastBarTime) { m_lastBarTime = time; return true; } return false; } SSignalResult GetSignals() { SSignalResult r; r.ok = false; r.buySignal = false; r.sellSignal = false; r.smaDistancePoints = 0; r.trendUp = true; r.volume = m_ctx.lotSize; double slowMaArray[]; if(CopyBuffer(m_handleSlowMa, 0, 1, 2, slowMaArray) != 2) return r; ArraySetAsSeries(slowMaArray, true); double fastMaArray[]; if(CopyBuffer(m_handleFastMa, 0, 1, 2, fastMaArray) != 2) return r; ArraySetAsSeries(fastMaArray, true); double rsiArray[]; if(CopyBuffer(m_handleRsi, 0, 1, 1, rsiArray) != 1) return r; ArraySetAsSeries(rsiArray, true); r.smaDistancePoints = MathAbs(fastMaArray[0] - slowMaArray[0]) / _Point; bool distanceOk = (m_ctx.smaMinDistancePoints <= 0) || (r.smaDistancePoints >= m_ctx.smaMinDistancePoints); bool trendOkBuy = true, trendOkSell = true; if(m_handleH1TrendMa != INVALID_HANDLE) { double h1MaArray[]; if(CopyBuffer(m_handleH1TrendMa, 0, 1, 1, h1MaArray) == 1) { ArraySetAsSeries(h1MaArray, true); double closeH1 = iClose(m_symbol, PERIOD_H1, 1); trendOkBuy = (closeH1 > h1MaArray[0]); trendOkSell = (closeH1 < h1MaArray[0]); r.trendUp = trendOkBuy; } } r.buySignal = (fastMaArray[0] > slowMaArray[0] && fastMaArray[1] < slowMaArray[1] && rsiArray[0] < m_ctx.rsiOverbought && distanceOk && trendOkBuy); r.sellSignal = (fastMaArray[0] < slowMaArray[0] && fastMaArray[1] > slowMaArray[1] && rsiArray[0] > m_ctx.rsiOversold && distanceOk && trendOkSell); double minVol = SymbolInfoDouble(m_symbol, SYMBOL_VOLUME_MIN); double maxVol = SymbolInfoDouble(m_symbol, SYMBOL_VOLUME_MAX); double stepVol = SymbolInfoDouble(m_symbol, SYMBOL_VOLUME_STEP); double vol = r.volume; if(vol < minVol) vol = minVol; if(vol > maxVol) vol = maxVol; if(stepVol > 0) vol = minVol + MathRound((vol - minVol) / stepVol) * stepVol; r.volume = vol; r.ok = true; return r; } SH1TrendResult GetH1Trend() { SH1TrendResult r; r.ok = false; r.trendUp = true; if(m_handleH1TrendMa == INVALID_HANDLE) return r; double h1MaArray[]; if(CopyBuffer(m_handleH1TrendMa, 0, 1, 1, h1MaArray) != 1) return r; double closeH1 = iClose(m_symbol, PERIOD_H1, 1); r.trendUp = (closeH1 > h1MaArray[0]); r.ok = true; return r; } SH1TrendResult GetTf1Trend() { SH1TrendResult r; r.ok = false; r.trendUp = true; if(m_handleTf1Ma == INVALID_HANDLE) return r; double maArray[]; if(CopyBuffer(m_handleTf1Ma, 0, 1, 1, maArray) != 1) return r; double close = iClose(m_symbol, m_ctx.timeframe1, 1); r.trendUp = (close > maArray[0]); r.ok = true; return r; } SH1TrendResult GetTf2Trend() { SH1TrendResult r; r.ok = false; r.trendUp = true; if(m_handleTf2Ma == INVALID_HANDLE) return r; double maArray[]; if(CopyBuffer(m_handleTf2Ma, 0, 1, 1, maArray) != 1) return r; double close = iClose(m_symbol, m_ctx.timeframe2, 1); r.trendUp = (close > maArray[0]); r.ok = true; return r; } bool IsBuyDirectionValid() { SH1TrendResult tf1 = GetTf1Trend(); SH1TrendResult tf2 = GetTf2Trend(); if(!tf1.ok || !tf2.ok) return true; return tf1.trendUp && tf2.trendUp; } bool IsSellDirectionValid() { SH1TrendResult tf1 = GetTf1Trend(); SH1TrendResult tf2 = GetTf2Trend(); if(!tf1.ok || !tf2.ok) return true; return !tf1.trendUp && !tf2.trendUp; } SLastBarValues GetLastBarValues() { SLastBarValues r; r.slowMa0 = r.slowMa1 = r.fastMa0 = r.fastMa1 = r.rsi0 = 0; double slowMaArray[], fastMaArray[], rsiArray[]; if(CopyBuffer(m_handleSlowMa, 0, 1, 2, slowMaArray) != 2) return r; if(CopyBuffer(m_handleFastMa, 0, 1, 2, fastMaArray) != 2) return r; if(CopyBuffer(m_handleRsi, 0, 1, 1, rsiArray) != 1) return r; ArraySetAsSeries(slowMaArray, true); ArraySetAsSeries(fastMaArray, true); ArraySetAsSeries(rsiArray, true); r.slowMa0 = slowMaArray[0]; r.slowMa1 = slowMaArray[1]; r.fastMa0 = fastMaArray[0]; r.fastMa1 = fastMaArray[1]; r.rsi0 = rsiArray[0]; return r; } }; #endif