Virtual_by_fxsaber/Include/fxsaber/Virtual/HistoryOrders.mqh

496 lines
27 KiB
MQL5
Raw Permalink Normal View History

<EFBFBD><EFBFBD>#define RESERVE_HISTORY_ORDERS 2000
#include "Order.mqh"
class HISTORYORDERS
{
private:
// HISTORYORDERS::Swap
int StartPos;
int LastPos;
int PrevAmountHistoryOrders;
int TradesProfit;
int TradesLoss;
int TradesShortTotal;
int TradesLongTotal;
int TradesShortProfit;
int TradesLongProfit;
double InitialDeposit;
double Withdrawal;
double GrossProfit;
double GrossLoss;
double MaxProfit;
double MaxLoss;
double Balance;
double MaxBalance;
double BalanceDD;
void InitStatistic()
{
this.LastPos = this.StartPos;
this.TradesProfit = 0;
this.TradesLoss = 0;
this.TradesShortTotal = 0;
this.TradesLongTotal = 0;
this.TradesShortProfit = 0;
this.TradesLongProfit = 0;
this.InitialDeposit = ((this.StartPos < this.AmountHistoryOrders) &&
(this.HistoryOrders[this.LastPos] == OP_BALANCE)) ? this.HistoryOrders[this.LastPos++].GetProfit() : 0;
this.Withdrawal = 0;
this.GrossProfit = 0;
this.GrossLoss = 0;
this.MaxProfit = 0;
this.MaxLoss = 0;
this.Balance = 0;
this.MaxBalance = 0;
this.BalanceDD = 0;
return;
}
void SetOrder( const double Profit, int &TradesLongShortProfit, int &TradesLongShortTotal )
{
if (Profit > 0)
{
if (Profit > this.MaxProfit)
this.MaxProfit = Profit;
this.GrossProfit += Profit;
this.TradesProfit++;
TradesLongShortProfit++;
}
else if (Profit < 0)
{
if (Profit < this.MaxLoss)
this.MaxLoss = Profit;
this.GrossLoss += Profit;
this.TradesLoss++;
}
this.Balance += Profit;
if (this.Balance > this.MaxBalance)
this.MaxBalance = this.Balance;
else if (this.MaxBalance - this.Balance > this.BalanceDD)
this.BalanceDD = this.MaxBalance - this.Balance;
TradesLongShortTotal++;
return;
}
void Refresh( void )
{
while (this.LastPos < this.AmountHistoryOrders)
{
switch (this.HistoryOrders[this.LastPos].GetType())
{
case ORDER_TYPE_BUY:
this.SetOrder(this.HistoryOrders[this.LastPos].GetFullProfit(), this.TradesLongProfit, this.TradesLongTotal);
break;
case ORDER_TYPE_SELL:
this.SetOrder(this.HistoryOrders[this.LastPos].GetFullProfit(), this.TradesShortProfit, this.TradesShortTotal);
break;
case OP_BALANCE:
if (this.LastPos != this.StartPos)
this.Withdrawal += this.HistoryOrders[this.LastPos].GetProfit();
else
this.InitialDeposit = this.HistoryOrders[this.LastPos].GetProfit();
}
this.LastPos++;
}
return;
}
static string DoubleToString( const double Num, const int digits )
{
return((Num == DBL_MAX) ? "Max" : ::DoubleToString(Num, digits));
}
protected:
// HISTORYORDERS::Swap
ORDER_BASE HistoryOrders[];
int ReserveHistoryOrders;
bool FlagChange;
bool Swap( HISTORYORDERS &OtherHistoryOrders )
{
#ifdef __MQL5__
const bool Res = ::ArraySwap(this.HistoryOrders, OtherHistoryOrders.HistoryOrders);
#else // #ifdef __MQL5__
const bool Res = false;
#endif // #ifdef __MQL5__ #else
if (Res)
{
this.AmountHistoryOrders = OtherHistoryOrders.AmountHistoryOrders;
this.ReserveHistoryOrders = OtherHistoryOrders.ReserveHistoryOrders;
this.FlagChange = OtherHistoryOrders.FlagChange;
this.StartPos = OtherHistoryOrders.StartPos;
this.LastPos = OtherHistoryOrders.LastPos;
this.PrevAmountHistoryOrders = OtherHistoryOrders.PrevAmountHistoryOrders;
this.TradesProfit = OtherHistoryOrders.TradesProfit;
this.TradesLoss = OtherHistoryOrders.TradesLoss;
this.TradesShortTotal = OtherHistoryOrders.TradesShortTotal;
this.TradesLongTotal = OtherHistoryOrders.TradesLongTotal;
this.TradesShortProfit = OtherHistoryOrders.TradesShortProfit;
this.TradesLongProfit = OtherHistoryOrders.TradesLongProfit;
this.InitialDeposit = OtherHistoryOrders.InitialDeposit;
this.Withdrawal = OtherHistoryOrders.Withdrawal;
this.GrossProfit = OtherHistoryOrders.GrossProfit;
this.GrossLoss = OtherHistoryOrders.GrossLoss;
this.MaxProfit = OtherHistoryOrders.MaxProfit;
this.MaxLoss = OtherHistoryOrders.MaxLoss;
this.Balance = OtherHistoryOrders.Balance;
this.MaxBalance = OtherHistoryOrders.MaxBalance;
this.BalanceDD = OtherHistoryOrders.BalanceDD;
}
return(Res);
}
public: // ORDER::IsChange2
void AddHistoryOrder( const ORDER_BASE &Order )
{
if (this.AmountHistoryOrders == this.ReserveHistoryOrders)
// https://www.mql5.com/ru/forum/282062/page48#comment_50770216
this.ReserveHistoryOrders = ::ArrayResize(this.HistoryOrders, ::MathMax(RESERVE_HISTORY_ORDERS,
this.ReserveHistoryOrders << 1));
// ORDER OrderTmp = Order; // https://www.mql5.com/ru/forum/492248/page3#comment_57718727
ORDER OrderTmp; OrderTmp = Order;
if (OrderTmp.IsNotNull() && this.AmountHistoryOrders < this.ReserveHistoryOrders)
{
OrderTmp.ToHistory();
this.HistoryOrders[this.AmountHistoryOrders++] = OrderTmp;
this.FlagChange = true;
}
return;
}
protected:
bool CopyFrom( const HISTORYORDERS* SourceOrders )
{
this.AmountHistoryOrders = SourceOrders.AmountHistoryOrders;
this.ReserveHistoryOrders = ::ArrayCopy(this.HistoryOrders, SourceOrders.HistoryOrders);
this.InitStat();
return(this.ReserveHistoryOrders == ::ArraySize(SourceOrders.HistoryOrders));
}
bool GetReduceOrder( ORDER_BASE &Order, const int Pos ) const
{
const bool Res = (Pos >= 0) && (Pos < this.AmountHistoryOrders);
if (Res)
{
double Sum = 0;
for (int i = 0; i <= Pos; i++)
Sum += this.HistoryOrders[i].GetFullProfit();
Order.Set(this.HistoryOrders[Pos].GetTicket(), OP_BALANCE, 0, NULL, "ReduceHistory", 0,
this.HistoryOrders[Pos].GetCloseTimeMsc(), 0, 0, 0,
this.HistoryOrders[Pos].GetCloseTimeMsc(), 0, Sum);
}
return(Res);
}
bool GetReduceOrder( ORDER_BASE &Order, const int Pos, const bool ChangeHistory )
{
const bool Res = this.GetReduceOrder(Order, Pos);
if (Res && ChangeHistory && Pos)
{
int Amount = 1;
for (int i = Pos + 1; i < this.AmountHistoryOrders; i++)
this.HistoryOrders[Amount++] = this.HistoryOrders[i];
this.HistoryOrders[0] = Order;
this.AmountHistoryOrders = Amount;
this.InitStatistic();
}
return(Res);
}
TICKET_TYPE GetMaxHistoryTicket( void ) const
{
TICKET_TYPE MaxTicket = 0;
for (int i = this.AmountHistoryOrders - 1; i >= 0; i--)
{
const TICKET_TYPE Ticket = this.HistoryOrders[i].GetTicket();
if (Ticket > MaxTicket)
MaxTicket = Ticket;
}
return(MaxTicket);
}
public:
int AmountHistoryOrders;
HISTORYORDERS() : AmountHistoryOrders(0), ReserveHistoryOrders(0), PrevAmountHistoryOrders(0), StartPos(0), FlagChange(true)
{
this.InitStatistic();
}
bool ResetTickets( void )
{
long Tickets[][2];
const int Size = ::ArrayResize(Tickets, this.AmountHistoryOrders);
#define VALUE 0
#define INDEX 1
for (int i = 0; i < Size; i++)
{
// Tickets[i][VALUE] = this.HistoryOrders[i].GetTicket();
Tickets[i][VALUE] = this.HistoryOrders[i].GetOpenTimeMsc();
Tickets[i][INDEX] = i;
}
::ArraySort(Tickets);
for (int i = 0; i < Size; i++)
this.HistoryOrders[(int)Tickets[i][INDEX]].SetTicket(i + 1);
#undef INDEX
#undef VALUE
ORDER::SetTicketCount(Size);
return(true);
}
bool InitStat( const long StartTime = 0 )
{
if (this.AmountHistoryOrders)
{
if (this.StartPos < this.AmountHistoryOrders)
{
if (this.HistoryOrders[this.StartPos] != StartTime)
{
if (this.HistoryOrders[this.StartPos] < StartTime)
{
while ((++this.StartPos < this.AmountHistoryOrders) && (this.HistoryOrders[this.StartPos] < StartTime))
;
this.InitStatistic();
}
else if (this.StartPos && (this.HistoryOrders[0] <= StartTime))
{
const int PrevStartPos = this.StartPos;
while ((--this.StartPos >= 0) && (this.HistoryOrders[this.StartPos] >= StartTime))
;
this.StartPos++;
if (this.StartPos != PrevStartPos)
this.InitStatistic();
}
}
}
else if (this.HistoryOrders[this.StartPos - 1] > StartTime)
{
while ((--this.StartPos >= 0) && (this.HistoryOrders[this.StartPos] >= StartTime))
;
this.StartPos++;
this.InitStatistic();
}
}
return(true);
}
double TesterStatistics( const ENUM_STATISTICS StatID )
{
double Res = 0;
this.Refresh();
switch (StatID)
{
case STAT_INITIAL_DEPOSIT: // =0G5=85 =0G0;L=>3> 45?>78B0
Res = this.InitialDeposit;
break;
case STAT_WITHDRAWAL: // >;8G5AB2> 2K2545==KE A> AG5B0 A@54AB2
Res = this.Withdrawal;
break;
case STAT_PROFIT: // '8AB0O ?@81K;L ?> >:>=G0=88 B5AB8@>20=8O
Res = this.GrossLoss + this.GrossProfit;
break;
case STAT_GROSS_PROFIT: // 1I0O ?@81K;L, AC<<0 2A5E ?@81K;L=KE (?>;>68B5;L=KE) B@594>2. =0G5=85 1>;LH5 8;8 @02=> =C;N
Res = this.GrossProfit;
break;
case STAT_GROSS_LOSS: // 1I89 C1KB>:, AC<<0 2A5E C1KB>G=KE (>B@8F0B5;L=KE) B@594>2. =0G5=85 <5=LH5 8;8 @02=> =C;N
Res = this.GrossLoss;
break;
case STAT_MAX_PROFITTRADE: // 0:A8<0;L=0O ?@81K;L  =081>;LH55 7=0G5=85 A@548 2A5E ?@81K;L=KE B@594>2. =0G5=85 1>;LH5 8;8 @02=> =C;N
Res = this.MaxProfit;
break;
case STAT_MAX_LOSSTRADE: // 0:A8<0;L=K9 C1KB>:  =08<5=LH55 7=0G5=85 A@548 2A5E C1KB>G=KE B@594>2. =0G5=85 <5=LH5 8;8 @02=> =C;N
Res = this.MaxLoss;
break;
case STAT_BALANCE_DD: // 0:A8<0;L=0O ?@>A04:0 10;0=A0 2 45=L30E.  ?@>F5AA5 B>@3>2;8 10;0=A <>65B 8A?KB0BL <=>65AB2> ?@>A04>:, 15@5BAO =081>;LH55 7=0G5=85.
Res = this.BalanceDD;
break;
case STAT_EXPECTED_PAYOFF: // 0B5<0B8G5A:>5 >6840=85 2K83@KH0
{
const int Total = this.TradesLongTotal + this.TradesShortTotal;
Res = Total ? (this.GrossLoss + this.GrossProfit) / Total : 0;
}
break;
case STAT_PROFIT_FACTOR: // @81K;L=>ABL  >B=>H5=85 STAT_GROSS_PROFIT/STAT_GROSS_LOSS. A;8 STAT_GROSS_LOSS=0, B> ?@81K;L=>ABL ?@8=8<05B 7=0G5=85 DBL_MAX
Res = this.GrossLoss ? -this.GrossProfit / this.GrossLoss : DBL_MAX;
break;
case STAT_RECOVERY_FACTOR: // $0:B>@ 2>AAB0=>2;5=8O  >B=>H5=85 STAT_PROFIT/STAT_BALANCE_DD
Res = this.BalanceDD ? (this.GrossLoss + this.GrossProfit) / this.BalanceDD : 0;
break;
case STAT_DEALS: // >;8G5AB2> A>25@H5==KE A45;>:
Res = (this.TradesLongTotal + this.TradesShortTotal) << 1;
break;
case STAT_TRADES: // >;8G5AB2> B@594>2
Res = this.TradesLongTotal + this.TradesShortTotal;
break;
case STAT_PROFIT_TRADES: // @81K;L=K5 B@594K
Res = this.TradesProfit;
break;
case STAT_LOSS_TRADES: // #1KB>G=K5 B@594K
Res = this.TradesLoss;
break;
case STAT_SHORT_TRADES: // >@>B:85 B@594K
Res = this.TradesShortTotal;
break;
case STAT_LONG_TRADES: // ;8==K5 B@594K
Res = this.TradesLongTotal;
break;
case STAT_PROFIT_SHORTTRADES: // >@>B:85 ?@81K;L=K5 B@594K
Res = this.TradesShortProfit;
break;
case STAT_PROFIT_LONGTRADES: // ;8==K5 ?@81K;L=K5 B@594K
Res = this.TradesLongProfit;
break;
}
return(Res);
}
bool IsHistoryChanged( void )
{
const bool Res = (this.AmountHistoryOrders != this.PrevAmountHistoryOrders);
if (Res)
this.PrevAmountHistoryOrders = this.AmountHistoryOrders;
return(Res);
}
bool GetFlagChange( void )
{
const bool Res = this.FlagChange;
this.FlagChange = false;
return(Res);
}
string StatToString( const int &Amount )
{
return("Profit = " + HISTORYORDERS::DoubleToString(this.TesterStatistics(STAT_PROFIT) / Amount, 2) + ", " +
"Trades = " + HISTORYORDERS::DoubleToString(this.TesterStatistics(STAT_TRADES) / Amount, 0) + ", " +
"PF = " + HISTORYORDERS::DoubleToString(this.TesterStatistics(STAT_PROFIT_FACTOR), 2) + ", " +
"Mean = " + HISTORYORDERS::DoubleToString(this.TesterStatistics(STAT_EXPECTED_PAYOFF), 2) + ", " +
"MaxDD = " + HISTORYORDERS::DoubleToString(-this.TesterStatistics(STAT_BALANCE_DD) / Amount, 2) + ", " +
"RF = " + HISTORYORDERS::DoubleToString(this.TesterStatistics(STAT_RECOVERY_FACTOR), 2));
}
double CalcSwaps( const double &SwapShort, const double &SwapLong, const datetime &RolloverTime, const int &Rollover3Days )
{
double ChangeSwaps = 0;
for (int i = this.AmountHistoryOrders - 1; i >= 0; i--)
ChangeSwaps += this.HistoryOrders[i].SetSwap(SwapShort, SwapLong, RolloverTime, Rollover3Days);
this.InitStatistic();
return(ChangeSwaps);
}
void MoveHistory( HISTORYORDERS& pOrders )
{
for (int i = 0; i < this.AmountHistoryOrders; i++)
pOrders.AddHistoryOrder(this.HistoryOrders[i]);
this.AmountHistoryOrders = 0;
return;
}
};
#undef RESERVE_HISTORY_ORDERS