Initial commit: TriDivergenceEA
Proprietary trading software. © Teenodi Ltd. All rights reserved. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
# Compiled binaries / logs / terminal artifacts
|
||||
*.ex4
|
||||
*.ex5
|
||||
*.log
|
||||
*.tmp
|
||||
*.bak
|
||||
@@ -0,0 +1,10 @@
|
||||
Copyright (c) 2023-2026 Teenodi Ltd. All rights reserved.
|
||||
|
||||
This software and its source code are the proprietary and confidential
|
||||
property of Teenodi Ltd. Unauthorized copying, distribution, modification,
|
||||
public display, or use of this software, in whole or in part, via any medium,
|
||||
is strictly prohibited without the express prior written permission of
|
||||
Teenodi Ltd.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. Trading
|
||||
financial instruments carries risk; use at your own risk.
|
||||
@@ -0,0 +1,125 @@
|
||||
# TriDivergenceEA
|
||||
|
||||
Expert Advisor that trades on confluence of divergence signals from RSI, Stochastic and Klinger indicators.
|
||||
|
||||
> **Proprietary software** — © Teenodi Ltd. All rights reserved. Private repository; not for distribution. See [LICENSE](LICENSE).
|
||||
|
||||
## TriDivergenceEA.mq5
|
||||
|
||||
_EA that trades based on divergence signals from RSI, Stochastic, and Klinger indicators_
|
||||
|
||||
### Inputs
|
||||
|
||||
|
||||
*Magicnumber and Comment Setting*
|
||||
|
||||
| Input | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `Magic` | int | `12345` | Magic number for trade identification (12345-123450) |
|
||||
| `comentar` | string | `"DivergenceEA"` | Trade comment field |
|
||||
|
||||
*Risk- and Moneymanagement*
|
||||
|
||||
| Input | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `volumetype` | VOLUME_TYPE | `VOLUME_FIXED` | Volume type: 1=FIXED, 2=PROCENT |
|
||||
| `Risk` | double | `0.1` | Risk percentage per trade (0.1-30%) |
|
||||
| `Lots` | double | `0.01` | Fixed lot size (0.01-100) |
|
||||
| `Sl_Point_pip` | int | `100` | Stop loss in pips (10-10000) |
|
||||
| `Tp_Point_pip` | int | `200` | Take profit in pips (10-10000) |
|
||||
|
||||
*Pending Orders Strategy*
|
||||
|
||||
| Input | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `PendingOrder` | bool | `false` | Enable pending orders |
|
||||
|
||||
*Condition 1*
|
||||
|
||||
|
||||
*Position*
|
||||
|
||||
| Input | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `Position` | POSITION_TYPE_FILTER | `POSITION_BOTH` | 1=BUY_ONLY, 2=SELL_ONLY, 3=BUY_or_SELL |
|
||||
| `CANDLESTICKS` | string | `"HIGHER_HIGH_OR_LOWER_LOW"` | Candlestick validation description |
|
||||
| `Numberofcandles_Back` | int | `20` | Lookback period for candlestick validation (1-100) |
|
||||
|
||||
*Condition 2*
|
||||
|
||||
|
||||
*INDICATORS DIVERGENCE:*
|
||||
|
||||
|
||||
*Klinger Oscillator KO INDICATOR*
|
||||
|
||||
| Input | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `KO` | bool | `true` | Enable Klinger Oscillator |
|
||||
| `KLINGER_LENGHT1` | int | `34` | First Klinger parameter (0-100) |
|
||||
| `KLINGER_LENGHT2` | int | `55` | Second Klinger parameter (0-100) |
|
||||
| `SIGNAL_LONG` | int | `13` | Klinger signal length (0-100) |
|
||||
| `Numberofcandles_Back_KO` | int | `20` | KO lookback period for analysis (1-100) |
|
||||
|
||||
*Condition 3*
|
||||
|
||||
|
||||
*RSI INDICATOR*
|
||||
|
||||
| Input | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `RSI` | bool | `true` | Enable RSI indicator |
|
||||
| `RSI_UPPER` | int | `70` | RSI upper threshold (0-100) |
|
||||
| `RSI_LOWER` | int | `30` | RSI lower threshold (0-100) |
|
||||
| `RSI_LONG` | int | `14` | RSI signal length (0-100) |
|
||||
| `Numberofcandles_Back_RSI` | int | `20` | RSI lookback period for analysis (1-100) |
|
||||
|
||||
*Condition 4*
|
||||
|
||||
|
||||
*STOCH INDICATOR*
|
||||
|
||||
| Input | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `STOCH` | bool | `true` | Enable Stochastic indicator |
|
||||
| `K` | int | `5` | Stochastic %K value (0-100) |
|
||||
| `D` | int | `3` | Stochastic %D value (0-100) |
|
||||
| `STOCHASTIC_LONG` | int | `5` | Stochastic signal length (0-100) |
|
||||
| `Numberofcandles_Back_STOCH` | int | `20` | STOCH lookback period for analysis (1-100) |
|
||||
|
||||
*BreakEven Settings*
|
||||
|
||||
| Input | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `BreakEven` | bool | `true` | Enable breakeven function |
|
||||
| `BreakEvenShift` | int | `10` | Distance to move stop above breakeven (in pips) (1-100) |
|
||||
| `StopNachzienWenn` | int | `50` | Profit level to trigger breakeven (in pips) (1-100) |
|
||||
|
||||
*Trallingstop Settings*
|
||||
|
||||
| Input | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `Trall` | bool | `false` | Enable trailing stop |
|
||||
| `traillingstop` | TRAILING_TYPE | `TRAIL_CONTINUOUS` | Trailing algorithm type: 1=continuous, 2=step |
|
||||
| `TralStop` | int | `30` | Trailing distance behind price (in pips) (1-2000) |
|
||||
| `TralStep` | int | `10` | Minimum move before trail adjusts (in pips) (1-2000) |
|
||||
|
||||
*Visual Settings*
|
||||
|
||||
| Input | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `RectangleColor` | color | `clrLimeGreen` | Rectangle color |
|
||||
|
||||
## Archive
|
||||
|
||||
Earlier versions / forks are preserved under [`archive/`](archive/) for reference.
|
||||
|
||||
## Install
|
||||
|
||||
1. Copy the source file(s) into your MetaTrader 5 `MQL5/` tree (`Experts/`, `Indicators/`, or `Scripts/` as appropriate).
|
||||
2. In MetaEditor, open the file and compile (**F7**).
|
||||
3. Attach the compiled program to a chart from the terminal Navigator.
|
||||
|
||||
## License
|
||||
|
||||
Proprietary and confidential. © Teenodi Ltd. All rights reserved. See [LICENSE](LICENSE).
|
||||
@@ -0,0 +1,723 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| TriDivergenceEA.mq5 |
|
||||
//| Copyright 2025, Teenodi Ltd. |
|
||||
//| https://www.jukwaese.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025, Teenodi Ltd."
|
||||
#property link "val.chioke@gmail.com"
|
||||
#property version "1.00"
|
||||
#property description "EA that trades based on divergence signals from RSI, Stochastic, and Klinger indicators"
|
||||
#property strict
|
||||
|
||||
//============================ Enums ===============================
|
||||
enum SIGNAL_TYPE {
|
||||
SIGNAL_NULL, // No Signal
|
||||
SIGNAL_BUY, // Buy Signal
|
||||
SIGNAL_SELL // Sell Signal
|
||||
};
|
||||
|
||||
enum VOLUME_TYPE {
|
||||
VOLUME_FIXED = 1, // Fixed Lots
|
||||
VOLUME_RISK = 2 // Percentage Risk
|
||||
};
|
||||
|
||||
enum POSITION_TYPE_FILTER {
|
||||
POSITION_BUY_ONLY = 1, // Buy Only
|
||||
POSITION_SELL_ONLY = 2, // Sell Only
|
||||
POSITION_BOTH = 3 // Both
|
||||
};
|
||||
|
||||
enum TRAILING_TYPE {
|
||||
TRAIL_CONTINUOUS, // Continuous Trail
|
||||
TRAIL_STEP // Step Trail
|
||||
};
|
||||
|
||||
//============================ Structures ===========================
|
||||
struct SignalInfo {
|
||||
SIGNAL_TYPE signal;
|
||||
int shift;
|
||||
|
||||
SignalInfo() {
|
||||
signal = SIGNAL_NULL;
|
||||
shift = -1;
|
||||
}
|
||||
};
|
||||
|
||||
//============================ Inputs ===============================
|
||||
input group "Magicnumber and Comment Setting"
|
||||
input int Magic = 12345; // Magic number for trade identification (12345-123450)
|
||||
input string comentar = "DivergenceEA"; // Trade comment field
|
||||
|
||||
input group "Risk- and Moneymanagement"
|
||||
input VOLUME_TYPE volumetype = VOLUME_FIXED; // Volume type: 1=FIXED, 2=PROCENT
|
||||
input double Risk = 0.1; // Risk percentage per trade (0.1-30%)
|
||||
input double Lots = 0.01; // Fixed lot size (0.01-100)
|
||||
input int Sl_Point_pip = 100; // Stop loss in pips (10-10000)
|
||||
input int Tp_Point_pip = 200; // Take profit in pips (10-10000)
|
||||
|
||||
input group "Pending Orders Strategy"
|
||||
input bool PendingOrder = false; // Enable pending orders
|
||||
|
||||
input group "Condition 1"
|
||||
input group "Position"
|
||||
input POSITION_TYPE_FILTER Position = POSITION_BOTH; // 1=BUY_ONLY, 2=SELL_ONLY, 3=BUY_or_SELL
|
||||
input string CANDLESTICKS = "HIGHER_HIGH_OR_LOWER_LOW"; // Candlestick validation description
|
||||
input int Numberofcandles_Back = 20; // Lookback period for candlestick validation (1-100)
|
||||
|
||||
input group "Condition 2"
|
||||
input group "INDICATORS DIVERGENCE:"
|
||||
input group "Klinger Oscillator KO INDICATOR"
|
||||
input bool KO = true; // Enable Klinger Oscillator
|
||||
input int KLINGER_LENGHT1 = 34; // First Klinger parameter (0-100)
|
||||
input int KLINGER_LENGHT2 = 55; // Second Klinger parameter (0-100)
|
||||
input int SIGNAL_LONG = 13; // Klinger signal length (0-100)
|
||||
input int Numberofcandles_Back_KO = 20; // KO lookback period for analysis (1-100)
|
||||
|
||||
input group "Condition 3"
|
||||
input group "RSI INDICATOR"
|
||||
input bool RSI = true; // Enable RSI indicator
|
||||
input int RSI_UPPER = 70; // RSI upper threshold (0-100)
|
||||
input int RSI_LOWER = 30; // RSI lower threshold (0-100)
|
||||
input int RSI_LONG = 14; // RSI signal length (0-100)
|
||||
input int Numberofcandles_Back_RSI = 20; // RSI lookback period for analysis (1-100)
|
||||
|
||||
input group "Condition 4"
|
||||
input group "STOCH INDICATOR"
|
||||
input bool STOCH = true; // Enable Stochastic indicator
|
||||
input int K = 5; // Stochastic %K value (0-100)
|
||||
input int D = 3; // Stochastic %D value (0-100)
|
||||
input int STOCHASTIC_LONG = 5; // Stochastic signal length (0-100)
|
||||
input int Numberofcandles_Back_STOCH = 20; // STOCH lookback period for analysis (1-100)
|
||||
|
||||
input group "BreakEven Settings"
|
||||
input bool BreakEven = true; // Enable breakeven function
|
||||
input int BreakEvenShift = 10; // Distance to move stop above breakeven (in pips) (1-100)
|
||||
input int StopNachzienWenn = 50; // Profit level to trigger breakeven (in pips) (1-100)
|
||||
|
||||
input group "Trallingstop Settings"
|
||||
input bool Trall = false; // Enable trailing stop
|
||||
input TRAILING_TYPE traillingstop = TRAIL_CONTINUOUS; // Trailing algorithm type: 1=continuous, 2=step
|
||||
input int TralStop = 30; // Trailing distance behind price (in pips) (1-2000)
|
||||
input int TralStep = 10; // Minimum move before trail adjusts (in pips) (1-2000)
|
||||
|
||||
input group "Visual Settings"
|
||||
input color RectangleColor = clrLimeGreen; // Rectangle color
|
||||
|
||||
//============================ Global Variables =====================
|
||||
int rsiDivergenceHandle = INVALID_HANDLE;
|
||||
int stochasticsDivergenceHandle = INVALID_HANDLE;
|
||||
int klingerDivergenceHandle = INVALID_HANDLE;
|
||||
|
||||
datetime prevBarTime = 0;
|
||||
int globalConfluenceCount;
|
||||
string rectangleName = "LookbackRectangle";
|
||||
|
||||
double pointValue;
|
||||
ENUM_ORDER_TYPE_FILLING orderFill;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
// Check autotrading
|
||||
if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) {
|
||||
Alert("Please enable autotrading on terminal to take trades");
|
||||
//return INIT_FAILED;
|
||||
}
|
||||
|
||||
// Initialize point value
|
||||
pointValue = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
||||
|
||||
// Set filling policy
|
||||
uint filling = (uint)SymbolInfoInteger(_Symbol, SYMBOL_FILLING_MODE);
|
||||
if((filling & SYMBOL_FILLING_FOK) == SYMBOL_FILLING_FOK) {
|
||||
orderFill = ORDER_FILLING_FOK;
|
||||
}
|
||||
else if((filling & SYMBOL_FILLING_IOC) == SYMBOL_FILLING_IOC) {
|
||||
orderFill = ORDER_FILLING_IOC;
|
||||
}
|
||||
else {
|
||||
orderFill = ORDER_FILLING_RETURN;
|
||||
}
|
||||
|
||||
// Initialize confluence count (automatically calculated from enabled indicators)
|
||||
int enabledIndicators = 0;
|
||||
if(RSI) enabledIndicators++;
|
||||
if(STOCH) enabledIndicators++;
|
||||
if(KO) enabledIndicators++;
|
||||
|
||||
if(enabledIndicators == 0) {
|
||||
Alert("At least one indicator must be enabled!");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
// Set confluence count: require at least 1 signal, up to all enabled indicators
|
||||
globalConfluenceCount = MathMax(1, MathMin(2, enabledIndicators));
|
||||
|
||||
// Create indicator handles
|
||||
if(RSI) {
|
||||
rsiDivergenceHandle = iCustom(_Symbol, PERIOD_CURRENT, "Divergence RSI");
|
||||
if(rsiDivergenceHandle == INVALID_HANDLE) {
|
||||
Print("Failed to create RSI Divergence indicator handle");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
ChartIndicatorAdd(0,1,rsiDivergenceHandle);
|
||||
}
|
||||
|
||||
if(STOCH) {
|
||||
stochasticsDivergenceHandle = iCustom(_Symbol, PERIOD_CURRENT, "StochasticsDivergence");
|
||||
if(stochasticsDivergenceHandle == INVALID_HANDLE) {
|
||||
Print("Failed to create Stochastics Divergence indicator handle");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
ChartIndicatorAdd(0,2,stochasticsDivergenceHandle);
|
||||
}
|
||||
|
||||
if(KO) {
|
||||
klingerDivergenceHandle = iCustom(_Symbol, PERIOD_CURRENT, "KlingerDivergence");
|
||||
if(klingerDivergenceHandle == INVALID_HANDLE) {
|
||||
Print("Failed to create Klinger Divergence indicator handle");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
ChartIndicatorAdd(0,3,klingerDivergenceHandle);
|
||||
}
|
||||
|
||||
// Initialize previous bar time
|
||||
prevBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
|
||||
|
||||
// Draw initial rectangle
|
||||
DrawLookbackRectangle();
|
||||
|
||||
Print("DivergenceEA initialized successfully");
|
||||
Print("Enabled indicators: RSI=", RSI, " STOCH=", STOCH, " KO=", KO);
|
||||
Print("Confluence count: ", globalConfluenceCount);
|
||||
Print("Candlestick validation period: ", Numberofcandles_Back, " bars");
|
||||
Print("Magic number: ", Magic, ", Volume type: ", (volumetype == VOLUME_FIXED ? "FIXED" : "PROCENT"));
|
||||
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
// Release indicator handles
|
||||
if(rsiDivergenceHandle != INVALID_HANDLE) {
|
||||
IndicatorRelease(rsiDivergenceHandle);
|
||||
}
|
||||
if(stochasticsDivergenceHandle != INVALID_HANDLE) {
|
||||
IndicatorRelease(stochasticsDivergenceHandle);
|
||||
}
|
||||
if(klingerDivergenceHandle != INVALID_HANDLE) {
|
||||
IndicatorRelease(klingerDivergenceHandle);
|
||||
}
|
||||
|
||||
// Delete rectangle
|
||||
ObjectDelete(0, rectangleName);
|
||||
|
||||
Print("DivergenceEA deinitialized, reason: ", reason);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
// Trade management (runs on every tick)
|
||||
ManageOpenPositions();
|
||||
|
||||
// Check for new bar
|
||||
if(IsNewBar()) {
|
||||
Print("New bar detected, analyzing signals...");
|
||||
|
||||
// Create signal array and analyze
|
||||
SignalInfo signals[];
|
||||
AnalyzeSignals(signals);
|
||||
|
||||
// Check for entry signals
|
||||
CheckEntrySignals(signals);
|
||||
|
||||
// Update rectangle
|
||||
DrawLookbackRectangle();
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if new bar formed |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsNewBar()
|
||||
{
|
||||
datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
|
||||
if(currentBarTime != prevBarTime) {
|
||||
prevBarTime = currentBarTime;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Analyze signals from all enabled indicators |
|
||||
//+------------------------------------------------------------------+
|
||||
void AnalyzeSignals(SignalInfo &signals[])
|
||||
{
|
||||
int signalCount = 0;
|
||||
|
||||
// Count enabled indicators
|
||||
if(RSI) signalCount++;
|
||||
if(STOCH) signalCount++;
|
||||
if(KO) signalCount++;
|
||||
|
||||
ArrayResize(signals, signalCount);
|
||||
int index = 0;
|
||||
|
||||
// Analyze RSI Divergence
|
||||
if(RSI && rsiDivergenceHandle != INVALID_HANDLE) {
|
||||
signals[index] = GetLatestSignal(rsiDivergenceHandle, 0, 1); // Buy buffer 0, Sell buffer 1
|
||||
index++;
|
||||
}
|
||||
|
||||
// Analyze Stochastics Divergence
|
||||
if(STOCH && stochasticsDivergenceHandle != INVALID_HANDLE) {
|
||||
signals[index] = GetLatestSignal(stochasticsDivergenceHandle, 1, 2); // Buy buffer 1, Sell buffer 2
|
||||
index++;
|
||||
}
|
||||
|
||||
// Analyze Klinger Divergence
|
||||
if(KO && klingerDivergenceHandle != INVALID_HANDLE) {
|
||||
signals[index] = GetLatestSignal(klingerDivergenceHandle, 2, 3); // Buy buffer 2, Sell buffer 3
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get latest signal from indicator within lookback period |
|
||||
//+------------------------------------------------------------------+
|
||||
SignalInfo GetLatestSignal(int handle, int buyBuffer, int sellBuffer)
|
||||
{
|
||||
SignalInfo signal;
|
||||
double buyValues[], sellValues[];
|
||||
|
||||
// Determine lookback period based on indicator type
|
||||
int lookbackPeriod = 20; // default
|
||||
string indicatorName = "Unknown";
|
||||
if(handle == rsiDivergenceHandle) {
|
||||
lookbackPeriod = Numberofcandles_Back_RSI;
|
||||
indicatorName = "RSI";
|
||||
}
|
||||
else if(handle == stochasticsDivergenceHandle) {
|
||||
lookbackPeriod = Numberofcandles_Back_STOCH;
|
||||
indicatorName = "STOCH";
|
||||
}
|
||||
else if(handle == klingerDivergenceHandle) {
|
||||
lookbackPeriod = Numberofcandles_Back_KO;
|
||||
indicatorName = "KLINGER";
|
||||
}
|
||||
|
||||
Print("Checking ", indicatorName, " indicator (Handle=", handle, ") buffers ", buyBuffer, "/", sellBuffer, " over ", lookbackPeriod, " bars");
|
||||
|
||||
// Copy buffers for the lookback period
|
||||
int buyCount = CopyBuffer(handle, buyBuffer, 1, lookbackPeriod, buyValues);
|
||||
int sellCount = CopyBuffer(handle, sellBuffer, 1, lookbackPeriod, sellValues);
|
||||
|
||||
if(buyCount <= 0 || sellCount <= 0) {
|
||||
Print("ERROR: Failed to copy ", indicatorName, " buffers - Buy copied: ", buyCount, ", Sell copied: ", sellCount);
|
||||
return signal;
|
||||
}
|
||||
|
||||
Print("Successfully copied ", indicatorName, " buffers - Buy: ", buyCount, ", Sell: ", sellCount);
|
||||
|
||||
// Search for latest signal (starting from most recent)
|
||||
for(int i = 0; i < lookbackPeriod; i++) {
|
||||
// Check for buy signal
|
||||
if(buyValues[i] != EMPTY_VALUE && buyValues[i] != 0) {
|
||||
Print(indicatorName, " BUY signal found at shift ", i+1, " with value ", NormalizeDouble(buyValues[i], 5));
|
||||
signal.signal = SIGNAL_BUY;
|
||||
signal.shift = i + 1; // Adjust for the fact we're looking at completed bars
|
||||
break;
|
||||
}
|
||||
|
||||
// Check for sell signal
|
||||
if(sellValues[i] != EMPTY_VALUE && sellValues[i] != 0) {
|
||||
Print(indicatorName, " SELL signal found at shift ", i+1, " with value ", NormalizeDouble(sellValues[i], 5));
|
||||
signal.signal = SIGNAL_SELL;
|
||||
signal.shift = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(signal.signal == SIGNAL_NULL)
|
||||
Print("No ", indicatorName, " signals found in ", lookbackPeriod, " bars");
|
||||
|
||||
return signal;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check entry signals and execute trades |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckEntrySignals(SignalInfo &signals[])
|
||||
{
|
||||
int buySignals = 0;
|
||||
int sellSignals = 0;
|
||||
bool hasRecentBuySignal = false;
|
||||
bool hasRecentSellSignal = false;
|
||||
|
||||
Print("=== SIGNAL ANALYSIS START ===");
|
||||
Print("Total signals to analyze: ", ArraySize(signals));
|
||||
Print("Required confluence count: ", globalConfluenceCount);
|
||||
|
||||
// Count signals and check for recent signals on last formed candle (shift = 1)
|
||||
for(int i = 0; i < ArraySize(signals); i++) {
|
||||
Print("Signal[", i, "]: Type=", EnumToString(signals[i].signal), " Shift=", signals[i].shift);
|
||||
|
||||
if(signals[i].signal == SIGNAL_BUY) {
|
||||
buySignals++;
|
||||
if(signals[i].shift == 1) hasRecentBuySignal = true;
|
||||
}
|
||||
else if(signals[i].signal == SIGNAL_SELL) {
|
||||
sellSignals++;
|
||||
if(signals[i].shift == 1) hasRecentSellSignal = true;
|
||||
}
|
||||
}
|
||||
|
||||
Print("Signal count - Buy: ", buySignals, " (recent: ", hasRecentBuySignal,
|
||||
"), Sell: ", sellSignals, " (recent: ", hasRecentSellSignal, ")");
|
||||
Print("Position filter: ", EnumToString(Position));
|
||||
|
||||
// Execute buy trade (with candlestick validation)
|
||||
if(hasRecentBuySignal && buySignals >= globalConfluenceCount &&
|
||||
(Position == POSITION_BOTH || Position == POSITION_BUY_ONLY)) {
|
||||
|
||||
Print("BUY conditions met - checking candlestick validation...");
|
||||
// Check for lower low condition
|
||||
if(ValidateCandlestickCondition(ORDER_TYPE_BUY)) {
|
||||
Print(">>> EXECUTING BUY TRADE <<<");
|
||||
ExecuteTrade(ORDER_TYPE_BUY);
|
||||
}
|
||||
else {
|
||||
Print("BUY signals detected but candlestick validation failed - trade rejected");
|
||||
}
|
||||
}
|
||||
else {
|
||||
Print("BUY conditions NOT met: recentSignal=", hasRecentBuySignal,
|
||||
" signalCount>=", buySignals, ">=" , globalConfluenceCount,
|
||||
" positionFilter=", (Position == POSITION_BOTH || Position == POSITION_BUY_ONLY));
|
||||
}
|
||||
|
||||
// Execute sell trade (with candlestick validation)
|
||||
if(hasRecentSellSignal && sellSignals >= globalConfluenceCount &&
|
||||
(Position == POSITION_BOTH || Position == POSITION_SELL_ONLY)) {
|
||||
|
||||
Print("SELL conditions met - checking candlestick validation...");
|
||||
// Check for higher high condition
|
||||
if(ValidateCandlestickCondition(ORDER_TYPE_SELL)) {
|
||||
Print(">>> EXECUTING SELL TRADE <<<");
|
||||
ExecuteTrade(ORDER_TYPE_SELL);
|
||||
}
|
||||
else {
|
||||
Print("SELL signals detected but candlestick validation failed - trade rejected");
|
||||
}
|
||||
}
|
||||
else {
|
||||
Print("SELL conditions NOT met: recentSignal=", hasRecentSellSignal,
|
||||
" signalCount>=", sellSignals, ">=" , globalConfluenceCount,
|
||||
" positionFilter=", (Position == POSITION_BOTH || Position == POSITION_SELL_ONLY));
|
||||
}
|
||||
|
||||
Print("=== SIGNAL ANALYSIS END ===");
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Execute trade |
|
||||
//+------------------------------------------------------------------+
|
||||
void ExecuteTrade(ENUM_ORDER_TYPE orderType)
|
||||
{
|
||||
double volume = CalculateVolume();
|
||||
if(volume <= 0) {
|
||||
Print("Invalid volume calculated: ", volume);
|
||||
return;
|
||||
}
|
||||
|
||||
double price, sl, tp;
|
||||
|
||||
if(orderType == ORDER_TYPE_BUY) {
|
||||
price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
sl = price - Sl_Point_pip * pointValue * 10;
|
||||
tp = price + Tp_Point_pip * pointValue * 10;
|
||||
}
|
||||
else {
|
||||
price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
sl = price + Sl_Point_pip * pointValue * 10;
|
||||
tp = price - Tp_Point_pip * pointValue * 10;
|
||||
}
|
||||
|
||||
MqlTradeRequest request = {};
|
||||
MqlTradeResult result = {};
|
||||
|
||||
request.action = TRADE_ACTION_DEAL;
|
||||
request.symbol = _Symbol;
|
||||
request.volume = NormalizeDouble(volume, 2);
|
||||
request.type = orderType;
|
||||
request.price = price;
|
||||
request.sl = NormalizeDouble(sl, _Digits);
|
||||
request.tp = NormalizeDouble(tp, _Digits);
|
||||
request.deviation = 10;
|
||||
request.type_filling = orderFill;
|
||||
request.magic = Magic;
|
||||
request.comment = comentar;
|
||||
|
||||
bool success = OrderSend(request, result);
|
||||
|
||||
if(success && result.retcode == TRADE_RETCODE_DONE) {
|
||||
Print("Trade executed successfully - ", (orderType == ORDER_TYPE_BUY ? "BUY" : "SELL"),
|
||||
" Volume: ", volume, " Price: ", price);
|
||||
}
|
||||
else {
|
||||
Print("Trade execution failed - Result code: ", result.retcode, " Comment: ", result.comment);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate volume based on risk management |
|
||||
//+------------------------------------------------------------------+
|
||||
double CalculateVolume()
|
||||
{
|
||||
double volume = Lots;
|
||||
|
||||
if(volumetype == VOLUME_RISK) {
|
||||
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
|
||||
double riskAmount = balance * Risk / 100.0;
|
||||
double slPoints = Sl_Point_pip * pointValue * 10;
|
||||
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
|
||||
|
||||
if(slPoints > 0 && tickValue > 0) {
|
||||
volume = riskAmount / (slPoints * tickValue / pointValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize volume
|
||||
double minVolume = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
|
||||
double maxVolume = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
|
||||
double volumeStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
|
||||
|
||||
volume = MathMax(volume, minVolume);
|
||||
volume = MathMin(volume, maxVolume);
|
||||
volume = NormalizeDouble(MathRound(volume / volumeStep) * volumeStep, 2);
|
||||
|
||||
return volume;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Manage open positions (breakeven and trailing stop) |
|
||||
//+------------------------------------------------------------------+
|
||||
void ManageOpenPositions()
|
||||
{
|
||||
for(int i = 0; i < PositionsTotal(); i++) {
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
|
||||
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
|
||||
PositionGetInteger(POSITION_MAGIC) == Magic) {
|
||||
|
||||
// Apply breakeven
|
||||
if(BreakEven) {
|
||||
ApplyBreakeven(ticket);
|
||||
}
|
||||
|
||||
// Apply trailing stop
|
||||
if(Trall) {
|
||||
ApplyTrailingStop(ticket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Apply breakeven to position |
|
||||
//+------------------------------------------------------------------+
|
||||
void ApplyBreakeven(ulong ticket)
|
||||
{
|
||||
if(!PositionSelectByTicket(ticket)) return;
|
||||
|
||||
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
double currentSL = PositionGetDouble(POSITION_SL);
|
||||
double currentTP = PositionGetDouble(POSITION_TP);
|
||||
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
|
||||
double currentPrice = (posType == POSITION_TYPE_BUY) ?
|
||||
SymbolInfoDouble(_Symbol, SYMBOL_BID) :
|
||||
SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
|
||||
double profitPoints = MathAbs(currentPrice - openPrice) / (pointValue * 10);
|
||||
|
||||
if(profitPoints >= StopNachzienWenn) {
|
||||
double newSL = 0;
|
||||
bool shouldModify = false;
|
||||
|
||||
if(posType == POSITION_TYPE_BUY) {
|
||||
newSL = openPrice + BreakEvenShift * pointValue * 10;
|
||||
if(newSL > currentSL) shouldModify = true;
|
||||
}
|
||||
else {
|
||||
newSL = openPrice - BreakEvenShift * pointValue * 10;
|
||||
if(newSL < currentSL || currentSL == 0) shouldModify = true;
|
||||
}
|
||||
|
||||
if(shouldModify) {
|
||||
ModifyPosition(ticket, newSL, currentTP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Apply trailing stop to position |
|
||||
//+------------------------------------------------------------------+
|
||||
void ApplyTrailingStop(ulong ticket)
|
||||
{
|
||||
if(!PositionSelectByTicket(ticket)) return;
|
||||
|
||||
double currentSL = PositionGetDouble(POSITION_SL);
|
||||
double currentTP = PositionGetDouble(POSITION_TP);
|
||||
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
|
||||
double currentPrice = (posType == POSITION_TYPE_BUY) ?
|
||||
SymbolInfoDouble(_Symbol, SYMBOL_BID) :
|
||||
SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
|
||||
double newSL = 0;
|
||||
bool shouldModify = false;
|
||||
|
||||
if(posType == POSITION_TYPE_BUY) {
|
||||
newSL = currentPrice - TralStop * pointValue * 10;
|
||||
|
||||
if(traillingstop == TRAIL_STEP) {
|
||||
double moveRequired = TralStep * pointValue * 10;
|
||||
if((newSL - currentSL) >= moveRequired) shouldModify = true;
|
||||
}
|
||||
else {
|
||||
if(newSL > currentSL) shouldModify = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
newSL = currentPrice + TralStop * pointValue * 10;
|
||||
|
||||
if(traillingstop == TRAIL_STEP) {
|
||||
double moveRequired = TralStep * pointValue * 10;
|
||||
if((currentSL - newSL) >= moveRequired || currentSL == 0) shouldModify = true;
|
||||
}
|
||||
else {
|
||||
if(newSL < currentSL || currentSL == 0) shouldModify = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(shouldModify) {
|
||||
ModifyPosition(ticket, newSL, currentTP);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Modify position stop loss and take profit |
|
||||
//+------------------------------------------------------------------+
|
||||
bool ModifyPosition(ulong ticket, double newSL, double tp)
|
||||
{
|
||||
MqlTradeRequest request = {};
|
||||
MqlTradeResult result = {};
|
||||
|
||||
request.action = TRADE_ACTION_SLTP;
|
||||
request.position = ticket;
|
||||
request.symbol = _Symbol;
|
||||
request.sl = NormalizeDouble(newSL, _Digits);
|
||||
request.tp = NormalizeDouble(tp, _Digits);
|
||||
|
||||
bool success = OrderSend(request, result);
|
||||
|
||||
if(!success || result.retcode != TRADE_RETCODE_DONE) {
|
||||
Print("Failed to modify position SL. Error: ", GetLastError(), " Result code: ", result.retcode);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Validate candlestick condition for entry |
|
||||
//+------------------------------------------------------------------+
|
||||
bool ValidateCandlestickCondition(ENUM_ORDER_TYPE orderType)
|
||||
{
|
||||
if(Numberofcandles_Back <= 0) {
|
||||
return true; // Skip validation if period is 0 or negative
|
||||
}
|
||||
|
||||
// Get current candle (shift 1 = last completed candle) price levels
|
||||
double currentHigh = iHigh(_Symbol, PERIOD_CURRENT, 1);
|
||||
double currentLow = iLow(_Symbol, PERIOD_CURRENT, 1);
|
||||
|
||||
if(orderType == ORDER_TYPE_SELL) {
|
||||
// For sell signal: current candle high should be higher than all previous X candle highs
|
||||
for(int i = 2; i <= Numberofcandles_Back + 1; i++) {
|
||||
double compareHigh = iHigh(_Symbol, PERIOD_CURRENT, i);
|
||||
if(currentHigh <= compareHigh) {
|
||||
Print("Sell candlestick validation failed: Current high (",
|
||||
DoubleToString(currentHigh, _Digits), ") not higher than candle at shift ", i,
|
||||
" high (", DoubleToString(compareHigh, _Digits), ")");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Print("Sell candlestick validation passed: Current high (",
|
||||
DoubleToString(currentHigh, _Digits), ") is higher than all ",
|
||||
Numberofcandles_Back, " previous highs");
|
||||
return true;
|
||||
}
|
||||
else if(orderType == ORDER_TYPE_BUY) {
|
||||
// For buy signal: current candle low should be lower than all previous X candle lows
|
||||
for(int i = 2; i <= Numberofcandles_Back + 1; i++) {
|
||||
double compareLow = iLow(_Symbol, PERIOD_CURRENT, i);
|
||||
if(currentLow >= compareLow) {
|
||||
Print("Buy candlestick validation failed: Current low (",
|
||||
DoubleToString(currentLow, _Digits), ") not lower than candle at shift ", i,
|
||||
" low (", DoubleToString(compareLow, _Digits), ")");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Print("Buy candlestick validation passed: Current low (",
|
||||
DoubleToString(currentLow, _Digits), ") is lower than all ",
|
||||
Numberofcandles_Back, " previous lows");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Draw lookback rectangle |
|
||||
//+------------------------------------------------------------------+
|
||||
void DrawLookbackRectangle()
|
||||
{
|
||||
datetime currentTime = iTime(_Symbol, PERIOD_CURRENT, 0);
|
||||
datetime lookbackTime = iTime(_Symbol, PERIOD_CURRENT, MathMax(Numberofcandles_Back_RSI, MathMax(Numberofcandles_Back_STOCH, Numberofcandles_Back_KO)));
|
||||
|
||||
double highPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK) * 1.1; // Chart top
|
||||
double lowPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID) * 0.9; // Chart bottom
|
||||
|
||||
// Delete existing rectangle if it exists
|
||||
ObjectDelete(0, rectangleName);
|
||||
|
||||
// Create new rectangle
|
||||
if(ObjectCreate(0, rectangleName, OBJ_RECTANGLE, 0, lookbackTime, lowPrice, currentTime, highPrice)) {
|
||||
ObjectSetInteger(0, rectangleName, OBJPROP_COLOR, RectangleColor);
|
||||
ObjectSetInteger(0, rectangleName, OBJPROP_STYLE, STYLE_SOLID);
|
||||
ObjectSetInteger(0, rectangleName, OBJPROP_WIDTH, 1);
|
||||
ObjectSetInteger(0, rectangleName, OBJPROP_BACK, true);
|
||||
ObjectSetInteger(0, rectangleName, OBJPROP_FILL, true);
|
||||
ObjectSetInteger(0, rectangleName, OBJPROP_HIDDEN, true);
|
||||
int maxLookback = MathMax(Numberofcandles_Back_RSI, MathMax(Numberofcandles_Back_STOCH, Numberofcandles_Back_KO));
|
||||
ObjectSetString(0, rectangleName, OBJPROP_TOOLTIP, "Max Lookback Period: " + IntegerToString(maxLookback) + " candles");
|
||||
|
||||
// Make it almost transparent
|
||||
color rectColor = RectangleColor;
|
||||
ObjectSetInteger(0, rectangleName, OBJPROP_COLOR, ColorToARGB(rectColor, 20)); // 20 out of 255 alpha
|
||||
}
|
||||
|
||||
ChartRedraw(0);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,608 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| TriDivergenceEA.mq5 |
|
||||
//| Copyright 2025, Teenodi Ltd. |
|
||||
//| https://www.jukwaese.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025, Teenodi Ltd."
|
||||
#property link "val.chioke@gmail.com"
|
||||
#property version "1.00"
|
||||
#property description "EA that trades based on divergence signals from RSI, Stochastic, and Klinger indicators"
|
||||
#property strict
|
||||
#property tester_indicator "Divergence RSI.ex5"
|
||||
#property tester_indicator "StochasticsDivergence.ex5"
|
||||
#property tester_indicator "KlingerDivergence.ex5"
|
||||
#property indicator_plots 3
|
||||
#property indicator_separate_window
|
||||
|
||||
//============================ Enums ===============================
|
||||
enum SIGNAL_TYPE {
|
||||
SIGNAL_NULL, // No Signal
|
||||
SIGNAL_BUY, // Buy Signal
|
||||
SIGNAL_SELL // Sell Signal
|
||||
};
|
||||
|
||||
enum VOLUME_TYPE {
|
||||
VOLUME_FIXED = 1, // Fixed Lots
|
||||
VOLUME_RISK = 2 // Percentage Risk
|
||||
};
|
||||
|
||||
enum POSITION_TYPE_FILTER {
|
||||
POSITION_BUY_ONLY = 1, // Buy Only
|
||||
POSITION_SELL_ONLY = 2, // Sell Only
|
||||
POSITION_BOTH = 3 // Both
|
||||
};
|
||||
|
||||
enum TRAILING_TYPE {
|
||||
TRAIL_CONTINUOUS, // Continuous Trail
|
||||
TRAIL_STEP // Step Trail
|
||||
};
|
||||
|
||||
//============================ Structures ===========================
|
||||
struct SignalInfo {
|
||||
SIGNAL_TYPE signal;
|
||||
int shift;
|
||||
|
||||
SignalInfo() {
|
||||
signal = SIGNAL_NULL;
|
||||
shift = -1;
|
||||
}
|
||||
};
|
||||
|
||||
//============================ Inputs ===============================
|
||||
input group "Trade Execution Settings"
|
||||
input int MagicNumber = 100001; // Magic number for trade identification
|
||||
input string Commentary = "DivergenceEA"; // Trade comment field
|
||||
input VOLUME_TYPE VolumeType = VOLUME_RISK; // Volume type: 1=Fixed lots, 2=Percentage risk
|
||||
input double Risk = 2.0; // Risk percentage per trade (0.1-30%)
|
||||
input double Lots = 0.1; // Fixed lot size (0.01-100)
|
||||
input int SlPointPip = 100; // Stop loss in pips
|
||||
input int TpPointPip = 200; // Take profit in pips
|
||||
input bool PendingOrder = false; // Enable pending orders
|
||||
input POSITION_TYPE_FILTER Position = POSITION_BOTH; // Trade direction
|
||||
input int NumberOfCandlesBack = 20; // Lookback period for analysis (1-100)
|
||||
input int ConfluenceCount = 2; // Confluence count (1 to 3)
|
||||
|
||||
input group "Klinger Oscillator Settings"
|
||||
input bool KO = true; // Enable Klinger Oscillator
|
||||
input int KLINGER_LENGTH1 = 34; // First Klinger parameter (0-100)
|
||||
input int KLINGER_LENGTH2 = 55; // Second Klinger parameter (0-100)
|
||||
input int SIGNAL_LONG = 13; // Klinger signal length (0-100)
|
||||
|
||||
input group "RSI Settings"
|
||||
input bool RSI = true; // Enable RSI indicator
|
||||
input int RSI_UPPER = 70; // RSI upper threshold (0-100)
|
||||
input int RSI_LOWER = 30; // RSI lower threshold (0-100)
|
||||
input int RSI_LONG = 14; // RSI signal length (0-100)
|
||||
|
||||
input group "Stochastic Settings"
|
||||
input bool STOCH = false; // Enable Stochastic indicator
|
||||
input int K = 5; // Stochastic %K value (0-100)
|
||||
input int D = 3; // Stochastic %D value (0-100)
|
||||
input int STOCHASTIC_LONG = 5; // Stochastic signal length (0-100)
|
||||
|
||||
input group "Trade Management"
|
||||
input bool BreakEven = true; // Enable breakeven function
|
||||
input int BreakEvenShift = 10; // Distance to move stop above breakeven (in pips)
|
||||
input int StopNachzienWenn = 50; // Profit level to trigger breakeven (in pips)
|
||||
input bool Trail = false; // Enable trailing stop
|
||||
input TRAILING_TYPE TrailingStop = TRAIL_CONTINUOUS; // Trailing algorithm type
|
||||
input int TralStop = 30; // Trailing distance behind price (in pips)
|
||||
input int TralStep = 10; // Minimum move before trail adjusts (in pips)
|
||||
|
||||
input group "Visual Settings"
|
||||
input color RectangleColor = clrDarkSlateGray; // Rectangle color
|
||||
|
||||
//============================ Global Variables =====================
|
||||
int rsiDivergenceHandle = INVALID_HANDLE;
|
||||
int stochasticsDivergenceHandle = INVALID_HANDLE;
|
||||
int klingerDivergenceHandle = INVALID_HANDLE;
|
||||
|
||||
datetime prevBarTime = 0;
|
||||
int globalConfluenceCount;
|
||||
string rectangleName = "LookbackRectangle";
|
||||
|
||||
double pointValue;
|
||||
ENUM_ORDER_TYPE_FILLING orderFill;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
// Check autotrading
|
||||
if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) {
|
||||
Alert("Please enable autotrading on terminal to take trades");
|
||||
//return INIT_FAILED;
|
||||
}
|
||||
|
||||
// Initialize point value
|
||||
pointValue = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
||||
|
||||
// Set filling policy
|
||||
uint filling = (uint)SymbolInfoInteger(_Symbol, SYMBOL_FILLING_MODE);
|
||||
if((filling & SYMBOL_FILLING_FOK) == SYMBOL_FILLING_FOK) {
|
||||
orderFill = ORDER_FILLING_FOK;
|
||||
}
|
||||
else if((filling & SYMBOL_FILLING_IOC) == SYMBOL_FILLING_IOC) {
|
||||
orderFill = ORDER_FILLING_IOC;
|
||||
}
|
||||
else {
|
||||
orderFill = ORDER_FILLING_RETURN;
|
||||
}
|
||||
|
||||
// Initialize confluence count
|
||||
int enabledIndicators = 0;
|
||||
if(RSI) enabledIndicators++;
|
||||
if(STOCH) enabledIndicators++;
|
||||
if(KO) enabledIndicators++;
|
||||
|
||||
if(enabledIndicators == 0) {
|
||||
Alert("At least one indicator must be enabled!");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
globalConfluenceCount = MathMin(ConfluenceCount, enabledIndicators);
|
||||
|
||||
// Create indicator handles
|
||||
if(RSI) {
|
||||
rsiDivergenceHandle = iCustom(_Symbol, PERIOD_CURRENT, "Divergence RSI");
|
||||
if(rsiDivergenceHandle == INVALID_HANDLE) {
|
||||
Print("Failed to create RSI Divergence indicator handle");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
ChartIndicatorAdd(0,1,rsiDivergenceHandle);
|
||||
}
|
||||
|
||||
if(STOCH) {
|
||||
stochasticsDivergenceHandle = iCustom(_Symbol, PERIOD_CURRENT, "StochasticsDivergence");
|
||||
if(stochasticsDivergenceHandle == INVALID_HANDLE) {
|
||||
Print("Failed to create Stochastics Divergence indicator handle");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
ChartIndicatorAdd(0,2,stochasticsDivergenceHandle);
|
||||
}
|
||||
|
||||
if(KO) {
|
||||
klingerDivergenceHandle = iCustom(_Symbol, PERIOD_CURRENT, "KlingerDivergence");
|
||||
if(klingerDivergenceHandle == INVALID_HANDLE) {
|
||||
Print("Failed to create Klinger Divergence indicator handle");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
ChartIndicatorAdd(0,3,klingerDivergenceHandle);
|
||||
}
|
||||
|
||||
// Initialize previous bar time
|
||||
prevBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
|
||||
|
||||
// Draw initial rectangle
|
||||
DrawLookbackRectangle();
|
||||
|
||||
Print("DivergenceEA initialized successfully");
|
||||
Print("Enabled indicators: RSI=", RSI, " STOCH=", STOCH, " KO=", KO);
|
||||
Print("Confluence count: ", globalConfluenceCount);
|
||||
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
// Release indicator handles
|
||||
if(rsiDivergenceHandle != INVALID_HANDLE) {
|
||||
IndicatorRelease(rsiDivergenceHandle);
|
||||
}
|
||||
if(stochasticsDivergenceHandle != INVALID_HANDLE) {
|
||||
IndicatorRelease(stochasticsDivergenceHandle);
|
||||
}
|
||||
if(klingerDivergenceHandle != INVALID_HANDLE) {
|
||||
IndicatorRelease(klingerDivergenceHandle);
|
||||
}
|
||||
|
||||
// Delete rectangle
|
||||
ObjectDelete(0, rectangleName);
|
||||
|
||||
Print("DivergenceEA deinitialized, reason: ", reason);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
// Trade management (runs on every tick)
|
||||
ManageOpenPositions();
|
||||
|
||||
// Check for new bar
|
||||
if(IsNewBar()) {
|
||||
Print("New bar detected, analyzing signals...");
|
||||
|
||||
// Create signal array and analyze
|
||||
SignalInfo signals[];
|
||||
AnalyzeSignals(signals);
|
||||
|
||||
// Check for entry signals
|
||||
CheckEntrySignals(signals);
|
||||
|
||||
// Update rectangle
|
||||
DrawLookbackRectangle();
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if new bar formed |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsNewBar()
|
||||
{
|
||||
datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
|
||||
if(currentBarTime != prevBarTime) {
|
||||
prevBarTime = currentBarTime;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Analyze signals from all enabled indicators |
|
||||
//+------------------------------------------------------------------+
|
||||
void AnalyzeSignals(SignalInfo &signals[])
|
||||
{
|
||||
int signalCount = 0;
|
||||
|
||||
// Count enabled indicators
|
||||
if(RSI) signalCount++;
|
||||
if(STOCH) signalCount++;
|
||||
if(KO) signalCount++;
|
||||
|
||||
ArrayResize(signals, signalCount);
|
||||
int index = 0;
|
||||
|
||||
// Analyze RSI Divergence
|
||||
if(RSI && rsiDivergenceHandle != INVALID_HANDLE) {
|
||||
signals[index] = GetLatestSignal(rsiDivergenceHandle, 0, 1); // Buy buffer 0, Sell buffer 1
|
||||
index++;
|
||||
}
|
||||
|
||||
// Analyze Stochastics Divergence
|
||||
if(STOCH && stochasticsDivergenceHandle != INVALID_HANDLE) {
|
||||
signals[index] = GetLatestSignal(stochasticsDivergenceHandle, 1, 2); // Buy buffer 1, Sell buffer 2
|
||||
index++;
|
||||
}
|
||||
|
||||
// Analyze Klinger Divergence
|
||||
if(KO && klingerDivergenceHandle != INVALID_HANDLE) {
|
||||
signals[index] = GetLatestSignal(klingerDivergenceHandle, 2, 3); // Buy buffer 2, Sell buffer 3
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get latest signal from indicator within lookback period |
|
||||
//+------------------------------------------------------------------+
|
||||
SignalInfo GetLatestSignal(int handle, int buyBuffer, int sellBuffer)
|
||||
{
|
||||
SignalInfo signal;
|
||||
double buyValues[], sellValues[];
|
||||
ArraySetAsSeries(buyValues, true);
|
||||
ArraySetAsSeries(sellValues, true);
|
||||
|
||||
// Copy buffers for the lookback period
|
||||
if(CopyBuffer(handle, buyBuffer, 1, NumberOfCandlesBack, buyValues) <= 0 ||
|
||||
CopyBuffer(handle, sellBuffer, 1, NumberOfCandlesBack, sellValues) <= 0) {
|
||||
Print("Failed to copy indicator buffers");
|
||||
return signal;
|
||||
}
|
||||
|
||||
// Search for latest signal (starting from most recent)
|
||||
for(int i = 0; i < NumberOfCandlesBack; i++) {
|
||||
// Check for buy signal
|
||||
if(buyValues[i] != EMPTY_VALUE && buyValues[i] != 0) {
|
||||
signal.signal = SIGNAL_BUY;
|
||||
signal.shift = i + 1; // Adjust for the fact we're looking at completed bars
|
||||
break;
|
||||
}
|
||||
|
||||
// Check for sell signal
|
||||
if(sellValues[i] != EMPTY_VALUE && sellValues[i] != 0) {
|
||||
signal.signal = SIGNAL_SELL;
|
||||
signal.shift = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return signal;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check entry signals and execute trades |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckEntrySignals(SignalInfo &signals[])
|
||||
{
|
||||
int buySignals = 0;
|
||||
int sellSignals = 0;
|
||||
bool hasRecentBuySignal = false;
|
||||
bool hasRecentSellSignal = false;
|
||||
|
||||
// Count signals and check for recent signals on last formed candle (shift = 1)
|
||||
for(int i = 0; i < ArraySize(signals); i++) {
|
||||
if(signals[i].signal == SIGNAL_BUY) {
|
||||
buySignals++;
|
||||
if(signals[i].shift == 1) hasRecentBuySignal = true;
|
||||
}
|
||||
else if(signals[i].signal == SIGNAL_SELL) {
|
||||
sellSignals++;
|
||||
if(signals[i].shift == 1) hasRecentSellSignal = true;
|
||||
}
|
||||
}
|
||||
|
||||
Print("Signal analysis - Buy signals: ", buySignals, " (recent: ", hasRecentBuySignal,
|
||||
"), Sell signals: ", sellSignals, " (recent: ", hasRecentSellSignal, ")");
|
||||
|
||||
// Execute buy trade
|
||||
if(hasRecentBuySignal && buySignals >= globalConfluenceCount &&
|
||||
(Position == POSITION_BOTH || Position == POSITION_BUY_ONLY)) {
|
||||
Print("Buy entry conditions met - executing buy trade");
|
||||
ExecuteTrade(ORDER_TYPE_BUY);
|
||||
}
|
||||
|
||||
// Execute sell trade
|
||||
if(hasRecentSellSignal && sellSignals >= globalConfluenceCount &&
|
||||
(Position == POSITION_BOTH || Position == POSITION_SELL_ONLY)) {
|
||||
Print("Sell entry conditions met - executing sell trade");
|
||||
ExecuteTrade(ORDER_TYPE_SELL);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Execute trade |
|
||||
//+------------------------------------------------------------------+
|
||||
void ExecuteTrade(ENUM_ORDER_TYPE orderType)
|
||||
{
|
||||
double volume = CalculateVolume();
|
||||
if(volume <= 0) {
|
||||
Print("Invalid volume calculated: ", volume);
|
||||
return;
|
||||
}
|
||||
|
||||
double price, sl, tp;
|
||||
|
||||
if(orderType == ORDER_TYPE_BUY) {
|
||||
price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
sl = price - SlPointPip * pointValue * 10;
|
||||
tp = price + TpPointPip * pointValue * 10;
|
||||
}
|
||||
else {
|
||||
price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
sl = price + SlPointPip * pointValue * 10;
|
||||
tp = price - TpPointPip * pointValue * 10;
|
||||
}
|
||||
|
||||
MqlTradeRequest request = {};
|
||||
MqlTradeResult result = {};
|
||||
|
||||
request.action = TRADE_ACTION_DEAL;
|
||||
request.symbol = _Symbol;
|
||||
request.volume = NormalizeDouble(volume, 2);
|
||||
request.type = orderType;
|
||||
request.price = price;
|
||||
request.sl = NormalizeDouble(sl, _Digits);
|
||||
request.tp = NormalizeDouble(tp, _Digits);
|
||||
request.deviation = 10;
|
||||
request.type_filling = orderFill;
|
||||
request.magic = MagicNumber;
|
||||
request.comment = Commentary;
|
||||
|
||||
bool success = OrderSend(request, result);
|
||||
|
||||
if(success && result.retcode == TRADE_RETCODE_DONE) {
|
||||
Print("Trade executed successfully - ", (orderType == ORDER_TYPE_BUY ? "BUY" : "SELL"),
|
||||
" Volume: ", volume, " Price: ", price);
|
||||
}
|
||||
else {
|
||||
Print("Trade execution failed - Result code: ", result.retcode, " Comment: ", result.comment);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate volume based on risk management |
|
||||
//+------------------------------------------------------------------+
|
||||
double CalculateVolume()
|
||||
{
|
||||
double volume = Lots;
|
||||
|
||||
if(VolumeType == VOLUME_RISK) {
|
||||
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
|
||||
double riskAmount = balance * Risk / 100.0;
|
||||
double slPoints = SlPointPip * pointValue * 10;
|
||||
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
|
||||
|
||||
if(slPoints > 0 && tickValue > 0) {
|
||||
volume = riskAmount / (slPoints * tickValue / pointValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize volume
|
||||
double minVolume = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
|
||||
double maxVolume = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
|
||||
double volumeStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
|
||||
|
||||
volume = MathMax(volume, minVolume);
|
||||
volume = MathMin(volume, maxVolume);
|
||||
volume = NormalizeDouble(MathRound(volume / volumeStep) * volumeStep, 2);
|
||||
|
||||
return volume;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Manage open positions (breakeven and trailing stop) |
|
||||
//+------------------------------------------------------------------+
|
||||
void ManageOpenPositions()
|
||||
{
|
||||
for(int i = 0; i < PositionsTotal(); i++) {
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
|
||||
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
|
||||
PositionGetInteger(POSITION_MAGIC) == MagicNumber) {
|
||||
|
||||
// Apply breakeven
|
||||
if(BreakEven) {
|
||||
ApplyBreakeven(ticket);
|
||||
}
|
||||
|
||||
// Apply trailing stop
|
||||
if(Trail) {
|
||||
ApplyTrailingStop(ticket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Apply breakeven to position |
|
||||
//+------------------------------------------------------------------+
|
||||
void ApplyBreakeven(ulong ticket)
|
||||
{
|
||||
if(!PositionSelectByTicket(ticket)) return;
|
||||
|
||||
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
double currentSL = PositionGetDouble(POSITION_SL);
|
||||
double currentTP = PositionGetDouble(POSITION_TP);
|
||||
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
|
||||
double currentPrice = (posType == POSITION_TYPE_BUY) ?
|
||||
SymbolInfoDouble(_Symbol, SYMBOL_BID) :
|
||||
SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
|
||||
double profitPoints = MathAbs(currentPrice - openPrice) / (pointValue * 10);
|
||||
|
||||
if(profitPoints >= StopNachzienWenn) {
|
||||
double newSL = 0;
|
||||
bool shouldModify = false;
|
||||
|
||||
if(posType == POSITION_TYPE_BUY) {
|
||||
newSL = openPrice + BreakEvenShift * pointValue * 10;
|
||||
if(newSL > currentSL) shouldModify = true;
|
||||
}
|
||||
else {
|
||||
newSL = openPrice - BreakEvenShift * pointValue * 10;
|
||||
if(newSL < currentSL || currentSL == 0) shouldModify = true;
|
||||
}
|
||||
|
||||
if(shouldModify) {
|
||||
ModifyPosition(ticket, newSL, currentTP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Apply trailing stop to position |
|
||||
//+------------------------------------------------------------------+
|
||||
void ApplyTrailingStop(ulong ticket)
|
||||
{
|
||||
if(!PositionSelectByTicket(ticket)) return;
|
||||
|
||||
double currentSL = PositionGetDouble(POSITION_SL);
|
||||
double currentTP = PositionGetDouble(POSITION_TP);
|
||||
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
|
||||
double currentPrice = (posType == POSITION_TYPE_BUY) ?
|
||||
SymbolInfoDouble(_Symbol, SYMBOL_BID) :
|
||||
SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
|
||||
double newSL = 0;
|
||||
bool shouldModify = false;
|
||||
|
||||
if(posType == POSITION_TYPE_BUY) {
|
||||
newSL = currentPrice - TralStop * pointValue * 10;
|
||||
|
||||
if(TrailingStop == TRAIL_STEP) {
|
||||
double moveRequired = TralStep * pointValue * 10;
|
||||
if((newSL - currentSL) >= moveRequired) shouldModify = true;
|
||||
}
|
||||
else {
|
||||
if(newSL > currentSL) shouldModify = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
newSL = currentPrice + TralStop * pointValue * 10;
|
||||
|
||||
if(TrailingStop == TRAIL_STEP) {
|
||||
double moveRequired = TralStep * pointValue * 10;
|
||||
if((currentSL - newSL) >= moveRequired || currentSL == 0) shouldModify = true;
|
||||
}
|
||||
else {
|
||||
if(newSL < currentSL || currentSL == 0) shouldModify = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(shouldModify) {
|
||||
ModifyPosition(ticket, newSL, currentTP);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Modify position stop loss and take profit |
|
||||
//+------------------------------------------------------------------+
|
||||
bool ModifyPosition(ulong ticket, double newSL, double tp)
|
||||
{
|
||||
MqlTradeRequest request = {};
|
||||
MqlTradeResult result = {};
|
||||
|
||||
request.action = TRADE_ACTION_SLTP;
|
||||
request.position = ticket;
|
||||
request.symbol = _Symbol;
|
||||
request.sl = NormalizeDouble(newSL, _Digits);
|
||||
request.tp = NormalizeDouble(tp, _Digits);
|
||||
|
||||
bool success = OrderSend(request, result);
|
||||
|
||||
if(!success || result.retcode != TRADE_RETCODE_DONE) {
|
||||
Print("Failed to modify position SL. Error: ", GetLastError(), " Result code: ", result.retcode);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Draw lookback rectangle |
|
||||
//+------------------------------------------------------------------+
|
||||
void DrawLookbackRectangle()
|
||||
{
|
||||
datetime currentTime = iTime(_Symbol, PERIOD_CURRENT, 0);
|
||||
datetime lookbackTime = iTime(_Symbol, PERIOD_CURRENT, NumberOfCandlesBack);
|
||||
|
||||
double highPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK) * 1.1; // Chart top
|
||||
double lowPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID) * 0.9; // Chart bottom
|
||||
|
||||
// Delete existing rectangle if it exists
|
||||
ObjectDelete(0, rectangleName);
|
||||
|
||||
// Create new rectangle
|
||||
if(ObjectCreate(0, rectangleName, OBJ_RECTANGLE, 0, lookbackTime, lowPrice, currentTime, highPrice)) {
|
||||
ObjectSetInteger(0, rectangleName, OBJPROP_COLOR, RectangleColor);
|
||||
ObjectSetInteger(0, rectangleName, OBJPROP_STYLE, STYLE_SOLID);
|
||||
ObjectSetInteger(0, rectangleName, OBJPROP_WIDTH, 1);
|
||||
ObjectSetInteger(0, rectangleName, OBJPROP_BACK, true);
|
||||
ObjectSetInteger(0, rectangleName, OBJPROP_FILL, true);
|
||||
ObjectSetInteger(0, rectangleName, OBJPROP_HIDDEN, true);
|
||||
ObjectSetString(0, rectangleName, OBJPROP_TOOLTIP, "Lookback Period: " + IntegerToString(NumberOfCandlesBack) + " candles");
|
||||
|
||||
// Make it almost transparent
|
||||
color rectColor = RectangleColor;
|
||||
ObjectSetInteger(0, rectangleName, OBJPROP_COLOR, ColorToARGB(rectColor, 20)); // 20 out of 255 alpha
|
||||
}
|
||||
|
||||
ChartRedraw(0);
|
||||
}
|
||||
/*
|
||||
//+------------------------------------------------------------------+
|
||||
//| Convert color to ARGB with alpha transparency |
|
||||
//+------------------------------------------------------------------+
|
||||
uint ColorToARGB(color clr, uchar alpha)
|
||||
{
|
||||
return (uint)(alpha << 24) | (uint)(clr & 0x00FFFFFF);
|
||||
}
|
||||
*/
|
||||
//+------------------------------------------------------------------+
|
||||
Reference in New Issue
Block a user