forked from MasoodIqbal/RSI-Stoch-MA-EA
219 lines
No EOL
8.3 KiB
MQL5
219 lines
No EOL
8.3 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| PositionManagement.mqh |
|
|
//+------------------------------------------------------------------+
|
|
#property strict
|
|
#include "GlobalVariables.mqh"
|
|
#include "InputParams.mqh"
|
|
#include "Utilities.mqh"
|
|
#include <Trade/Trade.mqh>
|
|
|
|
extern CTrade Trade;
|
|
|
|
void RecordClosure(SETUP_TYPE setup, TRADE_CATEGORY cat, bool was_buy, double close_price, bool at_sl, bool at_be)
|
|
{
|
|
RecentClosures[ClosureIndex].setup_type = setup;
|
|
RecentClosures[ClosureIndex].category = cat;
|
|
RecentClosures[ClosureIndex].was_buy = was_buy;
|
|
RecentClosures[ClosureIndex].close_price = close_price;
|
|
RecentClosures[ClosureIndex].close_time = TimeCurrent();
|
|
RecentClosures[ClosureIndex].closed_at_sl = at_sl;
|
|
RecentClosures[ClosureIndex].closed_at_be = at_be;
|
|
ClosureIndex = (ClosureIndex + 1) % 10;
|
|
}
|
|
|
|
bool RecentlyStopped(SETUP_TYPE setup)
|
|
{
|
|
datetime now = TimeCurrent();
|
|
datetime cutoff = now - 10 * PeriodSeconds(PERIOD_CURRENT);
|
|
|
|
for(int i=0; i<10; i++)
|
|
{
|
|
if(RecentClosures[i].setup_type == setup &&
|
|
RecentClosures[i].close_time > cutoff &&
|
|
(RecentClosures[i].closed_at_sl || RecentClosures[i].closed_at_be))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void MarkRunners()
|
|
{
|
|
double current = MidPrice();
|
|
for(int i=0; i<ArraySize(OpenPositions); i++)
|
|
{
|
|
if(PositionSelectByTicket(OpenPositions[i].ticket))
|
|
OpenPositions[i].distance_from_current = MathAbs(current - OpenPositions[i].entry) / _Point;
|
|
}
|
|
|
|
for(int i=0; i<ArraySize(OpenPositions)-1; i++)
|
|
for(int j=i+1; j<ArraySize(OpenPositions); j++)
|
|
if(OpenPositions[j].distance_from_current > OpenPositions[i].distance_from_current)
|
|
{
|
|
PositionRec tmp = OpenPositions[i];
|
|
OpenPositions[i] = OpenPositions[j];
|
|
OpenPositions[j] = tmp;
|
|
}
|
|
|
|
for(int i=0; i<ArraySize(OpenPositions); i++)
|
|
OpenPositions[i].is_runner = (i < Min_Runners_To_Keep);
|
|
}
|
|
|
|
void CloseOnFullReversal()
|
|
{
|
|
if(!Only_Close_On_Full_Reversal) return;
|
|
MarkRunners();
|
|
|
|
for(int i=PositionsTotal()-1; i>=0; i--)
|
|
{
|
|
if(PositionGetTicket(i) == 0) continue;
|
|
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
|
|
if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
|
|
|
|
ulong ticket = PositionGetTicket(i);
|
|
bool is_buy = (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY);
|
|
|
|
int idx = -1;
|
|
for(int j=0; j<ArraySize(OpenPositions); j++)
|
|
if(OpenPositions[j].ticket == ticket) { idx = j; break; }
|
|
|
|
if(idx >= 0 && OpenPositions[idx].is_runner) continue;
|
|
|
|
bool should_close = false;
|
|
if(is_buy && IsBearBias()) should_close = true;
|
|
if(!is_buy && IsBullBias()) should_close = true;
|
|
|
|
if(should_close && Trade.PositionClose(ticket))
|
|
ClosedByReversal++;
|
|
}
|
|
}
|
|
|
|
void ManagePositions()
|
|
{
|
|
for(int i=PositionsTotal()-1; i>=0; i--)
|
|
{
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(ticket == 0 || PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
|
|
if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
|
|
|
|
int idx = -1;
|
|
for(int j=0; j<ArraySize(OpenPositions); j++)
|
|
if(OpenPositions[j].ticket == ticket) { idx = j; break; }
|
|
if(idx == -1) continue;
|
|
|
|
double entry = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
double current_sl = PositionGetDouble(POSITION_SL);
|
|
double current_tp = PositionGetDouble(POSITION_TP);
|
|
bool is_buy = OpenPositions[idx].is_buy;
|
|
TRADE_CATEGORY cat = OpenPositions[idx].category;
|
|
|
|
double current = is_buy ? SymbolInfoDouble(_Symbol, SYMBOL_BID) : SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
|
double profit_points = is_buy ? (current - entry) / _Point : (entry - current) / _Point;
|
|
|
|
int partial_tp_pts = (cat == CATEGORY_COUNTER_TREND) ? Counter_Partial_TP : Continuation_Partial_TP;
|
|
double partial_pct = (cat == CATEGORY_COUNTER_TREND) ? Counter_Partial_Percent : Continuation_Partial_Percent;
|
|
int be_pts = (cat == CATEGORY_COUNTER_TREND) ? Counter_BreakEven_Points : Continuation_BreakEven_Points;
|
|
int trail_pts = (cat == CATEGORY_COUNTER_TREND) ? Counter_Trail_Points : Continuation_Trail_Points;
|
|
|
|
if(Use_Praise_System && cat == CATEGORY_CONTINUATION && Praise_Count >= 4)
|
|
trail_pts = Praise_Tight_Trail;
|
|
|
|
// Partial TP
|
|
if(!OpenPositions[idx].partial_tp_hit && profit_points >= partial_tp_pts)
|
|
{
|
|
double current_lot = PositionGetDouble(POSITION_VOLUME);
|
|
double close_size = NormalizeDouble(OpenPositions[idx].original_lot * partial_pct / 100.0, 2);
|
|
if(close_size > 0 && close_size <= current_lot && Trade.PositionClosePartial(ticket, close_size))
|
|
OpenPositions[idx].partial_tp_hit = true;
|
|
}
|
|
|
|
// MFIB Partials (War Survivor)
|
|
if(Use_MFIB_Partials && profit_points >= MFIB_Partial_Min_Profit && OpenPositions[idx].mfib_partials_taken < 4)
|
|
{
|
|
double mfib_levels[5] = {MFIB_Level_236, MFIB_Level_382, MFIB_Level_050, MFIB_Level_618, MFIB_Level_786};
|
|
|
|
for(int m=0; m<5; m++)
|
|
{
|
|
double dist_to_level = MathAbs(current - mfib_levels[m]) / _Point;
|
|
if(dist_to_level <= 50)
|
|
{
|
|
bool has_reject = (MFIB_Reject_Warning || Stoch_Reject_Warning || Stoch_Extreme_Warning || Warning_Confluence_3Plus);
|
|
bool new_level = (OpenPositions[idx].last_mfib_partial_price == 0) ||
|
|
(MathAbs(current - OpenPositions[idx].last_mfib_partial_price) / _Point > 100);
|
|
|
|
if(has_reject && new_level)
|
|
{
|
|
double current_lot = PositionGetDouble(POSITION_VOLUME);
|
|
double close_size = NormalizeDouble(OpenPositions[idx].original_lot * MFIB_Partial_Percent / 100.0, 2);
|
|
if(close_size > current_lot) close_size = current_lot * 0.25;
|
|
|
|
if(close_size > 0 && close_size >= SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN))
|
|
{
|
|
if(Trade.PositionClosePartial(ticket, close_size))
|
|
{
|
|
OpenPositions[idx].mfib_partials_taken++;
|
|
OpenPositions[idx].last_mfib_partial_price = mfib_levels[m];
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Breakeven
|
|
if(!OpenPositions[idx].be_set && profit_points >= be_pts)
|
|
{
|
|
if((is_buy && current_sl < entry) || (!is_buy && current_sl > entry))
|
|
{
|
|
if(Trade.PositionModify(ticket, entry, current_tp))
|
|
OpenPositions[idx].be_set = true;
|
|
}
|
|
}
|
|
|
|
// Trailing
|
|
if(profit_points >= be_pts + 50)
|
|
{
|
|
OpenPositions[idx].trailing_active = true;
|
|
double newSL = 0;
|
|
bool should_modify = false;
|
|
|
|
if(is_buy)
|
|
{
|
|
newSL = current - trail_pts * _Point;
|
|
if(newSL > current_sl + 30 * _Point) should_modify = true;
|
|
}
|
|
else
|
|
{
|
|
newSL = current + trail_pts * _Point;
|
|
if(newSL < current_sl - 30 * _Point) should_modify = true;
|
|
}
|
|
|
|
if(should_modify) Trade.PositionModify(ticket, newSL, current_tp);
|
|
}
|
|
|
|
OpenPositions[idx].close_price = current;
|
|
}
|
|
|
|
// Track closed positions
|
|
for(int i=ArraySize(OpenPositions)-1; i>=0; i--)
|
|
{
|
|
if(!PositionSelectByTicket(OpenPositions[i].ticket))
|
|
{
|
|
double entry = OpenPositions[i].entry;
|
|
double close_price = OpenPositions[i].close_price;
|
|
bool is_buy = OpenPositions[i].is_buy;
|
|
|
|
bool at_be = (MathAbs(close_price - entry) / _Point < 10);
|
|
bool at_sl = false;
|
|
if(is_buy && close_price < entry) at_sl = true;
|
|
if(!is_buy && close_price > entry) at_sl = true;
|
|
if(at_be) at_sl = false;
|
|
|
|
RecordClosure(OpenPositions[i].setup_type, OpenPositions[i].category, is_buy, close_price, at_sl, at_be);
|
|
|
|
for(int j=i; j<ArraySize(OpenPositions)-1; j++)
|
|
OpenPositions[j] = OpenPositions[j+1];
|
|
ArrayResize(OpenPositions, ArraySize(OpenPositions)-1);
|
|
}
|
|
}
|
|
} |