229 строки
8,1 КиБ
MQL5
229 строки
8,1 КиБ
MQL5
//+------------------------------------------------------------------+
|
|
//| 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
|