MQLArticles/PosMgmt/ConditionalPartial/Conds/Ict/SwingSweep.mqh
2026-05-28 13:03:25 -05:00

542 lines
No EOL
41 KiB
MQL5

//+------------------------------------------------------------------+
//| SwingSweep.mqh |
//| Copyright 2025, Niquel Mendoza. |
//| https://www.mql5.com/es/users/nique_372/news |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, Niquel Mendoza."
#property link "https://www.mql5.com/es/users/nique_372/news"
#property strict
#ifndef MQLARTICLES_POSMGMT_CONDPART_COND_ICT_SWINGSWEEP_MQH
#define MQLARTICLES_POSMGMT_CONDPART_COND_ICT_SWINGSWEEP_MQH
//+------------------------------------------------------------------+
//| Include |
//+------------------------------------------------------------------+
#include "..\\..\\Base\\Defines.mqh"
#include "..\\..\\..\\..\\Utils\\GraphicObjects.mqh"
//+------------------------------------------------------------------+
//| Defines |
//+------------------------------------------------------------------+
#define CONDITIONAL_PARTIAL_SWING_SWEEP_ARR_RESERVE 10
#define CONDITIONAL_PARTIAL_SWING_SWEEP_BUY 1
#define CONDITIONAL_PARTIAL_SWING_SWEEP_SELL 2
//+------------------------------------------------------------------+
//| Structures |
//+------------------------------------------------------------------+
//--- Structure for the initial configuration of parameters for swing detection in Init(..) of the CConditionalPartialsSwingSweep class
struct ConditionalPartialSwingSweepConfig
{
string swing_font;
color swing_high_color;
color swing_low_color;
ENUM_TIMEFRAMES swing_timeframe;
int swing_font_size;
int swing_bars_left;
int swing_bars_right;
bool draw_swings;
bool swing_considerate_equals;
int mitigation_obj_delete_delay;
//--- Constructor (default values are initialized)
ConditionalPartialSwingSweepConfig()
: swing_timeframe(Period()), swing_font("Arial"), swing_font_size(10), swing_high_color(clrBlue), swing_low_color(clrRed), draw_swings(true),
swing_considerate_equals(false), swing_bars_left(4), swing_bars_right(4), mitigation_obj_delete_delay((PeriodSeconds() * 30))
{
}
//--- Validates if the configurations are valid
bool IsValid(string& error) const
{
//---
if(draw_swings)
{
if(swing_font_size <= 0)
{
error = "Invalid font size";
return false;
}
}
//---
if(swing_bars_right <= 0)
{
error = "Invalid bars right for the swing detection";
return false;
}
//---
if(swing_bars_left <= 0)
{
error = "Invalid bars left for the swing detection";
return false;
}
return true;
}
};
//+------------------------------------------------------------------+
//| Class that implements a conditional partial closure based on |
//| swing manipulation (sweep). |
//| The buy partial closure occurs when a swing high higher than |
//| the previous one is manipulated, and the sell partial closure |
//| occurs when a swing low lower than the previous one is |
//| manipulated. |
//| (This resets each time a new "cycle" of partial closures |
//| is initiated) |
//+------------------------------------------------------------------+
class CConditionalPartialsSwingSweep : public IConditionPartial
{
private:
//--- Structure to store a swing
struct Swing
{
string name; // Swing name
datetime time; // Swing time
double price; // Swing price
Swing(): name(NULL), time(0), price(0.00) {}
};
//--- General variables
double m_next_price_buy; // Next (min) price to closure buy position
double m_next_price_sell; // Next (min) price to closure sell position
uint8_t m_partial_closure; // Flag indicating the type of "position" to close (0=none, 1=buy, 2=sell)
CGraphicObjEliminations* m_obj_eliminations; // Pointer of type CGraphicObjEliminations, for scheduled deletion of graphic objects
int m_mitigation_obj_delete_delay; // Seconds of "delay" to remove graphic objects (texts from mitigated swings)
//--- Variables for swing detections
// Bar controler detector
CBarControlerFast* m_swing_bar_controler;
// Arrays
Swing m_swing_highs[];
Swing m_swing_lows[];
// Parameters
ENUM_TIMEFRAMES m_swing_timeframe;
string m_swing_font;
int m_swing_font_size;
color m_swing_high_color;
color m_swing_low_color;
bool m_draw_swings;
bool m_swing_considerate_equals;
int m_swing_bars_left;
int m_swing_bars_right;
//--- Swings functions
void GetSwings(datetime current_time);
void ProccesSwingsWithDrawingEnable(datetime current_time);
void ProccesSwingsWithDrawingDisable(datetime current_time);
bool IsSwing(int index, bool find_swing_high, double&out_price);
public:
CConditionalPartialsSwingSweep(void);
~CConditionalPartialsSwingSweep(void);
//--- Initialize function
bool Init(); // Default values
bool Init(const ConditionalPartialSwingSweepConfig& config); // Custom values
//--- General setters and getters
// Swing high color
color SwingHighColor() const { return m_swing_high_color; }
void SwingHighColor(color new_value) { m_swing_high_color = new_value; }
// Swing low color
color SwingLowColor() const { return m_swing_low_color; }
void SwingLowColor(color new_value) { m_swing_low_color = new_value; }
// Swing considerate equals
bool SwingConsiderateEquals() const { return m_swing_considerate_equals; }
void SwingConsiderateEquals(bool new_value) { m_swing_considerate_equals = new_value; }
//--- General functions
void OnInitPartials(double initial_price) override final;
void OnNewWeek(const datetime curr_time) override final;
void Execute(const datetime current_time) override final;
bool CloseBuy() override final { return m_partial_closure == CONDITIONAL_PARTIAL_SWING_SWEEP_BUY; }
bool CloseSell() override final { return m_partial_closure == CONDITIONAL_PARTIAL_SWING_SWEEP_SELL; }
string Name() const override final { return "ConditionalPartials by Swing Sweep"; }
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CConditionalPartialsSwingSweep::CConditionalPartialsSwingSweep(void)
: m_partial_closure(0)
{
ArrayResize(m_swing_highs, 0, CONDITIONAL_PARTIAL_SWING_SWEEP_ARR_RESERVE);
ArrayResize(m_swing_lows, 0, CONDITIONAL_PARTIAL_SWING_SWEEP_ARR_RESERVE);
m_obj_eliminations = new CGraphicObjEliminations();
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CConditionalPartialsSwingSweep::~CConditionalPartialsSwingSweep()
{
if(CheckPointer(m_swing_bar_controler) == POINTER_DYNAMIC)
delete m_swing_bar_controler;
if(CheckPointer(m_obj_eliminations) == POINTER_DYNAMIC)
delete m_obj_eliminations;
}
//+------------------------------------------------------------------+
//| Initialization function, sets the swing parameters |
//| Inputs: |
//| - const ConditionalPartialSwingSweepConfig & config: Reference |
//| in which the swing configuration will be passed. |
//| Outputs: |
//| In case of success the function returns true, otherwise |
//| false. |
//+------------------------------------------------------------------+
bool CConditionalPartialsSwingSweep::Init(const ConditionalPartialSwingSweepConfig & config)
{
//---
string err = "";
if(!config.IsValid(err))
{
FastLog(FUNCION_ACTUAL, ERROR_TEXT, err);
return false;
}
//---
m_draw_swings = config.draw_swings;
m_swing_bars_left = config.swing_bars_left;
m_swing_bars_right = config.swing_bars_right;
m_swing_considerate_equals = config.swing_considerate_equals;
m_swing_font = config.swing_font;
m_swing_font_size = config.swing_font_size;
m_swing_high_color = config.swing_high_color;
m_swing_low_color = config.swing_low_color;
m_swing_timeframe = config.swing_timeframe;
m_mitigation_obj_delete_delay = config.mitigation_obj_delete_delay;
//---
m_swing_bar_controler = new CBarControlerFast(m_swing_timeframe);
//---
return true;
}
//+------------------------------------------------------------------+
//| Initialize function with default values |
//+------------------------------------------------------------------+
bool CConditionalPartialsSwingSweep::Init(void)
{
ConditionalPartialSwingSweepConfig config;
return Init(config); // Use default values
}
//+------------------------------------------------------------------+
//| Function that executes each time the first trade has been |
//| opened for partial closures |
//+------------------------------------------------------------------+
void CConditionalPartialsSwingSweep::OnInitPartials(double initial_price)
{
m_next_price_buy = initial_price;
m_next_price_sell = initial_price;
}
//+------------------------------------------------------------------+
//| Main execution function |
//+------------------------------------------------------------------+
void CConditionalPartialsSwingSweep::Execute(const datetime current_time)
{
m_partial_closure = 0;
GetSwings(current_time);
//---
if(m_draw_swings)
{
ProccesSwingsWithDrawingEnable(current_time);
m_obj_eliminations.OnNewBar(current_time);
}
else
{
ProccesSwingsWithDrawingDisable(current_time);
}
}
//+------------------------------------------------------------------------------+
//| Function to get swings |
//+------------------------------------------------------------------------------+
void CConditionalPartialsSwingSweep::GetSwings(datetime current_time)
{
//--- Find swings
if(m_swing_bar_controler.IsNewBar(current_time))
{
//--- Swing detection
const int index = m_swing_bars_right + 1;
Swing new_swing;
//---
if(IsSwing(index, true, new_swing.price))
{
new_swing.time = iTime(_Symbol, m_swing_timeframe, index);
new_swing.name = StringFormat("sh_cpsp_%s", TimeToString(new_swing.time));
//--- Draw
if(m_draw_swings)
TextCreate(0, new_swing.name, 0, new_swing.time, new_swing.price, "H", m_swing_font, m_swing_font_size, m_swing_high_color, 0.0, ANCHOR_CENTER, true);
//--- Add
AddArrayNoVerification2(m_swing_highs, new_swing, CONDITIONAL_PARTIAL_SWING_SWEEP_ARR_RESERVE)
}
else
if(IsSwing(index, false, new_swing.price))
{
new_swing.time = iTime(_Symbol, m_swing_timeframe, index);
new_swing.name = StringFormat("sl_cpsp_%s", TimeToString(new_swing.time));
//--- Draw
if(m_draw_swings)
TextCreate(0, new_swing.name, 0, new_swing.time, new_swing.price, "L", m_swing_font, m_swing_font_size, m_swing_low_color, 0.0, ANCHOR_CENTER, true);
//--- Add
AddArrayNoVerification2(m_swing_lows, new_swing, CONDITIONAL_PARTIAL_SWING_SWEEP_ARR_RESERVE)
}
}
}
//+------------------------------------------------------------------+
//| Function to process swings (search for sweeps) when |
//| swing drawing is disabled. |
//+------------------------------------------------------------------+
void CConditionalPartialsSwingSweep::ProccesSwingsWithDrawingDisable(datetime current_time)
{
//--- Initial variables
const double prev_high = iHigh(_Symbol, _Period, 1);
const double prev_low = iLow(_Symbol, _Period, 1);
const double prev_close = iClose(_Symbol, _Period, 1);
//---
int indexes_to_delete_swing_highs[];
int indexes_to_delete_swing_lows[];
//--- Swing high
for(int i = 0; i < ArraySize(m_swing_highs); i++)
{
//--- Cache the swing price
const double swing_price = m_swing_highs[i].price; // Swing price
//---
if(prev_high > swing_price) // Swing "manipulated"
{
// Confirm the manipulation and verify that it is a higher swing
if(swing_price > prev_close && swing_price > m_next_price_buy)
{
m_next_price_buy = swing_price;
m_partial_closure = CONDITIONAL_PARTIAL_SWING_SWEEP_BUY;
}
//---
indexes_to_delete_swing_highs.Push(i);
}
}
//--- Swing lows
for(int i = 0; i < ArraySize(m_swing_lows); i++)
{
//--- Cache the swing price
const double swing_price = m_swing_lows[i].price; // Swing price
//---
if(swing_price > prev_low) // Swing "manipulated"
{
// Confirm the manipulation and verify that it is a lower swing
if(prev_close > swing_price && swing_price < m_next_price_sell)
{
m_next_price_sell = swing_price;
m_partial_closure = CONDITIONAL_PARTIAL_SWING_SWEEP_SELL;
}
//---
indexes_to_delete_swing_lows.Push(i);
}
}
//--- Remove invalid swings high and lows
RemoveMultipleIndexes(m_swing_lows, indexes_to_delete_swing_lows, CONDITIONAL_PARTIAL_SWING_SWEEP_ARR_RESERVE);
RemoveMultipleIndexes(m_swing_highs, indexes_to_delete_swing_highs, CONDITIONAL_PARTIAL_SWING_SWEEP_ARR_RESERVE);
}
//+------------------------------------------------------------------+
//| Function to process swings (search for sweeps) when |
//| swing drawing is enabled. |
//+------------------------------------------------------------------+
void CConditionalPartialsSwingSweep::ProccesSwingsWithDrawingEnable(datetime current_time)
{
//--- Initial variables
const double prev_high = iHigh(_Symbol, _Period, 1);
const double prev_low = iLow(_Symbol, _Period, 1);
const double prev_close = iClose(_Symbol, _Period, 1);
//---
bool array_of_eliminations_is_resized = false;
int indexes_to_delete_swing_highs[];
int indexes_to_delete_swing_lows[];
//--- Swing high
for(int i = 0; i < ArraySize(m_swing_highs); i++)
{
//--- Cache the swing price
const double swing_price = m_swing_highs[i].price; // Swing price
//---
if(prev_high > swing_price) // Swing "manipulated"
{
// Confirm the manipulation and verify that it is a higher swing
if(swing_price > prev_close && swing_price > m_next_price_buy)
{
m_next_price_buy = swing_price;
m_partial_closure = CONDITIONAL_PARTIAL_SWING_SWEEP_BUY;
}
//---
if(!array_of_eliminations_is_resized)
{
m_obj_eliminations.InitAddElement(current_time + m_mitigation_obj_delete_delay);
array_of_eliminations_is_resized = true;
}
//---
m_obj_eliminations.AddElement(m_swing_highs[i].name);
//---
indexes_to_delete_swing_highs.Push(i);
}
}
//--- Swing lows
for(int i = 0; i < ArraySize(m_swing_lows); i++)
{
//--- Cache the swing price
const double swing_price = m_swing_lows[i].price; // Swing price
//---
if(swing_price > prev_low) // Swing "manipulated"
{
// Confirm the manipulation and verify that it is a lower swing
if(prev_close > swing_price && swing_price < m_next_price_sell)
{
m_next_price_sell = swing_price;
m_partial_closure = CONDITIONAL_PARTIAL_SWING_SWEEP_SELL;
}
//---
if(!array_of_eliminations_is_resized)
{
m_obj_eliminations.InitAddElement(current_time + m_mitigation_obj_delete_delay);
array_of_eliminations_is_resized = true;
}
//---
m_obj_eliminations.AddElement(m_swing_lows[i].name);
//---
indexes_to_delete_swing_lows.Push(i);
}
}
//--- Remove invalid swings high and lows
RemoveMultipleIndexes(m_swing_lows, indexes_to_delete_swing_lows, CONDITIONAL_PARTIAL_SWING_SWEEP_ARR_RESERVE);
RemoveMultipleIndexes(m_swing_highs, indexes_to_delete_swing_highs, CONDITIONAL_PARTIAL_SWING_SWEEP_ARR_RESERVE);
}
//+------------------------------------------------------------------+
//| Function that verifies if a specific index is a swing high |
//| or swing low. |
//| Inputs: |
//| - index: bar index to verify (0= current bar) |
//| - find_swing_high: true if searching for a swing high, for a |
//| swing low, false. |
//| Outputs: Returns true if candle "index" is a swing. |
//| - double & out_price: Swing price. |
//+------------------------------------------------------------------+
bool CConditionalPartialsSwingSweep::IsSwing(int index, bool find_swing_high, double & out_price)
{
//---
out_price = (find_swing_high) ? iHigh(_Symbol, m_swing_timeframe, index) : iLow(_Symbol, m_swing_timeframe, index);
//---
if(!m_swing_considerate_equals)
{
for(int i = index - m_swing_bars_right; i <= index + m_swing_bars_left; i++)
{
if(i == index)
continue; // Skip the current candle
if(find_swing_high)
{
if(out_price <= iHigh(_Symbol, m_swing_timeframe, i))
return false;
}
else
{
if(out_price >= iLow(_Symbol, m_swing_timeframe, i))
return false;
}
}
}
else
{
for(int i = index - m_swing_bars_right; i <= index + m_swing_bars_left; i++)
{
if(i == index)
continue; // Skip the current candle
if(find_swing_high)
{
if(out_price < iHigh(_Symbol, m_swing_timeframe, i))
return false;
}
else
{
if(out_price > iLow(_Symbol, m_swing_timeframe, i))
return false;
}
}
}
//---
return true; // Condition met
}
//+------------------------------------------------------------------+
//| Function that executes each new day |
//+------------------------------------------------------------------+
void CConditionalPartialsSwingSweep::OnNewWeek(const datetime curr_time)
{
if(m_draw_swings)
{
//--- Delete objects
for(int i = 0; i < ArraySize(m_swing_highs); i++)
ObjectDelete(0, m_swing_highs[i].name);
for(int i = 0; i < ArraySize(m_swing_lows); i++)
ObjectDelete(0, m_swing_lows[i].name);
//---
m_obj_eliminations.DeleteAll();
}
//--- Resize arrays
ArrayResize(m_swing_highs, 0, CONDITIONAL_PARTIAL_SWING_SWEEP_ARR_RESERVE);
ArrayResize(m_swing_lows, 0, CONDITIONAL_PARTIAL_SWING_SWEEP_ARR_RESERVE);
}
//+------------------------------------------------------------------+
#endif // MQLARTICLES_POSMGMT_CONDPART_COND_ICT_SWINGSWEEP_MQH
//+------------------------------------------------------------------+