199 lines
7.5 KiB
MQL5
199 lines
7.5 KiB
MQL5
|
//+------------------------------------------------------------------+
|
||
|
//| BOS.mq5 |
|
||
|
//| Copyright 2024, MetaQuotes Ltd. |
|
||
|
//| https://www.mql5.com |
|
||
|
//+------------------------------------------------------------------+
|
||
|
#property copyright "Copyright 2024, MetaQuotes Ltd."
|
||
|
#property link "https://www.mql5.com"
|
||
|
#property version "1.00"
|
||
|
|
||
|
#include <Trade/Trade.mqh>
|
||
|
|
||
|
CTrade obj_Trade;
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Expert initialization function |
|
||
|
//+------------------------------------------------------------------+
|
||
|
int OnInit()
|
||
|
{
|
||
|
return (INIT_SUCCEEDED);
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Expert deinitialization function |
|
||
|
//+------------------------------------------------------------------+
|
||
|
void OnDeinit(const int reason)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Expert tick function |
|
||
|
//+------------------------------------------------------------------+
|
||
|
void OnTick()
|
||
|
{
|
||
|
static bool isNewBar = false;
|
||
|
int currBars = iBars(_Symbol, _Period);
|
||
|
static int prevBars = currBars;
|
||
|
|
||
|
// Check if a new bar has formed
|
||
|
if (prevBars == currBars)
|
||
|
isNewBar = false;
|
||
|
else
|
||
|
{
|
||
|
isNewBar = true;
|
||
|
prevBars = currBars;
|
||
|
}
|
||
|
|
||
|
// Swing calculation parameters
|
||
|
const int length = 20; // Length for swing calculations
|
||
|
const int limit = 20; // Limit for bar checks
|
||
|
|
||
|
// Variables to hold swing points
|
||
|
static double swing_H = -1.0, swing_L = -1.0;
|
||
|
int curr_bar = limit;
|
||
|
|
||
|
if (isNewBar)
|
||
|
{
|
||
|
handleSwingPoints(curr_bar, length, swing_H, swing_L);
|
||
|
}
|
||
|
|
||
|
// Get market prices
|
||
|
double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits);
|
||
|
double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits);
|
||
|
|
||
|
// Check for breakouts
|
||
|
if (checkBreakUp(Bid, swing_H))
|
||
|
{
|
||
|
Print("BREAK UP NOW");
|
||
|
int swing_H_index = findSwingIndex(swing_H, length, true);
|
||
|
drawBreakLevel(TimeToString(time(0)), time(swing_H_index), high(swing_H_index),
|
||
|
time(0 + 1), high(swing_H_index), clrBlue, -1);
|
||
|
swing_H = -1.0; // Reset swing high after breakout
|
||
|
}
|
||
|
else if (checkBreakDown(Ask, swing_L))
|
||
|
{
|
||
|
Print("BREAK DOWN NOW");
|
||
|
int swing_L_index = findSwingIndex(swing_L, length, false);
|
||
|
drawBreakLevel(TimeToString(time(0)), time(swing_L_index), low(swing_L_index),
|
||
|
time(0 + 1), low(swing_L_index), clrRed, 1);
|
||
|
swing_L = -1.0; // Reset swing low after breakout
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Helper Functions |
|
||
|
//+------------------------------------------------------------------+
|
||
|
double high(int index) { return (iHigh(_Symbol, _Period, index)); }
|
||
|
double low(int index) { return (iLow(_Symbol, _Period, index)); }
|
||
|
double close(int index) { return (iClose(_Symbol, _Period, index)); }
|
||
|
datetime time(int index) { return (iTime(_Symbol, _Period, index)); }
|
||
|
|
||
|
// Handle swing points
|
||
|
void handleSwingPoints(int curr_bar, int length, double &swing_H, double &swing_L)
|
||
|
{
|
||
|
bool isSwingHigh = true, isSwingLow = true;
|
||
|
|
||
|
// Check for swing high and low
|
||
|
for (int j = 1; j <= length; j++)
|
||
|
{
|
||
|
int right_index = curr_bar - j;
|
||
|
int left_index = curr_bar + j;
|
||
|
|
||
|
// Determine if current bar is a swing high or low
|
||
|
if ((high(curr_bar) <= high(right_index)) || (high(curr_bar) < high(left_index)))
|
||
|
isSwingHigh = false; // Not a swing high
|
||
|
if ((low(curr_bar) >= low(right_index)) || (low(curr_bar) > low(left_index)))
|
||
|
isSwingLow = false; // Not a swing low
|
||
|
}
|
||
|
|
||
|
// Handle identified swing points
|
||
|
if (isSwingHigh)
|
||
|
{
|
||
|
swing_H = high(curr_bar);
|
||
|
Print("UP @ BAR INDEX ", curr_bar, " of High: ", swing_H);
|
||
|
drawSwingPoint(TimeToString(time(curr_bar)), time(curr_bar), swing_H, 77, clrBlue, -1);
|
||
|
}
|
||
|
if (isSwingLow)
|
||
|
{
|
||
|
swing_L = low(curr_bar);
|
||
|
Print("DOWN @ BAR INDEX ", curr_bar, " of Low: ", swing_L);
|
||
|
drawSwingPoint(TimeToString(time(curr_bar)), time(curr_bar), swing_L, 77, clrRed, 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check for breakout upwards
|
||
|
bool checkBreakUp(double Bid, double swing_H)
|
||
|
{
|
||
|
return (swing_H > 0 && Bid > swing_H && close(1) > swing_H);
|
||
|
}
|
||
|
|
||
|
// Check for breakout downwards
|
||
|
bool checkBreakDown(double Ask, double swing_L)
|
||
|
{
|
||
|
return (swing_L > 0 && Ask < swing_L && close(1) < swing_L);
|
||
|
}
|
||
|
|
||
|
// Find the index of the swing high/low
|
||
|
int findSwingIndex(double swingValue, int length, bool isHigh)
|
||
|
{
|
||
|
for (int i = 0; i <= length * 2 + 1000; i++)
|
||
|
{
|
||
|
double selectedValue = isHigh ? high(i) : low(i);
|
||
|
if (selectedValue == swingValue)
|
||
|
{
|
||
|
Print(isHigh ? "BREAK HIGH @ BAR " : "BREAK LOW @ BAR ", i);
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
return 0; // Default return
|
||
|
}
|
||
|
|
||
|
// Draw a swing point on the chart
|
||
|
void drawSwingPoint(string objName, datetime time, double price, int arrCode, color clr, int direction)
|
||
|
{
|
||
|
if (ObjectFind(0, objName) < 0)
|
||
|
{
|
||
|
ObjectCreate(0, objName, OBJ_ARROW, 0, time, price);
|
||
|
ObjectSetInteger(0, objName, OBJPROP_ARROWCODE, arrCode);
|
||
|
ObjectSetInteger(0, objName, OBJPROP_COLOR, clr);
|
||
|
ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, 10);
|
||
|
ObjectSetInteger(0, objName, OBJPROP_ANCHOR, direction > 0 ? ANCHOR_TOP : ANCHOR_BOTTOM);
|
||
|
|
||
|
string txt = " BoS";
|
||
|
string objNameDescr = objName + txt;
|
||
|
ObjectCreate(0, objNameDescr, OBJ_TEXT, 0, time, price);
|
||
|
ObjectSetInteger(0, objNameDescr, OBJPROP_COLOR, clr);
|
||
|
ObjectSetInteger(0, objNameDescr, OBJPROP_FONTSIZE, 10);
|
||
|
|
||
|
ObjectSetInteger(0, objNameDescr, OBJPROP_ANCHOR, direction > 0 ? ANCHOR_LEFT_UPPER : ANCHOR_LEFT_LOWER);
|
||
|
ObjectSetString(0, objNameDescr, OBJPROP_TEXT, " " + txt);
|
||
|
}
|
||
|
ChartRedraw(0);
|
||
|
}
|
||
|
|
||
|
// Draw a break level on the chart
|
||
|
void drawBreakLevel(string objName, datetime time1, double price1, datetime time2, double price2, color clr, int direction)
|
||
|
{
|
||
|
if (ObjectFind(0, objName) < 0)
|
||
|
{
|
||
|
ObjectCreate(0, objName, OBJ_ARROWED_LINE, 0, time1, price1, time2, price2);
|
||
|
ObjectSetInteger(0, objName, OBJPROP_TIME, 0, time1);
|
||
|
ObjectSetDouble(0, objName, OBJPROP_PRICE, 0, price1);
|
||
|
ObjectSetInteger(0, objName, OBJPROP_TIME, 1, time2);
|
||
|
ObjectSetDouble(0, objName, OBJPROP_PRICE, 1, price2);
|
||
|
ObjectSetInteger(0, objName, OBJPROP_COLOR, clr);
|
||
|
ObjectSetInteger(0, objName, OBJPROP_WIDTH, 2);
|
||
|
|
||
|
string txt = " Break ";
|
||
|
string objNameDescr = objName + txt;
|
||
|
ObjectCreate(0, objNameDescr, OBJ_TEXT, 0, time2, price2);
|
||
|
ObjectSetInteger(0, objNameDescr, OBJPROP_COLOR, clr);
|
||
|
ObjectSetInteger(0, objNameDescr, OBJPROP_FONTSIZE, 10);
|
||
|
|
||
|
ObjectSetInteger(0, objNameDescr, OBJPROP_ANCHOR, direction > 0 ? ANCHOR_RIGHT_UPPER : ANCHOR_RIGHT_LOWER);
|
||
|
ObjectSetString(0, objNameDescr, OBJPROP_TEXT, " " + txt);
|
||
|
}
|
||
|
ChartRedraw(0);
|
||
|
}
|
||
|
//+------------------------------------------------------------------+
|