commit f934e9a90f997183e37939fff4b6575e430d91a7 Author: Valentine Chibuike Ozoigboanugo Date: Sun Jun 14 15:32:12 2026 +0200 Initial commit: TriDivergenceEA Proprietary trading software. © Teenodi Ltd. All rights reserved. Co-Authored-By: Claude Opus 4.8 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..659b368 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# Compiled binaries / logs / terminal artifacts +*.ex4 +*.ex5 +*.log +*.tmp +*.bak diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d7128eb --- /dev/null +++ b/LICENSE @@ -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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..08b6a1f --- /dev/null +++ b/README.md @@ -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). diff --git a/TriDivergenceEA.mq5 b/TriDivergenceEA.mq5 new file mode 100644 index 0000000..386bad5 --- /dev/null +++ b/TriDivergenceEA.mq5 @@ -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); +} +//+------------------------------------------------------------------+ \ No newline at end of file diff --git a/archive/TriDivergenceEA.mq5 b/archive/TriDivergenceEA.mq5 new file mode 100644 index 0000000..2eb8696 --- /dev/null +++ b/archive/TriDivergenceEA.mq5 @@ -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); +} +*/ +//+------------------------------------------------------------------+ \ No newline at end of file