//+------------------------------------------------------------------+ //| 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 //+------------------------------------------------------------------+