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:
Valentine Chibuike Ozoigboanugo
2026-06-14 15:32:12 +02:00
commit f934e9a90f
5 changed files with 1472 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
# Compiled binaries / logs / terminal artifacts
*.ex4
*.ex5
*.log
*.tmp
*.bak
+10
View File
@@ -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.
+125
View File
@@ -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).
+723
View File
@@ -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);
}
//+------------------------------------------------------------------+
+608
View File
@@ -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);
}
*/
//+------------------------------------------------------------------+