285 lines
11 KiB
MQL5
285 lines
11 KiB
MQL5
|
//+------------------------------------------------------------------+
|
||
|
//| TradeHistoryPrint.mq5 |
|
||
|
//| Copyright 2022, MetaQuotes Ltd. |
|
||
|
//| https://www.mql5.com |
|
||
|
//| Print out positions/deal/orders from trade history |
|
||
|
//+------------------------------------------------------------------+
|
||
|
#property script_show_inputs
|
||
|
|
||
|
#include "..\..\Include\OrderFilter.mqh"
|
||
|
#include "..\..\Include\DealFilter.mqh"
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| History selection method |
|
||
|
//+------------------------------------------------------------------+
|
||
|
enum SELECTOR_TYPE
|
||
|
{
|
||
|
TOTAL, // Whole history
|
||
|
POSITION, // Position ID
|
||
|
};
|
||
|
|
||
|
input SELECTOR_TYPE Type = TOTAL;
|
||
|
input ulong PositionID = 0; // Position ID
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Custom tuple for pretty-printing deal properties |
|
||
|
//| First field must have name '_1' to be compatible with Filters |
|
||
|
//+------------------------------------------------------------------+
|
||
|
struct DealTuple
|
||
|
{
|
||
|
datetime _1; // deal time
|
||
|
ulong deal;
|
||
|
ulong order;
|
||
|
string type;
|
||
|
string in_out;
|
||
|
double volume;
|
||
|
double price;
|
||
|
double profit;
|
||
|
|
||
|
static int size() { return 8; }; // number of properties
|
||
|
static const int fields[]; // identifiers of requested deal properties
|
||
|
|
||
|
template<typename M> // M should be MonitorInterface<>
|
||
|
void assign(M &m)
|
||
|
{
|
||
|
static const int DEAL_TYPE_ = StringLen("DEAL_TYPE_");
|
||
|
static const int DEAL_ENTRY_ = StringLen("DEAL_ENTRY_");
|
||
|
static const ulong L = 0; // default type declarator (dummy)
|
||
|
|
||
|
_1 = (datetime)m.get(fields[0], L);
|
||
|
deal = m.get(fields[1], deal);
|
||
|
order = m.get(fields[2], order);
|
||
|
const ENUM_DEAL_TYPE t = (ENUM_DEAL_TYPE)m.get(fields[3], L);
|
||
|
type = StringSubstr(EnumToString(t), DEAL_TYPE_);
|
||
|
const ENUM_DEAL_ENTRY e = (ENUM_DEAL_ENTRY)m.get(fields[4], L);
|
||
|
in_out = StringSubstr(EnumToString(e), DEAL_ENTRY_);
|
||
|
volume = m.get(fields[5], volume);
|
||
|
price = m.get(fields[6], price);
|
||
|
profit = m.get(fields[7], profit);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
static const int DealTuple::fields[] =
|
||
|
{
|
||
|
DEAL_TIME, DEAL_TICKET, DEAL_ORDER, DEAL_TYPE,
|
||
|
DEAL_ENTRY, DEAL_VOLUME, DEAL_PRICE, DEAL_PROFIT
|
||
|
};
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Custom tuple for pretty-printing order properties |
|
||
|
//| First field must have name '_1' to be compatible with Filters |
|
||
|
//+------------------------------------------------------------------+
|
||
|
struct OrderTuple
|
||
|
{
|
||
|
ulong _1; // ticket (and 'ulong' type declarator)
|
||
|
datetime setup;
|
||
|
datetime done;
|
||
|
string type;
|
||
|
double volume;
|
||
|
double open;
|
||
|
double current;
|
||
|
double sl;
|
||
|
double tp;
|
||
|
string comment;
|
||
|
|
||
|
static int size() { return 10; }; // number of properties
|
||
|
static const int fields[]; // identifiers of requested order properties
|
||
|
|
||
|
template<typename M> // M should be MonitorInterface<>
|
||
|
void assign(M &m)
|
||
|
{
|
||
|
static const int ORDER_TYPE_ = StringLen("ORDER_TYPE_");
|
||
|
|
||
|
_1 = m.get(fields[0], _1);
|
||
|
setup = (datetime)m.get(fields[1], _1);
|
||
|
done = (datetime)m.get(fields[2], _1);
|
||
|
const ENUM_ORDER_TYPE t = (ENUM_ORDER_TYPE)m.get(fields[3], _1);
|
||
|
type = StringSubstr(EnumToString(t), ORDER_TYPE_);
|
||
|
volume = m.get(fields[4], volume);
|
||
|
open = m.get(fields[5], open);
|
||
|
current = m.get(fields[6], current);
|
||
|
sl = m.get(fields[7], sl);
|
||
|
tp = m.get(fields[8], tp);
|
||
|
comment = m.get(fields[9], comment);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
static const int OrderTuple::fields[] =
|
||
|
{
|
||
|
ORDER_TICKET, ORDER_TIME_SETUP, ORDER_TIME_DONE, ORDER_TYPE, ORDER_VOLUME_INITIAL,
|
||
|
ORDER_PRICE_OPEN, ORDER_PRICE_CURRENT, ORDER_SL, ORDER_TP, ORDER_COMMENT
|
||
|
};
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Help function to remove duplicates from array |
|
||
|
//+------------------------------------------------------------------+
|
||
|
template<typename T>
|
||
|
int ArrayUnique(T &array[], const T empty = (T)NULL)
|
||
|
{
|
||
|
const int n = ArraySize(array);
|
||
|
for(int i = 1; i < n; ++i)
|
||
|
{
|
||
|
if(array[i] == array[i - 1])
|
||
|
{
|
||
|
array[i - 1] = empty;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int i = 0, w = 0;
|
||
|
while(i < n)
|
||
|
{
|
||
|
while(i < n && array[i] == empty)
|
||
|
{
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
while(i < n && array[i] != empty)
|
||
|
{
|
||
|
if(w < i)
|
||
|
{
|
||
|
array[w] = array[i];
|
||
|
}
|
||
|
i++;
|
||
|
w++;
|
||
|
}
|
||
|
}
|
||
|
return n - ArrayResize(array, w);
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| Script program start function |
|
||
|
//+------------------------------------------------------------------+
|
||
|
void OnStart()
|
||
|
{
|
||
|
// filter objects for deals and orders
|
||
|
DealFilter filter;
|
||
|
HistoryOrderFilter subfilter;
|
||
|
|
||
|
// select specific history
|
||
|
if(PositionID == 0 || Type == TOTAL)
|
||
|
{
|
||
|
HistorySelect(0, LONG_MAX);
|
||
|
}
|
||
|
else if(Type == POSITION)
|
||
|
{
|
||
|
HistorySelectByPosition(PositionID);
|
||
|
}
|
||
|
|
||
|
// collect position IDs (all or a given single one)
|
||
|
ulong positions[];
|
||
|
if(PositionID == 0)
|
||
|
{
|
||
|
ulong tickets[];
|
||
|
filter.let(DEAL_SYMBOL, _Symbol).select(DEAL_POSITION_ID, tickets, positions, true);
|
||
|
ArrayUnique(positions);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PUSH(positions, PositionID);
|
||
|
}
|
||
|
|
||
|
const int n = ArraySize(positions);
|
||
|
Print("Positions total: ", n);
|
||
|
if(n == 0) return;
|
||
|
|
||
|
// show all positions: deals and orders
|
||
|
for(int i = 0; i < n; ++i)
|
||
|
{
|
||
|
DealTuple deals[];
|
||
|
filter.let(DEAL_POSITION_ID, positions[i]).select(deals, true);
|
||
|
const int m = ArraySize(deals);
|
||
|
if(m == 0)
|
||
|
{
|
||
|
Print("Wrong position ID: ", positions[i]);
|
||
|
break; // wrong position id specfied by user
|
||
|
}
|
||
|
double profit = 0; // TODO: consider commissons, swaps and fees, if needed
|
||
|
for(int j = 0; j < m; ++j) profit += deals[j].profit;
|
||
|
PrintFormat("Position: % 8d %16lld Profit:%f", i + 1, positions[i], (profit));
|
||
|
ArrayPrint(deals);
|
||
|
|
||
|
// NB: 'close-by' orders will be listed on both positions
|
||
|
// only if 'Whole history' is selected, because otherwise
|
||
|
// HistorySelectByPosition is used
|
||
|
// which selects 'close-by' order only by one side
|
||
|
|
||
|
Print("Order details:");
|
||
|
OrderTuple orders[];
|
||
|
subfilter.let(ORDER_POSITION_ID, positions[i], IS::OR_EQUAL)
|
||
|
.let(ORDER_POSITION_BY_ID, positions[i], IS::OR_EQUAL)
|
||
|
.select(orders);
|
||
|
ArrayPrint(orders);
|
||
|
}
|
||
|
}
|
||
|
//+------------------------------------------------------------------+
|
||
|
/*
|
||
|
EXAMPLE 1 (default settings)
|
||
|
|
||
|
Positions total: 3
|
||
|
Position: 1 1253500309 Profit:238.150000
|
||
|
[_1] [deal] [order] [type] [in_out] [volume] [price] [profit]
|
||
|
[0] 2022.02.04 17:34:57 1236049891 1253500309 "BUY" "IN" 1.00000 76.23900 0.00000
|
||
|
[1] 2022.02.14 16:28:41 1242295527 1259788704 "SELL" "OUT" 1.00000 76.42100 238.15000
|
||
|
Order details:
|
||
|
[_1] [setup] [done] [type] [volume] [open] [current] [sl] [tp] [comment]
|
||
|
[0] 1253500309 2022.02.04 17:34:57 2022.02.04 17:34:57 "BUY" 1.00000 76.23900 76.23900 0.00 0.00 ""
|
||
|
[1] 1259788704 2022.02.14 16:28:41 2022.02.14 16:28:41 "SELL" 1.00000 76.42100 76.42100 0.00 0.00 ""
|
||
|
Position: 2 1253526613 Profit:878.030000
|
||
|
[_1] [deal] [order] [type] [in_out] [volume] [price] [profit]
|
||
|
[0] 2022.02.07 10:00:00 1236611994 1253526613 "BUY" "IN" 1.00000 75.75000 0.00000
|
||
|
[1] 2022.02.14 16:28:40 1242295517 1259788693 "SELL" "OUT" 1.00000 76.42100 878.03000
|
||
|
Order details:
|
||
|
[_1] [setup] [done] [type] [volume] [open] [current] [sl] [tp] [comment]
|
||
|
[0] 1253526613 2022.02.04 17:55:18 2022.02.07 10:00:00 "BUY_LIMIT" 1.00000 75.75000 75.67000 0.00 0.00 ""
|
||
|
[1] 1259788693 2022.02.14 16:28:40 2022.02.14 16:28:40 "SELL" 1.00000 76.42100 76.42100 0.00 0.00 ""
|
||
|
Position: 3 1256280710 Profit:4449.040000
|
||
|
[_1] [deal] [order] [type] [in_out] [volume] [price] [profit]
|
||
|
[0] 2022.02.09 13:17:52 1238797056 1256280710 "BUY" "IN" 2.00000 74.72100 0.00000
|
||
|
[1] 2022.02.14 16:28:39 1242295509 1259788685 "SELL" "OUT" 2.00000 76.42100 4449.04000
|
||
|
Order details:
|
||
|
[_1] [setup] [done] [type] [volume] [open] [current] [sl] [tp] [comment]
|
||
|
[0] 1256280710 2022.02.09 13:17:52 2022.02.09 13:17:52 "BUY" 2.00000 74.72100 74.72100 0.00 0.00 ""
|
||
|
[1] 1259788685 2022.02.14 16:28:39 2022.02.14 16:28:39 "SELL" 2.00000 76.42100 76.42100 0.00 0.00 ""
|
||
|
|
||
|
|
||
|
EXAMPLE 2 (PositionID=1276109280, Type=Whole history/Position)
|
||
|
|
||
|
Positions total: 1
|
||
|
Position: 1 1276109280 Profit:-0.040000
|
||
|
[_1] [deal] [order] [type] [in_out] [volume] [price] [profit]
|
||
|
[0] 2022.03.07 12:20:53 1258725455 1276109280 "BUY" "IN" 0.01000 1.08344 0.00000
|
||
|
[1] 2022.03.07 12:20:58 1258725503 1276109328 "SELL" "OUT_BY" 0.01000 1.08340 -0.04000
|
||
|
Order details:
|
||
|
[_1] [setup] [done] [type] [volume] [open] [current] [sl] [tp] [comment]
|
||
|
[0] 1276109280 2022.03.07 12:20:53 2022.03.07 12:20:53 "BUY" 0.01000 1.08344 1.08344 0.00 0.00 ""
|
||
|
[1] 1276109328 2022.03.07 12:20:58 2022.03.07 12:20:58 "CLOSE_BY" 0.01000 1.08340 1.08340 0.00 0.00 "#1276109280 by #1276109283"
|
||
|
|
||
|
|
||
|
EXAMPLE 3 (PositionID=1276109283, Type=Position) - note that exit order (1276109328) is missing
|
||
|
|
||
|
Positions total: 1
|
||
|
Position: 1 1276109283 Profit:0.000000
|
||
|
[_1] [deal] [order] [type] [in_out] [volume] [price] [profit]
|
||
|
[0] 2022.03.07 12:20:53 1258725458 1276109283 "SELL" "IN" 0.01000 1.08340 0.00000
|
||
|
[1] 2022.03.07 12:20:58 1258725504 1276109328 "BUY" "OUT_BY" 0.01000 1.08344 0.00000
|
||
|
Order details:
|
||
|
[_1] [setup] [done] [type] [volume] [open] [current] [sl] [tp] [comment]
|
||
|
[0] 1276109283 2022.03.07 12:20:53 2022.03.07 12:20:53 "SELL" 0.01000 1.08340 1.08340 0.00 0.00 ""
|
||
|
|
||
|
|
||
|
EXAMPLE 4 (PositionID=1276109283, Type=Whole history) - note that exit order is present
|
||
|
|
||
|
Positions total: 1
|
||
|
Position: 1 1276109283 Profit:0.000000
|
||
|
[_1] [deal] [order] [type] [in_out] [volume] [price] [profit]
|
||
|
[0] 2022.03.07 12:20:53 1258725458 1276109283 "SELL" "IN" 0.01000 1.08340 0.00000
|
||
|
[1] 2022.03.07 12:20:58 1258725504 1276109328 "BUY" "OUT_BY" 0.01000 1.08344 0.00000
|
||
|
Order details:
|
||
|
[_1] [setup] [done] [type] [volume] [open] [current] [sl] [tp] [comment]
|
||
|
[0] 1276109283 2022.03.07 12:20:53 2022.03.07 12:20:53 "SELL" 0.01000 1.08340 1.08340 0.00 0.00 ""
|
||
|
[1] 1276109328 2022.03.07 12:20:58 2022.03.07 12:20:58 "CLOSE_BY" 0.01000 1.08340 1.08340 0.00 0.00 "#1276109280 by #1276109283"
|
||
|
|
||
|
|
||
|
*/
|
||
|
//+------------------------------------------------------------------+
|