RSI-Stoch-MA-EA/Positionmanagement.mqh

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);
}
}
}