328 行
11 KiB
MQL5
328 行
11 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| Ichimoku_Complete_Pro.mq5 |
| |||
//| Version 2.10 - Daily Loss Limit + Direction Filter |
| |||
//+------------------------------------------------------------------+
| |||
#property copyright "Educational - Optimized Version"
| |||
#property version "2.10"
| |||
#property strict
| |||
| |||
#include <Trade\Trade.mqh>
| |||
CTrade trade;
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Eingabeparameter |
| |||
//+------------------------------------------------------------------+
| |||
input group "=== Ichimoku Einstellungen ==="
| |||
input int InpTenkanPeriod = 9;
| |||
input int InpKijunPeriod = 26;
| |||
input int InpSenkouPeriod = 52;
| |||
| |||
input group "=== ATR Risk Management ==="
| |||
input double InpSL_Multiplier = 1.5;
| |||
input double InpTP_Multiplier = 2.5;
| |||
input double InpTrail_Start = 1.2;
| |||
input double InpTrail_Distance = 1.0;
| |||
input double InpBE_Trigger = 1.0;
| |||
input double InpBE_Offset = 0.2;
| |||
| |||
input group "=== Handelsrichtung ==="
| |||
enum ENUM_TRADE_DIRECTION
| |||
{
| |||
DIR_BOTH = 0, // Beide Richtungen
| |||
DIR_LONG = 1, // Nur Long
| |||
DIR_SHORT = 2 // Nur Short
| |||
};
| |||
input ENUM_TRADE_DIRECTION InpTradeDirection = DIR_BOTH;
| |||
| |||
input group "=== Tagesverlust-Limit ==="
| |||
input bool InpUseDailyLossLimit = true;
| |||
input double InpMaxDailyLoss = 50.0; // Maximaler Verlust in Account-Währung (z.B. 50 $)
| |||
| |||
input group "=== Filter ==="
| |||
input bool InpUseSessionFilter = true;
| |||
input int InpLondonStart = 8;
| |||
input int InpLondonEnd = 17;
| |||
input int InpNYStart = 13;
| |||
input int InpNYEnd = 22;
| |||
input int InpMaxSpreadPoints = 25;
| |||
| |||
input group "=== Benachrichtigungen ==="
| |||
input bool InpSendPush = true;
| |||
input bool InpSendMail = false;
| |||
input string InpMailSubject = "Ichimoku EA";
| |||
| |||
input group "=== Allgemein ==="
| |||
input double InpLotSize = 0.01;
| |||
input int InpMagicNumber = 20260915;
| |||
input bool InpOnlyOneTrade = true;
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Globale Variablen |
| |||
//+------------------------------------------------------------------+
| |||
int handleIchimoku = INVALID_HANDLE;
| |||
int handleATR = INVALID_HANDLE;
| |||
int atrPeriod = 14;
| |||
| |||
double tenkan[], kijun[], senkouA[], senkouB[], chikou[], atr[];
| |||
| |||
double dailyStartBalance = 0;
| |||
datetime lastDayChecked = 0;
| |||
bool tradingAllowedToday = true;
| |||
| |||
//+------------------------------------------------------------------+
| |||
int GetATRPeriodByTimeframe()
| |||
{
| |||
ENUM_TIMEFRAMES tf = Period();
| |||
if(tf <= PERIOD_M5) return 20;
| |||
if(tf <= PERIOD_M15) return 14;
| |||
if(tf <= PERIOD_M30) return 12;
| |||
if(tf <= PERIOD_H1) return 10;
| |||
if(tf <= PERIOD_H4) return 10;
| |||
return 8;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
int OnInit()
| |||
{
| |||
atrPeriod = GetATRPeriodByTimeframe();
| |||
| |||
handleIchimoku = iIchimoku(_Symbol, PERIOD_CURRENT, InpTenkanPeriod, InpKijunPeriod, InpSenkouPeriod);
| |||
handleATR = iATR(_Symbol, PERIOD_CURRENT, atrPeriod);
| |||
| |||
if(handleIchimoku == INVALID_HANDLE || handleATR == INVALID_HANDLE)
| |||
{
| |||
Print("Fehler beim Erstellen der Indikatoren");
| |||
return INIT_FAILED;
| |||
}
| |||
| |||
ArraySetAsSeries(tenkan, true);
| |||
ArraySetAsSeries(kijun, true);
| |||
ArraySetAsSeries(senkouA, true);
| |||
ArraySetAsSeries(senkouB, true);
| |||
ArraySetAsSeries(chikou, true);
| |||
ArraySetAsSeries(atr, true);
| |||
| |||
trade.SetExpertMagicNumber(InpMagicNumber);
| |||
trade.SetDeviationInPoints(30);
| |||
trade.SetTypeFilling(ORDER_FILLING_FOK);
| |||
| |||
// Tagesstart-Balance initialisieren
| |||
dailyStartBalance = AccountInfoDouble(ACCOUNT_BALANCE);
| |||
lastDayChecked = iTime(_Symbol, PERIOD_D1, 0);
| |||
tradingAllowedToday = true;
| |||
| |||
Print("Ichimoku EA v2.10 gestartet | ATR-Periode: ", atrPeriod);
| |||
return INIT_SUCCEEDED;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
void OnDeinit(const int reason)
| |||
{
| |||
if(handleIchimoku != INVALID_HANDLE) IndicatorRelease(handleIchimoku);
| |||
if(handleATR != INVALID_HANDLE) IndicatorRelease(handleATR);
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
void CheckDailyLossLimit()
| |||
{
| |||
if(!InpUseDailyLossLimit)
| |||
{
| |||
tradingAllowedToday = true;
| |||
return;
| |||
}
| |||
| |||
datetime currentDay = iTime(_Symbol, PERIOD_D1, 0);
| |||
| |||
// Neuer Tag → Reset
| |||
if(currentDay != lastDayChecked)
| |||
{
| |||
dailyStartBalance = AccountInfoDouble(ACCOUNT_BALANCE);
| |||
lastDayChecked = currentDay;
| |||
tradingAllowedToday = true;
| |||
Print("Neuer Handelstag – Tagesverlust-Limit zurückgesetzt");
| |||
}
| |||
| |||
double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
| |||
double dailyPL = currentEquity - dailyStartBalance;
| |||
| |||
if(dailyPL <= -InpMaxDailyLoss)
| |||
{
| |||
if(tradingAllowedToday)
| |||
{
| |||
tradingAllowedToday = false;
| |||
Notify("Tagesverlust-Limit erreicht (" + DoubleToString(dailyPL, 2) + "). Trading für heute gestoppt.");
| |||
}
| |||
}
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
bool IsTradingSession()
| |||
{
| |||
if(!InpUseSessionFilter) return true;
| |||
| |||
MqlDateTime dt;
| |||
TimeToStruct(TimeCurrent(), dt);
| |||
int hour = dt.hour;
| |||
| |||
bool london = (hour >= InpLondonStart && hour < InpLondonEnd);
| |||
bool ny = (hour >= InpNYStart && hour < InpNYEnd);
| |||
| |||
return (london || ny);
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
bool IsSpreadOK()
| |||
{
| |||
return (SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) <= InpMaxSpreadPoints);
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
void Notify(string message)
| |||
{
| |||
if(InpSendPush) SendNotification(message);
| |||
if(InpSendMail) SendMail(InpMailSubject, message);
| |||
Print(message);
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
void ManageOpenPosition()
| |||
{
| |||
if(!PositionSelect(_Symbol)) return;
| |||
if(PositionGetInteger(POSITION_MAGIC) != InpMagicNumber) return;
| |||
| |||
if(CopyBuffer(handleATR, 0, 0, 2, atr) < 2) return;
| |||
double currentATR = atr[0];
| |||
if(currentATR <= 0) return;
| |||
| |||
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
| |||
| |||
long type = PositionGetInteger(POSITION_TYPE);
| |||
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
| |||
double currentSL = PositionGetDouble(POSITION_SL);
| |||
double currentTP = PositionGetDouble(POSITION_TP);
| |||
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
| |||
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
| |||
| |||
double beTrigger = currentATR * InpBE_Trigger;
| |||
double beOffset = currentATR * InpBE_Offset;
| |||
double trailStart = currentATR * InpTrail_Start;
| |||
double trailDistance = currentATR * InpTrail_Distance;
| |||
double minMove = currentATR * 0.12;
| |||
| |||
// LONG
| |||
if(type == POSITION_TYPE_BUY)
| |||
{
| |||
double profit = bid - openPrice;
| |||
| |||
if(profit >= beTrigger)
| |||
{
| |||
double newSL = NormalizeDouble(openPrice + beOffset, digits);
| |||
if(newSL > currentSL + minMove || currentSL == 0)
| |||
if(trade.PositionModify(_Symbol, newSL, currentTP))
| |||
Notify("Break-Even BUY | SL: " + DoubleToString(newSL, digits));
| |||
}
| |||
| |||
if(profit >= trailStart)
| |||
{
| |||
double newSL = NormalizeDouble(bid - trailDistance, digits);
| |||
if(newSL > currentSL + minMove)
| |||
trade.PositionModify(_Symbol, newSL, currentTP);
| |||
}
| |||
}
| |||
| |||
// SHORT
| |||
if(type == POSITION_TYPE_SELL)
| |||
{
| |||
double profit = openPrice - ask;
| |||
| |||
if(profit >= beTrigger)
| |||
{
| |||
double newSL = NormalizeDouble(openPrice - beOffset, digits);
| |||
if(currentSL == 0 || newSL < currentSL - minMove)
| |||
if(trade.PositionModify(_Symbol, newSL, currentTP))
| |||
Notify("Break-Even SELL | SL: " + DoubleToString(newSL, digits));
| |||
}
| |||
| |||
if(profit >= trailStart)
| |||
{
| |||
double newSL = NormalizeDouble(ask + trailDistance, digits);
| |||
if(currentSL == 0 || newSL < currentSL - minMove)
| |||
trade.PositionModify(_Symbol, newSL, currentTP);
| |||
}
| |||
}
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
void OnTick()
| |||
{
| |||
// Tagesverlust prüfen
| |||
CheckDailyLossLimit();
| |||
| |||
// Offene Positionen verwalten
| |||
ManageOpenPosition();
| |||
| |||
// Filter
| |||
if(!tradingAllowedToday) return;
| |||
if(!IsTradingSession()) return;
| |||
if(!IsSpreadOK()) return;
| |||
| |||
// Nur bei neuer Kerze
| |||
static datetime lastBarTime = 0;
| |||
datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
| |||
if(currentBarTime == lastBarTime) return;
| |||
lastBarTime = currentBarTime;
| |||
| |||
if(InpOnlyOneTrade && PositionSelect(_Symbol)) return;
| |||
| |||
// Indikatoren
| |||
if(CopyBuffer(handleIchimoku, 0, 0, 3, tenkan) < 3) return;
| |||
if(CopyBuffer(handleIchimoku, 1, 0, 3, kijun) < 3) return;
| |||
if(CopyBuffer(handleIchimoku, 2, 0, 3, senkouA) < 3) return;
| |||
if(CopyBuffer(handleIchimoku, 3, 0, 3, senkouB) < 3) return;
| |||
if(CopyBuffer(handleIchimoku, 4, 0, 30, chikou) < 30) return;
| |||
if(CopyBuffer(handleATR, 0, 1, 2, atr) < 2) return;
| |||
| |||
double atrValue = atr[0];
| |||
double close1 = iClose(_Symbol, PERIOD_CURRENT, 1);
| |||
double cloudTop = MathMax(senkouA[1], senkouB[1]);
| |||
double cloudBottom = MathMin(senkouA[1], senkouB[1]);
| |||
| |||
bool buySignal =
| |||
(close1 > cloudTop) &&
| |||
(tenkan[1] > kijun[1] && tenkan[2] <= kijun[2]) &&
| |||
(chikou[26] > close1);
| |||
| |||
bool sellSignal =
| |||
(close1 < cloudBottom) &&
| |||
(tenkan[1] < kijun[1] && tenkan[2] >= kijun[2]) &&
| |||
(chikou[26] < close1);
| |||
| |||
// Handelsrichtung filtern
| |||
if(InpTradeDirection == DIR_LONG) sellSignal = false;
| |||
if(InpTradeDirection == DIR_SHORT) buySignal = false;
| |||
| |||
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
| |||
| |||
// BUY
| |||
if(buySignal)
| |||
{
| |||
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
| |||
double sl = NormalizeDouble(ask - atrValue * InpSL_Multiplier, digits);
| |||
double tp = NormalizeDouble(ask + atrValue * InpTP_Multiplier, digits);
| |||
| |||
if(trade.Buy(InpLotSize, _Symbol, ask, sl, tp, "Ichimoku Buy"))
| |||
Notify("BUY | " + _Symbol + " | SL: " + DoubleToString(sl, digits) + " | TP: " + DoubleToString(tp, digits));
| |||
}
| |||
| |||
// SELL
| |||
if(sellSignal)
| |||
{
| |||
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
| |||
double sl = NormalizeDouble(bid + atrValue * InpSL_Multiplier, digits);
| |||
double tp = NormalizeDouble(bid - atrValue * InpTP_Multiplier, digits);
| |||
| |||
if(trade.Sell(InpLotSize, _Symbol, bid, sl, tp, "Ichimoku Sell"))
| |||
Notify("SELL | " + _Symbol + " | SL: " + DoubleToString(sl, digits) + " | TP: " + DoubleToString(tp, digits));
| |||
}
| |||
}
| |||
//+------------------------------------------------------------------+
|