mql5/Experts/Scalper.mq5

350 linhas
23 KiB
MQL5

2026-01-29 14:08:04 +03:00
<EFBFBD><EFBFBD>//+------------------------------------------------------------------+
//| Scalper.mq5 |
2026-01-29 14:13:36 +03:00
//| Copyright 2026, MasterOfPuppets |
//| https://forge.mql5.io/masterofpuppets/mql5 |
2026-01-29 14:08:04 +03:00
//+------------------------------------------------------------------+
2026-02-02 19:25:27 +03:00
#include <Generic\LinkedList.mqh>
2026-01-29 14:08:04 +03:00
#include <Trade\PositionInfo.mqh>
#include <Trade\SymbolInfo.mqh>
#include <Trade\Trade.mqh>
2026-01-29 14:13:36 +03:00
#property copyright "Copyright 2026, MasterOfPuppets"
#property link "https://forge.mql5.io/masterofpuppets/mql5"
2026-01-29 14:08:04 +03:00
#property version "1.00"
2026-01-30 16:54:59 +03:00
//+------------------------------------------------------------------+
2026-02-01 00:15:25 +03:00
//| Modifier interface |
2026-01-30 16:54:59 +03:00
//+------------------------------------------------------------------+
2026-02-01 00:15:25 +03:00
interface Modifier
2026-01-30 16:54:59 +03:00
{
2026-02-01 00:15:25 +03:00
void Modify();
2026-01-30 16:54:59 +03:00
};
//+------------------------------------------------------------------+
//| Key to Action enum |
//| ASCII codes for keys |
//| https://www.w3.org/2002/09/tests/keys.html |
//+------------------------------------------------------------------+
2026-01-29 14:08:04 +03:00
enum Action
{
ALTERNATE = 65, // a
2026-01-30 17:31:16 +03:00
BUY = 66, // b
CLOSE = 67, // c
DEFEND = 68, // d
EXIT = 69, // e
2026-02-01 01:22:13 +03:00
QUIT = 81, // q
2026-01-30 17:31:16 +03:00
SELL = 83, // s
UNKNOWN = -1
2026-01-29 14:08:04 +03:00
};
const double CONTRACTS = 0.01;
2026-02-02 19:25:27 +03:00
const int LAST_PROFITS_SIZE = 10;
2026-02-03 02:37:31 +03:00
const string INDENT_FORMAT = "%96s\n";
2026-02-01 01:22:13 +03:00
const ulong MAGIC = 1234567;
2026-02-03 00:27:01 +03:00
const string MESSAGE_BUY = "BUY ";
const string MESSAGE_SELL = "SELL ";
2026-02-01 01:22:13 +03:00
const string SOUND_FILE_NAME = "ok.wav";
const double STOP_LOSS = 5;
2026-01-29 14:08:04 +03:00
CPositionInfo positionInfo;
CSymbolInfo symbolInfo;
CTrade trade;
2026-02-02 19:25:27 +03:00
CLinkedList<string> lastProfits;
string lastProfit = "";
2026-01-30 20:35:43 +03:00
2026-01-29 14:08:04 +03:00
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
if(!symbolInfo.Name(_Symbol))
{
return(INIT_FAILED);
}
2026-01-30 19:53:25 +03:00
EventSetTimer(1);
2026-02-01 01:22:13 +03:00
trade.SetExpertMagicNumber(MAGIC);
2026-01-29 14:08:04 +03:00
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
2026-01-30 19:53:25 +03:00
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
EventKillTimer();
}
//+------------------------------------------------------------------+
//| Timer function |
//+------------------------------------------------------------------+
void OnTimer()
{
2026-01-30 20:35:43 +03:00
string comment = StringFormat(INDENT_FORMAT, TimeToString(TimeLocal(), TIME_MINUTES | TIME_SECONDS));
2026-02-02 19:25:27 +03:00
comment += lastProfit;
2026-01-30 20:35:43 +03:00
Comment(comment);
2026-01-30 19:53:25 +03:00
}
//+------------------------------------------------------------------+
2026-02-03 00:27:01 +03:00
//| 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);
2026-02-03 03:16:12 +03:00
string sl_suffix = "(stop loss) ";
2026-02-03 00:27:01 +03:00
string message = "";
switch((int) HistoryDealGetInteger(dealTicket, DEAL_TYPE))
{
case ENUM_DEAL_TYPE::DEAL_TYPE_BUY:
2026-02-03 02:37:31 +03:00
message = MESSAGE_SELL + sl_suffix;
2026-02-03 00:27:01 +03:00
break;
case ENUM_DEAL_TYPE::DEAL_TYPE_SELL:
2026-02-03 02:37:31 +03:00
message = MESSAGE_BUY + sl_suffix;
2026-02-03 00:27:01 +03:00
break;
}
2026-02-03 02:24:07 +03:00
SetLastProfit(message, HistoryDealGetDouble(dealTicket, DEAL_PROFIT));
2026-02-03 03:06:14 +03:00
PlaySound(SOUND_FILE_NAME);
2026-02-03 00:27:01 +03:00
}
}
}
}
//+------------------------------------------------------------------+
2026-01-29 14:08:04 +03:00
//| 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:
2026-02-01 00:15:25 +03:00
CloseMagicPositions();
2026-01-29 14:08:04 +03:00
break;
2026-01-30 17:31:16 +03:00
case Action::DEFEND:
Defend();
break;
2026-01-29 14:08:04 +03:00
case Action::EXIT:
ExpertRemove();
2026-02-01 00:15:25 +03:00
PlaySound(SOUND_FILE_NAME);
2026-01-29 14:08:04 +03:00
break;
2026-02-01 01:22:13 +03:00
case Action::QUIT:
CloseAllPositions();
break;
2026-01-29 14:08:04 +03:00
}
}
//+------------------------------------------------------------------+
2026-01-30 16:54:59 +03:00
//| Alternate function |
2026-01-29 14:08:04 +03:00
//+------------------------------------------------------------------+
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;
}
}
2026-02-02 22:37:06 +03:00
CloseMagicPositions();
2026-01-29 14:08:04 +03:00
if(action != Action::UNKNOWN)
{
Deal(action);
}
}
//+------------------------------------------------------------------+
2026-01-30 16:54:59 +03:00
//| Deal function |
2026-01-29 14:08:04 +03:00
//+------------------------------------------------------------------+
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);
2026-02-01 00:15:25 +03:00
PlaySound(SOUND_FILE_NAME);
2026-01-29 14:08:04 +03:00
}
//+------------------------------------------------------------------+
2026-02-01 01:22:13 +03:00
//| ClosePositionModifier Modifier class |
2026-01-30 17:31:16 +03:00
//+------------------------------------------------------------------+
2026-02-01 01:22:13 +03:00
class ClosePositionModifier : public Modifier
2026-01-30 17:31:16 +03:00
{
2026-02-01 01:22:13 +03:00
private:
bool m_withMagic;
public:
ClosePositionModifier(bool withMagic = false)
{
m_withMagic = withMagic;
}
2026-02-01 00:15:25 +03:00
void Modify()
2026-01-30 17:31:16 +03:00
{
2026-02-01 01:22:13 +03:00
if(m_withMagic && positionInfo.Magic() != MAGIC)
{
return;
}
2026-01-30 17:31:16 +03:00
trade.PositionClose(positionInfo.Ticket());
2026-02-03 00:27:01 +03:00
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());
2026-02-02 19:25:27 +03:00
}
};
//+------------------------------------------------------------------+
//| Set last profit function |
//+------------------------------------------------------------------+
2026-02-03 00:27:01 +03:00
void SetLastProfit(string message, double profit)
2026-02-02 19:25:27 +03:00
{
2026-02-02 22:32:25 +03:00
lastProfit = "\n";
2026-02-03 00:27:01 +03:00
lastProfits.Add(StringFormat(INDENT_FORMAT, message + DoubleToString(profit, 2)));
2026-02-02 19:25:27 +03:00
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())
2026-02-02 18:24:04 +03:00
{
2026-02-02 19:25:27 +03:00
break;
2026-02-02 18:24:04 +03:00
}
2026-01-30 17:31:16 +03:00
}
2026-02-02 19:25:27 +03:00
}
2026-01-30 17:31:16 +03:00
//+------------------------------------------------------------------+
2026-02-01 00:15:25 +03:00
//| Close magic positions function |
2026-01-29 14:08:04 +03:00
//+------------------------------------------------------------------+
2026-02-01 00:15:25 +03:00
void CloseMagicPositions()
2026-01-29 14:08:04 +03:00
{
2026-02-01 01:22:13 +03:00
ClosePositionModifier closeMagicPositionModifier(true);
2026-02-01 01:31:24 +03:00
Modify("CLOSE", &closeMagicPositionModifier);
2026-02-01 01:22:13 +03:00
}
//+------------------------------------------------------------------+
//| Close all positions function |
//+------------------------------------------------------------------+
void CloseAllPositions()
{
ClosePositionModifier closePositionModifier;
2026-02-01 01:31:24 +03:00
Modify("QUIT", &closePositionModifier);
2026-01-30 17:31:16 +03:00
}
//+------------------------------------------------------------------+
2026-02-01 00:15:25 +03:00
//| DefendModifier Modifier class |
2026-01-30 17:31:16 +03:00
//+------------------------------------------------------------------+
2026-02-01 00:15:25 +03:00
class DefendModifier : public Modifier
2026-01-30 17:31:16 +03:00
{
2026-02-01 00:15:25 +03:00
void Modify()
2026-01-30 17:31:16 +03:00
{
2026-02-01 01:22:13 +03:00
if(positionInfo.Magic() != MAGIC)
{
return;
}
2026-01-30 17:31:16 +03:00
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()
{
2026-02-01 00:15:25 +03:00
DefendModifier defendModifier;
2026-02-01 01:31:24 +03:00
Modify("DEFEND", &defendModifier);
}
//+------------------------------------------------------------------+
//| Modify function |
//+------------------------------------------------------------------+
void Modify(string message, Modifier* modifier)
{
Print(message);
ModifyPositions(modifier);
2026-02-01 00:15:25 +03:00
PlaySound(SOUND_FILE_NAME);
2026-01-30 16:54:59 +03:00
}
//+------------------------------------------------------------------+
2026-02-01 00:15:25 +03:00
//| Modify positions function |
2026-01-30 16:54:59 +03:00
//+------------------------------------------------------------------+
2026-02-01 01:31:24 +03:00
void ModifyPositions(Modifier* modifier)
2026-01-30 16:54:59 +03:00
{
2026-01-29 14:41:52 +03:00
for(int i = PositionsTotal() - 1; i >= 0; i--)
2026-01-29 14:08:04 +03:00
{
if(positionInfo.SelectByIndex(i))
{
2026-02-01 01:31:24 +03:00
modifier.Modify();
2026-01-29 14:08:04 +03:00
}
}
}
//+------------------------------------------------------------------+