mql5/Experts/CC_ADR_Stoch_Grid_with_Buttons.mq5
2025-12-31 13:48:25 +01:00

1346 lines
No EOL
97 KiB
MQL5

//+------------------------------------------------------------------+
//| ADR_Reversion_Grid_v136.mq5 |
//| Copyright 2025, Algorithm Factory |
//| Feature: CSV Signals + MQL5 Economic Calendar News Filter |
//+------------------------------------------------------------------+
#property copyright "Algorithm Factory"
#property link ""
#property version "1.38"
#property strict
#include <Trade\Trade.mqh>
//--- ENUMS ---
enum ENUM_ENTRY_MODE
{
MODE_ADR = 0, // ADR Entry
MODE_RSI = 1 // RSI Entry
};
enum ENUM_DIR_FILTER
{
FILTER_BOTH = 0, // Long & Short
FILTER_LONG = 1, // Only Long
FILTER_SHORT = 2, // Only Short
FILTER_NEUTRAL = 3 // Neutral
};
enum ENUM_NEWS_IMPORTANCE
{
NEWS_HIGH_ONLY = 0, // Nur High Impact
NEWS_MEDIUM_HIGH = 1 // Medium + High Impact
};
//--- INPUT PARAMETERS ---
input group "=== CSV Signal Integration ==="
input bool InpUseCSVSignals = true; // CSV-Signale nutzen?
input int InpCSVCheckInterval = 15; // CSV Check Interval (Minuten)
input string InpCSVFilename = "last_known_signals.csv"; // CSV Dateiname
input bool InpDebugMode = false; // Debug-Modus (ausführliche Logs)
input group "=== News Filter (MQL5 Calendar) ==="
input bool InpUseNewsFilter = true; // News-Filter aktivieren?
input ENUM_NEWS_IMPORTANCE InpNewsImportance = NEWS_HIGH_ONLY; // Welche News filtern?
input int InpMinutesBeforeNews = 30; // Minuten VOR News
input int InpMinutesAfterNews = 30; // Minuten NACH News
input bool InpNewsFilterBothCurrencies = true; // Beide Währungen prüfen?
input group "--- Direction Filter ---"
input ENUM_DIR_FILTER InpStartDirection = FILTER_BOTH;
input group "--- Positions Management ---"
input double InpFirstLot = 0.03; // Start Lotgröße
input double InpGridLot = 0.03; // Grid Lotgröße
input int InpMaxPositions = 20; // Max Anzahl Positionen
input double InpIndividualSL_ADR = 3.0; // SL pro Trade in ADR
input group "--- Money Management ---"
input bool InpUseMM = false; // MM Aktivieren?
input double InpRefBalance = 10000.0; // Startkapital (Referenz)
input double InpBalanceStep_Pct = 10.0; // Balance Step %
input double InpLotIncrease_Pct = 10.0; // Lot Increase %
input group "--- Entry Logic Selection ---"
input ENUM_ENTRY_MODE InpEntryMode = MODE_ADR; // Entry Mode
input group "--- ADR Settings ---"
input int InpADRPeriod = 14; // ADR Periode
input double InpEntryADR_Pct = 60.0; // Entry: % von ADR
input double InpGridStep_ADR_Pct = 10.0; // Grid Step
input group "--- RSI Settings (D1) ---"
input int InpRSI_Period = 14; // RSI Periode
input int InpRSI_Upper = 70; // RSI Upper
input int InpRSI_Lower = 30; // RSI Lower
input group "--- Dynamic Exit Targets ---"
input double InpStartTarget_ADR = 10.0; // Ziel bei Start
input double InpTargetDecay_ADR = 0.0; // Abzug pro Grid-Level
input group "--- Stochastic Settings (H1) ---"
input int InpStochK = 5;
input int InpStochD = 3;
input int InpStochSlowing = 3;
input int InpStochUpper = 85;
input int InpStochLower = 15;
input group "--- Time Filter ---"
input bool InpUseEODClose = false;
input int InpEODHour = 23;
input int InpEODMinute = 50;
input int InpMagicNumber = 101; // WICHTIG: Unique ID pro Chart!
//--- GLOBALS ---
CTrade trade;
int handleStoch;
int handleRSI = INVALID_HANDLE;
double adrValue = 0.0;
double dayRange = 0.0;
datetime lastProcessedH1Bar = 0;
// Stats & Monitoring
double stat_HighWaterMark = 0.0;
double stat_MaxDD_Equity_Money = 0.0;
double stat_MaxDD_Equity_Percent = 0.0;
string stat_EA_Action = "Initializing...";
// Runtime State for Filter
ENUM_DIR_FILTER g_currentFilter;
// CSV Signal Integration
bool g_CSVMode = false;
ENUM_DIR_FILTER g_CSVDirection = FILTER_NEUTRAL;
datetime g_LastCSVCheck = 0;
string g_CSVStatus = "Not checked yet";
string g_CSVSignalText = "N/A";
// News Filter
bool g_NewsBlockActive = false;
string g_NextNewsInfo = "Keine News";
datetime g_NextNewsTime = 0;
string g_NextNewsName = "";
// Performance Optimization - NEU!
datetime g_LastNewsCheckTime = 0;
int g_NewsCheckInterval = 300; // News nur alle 5 Minuten prüfen
datetime g_LastADRCalcDay = 0;
// GUI Object Names
string btnBothName = "Btn_Dir_Both";
string btnLongName = "Btn_Dir_Long";
string btnShortName = "Btn_Dir_Short";
string btnNeutralName = "Btn_Dir_Neutral";
string lblMaxDDName = "Lbl_Info_MaxDD";
string lblPosName = "Lbl_Info_Pos";
string lblStatusName = "Lbl_Info_Status";
string lblSetupName = "Lbl_Info_Setup";
string lblCSVInfoName = "Lbl_CSV_Info";
string lblCSVTimeName = "Lbl_CSV_Time";
string lblNewsInfoName = "Lbl_News_Info";
string lblCSVDirectionName = "Lbl_CSV_Direction";
//+------------------------------------------------------------------+
//| Init |
//+------------------------------------------------------------------+
int OnInit()
{
// Prüfe ob wir im Strategy Tester sind
bool isInTester = MQLInfoInteger(MQL_TESTER) || MQLInfoInteger(MQL_OPTIMIZATION);
// Im Tester: FILTER_NEUTRAL macht keinen Sinn (würde alle Trades blockieren)
// Setze auf FILTER_BOTH wenn NEUTRAL gewählt wurde
if(isInTester && InpStartDirection == FILTER_NEUTRAL)
{
g_currentFilter = FILTER_BOTH;
Print("WARNUNG: FILTER_NEUTRAL im Tester nicht sinnvoll - nutze FILTER_BOTH stattdessen!");
}
else
{
g_currentFilter = InpStartDirection;
}
// Init HWM
stat_HighWaterMark = AccountInfoDouble(ACCOUNT_BALANCE);
// --- CHART STYLING ---
ChartSetInteger(0, CHART_SHOW_GRID, false);
ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrBlack);
ChartSetInteger(0, CHART_COLOR_FOREGROUND, clrWhite);
ChartSetInteger(0, CHART_COLOR_CHART_UP, clrWhite);
ChartSetInteger(0, CHART_COLOR_CHART_DOWN, clrWhite);
ChartSetInteger(0, CHART_COLOR_CANDLE_BULL, clrWhite);
ChartSetInteger(0, CHART_COLOR_CANDLE_BEAR, clrBlack);
ChartSetInteger(0, CHART_MODE, CHART_CANDLES);
Comment("");
ChartRedraw();
// Indikatoren
handleStoch = iStochastic(_Symbol, PERIOD_H1, InpStochK, InpStochD, InpStochSlowing, MODE_SMA, STO_LOWHIGH);
if(handleStoch == INVALID_HANDLE) return(INIT_FAILED);
if (InpEntryMode == MODE_RSI)
{
handleRSI = iRSI(_Symbol, PERIOD_D1, InpRSI_Period, PRICE_CLOSE);
if(handleRSI == INVALID_HANDLE) return(INIT_FAILED);
}
trade.SetExpertMagicNumber(InpMagicNumber);
trade.SetTypeFilling(ORDER_FILLING_IOC);
// GUI erstellen
CreateButtons();
CreateLabels();
UpdateButtonsState();
// CSV-Mode initialisieren
// WICHTIG: Im Strategy Tester macht CSV-Steuerung keinen Sinn!
if(InpUseCSVSignals && !isInTester)
{
if(InpDebugMode) Print("CSV-Signal Mode aktiviert. Prüfe Datei: ", InpCSVFilename);
CheckCSVFile();
// Timer für periodische CSV-Prüfung starten (in Sekunden)
int timerSeconds = InpCSVCheckInterval * 60;
if(!EventSetTimer(timerSeconds))
{
Print("FEHLER: Timer konnte nicht gestartet werden!");
return(INIT_FAILED);
}
if(InpDebugMode) Print("Timer gestartet: Prüfe CSV alle ", InpCSVCheckInterval, " Minuten");
}
else
{
if(isInTester)
{
if(InpDebugMode) Print("CSV-Signal Mode DEAKTIVIERT im Strategy Tester (keine Datei-Zugriffe im Tester)");
g_CSVMode = false;
}
else
{
if(InpDebugMode) Print("CSV-Signal Mode deaktiviert. Nutze manuelle Button-Steuerung.");
}
}
// News-Filter initialisieren
if(InpUseNewsFilter)
{
if(InpDebugMode)
{
Print("News-Filter aktiviert. Wichtigkeit: ",
(InpNewsImportance == NEWS_HIGH_ONLY) ? "Nur HIGH" : "MEDIUM + HIGH",
" | Zeitfenster: ", InpMinutesBeforeNews, " Min vorher / ", InpMinutesAfterNews, " Min nachher");
}
CheckUpcomingNews();
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Deinit |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(handleStoch != INVALID_HANDLE) IndicatorRelease(handleStoch);
if(handleRSI != INVALID_HANDLE) IndicatorRelease(handleRSI);
EventKillTimer();
ObjectDelete(0, btnBothName);
ObjectDelete(0, btnLongName);
ObjectDelete(0, btnShortName);
ObjectDelete(0, btnNeutralName);
ObjectDelete(0, lblMaxDDName);
ObjectDelete(0, lblPosName);
ObjectDelete(0, lblStatusName);
ObjectDelete(0, lblSetupName);
ObjectDelete(0, lblCSVInfoName);
ObjectDelete(0, lblCSVTimeName);
ObjectDelete(0, lblNewsInfoName);
ObjectDelete(0, lblCSVDirectionName);
Comment("");
}
//+------------------------------------------------------------------+
//| Timer Event |
//+------------------------------------------------------------------+
void OnTimer()
{
if(InpUseCSVSignals)
{
CheckCSVFile();
}
}
//+------------------------------------------------------------------+
//| OnTick |
//+------------------------------------------------------------------+
void OnTick()
{
// --- 1. MONITORING ---
double currentBalance = AccountInfoDouble(ACCOUNT_BALANCE);
double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
if(currentBalance > stat_HighWaterMark) stat_HighWaterMark = currentBalance;
double equityDD_Money = stat_HighWaterMark - currentEquity;
if(equityDD_Money > stat_MaxDD_Equity_Money) stat_MaxDD_Equity_Money = equityDD_Money;
double equityDD_Pct = 0.0;
if(stat_HighWaterMark > 0) equityDD_Pct = (equityDD_Money / stat_HighWaterMark) * 100.0;
if(equityDD_Pct > stat_MaxDD_Equity_Percent) stat_MaxDD_Equity_Percent = equityDD_Pct;
// --- 2. PREPARATIONS ---
ValidatePositionsAgainstFilter();
CalculateADR_and_Range();
CheckUpcomingNews(); // Jetzt optimiert mit Caching!
int openPositions = 0;
ENUM_POSITION_TYPE openType = POSITION_TYPE_BUY;
for(int i=0; i<PositionsTotal(); i++) {
if(PositionSelectByTicket(PositionGetTicket(i))) {
if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == InpMagicNumber) {
openPositions++;
openType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
}
}
}
// --- 3. STATUS LOGIC ---
if(openPositions > 0) {
double lastEntry = 0.0;
for(int i=PositionsTotal()-1; i>=0; i--) {
if(PositionSelectByTicket(PositionGetTicket(i))) {
if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == InpMagicNumber) {
lastEntry = PositionGetDouble(POSITION_PRICE_OPEN);
break;
}
}
}
stat_EA_Action = StringFormat("Managing Basket (%s). Next Grid: %.5f",
(openType == POSITION_TYPE_BUY) ? "Long" : "Short", lastEntry);
} else {
if(InpEntryMode == MODE_ADR) {
double neededRange = adrValue * (InpEntryADR_Pct / 100.0);
if(dayRange < neededRange) {
stat_EA_Action = StringFormat("Wait Range (%.5f < %.5f)", dayRange, neededRange);
} else {
stat_EA_Action = "ADR met. Scanning for Stoch Entry...";
}
} else if(InpEntryMode == MODE_RSI) {
stat_EA_Action = "Scanning for RSI + Stoch Entry...";
}
}
// --- 4. LOGIC EXECUTION ---
if(openPositions > 0) {
CheckBasketExit(openType, openPositions);
datetime currentH1Bar = iTime(_Symbol, PERIOD_H1, 0);
if(currentH1Bar != lastProcessedH1Bar && openPositions < InpMaxPositions)
{
lastProcessedH1Bar = currentH1Bar;
// News-Filter: Grid-Nachkauf blockieren wenn News anstehen
if(g_NewsBlockActive)
{
stat_EA_Action = StringFormat("Grid blockiert: %s", g_NextNewsInfo);
}
else
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double requiredDist = adrValue * (InpGridStep_ADR_Pct / 100.0);
double slDistancePoints = adrValue * InpIndividualSL_ADR;
double lastEntryPrice = 0.0;
for(int i=PositionsTotal()-1; i>=0; i--) {
if(PositionSelectByTicket(PositionGetTicket(i))) {
if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == InpMagicNumber) {
lastEntryPrice = PositionGetDouble(POSITION_PRICE_OPEN);
break;
}
}
}
if(lastEntryPrice > 0) {
bool gridOk = false;
double dynGridLot = GetDynamicLot(InpGridLot);
if(openType == POSITION_TYPE_SELL && ask >= (lastEntryPrice + requiredDist)) {
if(g_currentFilter == FILTER_SHORT || g_currentFilter == FILTER_BOTH) {
gridOk = true;
string comment = BuildTradeComment(false, openPositions + 1, "Short");
trade.Sell(dynGridLot, _Symbol, ask, ask + slDistancePoints, 0, comment);
}
}
if(openType == POSITION_TYPE_BUY && bid <= (lastEntryPrice - requiredDist)) {
if(g_currentFilter == FILTER_LONG || g_currentFilter == FILTER_BOTH) {
gridOk = true;
string comment = BuildTradeComment(false, openPositions + 1, "Long");
trade.Buy(dynGridLot, _Symbol, bid, bid - slDistancePoints, 0, comment);
}
}
}
}
}
}
// EOD Close
if(InpUseEODClose) {
MqlDateTime tm;
TimeToStruct(TimeCurrent(), tm);
if(tm.hour == InpEODHour && tm.min >= InpEODMinute) {
if(openPositions > 0) {
CloseAllPositions();
stat_EA_Action = "EOD Time. Closed all positions.";
} else {
stat_EA_Action = "EOD Time. Waiting for next day.";
}
}
}
// ENTRY LOGIC
if(openPositions == 0)
{
datetime currentH1Bar = iTime(_Symbol, PERIOD_H1, 0);
if(currentH1Bar != lastProcessedH1Bar)
{
lastProcessedH1Bar = currentH1Bar;
// News-Filter: Entry blockieren wenn News anstehen
if(g_NewsBlockActive)
{
stat_EA_Action = StringFormat("Entry blockiert: %s", g_NextNewsInfo);
}
else
{
bool entryConditionMet = false;
if(InpEntryMode == MODE_ADR) {
double neededRange = adrValue * (InpEntryADR_Pct / 100.0);
if(dayRange > neededRange) entryConditionMet = true;
}
else if(InpEntryMode == MODE_RSI) {
double rsiValues[];
if(CopyBuffer(handleRSI, 0, 0, 1, rsiValues) == 1) {
if(rsiValues[0] >= InpRSI_Upper || rsiValues[0] <= InpRSI_Lower) {
entryConditionMet = true;
}
}
}
if(entryConditionMet) {
double kBuffer[], dBuffer[];
ArraySetAsSeries(kBuffer, true);
ArraySetAsSeries(dBuffer, true);
if(CopyBuffer(handleStoch, 0, 0, 3, kBuffer) < 3) return;
if(CopyBuffer(handleStoch, 1, 0, 3, dBuffer) < 3) return;
bool shortSignal = (kBuffer[2] >= InpStochUpper && kBuffer[1] < InpStochUpper);
bool longSignal = (kBuffer[2] <= InpStochLower && kBuffer[1] > InpStochLower);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double slDistancePoints = adrValue * InpIndividualSL_ADR;
double dynFirstLot = GetDynamicLot(InpFirstLot);
if(shortSignal && (g_currentFilter == FILTER_SHORT || g_currentFilter == FILTER_BOTH)) {
string comment = BuildTradeComment(true, 1, "Short");
trade.Sell(dynFirstLot, _Symbol, ask, ask + slDistancePoints, 0, comment);
stat_EA_Action = "Entered Short";
}
if(longSignal && (g_currentFilter == FILTER_LONG || g_currentFilter == FILTER_BOTH)) {
string comment = BuildTradeComment(true, 1, "Long");
trade.Buy(dynFirstLot, _Symbol, bid, bid - slDistancePoints, 0, comment);
stat_EA_Action = "Entered Long";
}
}
}
}
}
// --- 5. VISUALS UPDATE ---
UpdateVisuals(openPositions);
}
//+------------------------------------------------------------------+
//| Chart Event Handler |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
if(id == CHARTEVENT_OBJECT_CLICK)
{
if(g_CSVMode)
{
Print("WARNUNG: Manuelle Button-Steuerung deaktiviert. CSV-Datei steuert die Richtung!");
ObjectSetInteger(0, sparam, OBJPROP_STATE, false);
ChartRedraw();
return;
}
if(sparam == btnBothName)
{
g_currentFilter = FILTER_BOTH;
UpdateButtonsState();
ValidatePositionsAgainstFilter();
}
else if(sparam == btnLongName)
{
g_currentFilter = FILTER_LONG;
UpdateButtonsState();
ValidatePositionsAgainstFilter();
}
else if(sparam == btnShortName)
{
g_currentFilter = FILTER_SHORT;
UpdateButtonsState();
ValidatePositionsAgainstFilter();
}
else if(sparam == btnNeutralName)
{
g_currentFilter = FILTER_NEUTRAL;
UpdateButtonsState();
ValidatePositionsAgainstFilter();
}
ChartRedraw();
}
}
//+------------------------------------------------------------------+
//| CSV Integration Functions |
//+------------------------------------------------------------------+
void CheckCSVFile()
{
g_LastCSVCheck = TimeCurrent();
int fileHandle = FileOpen(InpCSVFilename, FILE_READ|FILE_TXT|FILE_ANSI|FILE_SHARE_READ);
if(fileHandle == INVALID_HANDLE)
{
g_CSVMode = false;
g_CSVStatus = "Datei nicht gefunden";
g_CSVSignalText = "FILE NOT FOUND";
Print("FEHLER: CSV-Datei nicht gefunden: ", InpCSVFilename);
Print("Nutze manuelle Button-Steuerung als Fallback.");
UpdateButtonsState();
return;
}
bool foundSymbol = false;
string newSignal = "NEUTRAL";
string matchedCsvSymbol = "";
int lineNumber = 0;
// DEBUG: Nur bei aktiviertem Debug-Modus
if(InpDebugMode)
{
Print("========================================");
Print("CSV-Check: Suche Symbol '", _Symbol, "'");
Print("========================================");
}
while(!FileIsEnding(fileHandle))
{
string line = FileReadString(fileHandle);
lineNumber++;
// Entferne Carriage Return und Whitespace
StringReplace(line, "\r", "");
StringReplace(line, "\n", "");
StringTrimLeft(line);
StringTrimRight(line);
// Überspringe leere Zeilen
if(line == "")
{
if(InpDebugMode) Print("Zeile ", lineNumber, ": [LEER]");
continue;
}
// Überspringe Header-Zeile
if(lineNumber == 1 || StringFind(line, "Waehrungspaar") >= 0 || StringFind(line, "Letztes_Signal") >= 0)
{
if(InpDebugMode) Print("Zeile ", lineNumber, ": [HEADER] ", line);
continue;
}
// Debug: Zeige die gelesene Zeile (nur im Debug-Modus)
if(InpDebugMode) Print("Zeile ", lineNumber, ": '", line, "'");
string parts[];
int count = StringSplit(line, ';', parts);
if(count >= 2)
{
string csvSymbol = parts[0];
StringTrimLeft(csvSymbol);
StringTrimRight(csvSymbol);
string csvSignal = parts[1];
StringTrimLeft(csvSignal);
StringTrimRight(csvSignal);
StringToUpper(csvSignal);
// Debug: Zeige geparste Werte (nur im Debug-Modus)
if(InpDebugMode) Print(" -> Parsed: Symbol='", csvSymbol, "' Signal='", csvSignal, "'");
// VERBESSERTE VERGLEICHSLOGIK:
bool isMatch = false;
if(csvSymbol == _Symbol)
{
// Exakter Match
isMatch = true;
if(InpDebugMode) Print(" -> EXAKTER MATCH!");
}
else if(StringFind(_Symbol, csvSymbol) == 0)
{
// Symbol beginnt mit CSV-Symbol (z.B. NZDUSDm vs NZDUSD)
isMatch = true;
if(InpDebugMode) Print(" -> PREFIX MATCH!");
}
else if(StringFind(_Symbol, csvSymbol + ".") == 0 ||
StringFind(_Symbol, csvSymbol + "#") == 0 ||
StringFind(_Symbol, csvSymbol + "m") == 0)
{
// Symbol hat bekanntes Suffix
isMatch = true;
if(InpDebugMode) Print(" -> SUFFIX MATCH!");
}
else
{
if(InpDebugMode) Print(" -> Kein Match (suche '", _Symbol, "' vs gefunden '", csvSymbol, "')");
}
if(isMatch)
{
foundSymbol = true;
newSignal = csvSignal;
matchedCsvSymbol = csvSymbol;
if(InpDebugMode)
{
Print("========================================");
Print("ERFOLG! MT5-Symbol: '", _Symbol, "' = CSV-Symbol: '", csvSymbol, "' Signal: ", csvSignal);
Print("========================================");
}
break;
}
}
else
{
if(InpDebugMode) Print(" -> FEHLER: Konnte Zeile nicht in 2 Teile splitten (count=", count, ")");
}
}
FileClose(fileHandle);
if(!foundSymbol)
{
g_CSVMode = true;
g_CSVStatus = "Symbol nicht in CSV gefunden";
g_CSVSignalText = "NOT FOUND";
g_CSVDirection = FILTER_NEUTRAL;
// Fehler IMMER ausgeben (nicht nur im Debug-Modus)
Print("WARNUNG: Symbol '", _Symbol, "' nicht in CSV gefunden! (", lineNumber, " Zeilen gelesen)");
if(InpDebugMode)
{
Print("Prüfe das Expert-Log oben für Details.");
}
g_currentFilter = FILTER_NEUTRAL;
UpdateButtonsState();
return;
}
// Signal parsen
ENUM_DIR_FILTER oldFilter = g_currentFilter;
ENUM_DIR_FILTER newFilter = FILTER_NEUTRAL;
if(newSignal == "BUY")
{
newFilter = FILTER_LONG;
g_CSVSignalText = "BUY";
}
else if(newSignal == "SELL")
{
newFilter = FILTER_SHORT;
g_CSVSignalText = "SELL";
}
else
{
newFilter = FILTER_NEUTRAL;
g_CSVSignalText = "NEUTRAL";
}
g_CSVMode = true;
g_CSVDirection = newFilter;
g_CSVStatus = "CSV erfolgreich gelesen";
// Erfolg IMMER ausgeben (kompakt, nicht nur im Debug-Modus)
Print("CSV OK: '", _Symbol, "' → Signal: ", g_CSVSignalText);
// Ausführliche Details nur im Debug-Modus
if(InpDebugMode)
{
Print("========================================");
Print("CSV ERFOLGREICH GELESEN!");
Print("MT5-Symbol: '", _Symbol, "'");
Print("CSV-Symbol: '", matchedCsvSymbol, "'");
Print("Signal: ", g_CSVSignalText);
Print("Filter: ", (newFilter == FILTER_LONG ? "LONG" : (newFilter == FILTER_SHORT ? "SHORT" : "NEUTRAL")));
Print("========================================");
}
// Wenn Signal geändert hat: Gegenpositionen schließen
if(oldFilter != newFilter)
{
CloseOppositePositions(oldFilter, newFilter);
}
g_currentFilter = newFilter;
UpdateButtonsState();
}
//+------------------------------------------------------------------+
//| CloseOppositePositions |
//+------------------------------------------------------------------+
void CloseOppositePositions(ENUM_DIR_FILTER oldDir, ENUM_DIR_FILTER newDir)
{
// NEUTRAL als Ziel: Nichts schließen (einfach warten)
if(newDir == FILTER_NEUTRAL)
{
Print("Neues Signal: NEUTRAL - halte bestehende Positionen, keine neuen Trades");
return;
}
// Von NEUTRAL kommend: Nichts zu schließen (keine Positionen)
if(oldDir == FILTER_NEUTRAL)
{
Print("Signal wechselt von NEUTRAL zu ", (newDir == FILTER_LONG) ? "BUY" : "SELL");
return;
}
// BOTH sollte bei CSV-Steuerung nicht vorkommen
if(oldDir == FILTER_BOTH || newDir == FILTER_BOTH)
{
Print("WARNUNG: FILTER_BOTH sollte bei CSV-Steuerung nicht vorkommen!");
return;
}
// Gleiche Richtung: Nichts zu tun
if(oldDir == newDir)
{
return;
}
// BUY -> SELL oder SELL -> BUY: Gegenpositionen schließen
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == InpMagicNumber)
{
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
bool closeIt = false;
if(newDir == FILTER_LONG && type == POSITION_TYPE_SELL)
{
closeIt = true;
Print("Schließe Short-Position #", ticket, " wegen neuem BUY-Signal");
}
else if(newDir == FILTER_SHORT && type == POSITION_TYPE_BUY)
{
closeIt = true;
Print("Schließe Long-Position #", ticket, " wegen neuem SELL-Signal");
}
if(closeIt)
{
trade.PositionClose(ticket);
}
}
}
}
}
//+------------------------------------------------------------------+
//| News Filter Functions - OPTIMIERT! |
//+------------------------------------------------------------------+
void CheckUpcomingNews()
{
if(!InpUseNewsFilter)
{
g_NewsBlockActive = false;
g_NextNewsInfo = "Deaktiviert";
return;
}
// OPTIMIERUNG: Nur alle X Sekunden prüfen statt bei jedem Tick!
datetime currentTime = TimeCurrent();
if(currentTime - g_LastNewsCheckTime < g_NewsCheckInterval && g_LastNewsCheckTime > 0)
{
return; // Nutze gecachte Werte
}
g_LastNewsCheckTime = currentTime;
datetime serverTime = TimeTradeServer();
datetime dateFrom = serverTime - (InpMinutesAfterNews * 60);
datetime dateTo = serverTime + (InpMinutesBeforeNews * 60);
string currencyBase = SymbolInfoString(_Symbol, SYMBOL_CURRENCY_BASE);
string currencyQuote = SymbolInfoString(_Symbol, SYMBOL_CURRENCY_PROFIT);
ENUM_CALENDAR_EVENT_IMPORTANCE minImportance = (InpNewsImportance == NEWS_HIGH_ONLY)
? CALENDAR_IMPORTANCE_HIGH
: CALENDAR_IMPORTANCE_MODERATE;
MqlCalendarValue values[];
datetime nearestNewsTime = 0;
string nearestNewsName = "";
ENUM_CALENDAR_EVENT_IMPORTANCE nearestImportance = CALENDAR_IMPORTANCE_NONE;
// Base-Währung prüfen
if(CalendarValueHistory(values, dateFrom, dateTo, NULL, currencyBase))
{
for(int i = 0; i < ArraySize(values); i++)
{
MqlCalendarEvent event;
if(CalendarEventById(values[i].event_id, event))
{
if(event.time_mode != CALENDAR_TIMEMODE_DATETIME && event.time_mode != CALENDAR_TIMEMODE_DATE)
continue;
if(event.importance < minImportance)
continue;
if(nearestNewsTime == 0 || MathAbs((int)(values[i].time - serverTime)) < MathAbs((int)(nearestNewsTime - serverTime)))
{
nearestNewsTime = values[i].time;
nearestNewsName = event.name;
nearestImportance = event.importance;
}
}
}
}
// Quote-Währung prüfen (wenn aktiviert)
if(InpNewsFilterBothCurrencies && currencyBase != currencyQuote)
{
if(CalendarValueHistory(values, dateFrom, dateTo, NULL, currencyQuote))
{
for(int i = 0; i < ArraySize(values); i++)
{
MqlCalendarEvent event;
if(CalendarEventById(values[i].event_id, event))
{
if(event.time_mode != CALENDAR_TIMEMODE_DATETIME && event.time_mode != CALENDAR_TIMEMODE_DATE)
continue;
if(event.importance < minImportance)
continue;
if(nearestNewsTime == 0 || MathAbs((int)(values[i].time - serverTime)) < MathAbs((int)(nearestNewsTime - serverTime)))
{
nearestNewsTime = values[i].time;
nearestNewsName = event.name;
nearestImportance = event.importance;
}
}
}
}
}
// Auswertung
if(nearestNewsTime > 0)
{
int minutesDiff = (int)((nearestNewsTime - serverTime) / 60);
string impText = "";
switch(nearestImportance)
{
case CALENDAR_IMPORTANCE_HIGH: impText = "HIGH"; break;
case CALENDAR_IMPORTANCE_MODERATE: impText = "MEDIUM"; break;
case CALENDAR_IMPORTANCE_LOW: impText = "LOW"; break;
default: impText = "NONE"; break;
}
if(minutesDiff > 0)
{
g_NewsBlockActive = true;
g_NextNewsInfo = StringFormat("%s [%s] in %d Min", nearestNewsName, impText, minutesDiff);
}
else
{
g_NewsBlockActive = true;
g_NextNewsInfo = StringFormat("%s [%s] vor %d Min", nearestNewsName, impText, -minutesDiff);
}
g_NextNewsTime = nearestNewsTime;
g_NextNewsName = nearestNewsName;
}
else
{
g_NewsBlockActive = false;
g_NextNewsInfo = "Klar - Keine News";
g_NextNewsTime = 0;
g_NextNewsName = "";
}
}
//+------------------------------------------------------------------+
//| Display Logic - IM TESTER DEAKTIVIERT! |
//+------------------------------------------------------------------+
void UpdateVisuals(int positions)
{
// OPTIMIERUNG: Im Strategy Tester keine GUI-Updates (bringt nichts und kostet Zeit)
static bool isInTester = (MQLInfoInteger(MQL_TESTER) || MQLInfoInteger(MQL_OPTIMIZATION));
if(isInTester) return;
// Zeile 1: MaxDD
string txtDD = StringFormat("MaxDD zu HWM-Balance in %%: %.2f%%", stat_MaxDD_Equity_Percent);
ObjectSetString(0, lblMaxDDName, OBJPROP_TEXT, txtDD);
// Zeile 2: Positionen
string txtPos = StringFormat("Anzahl an Positionen: %d", positions);
ObjectSetString(0, lblPosName, OBJPROP_TEXT, txtPos);
// Zeile 3: Status
string txtStatus = StringFormat("Status: %s", stat_EA_Action);
ObjectSetString(0, lblStatusName, OBJPROP_TEXT, txtStatus);
// Zeile 4: Config / Setup Info (Mit Magic Number!)
string txtSetup = StringFormat("Config: Cap %.0f | Grid %.0f%% | Target %.0f%% | Lot %.2f | Magic %d",
InpEntryADR_Pct, InpGridStep_ADR_Pct, InpStartTarget_ADR, InpFirstLot, InpMagicNumber);
ObjectSetString(0, lblSetupName, OBJPROP_TEXT, txtSetup);
// Zeile 5: CSV Info
if(InpUseCSVSignals)
{
string modeText = g_CSVMode ? "AKTIV" : "INAKTIV";
color modeColor = g_CSVMode ? clrLimeGreen : clrOrange;
string txtCSVInfo = StringFormat("CSV-Mode: %s | Signal: %s | Status: %s",
modeText, g_CSVSignalText, g_CSVStatus);
ObjectSetString(0, lblCSVInfoName, OBJPROP_TEXT, txtCSVInfo);
ObjectSetInteger(0, lblCSVInfoName, OBJPROP_COLOR, modeColor);
// Zeile 6: Letzte CSV-Prüfung
string timeText = "Noch nie";
if(g_LastCSVCheck > 0)
{
timeText = TimeToString(g_LastCSVCheck, TIME_DATE|TIME_MINUTES);
}
string txtCSVTime = StringFormat("Letzte CSV-Prüfung: %s (Interval: %d Min)", timeText, InpCSVCheckInterval);
ObjectSetString(0, lblCSVTimeName, OBJPROP_TEXT, txtCSVTime);
}
else
{
ObjectSetString(0, lblCSVInfoName, OBJPROP_TEXT, "CSV-Mode: DEAKTIVIERT (Nutze manuelle Buttons)");
ObjectSetInteger(0, lblCSVInfoName, OBJPROP_COLOR, clrGray);
ObjectSetString(0, lblCSVTimeName, OBJPROP_TEXT, "");
}
// Zeile 7: News-Filter Info
if(InpUseNewsFilter)
{
string newsFilterText = "";
color newsColor = clrLimeGreen;
if(g_NewsBlockActive)
{
newsFilterText = StringFormat("News-Filter: BLOCKIERT | %s", g_NextNewsInfo);
newsColor = clrRed;
}
else
{
newsFilterText = StringFormat("News-Filter: %s", g_NextNewsInfo);
newsColor = clrLimeGreen;
}
ObjectSetString(0, lblNewsInfoName, OBJPROP_TEXT, newsFilterText);
ObjectSetInteger(0, lblNewsInfoName, OBJPROP_COLOR, newsColor);
}
else
{
ObjectSetString(0, lblNewsInfoName, OBJPROP_TEXT, "News-Filter: DEAKTIVIERT");
ObjectSetInteger(0, lblNewsInfoName, OBJPROP_COLOR, clrGray);
}
// Zeile 8: CSV Traderichtung
if(InpUseCSVSignals && g_CSVMode)
{
string directionText = "";
color directionColor = clrYellow;
if(g_CSVSignalText == "NOT FOUND")
{
directionText = StringFormat("%s: NICHT GEFUNDEN IN CSV → KEINE TRADES", _Symbol);
directionColor = clrOrange;
}
else
{
switch(g_currentFilter)
{
case FILTER_LONG:
directionText = StringFormat("%s: BUY → NUR LONG TRADES ERLAUBT", _Symbol);
directionColor = clrLimeGreen;
break;
case FILTER_SHORT:
directionText = StringFormat("%s: SELL → NUR SHORT TRADES ERLAUBT", _Symbol);
directionColor = clrRed;
break;
case FILTER_NEUTRAL:
directionText = StringFormat("%s: NEUTRAL → KEINE NEUEN TRADES", _Symbol);
directionColor = clrYellow;
break;
}
}
ObjectSetString(0, lblCSVDirectionName, OBJPROP_TEXT, directionText);
ObjectSetInteger(0, lblCSVDirectionName, OBJPROP_COLOR, directionColor);
}
else if(InpUseCSVSignals && !g_CSVMode)
{
ObjectSetString(0, lblCSVDirectionName, OBJPROP_TEXT, StringFormat("%s: CSV-DATEI NICHT GEFUNDEN → WARNUNG", _Symbol));
ObjectSetInteger(0, lblCSVDirectionName, OBJPROP_COLOR, clrOrange);
}
else
{
ObjectSetString(0, lblCSVDirectionName, OBJPROP_TEXT, "CSV-Steuerung: DEAKTIVIERT (Manuelle Buttons aktiv)");
ObjectSetInteger(0, lblCSVDirectionName, OBJPROP_COLOR, clrGray);
}
}
//+------------------------------------------------------------------+
//| GUI Helper |
//+------------------------------------------------------------------+
void CreateLabels()
{
int x = 20;
int fontSize = 11;
string font = "Arial Bold";
color c = clrWhite;
// Label 1: MaxDD (Y=50, da Buttons bei Y=5 ca 30px hoch sind)
CreateSingleLabel(lblMaxDDName, x, 50, "MaxDD: 0.0%", fontSize, font, c);
// Label 2: Positionen
CreateSingleLabel(lblPosName, x, 75, "Anzahl: 0", fontSize, font, c);
// Label 3: Status
CreateSingleLabel(lblStatusName, x, 100, "Status: Init", fontSize, font, c);
// Label 4: Setup Info
CreateSingleLabel(lblSetupName, x, 125, "Setup: ...", fontSize, font, c);
// Label 5: CSV Info
CreateSingleLabel(lblCSVInfoName, x, 150, "CSV: Init...", fontSize, font, clrYellow);
// Label 6: CSV Time
CreateSingleLabel(lblCSVTimeName, x, 175, "CSV Check: ...", fontSize, font, clrWhite);
// Label 7: News Info
CreateSingleLabel(lblNewsInfoName, x, 200, "News: Init...", fontSize, font, clrYellow);
// Label 8: CSV Traderichtung
CreateSingleLabel(lblCSVDirectionName, x, 225, "CSV-Richtung: N/A", fontSize+2, font, clrYellow);
}
//+------------------------------------------------------------------+
//| CreateSingleLabel |
//+------------------------------------------------------------------+
void CreateSingleLabel(string name, int x, int y, string text, int fSize, string font, color c)
{
if(ObjectFind(0, name) < 0)
{
ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
ObjectSetString(0, name, OBJPROP_TEXT, text);
ObjectSetString(0, name, OBJPROP_FONT, font);
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fSize);
ObjectSetInteger(0, name, OBJPROP_COLOR, c);
ObjectSetInteger(0, name, OBJPROP_BACK, false);
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
}
}
//+------------------------------------------------------------------+
//| CreateButtons |
//+------------------------------------------------------------------+
void CreateButtons()
{
int xBase = 20;
int yBase = 5; // Ganz oben
int width = 90;
int height = 30;
int gap = 5;
CreateSingleButton(btnBothName, xBase, yBase, width, height, "BOTH");
CreateSingleButton(btnLongName, xBase + width + gap, yBase, width, height, "LONG");
CreateSingleButton(btnShortName, xBase + (width + gap)*2, yBase, width, height, "SHORT");
CreateSingleButton(btnNeutralName, xBase + (width + gap)*3, yBase, width, height, "NEUTRAL");
}
//+------------------------------------------------------------------+
//| CreateSingleButton |
//+------------------------------------------------------------------+
void CreateSingleButton(string name, int x, int y, int w, int h, string text)
{
if(ObjectFind(0, name) < 0)
{
ObjectCreate(0, name, OBJ_BUTTON, 0, 0, 0);
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
ObjectSetInteger(0, name, OBJPROP_XSIZE, w);
ObjectSetInteger(0, name, OBJPROP_YSIZE, h);
ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetString(0, name, OBJPROP_TEXT, text);
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 9);
ObjectSetInteger(0, name, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(0, name, OBJPROP_BGCOLOR, clrSilver);
ObjectSetInteger(0, name, OBJPROP_BORDER_COLOR, clrGray);
ObjectSetInteger(0, name, OBJPROP_STATE, false);
ObjectSetInteger(0, name, OBJPROP_ZORDER, 10);
}
}
//+------------------------------------------------------------------+
//| UpdateButtonsState |
//+------------------------------------------------------------------+
void UpdateButtonsState()
{
color activeColor = clrSilver;
color inactiveColor = clrWhiteSmoke;
color disabledColor = clrDarkGray;
// Wenn CSV-Mode aktiv: Alle Buttons ausgrauen
if(g_CSVMode)
{
ObjectSetInteger(0, btnBothName, OBJPROP_BGCOLOR, disabledColor);
ObjectSetInteger(0, btnLongName, OBJPROP_BGCOLOR, disabledColor);
ObjectSetInteger(0, btnShortName, OBJPROP_BGCOLOR, disabledColor);
ObjectSetInteger(0, btnNeutralName, OBJPROP_BGCOLOR, disabledColor);
ObjectSetString(0, btnBothName, OBJPROP_TEXT, "BOTH (CSV)");
ObjectSetString(0, btnLongName, OBJPROP_TEXT, "LONG (CSV)");
ObjectSetString(0, btnShortName, OBJPROP_TEXT, "SHORT (CSV)");
ObjectSetString(0, btnNeutralName, OBJPROP_TEXT, "NEUTRAL (CSV)");
switch(g_currentFilter)
{
case FILTER_BOTH:
ObjectSetInteger(0, btnBothName, OBJPROP_BGCOLOR, clrGray);
break;
case FILTER_LONG:
ObjectSetInteger(0, btnLongName, OBJPROP_BGCOLOR, clrGray);
break;
case FILTER_SHORT:
ObjectSetInteger(0, btnShortName, OBJPROP_BGCOLOR, clrGray);
break;
case FILTER_NEUTRAL:
ObjectSetInteger(0, btnNeutralName, OBJPROP_BGCOLOR, clrGray);
break;
}
}
else
{
ObjectSetString(0, btnBothName, OBJPROP_TEXT, "BOTH");
ObjectSetString(0, btnLongName, OBJPROP_TEXT, "LONG");
ObjectSetString(0, btnShortName, OBJPROP_TEXT, "SHORT");
ObjectSetString(0, btnNeutralName, OBJPROP_TEXT, "NEUTRAL");
color cBoth = (g_currentFilter == FILTER_BOTH) ? clrSkyBlue : inactiveColor;
color cLong = (g_currentFilter == FILTER_LONG) ? clrLimeGreen : inactiveColor;
color cShort = (g_currentFilter == FILTER_SHORT) ? clrTomato : inactiveColor;
color cNeutral = (g_currentFilter == FILTER_NEUTRAL) ? clrKhaki : inactiveColor;
ObjectSetInteger(0, btnBothName, OBJPROP_BGCOLOR, cBoth);
ObjectSetInteger(0, btnLongName, OBJPROP_BGCOLOR, cLong);
ObjectSetInteger(0, btnShortName, OBJPROP_BGCOLOR, cShort);
ObjectSetInteger(0, btnNeutralName, OBJPROP_BGCOLOR, cNeutral);
}
ObjectSetInteger(0, btnBothName, OBJPROP_STATE, false);
ObjectSetInteger(0, btnLongName, OBJPROP_STATE, false);
ObjectSetInteger(0, btnShortName, OBJPROP_STATE, false);
ObjectSetInteger(0, btnNeutralName, OBJPROP_STATE, false);
ChartRedraw();
}
//+------------------------------------------------------------------+
//| Helpers |
//+------------------------------------------------------------------+
string BuildTradeComment(bool isFirstEntry, int positionNumber, string direction)
{
// CSV-Status
string csvPart = g_CSVMode ? StringFormat("CSV:%s", g_CSVSignalText) : "Manual";
// Entry-Typ
string entryType = "";
if(isFirstEntry)
{
entryType = (InpEntryMode == MODE_ADR) ? "ADR" : "RSI";
}
else
{
entryType = "Grid";
}
// Vollständiger Comment
string comment = StringFormat("%s | %s-%s #%d", csvPart, entryType, direction, positionNumber);
return comment;
}
//+------------------------------------------------------------------+
//| ADR Berechnung - OPTIMIERT! NUR 1x PRO TAG |
//+------------------------------------------------------------------+
void CalculateADR_and_Range()
{
datetime currentDay = iTime(_Symbol, PERIOD_D1, 0);
// OPTIMIERUNG: ADR nur einmal pro Tag berechnen!
if(currentDay != g_LastADRCalcDay || adrValue == 0)
{
g_LastADRCalcDay = currentDay;
double sum = 0;
for(int i=1; i<=InpADRPeriod; i++)
sum += (iHigh(_Symbol, PERIOD_D1, i) - iLow(_Symbol, PERIOD_D1, i));
adrValue = sum / InpADRPeriod;
if(adrValue == 0) adrValue = 100 * _Point;
}
// DayRange muss aktuell bleiben (aber viel schneller als ADR-Calc)
dayRange = iHigh(_Symbol, PERIOD_D1, 0) - iLow(_Symbol, PERIOD_D1, 0);
}
//+------------------------------------------------------------------+
//| ValidatePositionsAgainstFilter |
//+------------------------------------------------------------------+
void ValidatePositionsAgainstFilter()
{
if(g_currentFilter == FILTER_BOTH || g_currentFilter == FILTER_NEUTRAL) return;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == InpMagicNumber)
{
ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
bool closeIt = false;
if(g_currentFilter == FILTER_LONG && type == POSITION_TYPE_SELL) closeIt = true;
if(g_currentFilter == FILTER_SHORT && type == POSITION_TYPE_BUY) closeIt = true;
if(closeIt) trade.PositionClose(ticket);
}
}
}
}
//+------------------------------------------------------------------+
//| GetDynamicLot |
//+------------------------------------------------------------------+
double GetDynamicLot(double baseLot)
{
if(!InpUseMM || InpRefBalance <= 0 || InpBalanceStep_Pct <= 0) return(baseLot);
double currentBalance = AccountInfoDouble(ACCOUNT_BALANCE);
if(currentBalance < InpRefBalance) return(baseLot);
double profitAbs = currentBalance - InpRefBalance;
double profitPct = (profitAbs / InpRefBalance) * 100.0;
int steps = (int)(profitPct / InpBalanceStep_Pct);
double multiplier = 1.0 + (steps * (InpLotIncrease_Pct / 100.0));
double calculatedLot = baseLot * multiplier;
double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
double min = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double max = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
calculatedLot = MathFloor(calculatedLot / step) * step;
if(calculatedLot < min) calculatedLot = min;
if(calculatedLot > max) calculatedLot = max;
return(calculatedLot);
}
//+------------------------------------------------------------------+
//| CheckBasketExit |
//+------------------------------------------------------------------+
void CheckBasketExit(ENUM_POSITION_TYPE type, int count)
{
double totalLots = 0.0;
double weightedPrice = 0.0;
for(int i=PositionsTotal()-1; i>=0; i--) {
if(PositionSelectByTicket(PositionGetTicket(i))) {
if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == InpMagicNumber) {
double vol = PositionGetDouble(POSITION_VOLUME);
double price = PositionGetDouble(POSITION_PRICE_OPEN);
weightedPrice += (price * vol);
totalLots += vol;
}
}
}
if(totalLots <= 0) return;
double avgPrice = weightedPrice / totalLots;
double targetPct = InpStartTarget_ADR;
if (count > 1) {
targetPct = targetPct - ((count - 1) * InpTargetDecay_ADR);
}
double targetDistPoints = adrValue * (targetPct / 100.0);
bool triggerClose = false;
double currentPrice = 0.0;
if(type == POSITION_TYPE_BUY)
{
currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(currentPrice >= (avgPrice + targetDistPoints)) triggerClose = true;
}
else if(type == POSITION_TYPE_SELL)
{
currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(currentPrice <= (avgPrice - targetDistPoints)) triggerClose = true;
}
if(triggerClose) CloseAllPositions();
}
//+------------------------------------------------------------------+
//| CloseAllPositions |
//+------------------------------------------------------------------+
void CloseAllPositions()
{
for(int i=PositionsTotal()-1; i>=0; i--) {
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket)) {
if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == InpMagicNumber) {
trade.PositionClose(ticket);
}
}
}
}