//+------------------------------------------------------------------+ //| MasterOfPuppets.mq5 | //| Copyright 2026, MasterOfPuppets | //| https://forge.mql5.io/masterofpuppets/mql5 | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Includes | //+------------------------------------------------------------------+ #include #include #include #include #include #include #include #include #include "Action.mqh" #include "ClosePositionModifier.mq5" #include "DefendModifier.mq5" #include "ProtectModifier.mq5" //+------------------------------------------------------------------+ //| Properties | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, MasterOfPuppets" #property description "Advanced Position Builder, Pyramider && Scalper Terminal" #property link "https://forge.mql5.io/masterofpuppets/mql5" #property version "1.00" //+------------------------------------------------------------------+ //| Inputs | //+------------------------------------------------------------------+ input bool ASYNC_MODE_ENABLED = false; input double CONTRACTS = 0.01; input int INDENT_SIZE = 134; input int LAST_PROFITS_SIZE = 10; input bool SOUND_ENABLED = true; input string SOUND_FILE_NAME = "ok.wav"; input double STOP_LOSS = 7.0; input double TAKE_PROFIT = 0.0; input group "Action keys" input ActionKey buyActionKey = B; // BUY input ActionKey buyLimitActionKey = I; // BUY_LIMIT input ActionKey buyProtectActionKey = O; // BUY_PROTECT input ActionKey buyPyramidActionKey = M; // BUY_PYRAMID input ActionKey buyStopActionKey = Y; // BUY_STOP input ActionKey closeAllPositionsActionKey = Q; // CLOSE_ALL_POSITIONS input ActionKey closeExpertPositionsActionKey = C; // CLOSE_EXPERT_POSITIONS input ActionKey defendAllPositionsActionKey = F; // DEFEND_ALL_POSITIONS input ActionKey defendExpertPositionsActionKey = D; // DEFEND_EXPERT_POSITIONS input ActionKey exitScalperActionKey = E; // EXIT_SCALPER input ActionKey lockActionKey = L; // LOCK input ActionKey reverseAllPositionsActionKey = A; // REVERSE_ALL_POSITIONS input ActionKey reverseExpertPositionsActionKey = R; // REVERSE_EXPERT_POSITIONS input ActionKey sellActionKey = S; // SELL input ActionKey sellLimitActionKey = T; // SELL_LIMIT input ActionKey sellProtectActionKey = X; // SELL_PROTECT input ActionKey sellPyramidActionKey = Z; // SELL_PYRAMID input ActionKey sellStopActionKey = P; // SELL_STOP //+------------------------------------------------------------------+ //| Constants | //+------------------------------------------------------------------+ const string INDENT_FORMAT = StringFormat("%%%is\n", INDENT_SIZE); const ulong MAGIC = 1234567; const string MESSAGE_BUY = "BUY "; const string MESSAGE_SELL = "SELL "; //+------------------------------------------------------------------+ //| Structs | //+------------------------------------------------------------------+ struct LastProfit { string message; double profit; string suffix; }; //+------------------------------------------------------------------+ //| Variables | //+------------------------------------------------------------------+ Actions actions; string actionsUsage; bool buyPyramid = false; double buyPyramidPriceOpen; string lastProfit; LastProfit lastProfits[]; CPositionInfo positionInfo; bool sellPyramid = false; double sellPyramidPriceOpen; CSymbolInfo symbolInfo; double totalLastProfit; CTrade trade; TradeContext tradeContext; //+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int32_t id, const long &lparam, const double &dparam, const string &sparam) { if(id != CHARTEVENT_KEYDOWN) { return; } switch(actions.GetAction(lparam)) { case Action::BUY: Buy(); break; case Action::BUY_LIMIT: BuyLimit(); break; case Action::BUY_PROTECT: BuyProtect(); break; case Action::BUY_PYRAMID: BuyPyramid(); break; case Action::BUY_STOP: BuyStop(); break; case Action::CLOSE_ALL_POSITIONS: CloseAllPositions(); break; case Action::CLOSE_EXPERT_POSITIONS: CloseExpertPositions(); break; case Action::DEFEND_ALL_POSITIONS: DefendAllPositions(); break; case Action::DEFEND_EXPERT_POSITIONS: DefendExpertPositions(); break; case Action::EXIT_SCALPER: ExpertRemove(); PlaySoundFile(SOUND_ENABLED, SOUND_FILE_NAME); break; case Action::LOCK: Buy(); Sell(); break; case Action::REVERSE_ALL_POSITIONS: ReverseAllPositions(); break; case Action::REVERSE_EXPERT_POSITIONS: ReverseExpertPositions(); break; case Action::SELL: Sell(); break; case Action::SELL_LIMIT: SellLimit(); break; case Action::SELL_PROTECT: SellProtect(); break; case Action::SELL_PYRAMID: SellPyramid(); break; case Action::SELL_STOP: SellStop(); break; } } //+------------------------------------------------------------------+ //| Buy function | //+------------------------------------------------------------------+ void Buy() { PositionOpen(Action::BUY); } //+------------------------------------------------------------------+ //| Buy limit function | //+------------------------------------------------------------------+ void BuyLimit() { OrderOpen(Action::BUY_LIMIT); } //+------------------------------------------------------------------+ //| Buy protect function | //+------------------------------------------------------------------+ void BuyProtect() { ProtectModifier protectModifier(GetPointer(tradeContext)); Modify(EnumToString(Action::BUY_PROTECT), &protectModifier); Buy(); } //+------------------------------------------------------------------+ //| Buy pyramid function | //+------------------------------------------------------------------+ void BuyPyramid() { buyPyramid = true; PositionOpen(Action::BUY); } //+------------------------------------------------------------------+ //| Buy stop function | //+------------------------------------------------------------------+ void BuyStop() { OrderOpen(Action::BUY_STOP); } //+------------------------------------------------------------------+ //| Add last profit function | //+------------------------------------------------------------------+ void AddLastProfit(const LastProfit &lastProfitStruct) { if(lastProfits.Size() == LAST_PROFITS_SIZE) { ArrayRemove(lastProfits, 0, 1); } ArrayResize(lastProfits, lastProfits.Size() + 1); lastProfits[lastProfits.Size() - 1] = lastProfitStruct; } //+------------------------------------------------------------------+ //| Calculate total last profit function | //+------------------------------------------------------------------+ void CalculateTotalLastProfit() { totalLastProfit = 0.0; for(uint i = 0; i < lastProfits.Size(); i++) { totalLastProfit += lastProfits[i].profit; } } //+------------------------------------------------------------------+ //| Set last profit function | //+------------------------------------------------------------------+ void SetLastProfit() { lastProfit = "\n"; for(uint i = 0; i < lastProfits.Size(); i++) { lastProfit += StringFormat(INDENT_FORMAT, lastProfits[i].message + lastProfits[i].suffix + StringFormat("%-7.2f", lastProfits[i].profit)); } lastProfit += StringFormat(INDENT_FORMAT, DoubleToString(totalLastProfit, 2)); } //+------------------------------------------------------------------+ //| Close all positions function | //+------------------------------------------------------------------+ void CloseAllPositions() { ClosePositionModifier closePositionModifier(GetPointer(tradeContext)); Modify(EnumToString(Action::CLOSE_ALL_POSITIONS), &closePositionModifier); } //+------------------------------------------------------------------+ //| Close expert positions function | //+------------------------------------------------------------------+ void CloseExpertPositions() { ClosePositionModifier closePositionModifier(GetPointer(tradeContext), true); Modify(EnumToString(Action::CLOSE_EXPERT_POSITIONS), &closePositionModifier); } //+------------------------------------------------------------------+ //| Defend all positions function | //+------------------------------------------------------------------+ void DefendAllPositions() { DefendModifier defendModifier(GetPointer(tradeContext)); Modify(EnumToString(Action::DEFEND_ALL_POSITIONS), &defendModifier); } //+------------------------------------------------------------------+ //| Defend expert positions function | //+------------------------------------------------------------------+ void DefendExpertPositions() { DefendModifier defendModifier(GetPointer(tradeContext), true); Modify(EnumToString(Action::DEFEND_EXPERT_POSITIONS), &defendModifier); } //+------------------------------------------------------------------+ //| Modify function | //+------------------------------------------------------------------+ void Modify(const string message, Modifier* modifier) { Print(message); ModifyPositions(modifier); PlaySoundFile(SOUND_ENABLED, SOUND_FILE_NAME); } //+------------------------------------------------------------------+ //| Modify positions function | //+------------------------------------------------------------------+ void ModifyPositions(Modifier* modifier) { for(int i = PositionsTotal() - 1; i >= 0; i--) { if(positionInfo.SelectByIndex(i)) { modifier.Modify(); } } } //+------------------------------------------------------------------+ //| Reverse all positions function | //+------------------------------------------------------------------+ void ReverseAllPositions() { ReversePositions(true); } //+------------------------------------------------------------------+ //| Reverse expert positions function | //+------------------------------------------------------------------+ void ReverseExpertPositions() { ReversePositions(false); } //+------------------------------------------------------------------+ //| Reverse positions function | //+------------------------------------------------------------------+ void ReversePositions(const bool allPositions) { Action action; if(allPositions) { action = Action::REVERSE_ALL_POSITIONS; } else { action = Action::REVERSE_EXPERT_POSITIONS; } Print(EnumToString(action)); if(positionInfo.SelectByIndex(0)) { switch(positionInfo.PositionType()) { case ENUM_POSITION_TYPE::POSITION_TYPE_BUY: action = Action::SELL; break; case ENUM_POSITION_TYPE::POSITION_TYPE_SELL: action = Action::BUY; break; } } if(allPositions) { CloseAllPositions(); } else { CloseExpertPositions(); } if(action != Action::UNKNOWN) { PositionOpen(action); } } //+------------------------------------------------------------------+ //| Sell function | //+------------------------------------------------------------------+ void Sell() { PositionOpen(Action::SELL); } //+------------------------------------------------------------------+ //| Sell limit function | //+------------------------------------------------------------------+ void SellLimit() { OrderOpen(Action::SELL_LIMIT); } //+------------------------------------------------------------------+ //| Sell protect function | //+------------------------------------------------------------------+ void SellProtect() { ProtectModifier protectModifier(GetPointer(tradeContext)); Modify(EnumToString(Action::SELL_PROTECT), &protectModifier); Sell(); } //+------------------------------------------------------------------+ //| Sell pyramid function | //+------------------------------------------------------------------+ void SellPyramid() { sellPyramid = true; PositionOpen(Action::SELL); } //+------------------------------------------------------------------+ //| Sell stop function | //+------------------------------------------------------------------+ void SellStop() { OrderOpen(Action::SELL_STOP); } //+------------------------------------------------------------------+ //| Position open function | //+------------------------------------------------------------------+ void PositionOpen(const int action) { ENUM_ORDER_TYPE orderType; double price; double deltaStopLoss; double deltaTakeProfit; symbolInfo.RefreshRates(); switch(action) { case Action::BUY: orderType = ENUM_ORDER_TYPE::ORDER_TYPE_BUY; price = symbolInfo.Ask(); buyPyramidPriceOpen = price; deltaStopLoss = -STOP_LOSS; deltaTakeProfit = TAKE_PROFIT; break; case Action::SELL: orderType = ENUM_ORDER_TYPE::ORDER_TYPE_SELL; price = symbolInfo.Bid(); sellPyramidPriceOpen = price; deltaStopLoss = STOP_LOSS; deltaTakeProfit = -TAKE_PROFIT; break; default: return; } Print(EnumToString((Action) action)); double stopLoss = IsZero(STOP_LOSS) ? 0.0 : symbolInfo.NormalizePrice(price + deltaStopLoss); double takeProfit = IsZero(TAKE_PROFIT) ? 0.0 : symbolInfo.NormalizePrice(price + deltaTakeProfit); trade.PositionOpen(_Symbol, orderType, CONTRACTS, price, stopLoss, takeProfit); PlaySoundFile(SOUND_ENABLED, SOUND_FILE_NAME); } //+------------------------------------------------------------------+ //| Order open function | //+------------------------------------------------------------------+ void OrderOpen(const int action) { ENUM_ORDER_TYPE orderType; double price; double deltaStopLoss; double deltaTakeProfit; symbolInfo.RefreshRates(); switch(action) { case Action::BUY_LIMIT: orderType = ENUM_ORDER_TYPE::ORDER_TYPE_BUY_LIMIT; price = symbolInfo.Ask() - STOP_LOSS; deltaStopLoss = -STOP_LOSS; deltaTakeProfit = TAKE_PROFIT; break; case Action::BUY_STOP: orderType = ENUM_ORDER_TYPE::ORDER_TYPE_BUY_STOP; price = symbolInfo.Ask() + STOP_LOSS; deltaStopLoss = -STOP_LOSS; deltaTakeProfit = TAKE_PROFIT; break; case Action::SELL_LIMIT: orderType = ENUM_ORDER_TYPE::ORDER_TYPE_SELL_LIMIT; price = symbolInfo.Bid() + STOP_LOSS; deltaStopLoss = STOP_LOSS; deltaTakeProfit = -TAKE_PROFIT; break; case Action::SELL_STOP: orderType = ENUM_ORDER_TYPE::ORDER_TYPE_SELL_STOP; price = symbolInfo.Bid() - STOP_LOSS; deltaStopLoss = STOP_LOSS; deltaTakeProfit = -TAKE_PROFIT; break; default: return; } Print(EnumToString((Action) action)); double stopLoss = IsZero(STOP_LOSS) ? 0.0 : symbolInfo.NormalizePrice(price + deltaStopLoss); double takeProfit = IsZero(TAKE_PROFIT) ? 0.0 : symbolInfo.NormalizePrice(price + deltaTakeProfit); trade.OrderOpen(_Symbol, orderType, CONTRACTS, price, price, stopLoss, takeProfit); PlaySoundFile(SOUND_ENABLED, SOUND_FILE_NAME); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { actions.Clear(); EventKillTimer(); } //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { if(!symbolInfo.Name(_Symbol)) { PlaySoundFile(SOUND_ENABLED, SOUND_FILE_NAME); return(INIT_FAILED); } InitActions(); if(!IsKeyMappingCorrect()) { PlaySoundFile(SOUND_ENABLED, SOUND_FILE_NAME); return(INIT_PARAMETERS_INCORRECT); } EventSetTimer(1); trade.SetAsyncMode(ASYNC_MODE_ENABLED); trade.SetExpertMagicNumber(MAGIC); tradeContext.Init(positionInfo, symbolInfo, trade); InitLastProfits(); InitPositions(); actionsUsage = actions.Usage(); Print(actionsUsage); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Actions initialization function | //+------------------------------------------------------------------+ void InitActions() { actions.AddAction(buyActionKey, Action::BUY); actions.AddAction(buyLimitActionKey, Action::BUY_LIMIT); actions.AddAction(buyProtectActionKey, Action::BUY_PROTECT); actions.AddAction(buyPyramidActionKey, Action::BUY_PYRAMID); actions.AddAction(buyStopActionKey, Action::BUY_STOP); actions.AddAction(closeAllPositionsActionKey, Action::CLOSE_ALL_POSITIONS); actions.AddAction(closeExpertPositionsActionKey, Action::CLOSE_EXPERT_POSITIONS); actions.AddAction(defendAllPositionsActionKey, Action::DEFEND_ALL_POSITIONS); actions.AddAction(defendExpertPositionsActionKey, Action::DEFEND_EXPERT_POSITIONS); actions.AddAction(exitScalperActionKey, Action::EXIT_SCALPER); actions.AddAction(lockActionKey, Action::LOCK); actions.AddAction(reverseAllPositionsActionKey, Action::REVERSE_ALL_POSITIONS); actions.AddAction(reverseExpertPositionsActionKey, Action::REVERSE_EXPERT_POSITIONS); actions.AddAction(sellActionKey, Action::SELL); actions.AddAction(sellLimitActionKey, Action::SELL_LIMIT); actions.AddAction(sellProtectActionKey, Action::SELL_PROTECT); actions.AddAction(sellPyramidActionKey, Action::SELL_PYRAMID); actions.AddAction(sellStopActionKey, Action::SELL_STOP); } //+------------------------------------------------------------------+ //| Last profits initialization function | //+------------------------------------------------------------------+ void InitLastProfits() { ArrayFree(lastProfits); datetime now = TimeCurrent(); datetime yesterday = now - 86400; // PERIOD_D1 * 60 HistorySelect(yesterday, now); int lastProfitIndex = 0; LastProfit historyLastProfits[]; for(int i = HistoryDealsTotal() - 1; i >= 0; i--) { if(lastProfitIndex >= LAST_PROFITS_SIZE) { break; } ulong dealTicket = HistoryDealGetTicket(i); double dealProfit = HistoryDealGetDouble(dealTicket, DEAL_PROFIT); if(IsZero(dealProfit)) { continue; } long dealReason = HistoryDealGetInteger(dealTicket, DEAL_REASON); if(IsStandardDealReason((int) dealReason)) { LastProfit lastProfitStruct; lastProfitStruct.message = GetMessage(dealTicket); lastProfitStruct.profit = dealProfit; lastProfitStruct.suffix = GetSuffix(dealReason); ArrayResize(historyLastProfits, historyLastProfits.Size() + 1); historyLastProfits[lastProfitIndex] = lastProfitStruct; lastProfitIndex++; } } for(int i = (int) historyLastProfits.Size() - 1; i >= 0; i--) { AddLastProfit(historyLastProfits[i]); } CalculateTotalLastProfit(); SetLastProfit(); } //+------------------------------------------------------------------+ //| Get message function | //+------------------------------------------------------------------+ string GetMessage(const ulong dealTicket) { switch((int) HistoryDealGetInteger(dealTicket, DEAL_TYPE)) { case ENUM_DEAL_TYPE::DEAL_TYPE_BUY: return MESSAGE_SELL; case ENUM_DEAL_TYPE::DEAL_TYPE_SELL: return MESSAGE_BUY; } return ""; } //+------------------------------------------------------------------+ //| Get suffix function | //+------------------------------------------------------------------+ string GetSuffix(const long dealReason) { if(dealReason == DEAL_REASON_SL) { return "(stop loss) "; } if(dealReason == DEAL_REASON_TP) { return "(take profit) "; } return ""; } //+------------------------------------------------------------------+ //| Positions initialization function | //+------------------------------------------------------------------+ void InitPositions() { ulong allPositionsTickets[]; int positionsTotal = PositionsTotal(); Print("positionsTotal: ", positionsTotal); ArrayFree(allPositionsTickets); ArrayResize(allPositionsTickets, positionsTotal); for(int i = 0; i < positionsTotal; i++) { if(positionInfo.SelectByIndex(i)) { allPositionsTickets[i] = positionInfo.Ticket(); } } ArrayPrint(allPositionsTickets); } //+------------------------------------------------------------------+ //| Tick function | //+------------------------------------------------------------------+ void OnTick() { if(!(buyPyramid || sellPyramid)) { return; } symbolInfo.RefreshRates(); if(buyPyramid && (symbolInfo.Ask() > buyPyramidPriceOpen + STOP_LOSS)) { DefendAllPositions(); PositionOpen(Action::BUY); } if(sellPyramid && (symbolInfo.Bid() < sellPyramidPriceOpen - STOP_LOSS)) { DefendAllPositions(); PositionOpen(Action::SELL); } } //+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer() { string comment = StringFormat(INDENT_FORMAT, TimeToString(TimeLocal(), TIME_MINUTES | TIME_SECONDS)); comment += lastProfit; comment += actionsUsage; Comment(comment); } //+------------------------------------------------------------------+ //| Trade transaction function | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result) { if(trans.type != TRADE_TRANSACTION_DEAL_ADD) { return; } InitLastProfits(); PlaySoundFile(SOUND_ENABLED, SOUND_FILE_NAME); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| ToDo | //| - error processing | //| - objects | //+------------------------------------------------------------------+