349 lines
23 KiB
MQL5
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();
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|