forked from nique_372/MQLArticles
548 lines
No EOL
20 KiB
MQL5
548 lines
No EOL
20 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"
| |
| |
//+------------------------------------------------------------------+
| |
//| |
| |
//+------------------------------------------------------------------+
| |
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
| |
//+------------------------------------------------------------------+
| |
|