788 lines
30 KiB
Plaintext
788 lines
30 KiB
Plaintext
#include <Trade/DealInfo.mqh>
|
|
#include <Arrays/ArrayDouble.mqh>
|
|
#include <Generic/HashSet.mqh>
|
|
#include <Retrace/FileSave.mqh>
|
|
#include <Retrace/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:
|
|
CDealInfo m_deal;
|
|
CFileSaving m_FileSave;
|
|
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;
|
|
|
|
// only to save file CSV
|
|
datetime c_opTime;
|
|
datetime c_clTime;
|
|
string c_type;
|
|
long c_magic;
|
|
double c_volume;
|
|
double c_opPrice;
|
|
double c_sl;
|
|
double c_tp;
|
|
double c_clPrice;
|
|
double c_commission;
|
|
double c_swap;
|
|
double c_profit;
|
|
double c_profitPoint;
|
|
string c_duration;
|
|
string c_opComment;
|
|
string c_clComment;
|
|
|
|
public:
|
|
CHistory(void);
|
|
~CHistory(void);
|
|
|
|
bool ExportHistoryPositions(const string Cmment);
|
|
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);
|
|
|
|
int CountRowsInCSV();
|
|
bool AppendDataToCSV();
|
|
bool CheckIsFileBot() {return(FileIsExist(m_mqlName + " EA" + "\\" + AccountInfoString(ACCOUNT_COMPANY) +"\\"+ "Trade_Transaction.csv"));}
|
|
string AnalyzePathFile() {return(m_mqlName + " EA" + "\\" + AccountInfoString(ACCOUNT_COMPANY) +"\\"+"Trade_Transaction.csv");}
|
|
|
|
bool Calculate(const string Symbl,const long Mgc,datetime time_start,datetime time_end);
|
|
bool HistorySelectByPositionProcess(const long position_id, const bool saveCsv);
|
|
string GetExpertNameWithSum();
|
|
|
|
};
|
|
|
|
CHistory::CHistory(void)
|
|
{
|
|
m_mqlName = MQLInfoString(MQL_PROGRAM_NAME);
|
|
c_magic = 1;
|
|
|
|
}
|
|
|
|
CHistory::~CHistory(void)
|
|
{
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CHistory::ExportHistoryPositions(const string Cmment)
|
|
{
|
|
datetime time_start = D'2024.12.24';
|
|
int handle= FileOpen(AnalyzePathFile(),FILE_WRITE|FILE_CSV|FILE_ANSI,',');// |FILE_ANSI,','
|
|
if(handle == 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(handle, "Open Time", "Symbol", "Magic Number", "Type", "Volume", "Open Price", "S/L", "T/P", "Close Price", "Close Time", "Commission", "Swap", "Profit",
|
|
"Profit Points", "Duration", "Open Comment", "Close Comment");
|
|
|
|
ulong s_time=GetMicrosecondCount();
|
|
double Balanc=0;
|
|
|
|
|
|
if(!HistorySelect(time_start,TimeCurrent()))
|
|
{
|
|
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 = HistoryDealsTotal();
|
|
int sort_by = HISTORY_SORT_CLOSETIME;
|
|
|
|
if(sort_by==HISTORY_SORT_OPENTIME)
|
|
{
|
|
for(int i = 0; i < deals && !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)
|
|
{
|
|
//--- save position ids to the array
|
|
long position_id=m_deal.PositionId();
|
|
int arr_size=ArraySize(arr_Positions);
|
|
ArrayResize(arr_Positions,arr_size+1,100);
|
|
arr_Positions[arr_size]=position_id;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//---
|
|
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-1; i >= 0 && !IsStopped(); i--)
|
|
if(m_deal.SelectByIndex(i))
|
|
{
|
|
if(m_deal.Entry() == DEAL_ENTRY_IN && StringFind(m_deal.Comment(), Cmment) != -1)
|
|
{
|
|
if(m_deal.DealType() == DEAL_TYPE_BUY || m_deal.DealType() == DEAL_TYPE_SELL)
|
|
{
|
|
hashset.Add(m_deal.PositionId());
|
|
}
|
|
}
|
|
// 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)
|
|
// 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);
|
|
}
|
|
|
|
//--- now process the list of positions stored in the array
|
|
int positions=ArraySize(arr_Positions);
|
|
for(int i=0; i<positions && !IsStopped(); i++)
|
|
{
|
|
string pos_symbol=NULL;
|
|
long pos_id=-1;
|
|
long pos_type=-1;
|
|
long pos_magic=-1;
|
|
double pos_open_price=0;
|
|
double pos_close_price=0;
|
|
double pos_sl = 0;
|
|
double pos_tp = 0;
|
|
double pos_commission = 0;
|
|
double pos_swap=0;
|
|
double pos_profit=0;
|
|
double pos_open_volume= 0;
|
|
double pos_close_volume=0;
|
|
datetime pos_open_time=0;
|
|
datetime pos_close_time=0;
|
|
double pos_sum_cost=0;
|
|
long pos_open_reason=-1;
|
|
long pos_close_reason=-1;
|
|
string pos_open_comment = NULL;
|
|
string pos_close_comment = NULL;
|
|
|
|
|
|
//--- request the history of deals and orders for the specified position
|
|
if(HistorySelectByPosition(arr_Positions[i]) && HistoryDealsTotal()>1)
|
|
{
|
|
//--- now process the list of received deals for the specified position
|
|
deals=HistoryDealsTotal();
|
|
for(int j=0; j<deals && !IsStopped(); j++)
|
|
{
|
|
//--- select deal ticket by its position in the list
|
|
if(m_deal.SelectByIndex(j))
|
|
{
|
|
pos_id = m_deal.PositionId();
|
|
pos_symbol = m_deal.Symbol();
|
|
pos_commission += m_deal.Commission();
|
|
pos_swap += m_deal.Swap();
|
|
pos_profit += m_deal.Profit();
|
|
|
|
//--- Entry deal for position
|
|
if(m_deal.Entry()==DEAL_ENTRY_IN)
|
|
{
|
|
pos_magic = m_deal.Magic();
|
|
pos_type = m_deal.DealType();
|
|
pos_open_time = m_deal.Time();
|
|
pos_open_price = m_deal.Price();
|
|
pos_open_volume = m_deal.Volume();
|
|
//---
|
|
pos_open_comment = m_deal.Comment();
|
|
pos_open_reason = HistoryDealGetInteger(m_deal.Ticket(), DEAL_REASON);
|
|
}
|
|
|
|
//--- Exit deal(s) for position
|
|
else if(m_deal.Entry()==DEAL_ENTRY_OUT || m_deal.Entry()==DEAL_ENTRY_OUT_BY)
|
|
{
|
|
pos_close_time = m_deal.Time();
|
|
pos_sum_cost += m_deal.Volume() * m_deal.Price();
|
|
pos_close_volume += m_deal.Volume();
|
|
pos_close_price = pos_sum_cost / pos_close_volume;
|
|
pos_sl = HistoryDealGetDouble(m_deal.Ticket(), DEAL_SL);
|
|
pos_tp = HistoryDealGetDouble(m_deal.Ticket(), DEAL_TP);
|
|
//---
|
|
pos_close_comment += m_deal.Comment() + " ";
|
|
pos_close_reason = HistoryDealGetInteger(m_deal.Ticket(), DEAL_REASON);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//--- If the position is still open, it will not be displayed in the history.
|
|
if(MathAbs(pos_open_volume-pos_close_volume)>0.00001)
|
|
continue;
|
|
|
|
//--- Closed position is reconstructed
|
|
StringTrimLeft(pos_close_comment);
|
|
StringTrimRight(pos_close_comment);
|
|
|
|
//--- sums
|
|
Balanc+=pos_profit+pos_swap+pos_commission;
|
|
|
|
//---
|
|
SymbolSelect(pos_symbol,true);
|
|
int digits=(int)SymbolInfoInteger(pos_symbol,SYMBOL_DIGITS);
|
|
double point=SymbolInfoDouble(pos_symbol,SYMBOL_POINT);
|
|
|
|
FileWrite(handle,
|
|
(string)pos_open_time,
|
|
pos_symbol,
|
|
pos_magic,
|
|
(pos_type==DEAL_TYPE_BUY) ? "buy" : (pos_type==DEAL_TYPE_SELL) ? "sell" : "other",
|
|
DoubleToString(pos_close_volume,2),
|
|
DoubleToString(pos_open_price,digits),
|
|
(pos_sl ? DoubleToString(pos_sl,digits) : ""),
|
|
(pos_tp ? DoubleToString(pos_tp,digits) : ""),
|
|
DoubleToString(pos_close_price,(deals==2 ? digits : digits+3)),
|
|
(string)pos_close_time,
|
|
DoubleToString(pos_commission,2),
|
|
DoubleToString(pos_swap,2),
|
|
DoubleToString(pos_profit,2),
|
|
MathRound((pos_type == DEAL_TYPE_BUY ? pos_close_price - pos_open_price : pos_open_price - pos_close_price) / point),
|
|
//
|
|
// (sort_by == HISTORY_SORT_CLOSETIME ? DoubleToString(Balanc,2) : ""),
|
|
//
|
|
|
|
TimeElapsedToString(pos_close_time - pos_open_time),
|
|
pos_open_comment,
|
|
pos_close_comment
|
|
);
|
|
|
|
}
|
|
}
|
|
|
|
FileClose(handle);
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
string TimeElapsedToString(const datetime pElapsedSeconds)
|
|
{
|
|
const long days = pElapsedSeconds / PeriodSeconds(PERIOD_D1);
|
|
|
|
return((days ? (string)days + "d " : "") + TimeToString(pElapsedSeconds,TIME_SECONDS));
|
|
}
|
|
|
|
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;
|
|
|
|
datetime open_times[];
|
|
|
|
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, false);
|
|
}
|
|
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, const bool saveCsv)
|
|
{
|
|
|
|
|
|
double profit=0, profit_close=0;
|
|
m_profit_factor = 0;
|
|
c_commission = 0.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);
|
|
|
|
ulong c_dealticket = HistoryDealGetInteger(ticket, DEAL_TICKET);
|
|
ENUM_DEAL_ENTRY c_dealentry=(ENUM_DEAL_ENTRY)HistoryDealGetInteger(c_dealticket, DEAL_ENTRY);
|
|
ENUM_DEAL_TYPE c_dealTyp = (ENUM_DEAL_TYPE)HistoryDealGetInteger(c_dealticket, DEAL_TYPE);
|
|
string c_symbol = HistoryDealGetString(c_dealticket, DEAL_SYMBOL);
|
|
double c_point = SymbolInfoDouble(c_symbol, SYMBOL_POINT);
|
|
|
|
if(ticket==0)
|
|
continue;
|
|
|
|
if(c_dealentry == DEAL_ENTRY_IN || c_dealentry == DEAL_ENTRY_INOUT)
|
|
{
|
|
c_opTime = (datetime)HistoryDealGetInteger(c_dealticket, DEAL_TIME);
|
|
c_type = HistoryDealGetInteger(c_dealticket, DEAL_TYPE) == 0 ? "Buy" : "Sell";
|
|
c_volume = HistoryDealGetDouble(c_dealticket, DEAL_VOLUME);
|
|
c_opPrice = HistoryDealGetDouble(c_dealticket, DEAL_PRICE);
|
|
c_commission += HistoryDealGetDouble(c_dealticket, DEAL_COMMISSION);
|
|
c_opComment = HistoryDealGetString(c_dealticket, DEAL_COMMENT);
|
|
}
|
|
if(c_dealentry == DEAL_ENTRY_OUT || c_dealentry == DEAL_ENTRY_OUT_BY)
|
|
{
|
|
c_sl = HistoryDealGetDouble(c_dealticket, DEAL_SL);
|
|
c_tp = HistoryDealGetDouble(c_dealticket, DEAL_TP);
|
|
c_clPrice = HistoryDealGetDouble(c_dealticket, DEAL_PRICE);
|
|
c_clTime = (datetime)HistoryDealGetInteger(c_dealticket, DEAL_TIME);
|
|
c_commission += HistoryDealGetDouble(c_dealticket, DEAL_COMMISSION);
|
|
c_swap = HistoryDealGetDouble(c_dealticket, DEAL_SWAP);
|
|
c_profit = HistoryDealGetDouble(c_dealticket, DEAL_PROFIT);
|
|
c_profitPoint = MathRound((c_dealTyp == DEAL_TYPE_BUY ? c_clPrice - c_opPrice : c_opPrice - c_clPrice) / c_point);
|
|
c_duration = TimeElapsedToString(c_clTime - c_opTime);
|
|
c_clComment = HistoryDealGetString(c_dealticket, DEAL_COMMENT);
|
|
c_magic = HistoryDealGetInteger(c_dealticket, DEAL_MAGIC);
|
|
}
|
|
|
|
|
|
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;}
|
|
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
// Print("c_optime: ", TimeToString(c_opTime, TIME_DATE | TIME_MINUTES | TIME_SECONDS),
|
|
// " c_type: ", c_type,
|
|
// " c_volume: ", c_volume,
|
|
// " c_opPrice: ", c_opPrice,
|
|
// " c_sl: ", c_sl,
|
|
// " c_tp: ", c_tp,
|
|
// " c_clPrice: ", c_clPrice,"\n",
|
|
// " c_clTime: ", TimeToString(c_clTime, TIME_DATE | TIME_MINUTES | TIME_SECONDS),
|
|
// " commis: " ,c_commission,
|
|
// " swap: ", c_swap,
|
|
// " profit: ", c_profit,"\n",
|
|
// " profit point: ", c_profitPoint,
|
|
// " duration: ", c_duration,
|
|
// " opcoment: ", c_opComment," cclcom ",c_clComment);
|
|
if(saveCsv) {AppendDataToCSV();}
|
|
return true;
|
|
}
|
|
|
|
double CHistory::Percent(double value,double divider)
|
|
{
|
|
if(MathAbs(value)<=FLT_EPSILON)
|
|
return(0);
|
|
return(100*value/divider);
|
|
}
|
|
|
|
bool CHistory::AppendDataToCSV()
|
|
{
|
|
int retryCount = 0; // Initialize the retry counter
|
|
const int maxRetries = 3; // Maximum allowed retries
|
|
|
|
while (retryCount < maxRetries)
|
|
{
|
|
int handle = FileOpen(AnalyzePathFile(), FILE_READ | FILE_WRITE | FILE_CSV|FILE_ANSI,','); // | FILE_ANSI, ','
|
|
if (handle != INVALID_HANDLE)
|
|
{
|
|
// File opened successfully, append data
|
|
FileSeek(handle, 0, SEEK_END);
|
|
FileWrite(handle,
|
|
c_opTime, Symbol(), c_magic, c_type, c_volume, c_opPrice, c_sl, c_tp,
|
|
c_clPrice, c_clTime, c_commission, c_swap, c_profit, c_profitPoint,
|
|
c_duration, c_opComment, c_clComment);
|
|
FileClose(handle);
|
|
|
|
return true; // Successfully written to the file
|
|
}
|
|
else
|
|
{
|
|
// Handle the file open error
|
|
retryCount++;
|
|
ResetLastError();
|
|
string errorDetails = TRANSLATION_FILED_TO_OPEN_FILE_WRITING + AnalyzePathFile() +
|
|
TRANSLATION_MESSAGE_ERROR + IntegerToString(GetLastError());
|
|
m_FileSave.IniFileSaveRun(errorDetails, false);
|
|
m_FileSave.IniFileSave(__FUNCTION__ + ":: " + errorDetails, false);
|
|
|
|
// Prepare warning message for the user
|
|
string caption = TRANSLATION_FILED_TO_OPEN_FILE_WRITING2;
|
|
string warning = StringFormat(
|
|
"%s\n%s\n\n%s\n%s (%d/%d)%s%d%s",
|
|
TRANSLATION_SAVE_ERR_MESSAG_FILE, AnalyzePathFile(),
|
|
TRANSLATION_SAVE_ERR_MESSAG_FILE_OPEN, TRANSLATION_SAVE_ERR_MESSAG_ATTEMPT,
|
|
retryCount, maxRetries, TRANSLATION_SAVE_ERR_MESSAG_WILL, maxRetries,
|
|
TRANSLATION_SAVE_ERR_MESSAG_ATTEMPTS
|
|
);
|
|
|
|
// Show the warning message
|
|
int ret = MessageBox(warning, caption, MB_OK | MB_ICONWARNING);
|
|
if (ret == IDOK)
|
|
{
|
|
ExportHistoryPositions("Break EA 651"); // GetExpertNameWithSum()
|
|
// continue; // User clicked OK, retry file access
|
|
}
|
|
}
|
|
}
|
|
|
|
// Final warning after maximum retries exceeded
|
|
string finalWarning = StringFormat(
|
|
"%s\n%s\n\n%s%d%s\n%s",
|
|
TRANSLATION_SAVE_ERR_MESSAG_FILE, AnalyzePathFile(),
|
|
TRANSLATION_SAVE_ERR_MESSAG_AFFTER, maxRetries,
|
|
TRANSLATION_SAVE_ERR_MESSAG_ATTEMPTS, TRANSLATION_SAVE_ERR_MESSAG_STOP
|
|
);
|
|
MessageBox(finalWarning, "Error", MB_OK | MB_ICONERROR);
|
|
|
|
ExpertRemove(); // Stop the expert after exceeding retries
|
|
|
|
return false;
|
|
}
|
|
|
|
string CHistory::GetExpertNameWithSum()
|
|
{
|
|
string result;
|
|
string expName = MQLInfoString(MQL_PROGRAM_NAME);
|
|
int totalSum = 0;
|
|
|
|
// Loop through each character in the expert name
|
|
for (int i = 0; i < StringLen(expName); i++)
|
|
{
|
|
int charValue = StringGetCharacter(expName, i);
|
|
totalSum += charValue;
|
|
}
|
|
|
|
result = expName +" EA_"+ IntegerToString(totalSum);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
string baseSymbol = "";
|
|
|
|
|
|
void GetMatchingSymbols(string &matchingSymbols[])
|
|
{
|
|
string currentSymbol = Symbol(); // Get the current chart symbol
|
|
|
|
if (baseSymbol == "")
|
|
{
|
|
baseSymbol = currentSymbol; // Set base symbol globally
|
|
ArrayResize(matchingSymbols, 1);
|
|
matchingSymbols[0] = currentSymbol; // Store the first matching symbol
|
|
Print("Base symbol initialized: ", baseSymbol);
|
|
return; // Return early with the base symbol
|
|
}
|
|
|
|
int minLength = MathMin(StringLen(baseSymbol), StringLen(currentSymbol));
|
|
int commonLength = 0;
|
|
|
|
for (int i = 0; i < minLength; i++)
|
|
{
|
|
if (StringGetCharacter(baseSymbol, i) == StringGetCharacter(currentSymbol, i))
|
|
commonLength++;
|
|
else
|
|
break;
|
|
}
|
|
|
|
string baseCore = StringSubstr(baseSymbol, 0, commonLength);
|
|
string currentCore = StringSubstr(currentSymbol, 0, commonLength);
|
|
|
|
if (baseCore != currentCore)
|
|
{
|
|
baseSymbol = currentSymbol;
|
|
ArrayResize(matchingSymbols, 1);
|
|
matchingSymbols[0] = currentSymbol;
|
|
Print("Symbol change detected: Previous Base = ", baseSymbol, ", New Core = ", currentCore);
|
|
}
|
|
else
|
|
{
|
|
ArrayResize(matchingSymbols, ArraySize(matchingSymbols) + 1);
|
|
matchingSymbols[ArraySize(matchingSymbols) - 1] = currentSymbol;
|
|
}
|
|
}
|
|
|
|
// Function to count rows in a CSV file
|
|
int CHistory::CountRowsInCSV() {
|
|
int row_count = 0;
|
|
string filename = AnalyzePathFile();
|
|
int file_handle = FileOpen(filename, FILE_READ | FILE_CSV | FILE_ANSI);
|
|
|
|
if (file_handle == INVALID_HANDLE) {
|
|
ResetLastError();
|
|
string txt = " ⚠ "+ TRANSLATION_FILED_TO_OPEN_FILE_WRITING3 + AnalyzePathFile() + TRANSLATION_MESSAGE_ERROR + IntegerToString(GetLastError());
|
|
m_FileSave.IniFileSave(__FUNCTION__+"::"+txt,false);
|
|
m_FileSave.IniFileSaveRun(txt,false);
|
|
return(-1);
|
|
}
|
|
|
|
// Read the file line by line
|
|
while (!FileIsEnding(file_handle)) // Loop until the end of the file
|
|
{
|
|
string firstColumn = FileReadString(file_handle); // Read the first column
|
|
if (firstColumn != "") // If the first column is not empty, increment the row count
|
|
{
|
|
row_count++;
|
|
}
|
|
// Skip the rest of the columns in the current row
|
|
while (!FileIsLineEnding(file_handle) && !FileIsEnding(file_handle))
|
|
{
|
|
FileReadString(file_handle); // Read and discard the remaining columns
|
|
}
|
|
}
|
|
|
|
|
|
FileClose(file_handle); // Close the file
|
|
return row_count - 1;
|
|
}
|
|
|
|
|
|
|
|
//--- global variable
|
|
double max_balance = -DBL_MAX;
|
|
double drawdown = 0.0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|