#include #include #include #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) CHashSethashset; //--- 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 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= 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; }