//+------------------------------------------------------------------+ //| PositionManagement.mqh | //+------------------------------------------------------------------+ #property strict #include "GlobalVariables.mqh" #include "InputParams.mqh" #include "Utilities.mqh" #include 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 OpenPositions[i].distance_from_current) { PositionRec tmp = OpenPositions[i]; OpenPositions[i] = OpenPositions[j]; OpenPositions[j] = tmp; } for(int i=0; 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= 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= 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