542 lines
No EOL
41 KiB
MQL5
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
|
|
//+------------------------------------------------------------------+
|
|
|