mql5/Experts/Scalper.mq5
2026-02-03 03:16:12 +03:00

349 lines
23 KiB
MQL5

//+------------------------------------------------------------------+
//| Scalper.mq5 |
//| Copyright 2026, MasterOfPuppets |
//| https://forge.mql5.io/masterofpuppets/mql5 |
//+------------------------------------------------------------------+
#include <Generic\LinkedList.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\SymbolInfo.mqh>
#include <Trade\Trade.mqh>
#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<string> 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<string>* 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();
}
}
}
//+------------------------------------------------------------------+