2026-09-13 08:47:52 -05:00 | | | //+------------------------------------------------------------------+
|
| | | //| 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"
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| |
|
| | | //+------------------------------------------------------------------+
|
| | | namespace TSN
|
| | | {
|
| | | //+------------------------------------------------------------------+
|
| | | //| 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
|
| | | //+------------------------------------------------------------------+
|
| | |
|