467 lines
19 KiB
Plaintext
467 lines
19 KiB
Plaintext
#include <Trade/DealInfo.mqh>
|
|
#include <Arrays/ArrayDouble.mqh>
|
|
#include <Generic/HashSet.mqh>
|
|
#include "FileSave.mqh"
|
|
#include "Translations/English.mqh"
|
|
|
|
enum deal_result
|
|
{
|
|
NO_VALUE=0,
|
|
WIN=1,
|
|
LOSS
|
|
};
|
|
enum ENUM_HISTORY_SORT
|
|
{
|
|
HISTORY_SORT_OPENTIME, // Open time
|
|
HISTORY_SORT_CLOSETIME // Close time
|
|
};
|
|
|
|
class CHistory
|
|
{
|
|
private:
|
|
CFileSaving m_FileSave;
|
|
CDealInfo m_deal;
|
|
CArrayDouble m_profit_data;
|
|
CArrayDouble m_balance_data;
|
|
CArrayDouble m_sharp_balance;
|
|
CArrayDouble m_balance_line;
|
|
|
|
double m_max_lot;
|
|
double m_initial_deposit;
|
|
double m_withdrawal;
|
|
double m_volume;
|
|
double m_swap;
|
|
double m_commission;
|
|
double m_profit;
|
|
double m_gross_profit;
|
|
double m_gross_loss;
|
|
|
|
double m_profit_factor;
|
|
|
|
double m_expected_payoff;
|
|
|
|
double m_balance;
|
|
double m_maxdraw_MDD;
|
|
double m_maxdraw_percent;
|
|
// double m_balance_min;
|
|
// double m_balance_dd;
|
|
// double m_balance_dd_percent;
|
|
// double m_balance_dd_relative;
|
|
// double m_balance_dd_relative_percent;
|
|
// double m_balance_dd_absolute;
|
|
|
|
double m_min_peak;
|
|
double m_max_peak;
|
|
|
|
long arr_Positions[];
|
|
long m_type;
|
|
long m_magic;
|
|
|
|
int m_deals;
|
|
int m_trades;
|
|
int m_profit_trades;
|
|
double m_profit_trades_percent;
|
|
int m_loss_trades;
|
|
|
|
string m_symbol;
|
|
string m_comment;
|
|
string m_openTime_str;
|
|
string m_closeTime_str;
|
|
|
|
datetime m_openTime_dt;
|
|
datetime m_closeTime_dt;
|
|
|
|
string m_mqlName;
|
|
|
|
public:
|
|
CHistory(void);
|
|
~CHistory(void);
|
|
|
|
|
|
bool Calculate(const string Symbl,const long Mgc,datetime time_start,datetime time_end);
|
|
double MaxLots() {return(m_max_lot);}
|
|
// double InitialDeposit() {return(m_initial_deposit);}
|
|
// double Withdrawal() {return(m_withdrawal);}
|
|
double Profit() {return(m_profit);};
|
|
double GrossProfit() {return(m_gross_profit);}
|
|
double GrossLoss() {return(m_gross_loss);}
|
|
double MDD() {return(m_maxdraw_MDD);}
|
|
double ProfitFactor() {return(m_profit_factor);}
|
|
int Trades() {return(m_trades);}
|
|
int ProfitTrades() {return(m_profit_trades);};
|
|
double ExpectedPayoff() {return(m_expected_payoff);}
|
|
|
|
string OpenTime() {return(m_openTime_str);}
|
|
string CloseTime() {return(m_closeTime_str);}
|
|
|
|
double Percent(double value,double divider);
|
|
|
|
bool AppendDataToCSV(datetime open_time, string symbol, ulong magic, string posType, double volume, double openPric, double Sl, double Tp, double closePrice, datetime close_time, double Commis, double Swp, double profit);
|
|
bool CheckIsFileBot() {return(FileIsExist(m_mqlName + " EA" + "\\" + AccountInfoString(ACCOUNT_COMPANY) +"\\"+ IntegerToString(GenerateMagicNumbers())+"\\AnalysisOutput\\"+"Transaction11209.csv"));}
|
|
string AnalyzePathFile() {return(m_mqlName + " EA" + "\\" + AccountInfoString(ACCOUNT_COMPANY) +"\\"+ IntegerToString(GenerateMagicNumbers())+"\\AnalysisOutput\\"+"Transaction11209.csv");}
|
|
|
|
protected:
|
|
bool HistorySelectByPositionProcess(const long position_id);
|
|
|
|
};
|
|
//m_withdrawal(0.0), m_max_lot(0.0), m_symbol(NULL), m_type(-1), m_magic(-1), m_volume(0.0),
|
|
// m_openTime_str("-"), m_closeTime_str("-"), m_profit(0.0), m_gross_loss(0.0), m_gross_profit(0.0)
|
|
CHistory::CHistory(void) : m_max_lot(0.0), m_openTime_str("-"), m_closeTime_str("-"), m_balance(0.0), m_max_peak(0.0), m_min_peak(0.0), m_trades(0),
|
|
m_expected_payoff(0.0)
|
|
{
|
|
m_mqlName = MQLInfoString(MQL_PROGRAM_NAME);
|
|
// datetime timestart = D'2024.12.24';
|
|
// if(!CheckIsFileBot())
|
|
// {
|
|
// Calculate(Symbol(), GenerateMagicNumbers(), timestart, TimeCurrent());
|
|
// AppendDataToCSV(OpenTime(), Symbol(), GenerateMagicNumbers(), )
|
|
// }
|
|
}
|
|
|
|
CHistory::~CHistory(void)
|
|
{
|
|
}
|
|
|
|
bool CHistory::Calculate(const string Symbl, const long Mgc,datetime time_start,datetime time_end)
|
|
{
|
|
deal_result result = NO_VALUE;
|
|
|
|
int sort_by = HISTORY_SORT_OPENTIME;
|
|
|
|
datetime first_entry_time = 0;
|
|
datetime overall_first_entry_time = 0;
|
|
|
|
m_profit=0;
|
|
m_gross_profit=0;
|
|
m_gross_loss=0;
|
|
|
|
if(!HistorySelect(time_start,time_end))
|
|
{
|
|
ResetLastError();
|
|
string txt = " ⚠ "+ TRANSLATION_HISTORY_GET_TRADE + TRANSLATION_MESSAGE_ERROR + IntegerToString(GetLastError());
|
|
m_FileSave.IniFileSave(__FUNCTION__+"::"+txt,false);
|
|
m_FileSave.IniFileSaveRun(txt,false);
|
|
return(false);
|
|
}
|
|
|
|
int deals_total = HistoryDealsTotal();
|
|
|
|
if(sort_by==HISTORY_SORT_OPENTIME)
|
|
{
|
|
for(int i = 0; i < deals_total && !IsStopped(); i++)
|
|
if(m_deal.SelectByIndex(i))
|
|
if(m_deal.Entry()==DEAL_ENTRY_IN)
|
|
if(m_deal.DealType()==DEAL_TYPE_BUY || m_deal.DealType()==DEAL_TYPE_SELL)
|
|
{
|
|
if(m_deal.Symbol() != Symbl)
|
|
continue;
|
|
if(m_deal.Magic() != Mgc)
|
|
continue;
|
|
//--- save position ids to the array
|
|
long position_id=m_deal.PositionId();
|
|
int arr_size=ArraySize(arr_Positions);
|
|
if(ArrayResize(arr_Positions,arr_size+1)==arr_size+1)
|
|
arr_Positions[arr_size]=position_id;
|
|
|
|
m_trades++;
|
|
|
|
if (first_entry_time == 0 || m_deal.Time() < first_entry_time) {first_entry_time = m_deal.Time();}
|
|
}
|
|
}
|
|
if(sort_by==HISTORY_SORT_CLOSETIME)
|
|
{
|
|
//--- define a hashset to collect position IDs (with no duplicates)
|
|
CHashSet<long>hashset;
|
|
|
|
//--- handle the case when a position has multiple deals out.
|
|
for(int i = deals_total-1; i >= 0 && !IsStopped(); i--)
|
|
if(m_deal.SelectByIndex(i))
|
|
if(m_deal.Entry()==DEAL_ENTRY_OUT || m_deal.Entry()==DEAL_ENTRY_OUT_BY)
|
|
if(m_deal.DealType()==DEAL_TYPE_BUY || m_deal.DealType()==DEAL_TYPE_SELL)
|
|
{
|
|
if(m_deal.Symbol() != Symbl)
|
|
continue;
|
|
if(m_deal.Magic() != Mgc)
|
|
continue;
|
|
|
|
hashset.Add(m_deal.PositionId());
|
|
}
|
|
|
|
|
|
//--- copy the elements from the set to a compatible one-dimensional array
|
|
hashset.CopyTo(arr_Positions,0);
|
|
//ArrayReverse(arr_Positions);
|
|
ArraySetAsSeries(arr_Positions,true);
|
|
}
|
|
|
|
deals_total = ArraySize(arr_Positions);
|
|
for(int i=0; i<deals_total; i++)
|
|
{
|
|
long position_id = arr_Positions[i];
|
|
HistorySelectByPositionProcess(position_id);
|
|
}
|
|
if (first_entry_time > 0)
|
|
{
|
|
if (overall_first_entry_time == 0 || first_entry_time < overall_first_entry_time)
|
|
{
|
|
overall_first_entry_time = first_entry_time;
|
|
m_openTime_dt = overall_first_entry_time;
|
|
m_openTime_str = TimeToString(overall_first_entry_time, TIME_DATE);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Select history of orders and deals by position ID and |
|
|
//| prints a list of orders and deals for the position in the journal|
|
|
//+------------------------------------------------------------------+
|
|
bool CHistory::HistorySelectByPositionProcess(const long position_id)
|
|
{
|
|
|
|
|
|
double profit=0, profit_close=0;
|
|
m_profit_factor = 0;
|
|
|
|
|
|
datetime last_exit_time = 0;
|
|
datetime overall_last_exit_time = 0;
|
|
|
|
if(!HistorySelectByPosition(position_id))
|
|
{
|
|
ResetLastError();
|
|
string txt = " ⚠ "+ TRANSLATION_HISTORY_SELECTPOSITION_ERR + "(#"+IntegerToString(position_id,0)+") " + TRANSLATION_HISTORY_SELECTPOSITION_FAILED + TRANSLATION_MESSAGE_ERROR + IntegerToString(GetLastError());
|
|
m_FileSave.IniFileSave(__FUNCTION__+"::"+txt,false);
|
|
m_FileSave.IniFileSaveRun(txt,false);
|
|
return(false);
|
|
}
|
|
|
|
int orders_total=HistoryOrdersTotal();
|
|
for(int i=0; i<orders_total; i++)
|
|
{
|
|
ulong ticket = HistoryOrderGetTicket(i);
|
|
string ordersymbol = HistoryOrderGetString(ticket, ORDER_SYMBOL);
|
|
long ordermagic = HistoryOrderGetInteger(ticket, ORDER_MAGIC);
|
|
if(ticket==0)
|
|
continue;
|
|
|
|
}
|
|
|
|
int deals_total =HistoryDealsTotal();
|
|
for(int i=0; i<deals_total; i++)
|
|
{
|
|
ulong ticket=HistoryDealGetTicket(i);
|
|
double dealprofit = HistoryDealGetDouble(ticket, DEAL_PROFIT);
|
|
string dealsymbl = HistoryDealGetString(ticket, DEAL_SYMBOL);
|
|
ulong dealmagic = HistoryDealGetInteger(ticket, DEAL_MAGIC);
|
|
double dealswap = HistoryDealGetDouble(ticket, DEAL_SWAP);
|
|
double dealcomision = HistoryDealGetDouble(ticket, DEAL_COMMISSION);
|
|
double dealvolume = HistoryDealGetDouble(ticket, DEAL_VOLUME);
|
|
datetime dealtimeClose = (datetime)HistoryDealGetInteger(ticket, DEAL_TIME);
|
|
|
|
if(ticket==0)
|
|
continue;
|
|
ENUM_DEAL_ENTRY deal_entry=(ENUM_DEAL_ENTRY)HistoryDealGetInteger(ticket, DEAL_ENTRY);
|
|
ENUM_DEAL_TYPE deal_type= (ENUM_DEAL_TYPE)HistoryDealGetInteger(ticket, DEAL_TYPE);
|
|
if(deal_type == DEAL_TYPE_BUY || deal_type == DEAL_TYPE_SELL)
|
|
{
|
|
|
|
profit = NormalizeDouble(dealprofit + dealswap + dealcomision, 2);
|
|
if(profit >= 0.0) {m_gross_profit += profit;}
|
|
else {m_gross_loss += profit;}
|
|
|
|
// Print(dealprofit," |in| ",dealswap," |in| ",dealcomision);
|
|
|
|
if(dealvolume > m_max_lot) {m_max_lot = dealvolume;}
|
|
|
|
if (last_exit_time < dealtimeClose) {last_exit_time = dealtimeClose;}
|
|
}
|
|
|
|
if(deal_entry == DEAL_ENTRY_OUT || deal_entry == DEAL_ENTRY_OUT_BY || deal_entry == DEAL_ENTRY_IN || deal_entry == DEAL_ENTRY_INOUT)
|
|
{
|
|
profit_close = NormalizeDouble(dealprofit + dealswap + dealcomision,2);
|
|
m_balance += profit_close;
|
|
// m_trades++;
|
|
// Print(dealprofit," |out| ",dealswap," |out| ",dealcomision);
|
|
|
|
if(profit >= 0.0)
|
|
{
|
|
m_profit_trades++;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
}
|
|
if (last_exit_time > 0)
|
|
{
|
|
if (overall_last_exit_time == 0 || last_exit_time > overall_last_exit_time)
|
|
{
|
|
overall_last_exit_time = last_exit_time;
|
|
m_closeTime_dt = overall_last_exit_time;
|
|
m_closeTime_str = TimeToString(overall_last_exit_time, TIME_DATE);
|
|
}
|
|
}
|
|
if (m_balance > max_balance)
|
|
{
|
|
max_balance = m_balance;
|
|
}
|
|
drawdown = NormalizeDouble(max_balance - m_balance, 2);
|
|
if(drawdown > m_maxdraw_MDD) {m_maxdraw_MDD = drawdown;}
|
|
// Print("balance ",m_balance," | ",max_balance," | ",drawdown," | ",m_maxdraw_MDD);
|
|
m_profit = m_gross_profit+m_gross_loss;
|
|
|
|
if(MathAbs(m_gross_loss) > FLT_EPSILON) {m_profit_factor = MathAbs(m_gross_profit/m_gross_loss);}
|
|
|
|
if(m_trades > 0)
|
|
{
|
|
m_expected_payoff=m_profit/m_trades;
|
|
m_profit_trades_percent = (double)m_profit_trades / m_trades * 100;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
double CHistory::Percent(double value,double divider)
|
|
{
|
|
if(MathAbs(value)<=FLT_EPSILON)
|
|
return(0);
|
|
return(100*value/divider);
|
|
}
|
|
|
|
bool CHistory::AppendDataToCSV(datetime open_time, string symbol, ulong magic, string posType, double volume, double openPric, double Sl, double Tp, double closePrice, datetime close_time, double Commis, double Swp, double profit)
|
|
{
|
|
|
|
int fh;
|
|
// Save to new format only.
|
|
fh = FileOpen(AnalyzePathFile(), FILE_WRITE|FILE_CSV|FILE_ANSI,',');
|
|
if (fh == INVALID_HANDLE)
|
|
{
|
|
ResetLastError();
|
|
string txt = TRANSLATION_FILED_TO_OPEN_FILE_WRITING + AnalyzePathFile() + TRANSLATION_MESSAGE_ERROR + IntegerToString(GetLastError());
|
|
m_FileSave.IniFileSaveRun(txt,false);
|
|
m_FileSave.IniFileSave(__FUNCTION__+":: "+ txt, false);
|
|
return false;
|
|
}
|
|
FileWrite(fh, "Open Time", "Symbol", "Magic Number", "Type", "Volume", "Open Price", "S/L", "T/P", "Close Price", "Close Time", "Commission", "Swap", "Profit");
|
|
FileWrite(fh, open_time, symbol, magic, posType, volume, openPric, Sl, Tp, closePrice, close_time, Commis, Swp, profit);
|
|
|
|
FileClose(fh);
|
|
|
|
|
|
|
|
return true;
|
|
}
|
|
//--- global variable
|
|
double max_balance = -DBL_MAX;
|
|
double drawdown = 0.0;
|
|
/*
|
|
bool CHistory::Calculate(const string Symbl, const long Mgc,datetime time_start,datetime time_end,double initial_deposit=0.0)
|
|
{
|
|
|
|
if(!HistorySelect(time_start,time_end))
|
|
{
|
|
ResetLastError();
|
|
string txt = " ⚠ "+ TRANSLATION_HISTORY_GET_TRADE + TRANSLATION_MESSAGE_ERROR + IntegerToString(GetLastError());
|
|
m_FileSave.IniFileSave(__FUNCTION__+"::"+txt,false);
|
|
m_FileSave.IniFileSaveRun(txt,false);
|
|
return(false);
|
|
}
|
|
for(int i = HistoryDealsTotal()-1; i >=0; i--)
|
|
{
|
|
// deals
|
|
ulong dealticket = HistoryDealGetTicket(i);
|
|
if (dealticket <= 0) {
|
|
ResetLastError();
|
|
string txt = " ⚠ "+ TRANSLATION_DEAL_ERROR_TICKT + TRANSLATION_MESSAGE_ERROR + IntegerToString(GetLastError());
|
|
m_FileSave.IniFileSave(__FUNCTION__+"::"+txt,false);
|
|
m_FileSave.IniFileSaveRun(txt,false);
|
|
return(false);
|
|
}
|
|
datetime first_entry_time = 0;
|
|
datetime last_exit_time = 0;
|
|
datetime overall_first_entry_time = 0;
|
|
datetime overall_last_exit_time = 0;
|
|
|
|
long dealID = 0;
|
|
double dealprice = HistoryDealGetDouble(dealticket,DEAL_PRICE);
|
|
double dealvolume = HistoryDealGetDouble(dealticket,DEAL_VOLUME);
|
|
double dealprofit = HistoryDealGetDouble(dealticket,DEAL_PROFIT);
|
|
double dealcommision = HistoryDealGetDouble(dealticket,DEAL_COMMISSION);
|
|
double dealswap = HistoryDealGetDouble(dealticket,DEAL_SWAP);
|
|
ulong dealmagic = HistoryDealGetInteger(dealticket,DEAL_MAGIC);
|
|
string dealcomment = HistoryDealGetString(dealticket,DEAL_COMMENT);
|
|
datetime dealtimed = (datetime)HistoryDealGetInteger(dealticket, DEAL_TIME);
|
|
string dealDate = TimeToString(dealtimed, TIME_DATE|TIME_MINUTES|TIME_SECONDS);
|
|
string dealsymbol = HistoryDealGetString(dealticket,DEAL_SYMBOL);
|
|
ENUM_DEAL_ENTRY dealentry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(dealticket,DEAL_ENTRY);
|
|
ENUM_DEAL_TYPE dealtype = (ENUM_DEAL_TYPE)HistoryDealGetInteger(dealticket,DEAL_TYPE);
|
|
long dealreason = HistoryDealGetInteger(dealticket,DEAL_REASON);
|
|
|
|
long posID = 0;
|
|
|
|
// orders
|
|
ulong orderticket = HistoryDealGetInteger(dealticket,DEAL_ORDER);
|
|
long orderID = HistoryOrderGetInteger(orderticket, ORDER_POSITION_ID);
|
|
double orderprice = HistoryOrderGetDouble(orderticket,ORDER_PRICE_OPEN);
|
|
double ordervolume = HistoryOrderGetDouble(dealticket,ORDER_VOLUME_INITIAL);
|
|
datetime ordertimed = (datetime)HistoryOrderGetInteger(orderticket,ORDER_TIME_DONE);
|
|
string orderDate = TimeToString(ordertimed, TIME_DATE|TIME_MINUTES|TIME_SECONDS);
|
|
string ordersymbol = HistoryOrderGetString(orderticket,ORDER_SYMBOL);
|
|
ENUM_ORDER_TYPE ordertype = (ENUM_ORDER_TYPE)HistoryOrderGetInteger(orderticket,ORDER_TYPE);
|
|
|
|
if((dealsymbol == Symbl) && (dealmagic == Mgc))
|
|
{
|
|
if(dealentry == DEAL_ENTRY_IN)
|
|
{
|
|
if(dealvolume > m_max_lot) {m_max_lot = dealvolume;}
|
|
if (first_entry_time == 0 || dealtimed < first_entry_time) {first_entry_time = dealtimed;}
|
|
|
|
dealID = HistoryDealGetInteger(dealticket,DEAL_POSITION_ID);
|
|
posID = dealID;
|
|
// Print(dealsymbol," in| ",dealcommision," | ",dealprofit," | ",dealmagic," | ",dealticket," | ",dealvolume," |id| ",dealID);
|
|
// Print(ordersymbol," in| ",0," | ",0," | ",0," | ",orderticket," | ",ordervolume," |id| ",orderID);
|
|
|
|
}
|
|
// else if (dealentry == DEAL_ENTRY_OUT)
|
|
// {
|
|
// if(orderticket == dealID)
|
|
// {
|
|
// Print("tick ",dealticket, " | ",orderticket);
|
|
// }
|
|
// // Print(dealsymbol," | ",dealcommision," | ",dealprofit," | ",dealmagic," | ",dealticket," | ",dealvolume);
|
|
// // Print(ordersymbol," | ",0," | ",0," | ",0," | ",orderticket," | ",ordervolume);
|
|
// }
|
|
|
|
// Print(dealsymbol," in| ",dealcommision," | ",dealprofit," | ",dealmagic," | ",dealticket," | ",dealvolume," |id| ",dealID);
|
|
// Print(ordersymbol," in| ",0," | ",0," | ",0," | ",orderticket," | ",ordervolume," |id| ",orderID);
|
|
}
|
|
|
|
if(dealticket == posID)
|
|
{
|
|
Print(dealticket," | ",orderticket);
|
|
// Print(dealsymbol," in| ",dealcommision," | ",dealprofit," | ",dealmagic," | ",dealticket," | ",dealvolume," |id| ",dealID);
|
|
// Print(ordersymbol," in| ",0," | ",0," | ",0," | ",orderticket," | ",ordervolume," |id| ",orderID);
|
|
}
|
|
|
|
|
|
// if(dealID == posID)
|
|
// {
|
|
// Print(dealsymbol," in| ",dealcommision," | ",dealprofit," | ",dealmagic," | ",dealticket," | ",dealvolume," |id| ",dealID);
|
|
// Print(ordersymbol," in| ",0," | ",0," | ",0," | ",orderticket," | ",ordervolume," |id| ",orderID);
|
|
// }
|
|
if (first_entry_time > 0)
|
|
{
|
|
if (overall_first_entry_time == 0 || first_entry_time < overall_first_entry_time)
|
|
{
|
|
overall_first_entry_time = first_entry_time;
|
|
m_openTime_str = TimeToString(overall_first_entry_time, TIME_DATE);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Print("maxlot ",m_max_lot," open time ",m_openTime_str," close time ",m_closeTime_str);
|
|
return true;
|
|
} |