//+------------------------------------------------------------------+ //| DarvasBoxStrategy.mqh | //+------------------------------------------------------------------+ // MQL5: no #if — use #ifdef only (no defined() / || in one #if) #ifdef UNITED_V2_DYNAMIC_LOTS extern double g_DB_LotSize; #define DARVAS_TRADE_LOT (g_DB_LotSize) #else #ifdef CLUSTER0_ORCHESTRATOR extern double g_DB_LotSize; #define DARVAS_TRADE_LOT (g_DB_LotSize) #else #define DARVAS_TRADE_LOT 0.01 #endif #endif bool InitDarvasBox(string symbol) { dbData.symbol = symbol; dbData.boxHigh = 0; dbData.boxLow = 0; dbData.boxFormed = false; dbData.lastBoxTime = 0; dbData.boxName = "DarvasBox_" + IntegerToString(DB_MagicNumber) + "_"; // Check if symbol exists if(!SymbolSelect(symbol, true)) { Print("DarvasBox: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name."); return false; } Sleep(100); // Wait for symbol to be ready dbData.point = SymbolInfoDouble(symbol, SYMBOL_POINT); dbData.minStopLevel = SymbolInfoInteger(symbol, SYMBOL_TRADE_STOPS_LEVEL) * dbData.point; dbData.maHandle = iMA(symbol, DB_TrendTimeframe, DB_MA_Period, 0, DB_MA_Method, DB_MA_Price); // Same TF as box highs/lows (H1 loop in CalculateDarvasBox). PERIOD_CURRENT breaks when United EA // runs on a chart timeframe other than H1 (volume/breakout no longer match the box). dbData.volumeHandle = iVolumes(symbol, PERIOD_H1, VOLUME_TICK); if(dbData.maHandle == INVALID_HANDLE || dbData.volumeHandle == INVALID_HANDLE) { Print("DarvasBox: Error creating indicators for '", symbol, "'"); return false; } dbData.trade.SetDeviationInPoints(10); dbData.trade.SetTypeFilling(ORDER_FILLING_IOC); dbData.trade.SetAsyncMode(false); dbData.trade.SetExpertMagicNumber(DB_MagicNumber); ObjectsDeleteAll(0, dbData.boxName); dbData.isInitialized = true; Print("DarvasBox: Successfully initialized for symbol '", symbol, "'"); return true; } void DeinitDarvasBox() { if(dbData.maHandle != INVALID_HANDLE) IndicatorRelease(dbData.maHandle); if(dbData.volumeHandle != INVALID_HANDLE) IndicatorRelease(dbData.volumeHandle); ObjectsDeleteAll(0, dbData.boxName); } void DrawDarvasBox() { if(!dbData.boxFormed) return; datetime time1 = iTime(dbData.symbol, PERIOD_H1, DB_BoxPeriod); datetime time2 = iTime(dbData.symbol, PERIOD_H1, 0); ObjectsDeleteAll(0, dbData.boxName); ObjectCreate(0, dbData.boxName + "Top", OBJ_TREND, 0, time1, dbData.boxHigh, time2, dbData.boxHigh); ObjectCreate(0, dbData.boxName + "Bottom", OBJ_TREND, 0, time1, dbData.boxLow, time2, dbData.boxLow); ObjectSetInteger(0, dbData.boxName + "Top", OBJPROP_COLOR, DB_BoxColor); ObjectSetInteger(0, dbData.boxName + "Bottom", OBJPROP_COLOR, DB_BoxColor); ObjectSetInteger(0, dbData.boxName + "Top", OBJPROP_WIDTH, DB_BoxWidth); ObjectSetInteger(0, dbData.boxName + "Bottom", OBJPROP_WIDTH, DB_BoxWidth); ObjectSetInteger(0, dbData.boxName + "Top", OBJPROP_RAY_RIGHT, true); ObjectSetInteger(0, dbData.boxName + "Bottom", OBJPROP_RAY_RIGHT, true); } void CalculateDarvasBox() { double high = 0; double low = DBL_MAX; // Find highest high and lowest low in the period - EXACTLY like original for(int i = 0; i < DB_BoxPeriod; i++) { high = MathMax(high, iHigh(dbData.symbol, PERIOD_H1, i)); low = MathMin(low, iLow(dbData.symbol, PERIOD_H1, i)); } double range = high - low; double allowedRange = DB_BoxDeviation * dbData.point; // Use dbData.point instead of _Point if(DB_EnableLogging) { Print("DarvasBox: Box Calculation - High: ", high, " Low: ", low, " Range: ", range, " Allowed Range: ", allowedRange); } // Check if box is formed - EXACTLY like original if(range <= allowedRange) { dbData.boxHigh = high; dbData.boxLow = low; dbData.boxFormed = true; dbData.lastBoxTime = iTime(dbData.symbol, PERIOD_CURRENT, 0); // Draw the box DrawDarvasBox(); if(DB_EnableLogging) Print("DarvasBox: Box Formed - High: ", dbData.boxHigh, " Low: ", dbData.boxLow, " Time: ", dbData.lastBoxTime); } else { dbData.boxFormed = false; // Delete box if it exists ObjectsDeleteAll(0, dbData.boxName); } } bool ValidateStopLevels(double price, double &sl, double &tp, ENUM_ORDER_TYPE orderType) { double minSlDistance = MathMax(dbData.minStopLevel, DB_StopLoss * dbData.point); double minTpDistance = MathMax(dbData.minStopLevel, DB_TakeProfit * dbData.point); if(orderType == ORDER_TYPE_BUY) { sl = price - minSlDistance; tp = price + minTpDistance; } else { sl = price + minSlDistance; tp = price - minTpDistance; } return true; } bool IsTrendFavorable(ENUM_ORDER_TYPE orderType) { if(!DB_UseTrendFilter) return true; double ma[]; ArraySetAsSeries(ma, true); if(CopyBuffer(dbData.maHandle, 0, 0, 2, ma) <= 0) return false; double currentPrice = SymbolInfoDouble(dbData.symbol, SYMBOL_ASK); double trendStrength = MathAbs(currentPrice - ma[0]) / dbData.point; if(orderType == ORDER_TYPE_BUY) return (currentPrice > ma[0] && trendStrength > DB_TrendThreshold); else return (currentPrice < ma[0] && trendStrength > DB_TrendThreshold); } bool CheckVolumeConditions() { if(!DB_UseVolumeSpikeFilter) return true; double volumes[]; ArraySetAsSeries(volumes, true); if(CopyBuffer(dbData.volumeHandle, 0, 0, DB_VolumeMA_Period + 1, volumes) <= 0) return false; double volumeMA = 0; for(int i = 1; i <= DB_VolumeMA_Period; i++) volumeMA += volumes[i]; volumeMA /= DB_VolumeMA_Period; double currentVolume = volumes[0]; if(volumeMA <= 0.0) return (currentVolume > 0.0); double volumeRatio = currentVolume / volumeMA; return (volumeRatio > DB_VolumeThresholdMultiplier); } bool PlaceOrder(ENUM_ORDER_TYPE orderType, double price, double sl, double tp) { if(!ValidateStopLevels(price, sl, tp, orderType)) { if(DB_EnableLogging) Print("DarvasBox: Order rejected - Stop levels validation failed"); return false; } if(!IsTrendFavorable(orderType)) { if(DB_EnableLogging) Print("DarvasBox: Order rejected - Trend not favorable for ", EnumToString(orderType)); return false; } if(!CheckVolumeConditions()) { if(DB_EnableLogging) Print("DarvasBox: Order rejected - Volume conditions not met"); return false; } bool result = false; const double lot = United_NormalizeVolume(dbData.symbol, DARVAS_TRADE_LOT); if(lot <= 0.0) { Print("DarvasBox: Order rejected - invalid lot after normalize (raw=", DARVAS_TRADE_LOT, ")"); return false; } // Use market price (0) instead of explicit price - this ensures market order execution // In backtesting, explicit price might fail if price has moved if(orderType == ORDER_TYPE_BUY) result = dbData.trade.Buy(lot, dbData.symbol, 0, sl, tp, "Darvas Box Breakout"); else result = dbData.trade.Sell(lot, dbData.symbol, 0, sl, tp, "Darvas Box Breakdown"); // Always log errors, success only if logging enabled if(result) { if(DB_EnableLogging) Print("DarvasBox: ", (orderType == ORDER_TYPE_BUY ? "Buy" : "Sell"), " Order Placed Successfully"); } else { // Always log failures with detailed info uint retcode_uint = dbData.trade.ResultRetcode(); int retcode = (int)retcode_uint; string desc = dbData.trade.ResultRetcodeDescription(); ulong deal = dbData.trade.ResultDeal(); ulong order = dbData.trade.ResultOrder(); Print("DarvasBox: ", (orderType == ORDER_TYPE_BUY ? "Buy" : "Sell"), " Order Failed - Retcode: ", retcode, ", Description: ", desc, ", Deal: ", deal, ", Order: ", order, ", Symbol: ", dbData.symbol, ", Requested Price: ", price, ", SL: ", sl, ", TP: ", tp); } return result; } void ProcessDarvasBox(string symbol) { // Skip if not initialized (symbol not available) if(!dbData.isInitialized) return; dbData.symbol = symbol; // Update symbol in case it changed // Calculate new box levels - EXACTLY like original (called every tick) CalculateDarvasBox(); // Check for trading signals - EXACTLY like original (checked every tick) if(dbData.boxFormed) { double currentPrice = SymbolInfoDouble(dbData.symbol, SYMBOL_ASK); long currentVolume_long = iVolume(dbData.symbol, PERIOD_H1, 0); double currentVolume = (double)currentVolume_long; if(DB_EnableLogging) { Print("DarvasBox: Current Price: ", currentPrice, " Box High: ", dbData.boxHigh, " Box Low: ", dbData.boxLow); Print("DarvasBox: Current Volume: ", currentVolume, " Volume Threshold: ", DB_VolumeThreshold); } // Check for breakout above box - EXACTLY like original if(currentPrice > dbData.boxHigh && currentVolume > DB_VolumeThreshold) { if(DB_EnableLogging) Print("DarvasBox: Breakout Signal Detected - Price above box high"); // Buy signal if(!PositionExistsByMagic(dbData.symbol, (ulong)DB_MagicNumber)) // No existing positions with our magic number { double sl = currentPrice - DB_StopLoss * dbData.point; double tp = currentPrice + DB_TakeProfit * dbData.point; if(DB_EnableLogging) Print("DarvasBox: Preparing Buy Order - Price: ", currentPrice, " SL: ", sl, " TP: ", tp); PlaceOrder(ORDER_TYPE_BUY, currentPrice, sl, tp); } else if(DB_EnableLogging) Print("DarvasBox: Skipping Buy Signal - Position already exists"); } // Check for breakdown below box - EXACTLY like original if(currentPrice < dbData.boxLow && currentVolume > DB_VolumeThreshold) { if(DB_EnableLogging) Print("DarvasBox: Breakdown Signal Detected - Price below box low"); // Sell signal if(!PositionExistsByMagic(dbData.symbol, (ulong)DB_MagicNumber)) // No existing positions with our magic number { double sl = currentPrice + DB_StopLoss * dbData.point; double tp = currentPrice - DB_TakeProfit * dbData.point; if(DB_EnableLogging) Print("DarvasBox: Preparing Sell Order - Price: ", currentPrice, " SL: ", sl, " TP: ", tp); PlaceOrder(ORDER_TYPE_SELL, currentPrice, sl, tp); } else if(DB_EnableLogging) Print("DarvasBox: Skipping Sell Signal - Position already exists"); } } else if(DB_EnableLogging) Print("DarvasBox: No Box Formed - Waiting for consolidation"); } //+------------------------------------------------------------------+