Test/2.mq5
Hector001 121b942f0d
2026-04-13 23:28:11 +01:00

2727 lines
No EOL
121 KiB
MQL5

//+------------------------------------------------------------------+
//| Daily Sweep + M15 Turtlesoup.mq5|
//| Copyright 2025, Setup Radar |
//| https://www.Setup-Radar.com |
//| Author: X.com/Hectorand |
//|Production Ready Code |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, Setup Radar"
#property link "https://www.Setup-Radar.com"
#property version "1.5" // Updated version with trade execution
// Include sector mapping
#include <SymbolToSectorLink.mqh>
#include <Trade/Trade.mqh>
// ============================================================
// INPUT PARAMETERS
// ============================================================
input int SwingLeftBars = 2; // Number of bars to check on the left for swing points
input int SwingRightBars = 2; // Number of bars to check on the right for swing points
input bool EnableDebugPrints = false; // Enable debug prints for screenshot and API calls
input int MinCandlesForTradeableDay = 48; // Minimum M15 candles for a day to be considered "tradeable"
input bool CloseChartAfterScreenshot = true; // Close temporary chart after screenshot
input double LotSize = 0.01; // Lot size for trades
input double MaxRiskPercent = 2.0; // Maximum risk as percentage of account balance
input bool EnableAutoTrading = true; // Enable automatic trade execution
string strategyName = "Daily Sweep + M15 Turtlesoup"; // Strategy identifier for alerts
string strategyId = "0000001";
string allowedTimeframe[]; // Empty array, no elements
// ============================================================
// CONFIGURATION CONSTANTS
// ============================================================
int SwingLookbackBars = 20; // Number of bars to look back for swing detection
// Server URLs for alert and screenshot delivery
string alertServerURL = "https://api.setupradar.app/api/v1/strategy/webhook"; // Endpoint for alert JSON data
string screenshotServerURL = "https://api.setupradar.app/api/v1/strategy/webhook"; // Endpoint for screenshot binary data
// Trading session definitions
string SydneySessionStart = "22:00"; // Sydney session start time (crosses midnight)
string SydneySessionEnd = "00:00"; // Sydney session end time
string AsiaSessionStart = "00:00"; // Asian session start time
string AsiaSessionEnd = "07:00"; // Asian session end time
string LondonSessionStart = "07:00"; // London session start time
string LondonSessionEnd = "12:00"; // London session end time
string NewYorkSessionStart = "12:00"; // New York session start time
string NewYorkSessionEnd = "22:00"; // New York session end time
// ============================================================
// TIME TRACKING VARIABLES
// ============================================================
string currentSession = "OutsideSession"; // Current trading session name
// ============================================================
// MARKET BIAS ENUMERATION AND STATE
// ============================================================
enum ENUM_BIAS
{
BIAS_NEUTRAL,
BIAS_BULLISH,
BIAS_BEARISH
};
// ============================================================
// TRADE STATE ENUMERATION
// ============================================================
enum ENUM_TRADE_STATE
{
TRADE_NONE, // No trade placed
TRADE_PENDING, // Alert triggered, waiting for retracement
TRADE_EXECUTED // Trade has been executed
};
// ============================================================
// SYMBOL-SPECIFIC DATA STRUCTURES
// ============================================================
// Market bias per symbol
struct BiasData
{
string symbol;
ENUM_BIAS bias;
};
// Retracement tracking per symbol (NEW - separate from main alerts)
struct RetracementTracker
{
string symbol; // Symbol name
// Bullish retracement data
bool bullishBreakoutOccurred; // Flag if bullish breakout happened
datetime bullishBreakoutTime; // Time of bullish breakout
double bullishBreakoutPrice; // Price at breakout (A point)
double bullishBPrice; // B point price
double bullishSwingHigh; // Swing high price
datetime bullishSwingHighTime; // Swing high time
double bullishFortyPercentLevel; // 40% level for retracement
double bullishLast_LL_Prices[2]; // Last two lower low prices
datetime bullishLast_LL_Times[2]; // Last two lower low times
double bullishLast_HH_Prices[2]; // Last two higher high prices (for reference)
datetime bullishLast_HH_Times[2]; // Last two higher high times (for reference)
double bullishPrevDayLow; // Previous day low
datetime bullishPrevDayLowTime; // Previous day low time
ENUM_TRADE_STATE bullishTradeState; // Track trade state for bullish
ulong bullishTradeTicket; // Ticket of executed trade
// Bearish retracement data
bool bearishBreakoutOccurred; // Flag if bearish breakout happened
datetime bearishBreakoutTime; // Time of bearish breakout
double bearishBreakoutPrice; // Price at breakout (A point)
double bearishBPrice; // B point price
double bearishSwingLow; // Swing low price
datetime bearishSwingLowTime; // Swing low time
double bearishFortyPercentLevel; // 40% level for retracement
double bearishLast_HH_Prices[2]; // Last two higher high prices
datetime bearishLast_HH_Times[2]; // Last two higher high times
double bearishLast_LL_Prices[2]; // Last two lower low prices (for reference)
datetime bearishLast_LL_Times[2]; // Last two lower low times (for reference)
double bearishPrevDayHigh; // Previous day high
datetime bearishPrevDayHighTime; // Previous day high time
ENUM_TRADE_STATE bearishTradeState; // Track trade state for bearish
ulong bearishTradeTicket; // Ticket of executed trade
};
// Alert tracking per symbol (ORIGINAL - UNCHANGED)
struct AlertTracker
{
string symbol; // Symbol name
bool bullishAlertSent; // Bullish alert sent flag for this symbol
int bullishAlertCount; // Bullish alert count for this symbol
bool bearishAlertSent; // Bearish alert sent flag for this symbol
int bearishAlertCount; // Bearish alert count for this symbol
double lastBullishSwingHigh; // Last alerted bullish swing high for this symbol
double lastBearishSwingLow; // Last alerted bearish swing low for this symbol
datetime bullishAlertTime; // Time when bullish alert was sent (C time - FIXED)
datetime bearishAlertTime; // Time when bearish alert was sent (C time - FIXED)
};
// Previous day data per symbol
struct PrevDayData
{
string symbol;
datetime lowTime; // Time of previous day's low (M15 candle)
int lowIndex; // Bar index of previous day's low
double lowPrice; // Price of previous day's low
datetime highTime; // Time of previous day's high (M15 candle)
int highIndex; // Bar index of previous day's high
double highPrice; // Price of previous day's high
};
// Swing point data per symbol
struct SwingData
{
string symbol;
// Dynamic arrays for storing all detected swing points
double swingLowPrices[];
datetime swingLowTimes[];
double swingHighPrices[];
datetime swingHighTimes[];
// Arrays for last 2 consecutive higher highs (temporary)
double HH_Prices[2]; // [0] = H1 (older), [1] = H2 (newer)
datetime HH_Times[2]; // [0] = H1 time, [1] = H2 time
// Arrays for last 2 consecutive lower lows (temporary)
double LL_Prices[2]; // [0] = L1 (older), [1] = L2 (newer)
datetime LL_Times[2]; // [0] = L1 time, [1] = L2 time
// Static arrays for valid last 2 consecutive swings (persistent)
double Last_HH_Prices[2]; // [0] = H1 (older), [1] = H2 (newer) - static
datetime Last_HH_Times[2]; // [0] = H1 time, [1] = H2 time - static
double Last_LL_Prices[2]; // [0] = L1 (older), [1] = L2 (newer) - static
datetime Last_LL_Times[2]; // [0] = L1 time, [1] = L2 time - static
// Bullish trading scenario variables
double bullishSwingHigh; // Current bullish swing high (resistance level) - Point A
datetime bullishSwingHighTime; // Time when bullish swing high occurred
int bullishBarsInBetween; // Number of bars between two bullish swing lows
// Bearish trading scenario variables
double bearishSwingLow; // Current bearish swing low (support level) - Point A
datetime bearishSwingLowTime; // Time when bearish swing low occurred
int bearishBarsInBetween; // Number of bars between two bearish swing highs
// Current market prices
double currentAskPrice;
double currentBidPrice;
// Pattern validity flags
bool bullishPatternValid; // Flag indicating if bullish pattern is valid based on PDL condition
bool bearishPatternValid; // Flag indicating if bearish pattern is valid based on PDH condition
// Enhanced validation flags
bool bullishBIsLowest; // Flag: B is the lowest low between SSL and C
bool bullishAIsHighest; // Flag: A is the highest high between A and C+1
bool bearishBIsHighest; // Flag: B is the highest high between BSL and C
bool bearishAIsLowest; // Flag: A is the lowest low between A and C+1
};
// ============================================================
// GLOBAL ARRAYS (Symbol-Specific)
// ============================================================
string Symbols[];
int SymbolsCount;
datetime last_bar_time[];
datetime last_bar_time_d[];
BiasData biasData[];
AlertTracker alertTrackers[]; // ORIGINAL - UNCHANGED
RetracementTracker retraceTrackers[]; // NEW - separate tracking
PrevDayData prevDayData[];
SwingData swingData[];
CTrade trade; // Global trade object
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
ArrayResize(allowedTimeframe, 1);
allowedTimeframe[0] = "M15";
// Initialize trade object
trade.SetExpertMagicNumber(12345);
trade.SetDeviationInPoints(10);
// Get all symbols
SymbolsCount = SymbolsTotal(true);
ArrayResize(Symbols, SymbolsCount);
ArrayResize(last_bar_time, SymbolsCount);
ArrayResize(last_bar_time_d, SymbolsCount);
// Initialize symbol-specific data arrays
ArrayResize(biasData, SymbolsCount);
ArrayResize(alertTrackers, SymbolsCount); // ORIGINAL
ArrayResize(retraceTrackers, SymbolsCount); // NEW
ArrayResize(prevDayData, SymbolsCount);
ArrayResize(swingData, SymbolsCount);
for(int i = 0; i < SymbolsCount; i++)
{
string symbol = SymbolName(i, true);
Symbols[i] = symbol;
// Initialize time tracking
last_bar_time[i] = iTime(symbol, PERIOD_M15, 0);
last_bar_time_d[i] = iTime(symbol, PERIOD_D1, 0);
// Initialize bias data
biasData[i].symbol = symbol;
biasData[i].bias = BIAS_NEUTRAL;
// Initialize alert trackers (ORIGINAL - UNCHANGED)
alertTrackers[i].symbol = symbol;
alertTrackers[i].bullishAlertSent = false;
alertTrackers[i].bullishAlertCount = 0;
alertTrackers[i].bearishAlertSent = false;
alertTrackers[i].bearishAlertCount = 0;
alertTrackers[i].lastBullishSwingHigh = 0;
alertTrackers[i].lastBearishSwingLow = 0;
alertTrackers[i].bullishAlertTime = 0;
alertTrackers[i].bearishAlertTime = 0;
// Initialize retracement trackers (NEW)
retraceTrackers[i].symbol = symbol;
retraceTrackers[i].bullishBreakoutOccurred = false;
retraceTrackers[i].bullishBreakoutTime = 0;
retraceTrackers[i].bullishBreakoutPrice = 0;
retraceTrackers[i].bullishBPrice = 0;
retraceTrackers[i].bullishSwingHigh = 0;
retraceTrackers[i].bullishSwingHighTime = 0;
retraceTrackers[i].bullishFortyPercentLevel = 0;
retraceTrackers[i].bullishTradeState = TRADE_NONE;
retraceTrackers[i].bullishTradeTicket = 0;
ArrayInitialize(retraceTrackers[i].bullishLast_LL_Prices, 0);
ArrayInitialize(retraceTrackers[i].bullishLast_LL_Times, 0);
ArrayInitialize(retraceTrackers[i].bullishLast_HH_Prices, 0);
ArrayInitialize(retraceTrackers[i].bullishLast_HH_Times, 0);
retraceTrackers[i].bullishPrevDayLow = 0;
retraceTrackers[i].bullishPrevDayLowTime = 0;
retraceTrackers[i].bearishBreakoutOccurred = false;
retraceTrackers[i].bearishBreakoutTime = 0;
retraceTrackers[i].bearishBreakoutPrice = 0;
retraceTrackers[i].bearishBPrice = 0;
retraceTrackers[i].bearishSwingLow = 0;
retraceTrackers[i].bearishSwingLowTime = 0;
retraceTrackers[i].bearishFortyPercentLevel = 0;
retraceTrackers[i].bearishTradeState = TRADE_NONE;
retraceTrackers[i].bearishTradeTicket = 0;
ArrayInitialize(retraceTrackers[i].bearishLast_HH_Prices, 0);
ArrayInitialize(retraceTrackers[i].bearishLast_HH_Times, 0);
ArrayInitialize(retraceTrackers[i].bearishLast_LL_Prices, 0);
ArrayInitialize(retraceTrackers[i].bearishLast_LL_Times, 0);
retraceTrackers[i].bearishPrevDayHigh = 0;
retraceTrackers[i].bearishPrevDayHighTime = 0;
// Initialize previous day data
prevDayData[i].symbol = symbol;
prevDayData[i].lowTime = 0;
prevDayData[i].lowIndex = -1;
prevDayData[i].lowPrice = 0;
prevDayData[i].highTime = 0;
prevDayData[i].highIndex = -1;
prevDayData[i].highPrice = 0;
// Initialize swing data arrays
swingData[i].symbol = symbol;
ArrayResize(swingData[i].swingLowPrices, 0);
ArrayResize(swingData[i].swingLowTimes, 0);
ArrayResize(swingData[i].swingHighPrices, 0);
ArrayResize(swingData[i].swingHighTimes, 0);
ArrayInitialize(swingData[i].HH_Prices, EMPTY_VALUE);
ArrayInitialize(swingData[i].HH_Times, 0);
ArrayInitialize(swingData[i].LL_Prices, EMPTY_VALUE);
ArrayInitialize(swingData[i].LL_Times, 0);
ArrayInitialize(swingData[i].Last_HH_Prices, 0);
ArrayInitialize(swingData[i].Last_HH_Times, 0);
ArrayInitialize(swingData[i].Last_LL_Prices, 0);
ArrayInitialize(swingData[i].Last_LL_Times, 0);
swingData[i].bullishSwingHigh = 0;
swingData[i].bullishSwingHighTime = 0;
swingData[i].bullishBarsInBetween = 0;
swingData[i].bearishSwingLow = 0;
swingData[i].bearishSwingLowTime = 0;
swingData[i].bearishBarsInBetween = 0;
swingData[i].currentAskPrice = 0;
swingData[i].currentBidPrice = 0;
// Initialize pattern validity flags
swingData[i].bullishPatternValid = false;
swingData[i].bearishPatternValid = false;
// Initialize enhanced validation flags
swingData[i].bullishBIsLowest = false;
swingData[i].bullishAIsHighest = false;
swingData[i].bearishBIsHighest = false;
swingData[i].bearishAIsLowest = false;
}
if(EnableDebugPrints)
{
Print("DEBUG: Expert Advisor initialized. Monitoring ", SymbolsCount, " symbols");
Print("DEBUG: Created symbol-specific data structures for all symbols");
Print("DEBUG: 2-Alert system enabled (Breakout + Retracement)");
Print("DEBUG: Auto-trading is ", EnableAutoTrading ? "ENABLED" : "DISABLED");
}
EventSetTimer(2);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// Clean up all chart objects
DeleteAllObjects();
EventKillTimer();
if(EnableDebugPrints)
{
Print("DEBUG: Expert Advisor deinitialized. Reason: ", reason);
}
}
//+------------------------------------------------------------------+
//| Get symbol index in all data arrays |
//+------------------------------------------------------------------+
int GetSymbolIndex(string symbol)
{
for(int i = 0; i < SymbolsCount; i++)
{
if(Symbols[i] == symbol)
return i;
}
return -1;
}
//+------------------------------------------------------------------+
//| Expert timer function - MAIN EXECUTION LOOP |
//+------------------------------------------------------------------+
void OnTimer()
{
for(int i = 0; i < SymbolsCount; i++)
{
string symbol = Symbols[i];
// Update current market prices
swingData[i].currentAskPrice = SymbolInfoDouble(symbol, SYMBOL_ASK);
swingData[i].currentBidPrice = SymbolInfoDouble(symbol, SYMBOL_BID);
// Check if new trading day has started
if(IsNewDay(i))
{
biasData[i].bias = BIAS_NEUTRAL; // Reset bias for this symbol
// Reset alert counters for this symbol (ORIGINAL - UNCHANGED)
alertTrackers[i].bullishAlertCount = 0;
alertTrackers[i].bearishAlertCount = 0;
alertTrackers[i].bullishAlertSent = false;
alertTrackers[i].bearishAlertSent = false;
// Reset retracement trackers (NEW)
retraceTrackers[i].bullishBreakoutOccurred = false;
retraceTrackers[i].bearishBreakoutOccurred = false;
retraceTrackers[i].bullishTradeState = TRADE_NONE;
retraceTrackers[i].bearishTradeState = TRADE_NONE;
// Reset pattern validity flags
swingData[i].bullishPatternValid = false;
swingData[i].bearishPatternValid = false;
swingData[i].bullishBIsLowest = false;
swingData[i].bullishAIsHighest = false;
swingData[i].bearishBIsHighest = false;
swingData[i].bearishAIsLowest = false;
if(EnableDebugPrints)
{
Print("DEBUG: Daily reset for ", symbol, " - Counters and retracement flags reset");
}
}
// Check for new 15-minute bar formation
if(IsNew15MinBar(i))
{
currentSession = GetCurrentSession(); // Global session (same for all)
// Calculate previous day's high and low from M15 candles for THIS symbol
prevDayData[i].lowTime = GetM15PrevDayLowTime(symbol);
if(prevDayData[i].lowTime > 0)
{
prevDayData[i].lowIndex = iBarShift(symbol, PERIOD_M15, prevDayData[i].lowTime, true);
prevDayData[i].lowPrice = iLow(symbol, PERIOD_M15, prevDayData[i].lowIndex);
}
prevDayData[i].highTime = GetM15PrevDayHighTime(symbol);
if(prevDayData[i].highTime > 0)
{
prevDayData[i].highIndex = iBarShift(symbol, PERIOD_M15, prevDayData[i].highTime, true);
prevDayData[i].highPrice = iHigh(symbol, PERIOD_M15, prevDayData[i].highIndex);
}
// Debug print for previous day values
if(EnableDebugPrints)
{
Print("DEBUG: Symbol=", symbol,
" PrevDayHigh=", prevDayData[i].highPrice,
" at ", TimeToString(prevDayData[i].highTime),
" PrevDayLow=", prevDayData[i].lowPrice,
" at ", TimeToString(prevDayData[i].lowTime));
}
// Reset swing arrays for fresh analysis
ArrayResize(swingData[i].swingLowPrices, 0);
ArrayResize(swingData[i].swingLowTimes, 0);
ArrayResize(swingData[i].swingHighPrices, 0);
ArrayResize(swingData[i].swingHighTimes, 0);
// Core analysis functions for THIS symbol
FindSwingPoints(i); // Find swing highs and lows
GetConsecutiveHighsAndLows(i); // Identify consecutive swings
GetBreakPoint(i); // Determine breakout points
// Validate patterns against previous day levels
ValidatePatterns(i);
}
// Update market bias based on daily price action for THIS symbol
FindBias(i);
// Check for ORIGINAL alert conditions (UNCHANGED)
CheckOriginalAlerts(i);
// Check for retracement alerts (NEW - separate monitoring)
CheckRetracementAlerts(i);
}
}
// ============================================================
// TRADE EXECUTION FUNCTIONS
// ============================================================
//+------------------------------------------------------------------+
//| Calculate position size based on risk percentage |
//+------------------------------------------------------------------+
double CalculatePositionSize(string symbol, double entryPrice, double stopLossPrice, ENUM_ORDER_TYPE orderType)
{
double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE);
double riskAmount = accountBalance * (MaxRiskPercent / 100.0);
double pointValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
double stopDistancePoints = MathAbs(entryPrice - stopLossPrice) / tickSize;
double riskPerLot = stopDistancePoints * pointValue;
if(riskPerLot <= 0)
return LotSize; // Return default lot size if calculation fails
double lotSize = riskAmount / riskPerLot;
// Normalize lot size
double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
lotSize = MathFloor(lotSize / lotStep) * lotStep;
lotSize = MathMax(minLot, MathMin(maxLot, lotSize));
if(EnableDebugPrints)
{
Print("DEBUG: Position size calculation for ", symbol);
Print("DEBUG: Entry: ", entryPrice, " SL: ", stopLossPrice);
Print("DEBUG: Stop distance points: ", stopDistancePoints);
Print("DEBUG: Risk amount: ", riskAmount);
Print("DEBUG: Calculated lot size: ", lotSize);
}
return lotSize;
}
//+------------------------------------------------------------------+
//| Execute bullish market buy trade |
//+------------------------------------------------------------------+
bool ExecuteBullishTrade(int symbolIndex)
{
if(!EnableAutoTrading)
{
if(EnableDebugPrints) Print("DEBUG: Auto-trading disabled, skipping bullish trade for ", Symbols[symbolIndex]);
return false;
}
string symbol = Symbols[symbolIndex];
double entryPrice = retraceTrackers[symbolIndex].bullishFortyPercentLevel;
double stopLoss = retraceTrackers[symbolIndex].bullishBPrice;
double takeProfit = retraceTrackers[symbolIndex].bullishSwingHigh;
// Calculate position size
double lotSize = CalculatePositionSize(symbol, entryPrice, stopLoss, ORDER_TYPE_BUY);
// Ensure price is normalized
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
entryPrice = NormalizeDouble(entryPrice, digits);
stopLoss = NormalizeDouble(stopLoss, digits);
takeProfit = NormalizeDouble(takeProfit, digits);
if(EnableDebugPrints)
{
Print("DEBUG: Executing BULLISH MARKET BUY for ", symbol);
Print("DEBUG: Entry (40% level): ", entryPrice);
Print("DEBUG: Stop Loss (B price): ", stopLoss);
Print("DEBUG: Take Profit (Swing High): ", takeProfit);
Print("DEBUG: Lot Size: ", lotSize);
}
// Set slippage
trade.SetDeviationInPoints(10);
// Open buy market order
bool result = trade.Buy(lotSize, symbol, entryPrice, stopLoss, takeProfit, "TurtleSoup Bullish Setup");
if(result)
{
retraceTrackers[symbolIndex].bullishTradeTicket = trade.ResultDeal();
retraceTrackers[symbolIndex].bullishTradeState = TRADE_EXECUTED;
if(EnableDebugPrints)
{
Print("DEBUG: SUCCESS - Bullish trade executed! Ticket: ", retraceTrackers[symbolIndex].bullishTradeTicket);
}
return true;
}
else
{
if(EnableDebugPrints)
{
Print("DEBUG: FAILED - Bullish trade execution failed. Error: ", GetLastError());
}
return false;
}
}
//+------------------------------------------------------------------+
//| Execute bearish market sell trade |
//+------------------------------------------------------------------+
bool ExecuteBearishTrade(int symbolIndex)
{
if(!EnableAutoTrading)
{
if(EnableDebugPrints) Print("DEBUG: Auto-trading disabled, skipping bearish trade for ", Symbols[symbolIndex]);
return false;
}
string symbol = Symbols[symbolIndex];
double entryPrice = retraceTrackers[symbolIndex].bearishFortyPercentLevel;
double stopLoss = retraceTrackers[symbolIndex].bearishBPrice;
double takeProfit = retraceTrackers[symbolIndex].bearishSwingLow;
// Calculate position size
double lotSize = CalculatePositionSize(symbol, entryPrice, stopLoss, ORDER_TYPE_SELL);
// Ensure price is normalized
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
entryPrice = NormalizeDouble(entryPrice, digits);
stopLoss = NormalizeDouble(stopLoss, digits);
takeProfit = NormalizeDouble(takeProfit, digits);
if(EnableDebugPrints)
{
Print("DEBUG: Executing BEARISH MARKET SELL for ", symbol);
Print("DEBUG: Entry (40% level): ", entryPrice);
Print("DEBUG: Stop Loss (B price): ", stopLoss);
Print("DEBUG: Take Profit (Swing Low): ", takeProfit);
Print("DEBUG: Lot Size: ", lotSize);
}
// Set slippage
trade.SetDeviationInPoints(10);
// Open sell market order
bool result = trade.Sell(lotSize, symbol, entryPrice, stopLoss, takeProfit, "TurtleSoup Bearish Setup");
if(result)
{
retraceTrackers[symbolIndex].bearishTradeTicket = trade.ResultDeal();
retraceTrackers[symbolIndex].bearishTradeState = TRADE_EXECUTED;
if(EnableDebugPrints)
{
Print("DEBUG: SUCCESS - Bearish trade executed! Ticket: ", retraceTrackers[symbolIndex].bearishTradeTicket);
}
return true;
}
else
{
if(EnableDebugPrints)
{
Print("DEBUG: FAILED - Bearish trade execution failed. Error: ", GetLastError());
}
return false;
}
}
// ============================================================
// ORIGINAL ALERT FUNCTION - COMPLETELY UNCHANGED
// ============================================================
//+------------------------------------------------------------------+
//| Original alert monitoring (unchanged from version 1.3) |
//+------------------------------------------------------------------+
void CheckOriginalAlerts(int symbolIndex)
{
string symbol = Symbols[symbolIndex];
// Get close price of previous candle for gap detection
double closeLastCandle = iClose(symbol, PERIOD_M15, 1);
// BULLISH ALERT CONDITION (ORIGINAL - UNCHANGED)
if(biasData[symbolIndex].bias == BIAS_BULLISH &&
swingData[symbolIndex].bullishPatternValid && // Check PDL validation
swingData[symbolIndex].currentBidPrice > swingData[symbolIndex].bullishSwingHigh &&
closeLastCandle < swingData[symbolIndex].bullishSwingHigh &&
swingData[symbolIndex].bullishSwingHigh != alertTrackers[symbolIndex].lastBullishSwingHigh &&
alertTrackers[symbolIndex].bullishAlertSent == false &&
alertTrackers[symbolIndex].bullishAlertCount < 1)
{
datetime timeNow = TimeCurrent();
// Perform enhanced validations
swingData[symbolIndex].bullishBIsLowest = ValidateBullishBIsLowest(symbolIndex, timeNow);
swingData[symbolIndex].bullishAIsHighest = ValidateBullishAIsHighest(symbolIndex, timeNow);
// Validate price action: No trading above bullishSwingHigh before trigger
int barsBetweenSwingHighAndTrigger = GetBarsBetweenTimes(swingData[symbolIndex].bullishSwingHighTime, timeNow, symbol);
int barsBetweenFirstLowAndTrigger = GetBarsBetweenTimes(swingData[symbolIndex].Last_LL_Times[0], timeNow, symbol);
bool allBarsValid = true;
// Check all bars between swing high and trigger time
for(int b = barsBetweenSwingHighAndTrigger - 1; b >= 1; b--)
{
if(iHigh(symbol, PERIOD_M15, b) >= swingData[symbolIndex].bullishSwingHigh)
{
allBarsValid = false;
break;
}
}
// Check all bars between first low and trigger time
for(int b = barsBetweenFirstLowAndTrigger; b >= 0; b--)
{
if(iLow(symbol, PERIOD_M15, b) < swingData[symbolIndex].Last_LL_Prices[1])
{
allBarsValid = false;
break;
}
}
// If all validations pass, trigger bullish alert
if(allBarsValid && swingData[symbolIndex].bullishBIsLowest && swingData[symbolIndex].bullishAIsHighest)
{
// Update alert state for THIS SYMBOL (ORIGINAL)
alertTrackers[symbolIndex].bullishAlertSent = true;
alertTrackers[symbolIndex].bullishAlertCount++;
alertTrackers[symbolIndex].lastBullishSwingHigh = swingData[symbolIndex].bullishSwingHigh;
alertTrackers[symbolIndex].bullishAlertTime = TimeCurrent(); // STORE C TIME - NEVER CHANGES
// SAVE DATA FOR RETRACEMENT ALERT (NEW)
SaveBullishRetracementData(symbolIndex);
if(EnableDebugPrints)
{
Print("DEBUG: BULLISH BREAKOUT ALERT for ", symbol, " at ", TimeToString(timeNow));
Print("DEBUG: Pattern validation: PASSED (B <= PDL)");
Print("DEBUG: Enhanced validations: B is lowest ✓, A is highest ✓");
Print("DEBUG: Swing High Price (A): ", swingData[symbolIndex].bullishSwingHigh,
" Time: ", TimeToString(swingData[symbolIndex].bullishSwingHighTime));
Print("DEBUG: B Point Price: ", swingData[symbolIndex].Last_LL_Prices[1]);
Print("DEBUG: Market Bid Price: ", swingData[symbolIndex].currentBidPrice);
Print("DEBUG: Alert #", alertTrackers[symbolIndex].bullishAlertCount, " for today");
Print("DEBUG: C time FIXED at: ", TimeToString(alertTrackers[symbolIndex].bullishAlertTime));
Print("DEBUG: Now monitoring for retracement to 40% level...");
}
// Capture chart state using BREAKOUT annotations
string screenshotFilename = TakeScreenshot("Bullish_Breakout", symbol, symbolIndex);
// Generate unique identifier
string uniqueId = GenerateUniqueId();
// Send alert data (ORIGINAL - UNCHANGED)
SendAlertData("bullish",
swingData[symbolIndex].bullishSwingHigh,
swingData[symbolIndex].bullishSwingHighTime,
alertTrackers[symbolIndex].bullishAlertTime,
screenshotFilename, uniqueId, symbol, symbolIndex);
// Send screenshot
if(screenshotFilename != "")
SendScreenshotBinary(screenshotFilename, "bullish", uniqueId, symbol, symbolIndex);
}
else
{
if(EnableDebugPrints)
{
Print("DEBUG: Bullish alert conditions met but enhanced validations failed for ", symbol);
Print("DEBUG: B is lowest: ", swingData[symbolIndex].bullishBIsLowest ? "" : "");
Print("DEBUG: A is highest: ", swingData[symbolIndex].bullishAIsHighest ? "" : "");
Print("DEBUG: Price action valid: ", allBarsValid ? "" : "");
}
}
}
// BEARISH ALERT CONDITION (ORIGINAL - UNCHANGED)
if(biasData[symbolIndex].bias == BIAS_BEARISH &&
swingData[symbolIndex].bearishPatternValid && // Check PDH validation
swingData[symbolIndex].currentAskPrice < swingData[symbolIndex].bearishSwingLow &&
closeLastCandle > swingData[symbolIndex].bearishSwingLow &&
swingData[symbolIndex].bearishSwingLow != alertTrackers[symbolIndex].lastBearishSwingLow &&
alertTrackers[symbolIndex].bearishAlertSent == false &&
alertTrackers[symbolIndex].bearishAlertCount < 1)
{
datetime timeNow = TimeCurrent();
// Perform enhanced validations
swingData[symbolIndex].bearishBIsHighest = ValidateBearishBIsHighest(symbolIndex, timeNow);
swingData[symbolIndex].bearishAIsLowest = ValidateBearishAIsLowest(symbolIndex, timeNow);
// Validate price action: No trading below bearishSwingLow before trigger
int barsBetweenSwingLowAndTrigger = GetBarsBetweenTimes(swingData[symbolIndex].bearishSwingLowTime, timeNow, symbol);
int barsBetweenFirstHighAndTrigger = GetBarsBetweenTimes(swingData[symbolIndex].Last_HH_Times[0], timeNow, symbol);
bool allBarsValid = true;
// Check all bars between swing low and trigger time
for(int b = barsBetweenSwingLowAndTrigger - 1; b >= 1; b--)
{
if(iLow(symbol, PERIOD_M15, b) <= swingData[symbolIndex].bearishSwingLow)
{
allBarsValid = false;
break;
}
}
// Check all bars between first high and trigger time
for(int b = barsBetweenFirstHighAndTrigger; b >= 0; b--)
{
if(iHigh(symbol, PERIOD_M15, b) > swingData[symbolIndex].Last_HH_Prices[1])
{
allBarsValid = false;
break;
}
}
// If all validations pass, trigger bearish alert
if(allBarsValid && swingData[symbolIndex].bearishBIsHighest && swingData[symbolIndex].bearishAIsLowest)
{
// Update alert state for THIS SYMBOL (ORIGINAL)
alertTrackers[symbolIndex].bearishAlertSent = true;
alertTrackers[symbolIndex].bearishAlertCount++;
alertTrackers[symbolIndex].lastBearishSwingLow = swingData[symbolIndex].bearishSwingLow;
alertTrackers[symbolIndex].bearishAlertTime = TimeCurrent(); // STORE C TIME - NEVER CHANGES
// SAVE DATA FOR RETRACEMENT ALERT (NEW)
SaveBearishRetracementData(symbolIndex);
if(EnableDebugPrints)
{
Print("DEBUG: BEARISH BREAKOUT ALERT for ", symbol, " at ", TimeToString(timeNow));
Print("DEBUG: Pattern validation: PASSED (B >= PDH)");
Print("DEBUG: Enhanced validations: B is highest ✓, A is lowest ✓");
Print("DEBUG: Swing Low Price (A): ", swingData[symbolIndex].bearishSwingLow,
" Time: ", TimeToString(swingData[symbolIndex].bearishSwingLowTime));
Print("DEBUG: B Point Price: ", swingData[symbolIndex].Last_HH_Prices[1]);
Print("DEBUG: Market Ask Price: ", swingData[symbolIndex].currentAskPrice);
Print("DEBUG: Alert #", alertTrackers[symbolIndex].bearishAlertCount, " for today");
Print("DEBUG: C time FIXED at: ", TimeToString(alertTrackers[symbolIndex].bearishAlertTime));
Print("DEBUG: Now monitoring for retracement to 40% level...");
}
// Capture chart state using BREAKOUT annotations
string screenshotFilename = TakeScreenshot("Bearish_Breakout", symbol, symbolIndex);
// Generate unique identifier
string uniqueId = GenerateUniqueId();
// Send alert data (ORIGINAL - UNCHANGED)
SendAlertData("bearish",
swingData[symbolIndex].bearishSwingLow,
swingData[symbolIndex].bearishSwingLowTime,
alertTrackers[symbolIndex].bearishAlertTime,
screenshotFilename, uniqueId, symbol, symbolIndex);
// Send screenshot
if(screenshotFilename != "")
SendScreenshotBinary(screenshotFilename, "bearish", uniqueId, symbol, symbolIndex);
}
else
{
if(EnableDebugPrints)
{
Print("DEBUG: Bearish alert conditions met but enhanced validations failed for ", symbol);
Print("DEBUG: B is highest: ", swingData[symbolIndex].bearishBIsHighest ? "" : "");
Print("DEBUG: A is lowest: ", swingData[symbolIndex].bearishAIsLowest ? "" : "");
Print("DEBUG: Price action valid: ", allBarsValid ? "" : "");
}
}
}
}
// ============================================================
// NEW RETRACEMENT DATA SAVING FUNCTIONS
// ============================================================
//+------------------------------------------------------------------+
//| Save bullish data for retracement alert |
//+------------------------------------------------------------------+
void SaveBullishRetracementData(int symbolIndex)
{
retraceTrackers[symbolIndex].bullishBreakoutOccurred = true;
retraceTrackers[symbolIndex].bullishBreakoutTime = alertTrackers[symbolIndex].bullishAlertTime;
retraceTrackers[symbolIndex].bullishBreakoutPrice = swingData[symbolIndex].bullishSwingHigh;
retraceTrackers[symbolIndex].bullishBPrice = swingData[symbolIndex].Last_LL_Prices[1];
retraceTrackers[symbolIndex].bullishSwingHigh = swingData[symbolIndex].bullishSwingHigh;
retraceTrackers[symbolIndex].bullishSwingHighTime = swingData[symbolIndex].bullishSwingHighTime;
retraceTrackers[symbolIndex].bullishTradeState = TRADE_PENDING;
// Calculate 40% level
double moveDistance = swingData[symbolIndex].bullishSwingHigh - swingData[symbolIndex].Last_LL_Prices[1];
retraceTrackers[symbolIndex].bullishFortyPercentLevel = swingData[symbolIndex].Last_LL_Prices[1] + (moveDistance * 0.40);
// Save swing point data
retraceTrackers[symbolIndex].bullishLast_LL_Prices[0] = swingData[symbolIndex].Last_LL_Prices[0];
retraceTrackers[symbolIndex].bullishLast_LL_Prices[1] = swingData[symbolIndex].Last_LL_Prices[1];
retraceTrackers[symbolIndex].bullishLast_LL_Times[0] = swingData[symbolIndex].Last_LL_Times[0];
retraceTrackers[symbolIndex].bullishLast_LL_Times[1] = swingData[symbolIndex].Last_LL_Times[1];
retraceTrackers[symbolIndex].bullishLast_HH_Prices[0] = swingData[symbolIndex].Last_HH_Prices[0];
retraceTrackers[symbolIndex].bullishLast_HH_Prices[1] = swingData[symbolIndex].Last_HH_Prices[1];
retraceTrackers[symbolIndex].bullishLast_HH_Times[0] = swingData[symbolIndex].Last_HH_Times[0];
retraceTrackers[symbolIndex].bullishLast_HH_Times[1] = swingData[symbolIndex].Last_HH_Times[1];
// Save previous day data
retraceTrackers[symbolIndex].bullishPrevDayLow = prevDayData[symbolIndex].lowPrice;
retraceTrackers[symbolIndex].bullishPrevDayLowTime = prevDayData[symbolIndex].lowTime;
}
//+------------------------------------------------------------------+
//| Save bearish data for retracement alert |
//+------------------------------------------------------------------+
void SaveBearishRetracementData(int symbolIndex)
{
retraceTrackers[symbolIndex].bearishBreakoutOccurred = true;
retraceTrackers[symbolIndex].bearishBreakoutTime = alertTrackers[symbolIndex].bearishAlertTime;
retraceTrackers[symbolIndex].bearishBreakoutPrice = swingData[symbolIndex].bearishSwingLow;
retraceTrackers[symbolIndex].bearishBPrice = swingData[symbolIndex].Last_HH_Prices[1];
retraceTrackers[symbolIndex].bearishSwingLow = swingData[symbolIndex].bearishSwingLow;
retraceTrackers[symbolIndex].bearishSwingLowTime = swingData[symbolIndex].bearishSwingLowTime;
retraceTrackers[symbolIndex].bearishTradeState = TRADE_PENDING;
// Calculate 40% level
double moveDistance = swingData[symbolIndex].Last_HH_Prices[1] - swingData[symbolIndex].bearishSwingLow;
retraceTrackers[symbolIndex].bearishFortyPercentLevel = swingData[symbolIndex].Last_HH_Prices[1] - (moveDistance * 0.40);
// Save swing point data
retraceTrackers[symbolIndex].bearishLast_HH_Prices[0] = swingData[symbolIndex].Last_HH_Prices[0];
retraceTrackers[symbolIndex].bearishLast_HH_Prices[1] = swingData[symbolIndex].Last_HH_Prices[1];
retraceTrackers[symbolIndex].bearishLast_HH_Times[0] = swingData[symbolIndex].Last_HH_Times[0];
retraceTrackers[symbolIndex].bearishLast_HH_Times[1] = swingData[symbolIndex].Last_HH_Times[1];
retraceTrackers[symbolIndex].bearishLast_LL_Prices[0] = swingData[symbolIndex].Last_LL_Prices[0];
retraceTrackers[symbolIndex].bearishLast_LL_Prices[1] = swingData[symbolIndex].Last_LL_Prices[1];
retraceTrackers[symbolIndex].bearishLast_LL_Times[0] = swingData[symbolIndex].Last_LL_Times[0];
retraceTrackers[symbolIndex].bearishLast_LL_Times[1] = swingData[symbolIndex].Last_LL_Times[1];
// Save previous day data
retraceTrackers[symbolIndex].bearishPrevDayHigh = prevDayData[symbolIndex].highPrice;
retraceTrackers[symbolIndex].bearishPrevDayHighTime = prevDayData[symbolIndex].highTime;
}
// ============================================================
// NEW RETRACEMENT ALERT MONITORING WITH TRADE EXECUTION
// ============================================================
//+------------------------------------------------------------------+
//| Check for retracement alerts (separate monitoring) |
//+------------------------------------------------------------------+
void CheckRetracementAlerts(int symbolIndex)
{
string symbol = Symbols[symbolIndex];
// Check for bullish retracement
if(retraceTrackers[symbolIndex].bullishBreakoutOccurred &&
retraceTrackers[symbolIndex].bullishTradeState == TRADE_PENDING)
{
// Monitor for retracement to 40% level
if(swingData[symbolIndex].currentBidPrice <= retraceTrackers[symbolIndex].bullishFortyPercentLevel)
{
datetime timeNow = TimeCurrent();
if(EnableDebugPrints)
{
Print("DEBUG: BULLISH RETRACEMENT ALERT for ", symbol, " at ", TimeToString(timeNow));
Print("DEBUG: Price retraced to 40% level: ", retraceTrackers[symbolIndex].bullishFortyPercentLevel);
Print("DEBUG: Original breakout at: ", TimeToString(retraceTrackers[symbolIndex].bullishBreakoutTime));
}
// EXECUTE TRADE AT MARKET ON RETRACEMENT
ExecuteBullishTrade(symbolIndex);
// Capture chart state using RETRACEMENT annotations
string screenshotFilename = TakeScreenshot("Bullish_Retracement", symbol, symbolIndex);
// Generate unique identifier
string uniqueId = GenerateUniqueId();
// Send alert data (same payload structure)
SendAlertData("bullish_retracement",
retraceTrackers[symbolIndex].bullishSwingHigh,
retraceTrackers[symbolIndex].bullishSwingHighTime,
timeNow,
screenshotFilename, uniqueId, symbol, symbolIndex);
// Send screenshot
if(screenshotFilename != "")
SendScreenshotBinary(screenshotFilename, "bullish_retracement", uniqueId, symbol, symbolIndex);
// Don't reset flag - keep as executed to prevent re-triggering
}
}
// Check for bearish retracement
if(retraceTrackers[symbolIndex].bearishBreakoutOccurred &&
retraceTrackers[symbolIndex].bearishTradeState == TRADE_PENDING)
{
// Monitor for retracement to 40% level
if(swingData[symbolIndex].currentAskPrice >= retraceTrackers[symbolIndex].bearishFortyPercentLevel)
{
datetime timeNow = TimeCurrent();
if(EnableDebugPrints)
{
Print("DEBUG: BEARISH RETRACEMENT ALERT for ", symbol, " at ", TimeToString(timeNow));
Print("DEBUG: Price retraced to 40% level: ", retraceTrackers[symbolIndex].bearishFortyPercentLevel);
Print("DEBUG: Original breakout at: ", TimeToString(retraceTrackers[symbolIndex].bearishBreakoutTime));
}
// EXECUTE TRADE AT MARKET ON RETRACEMENT
ExecuteBearishTrade(symbolIndex);
// Capture chart state using RETRACEMENT annotations
string screenshotFilename = TakeScreenshot("Bearish_Retracement", symbol, symbolIndex);
// Generate unique identifier
string uniqueId = GenerateUniqueId();
// Send alert data (same payload structure)
SendAlertData("bearish_retracement",
retraceTrackers[symbolIndex].bearishSwingLow,
retraceTrackers[symbolIndex].bearishSwingLowTime,
timeNow,
screenshotFilename, uniqueId, symbol, symbolIndex);
// Send screenshot
if(screenshotFilename != "")
SendScreenshotBinary(screenshotFilename, "bearish_retracement", uniqueId, symbol, symbolIndex);
// Don't reset flag - keep as executed to prevent re-triggering
}
}
}
// ============================================================
// FUNCTION: Validate patterns against previous day levels
// ============================================================
//+------------------------------------------------------------------+
//| Validate bullish and bearish patterns against PDH/PDL |
//+------------------------------------------------------------------+
void ValidatePatterns(int symbolIndex)
{
string symbol = Symbols[symbolIndex];
// Reset validity flags
swingData[symbolIndex].bullishPatternValid = false;
swingData[symbolIndex].bearishPatternValid = false;
// Check if we have valid swing points and previous day data
bool hasBullishSwings = (swingData[symbolIndex].Last_LL_Prices[0] > 0 &&
swingData[symbolIndex].Last_LL_Prices[1] > 0);
bool hasBearishSwings = (swingData[symbolIndex].Last_HH_Prices[0] > 0 &&
swingData[symbolIndex].Last_HH_Prices[1] > 0);
bool hasPrevDayData = (prevDayData[symbolIndex].lowPrice > 0 &&
prevDayData[symbolIndex].highPrice > 0);
if(!hasPrevDayData)
{
if(EnableDebugPrints)
{
Print("DEBUG: Cannot validate patterns for ", symbol, " - Previous day data missing");
}
return;
}
// Validate BULLISH pattern
if(hasBullishSwings)
{
// Bullish pattern is VALID only if Last_LL_Prices[1] (B point) is LESS THAN or equal to previous day low
if(swingData[symbolIndex].Last_LL_Prices[1] <= prevDayData[symbolIndex].lowPrice)
{
swingData[symbolIndex].bullishPatternValid = true;
if(EnableDebugPrints)
{
Print("DEBUG: Bullish pattern VALID for ", symbol);
Print("DEBUG: B point (LL2): ", swingData[symbolIndex].Last_LL_Prices[1]);
Print("DEBUG: PDL: ", prevDayData[symbolIndex].lowPrice);
Print("DEBUG: Condition: B <= PDL ✓");
}
}
else
{
if(EnableDebugPrints)
{
Print("DEBUG: Bullish pattern INVALID for ", symbol);
Print("DEBUG: B point (LL2): ", swingData[symbolIndex].Last_LL_Prices[1]);
Print("DEBUG: PDL: ", prevDayData[symbolIndex].lowPrice);
Print("DEBUG: Condition failed: B > PDL");
}
}
}
// Validate BEARISH pattern
if(hasBearishSwings)
{
// Bearish pattern is VALID only if Last_HH_Prices[1] (B point) is GREATER THAN or equal to previous day high
if(swingData[symbolIndex].Last_HH_Prices[1] >= prevDayData[symbolIndex].highPrice)
{
swingData[symbolIndex].bearishPatternValid = true;
if(EnableDebugPrints)
{
Print("DEBUG: Bearish pattern VALID for ", symbol);
Print("DEBUG: B point (HH2): ", swingData[symbolIndex].Last_HH_Prices[1]);
Print("DEBUG: PDH: ", prevDayData[symbolIndex].highPrice);
Print("DEBUG: Condition: B >= PDH ✓");
}
}
else
{
if(EnableDebugPrints)
{
Print("DEBUG: Bearish pattern INVALID for ", symbol);
Print("DEBUG: B point (HH2): ", swingData[symbolIndex].Last_HH_Prices[1]);
Print("DEBUG: PDH: ", prevDayData[symbolIndex].highPrice);
Print("DEBUG: Condition failed: B < PDH");
}
}
}
}
// ============================================================
// ENHANCED VALIDATION FUNCTIONS
// ============================================================
//+------------------------------------------------------------------+
//| Validate Bullish B is Lowest Low between SSL and C |
//+------------------------------------------------------------------+
bool ValidateBullishBIsLowest(int symbolIndex, datetime triggerTime)
{
string symbol = Symbols[symbolIndex];
double bPrice = swingData[symbolIndex].Last_LL_Prices[1];
datetime sslTime = swingData[symbolIndex].Last_LL_Times[0];
// Get bar indices
int sslBar = iBarShift(symbol, PERIOD_M15, sslTime, true);
int triggerBar = iBarShift(symbol, PERIOD_M15, triggerTime, true);
if(sslBar < 0 || triggerBar < 0 || sslBar <= triggerBar)
{
if(EnableDebugPrints) Print("DEBUG: Cannot validate bullish B - invalid bar indices");
return false;
}
// Check all bars between SSL and C (inclusive)
for(int bar = sslBar; bar >= triggerBar; bar--)
{
double barLow = iLow(symbol, PERIOD_M15, bar);
if(barLow < bPrice - 0.00001) // Allow small floating point tolerance
{
if(EnableDebugPrints)
{
Print("DEBUG: Bullish B validation FAILED - found lower low at bar ", bar);
Print("DEBUG: B price: ", bPrice, " Lower low: ", barLow);
}
return false;
}
}
if(EnableDebugPrints)
{
Print("DEBUG: Bullish B validation PASSED - B is the lowest low between SSL and C");
}
return true;
}
//+------------------------------------------------------------------+
//| Validate Bullish A is Highest High between A and C+1 |
//+------------------------------------------------------------------+
bool ValidateBullishAIsHighest(int symbolIndex, datetime triggerTime)
{
string symbol = Symbols[symbolIndex];
double aPrice = swingData[symbolIndex].bullishSwingHigh;
datetime aTime = swingData[symbolIndex].bullishSwingHighTime;
// C+1 is the candle before trigger (higher bar index)
int triggerBar = iBarShift(symbol, PERIOD_M15, triggerTime, true);
int aBar = iBarShift(symbol, PERIOD_M15, aTime, true);
int cPlus1Bar = triggerBar + 1; // C+1 is one bar earlier
if(aBar < 0 || triggerBar < 0 || aBar <= cPlus1Bar)
{
if(EnableDebugPrints) Print("DEBUG: Cannot validate bullish A - invalid bar indices");
return false;
}
// Check all bars between A and C+1 (inclusive)
for(int bar = aBar; bar >= cPlus1Bar; bar--)
{
double barHigh = iHigh(symbol, PERIOD_M15, bar);
if(barHigh > aPrice + 0.00001) // Allow small floating point tolerance
{
if(EnableDebugPrints)
{
Print("DEBUG: Bullish A validation FAILED - found higher high at bar ", bar);
Print("DEBUG: A price: ", aPrice, " Higher high: ", barHigh);
}
return false;
}
}
if(EnableDebugPrints)
{
Print("DEBUG: Bullish A validation PASSED - A is the highest high between A and C+1");
}
return true;
}
//+------------------------------------------------------------------+
//| Validate Bearish B is Highest High between BSL and C |
//+------------------------------------------------------------------+
bool ValidateBearishBIsHighest(int symbolIndex, datetime triggerTime)
{
string symbol = Symbols[symbolIndex];
double bPrice = swingData[symbolIndex].Last_HH_Prices[1];
datetime bslTime = swingData[symbolIndex].Last_HH_Times[0];
// Get bar indices
int bslBar = iBarShift(symbol, PERIOD_M15, bslTime, true);
int triggerBar = iBarShift(symbol, PERIOD_M15, triggerTime, true);
if(bslBar < 0 || triggerBar < 0 || bslBar <= triggerBar)
{
if(EnableDebugPrints) Print("DEBUG: Cannot validate bearish B - invalid bar indices");
return false;
}
// Check all bars between BSL and C (inclusive)
for(int bar = bslBar; bar >= triggerBar; bar--)
{
double barHigh = iHigh(symbol, PERIOD_M15, bar);
if(barHigh > bPrice + 0.00001) // Allow small floating point tolerance
{
if(EnableDebugPrints)
{
Print("DEBUG: Bearish B validation FAILED - found higher high at bar ", bar);
Print("DEBUG: B price: ", bPrice, " Higher high: ", barHigh);
}
return false;
}
}
if(EnableDebugPrints)
{
Print("DEBUG: Bearish B validation PASSED - B is the highest high between BSL and C");
}
return true;
}
//+------------------------------------------------------------------+
//| Validate Bearish A is Lowest Low between A and C+1 |
//+------------------------------------------------------------------+
bool ValidateBearishAIsLowest(int symbolIndex, datetime triggerTime)
{
string symbol = Symbols[symbolIndex];
double aPrice = swingData[symbolIndex].bearishSwingLow;
datetime aTime = swingData[symbolIndex].bearishSwingLowTime;
// C+1 is the candle before trigger (higher bar index)
int triggerBar = iBarShift(symbol, PERIOD_M15, triggerTime, true);
int aBar = iBarShift(symbol, PERIOD_M15, aTime, true);
int cPlus1Bar = triggerBar + 1; // C+1 is one bar earlier
if(aBar < 0 || triggerBar < 0 || aBar <= cPlus1Bar)
{
if(EnableDebugPrints) Print("DEBUG: Cannot validate bearish A - invalid bar indices");
return false;
}
// Check all bars between A and C+1 (inclusive)
for(int bar = aBar; bar >= cPlus1Bar; bar--)
{
double barLow = iLow(symbol, PERIOD_M15, bar);
if(barLow < aPrice - 0.00001) // Allow small floating point tolerance
{
if(EnableDebugPrints)
{
Print("DEBUG: Bearish A validation FAILED - found lower low at bar ", bar);
Print("DEBUG: A price: ", aPrice, " Lower low: ", barLow);
}
return false;
}
}
if(EnableDebugPrints)
{
Print("DEBUG: Bearish A validation PASSED - A is the lowest low between A and C+1");
}
return true;
}
// ============================================================
// ALERT MANAGEMENT FUNCTIONS
// ============================================================
//+------------------------------------------------------------------+
//| Generate a unique identifier |
//+------------------------------------------------------------------+
string GenerateUniqueId()
{
int randomNum = (int)MathRand();
string uniqueId = IntegerToString(TimeCurrent()) + "_" + IntegerToString(randomNum) + "_" + IntegerToString(GetTickCount());
return uniqueId;
}
// ============================================================
// CHART OBJECT MANAGEMENT FUNCTIONS
// ============================================================
//+------------------------------------------------------------------+
//| Delete all chart objects |
//+------------------------------------------------------------------+
void DeleteAllObjects()
{
int totalObjects = ObjectsTotal(0, 0, -1);
bool objectsDeleted = false;
for(int i = totalObjects - 1; i >= 0; i--)
{
string objName = ObjectName(0, i, 0, -1);
if(StringFind(objName, "BullishTrend_") == 0 || StringFind(objName, "BearishTrend_") == 0 ||
StringFind(objName, "BSL_") == 0 || StringFind(objName, "SSL_") == 0 ||
StringFind(objName, "PDL_") == 0 || StringFind(objName, "PDH_") == 0 ||
StringFind(objName, "PDH") == 0 || StringFind(objName, "PDL") == 0 ||
StringFind(objName, "bearishSwingLow") == 0 || StringFind(objName, "bullishSwingHigh") == 0 ||
StringFind(objName, "H2") == 0 || StringFind(objName, "L2") == 0 ||
StringFind(objName, "triggerPoint") == 0)
{
ObjectDelete(0, objName);
objectsDeleted = true;
}
}
if(EnableDebugPrints && objectsDeleted)
{
Print("DEBUG: Deleted all chart objects. Total objects removed: ", totalObjects);
}
}
//+------------------------------------------------------------------+
//| Draw bullish BREAKOUT annotations (identical to original) |
//+------------------------------------------------------------------+
void DrawBullishBreakoutAnnotations(long chart_id, string symbol, int symbolIndex)
{
if(EnableDebugPrints) Print("DEBUG: Drawing bullish BREAKOUT annotations on chart ID: ", chart_id, " for ", symbol);
// USE STORED ALERT TIME - THIS NEVER CHANGES!
datetime timeNow = alertTrackers[symbolIndex].bullishAlertTime;
if(timeNow == 0)
{
Print("ERROR: Bullish alert time not set for ", symbol);
return;
}
// 1. Draw Previous Day Low (PDL) Trendline
string PDL_Linename = "PDL_Bullish_" + IntegerToString(alertTrackers[symbolIndex].bullishAlertTime);
ObjectCreate(chart_id, PDL_Linename, OBJ_TREND, 0,
prevDayData[symbolIndex].lowTime, prevDayData[symbolIndex].lowPrice,
swingData[symbolIndex].Last_LL_Times[0], prevDayData[symbolIndex].lowPrice);
ObjectSetInteger(chart_id, PDL_Linename, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(chart_id, PDL_Linename, OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(chart_id, PDL_Linename, OBJPROP_WIDTH, 3);
ObjectSetInteger(chart_id, PDL_Linename, OBJPROP_RAY_RIGHT, false);
ObjectSetString(chart_id, PDL_Linename, OBJPROP_TEXT, "PDL Trend");
// 2. Draw SSL (Swing Structure Low) Trendline
string SSL_Linename = "SSL_Bullish_" + IntegerToString(alertTrackers[symbolIndex].bullishAlertTime);
ObjectCreate(chart_id, SSL_Linename, OBJ_TREND, 0,
swingData[symbolIndex].Last_LL_Times[0], swingData[symbolIndex].Last_LL_Prices[0],
swingData[symbolIndex].Last_LL_Times[1], swingData[symbolIndex].Last_LL_Prices[0]);
ObjectSetInteger(chart_id, SSL_Linename, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(chart_id, SSL_Linename, OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(chart_id, SSL_Linename, OBJPROP_WIDTH, 1);
ObjectSetInteger(chart_id, SSL_Linename, OBJPROP_RAY_RIGHT, false);
ObjectSetString(chart_id, SSL_Linename, OBJPROP_TEXT, "SSL Trend");
// 3. Draw Bullish Swing High Trendline
string bullishSwingHigh_Linename = "BullishTrend_" + IntegerToString(alertTrackers[symbolIndex].bullishAlertTime);
ObjectCreate(chart_id, bullishSwingHigh_Linename, OBJ_TREND, 0,
swingData[symbolIndex].bullishSwingHighTime, swingData[symbolIndex].bullishSwingHigh,
alertTrackers[symbolIndex].bullishAlertTime, swingData[symbolIndex].bullishSwingHigh);
ObjectSetInteger(chart_id, bullishSwingHigh_Linename, OBJPROP_COLOR, clrGreen);
ObjectSetInteger(chart_id, bullishSwingHigh_Linename, OBJPROP_STYLE, STYLE_DASH);
ObjectSetInteger(chart_id, bullishSwingHigh_Linename, OBJPROP_WIDTH, 2);
ObjectSetInteger(chart_id, bullishSwingHigh_Linename, OBJPROP_RAY_RIGHT, false);
ObjectSetString(chart_id, bullishSwingHigh_Linename, OBJPROP_TEXT, "Bullish Swing High");
// 4. Text Labels
string pdlTextName = "PDL_Text_" + IntegerToString(alertTrackers[symbolIndex].bullishAlertTime);
ObjectCreate(chart_id, pdlTextName, OBJ_TEXT, 0, prevDayData[symbolIndex].lowTime, prevDayData[symbolIndex].lowPrice);
ObjectSetString(chart_id, pdlTextName, OBJPROP_TEXT, "PDL");
ObjectSetInteger(chart_id, pdlTextName, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(chart_id, pdlTextName, OBJPROP_FONTSIZE, 10);
ObjectSetInteger(chart_id, pdlTextName, OBJPROP_ANCHOR, ANCHOR_UPPER);
string swingHighTextName = "SwingHigh_Text_" + IntegerToString(alertTrackers[symbolIndex].bullishAlertTime);
ObjectCreate(chart_id, swingHighTextName, OBJ_TEXT, 0,
swingData[symbolIndex].bullishSwingHighTime, swingData[symbolIndex].bullishSwingHigh);
ObjectSetString(chart_id, swingHighTextName, OBJPROP_TEXT, "A");
ObjectSetInteger(chart_id, swingHighTextName, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(chart_id, swingHighTextName, OBJPROP_FONTSIZE, 10);
ObjectSetInteger(chart_id, swingHighTextName, OBJPROP_ANCHOR, ANCHOR_LOWER);
string l2TextName = "L2_Text_" + IntegerToString(alertTrackers[symbolIndex].bullishAlertTime);
ObjectCreate(chart_id, l2TextName, OBJ_TEXT, 0,
swingData[symbolIndex].Last_LL_Times[1], swingData[symbolIndex].Last_LL_Prices[1]);
ObjectSetString(chart_id, l2TextName, OBJPROP_TEXT, "B");
ObjectSetInteger(chart_id, l2TextName, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(chart_id, l2TextName, OBJPROP_FONTSIZE, 10);
ObjectSetInteger(chart_id, l2TextName, OBJPROP_ANCHOR, ANCHOR_UPPER);
string triggerTextName = "Trigger_Text_" + IntegerToString(alertTrackers[symbolIndex].bullishAlertTime);
ObjectCreate(chart_id, triggerTextName, OBJ_TEXT, 0, timeNow, swingData[symbolIndex].bullishSwingHigh);
ObjectSetString(chart_id, triggerTextName, OBJPROP_TEXT, "C");
ObjectSetInteger(chart_id, triggerTextName, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(chart_id, triggerTextName, OBJPROP_FONTSIZE, 10);
ObjectSetInteger(chart_id, triggerTextName, OBJPROP_ANCHOR, ANCHOR_LOWER);
//===================================================================================
// MODIFIED RECTANGLE - Bullish (40% from B to C, time fixed)
//===================================================================================
// Calculate the move from B price to C price
double bPrice = swingData[symbolIndex].Last_LL_Prices[1];
double cPrice = swingData[symbolIndex].bullishSwingHigh; // C is the swing high (breakout level)
// For bullish: B is lower (support), C is higher (resistance)
double moveDistance = cPrice - bPrice;
// Calculate 40% level (measured FROM B upward)
double rectangleTopPrice = bPrice + (moveDistance * 0.40);
if(EnableDebugPrints)
{
Print("DEBUG: Bullish Rectangle Calculation:");
Print("DEBUG: B Price: ", bPrice);
Print("DEBUG: C Price: ", cPrice);
Print("DEBUG: Move Distance: ", moveDistance);
Print("DEBUG: 40% Level: ", rectangleTopPrice, " (40% from B)");
Print("DEBUG: Rectangle from B (", swingData[symbolIndex].Last_LL_Prices[1], ") to ", rectangleTopPrice);
Print("DEBUG: C Time FIXED at: ", TimeToString(timeNow));
}
// Rectangle from B (bottom) to 40% level (top)
// Time coordinates are FIXED: from C time to C time + 5 periods (never changes)
string rectangleName = "Bullish_Rect_" + IntegerToString(alertTrackers[symbolIndex].bullishAlertTime);
ObjectCreate(chart_id, rectangleName, OBJ_RECTANGLE, 0,
timeNow, // Left time (C time - FIXED)
swingData[symbolIndex].Last_LL_Prices[1], // Bottom price (B)
timeNow + (5 * GetTimeframeSeconds()), // Right time (C time + 5 periods - FIXED)
rectangleTopPrice); // Top price (40% level)
ObjectSetInteger(chart_id, rectangleName, OBJPROP_COLOR, clrPaleGreen);
ObjectSetInteger(chart_id, rectangleName, OBJPROP_BGCOLOR, clrPaleGreen);
ObjectSetInteger(chart_id, rectangleName, OBJPROP_WIDTH, 1);
ObjectSetInteger(chart_id, rectangleName, OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(chart_id, rectangleName, OBJPROP_BACK, false);
ObjectSetInteger(chart_id, rectangleName, OBJPROP_FILL, true);
ObjectSetDouble(chart_id, rectangleName, OBJPROP_LEVELVALUE, 0.3);
//Arrowed line - using original coordinates with FIXED time
string arrowLineName = "Bullish_Arrow_" + IntegerToString(alertTrackers[symbolIndex].bullishAlertTime);
ObjectCreate(chart_id, arrowLineName, OBJ_ARROWED_LINE, 0,
timeNow, bPrice,
timeNow + (5 * GetTimeframeSeconds()), cPrice);
ObjectSetInteger(chart_id, arrowLineName, OBJPROP_COLOR, clrBlue);
ObjectSetInteger(chart_id, arrowLineName, OBJPROP_WIDTH, 1);
ObjectSetInteger(chart_id, arrowLineName, OBJPROP_RAY_RIGHT, false);
ObjectSetInteger(chart_id, arrowLineName, OBJPROP_STYLE, STYLE_DOT);
ObjectSetInteger(chart_id, arrowLineName, OBJPROP_BACK, false);
if(EnableDebugPrints) Print("DEBUG: Completed drawing bullish BREAKOUT annotations with FIXED C time: ", TimeToString(timeNow));
}
//+------------------------------------------------------------------+
//| Draw bullish RETRACEMENT annotations (IDENTICAL code, different name) |
//+------------------------------------------------------------------+
void DrawBullishRetracementAnnotations(long chart_id, string symbol, int symbolIndex)
{
if(EnableDebugPrints) Print("DEBUG: Drawing bullish RETRACEMENT annotations on chart ID: ", chart_id, " for ", symbol);
// USE STORED RETRACEMENT DATA
datetime timeNow = retraceTrackers[symbolIndex].bullishBreakoutTime; // Use breakout time for consistency
if(timeNow == 0)
{
Print("ERROR: Bullish retracement data not available for ", symbol);
return;
}
// 1. Draw Previous Day Low (PDL) Trendline
string PDL_Linename = "PDL_Bullish_Retrace_" + IntegerToString(timeNow);
ObjectCreate(chart_id, PDL_Linename, OBJ_TREND, 0,
retraceTrackers[symbolIndex].bullishPrevDayLowTime, retraceTrackers[symbolIndex].bullishPrevDayLow,
retraceTrackers[symbolIndex].bullishLast_LL_Times[0], retraceTrackers[symbolIndex].bullishPrevDayLow);
ObjectSetInteger(chart_id, PDL_Linename, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(chart_id, PDL_Linename, OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(chart_id, PDL_Linename, OBJPROP_WIDTH, 3);
ObjectSetInteger(chart_id, PDL_Linename, OBJPROP_RAY_RIGHT, false);
ObjectSetString(chart_id, PDL_Linename, OBJPROP_TEXT, "PDL Trend");
// 2. Draw SSL (Swing Structure Low) Trendline
string SSL_Linename = "SSL_Bullish_Retrace_" + IntegerToString(timeNow);
ObjectCreate(chart_id, SSL_Linename, OBJ_TREND, 0,
retraceTrackers[symbolIndex].bullishLast_LL_Times[0], retraceTrackers[symbolIndex].bullishLast_LL_Prices[0],
retraceTrackers[symbolIndex].bullishLast_LL_Times[1], retraceTrackers[symbolIndex].bullishLast_LL_Prices[0]);
ObjectSetInteger(chart_id, SSL_Linename, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(chart_id, SSL_Linename, OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(chart_id, SSL_Linename, OBJPROP_WIDTH, 1);
ObjectSetInteger(chart_id, SSL_Linename, OBJPROP_RAY_RIGHT, false);
ObjectSetString(chart_id, SSL_Linename, OBJPROP_TEXT, "SSL Trend");
// 3. Draw Bullish Swing High Trendline
string bullishSwingHigh_Linename = "BullishTrend_Retrace_" + IntegerToString(timeNow);
ObjectCreate(chart_id, bullishSwingHigh_Linename, OBJ_TREND, 0,
retraceTrackers[symbolIndex].bullishSwingHighTime, retraceTrackers[symbolIndex].bullishSwingHigh,
timeNow, retraceTrackers[symbolIndex].bullishSwingHigh);
ObjectSetInteger(chart_id, bullishSwingHigh_Linename, OBJPROP_COLOR, clrGreen);
ObjectSetInteger(chart_id, bullishSwingHigh_Linename, OBJPROP_STYLE, STYLE_DASH);
ObjectSetInteger(chart_id, bullishSwingHigh_Linename, OBJPROP_WIDTH, 2);
ObjectSetInteger(chart_id, bullishSwingHigh_Linename, OBJPROP_RAY_RIGHT, false);
ObjectSetString(chart_id, bullishSwingHigh_Linename, OBJPROP_TEXT, "Bullish Swing High");
// 4. Text Labels
string pdlTextName = "PDL_Text_Retrace_" + IntegerToString(timeNow);
ObjectCreate(chart_id, pdlTextName, OBJ_TEXT, 0, retraceTrackers[symbolIndex].bullishPrevDayLowTime, retraceTrackers[symbolIndex].bullishPrevDayLow);
ObjectSetString(chart_id, pdlTextName, OBJPROP_TEXT, "PDL");
ObjectSetInteger(chart_id, pdlTextName, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(chart_id, pdlTextName, OBJPROP_FONTSIZE, 10);
ObjectSetInteger(chart_id, pdlTextName, OBJPROP_ANCHOR, ANCHOR_UPPER);
string swingHighTextName = "SwingHigh_Text_Retrace_" + IntegerToString(timeNow);
ObjectCreate(chart_id, swingHighTextName, OBJ_TEXT, 0,
retraceTrackers[symbolIndex].bullishSwingHighTime, retraceTrackers[symbolIndex].bullishSwingHigh);
ObjectSetString(chart_id, swingHighTextName, OBJPROP_TEXT, "A");
ObjectSetInteger(chart_id, swingHighTextName, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(chart_id, swingHighTextName, OBJPROP_FONTSIZE, 10);
ObjectSetInteger(chart_id, swingHighTextName, OBJPROP_ANCHOR, ANCHOR_LOWER);
string l2TextName = "L2_Text_Retrace_" + IntegerToString(timeNow);
ObjectCreate(chart_id, l2TextName, OBJ_TEXT, 0,
retraceTrackers[symbolIndex].bullishLast_LL_Times[1], retraceTrackers[symbolIndex].bullishLast_LL_Prices[1]);
ObjectSetString(chart_id, l2TextName, OBJPROP_TEXT, "B");
ObjectSetInteger(chart_id, l2TextName, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(chart_id, l2TextName, OBJPROP_FONTSIZE, 10);
ObjectSetInteger(chart_id, l2TextName, OBJPROP_ANCHOR, ANCHOR_UPPER);
string triggerTextName = "Trigger_Text_Retrace_" + IntegerToString(timeNow);
ObjectCreate(chart_id, triggerTextName, OBJ_TEXT, 0, timeNow, retraceTrackers[symbolIndex].bullishSwingHigh);
ObjectSetString(chart_id, triggerTextName, OBJPROP_TEXT, "C");
ObjectSetInteger(chart_id, triggerTextName, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(chart_id, triggerTextName, OBJPROP_FONTSIZE, 10);
ObjectSetInteger(chart_id, triggerTextName, OBJPROP_ANCHOR, ANCHOR_LOWER);
//===================================================================================
// MODIFIED RECTANGLE - Bullish (40% from B to C, time fixed)
//===================================================================================
// Calculate the move from B price to C price
double bPrice = retraceTrackers[symbolIndex].bullishLast_LL_Prices[1];
double cPrice = retraceTrackers[symbolIndex].bullishSwingHigh;
double moveDistance = cPrice - bPrice;
double rectangleTopPrice = bPrice + (moveDistance * 0.40);
if(EnableDebugPrints)
{
Print("DEBUG: Bullish Retracement Rectangle Calculation:");
Print("DEBUG: B Price: ", bPrice);
Print("DEBUG: C Price: ", cPrice);
Print("DEBUG: Move Distance: ", moveDistance);
Print("DEBUG: 40% Level: ", rectangleTopPrice, " (40% from B)");
Print("DEBUG: Rectangle from B (", bPrice, ") to ", rectangleTopPrice);
Print("DEBUG: C Time FIXED at: ", TimeToString(timeNow));
}
// Rectangle from B (bottom) to 40% level (top)
string rectangleName = "Bullish_Rect_Retrace_" + IntegerToString(timeNow);
ObjectCreate(chart_id, rectangleName, OBJ_RECTANGLE, 0,
timeNow, // Left time (C time - FIXED)
bPrice, // Bottom price (B)
timeNow + (10 * GetTimeframeSeconds()), // Right time (C time + 10 periods - FIXED)
rectangleTopPrice); // Top price (40% level)
ObjectSetInteger(chart_id, rectangleName, OBJPROP_COLOR, clrPaleGreen);
ObjectSetInteger(chart_id, rectangleName, OBJPROP_BGCOLOR, clrPaleGreen);
ObjectSetInteger(chart_id, rectangleName, OBJPROP_WIDTH, 1);
ObjectSetInteger(chart_id, rectangleName, OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(chart_id, rectangleName, OBJPROP_BACK, false);
ObjectSetInteger(chart_id, rectangleName, OBJPROP_FILL, true);
ObjectSetDouble(chart_id, rectangleName, OBJPROP_LEVELVALUE, 0.3);
//Arrowed line
string arrowLineName = "Bullish_Arrow_Retrace_" + IntegerToString(timeNow);
ObjectCreate(chart_id, arrowLineName, OBJ_ARROWED_LINE, 0,
timeNow, bPrice,
timeNow + (10 * GetTimeframeSeconds()), cPrice);
ObjectSetInteger(chart_id, arrowLineName, OBJPROP_COLOR, clrBlue);
ObjectSetInteger(chart_id, arrowLineName, OBJPROP_WIDTH, 1);
ObjectSetInteger(chart_id, arrowLineName, OBJPROP_RAY_RIGHT, false);
ObjectSetInteger(chart_id, arrowLineName, OBJPROP_STYLE, STYLE_DOT);
ObjectSetInteger(chart_id, arrowLineName, OBJPROP_BACK, false);
if(EnableDebugPrints) Print("DEBUG: Completed drawing bullish RETRACEMENT annotations with FIXED C time: ", TimeToString(timeNow));
}
//+------------------------------------------------------------------+
//| Draw bearish BREAKOUT annotations (identical to original) |
//+------------------------------------------------------------------+
void DrawBearishBreakoutAnnotations(long chart_id, string symbol, int symbolIndex)
{
if(EnableDebugPrints) Print("DEBUG: Drawing bearish BREAKOUT annotations on chart ID: ", chart_id, " for ", symbol);
// USE STORED ALERT TIME - THIS NEVER CHANGES!
datetime timeNow = alertTrackers[symbolIndex].bearishAlertTime;
if(timeNow == 0)
{
Print("ERROR: Bearish alert time not set for ", symbol);
return;
}
// 1. Draw Previous Day High (PDH) Trendline
string PDH_Linename = "PDH_Bearish_" + IntegerToString(alertTrackers[symbolIndex].bearishAlertTime);
ObjectCreate(chart_id, PDH_Linename, OBJ_TREND, 0,
prevDayData[symbolIndex].highTime, prevDayData[symbolIndex].highPrice,
swingData[symbolIndex].Last_HH_Times[0], prevDayData[symbolIndex].highPrice);
ObjectSetInteger(chart_id, PDH_Linename, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(chart_id, PDH_Linename, OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(chart_id, PDH_Linename, OBJPROP_WIDTH, 3);
ObjectSetInteger(chart_id, PDH_Linename, OBJPROP_RAY_RIGHT, false);
ObjectSetString(chart_id, PDH_Linename, OBJPROP_TEXT, "PDH Trend");
// 2. Draw BSL (Bearish Structure Level) Trendline
string BSL_Linename = "BSL_Bearish_" + IntegerToString(alertTrackers[symbolIndex].bearishAlertTime);
ObjectCreate(chart_id, BSL_Linename, OBJ_TREND, 0,
swingData[symbolIndex].Last_HH_Times[0], swingData[symbolIndex].Last_HH_Prices[0],
swingData[symbolIndex].Last_HH_Times[1], swingData[symbolIndex].Last_HH_Prices[0]);
ObjectSetInteger(chart_id, BSL_Linename, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(chart_id, BSL_Linename, OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(chart_id, BSL_Linename, OBJPROP_WIDTH, 1);
ObjectSetInteger(chart_id, BSL_Linename, OBJPROP_RAY_RIGHT, false);
ObjectSetString(chart_id, BSL_Linename, OBJPROP_TEXT, "BSL Trend");
// 3. Draw Bearish Swing Low Trendline
string bearishSwingLow_Linename = "BearishTrend_" + IntegerToString(alertTrackers[symbolIndex].bearishAlertTime);
ObjectCreate(chart_id, bearishSwingLow_Linename, OBJ_TREND, 0,
swingData[symbolIndex].bearishSwingLowTime, swingData[symbolIndex].bearishSwingLow,
alertTrackers[symbolIndex].bearishAlertTime, swingData[symbolIndex].bearishSwingLow);
ObjectSetInteger(chart_id, bearishSwingLow_Linename, OBJPROP_COLOR, clrRed);
ObjectSetInteger(chart_id, bearishSwingLow_Linename, OBJPROP_STYLE, STYLE_DASH);
ObjectSetInteger(chart_id, bearishSwingLow_Linename, OBJPROP_WIDTH, 2);
ObjectSetInteger(chart_id, bearishSwingLow_Linename, OBJPROP_RAY_RIGHT, false);
ObjectSetString(chart_id, bearishSwingLow_Linename, OBJPROP_TEXT, "Bearish Swing Low");
// 4. Text Labels
string pdhTextName = "PDH_Text_" + IntegerToString(alertTrackers[symbolIndex].bearishAlertTime);
ObjectCreate(chart_id, pdhTextName, OBJ_TEXT, 0, prevDayData[symbolIndex].highTime, prevDayData[symbolIndex].highPrice);
ObjectSetString(chart_id, pdhTextName, OBJPROP_TEXT, "PDH");
ObjectSetInteger(chart_id, pdhTextName, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(chart_id, pdhTextName, OBJPROP_FONTSIZE, 10);
ObjectSetInteger(chart_id, pdhTextName, OBJPROP_ANCHOR, ANCHOR_LOWER);
string swingLowTextName = "SwingLow_Text_" + IntegerToString(alertTrackers[symbolIndex].bearishAlertTime);
ObjectCreate(chart_id, swingLowTextName, OBJ_TEXT, 0,
swingData[symbolIndex].bearishSwingLowTime, swingData[symbolIndex].bearishSwingLow);
ObjectSetString(chart_id, swingLowTextName, OBJPROP_TEXT, "A");
ObjectSetInteger(chart_id, swingLowTextName, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(chart_id, swingLowTextName, OBJPROP_FONTSIZE, 10);
ObjectSetInteger(chart_id, swingLowTextName, OBJPROP_ANCHOR, ANCHOR_UPPER);
string h2TextName = "H2_Text_" + IntegerToString(alertTrackers[symbolIndex].bearishAlertTime);
ObjectCreate(chart_id, h2TextName, OBJ_TEXT, 0,
swingData[symbolIndex].Last_HH_Times[1], swingData[symbolIndex].Last_HH_Prices[1]);
ObjectSetString(chart_id, h2TextName, OBJPROP_TEXT, "B");
ObjectSetInteger(chart_id, h2TextName, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(chart_id, h2TextName, OBJPROP_FONTSIZE, 10);
ObjectSetInteger(chart_id, h2TextName, OBJPROP_ANCHOR, ANCHOR_LOWER);
string triggerTextName = "Trigger_Text_" + IntegerToString(alertTrackers[symbolIndex].bearishAlertTime);
ObjectCreate(chart_id, triggerTextName, OBJ_TEXT, 0, timeNow, swingData[symbolIndex].bearishSwingLow);
ObjectSetString(chart_id, triggerTextName, OBJPROP_TEXT, "C");
ObjectSetInteger(chart_id, triggerTextName, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(chart_id, triggerTextName, OBJPROP_FONTSIZE, 10);
ObjectSetInteger(chart_id, triggerTextName, OBJPROP_ANCHOR, ANCHOR_UPPER);
//===================================================================================
// MODIFIED RECTANGLE - Bearish (40% from B to C, time fixed)
//===================================================================================
// Calculate the move from B price to C price
double bPrice = swingData[symbolIndex].Last_HH_Prices[1];
double cPrice = swingData[symbolIndex].bearishSwingLow; // C is the swing low (breakout level)
// For bearish: B is higher (resistance), C is lower (support)
double moveDistance = bPrice - cPrice;
// Calculate 40% level (measured FROM B downward)
double rectangleBottomPrice = bPrice - (moveDistance * 0.40);
if(EnableDebugPrints)
{
Print("DEBUG: Bearish Rectangle Calculation:");
Print("DEBUG: B Price: ", bPrice);
Print("DEBUG: C Price: ", cPrice);
Print("DEBUG: Move Distance: ", moveDistance);
Print("DEBUG: 40% Level: ", rectangleBottomPrice, " (40% from B)");
Print("DEBUG: Rectangle from ", rectangleBottomPrice, " to B (", swingData[symbolIndex].Last_HH_Prices[1], ")");
Print("DEBUG: C Time FIXED at: ", TimeToString(timeNow));
}
// Rectangle from 40% level (bottom) to B (top)
string rectangleName = "Bearish_Rect_" + IntegerToString(alertTrackers[symbolIndex].bearishAlertTime);
ObjectCreate(chart_id, rectangleName, OBJ_RECTANGLE, 0,
timeNow, // Left time (C time - FIXED)
rectangleBottomPrice, // Bottom price (40% level)
timeNow + (5 * GetTimeframeSeconds()), // Right time (C time + 5 periods - FIXED)
swingData[symbolIndex].Last_HH_Prices[1]); // Top price (B)
ObjectSetInteger(chart_id, rectangleName, OBJPROP_COLOR, clrLightPink);
ObjectSetInteger(chart_id, rectangleName, OBJPROP_BGCOLOR, clrLightPink);
ObjectSetInteger(chart_id, rectangleName, OBJPROP_WIDTH, 1);
ObjectSetInteger(chart_id, rectangleName, OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(chart_id, rectangleName, OBJPROP_BACK, false);
ObjectSetInteger(chart_id, rectangleName, OBJPROP_FILL, true);
ObjectSetDouble(chart_id, rectangleName, OBJPROP_LEVELVALUE, 0.3);
//Arrowed line
string arrowLineName = "Bearish_Arrow_" + IntegerToString(alertTrackers[symbolIndex].bearishAlertTime);
ObjectCreate(chart_id, arrowLineName, OBJ_ARROWED_LINE, 0,
timeNow, bPrice,
timeNow + (5 * GetTimeframeSeconds()), cPrice);
ObjectSetInteger(chart_id, arrowLineName, OBJPROP_COLOR, clrBlue);
ObjectSetInteger(chart_id, arrowLineName, OBJPROP_WIDTH, 1);
ObjectSetInteger(chart_id, arrowLineName, OBJPROP_RAY_RIGHT, false);
ObjectSetInteger(chart_id, arrowLineName, OBJPROP_STYLE, STYLE_DOT);
ObjectSetInteger(chart_id, arrowLineName, OBJPROP_BACK, false);
if(EnableDebugPrints) Print("DEBUG: Completed drawing bearish BREAKOUT annotations with FIXED C time: ", TimeToString(timeNow));
}
//+------------------------------------------------------------------+
//| Draw bearish RETRACEMENT annotations (IDENTICAL code, different name) |
//+------------------------------------------------------------------+
void DrawBearishRetracementAnnotations(long chart_id, string symbol, int symbolIndex)
{
if(EnableDebugPrints) Print("DEBUG: Drawing bearish RETRACEMENT annotations on chart ID: ", chart_id, " for ", symbol);
// USE STORED RETRACEMENT DATA
datetime timeNow = retraceTrackers[symbolIndex].bearishBreakoutTime; // Use breakout time for consistency
if(timeNow == 0)
{
Print("ERROR: Bearish retracement data not available for ", symbol);
return;
}
// 1. Draw Previous Day High (PDH) Trendline
string PDH_Linename = "PDH_Bearish_Retrace_" + IntegerToString(timeNow);
ObjectCreate(chart_id, PDH_Linename, OBJ_TREND, 0,
retraceTrackers[symbolIndex].bearishPrevDayHighTime, retraceTrackers[symbolIndex].bearishPrevDayHigh,
retraceTrackers[symbolIndex].bearishLast_HH_Times[0], retraceTrackers[symbolIndex].bearishPrevDayHigh);
ObjectSetInteger(chart_id, PDH_Linename, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(chart_id, PDH_Linename, OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(chart_id, PDH_Linename, OBJPROP_WIDTH, 3);
ObjectSetInteger(chart_id, PDH_Linename, OBJPROP_RAY_RIGHT, false);
ObjectSetString(chart_id, PDH_Linename, OBJPROP_TEXT, "PDH Trend");
// 2. Draw BSL (Bearish Structure Level) Trendline
string BSL_Linename = "BSL_Bearish_Retrace_" + IntegerToString(timeNow);
ObjectCreate(chart_id, BSL_Linename, OBJ_TREND, 0,
retraceTrackers[symbolIndex].bearishLast_HH_Times[0], retraceTrackers[symbolIndex].bearishLast_HH_Prices[0],
retraceTrackers[symbolIndex].bearishLast_HH_Times[1], retraceTrackers[symbolIndex].bearishLast_HH_Prices[0]);
ObjectSetInteger(chart_id, BSL_Linename, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(chart_id, BSL_Linename, OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(chart_id, BSL_Linename, OBJPROP_WIDTH, 1);
ObjectSetInteger(chart_id, BSL_Linename, OBJPROP_RAY_RIGHT, false);
ObjectSetString(chart_id, BSL_Linename, OBJPROP_TEXT, "BSL Trend");
// 3. Draw Bearish Swing Low Trendline
string bearishSwingLow_Linename = "BearishTrend_Retrace_" + IntegerToString(timeNow);
ObjectCreate(chart_id, bearishSwingLow_Linename, OBJ_TREND, 0,
retraceTrackers[symbolIndex].bearishSwingLowTime, retraceTrackers[symbolIndex].bearishSwingLow,
timeNow, retraceTrackers[symbolIndex].bearishSwingLow);
ObjectSetInteger(chart_id, bearishSwingLow_Linename, OBJPROP_COLOR, clrRed);
ObjectSetInteger(chart_id, bearishSwingLow_Linename, OBJPROP_STYLE, STYLE_DASH);
ObjectSetInteger(chart_id, bearishSwingLow_Linename, OBJPROP_WIDTH, 2);
ObjectSetInteger(chart_id, bearishSwingLow_Linename, OBJPROP_RAY_RIGHT, false);
ObjectSetString(chart_id, bearishSwingLow_Linename, OBJPROP_TEXT, "Bearish Swing Low");
// 4. Text Labels
string pdhTextName = "PDH_Text_Retrace_" + IntegerToString(timeNow);
ObjectCreate(chart_id, pdhTextName, OBJ_TEXT, 0, retraceTrackers[symbolIndex].bearishPrevDayHighTime, retraceTrackers[symbolIndex].bearishPrevDayHigh);
ObjectSetString(chart_id, pdhTextName, OBJPROP_TEXT, "PDH");
ObjectSetInteger(chart_id, pdhTextName, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(chart_id, pdhTextName, OBJPROP_FONTSIZE, 10);
ObjectSetInteger(chart_id, pdhTextName, OBJPROP_ANCHOR, ANCHOR_LOWER);
string swingLowTextName = "SwingLow_Text_Retrace_" + IntegerToString(timeNow);
ObjectCreate(chart_id, swingLowTextName, OBJ_TEXT, 0,
retraceTrackers[symbolIndex].bearishSwingLowTime, retraceTrackers[symbolIndex].bearishSwingLow);
ObjectSetString(chart_id, swingLowTextName, OBJPROP_TEXT, "A");
ObjectSetInteger(chart_id, swingLowTextName, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(chart_id, swingLowTextName, OBJPROP_FONTSIZE, 10);
ObjectSetInteger(chart_id, swingLowTextName, OBJPROP_ANCHOR, ANCHOR_UPPER);
string h2TextName = "H2_Text_Retrace_" + IntegerToString(timeNow);
ObjectCreate(chart_id, h2TextName, OBJ_TEXT, 0,
retraceTrackers[symbolIndex].bearishLast_HH_Times[1], retraceTrackers[symbolIndex].bearishLast_HH_Prices[1]);
ObjectSetString(chart_id, h2TextName, OBJPROP_TEXT, "B");
ObjectSetInteger(chart_id, h2TextName, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(chart_id, h2TextName, OBJPROP_FONTSIZE, 10);
ObjectSetInteger(chart_id, h2TextName, OBJPROP_ANCHOR, ANCHOR_LOWER);
string triggerTextName = "Trigger_Text_Retrace_" + IntegerToString(timeNow);
ObjectCreate(chart_id, triggerTextName, OBJ_TEXT, 0, timeNow, retraceTrackers[symbolIndex].bearishSwingLow);
ObjectSetString(chart_id, triggerTextName, OBJPROP_TEXT, "C");
ObjectSetInteger(chart_id, triggerTextName, OBJPROP_COLOR, clrBlack);
ObjectSetInteger(chart_id, triggerTextName, OBJPROP_FONTSIZE, 10);
ObjectSetInteger(chart_id, triggerTextName, OBJPROP_ANCHOR, ANCHOR_UPPER);
//===================================================================================
// MODIFIED RECTANGLE - Bearish (40% from B to C, time fixed)
//===================================================================================
// Calculate the move from B price to C price
double bPrice = retraceTrackers[symbolIndex].bearishLast_HH_Prices[1];
double cPrice = retraceTrackers[symbolIndex].bearishSwingLow;
double moveDistance = bPrice - cPrice;
double rectangleBottomPrice = bPrice - (moveDistance * 0.40);
if(EnableDebugPrints)
{
Print("DEBUG: Bearish Retracement Rectangle Calculation:");
Print("DEBUG: B Price: ", bPrice);
Print("DEBUG: C Price: ", cPrice);
Print("DEBUG: Move Distance: ", moveDistance);
Print("DEBUG: 40% Level: ", rectangleBottomPrice, " (40% from B)");
Print("DEBUG: Rectangle from ", rectangleBottomPrice, " to B (", bPrice, ")");
Print("DEBUG: C Time FIXED at: ", TimeToString(timeNow));
}
// Rectangle from 40% level (bottom) to B (top)
string rectangleName = "Bearish_Rect_Retrace_" + IntegerToString(timeNow);
ObjectCreate(chart_id, rectangleName, OBJ_RECTANGLE, 0,
timeNow, // Left time (C time - FIXED)
rectangleBottomPrice, // Bottom price (40% level)
timeNow + (10 * GetTimeframeSeconds()), // Right time (C time + 10 periods - FIXED)
bPrice); // Top price (B)
ObjectSetInteger(chart_id, rectangleName, OBJPROP_COLOR, clrLightPink);
ObjectSetInteger(chart_id, rectangleName, OBJPROP_BGCOLOR, clrLightPink);
ObjectSetInteger(chart_id, rectangleName, OBJPROP_WIDTH, 1);
ObjectSetInteger(chart_id, rectangleName, OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(chart_id, rectangleName, OBJPROP_BACK, false);
ObjectSetInteger(chart_id, rectangleName, OBJPROP_FILL, true);
ObjectSetDouble(chart_id, rectangleName, OBJPROP_LEVELVALUE, 0.3);
//Arrowed line
string arrowLineName = "Bearish_Arrow_Retrace_" + IntegerToString(timeNow);
ObjectCreate(chart_id, arrowLineName, OBJ_ARROWED_LINE, 0,
timeNow, bPrice,
timeNow + (10 * GetTimeframeSeconds()), cPrice);
ObjectSetInteger(chart_id, arrowLineName, OBJPROP_COLOR, clrBlue);
ObjectSetInteger(chart_id, arrowLineName, OBJPROP_WIDTH, 1);
ObjectSetInteger(chart_id, arrowLineName, OBJPROP_RAY_RIGHT, false);
ObjectSetInteger(chart_id, arrowLineName, OBJPROP_STYLE, STYLE_DOT);
ObjectSetInteger(chart_id, arrowLineName, OBJPROP_BACK, false);
if(EnableDebugPrints) Print("DEBUG: Completed drawing bearish RETRACEMENT annotations with FIXED C time: ", TimeToString(timeNow));
}
//+------------------------------------------------------------------+
//| Take screenshot |
//+------------------------------------------------------------------+
string TakeScreenshot(string alertType, string symbol, int symbolIndex)
{
string filename = alertType + "_" + symbol + "_" + IntegerToString(TimeCurrent()) + ".png";
long chart_id = 0;
/*
ChartOpen(symbol, _Period);
if(chart_id <= 0)
{
Print("ERROR: Failed to open chart for ", symbol);
return "";
}
*/
Sleep(500);
tradeviewtpl(chart_id);
Sleep(200);
// Call the appropriate annotation function based on alert type
if(alertType == "Bullish_Breakout")
{
DrawBullishBreakoutAnnotations(chart_id, symbol, symbolIndex);
}
else if(alertType == "Bullish_Retracement")
{
DrawBullishRetracementAnnotations(chart_id, symbol, symbolIndex);
}
else if(alertType == "Bearish_Breakout")
{
DrawBearishBreakoutAnnotations(chart_id, symbol, symbolIndex);
}
else if(alertType == "Bearish_Retracement")
{
DrawBearishRetracementAnnotations(chart_id, symbol, symbolIndex);
}
DrawWatermark(chart_id);
Sleep(300);
if(EnableDebugPrints)
{
Print("DEBUG: Attempting to capture screenshot for ", alertType, " alert on chart ID: ", chart_id);
Print("DEBUG: Screenshot filename: ", filename);
}
if(ChartScreenShot(chart_id, filename, 1350, 1080, ALIGN_RIGHT))
{
if(CloseChartAfterScreenshot)
{
ChartClose(chart_id);
}
if(EnableDebugPrints)
{
Print("DEBUG: Screenshot captured successfully: ", filename);
Print("DEBUG: Saved to: ", TerminalInfoString(TERMINAL_DATA_PATH) + "\\MQL5\\Files\\" + filename);
if(CloseChartAfterScreenshot) Print("DEBUG: Temporary chart closed");
}
return filename;
}
else
{
if(EnableDebugPrints)
{
Print("DEBUG: ERROR - Screenshot capture failed. Last error: ", GetLastError());
}
ChartClose(chart_id);
return "";
}
}
//+------------------------------------------------------------------+
//| Draw watermark |
//+------------------------------------------------------------------+
string WM_NAME = "TV_STYLE_WATERMARK";
bool DrawWatermark(long chartid)
{
string tf = GetTimeframeString();
string symbol = ChartSymbol(chartid);
string text = symbol + " " + tf;
if(ObjectFind(chartid, WM_NAME) == -1)
{
ObjectCreate(chartid, WM_NAME, OBJ_LABEL, 0, 0, 0);
ObjectSetString(chartid, WM_NAME, OBJPROP_TEXT, text);
ObjectSetInteger(chartid, WM_NAME, OBJPROP_COLOR, clrSilver);
ObjectSetInteger(chartid, WM_NAME, OBJPROP_FONTSIZE, 50);
ObjectSetString(chartid, WM_NAME, OBJPROP_FONT, "Arial Black");
ObjectSetInteger(chartid, WM_NAME, OBJPROP_BACK, true);
ObjectSetInteger(chartid, WM_NAME, OBJPROP_SELECTABLE, false);
ObjectSetInteger(chartid, WM_NAME, OBJPROP_HIDDEN, true);
// Calculate center position
int center_x = 1350 / 2;
int center_y = 1080 / 2;
ObjectSetInteger(chartid, WM_NAME, OBJPROP_XDISTANCE, center_x);
ObjectSetInteger(chartid, WM_NAME, OBJPROP_YDISTANCE, center_y);
ObjectSetInteger(chartid, WM_NAME, OBJPROP_ANCHOR, ANCHOR_CENTER);
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| Apply TradingView style template |
//+------------------------------------------------------------------+
void tradeviewtpl(long chartid)
{
ChartSetInteger(chartid, CHART_COLOR_BACKGROUND, C'242,242,242');
ChartSetInteger(chartid, CHART_COLOR_FOREGROUND, C'19,23,34');
ChartSetInteger(chartid, CHART_COLOR_GRID, clrNONE);
ChartSetInteger(chartid, CHART_COLOR_CHART_UP, C'38,166,154');
ChartSetInteger(chartid, CHART_COLOR_CHART_DOWN, C'239,83,80');
ChartSetInteger(chartid, CHART_COLOR_CANDLE_BULL, C'38,166,154');
ChartSetInteger(chartid, CHART_COLOR_CANDLE_BEAR, C'239,83,80');
ChartSetInteger(chartid, CHART_COLOR_CHART_LINE, C'38,166,154');
ChartSetInteger(chartid, CHART_SHOW_VOLUMES, false);
ChartSetInteger(chartid, CHART_SHIFT, true);
ChartSetInteger(chartid, CHART_SCALE, 3);
}
//+------------------------------------------------------------------+
//| Send alert data to server |
//+------------------------------------------------------------------+
void SendAlertData(string alertType, double priceLevel, datetime swingTime, datetime alertTime,
string screenshotFilename, string uniqueId, string symbol, int symbolIndex)
{
//Ensures screenshot if fully saved
Sleep(5000);
if(EnableDebugPrints)
{
Print("DEBUG: Preparing to send alert data to API for ", symbol);
Print("DEBUG: Alert Type: ", alertType);
Print("DEBUG: Price Level: ", priceLevel);
Print("DEBUG: Swing Time: ", TimeToString(swingTime));
Print("DEBUG: Alert Time: ", TimeToString(alertTime));
Print("DEBUG: Unique ID: ", uniqueId);
}
string symbolSector = GetCustomSector(symbol);
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
string jsonPayload = "{";
jsonPayload += "\"symbol\":\"" + symbol + "\",";
jsonPayload += "\"sector\":\"" + symbolSector + "\",";
jsonPayload += "\"timeframe\":\"" + GetTimeframeString() + "\",";
jsonPayload += "\"alert_type\":\"" + alertType + "\",";
jsonPayload += "\"price_level\":" + DoubleToString(priceLevel, digits) + ",";
jsonPayload += "\"swing_time\":" + IntegerToString(swingTime) + ",";
jsonPayload += "\"alert_time\":" + IntegerToString(alertTime) + ",";
jsonPayload += "\"bias\":\"" + BiasEnumToString(biasData[symbolIndex].bias) + "\",";
jsonPayload += "\"session\":\"" + currentSession + "\",";
jsonPayload += "\"strategy_name\":\"" + strategyName + "\",";
jsonPayload += "\"strategy_id\":\"" + strategyId + "\",";
jsonPayload += "\"unique_id\":\"" + uniqueId + "\",";
jsonPayload += "\"screenshot_filename\":\"" + screenshotFilename + "\",";
jsonPayload += "\"allowed_timeframes\":[";
int tfCount = ArraySize(allowedTimeframe);
for(int i = 0; i < tfCount; i++)
{
jsonPayload += "\"" + allowedTimeframe[i] + "\"";
if(i < tfCount - 1)
jsonPayload += ",";
}
jsonPayload += "],";
jsonPayload += "\"current_bid\":" + DoubleToString(swingData[symbolIndex].currentBidPrice, digits) + ",";
jsonPayload += "\"current_ask\":" + DoubleToString(swingData[symbolIndex].currentAskPrice, digits);
jsonPayload += "}";
if(EnableDebugPrints)
{
Print("DEBUG: JSON Payload:");
Print(jsonPayload);
}
string headers = "Content-Type: application/json\r\n";
uchar dataArray[];
StringToCharArray(jsonPayload, dataArray, 0, StringLen(jsonPayload));
uchar result[];
string resultHeaders;
int response = WebRequest("POST", alertServerURL, headers, 10000, dataArray, result, resultHeaders);
if(EnableDebugPrints)
{
Print("DEBUG: Alert data sent. Response: ", response);
if(response == 200)
{
Print("DEBUG: SUCCESS - Alert data sent | Unique ID: ", uniqueId);
}
else if(response == -1)
{
Print("DEBUG: ERROR - WebRequest failed. Error: ", GetLastError());
}
else
{
Print("DEBUG: Server response: ", response);
}
}
}
//+------------------------------------------------------------------+
//| Convert bias enum to string |
//+------------------------------------------------------------------+
string BiasEnumToString(ENUM_BIAS bias)
{
switch(bias)
{
case BIAS_NEUTRAL: return "NEUTRAL";
case BIAS_BULLISH: return "BULLISH";
case BIAS_BEARISH: return "BEARISH";
default: return "UNKNOWN";
}
}
//+------------------------------------------------------------------+
//| Send screenshot binary data |
//+------------------------------------------------------------------+
void SendScreenshotBinary(string filename, string alertType, string uniqueId, string symbol, int symbolIndex)
{
if(EnableDebugPrints)
{
Print("DEBUG: Preparing to send screenshot for ", symbol);
Print("DEBUG: File: ", filename);
Print("DEBUG: Unique ID: ", uniqueId);
}
int filehandle = FileOpen(filename, FILE_READ | FILE_BIN);
if(filehandle == INVALID_HANDLE)
{
if(EnableDebugPrints)
{
Print("DEBUG: ERROR - Could not open file: ", filename);
Print("DEBUG: Last error: ", GetLastError());
}
return;
}
int fileSize = (int)FileSize(filehandle);
uchar byteArray[];
ArrayResize(byteArray, fileSize);
FileReadArray(filehandle, byteArray, 0, fileSize);
FileClose(filehandle);
string boundary = "----WebKitFormBoundary" + IntegerToString(TimeCurrent());
string headers = "Content-Type: multipart/form-data; boundary=" + boundary + "\r\n";
string formData = "--" + boundary + "\r\n";
formData += "Content-Disposition: form-data; name=\"alert_type\"\r\n\r\n";
formData += alertType + "\r\n";
formData += "--" + boundary + "\r\n";
formData += "Content-Disposition: form-data; name=\"symbol\"\r\n\r\n";
formData += symbol + "\r\n";
formData += "--" + boundary + "\r\n";
formData += "Content-Disposition: form-data; name=\"timestamp\"\r\n\r\n";
formData += IntegerToString(TimeCurrent()) + "\r\n";
formData += "--" + boundary + "\r\n";
formData += "Content-Disposition: form-data; name=\"strategy_name\"\r\n\r\n";
formData += strategyName + "\r\n";
formData += "--" + boundary + "\r\n";
formData += "Content-Disposition: form-data; name=\"strategy_id\"\r\n\r\n";
formData += strategyId + "\r\n";
formData += "--" + boundary + "\r\n";
formData += "Content-Disposition: form-data; name=\"sector\"\r\n\r\n";
formData += GetCustomSector(symbol) + "\r\n";
formData += "--" + boundary + "\r\n";
formData += "Content-Disposition: form-data; name=\"timeframe\"\r\n\r\n";
formData += GetTimeframeString() + "\r\n";
formData += "--" + boundary + "\r\n";
formData += "Content-Disposition: form-data; name=\"session\"\r\n\r\n";
formData += currentSession + "\r\n";
formData += "--" + boundary + "\r\n";
formData += "Content-Disposition: form-data; name=\"bias\"\r\n\r\n";
formData += BiasEnumToString(biasData[symbolIndex].bias) + "\r\n";
formData += "--" + boundary + "\r\n";
formData += "Content-Disposition: form-data; name=\"unique_id\"\r\n\r\n";
formData += uniqueId + "\r\n";
formData += "--" + boundary + "\r\n";
formData += "Content-Disposition: form-data; name=\"screenshot_filename\"\r\n\r\n";
formData += filename + "\r\n";
formData += "--" + boundary + "\r\n";
formData += "Content-Disposition: form-data; name=\"allowed_timeframes\"\r\n\r\n";
string allowedTFsStr = "";
int tfCount = ArraySize(allowedTimeframe);
for(int i = 0; i < tfCount; i++)
{
allowedTFsStr += allowedTimeframe[i];
if(i < tfCount - 1)
allowedTFsStr += ",";
}
formData += allowedTFsStr + "\r\n";
formData += "--" + boundary + "\r\n";
formData += "Content-Disposition: form-data; name=\"screenshot\"; filename=\"" + filename + "\"\r\n";
formData += "Content-Type: image/png\r\n\r\n";
uchar headerArray[];
StringToCharArray(formData, headerArray, 0, StringLen(formData));
string footer = "\r\n--" + boundary + "--\r\n";
uchar footerArray[];
StringToCharArray(footer, footerArray, 0, StringLen(footer));
uchar postData[];
int totalSize = ArraySize(headerArray) + ArraySize(byteArray) + ArraySize(footerArray);
ArrayResize(postData, totalSize);
for(int i = 0; i < ArraySize(headerArray); i++)
postData[i] = headerArray[i];
int offset = ArraySize(headerArray);
for(int i = 0; i < ArraySize(byteArray); i++)
postData[offset + i] = byteArray[i];
offset += ArraySize(byteArray);
for(int i = 0; i < ArraySize(footerArray); i++)
postData[offset + i] = footerArray[i];
if(EnableDebugPrints)
{
Print("DEBUG: Sending multipart data, size: ", totalSize, " bytes");
}
uchar result[];
string resultHeaders;
int response = WebRequest("POST", screenshotServerURL, headers, 15000, postData, result, resultHeaders);
if(EnableDebugPrints)
{
Print("DEBUG: Screenshot sent. Response: ", response);
if(response == 200)
{
Print("DEBUG: SUCCESS - Screenshot sent | Unique ID: ", uniqueId);
}
else if(response == 400)
{
Print("DEBUG: ERROR - Bad Request (400)");
}
else if(response == -1)
{
Print("DEBUG: ERROR - WebRequest failed. Error: ", GetLastError());
}
}
}
// ============================================================
// SWING ANALYSIS FUNCTIONS
// ============================================================
//+------------------------------------------------------------------+
//| Find swing points |
//+------------------------------------------------------------------+
void FindSwingPoints(int symbolIndex)
{
string symbol = Symbols[symbolIndex];
int symbol_digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
int startBar = SwingLookbackBars + SwingLeftBars;
for(int i = startBar; i >= SwingRightBars; i--)
{
double currentLow = iLow(symbol, PERIOD_CURRENT, i);
double currentHigh = iHigh(symbol, PERIOD_CURRENT, i);
bool isSwingLow = true;
bool isSwingHigh = true;
// Check swing low left side
for(int left = 1; left <= SwingLeftBars; left++)
{
double leftLow = iLow(symbol, PERIOD_CURRENT, i + left);
if(currentLow >= leftLow)
{
isSwingLow = false;
break;
}
}
// Check swing low right side
if(isSwingLow)
{
for(int right = 1; right <= SwingRightBars; right++)
{
double rightLow = iLow(symbol, PERIOD_CURRENT, i - right);
if(currentLow >= rightLow)
{
isSwingLow = false;
break;
}
}
}
// Check swing high left side
for(int left = 1; left <= SwingLeftBars; left++)
{
double leftHigh = iHigh(symbol, PERIOD_CURRENT, i + left);
if(currentHigh <= leftHigh)
{
isSwingHigh = false;
break;
}
}
// Check swing high right side
if(isSwingHigh)
{
for(int right = 1; right <= SwingRightBars; right++)
{
double rightHigh = iHigh(symbol, PERIOD_CURRENT, i - right);
if(currentHigh <= rightHigh)
{
isSwingHigh = false;
break;
}
}
}
// Add swing low
if(isSwingLow)
{
double swingLowPrice = NormalizeDouble(currentLow, symbol_digits);
datetime swingLowTime = iTime(symbol, PERIOD_CURRENT, i);
int size = ArraySize(swingData[symbolIndex].swingLowPrices);
ArrayResize(swingData[symbolIndex].swingLowPrices, size + 1);
ArrayResize(swingData[symbolIndex].swingLowTimes, size + 1);
swingData[symbolIndex].swingLowPrices[size] = swingLowPrice;
swingData[symbolIndex].swingLowTimes[size] = swingLowTime;
}
// Add swing high
if(isSwingHigh)
{
double swingHighPrice = NormalizeDouble(currentHigh, symbol_digits);
datetime swingHighTime = iTime(symbol, PERIOD_CURRENT, i);
int size = ArraySize(swingData[symbolIndex].swingHighPrices);
ArrayResize(swingData[symbolIndex].swingHighPrices, size + 1);
ArrayResize(swingData[symbolIndex].swingHighTimes, size + 1);
swingData[symbolIndex].swingHighPrices[size] = swingHighPrice;
swingData[symbolIndex].swingHighTimes[size] = swingHighTime;
}
}
}
//+------------------------------------------------------------------+
//| Get consecutive highs and lows |
//+------------------------------------------------------------------+
void GetConsecutiveHighsAndLows(int symbolIndex)
{
ArrayInitialize(swingData[symbolIndex].HH_Prices, 0);
ArrayInitialize(swingData[symbolIndex].HH_Times, 0);
ArrayInitialize(swingData[symbolIndex].LL_Prices, 0);
ArrayInitialize(swingData[symbolIndex].LL_Times, 0);
int swingHighCount = ArraySize(swingData[symbolIndex].swingHighPrices);
int swingLowCount = ArraySize(swingData[symbolIndex].swingLowPrices);
// Find last 2 consecutive higher highs
for(int i = swingHighCount - 1; i >= 1; i--)
{
if(swingData[symbolIndex].swingHighPrices[i] > swingData[symbolIndex].swingHighPrices[i - 1])
{
swingData[symbolIndex].HH_Prices[1] = swingData[symbolIndex].swingHighPrices[i];
swingData[symbolIndex].HH_Times[1] = swingData[symbolIndex].swingHighTimes[i];
swingData[symbolIndex].HH_Prices[0] = swingData[symbolIndex].swingHighPrices[i - 1];
swingData[symbolIndex].HH_Times[0] = swingData[symbolIndex].swingHighTimes[i - 1];
break;
}
}
// Find last 2 consecutive lower lows
for(int i = swingLowCount - 1; i >= 1; i--)
{
if(swingData[symbolIndex].swingLowPrices[i] < swingData[symbolIndex].swingLowPrices[i - 1])
{
swingData[symbolIndex].LL_Prices[1] = swingData[symbolIndex].swingLowPrices[i];
swingData[symbolIndex].LL_Times[1] = swingData[symbolIndex].swingLowTimes[i];
swingData[symbolIndex].LL_Prices[0] = swingData[symbolIndex].swingLowPrices[i - 1];
swingData[symbolIndex].LL_Times[0] = swingData[symbolIndex].swingLowTimes[i - 1];
break;
}
}
}
//+------------------------------------------------------------------+
//| Determine breakout points |
//+------------------------------------------------------------------+
void GetBreakPoint(int symbolIndex)
{
string symbol = Symbols[symbolIndex];
if(biasData[symbolIndex].bias == BIAS_BULLISH)
{
int Bullish_BarsInBetween = GetBarsBetweenTimes(swingData[symbolIndex].LL_Times[0],
swingData[symbolIndex].LL_Times[1], symbol);
int L2_Index = iBarShift(symbol, PERIOD_CURRENT, swingData[symbolIndex].LL_Times[1], true);
int highestHigh_Index = iHighest(symbol, PERIOD_CURRENT, MODE_HIGH,
Bullish_BarsInBetween - 1, L2_Index + 1);
if(Is3BarSwingHigh(highestHigh_Index, symbol))
{
swingData[symbolIndex].bullishSwingHigh = iHigh(symbol, PERIOD_CURRENT, highestHigh_Index);
swingData[symbolIndex].bullishSwingHighTime = iTime(symbol, PERIOD_M15, highestHigh_Index);
swingData[symbolIndex].Last_LL_Times[0] = swingData[symbolIndex].LL_Times[0];
swingData[symbolIndex].Last_LL_Times[1] = swingData[symbolIndex].LL_Times[1];
swingData[symbolIndex].Last_LL_Prices[0] = swingData[symbolIndex].LL_Prices[0];
swingData[symbolIndex].Last_LL_Prices[1] = swingData[symbolIndex].LL_Prices[1];
}
}
else if(biasData[symbolIndex].bias == BIAS_BEARISH)
{
int Bearish_BarsInBetween = GetBarsBetweenTimes(swingData[symbolIndex].HH_Times[0],
swingData[symbolIndex].HH_Times[1], symbol);
int H2_Index = iBarShift(symbol, PERIOD_CURRENT, swingData[symbolIndex].HH_Times[1], true);
int lowestLow_Index = iLowest(symbol, PERIOD_CURRENT, MODE_LOW,
Bearish_BarsInBetween - 1, H2_Index + 1);
if(Is3BarSwingLow(lowestLow_Index, symbol))
{
swingData[symbolIndex].bearishSwingLow = iLow(symbol, PERIOD_CURRENT, lowestLow_Index);
swingData[symbolIndex].bearishSwingLowTime = iTime(symbol, PERIOD_M15, lowestLow_Index);
swingData[symbolIndex].Last_HH_Times[0] = swingData[symbolIndex].HH_Times[0];
swingData[symbolIndex].Last_HH_Times[1] = swingData[symbolIndex].HH_Times[1];
swingData[symbolIndex].Last_HH_Prices[0] = swingData[symbolIndex].HH_Prices[0];
swingData[symbolIndex].Last_HH_Prices[1] = swingData[symbolIndex].HH_Prices[1];
}
}
}
// ============================================================
// SWING PATTERN VALIDATION FUNCTIONS
// ============================================================
//+------------------------------------------------------------------+
//| Check if 3-bar swing low |
//+------------------------------------------------------------------+
bool Is3BarSwingLow(int swingLowBarIndex, string symbol)
{
if(swingLowBarIndex < 1) return false;
return (iLow(symbol, PERIOD_CURRENT, swingLowBarIndex) < iLow(symbol, PERIOD_CURRENT, swingLowBarIndex + 1) &&
iLow(symbol, PERIOD_CURRENT, swingLowBarIndex) < iLow(symbol, PERIOD_CURRENT, swingLowBarIndex - 1));
}
//+------------------------------------------------------------------+
//| Check if 3-bar swing high |
//+------------------------------------------------------------------+
bool Is3BarSwingHigh(int swingHighBarIndex, string symbol)
{
if(swingHighBarIndex < 1) return false;
return (iHigh(symbol, PERIOD_CURRENT, swingHighBarIndex) > iHigh(symbol, PERIOD_CURRENT, swingHighBarIndex + 1) &&
iHigh(symbol, PERIOD_CURRENT, swingHighBarIndex) > iHigh(symbol, PERIOD_CURRENT, swingHighBarIndex - 1));
}
// ============================================================
// TIME AND BAR CALCULATION FUNCTIONS
// ============================================================
//+------------------------------------------------------------------+
//| Calculate bars between times |
//+------------------------------------------------------------------+
int GetBarsBetweenTimes(datetime startTime, datetime endTime, string symbol)
{
if(startTime > endTime)
{
datetime temp = startTime;
startTime = endTime;
endTime = temp;
}
int startBar = iBarShift(symbol, PERIOD_CURRENT, startTime, true);
int endBar = iBarShift(symbol, PERIOD_CURRENT, endTime, true);
if(startBar != -1 && endBar != -1)
{
return MathAbs(startBar - endBar);
}
return 0;
}
//+------------------------------------------------------------------+
//| Determine market bias |
//+------------------------------------------------------------------+
void FindBias(int symbolIndex)
{
string symbol = Symbols[symbolIndex];
double dailyHigh0 = iHigh(symbol, PERIOD_D1, 0);
double dailyHigh1 = prevDayData[symbolIndex].highPrice;
double dailyLow0 = iLow(symbol, PERIOD_D1, 0);
double dailyLow1 = prevDayData[symbolIndex].lowPrice;
if(dailyHigh0 > dailyHigh1)
{
biasData[symbolIndex].bias = BIAS_BEARISH;
}
if(dailyLow0 < dailyLow1)
{
biasData[symbolIndex].bias = BIAS_BULLISH;
}
}
//+------------------------------------------------------------------+
//| Check for new day |
//+------------------------------------------------------------------+
bool IsNewDay(int index)
{
datetime t = iTime(Symbols[index], PERIOD_D1, 0);
if(t != last_bar_time_d[index])
{
last_bar_time_d[index] = t;
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| Check for new 15-min bar |
//+------------------------------------------------------------------+
bool IsNew15MinBar(int index)
{
datetime t = iTime(Symbols[index], PERIOD_M15, 0);
if(t != last_bar_time[index])
{
last_bar_time[index] = t;
return true;
}
return false;
}
// ============================================================
// PREVIOUS DAY ANALYSIS FUNCTIONS
// ============================================================
//+------------------------------------------------------------------+
//| Get last tradeable day start |
//+------------------------------------------------------------------+
datetime GetLastTradeableDayStart(string symbol)
{
int maxDaysToCheck = 10;
datetime currentTime = TimeCurrent();
for(int daysBack = 1; daysBack <= maxDaysToCheck; daysBack++)
{
datetime dayToCheck = currentTime - (daysBack * 86400);
MqlDateTime dt;
TimeToStruct(dayToCheck, dt);
dt.hour = 0;
dt.min = 0;
dt.sec = 0;
dayToCheck = StructToTime(dt);
datetime nextDay = dayToCheck + 86400;
MqlRates rates[];
int copied = CopyRates(symbol, PERIOD_M15, dayToCheck, nextDay, rates);
if(copied >= MinCandlesForTradeableDay)
{
return dayToCheck;
}
}
datetime yesterday = currentTime - 86400;
MqlDateTime dtYest;
TimeToStruct(yesterday, dtYest);
dtYest.hour = 0;
dtYest.min = 0;
dtYest.sec = 0;
yesterday = StructToTime(dtYest);
if(EnableDebugPrints)
{
Print("DEBUG: Using fallback - yesterday: ", TimeToString(yesterday, TIME_DATE));
}
return yesterday;
}
//+------------------------------------------------------------------+
//| Get previous day high time |
//+------------------------------------------------------------------+
datetime GetM15PrevDayHighTime(string symbol)
{
MqlRates rates[];
datetime highTime = 0;
double highest = 0;
datetime prevTradeableDay = GetLastTradeableDayStart(symbol);
datetime currentDayOpen = iTime(symbol, PERIOD_D1, 0);
int copied = CopyRates(symbol, PERIOD_M15, prevTradeableDay, currentDayOpen, rates);
if(copied <= 0)
{
return 0;
}
for(int i = 0; i < copied; i++)
{
if(rates[i].high > highest)
{
highest = rates[i].high;
highTime = rates[i].time;
}
}
return highTime;
}
//+------------------------------------------------------------------+
//| Get previous day low time |
//+------------------------------------------------------------------+
datetime GetM15PrevDayLowTime(string symbol)
{
MqlRates rates[];
datetime lowTime = 0;
double lowest = DBL_MAX;
datetime prevTradeableDay = GetLastTradeableDayStart(symbol);
datetime currentDayOpen = iTime(symbol, PERIOD_D1, 0);
int copied = CopyRates(symbol, PERIOD_M15, prevTradeableDay, currentDayOpen, rates);
if(copied <= 0)
{
return 0;
}
for(int i = 0; i < copied; i++)
{
if(rates[i].low < lowest)
{
lowest = rates[i].low;
lowTime = rates[i].time;
}
}
return lowTime;
}
// ============================================================
// SESSION MANAGEMENT FUNCTIONS
// ============================================================
//+------------------------------------------------------------------+
//| Check if in session |
//+------------------------------------------------------------------+
bool IsInSession(string sessionStart, string sessionEnd)
{
datetime currentTime = TimeCurrent();
MqlDateTime timeStruct;
TimeToStruct(currentTime, timeStruct);
string startTimeStr = TimeToString(currentTime, TIME_DATE) + " " + sessionStart;
string endTimeStr = TimeToString(currentTime, TIME_DATE) + " " + sessionEnd;
datetime sessionStartTime = StringToTime(startTimeStr);
datetime sessionEndTime = StringToTime(endTimeStr);
if(sessionEndTime <= sessionStartTime)
{
sessionEndTime += 24 * 60 * 60;
}
return (currentTime >= sessionStartTime && currentTime < sessionEndTime);
}
//+------------------------------------------------------------------+
//| Get current session |
//+------------------------------------------------------------------+
string GetCurrentSession()
{
if(IsInSession(SydneySessionStart, SydneySessionEnd))
return "Sydney";
if(IsInSession(AsiaSessionStart, AsiaSessionEnd))
return "Asian";
else if(IsInSession(LondonSessionStart, LondonSessionEnd))
return "London";
else if(IsInSession(NewYorkSessionStart, NewYorkSessionEnd))
return "NewYork";
else
return "OutsideSession";
}
//+------------------------------------------------------------------+
//| Get timeframe string |
//+------------------------------------------------------------------+
string GetTimeframeString()
{
switch(_Period)
{
case PERIOD_M1: return "M1";
case PERIOD_M5: return "M5";
case PERIOD_M15: return "M15";
case PERIOD_M30: return "M30";
case PERIOD_H1: return "H1";
case PERIOD_H4: return "H4";
case PERIOD_H12: return "H12";
case PERIOD_D1: return "D1";
case PERIOD_W1: return "W1";
case PERIOD_MN1: return "MN1";
default: return "Unknown";
}
}
//+------------------------------------------------------------------+
//| Get timeframe seconds |
//+------------------------------------------------------------------+
int GetTimeframeSeconds()
{
switch(_Period)
{
case PERIOD_M1: return 60;
case PERIOD_M2: return 120;
case PERIOD_M3: return 180;
case PERIOD_M4: return 240;
case PERIOD_M5: return 300;
case PERIOD_M6: return 360;
case PERIOD_M10: return 600;
case PERIOD_M12: return 720;
case PERIOD_M15: return 900;
case PERIOD_M20: return 1200;
case PERIOD_M30: return 1800;
case PERIOD_H1: return 3600;
case PERIOD_H2: return 7200;
case PERIOD_H3: return 10800;
case PERIOD_H4: return 14400;
case PERIOD_H6: return 21600;
case PERIOD_H8: return 28800;
case PERIOD_H12: return 43200;
case PERIOD_D1: return 86400;
case PERIOD_W1: return 604800;
default: return 3600;
}
}
//+------------------------------------------------------------------+