generated from Stingdondaleatih/Mql5
427 行
EOLなし
17 KiB
MQL5
427 行
EOLなし
17 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| Expert Advisor: CCI Channel EA (مع Debug مفصل) |
|
|
//| Description: نفس الـ EA السابق مع إضافات Print debugging مفصّلة |
|
|
//| لتتبع قيم المؤشر، أسباب رفض/فتح الصفقات، وحساب حجم اللوت خطوةً بخطوة.|
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Generated by Copilot"
|
|
#property version "1.02"
|
|
#property strict
|
|
|
|
#include <Trade\Trade.mqh>
|
|
|
|
input int InpCCIPeriod = 20; // فترة مؤشر CCI
|
|
input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_TYPICAL; // نوع السعر المستخدم لمؤشر CCI
|
|
input double InpUpperLevel = 100.0; // مستوى التشبع الشرائي (Overbought)
|
|
input double InpLowerLevel = -100.0; // مستوى التشبع البيعي (Oversold)
|
|
|
|
input double InpFixedLot = 0.01; // حجم اللوت الثابت (إذا لم تستخدم مخاطرة نسبة)
|
|
input bool InpUseRiskPercent = false; // هل استخدام نسبة مخاطرة لحساب اللوت؟
|
|
input double InpRiskPercent = 1.0; // نسبة المخاطرة من الرصيد لكل صفقة (إن اخترت استخدام النسبة)
|
|
input int InpStopLossPoints = 200; // وقف الخسارة (نقطة) مثال: 200
|
|
input int InpTakeProfitPoints= 400; // جني الأرباح (نقطة)
|
|
input int InpMaxSpreadPoints = 50; // أقصى سبريد (بـ Points) مسموح به لفتح الصفقات
|
|
input ulong InpMagicNumber = 20251201; // رقم سحري للتعرف على الصفقات
|
|
input bool InpAllowBuy = true; // تمكين الصفقات الشرائية
|
|
input bool InpAllowSell = true; // تمكين الصفقات البيعية
|
|
input bool InpCloseOppositeOnSignal = false; // إغلاق الصفقة المعاكسة عند وجود إشارة (false = لا تغلق)
|
|
input bool InpEnableTrailing = false; // تفعيل التريلينج ستوب
|
|
input int InpTrailingStart = 200; // تريلينج يبدأ بعد (نقطة)
|
|
input int InpTrailingStep = 50; // خطوة التريلينج (نقطة)
|
|
|
|
/* ---------- إعدادات ساعات التداول ---------- */
|
|
input bool InpEnableTimeFilter = false; // تفعيل فلترة الساعات
|
|
input bool InpTradeSunday = false;
|
|
input bool InpTradeMonday = true;
|
|
input bool InpTradeTuesday = true;
|
|
input bool InpTradeWednesday = true;
|
|
input bool InpTradeThursday = true;
|
|
input bool InpTradeFriday = true;
|
|
input bool InpTradeSaturday = false;
|
|
input int InpTradeStartHour = 8;
|
|
input int InpTradeStartMinute = 0;
|
|
input int InpTradeEndHour = 17;
|
|
input int InpTradeEndMinute = 0;
|
|
/* ------------------------------------------- */
|
|
|
|
/* ---------- Debugging toggle ---------- */
|
|
input bool InpEnableDebug = true; // تفعيل/تعطيل الطباعة التفصيلية في Journal
|
|
/* ------------------------------------- */
|
|
|
|
CTrade trade;
|
|
|
|
int cci_handle = INVALID_HANDLE;
|
|
datetime last_bar_time = 0;
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Normalize lot to broker limits |
|
|
//+------------------------------------------------------------------+
|
|
double NormalizeLot(double lot)
|
|
{
|
|
double min_lot=0, max_lot=0, step=0;
|
|
if(!SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN, min_lot))
|
|
min_lot = 0.01;
|
|
if(!SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MAX, max_lot))
|
|
max_lot = 100.0;
|
|
if(!SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP, step))
|
|
step = 0.01;
|
|
if(step <= 0) step = 0.01;
|
|
double steps = MathRound(lot/step);
|
|
double res = steps * step;
|
|
if(res < min_lot) res = min_lot;
|
|
if(res > max_lot) res = max_lot;
|
|
double final = MathRound(res/step)*step;
|
|
if(InpEnableDebug)
|
|
PrintFormat("NormalizeLot: requested=%.8f -> normalized=%.8f (min=%.8f max=%.8f step=%.8f)", lot, final, min_lot, max_lot, step);
|
|
return(final);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| تحقق من إذا الوقت الحالي داخل نافذة التداول المسموح بها |
|
|
//+------------------------------------------------------------------+
|
|
bool IsTradingAllowed()
|
|
{
|
|
if(!InpEnableTimeFilter) return true;
|
|
|
|
datetime now = TimeCurrent();
|
|
int dow = TimeDayOfWeek(now); // 0=Sunday .. 6=Saturday
|
|
|
|
bool allowedDays[7];
|
|
allowedDays[0] = InpTradeSunday;
|
|
allowedDays[1] = InpTradeMonday;
|
|
allowedDays[2] = InpTradeTuesday;
|
|
allowedDays[3] = InpTradeWednesday;
|
|
allowedDays[4] = InpTradeThursday;
|
|
allowedDays[5] = InpTradeFriday;
|
|
allowedDays[6] = InpTradeSaturday;
|
|
|
|
if(!allowedDays[dow])
|
|
{
|
|
if(InpEnableDebug) PrintFormat("IsTradingAllowed: current day (%d) is not allowed", dow);
|
|
return false;
|
|
}
|
|
|
|
int curHour = TimeHour(now);
|
|
int curMin = TimeMinute(now);
|
|
int curTotal = curHour*60 + curMin;
|
|
int startTotal = InpTradeStartHour*60 + InpTradeStartMinute;
|
|
int endTotal = InpTradeEndHour*60 + InpTradeEndMinute;
|
|
|
|
if(startTotal <= endTotal)
|
|
{
|
|
if(curTotal < startTotal || curTotal > endTotal)
|
|
{
|
|
if(InpEnableDebug) PrintFormat("IsTradingAllowed: outside time window (%02d:%02d not in %02d:%02d - %02d:%02d)", curHour, curMin, InpTradeStartHour, InpTradeStartMinute, InpTradeEndHour, InpTradeEndMinute);
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(curTotal < startTotal && curTotal > endTotal)
|
|
{
|
|
if(InpEnableDebug) PrintFormat("IsTradingAllowed: outside overnight window (%02d:%02d)", curHour, curMin);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Initialize |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
cci_handle = iCCI(Symbol(), Period(), InpCCIPeriod, InpAppliedPrice);
|
|
if(cci_handle == INVALID_HANDLE)
|
|
{
|
|
Print("خطأ: تعذّر إنشاء مؤشر CCI");
|
|
return(INIT_FAILED);
|
|
}
|
|
last_bar_time = 0;
|
|
|
|
if(InpEnableDebug)
|
|
{
|
|
PrintFormat("OnInit: Symbol=%s Period=%s InpCCIPeriod=%d InpUpper=%.2f InpLower=%.2f TimeFilter=%s",
|
|
Symbol(), EnumToString(Period()), InpCCIPeriod, InpUpperLevel, InpLowerLevel, InpEnableTimeFilter ? "ON":"OFF");
|
|
}
|
|
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Deinit |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
if(cci_handle != INVALID_HANDLE)
|
|
{
|
|
IndicatorRelease(cci_handle);
|
|
cci_handle = INVALID_HANDLE;
|
|
}
|
|
if(InpEnableDebug) PrintFormat("OnDeinit: reason=%d", reason);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Main tick |
|
|
//+------------------------------------------------------------------+
|
|
void OnTick()
|
|
{
|
|
datetime current_closed_time = (datetime) iTime(Symbol(), Period(), 1);
|
|
if(current_closed_time == 0) return;
|
|
if(current_closed_time == last_bar_time)
|
|
return;
|
|
last_bar_time = current_closed_time;
|
|
|
|
// Print basic tick/time info
|
|
if(InpEnableDebug) PrintFormat("OnTick: new closed bar at %s", TimeToString(current_closed_time, TIME_DATE|TIME_MINUTES));
|
|
|
|
if(!IsTradingAllowed())
|
|
{
|
|
if(InpEnableDebug) Print("خارج نافذة التداول المسموح بها - لا يتم فتح صفقات حاليا.");
|
|
return;
|
|
}
|
|
|
|
double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
|
|
double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
|
|
double point = SymbolInfoDouble(Symbol(), SYMBOL_POINT);
|
|
if(point <= 0) { if(InpEnableDebug) Print("OnTick: invalid point value"); return; }
|
|
double spread_points = MathRound((ask - bid)/point);
|
|
|
|
if(InpEnableDebug) PrintFormat("Market: ask=%.5f bid=%.5f spread_points=%.0f", ask, bid, spread_points);
|
|
|
|
if(spread_points > InpMaxSpreadPoints)
|
|
{
|
|
if(InpEnableDebug) PrintFormat("تجاوز السبريد الحد المسموح به: %d > %d", (int)spread_points, InpMaxSpreadPoints);
|
|
return;
|
|
}
|
|
|
|
double cci_buf[2];
|
|
int copied = CopyBuffer(cci_handle, 0, 1, 2, cci_buf); // array[0]=value@1, array[1]=value@2
|
|
if(copied != 2)
|
|
{
|
|
Print("تعذر نسخ قيم CCI");
|
|
return;
|
|
}
|
|
double cci_current = cci_buf[0];
|
|
double cci_previous= cci_buf[1];
|
|
|
|
if(InpEnableDebug) PrintFormat("CCI: previous=%.4f current=%.4f (upper=%.2f lower=%.2f)", cci_previous, cci_current, InpUpperLevel, InpLowerLevel);
|
|
|
|
bool buy_signal = false;
|
|
bool sell_signal = false;
|
|
|
|
if(InpAllowBuy)
|
|
{
|
|
if(cci_previous <= InpLowerLevel && cci_current > InpLowerLevel)
|
|
buy_signal = true;
|
|
}
|
|
if(InpAllowSell)
|
|
{
|
|
if(cci_previous >= InpUpperLevel && cci_current < InpUpperLevel)
|
|
sell_signal = true;
|
|
}
|
|
|
|
if(InpEnableDebug)
|
|
PrintFormat("Signals: buy=%s sell=%s", buy_signal ? "YES":"no", sell_signal ? "YES":"no");
|
|
|
|
if(!buy_signal && !sell_signal) return;
|
|
|
|
bool hasPosition = PositionSelect(Symbol());
|
|
ulong pos_type = POSITION_TYPE_BUY;
|
|
double pos_volume = 0.0;
|
|
if(hasPosition)
|
|
{
|
|
pos_type = (ulong) PositionGetInteger(POSITION_TYPE);
|
|
pos_volume = PositionGetDouble(POSITION_VOLUME);
|
|
}
|
|
if(InpEnableDebug) PrintFormat("Position: has=%s type=%d volume=%.2f", hasPosition ? "YES":"no", (int)pos_type, pos_volume);
|
|
|
|
if(buy_signal)
|
|
{
|
|
if(hasPosition && pos_type == POSITION_TYPE_BUY)
|
|
{
|
|
if(InpEnableDebug) Print("مركز شراء موجود بالفعل، تجاهل إشارة الشراء.");
|
|
}
|
|
else
|
|
{
|
|
if(hasPosition && pos_type == POSITION_TYPE_SELL && InpCloseOppositeOnSignal)
|
|
{
|
|
if(!ClosePositionByType(POSITION_TYPE_SELL))
|
|
Print("تعذر إغلاق الصفقة المعاكسة قبل فتح شراء.");
|
|
}
|
|
if(!hasPosition || (hasPosition && pos_type != POSITION_TYPE_BUY))
|
|
OpenOrder(true, ask, bid, cci_previous, cci_current, spread_points);
|
|
}
|
|
}
|
|
|
|
if(sell_signal)
|
|
{
|
|
if(hasPosition && pos_type == POSITION_TYPE_SELL)
|
|
{
|
|
if(InpEnableDebug) Print("مركز بيعي موجود بالفعل، تجاهل إشارة البيع.");
|
|
}
|
|
else
|
|
{
|
|
if(hasPosition && pos_type == POSITION_TYPE_BUY && InpCloseOppositeOnSignal)
|
|
{
|
|
if(!ClosePositionByType(POSITION_TYPE_BUY))
|
|
Print("تعذر إغلاق الصفقة المعاكسة قبل فتح بيع.");
|
|
}
|
|
if(!hasPosition || (hasPosition && pos_type != POSITION_TYPE_SELL))
|
|
OpenOrder(false, ask, bid, cci_previous, cci_current, spread_points);
|
|
}
|
|
}
|
|
|
|
OnTick_ManageTrailing(); // إدارة التريلينج بعد أي قرار
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| فتح أمر شراء/بيع (مع Debug) |
|
|
//+------------------------------------------------------------------+
|
|
void OpenOrder(bool isBuy, double ask, double bid, double cci_prev, double cci_curr, double spread_points)
|
|
{
|
|
double sl_price=0, tp_price=0;
|
|
double price = isBuy ? ask : bid;
|
|
double point = SymbolInfoDouble(Symbol(), SYMBOL_POINT);
|
|
|
|
if(InpStopLossPoints > 0)
|
|
{
|
|
if(isBuy)
|
|
sl_price = NormalizeDouble(price - InpStopLossPoints * point, (int)SymbolInfoInteger(Symbol(), SYMBOL_DIGITS));
|
|
else
|
|
sl_price = NormalizeDouble(price + InpStopLossPoints * point, (int)SymbolInfoInteger(Symbol(), SYMBOL_DIGITS));
|
|
}
|
|
if(InpTakeProfitPoints > 0)
|
|
{
|
|
if(isBuy)
|
|
tp_price = NormalizeDouble(price + InpTakeProfitPoints * point, (int)SymbolInfoInteger(Symbol(), SYMBOL_DIGITS));
|
|
else
|
|
tp_price = NormalizeDouble(price - InpTakeProfitPoints * point, (int)SymbolInfoInteger(Symbol(), SYMBOL_DIGITS));
|
|
}
|
|
|
|
double lot = InpFixedLot;
|
|
double before_norm_lot = lot;
|
|
double tick_value = 0.0;
|
|
double account_balance = AccountInfoDouble(ACCOUNT_BALANCE);
|
|
double risk_amount = 0.0;
|
|
double loss_per_lot = 0.0;
|
|
|
|
if(InpUseRiskPercent && InpStopLossPoints > 0)
|
|
{
|
|
tick_value = SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_VALUE);
|
|
if(tick_value <= 0) tick_value = 0.0;
|
|
risk_amount = account_balance * InpRiskPercent / 100.0;
|
|
loss_per_lot = InpStopLossPoints * point * tick_value;
|
|
if(loss_per_lot > 0)
|
|
lot = risk_amount / loss_per_lot;
|
|
else
|
|
lot = InpFixedLot;
|
|
if(InpEnableDebug)
|
|
{
|
|
PrintFormat("Lot calc (risk): balance=%.2f risk%%=%.2f risk_amount=%.2f tick_value=%.8f loss_per_lot=%.8f lot_before_norm=%.8f",
|
|
account_balance, InpRiskPercent, risk_amount, tick_value, loss_per_lot, lot);
|
|
}
|
|
lot = NormalizeLot(lot);
|
|
}
|
|
else
|
|
{
|
|
lot = NormalizeLot(lot);
|
|
if(InpEnableDebug)
|
|
PrintFormat("Lot calc (fixed): requested=%.8f normalized=%.8f", before_norm_lot, lot);
|
|
}
|
|
|
|
if(lot <= 0)
|
|
{
|
|
Print("حجم اللوت غير صالح: ", DoubleToString(lot,8));
|
|
return;
|
|
}
|
|
|
|
trade.SetExpertMagicNumber(InpMagicNumber);
|
|
trade.SetDeviationInPoints( (int) MathMax(10, InpMaxSpreadPoints) );
|
|
|
|
if(InpEnableDebug)
|
|
{
|
|
PrintFormat("Attempting to open %s: price=%.5f SL=%.5f TP=%.5f lot=%.8f spread_points=%.0f CCI_prev=%.4f CCI_cur=%.4f",
|
|
isBuy ? "BUY":"SELL", price, sl_price, tp_price, lot, spread_points, cci_prev, cci_curr);
|
|
}
|
|
|
|
bool result=false;
|
|
if(isBuy)
|
|
result = trade.Buy(lot, Symbol(), price, sl_price, tp_price, "CCI_EA_debug");
|
|
else
|
|
result = trade.Sell(lot, Symbol(), price, sl_price, tp_price, "CCI_EA_debug");
|
|
|
|
int retcode = trade.ResultRetcode();
|
|
string comment = trade.ResultComment();
|
|
ulong order_ticket = trade.ResultOrder();
|
|
|
|
if(result)
|
|
PrintFormat("تم فتح %s بنجاح على %s حجم %.8f SL=%.5f TP=%.5f ticket=%I64u retcode=%d comment=%s", isBuy ? "شراء" : "بيع", Symbol(), lot, sl_price, tp_price, order_ticket, retcode, comment);
|
|
else
|
|
PrintFormat("فشل فتح %s: retcode=%d comment=%s", isBuy ? "شراء" : "بيع", retcode, comment);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| إغلاق الصفقة حسب النوع |
|
|
//+------------------------------------------------------------------+
|
|
bool ClosePositionByType(ulong pos_type)
|
|
{
|
|
if(!PositionSelect(Symbol())) { if(InpEnableDebug) Print("ClosePositionByType: no position selected"); return false; }
|
|
if(PositionGetInteger(POSITION_TYPE) != pos_type) { if(InpEnableDebug) Print("ClosePositionByType: position type mismatch"); return false; }
|
|
double volume = PositionGetDouble(POSITION_VOLUME);
|
|
if(volume <= 0) { if(InpEnableDebug) Print("ClosePositionByType: zero volume"); return false; }
|
|
|
|
if(InpEnableDebug) PrintFormat("Closing opposite position type=%d volume=%.2f", (int)pos_type, volume);
|
|
|
|
if(trade.PositionClose(Symbol()))
|
|
{
|
|
PrintFormat("تم إغلاق المركز المعاكس (نوع=%d) بنجاح. retcode=%d comment=%s", pos_type, trade.ResultRetcode(), trade.ResultComment());
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
PrintFormat("فشل إغلاق المركز المعاكس: retcode=%d comment=%s", trade.ResultRetcode(), trade.ResultComment());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| إدارة التريلينج ستوب |
|
|
//+------------------------------------------------------------------+
|
|
void OnTick_ManageTrailing()
|
|
{
|
|
if(!InpEnableTrailing) return;
|
|
|
|
if(!PositionSelect(Symbol())) return;
|
|
ulong ptype = (ulong)PositionGetInteger(POSITION_TYPE);
|
|
double open_price = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
double volume = PositionGetDouble(POSITION_VOLUME);
|
|
double current_profit_points = 0;
|
|
double point = SymbolInfoDouble(Symbol(), SYMBOL_POINT);
|
|
double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
|
|
double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
|
|
|
|
if(ptype == POSITION_TYPE_BUY)
|
|
current_profit_points = (bid - open_price) / point;
|
|
else
|
|
current_profit_points = (open_price - ask) / point;
|
|
|
|
if(InpEnableDebug) PrintFormat("Trailing: ptype=%d open=%.5f current profit points=%.1f", (int)ptype, open_price, current_profit_points);
|
|
|
|
if(current_profit_points >= InpTrailingStart)
|
|
{
|
|
double new_sl;
|
|
if(ptype == POSITION_TYPE_BUY)
|
|
{
|
|
new_sl = NormalizeDouble(bid - InpTrailingStep * point, (int)SymbolInfoInteger(Symbol(), SYMBOL_DIGITS));
|
|
}
|
|
else
|
|
{
|
|
new_sl = NormalizeDouble(ask + InpTrailingStep * point, (int)SymbolInfoInteger(Symbol(), SYMBOL_DIGITS));
|
|
}
|
|
bool mod = trade.PositionModify(Symbol(), new_sl, PositionGetDouble(POSITION_TP));
|
|
if(InpEnableDebug) PrintFormat("Trailing: modify SL to %.5f result=%s retcode=%d comment=%s", new_sl, mod ? "OK":"FAIL", trade.ResultRetcode(), trade.ResultComment());
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+ |