mql5/Experts/Scalper.mq5

767 lines
54 KiB
MQL5
Raw Permalink Normal View History

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-06 13:57:35 +03:00
#include <Generic\LinkedList.mqh>
2026-02-06 09:29:10 +03:00
#include <Generic\HashMap.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-06 13:57:35 +03:00
//| Classes |
//+------------------------------------------------------------------+
class Indexes
{
public:
int data[];
Indexes() { ArrayFree(data); }
~Indexes() { ArrayFree(data); }
};
//+------------------------------------------------------------------+
//| Enums |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
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
{
2026-02-06 03:48:57 +03:00
BUY = 'B',
CLOSE_ALL_POSITIONS = 'Q',
CLOSE_EXPERT_POSITIONS = 'C',
DEFEND_ALL_POSITIONS = 'F',
DEFEND_EXPERT_POSITIONS = 'D',
EXIT_SCALPER = 'E',
REVERSE_ALL_POSITIONS = 'R',
REVERSE_EXPERT_POSITIONS = 'A',
SELL = 'S',
UNKNOWN = -1
2026-01-29 14:08:04 +03:00
};
2026-02-06 09:29:10 +03:00
enum ActionKey
{
A = 'A',
B = 'B',
C = 'C',
D = 'D',
E = 'E',
F = 'F',
G = 'G',
H = 'H',
I = 'I',
J = 'J',
K = 'K',
L = 'L',
M = 'M',
N = 'N',
O = 'O',
P = 'P',
Q = 'Q',
R = 'R',
S = 'S',
T = 'T',
U = 'U',
V = 'V',
W = 'W',
X = 'X',
Y = 'Y',
Z = 'Z',
_1 = '1',
_2 = '2',
_3 = '3',
_4 = '4',
_5 = '5',
_6 = '6',
_7 = '7',
_8 = '8',
_9 = '9',
_0 = '0'
};
//+------------------------------------------------------------------+
//| Inputs |
//+------------------------------------------------------------------+
input double CONTRACTS = 0.01;
input int INDENT_SIZE = 144;
input int LAST_PROFITS_SIZE = 10;
input double STOP_LOSS = 7.0;
input string SOUND_FILE_NAME = "ok.wav";
input double TAKE_PROFIT = 0.0;
input group "Action keys"
2026-02-06 13:57:35 +03:00
input ActionKey buyActionKey = B; // BUY
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 reverseAllPositionsActionKey = R; // REVERSE_ALL_POSITIONS
input ActionKey reverseExpertPositionsActionKey = A; // REVERSE_EXPERT_POSITIONS
input ActionKey sellActionKey = S; // SELL
2026-02-06 09:29:10 +03:00
//+------------------------------------------------------------------+
//| Constants |
//+------------------------------------------------------------------+
const string INDENT_FORMAT = StringFormat("%%%is\n", INDENT_SIZE);
const ulong MAGIC = 1234567;
const string MESSAGE_BUY = "BUY ";
const string MESSAGE_SELL = "SELL ";
//+------------------------------------------------------------------+
//| Interfaces |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Modifier interface |
//+------------------------------------------------------------------+
interface Modifier
{
void Modify();
};
//+------------------------------------------------------------------+
//| Structs |
//+------------------------------------------------------------------+
2026-02-05 01:27:07 +03:00
struct LastProfit
{
string message;
double profit;
string suffix;
};
2026-02-03 21:29:24 +03:00
//+------------------------------------------------------------------+
//| Variables |
2026-02-03 21:04:20 +03:00
//+------------------------------------------------------------------+
2026-02-06 09:29:10 +03:00
CHashMap<long, int> actions;
CHashMap<long, string> actionNames;
2026-02-06 13:57:35 +03:00
CHashMap<int, Indexes*> keyToIndexesMap;
2026-02-05 01:27:07 +03:00
string lastProfit;
LastProfit lastProfits[];
2026-02-06 09:29:10 +03:00
CPositionInfo positionInfo;
CSymbolInfo symbolInfo;
2026-02-05 01:44:53 +03:00
double totalLastProfit;
2026-02-06 09:29:10 +03:00
CTrade trade;
2026-01-30 20:35:43 +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;
}
2026-02-06 09:29:10 +03:00
int action;
actions.TryGetValue(lparam, action);
2026-01-29 14:08:04 +03:00
switch(action)
{
case Action::BUY:
case Action::SELL:
Deal(action);
break;
2026-02-06 03:48:57 +03:00
case Action::CLOSE_ALL_POSITIONS:
CloseAllPositions();
break;
case Action::CLOSE_EXPERT_POSITIONS:
CloseExpertPositions();
2026-01-29 14:08:04 +03:00
break;
2026-02-06 03:48:57 +03:00
case Action::DEFEND_ALL_POSITIONS:
DefendAllPositions();
2026-01-30 17:31:16 +03:00
break;
2026-02-06 03:48:57 +03:00
case Action::DEFEND_EXPERT_POSITIONS:
DefendExpertPositions();
break;
case Action::EXIT_SCALPER:
2026-01-29 14:08:04 +03:00
ExpertRemove();
2026-02-01 00:15:25 +03:00
PlaySound(SOUND_FILE_NAME);
2026-01-29 14:08:04 +03:00
break;
2026-02-06 03:48:57 +03:00
case Action::REVERSE_ALL_POSITIONS:
ReverseAllPositions();
break;
case Action::REVERSE_EXPERT_POSITIONS:
ReverseExpertPositions();
2026-02-01 01:22:13 +03:00
break;
2026-01-29 14:08:04 +03:00
}
}
//+------------------------------------------------------------------+
2026-01-30 16:54:59 +03:00
//| Deal function |
2026-01-29 14:08:04 +03:00
//+------------------------------------------------------------------+
void Deal(int action)
{
ENUM_ORDER_TYPE orderType;
double price;
2026-02-03 21:04:20 +03:00
double deltaStopLoss;
2026-02-03 21:21:01 +03:00
double deltaTakeProfit;
2026-01-29 14:08:04 +03:00
symbolInfo.RefreshRates();
switch(action)
{
case Action::BUY:
orderType = ENUM_ORDER_TYPE::ORDER_TYPE_BUY;
price = symbolInfo.Ask();
2026-02-03 21:21:01 +03:00
deltaStopLoss = -STOP_LOSS;
deltaTakeProfit = TAKE_PROFIT;
2026-01-29 14:08:04 +03:00
break;
case Action::SELL:
orderType = ENUM_ORDER_TYPE::ORDER_TYPE_SELL;
price = symbolInfo.Bid();
2026-02-03 21:04:20 +03:00
deltaStopLoss = STOP_LOSS;
2026-02-03 21:21:01 +03:00
deltaTakeProfit = -TAKE_PROFIT;
2026-01-29 14:08:04 +03:00
break;
default:
return;
}
2026-02-06 03:48:57 +03:00
Print(EnumToString((Action) action));
2026-02-06 03:57:12 +03:00
double stopLoss = IsZero(STOP_LOSS) ? 0.0 : symbolInfo.NormalizePrice(price + deltaStopLoss);
double takeProfit = IsZero(TAKE_PROFIT) ? 0.0 : symbolInfo.NormalizePrice(price + deltaTakeProfit);
2026-02-03 21:21:01 +03:00
trade.PositionOpen(_Symbol, orderType, CONTRACTS, price, stopLoss, takeProfit);
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:
2026-02-06 03:48:57 +03:00
bool m_withExpert;
2026-02-01 01:22:13 +03:00
public:
2026-02-06 03:48:57 +03:00
ClosePositionModifier(bool withExpert = false)
2026-02-01 01:22:13 +03:00
{
2026-02-06 03:48:57 +03:00
m_withExpert = withExpert;
2026-02-01 01:22:13 +03:00
}
2026-02-01 00:15:25 +03:00
void Modify()
2026-01-30 17:31:16 +03:00
{
2026-02-06 03:48:57 +03:00
if(m_withExpert && positionInfo.Magic() != MAGIC)
2026-02-01 01:22:13 +03:00
{
return;
}
2026-01-30 17:31:16 +03:00
trade.PositionClose(positionInfo.Ticket());
2026-02-03 00:27:01 +03:00
2026-02-05 01:27:07 +03:00
string message;
2026-02-03 00:27:01 +03:00
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;
}
2026-02-05 01:27:07 +03:00
LastProfit lastProfitStruct;
lastProfitStruct.message = message;
lastProfitStruct.profit = positionInfo.Profit();
AddLastProfit(lastProfitStruct);
2026-02-05 01:44:53 +03:00
CalculateTotalLastProfit();
2026-02-05 01:27:07 +03:00
SetLastProfit();
2026-02-02 19:25:27 +03:00
}
};
//+------------------------------------------------------------------+
2026-02-05 01:27:07 +03:00
//| Add last profit function |
2026-02-02 19:25:27 +03:00
//+------------------------------------------------------------------+
2026-02-05 01:27:07 +03:00
void AddLastProfit(LastProfit &lastProfitStruct)
2026-02-02 19:25:27 +03:00
{
2026-02-05 01:27:07 +03:00
if(lastProfits.Size() == LAST_PROFITS_SIZE)
2026-02-02 19:25:27 +03:00
{
2026-02-05 01:27:07 +03:00
ArrayRemove(lastProfits, 0, 1);
2026-02-02 19:25:27 +03:00
}
2026-02-05 01:27:07 +03:00
ArrayResize(lastProfits, lastProfits.Size() + 1);
lastProfits[lastProfits.Size() - 1] = lastProfitStruct;
}
//+------------------------------------------------------------------+
2026-02-05 01:44:53 +03:00
//| Calculate total last profit function |
//+------------------------------------------------------------------+
void CalculateTotalLastProfit()
{
totalLastProfit = 0.0;
for(int i = 0; i < (int) lastProfits.Size(); i++)
{
totalLastProfit += lastProfits[i].profit;
}
}
//+------------------------------------------------------------------+
2026-02-05 01:27:07 +03:00
//| Set last profit function |
//+------------------------------------------------------------------+
void SetLastProfit()
{
lastProfit = "\n";
for(int i = 0; i < (int) lastProfits.Size(); i++)
2026-02-02 19:25:27 +03:00
{
2026-02-05 01:27:07 +03:00
lastProfit += StringFormat(INDENT_FORMAT,
lastProfits[i].message + lastProfits[i].suffix + DoubleToString(lastProfits[i].profit, 2));
2026-01-30 17:31:16 +03:00
}
2026-02-05 01:44:53 +03:00
lastProfit += StringFormat(INDENT_FORMAT, DoubleToString(totalLastProfit, 2));
2026-02-02 19:25:27 +03:00
}
2026-01-30 17:31:16 +03:00
//+------------------------------------------------------------------+
2026-02-06 03:48:57 +03:00
//| Close all positions function |
2026-01-29 14:08:04 +03:00
//+------------------------------------------------------------------+
2026-02-06 03:48:57 +03:00
void CloseAllPositions()
2026-01-29 14:08:04 +03:00
{
2026-02-06 03:48:57 +03:00
ClosePositionModifier closePositionModifier;
Modify(EnumToString(Action::CLOSE_ALL_POSITIONS), &closePositionModifier);
2026-02-01 01:22:13 +03:00
}
//+------------------------------------------------------------------+
2026-02-06 03:48:57 +03:00
//| Close expert positions function |
2026-02-01 01:22:13 +03:00
//+------------------------------------------------------------------+
2026-02-06 03:48:57 +03:00
void CloseExpertPositions()
2026-02-01 01:22:13 +03:00
{
2026-02-06 03:48:57 +03:00
ClosePositionModifier closePositionModifier(true);
Modify(EnumToString(Action::CLOSE_EXPERT_POSITIONS), &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-06 03:48:57 +03:00
private:
bool m_withExpert;
public:
DefendModifier(bool withExpert = false)
{
this.m_withExpert = withExpert;
}
2026-02-01 00:15:25 +03:00
void Modify()
2026-01-30 17:31:16 +03:00
{
2026-02-06 03:48:57 +03:00
if(m_withExpert && positionInfo.Magic() != MAGIC)
2026-02-01 01:22:13 +03:00
{
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());
}
};
//+------------------------------------------------------------------+
2026-02-06 03:48:57 +03:00
//| Defend all positions function |
2026-01-30 17:31:16 +03:00
//+------------------------------------------------------------------+
2026-02-06 03:48:57 +03:00
void DefendAllPositions()
2026-01-30 17:31:16 +03:00
{
2026-02-01 00:15:25 +03:00
DefendModifier defendModifier;
2026-02-06 03:48:57 +03:00
Modify(EnumToString(Action::DEFEND_ALL_POSITIONS), &defendModifier);
}
//+------------------------------------------------------------------+
//| Defend expert positions function |
//+------------------------------------------------------------------+
void DefendExpertPositions()
{
DefendModifier defendModifier(true);
Modify(EnumToString(Action::DEFEND_EXPERT_POSITIONS), &defendModifier);
2026-02-01 01:31:24 +03:00
}
//+------------------------------------------------------------------+
//| 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
}
}
}
//+------------------------------------------------------------------+
2026-02-06 03:48:57 +03:00
//| Reverse all positions function |
//+------------------------------------------------------------------+
void ReverseAllPositions()
{
ReversePositions(true);
}
//+------------------------------------------------------------------+
//| Reverse expert positions function |
//+------------------------------------------------------------------+
void ReverseExpertPositions()
{
ReversePositions(false);
}
//+------------------------------------------------------------------+
//| Reverse positions function |
//+------------------------------------------------------------------+
void ReversePositions(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)
{
Deal(action);
}
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
EventKillTimer();
}
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
if(!symbolInfo.Name(_Symbol))
{
return(INIT_FAILED);
}
2026-02-06 13:57:35 +03:00
if(!IsKeysCorrect())
{
return(INIT_PARAMETERS_INCORRECT);
}
EventSetTimer(1);
trade.SetExpertMagicNumber(MAGIC);
2026-02-06 09:29:10 +03:00
InitActions();
InitActionNames();
InitLastProfits();
2026-02-06 03:48:57 +03:00
Print(Usage());
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
2026-02-06 13:57:35 +03:00
//| Is keys correct function |
//+------------------------------------------------------------------+
bool IsKeysCorrect()
{
string names[9];
names[0] = EnumToString(Action::BUY);
names[1] = EnumToString(Action::CLOSE_ALL_POSITIONS);
names[2] = EnumToString(Action::CLOSE_EXPERT_POSITIONS);
names[3] = EnumToString(Action::DEFEND_ALL_POSITIONS);
names[4] = EnumToString(Action::DEFEND_EXPERT_POSITIONS);
names[5] = EnumToString(Action::EXIT_SCALPER);
names[6] = EnumToString(Action::REVERSE_ALL_POSITIONS);
names[7] = EnumToString(Action::REVERSE_EXPERT_POSITIONS);
names[8] = EnumToString(Action::SELL);
AddIndexesToMap(buyActionKey, 0);
AddIndexesToMap(closeAllPositionsActionKey, 1);
AddIndexesToMap(closeExpertPositionsActionKey, 2);
AddIndexesToMap(defendAllPositionsActionKey, 3);
AddIndexesToMap(defendExpertPositionsActionKey, 4);
AddIndexesToMap(exitScalperActionKey, 5);
AddIndexesToMap(reverseAllPositionsActionKey, 6);
AddIndexesToMap(reverseExpertPositionsActionKey, 7);
AddIndexesToMap(sellActionKey, 8);
string error;
int keys[];
Indexes* indexes[];
keyToIndexesMap.CopyTo(keys, indexes);
for(int i = 0; i < (int) keys.Size(); i++)
{
int key = keys[i];
if(indexes[i].data.Size() > 1)
{
string mappedActionNames;
for(int j = 0; j < (int) indexes[i].data.Size(); j++)
{
if(j > 0)
{
mappedActionNames += ", ";
}
mappedActionNames += names[indexes[i].data[j]];
}
error += StringFormat("Error: duplicated key [%s] for %s\n", CharToString((uchar) key), mappedActionNames);
}
}
ClearMemory();
if(error.Length() > 0)
{
Alert(error);
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Add indexes to map function |
//+------------------------------------------------------------------+
void AddIndexesToMap(int key, int index)
{
Indexes* indexes;
if(!keyToIndexesMap.TryGetValue(key, indexes))
{
indexes = new Indexes();
keyToIndexesMap.Add(key, indexes);
}
int size = (int) indexes.data.Size();
ArrayResize(indexes.data, size + 1);
indexes.data[size] = index;
}
//+------------------------------------------------------------------+
//| Clear memory function |
//+------------------------------------------------------------------+
void ClearMemory()
{
if(keyToIndexesMap.Count() > 0)
{
int keys[];
Indexes* indexes[];
keyToIndexesMap.CopyTo(keys, indexes);
for(int i = 0; i < (int) indexes.Size(); i++)
{
if(CheckPointer(indexes[i]) == POINTER_DYNAMIC)
{
delete indexes[i];
}
}
}
keyToIndexesMap.Clear();
}
//+------------------------------------------------------------------+
2026-02-06 09:29:10 +03:00
//| Actions initialization function |
//+------------------------------------------------------------------+
void InitActions()
{
actions.Add(buyActionKey, Action::BUY);
2026-02-06 13:57:35 +03:00
actions.Add(closeAllPositionsActionKey, Action::CLOSE_ALL_POSITIONS);
actions.Add(closeExpertPositionsActionKey, Action::CLOSE_EXPERT_POSITIONS);
actions.Add(defendAllPositionsActionKey, Action::DEFEND_ALL_POSITIONS);
actions.Add(defendExpertPositionsActionKey, Action::DEFEND_EXPERT_POSITIONS);
actions.Add(exitScalperActionKey, Action::EXIT_SCALPER);
actions.Add(reverseAllPositionsActionKey, Action::REVERSE_ALL_POSITIONS);
actions.Add(reverseExpertPositionsActionKey, Action::REVERSE_EXPERT_POSITIONS);
actions.Add(sellActionKey, Action::SELL);
2026-02-06 09:29:10 +03:00
}
//+------------------------------------------------------------------+
//| Action names initialization function |
//+------------------------------------------------------------------+
void InitActionNames()
{
actionNames.Add(buyActionKey, EnumToString(Action::BUY));
2026-02-06 13:57:35 +03:00
actionNames.Add(closeAllPositionsActionKey, EnumToString(Action::CLOSE_ALL_POSITIONS));
actionNames.Add(closeExpertPositionsActionKey, EnumToString(Action::CLOSE_EXPERT_POSITIONS));
actionNames.Add(defendAllPositionsActionKey, EnumToString(Action::DEFEND_ALL_POSITIONS));
actionNames.Add(defendExpertPositionsActionKey, EnumToString(Action::DEFEND_EXPERT_POSITIONS));
actionNames.Add(exitScalperActionKey, EnumToString(Action::EXIT_SCALPER));
actionNames.Add(reverseAllPositionsActionKey, EnumToString(Action::REVERSE_ALL_POSITIONS));
actionNames.Add(reverseExpertPositionsActionKey, EnumToString(Action::REVERSE_EXPERT_POSITIONS));
actionNames.Add(sellActionKey, EnumToString(Action::SELL));
2026-02-06 09:29:10 +03:00
}
//+------------------------------------------------------------------+
//| Last profits initialization function |
//+------------------------------------------------------------------+
void InitLastProfits()
{
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);
2026-02-06 03:57:12 +03:00
if(IsZero(dealProfit))
{
continue;
}
long dealReason = HistoryDealGetInteger(dealTicket, DEAL_REASON);
2026-02-10 14:55:36 +03:00
if(dealReason == DEAL_REASON_CLIENT || dealReason == DEAL_REASON_EXPERT || DEAL_REASON_MOBILE
|| dealReason == DEAL_REASON_SL || dealReason == DEAL_REASON_TP || DEAL_REASON_WEB)
{
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(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(long dealReason)
{
if(dealReason == DEAL_REASON_SL)
{
return "(stop loss) ";
}
if(dealReason == DEAL_REASON_TP)
{
return "(take profit) ";
}
return "";
}
//+------------------------------------------------------------------+
//| Timer function |
//+------------------------------------------------------------------+
void OnTimer()
{
string comment = StringFormat(INDENT_FORMAT, TimeToString(TimeLocal(), TIME_MINUTES | TIME_SECONDS));
comment += lastProfit;
2026-02-06 03:48:57 +03:00
comment += Usage();
Comment(comment);
}
//+------------------------------------------------------------------+
//| Trade transaction function |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction &trans,
const MqlTradeRequest &request,
const MqlTradeResult &result)
{
if(trans.type != TRADE_TRANSACTION_DEAL_ADD)
{
return;
}
ulong dealTicket = trans.deal;
if(!HistoryDealSelect(dealTicket))
{
return;
}
long dealReason = HistoryDealGetInteger(dealTicket, DEAL_REASON);
if(dealReason == DEAL_REASON_SL || dealReason == DEAL_REASON_TP)
{
LastProfit lastProfitStruct;
lastProfitStruct.message = GetMessage(dealTicket);
lastProfitStruct.profit = HistoryDealGetDouble(dealTicket, DEAL_PROFIT);
lastProfitStruct.suffix = GetSuffix(dealReason);
AddLastProfit(lastProfitStruct);
CalculateTotalLastProfit();
SetLastProfit();
}
2026-02-11 03:50:34 +03:00
PlaySound(SOUND_FILE_NAME);
}
2026-02-06 03:48:57 +03:00
//+------------------------------------------------------------------+
//| Usage function |
//+------------------------------------------------------------------+
string Usage()
{
2026-02-06 09:29:10 +03:00
return Usage(buyActionKey)
2026-02-06 13:57:35 +03:00
+ Usage(closeAllPositionsActionKey) + Usage(closeExpertPositionsActionKey)
+ Usage(defendAllPositionsActionKey) + Usage(defendExpertPositionsActionKey)
+ Usage(exitScalperActionKey)
+ Usage(reverseAllPositionsActionKey) + Usage(reverseExpertPositionsActionKey)
+ Usage(sellActionKey);
2026-02-06 03:48:57 +03:00
}
//+------------------------------------------------------------------+
//| Action usage function |
//+------------------------------------------------------------------+
2026-02-06 09:29:10 +03:00
string Usage(ActionKey actionKey)
2026-02-06 03:48:57 +03:00
{
2026-02-06 09:29:10 +03:00
string actionName;
actionNames.TryGetValue(actionKey, actionName);
return StringFormat("[%c] %s\n", actionKey, actionName);
2026-02-06 03:48:57 +03:00
}
2026-02-06 03:57:12 +03:00
//+------------------------------------------------------------------+
2026-02-06 13:57:35 +03:00
//| Is zero function |
2026-02-06 03:57:12 +03:00
//+------------------------------------------------------------------+
bool IsZero(double value)
{
return MathAbs(value) < DBL_EPSILON ? true : false;
}
2026-01-29 14:08:04 +03:00
//+------------------------------------------------------------------+
2026-02-09 16:11:33 +03:00
//+------------------------------------------------------------------+
//| ToDo |
2026-02-11 02:14:22 +03:00
//| - pending order |
2026-02-09 16:11:33 +03:00
//| - trailing stop |
2026-02-11 02:14:22 +03:00
//| - objects Drag'n'Drp |
2026-02-09 16:11:33 +03:00
//| - error processing |
//+------------------------------------------------------------------+
2026-01-29 14:08:04 +03:00