1
0
Fork 0
mql5/Experts/MasterOfPuppets/MasterOfPuppets.mq5
2026-03-03 01:47:23 +03:00

698 Zeilen
25 KiB
MQL5

//+------------------------------------------------------------------+
//| MasterOfPuppets.mq5 |
//| Copyright 2026, MasterOfPuppets |
//| https://forge.mql5.io/masterofpuppets/mql5 |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Includes |
//+------------------------------------------------------------------+
#include <Trade\PositionInfo.mqh>
#include <Trade\SymbolInfo.mqh>
#include <Trade\Trade.mqh>
#include <MasterOfPuppetsLib\ActionKey.mqh>
#include <MasterOfPuppetsLib\Actions.mqh>
#include <MasterOfPuppetsLib\DuplicateFinder.mqh>
#include <MasterOfPuppetsLib\TradeContext.mqh>
#include <MasterOfPuppetsLib\Utils.mqh>
#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<Action> 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 |
//+------------------------------------------------------------------+