bifurqué depuis masterofpuppets/mql5
766 lignes
54 Kio
MQL5
766 lignes
54 Kio
MQL5
//+------------------------------------------------------------------+
|
|
//| Scalper.mq5 |
|
|
//| Copyright 2026, MasterOfPuppets |
|
|
//| https://forge.mql5.io/masterofpuppets/mql5 |
|
|
//+------------------------------------------------------------------+
|
|
#include <Generic\LinkedList.mqh>
|
|
#include <Generic\HashMap.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"
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Classes |
|
|
//+------------------------------------------------------------------+
|
|
class Indexes
|
|
{
|
|
public:
|
|
int data[];
|
|
Indexes() { ArrayFree(data); }
|
|
~Indexes() { ArrayFree(data); }
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Enums |
|
|
//+------------------------------------------------------------------+
|
|
//+------------------------------------------------------------------+
|
|
//| Key to Action enum |
|
|
//| ASCII codes for keys |
|
|
//| https://www.w3.org/2002/09/tests/keys.html |
|
|
//+------------------------------------------------------------------+
|
|
enum Action
|
|
{
|
|
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
|
|
};
|
|
|
|
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"
|
|
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
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 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 |
|
|
//+------------------------------------------------------------------+
|
|
struct LastProfit
|
|
{
|
|
string message;
|
|
double profit;
|
|
string suffix;
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Variables |
|
|
//+------------------------------------------------------------------+
|
|
CHashMap<long, int> actions;
|
|
CHashMap<long, string> actionNames;
|
|
CHashMap<int, Indexes*> keyToIndexesMap;
|
|
string lastProfit;
|
|
LastProfit lastProfits[];
|
|
CPositionInfo positionInfo;
|
|
CSymbolInfo symbolInfo;
|
|
double totalLastProfit;
|
|
CTrade trade;
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| ChartEvent function |
|
|
//+------------------------------------------------------------------+
|
|
void OnChartEvent(const int32_t id, const long &lparam, const double &dparam, const string &sparam)
|
|
{
|
|
if(id != CHARTEVENT_KEYDOWN)
|
|
{
|
|
return;
|
|
}
|
|
int action;
|
|
actions.TryGetValue(lparam, action);
|
|
switch(action)
|
|
{
|
|
case Action::BUY:
|
|
case Action::SELL:
|
|
Deal(action);
|
|
break;
|
|
case Action::CLOSE_ALL_POSITIONS:
|
|
CloseAllPositions();
|
|
break;
|
|
case Action::CLOSE_EXPERT_POSITIONS:
|
|
CloseExpertPositions();
|
|
break;
|
|
case Action::DEFEND_ALL_POSITIONS:
|
|
DefendAllPositions();
|
|
break;
|
|
case Action::DEFEND_EXPERT_POSITIONS:
|
|
DefendExpertPositions();
|
|
break;
|
|
case Action::EXIT_SCALPER:
|
|
ExpertRemove();
|
|
PlaySound(SOUND_FILE_NAME);
|
|
break;
|
|
case Action::REVERSE_ALL_POSITIONS:
|
|
ReverseAllPositions();
|
|
break;
|
|
case Action::REVERSE_EXPERT_POSITIONS:
|
|
ReverseExpertPositions();
|
|
break;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Deal function |
|
|
//+------------------------------------------------------------------+
|
|
void Deal(int action)
|
|
{
|
|
ENUM_ORDER_TYPE orderType;
|
|
double price;
|
|
double deltaStopLoss;
|
|
double deltaTakeProfit;
|
|
symbolInfo.RefreshRates();
|
|
switch(action)
|
|
{
|
|
case Action::BUY:
|
|
orderType = ENUM_ORDER_TYPE::ORDER_TYPE_BUY;
|
|
price = symbolInfo.Ask();
|
|
deltaStopLoss = -STOP_LOSS;
|
|
deltaTakeProfit = TAKE_PROFIT;
|
|
break;
|
|
case Action::SELL:
|
|
orderType = ENUM_ORDER_TYPE::ORDER_TYPE_SELL;
|
|
price = symbolInfo.Bid();
|
|
deltaStopLoss = STOP_LOSS;
|
|
deltaTakeProfit = -TAKE_PROFIT;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
Print(EnumToString((Action) action));
|
|
double stopLoss = IsZero(STOP_LOSS) ? 0.0 : symbolInfo.NormalizePrice(price + deltaStopLoss);
|
|
double takeProfit = IsZero(TAKE_PROFIT) ? 0.0 : symbolInfo.NormalizePrice(price + deltaTakeProfit);
|
|
trade.PositionOpen(_Symbol, orderType, CONTRACTS, price, stopLoss, takeProfit);
|
|
PlaySound(SOUND_FILE_NAME);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| ClosePositionModifier Modifier class |
|
|
//+------------------------------------------------------------------+
|
|
class ClosePositionModifier : public Modifier
|
|
{
|
|
private:
|
|
bool m_withExpert;
|
|
public:
|
|
ClosePositionModifier(bool withExpert = false)
|
|
{
|
|
m_withExpert = withExpert;
|
|
}
|
|
void Modify()
|
|
{
|
|
if(m_withExpert && 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;
|
|
}
|
|
LastProfit lastProfitStruct;
|
|
lastProfitStruct.message = message;
|
|
lastProfitStruct.profit = positionInfo.Profit();
|
|
AddLastProfit(lastProfitStruct);
|
|
CalculateTotalLastProfit();
|
|
SetLastProfit();
|
|
}
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Add last profit function |
|
|
//+------------------------------------------------------------------+
|
|
void AddLastProfit(LastProfit &lastProfitStruct)
|
|
{
|
|
if(lastProfits.Size() == LAST_PROFITS_SIZE)
|
|
{
|
|
ArrayRemove(lastProfits, 0, 1);
|
|
}
|
|
ArrayResize(lastProfits, lastProfits.Size() + 1);
|
|
lastProfits[lastProfits.Size() - 1] = lastProfitStruct;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate total last profit function |
|
|
//+------------------------------------------------------------------+
|
|
void CalculateTotalLastProfit()
|
|
{
|
|
totalLastProfit = 0.0;
|
|
for(int i = 0; i < (int) lastProfits.Size(); i++)
|
|
{
|
|
totalLastProfit += lastProfits[i].profit;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Set last profit function |
|
|
//+------------------------------------------------------------------+
|
|
void SetLastProfit()
|
|
{
|
|
lastProfit = "\n";
|
|
for(int i = 0; i < (int) lastProfits.Size(); i++)
|
|
{
|
|
lastProfit += StringFormat(INDENT_FORMAT,
|
|
lastProfits[i].message + lastProfits[i].suffix + DoubleToString(lastProfits[i].profit, 2));
|
|
}
|
|
lastProfit += StringFormat(INDENT_FORMAT, DoubleToString(totalLastProfit, 2));
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Close all positions function |
|
|
//+------------------------------------------------------------------+
|
|
void CloseAllPositions()
|
|
{
|
|
ClosePositionModifier closePositionModifier;
|
|
Modify(EnumToString(Action::CLOSE_ALL_POSITIONS), &closePositionModifier);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Close expert positions function |
|
|
//+------------------------------------------------------------------+
|
|
void CloseExpertPositions()
|
|
{
|
|
ClosePositionModifier closePositionModifier(true);
|
|
Modify(EnumToString(Action::CLOSE_EXPERT_POSITIONS), &closePositionModifier);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| DefendModifier Modifier class |
|
|
//+------------------------------------------------------------------+
|
|
class DefendModifier : public Modifier
|
|
{
|
|
private:
|
|
bool m_withExpert;
|
|
public:
|
|
DefendModifier(bool withExpert = false)
|
|
{
|
|
this.m_withExpert = withExpert;
|
|
}
|
|
void Modify()
|
|
{
|
|
if(m_withExpert && 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 all positions function |
|
|
//+------------------------------------------------------------------+
|
|
void DefendAllPositions()
|
|
{
|
|
DefendModifier defendModifier;
|
|
Modify(EnumToString(Action::DEFEND_ALL_POSITIONS), &defendModifier);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Defend expert positions function |
|
|
//+------------------------------------------------------------------+
|
|
void DefendExpertPositions()
|
|
{
|
|
DefendModifier defendModifier(true);
|
|
Modify(EnumToString(Action::DEFEND_EXPERT_POSITIONS), &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();
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 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);
|
|
}
|
|
if(!IsKeysCorrect())
|
|
{
|
|
return(INIT_PARAMETERS_INCORRECT);
|
|
}
|
|
EventSetTimer(1);
|
|
trade.SetExpertMagicNumber(MAGIC);
|
|
InitActions();
|
|
InitActionNames();
|
|
InitLastProfits();
|
|
Print(Usage());
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 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();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Actions initialization function |
|
|
//+------------------------------------------------------------------+
|
|
void InitActions()
|
|
{
|
|
actions.Add(buyActionKey, Action::BUY);
|
|
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);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Action names initialization function |
|
|
//+------------------------------------------------------------------+
|
|
void InitActionNames()
|
|
{
|
|
actionNames.Add(buyActionKey, EnumToString(Action::BUY));
|
|
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));
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 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);
|
|
if(IsZero(dealProfit))
|
|
{
|
|
continue;
|
|
}
|
|
long dealReason = HistoryDealGetInteger(dealTicket, DEAL_REASON);
|
|
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;
|
|
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();
|
|
}
|
|
PlaySound(SOUND_FILE_NAME);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Usage function |
|
|
//+------------------------------------------------------------------+
|
|
string Usage()
|
|
{
|
|
return Usage(buyActionKey)
|
|
+ Usage(closeAllPositionsActionKey) + Usage(closeExpertPositionsActionKey)
|
|
+ Usage(defendAllPositionsActionKey) + Usage(defendExpertPositionsActionKey)
|
|
+ Usage(exitScalperActionKey)
|
|
+ Usage(reverseAllPositionsActionKey) + Usage(reverseExpertPositionsActionKey)
|
|
+ Usage(sellActionKey);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Action usage function |
|
|
//+------------------------------------------------------------------+
|
|
string Usage(ActionKey actionKey)
|
|
{
|
|
string actionName;
|
|
actionNames.TryGetValue(actionKey, actionName);
|
|
return StringFormat("[%c] %s\n", actionKey, actionName);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Is zero function |
|
|
//+------------------------------------------------------------------+
|
|
bool IsZero(double value)
|
|
{
|
|
return MathAbs(value) < DBL_EPSILON ? true : false;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//+------------------------------------------------------------------+
|
|
//| ToDo |
|
|
//| - pending order |
|
|
//| - trailing stop |
|
|
//| - objects Drag'n'Drp |
|
|
//| - error processing |
|
|
//+------------------------------------------------------------------+
|