//+------------------------------------------------------------------+ //| Scalper.mq5 | //| Copyright 2026, MasterOfPuppets | //| https://forge.mql5.io/masterofpuppets/mql5 | //+------------------------------------------------------------------+ #include #include #include #include #property copyright "Copyright 2026, MasterOfPuppets" #property link "https://forge.mql5.io/masterofpuppets/mql5" #property version "1.00" //+------------------------------------------------------------------+ //| Modifier interface | //+------------------------------------------------------------------+ interface Modifier { void Modify(); }; //+------------------------------------------------------------------+ //| Key to Action enum | //| ASCII codes for keys | //| https://www.w3.org/2002/09/tests/keys.html | //+------------------------------------------------------------------+ enum Action { ALTERNATE = 65, // a BUY = 66, // b CLOSE = 67, // c DEFEND = 68, // d EXIT = 69, // e QUIT = 81, // q SELL = 83, // s UNKNOWN = -1 }; const double CONTRACTS = 0.01; const int LAST_PROFITS_SIZE = 10; const string INDENT_FORMAT = "%96s\n"; const ulong MAGIC = 1234567; const string MESSAGE_BUY = "BUY "; const string MESSAGE_SELL = "SELL "; const string SOUND_FILE_NAME = "ok.wav"; const double STOP_LOSS = 5; CPositionInfo positionInfo; CSymbolInfo symbolInfo; CTrade trade; CLinkedList lastProfits; string lastProfit = ""; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { if(!symbolInfo.Name(_Symbol)) { return(INIT_FAILED); } EventSetTimer(1); trade.SetExpertMagicNumber(MAGIC); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { EventKillTimer(); } //+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer() { string comment = StringFormat(INDENT_FORMAT, TimeToString(TimeLocal(), TIME_MINUTES | TIME_SECONDS)); comment += lastProfit; Comment(comment); } //+------------------------------------------------------------------+ //| Trade transaction function | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result) { if(trans.type == TRADE_TRANSACTION_DEAL_ADD) { ulong dealTicket = trans.deal; if(HistoryDealSelect(dealTicket)) { long dealReason = HistoryDealGetInteger(dealTicket, DEAL_REASON); if(dealReason == DEAL_REASON_SL) { long dealReason = HistoryDealGetInteger(dealTicket, DEAL_TYPE); string sl_suffix = "(stop loss) "; string message = ""; switch((int) HistoryDealGetInteger(dealTicket, DEAL_TYPE)) { case ENUM_DEAL_TYPE::DEAL_TYPE_BUY: message = MESSAGE_SELL + sl_suffix; break; case ENUM_DEAL_TYPE::DEAL_TYPE_SELL: message = MESSAGE_BUY + sl_suffix; break; } SetLastProfit(message, HistoryDealGetDouble(dealTicket, DEAL_PROFIT)); PlaySound(SOUND_FILE_NAME); } } } } //+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int32_t id, const long &lparam, const double &dparam, const string &sparam) { if(id != CHARTEVENT_KEYDOWN) { return; } int action = (int) lparam; switch(action) { case Action::ALTERNATE: Alternate(); break; case Action::BUY: case Action::SELL: Deal(action); break; case Action::CLOSE: CloseMagicPositions(); break; case Action::DEFEND: Defend(); break; case Action::EXIT: ExpertRemove(); PlaySound(SOUND_FILE_NAME); break; case Action::QUIT: CloseAllPositions(); break; } } //+------------------------------------------------------------------+ //| Alternate function | //+------------------------------------------------------------------+ void Alternate() { Print("ALTERNATE"); Action action = Action::UNKNOWN; 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; } } CloseMagicPositions(); if(action != Action::UNKNOWN) { Deal(action); } } //+------------------------------------------------------------------+ //| Deal function | //+------------------------------------------------------------------+ void Deal(int action) { string message; ENUM_ORDER_TYPE orderType; double price; double stopLoss; symbolInfo.RefreshRates(); switch(action) { case Action::BUY: message = "BUY"; orderType = ENUM_ORDER_TYPE::ORDER_TYPE_BUY; price = symbolInfo.Ask(); stopLoss = symbolInfo.NormalizePrice(symbolInfo.Ask() - STOP_LOSS); break; case Action::SELL: message = "SELL"; orderType = ENUM_ORDER_TYPE::ORDER_TYPE_SELL; price = symbolInfo.Bid(); stopLoss = symbolInfo.NormalizePrice(symbolInfo.Bid() + STOP_LOSS); break; default: return; } Print(message); trade.PositionOpen(_Symbol, orderType, CONTRACTS, price, stopLoss, 0.0); PlaySound(SOUND_FILE_NAME); } //+------------------------------------------------------------------+ //| ClosePositionModifier Modifier class | //+------------------------------------------------------------------+ class ClosePositionModifier : public Modifier { private: bool m_withMagic; public: ClosePositionModifier(bool withMagic = false) { m_withMagic = withMagic; } void Modify() { if(m_withMagic && positionInfo.Magic() != MAGIC) { return; } trade.PositionClose(positionInfo.Ticket()); string message = ""; switch(positionInfo.PositionType()) { case ENUM_POSITION_TYPE::POSITION_TYPE_BUY: message = MESSAGE_BUY; break; case ENUM_POSITION_TYPE::POSITION_TYPE_SELL: message = MESSAGE_SELL; break; } SetLastProfit(message, positionInfo.Profit()); } }; //+------------------------------------------------------------------+ //| Set last profit function | //+------------------------------------------------------------------+ void SetLastProfit(string message, double profit) { lastProfit = "\n"; lastProfits.Add(StringFormat(INDENT_FORMAT, message + DoubleToString(profit, 2))); if(lastProfits.Count() > LAST_PROFITS_SIZE) { lastProfits.RemoveFirst(); } CLinkedListNode* lastProfitNode = lastProfits.First(); while(lastProfitNode != NULL) { lastProfit += lastProfitNode.Value(); lastProfitNode = lastProfitNode.Next(); if(lastProfitNode == lastProfits.First()) { break; } } } //+------------------------------------------------------------------+ //| Close magic positions function | //+------------------------------------------------------------------+ void CloseMagicPositions() { ClosePositionModifier closeMagicPositionModifier(true); Modify("CLOSE", &closeMagicPositionModifier); } //+------------------------------------------------------------------+ //| Close all positions function | //+------------------------------------------------------------------+ void CloseAllPositions() { ClosePositionModifier closePositionModifier; Modify("QUIT", &closePositionModifier); } //+------------------------------------------------------------------+ //| DefendModifier Modifier class | //+------------------------------------------------------------------+ class DefendModifier : public Modifier { void Modify() { if(positionInfo.Magic() != MAGIC) { return; } double deltaStopLoss = 0.0; switch(positionInfo.PositionType()) { case ENUM_POSITION_TYPE::POSITION_TYPE_BUY: deltaStopLoss = STOP_LOSS; break; case ENUM_POSITION_TYPE::POSITION_TYPE_SELL: deltaStopLoss = -STOP_LOSS; break; } trade.PositionModify(positionInfo.Ticket(), symbolInfo.NormalizePrice(positionInfo.StopLoss() + deltaStopLoss), positionInfo.TakeProfit()); } }; //+------------------------------------------------------------------+ //| Defend function | //+------------------------------------------------------------------+ void Defend() { DefendModifier defendModifier; Modify("DEFEND", &defendModifier); } //+------------------------------------------------------------------+ //| Modify function | //+------------------------------------------------------------------+ void Modify(string message, Modifier* modifier) { Print(message); ModifyPositions(modifier); PlaySound(SOUND_FILE_NAME); } //+------------------------------------------------------------------+ //| Modify positions function | //+------------------------------------------------------------------+ void ModifyPositions(Modifier* modifier) { for(int i = PositionsTotal() - 1; i >= 0; i--) { if(positionInfo.SelectByIndex(i)) { modifier.Modify(); } } } //+------------------------------------------------------------------+