Account Statistics Default by Popov
2809 downloads / 17014 views / Created: 30.09.2013 Average Rating: 5
Addon Description
This is the default Account Statistics file.
You can modify the file and it will override the stats you see in the right side of the strategy page.
Usage:
1. Place it in "C:\Program Files\Forex Strategy Builder Pro\User Files\Code" folder.
2. Check on "Load custom account statistics at startup" option in Control Panel -> Custom Code page.
You can modify the file and it will override the stats you see in the right side of the strategy page.
Usage:
1. Place it in "C:\Program Files\Forex Strategy Builder Pro\User Files\Code" folder.
2. Check on "Load custom account statistics at startup" option in Control Panel -> Custom Code page.
Comments
//==============================================================
// Forex Strategy Builder
// Copyright © Miroslav Popov. All rights reserved.
//==============================================================
// THIS CODE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE.
//==============================================================
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using ForexStrategyBuilder.Infrastructure.Entities;
using ForexStrategyBuilder.Infrastructure.Enums;
using ForexStrategyBuilder.Infrastructure.Interfaces;
namespace ForexStrategyBuilder.Core.Backtest
{
public class AccountStatistics : IAccountStatistics
{
private const double Epsilon = 1E-10;
private string accountCurrency;
private int ambiguousBars;
private int averageLoss;
private double averageMoneyLoss;
private double averageMoneyProfit;
private int averageProfit;
private double equityPercentDrawdown;
private int executedOrders;
private double grossMoneyLoss;
private double grossMoneyProfit;
private bool isInfoInCurrency;
private bool isScanPerformed;
private int losingTrades;
private int maxBalance;
private int maxConsecLoses;
private int maxDrawdown;
private int maxEquityDrawdown;
private double maxMoneyBalance;
private double maxMoneyDrawdown;
private double maxMoneyEquityDrawdown;
private int minBalance;
private double minMoneyBalance;
private double moneyEquityPercentDrawdown;
private double moneyProfitPerDay;
private int sentOrders;
private int testedBars;
private int testedDays;
private int timeInPosition;
private int totalTrades;
private int winningTrades;
private double stdProfitLoss;
public bool IsCriteriaPassed { get; private set; }
public int NetBalance { get; private set; }
public double NetMoneyBalance { get; private set; }
public double WinLossRatio { get; private set; }
public int ProfitPerDay { get; private set; }
public double SystemQualityNumber { get; private set; }
public double SharpeRatio { get; private set; }
public double AverageHprPercent { get; private set; }
public double GeometricHprPercent { get; private set; }
public double AnnualizedProfit { get; private set; }
public double ProfitFactor { get; private set; }
public void CalculateAccountStats(IBacktester backtester)
{
IStrategy strategy = backtester.Strategy;
IDataSet dataSet = backtester.DataSet;
IProfile profile = backtester.Profile;
int bars = dataSet.Bars;
int firstBar = strategy.FirstBar;
isInfoInCurrency = profile.IsInfoInCurrency;
accountCurrency = profile.AccountCurrency;
isScanPerformed = backtester.IsScanPerformed;
testedBars = bars - firstBar;
sentOrders = backtester.SentOrders;
NetBalance = backtester.NetBalance;
NetMoneyBalance = backtester.NetMoneyBalance;
minBalance = backtester.MinBalance;
maxBalance = backtester.MaxBalance;
ProfitPerDay = backtester.ProfitPerDay;
moneyProfitPerDay = backtester.MoneyProfitPerDay;
minMoneyBalance = backtester.MinMoneyBalance;
maxMoneyBalance = backtester.MaxMoneyBalance;
maxDrawdown = backtester.MaxDrawdown;
maxEquityDrawdown = backtester.MaxEquityDrawdown;
maxMoneyDrawdown = backtester.MaxMoneyDrawdown;
maxMoneyEquityDrawdown = backtester.MaxMoneyEquityDrawdown;
equityPercentDrawdown = backtester.EquityPercentDrawdown;
moneyEquityPercentDrawdown = backtester.MoneyEquityPercentDrawdown;
grossMoneyProfit = backtester.GrossMoneyProfit;
grossMoneyLoss = backtester.GrossMoneyLoss;
ProfitFactor = Math.Abs(grossMoneyLoss - 0) < Epsilon
? grossMoneyProfit
: grossMoneyProfit/-grossMoneyLoss;
testedDays = backtester.TestedDays;
WinLossRatio = backtester.WinLossRatio;
winningTrades = backtester.WinningTrades;
losingTrades = backtester.LosingTrades;
totalTrades = backtester.TotalTrades;
// Bars in position
int barsInPosition = 0;
for (int bar = firstBar; bar < bars; bar++)
if (backtester.Session[bar].Positions > 0)
barsInPosition++;
// Ambiguous bars.
ambiguousBars = 0;
for (int bar = firstBar; bar < bars; bar++)
if (backtester.Session[bar].BacktestEval == BacktestEval.Ambiguous)
ambiguousBars++;
timeInPosition = (int) Math.Round(100.0*barsInPosition/(bars - firstBar));
AnnualizedProfit = (365f/testedDays)*(NetMoneyBalance - profile.InitialAccount);
CalculateExecutedOrders(backtester);
CalculateMaxConsecutiveLoses(backtester);
CalculateProfitLoss(backtester);
CalculateSystemQualityNumber(backtester);
CalculateSharpeRatioAndHpr(backtester);
}
public void CalculateAcceptanceCriteria(IBacktester backtester, AcceptanceCriteria criteria)
{
IsCriteriaPassed = false;
if (criteria.UseMaxAmbiguousBars && ambiguousBars > criteria.MaxAmbiguousBars)
return;
if (criteria.UseMinProfitPerDay && ProfitPerDay < criteria.MinProfitPerDay)
return;
if (criteria.UseMaxMoneyEquityDrawdown && maxMoneyEquityDrawdown > criteria.MaxMoneyEquityDrawdown)
return;
if (criteria.UseMaxPointsEquityDrawdown && maxEquityDrawdown > criteria.MaxPointsEquityDrawdown)
return;
if (criteria.UseMaxEquityPercentDrawdown && moneyEquityPercentDrawdown > criteria.MaxEquityPercentDrawdown)
return;
if (criteria.UseMinCountOfTrades && totalTrades < criteria.MinCountOfTrades)
return;
if (criteria.UseMaxCountOfTrades && totalTrades > criteria.MaxCountOfTrades)
return;
if (criteria.UseMinWinLossRatio && WinLossRatio < criteria.MinWinLossRatio)
return;
if (criteria.UseMinSharpeRatio && SharpeRatio < criteria.MinSharpeRatio)
return;
if (criteria.UseMaxConsecLosses && maxConsecLoses > criteria.MaxConsecLosses)
return;
if (criteria.UseMinSystemQualityNumber && SystemQualityNumber < criteria.MinSystemQualityNumber)
return;
if (criteria.UseMaxSmoothBalancePercent)
{
double maxDeviationPercent = CalculateMaxSmoothBalanceDeviationPercent(backtester);
if (maxDeviationPercent > criteria.MaxSmoothBalancePercent/100.0)
return;
}
IsCriteriaPassed = true;
}
public InfoRecord[] GetInfoRecords()
{
var records = new List();
string unit = isInfoInCurrency
? " " + accountCurrency
: " points";
records.Add(new InfoRecord
{
Name = "Acceptance criteria",
Value = IsCriteriaPassed ? "Fulfilled" : "Not fulfilled",
Flag = IsCriteriaPassed
? InfoRecordFlag.Normal
: InfoRecordFlag.Bad
});
records.Add(new InfoRecord
{
Name = "Net balance",
Value = isInfoInCurrency
? NetMoneyBalance.ToString("F2") + unit
: NetBalance + unit,
Flag = NetMoneyBalance < 0
? InfoRecordFlag.Bad
: InfoRecordFlag.Normal
});
records.Add(new InfoRecord
{
Name = "Intrabar scanning",
Value = isScanPerformed ? "Accomplished" : "Not accomplished",
Flag = ambiguousBars > 0 && !isScanPerformed
? InfoRecordFlag.Bad
: InfoRecordFlag.Normal
});
records.Add(new InfoRecord
{
Name = "Ambiguous bars",
Value = ambiguousBars.ToString(CultureInfo.InvariantCulture),
Flag = ambiguousBars > 0
? InfoRecordFlag.Bad
: InfoRecordFlag.Normal
});
records.Add(new InfoRecord
{
Name = "Profit per day",
Value = isInfoInCurrency
? moneyProfitPerDay.ToString("F2") + unit
: ProfitPerDay + unit,
Flag = ProfitPerDay < 0
? InfoRecordFlag.Bad
: InfoRecordFlag.Normal
});
records.Add(new InfoRecord
{
Name = "Max consecutive loses",
Value = maxConsecLoses.ToString(CultureInfo.InvariantCulture),
});
records.Add(new InfoRecord
{
Name = "System quality number",
Value = SystemQualityNumber.ToString("F2"),
Flag = SystemQualityNumber < 1
? InfoRecordFlag.Bad
: InfoRecordFlag.Normal
});
records.Add(new InfoRecord
{
Name = "Std dev profit loss",
Value = stdProfitLoss.ToString("F2"),
});
records.Add(new InfoRecord
{
Name = "Sharpe ratio",
Value = SharpeRatio.ToString("F2"),
});
records.Add(new InfoRecord
{
Name = "Average HPR",
Value = AverageHprPercent.ToString("F2") + " %",
});
records.Add(new InfoRecord
{
Name = "Profit factor",
Value = ProfitFactor.ToString("F2"),
});
records.Add(new InfoRecord
{
Name = "Tested bars",
Value = testedBars.ToString(CultureInfo.InvariantCulture),
});
records.Add(new InfoRecord
{
Name = "Minimum balance",
Value = isInfoInCurrency
? minMoneyBalance.ToString("F2") + unit
: minBalance + unit
});
records.Add(new InfoRecord
{
Name = "Maximum balance",
Value = isInfoInCurrency
? maxMoneyBalance.ToString("F2") + unit
: maxBalance + unit
});
records.Add(new InfoRecord
{
Name = "Max balance drawdown",
Value = isInfoInCurrency
? maxMoneyDrawdown.ToString("F2") + unit
: maxDrawdown + unit,
});
records.Add(new InfoRecord
{
Name = "Max equity drawdown",
Value = isInfoInCurrency
? maxMoneyEquityDrawdown.ToString("F2") + unit
: maxEquityDrawdown + unit,
});
records.Add(new InfoRecord
{
Name = "Max equity drawdown",
Value = isInfoInCurrency
? moneyEquityPercentDrawdown.ToString("F2") + " %"
: equityPercentDrawdown.ToString("F2") + " %",
});
records.Add(new InfoRecord
{
Name = "Average profit",
Value = isInfoInCurrency
? averageMoneyProfit.ToString("F2") + unit
: averageProfit + unit,
});
records.Add(new InfoRecord
{
Name = "Average loss",
Value = isInfoInCurrency
? averageMoneyLoss.ToString("F2") + unit
: averageLoss + unit,
});
records.Add(new InfoRecord
{
Name = "Executed orders",
Value = executedOrders.ToString(CultureInfo.InvariantCulture),
});
records.Add(new InfoRecord
{
Name = "Winning trades",
Value = winningTrades.ToString(CultureInfo.InvariantCulture),
});
records.Add(new InfoRecord
{
Name = "Losing trades",
Value = losingTrades.ToString(CultureInfo.InvariantCulture),
});
records.Add(new InfoRecord
{
Name = "Win/loss ratio",
Value = WinLossRatio.ToString("F2"),
});
records.Add(new InfoRecord
{
Name = "Time in position",
Value = timeInPosition + " %",
});
return records.ToArray();
}
private void CalculateExecutedOrders(IBacktester backtester)
{
executedOrders = 0;
for (int ord = 0; ord < sentOrders; ord++)
if (backtester.OrdFromNumb(ord).OrdStatus == OrderStatus.Executed)
executedOrders++;
}
private void CalculateSystemQualityNumber(IBacktester backtester)
{
// Build the Trades List Curve
var trades = new List();
for (int pos = 0; pos < backtester.PositionsTotal; pos++)
{
Position position = backtester.PosFromNumb(pos);
if (position.Transaction != Transaction.Close &&
position.Transaction != Transaction.Reduce &&
position.Transaction != Transaction.Reverse)
continue; // There is no profit/loss taken.
trades.Add(position.ProfitLoss);
}
stdProfitLoss = StdDev(trades);
if (trades.Count < 30)
SystemQualityNumber = 0;
else
SystemQualityNumber = Math.Sqrt(trades.Count)*trades.Average()/stdProfitLoss;
}
private void CalculateSharpeRatioAndHpr(IBacktester backtester)
{
double previousBalance = backtester.Profile.InitialAccount;
var holdingPeriodReturnList = new List();
for (int pos = 0; pos < backtester.PositionsTotal; pos++)
{
Position position = backtester.PosFromNumb(pos);
if (position.Transaction != Transaction.Close &&
position.Transaction != Transaction.Reduce &&
position.Transaction != Transaction.Reverse)
continue; // There is no profit/loss taken.
double profit = position.MoneyProfitLoss;
holdingPeriodReturnList.Add(1 + profit/previousBalance);
previousBalance += profit;
}
if (holdingPeriodReturnList.Count == 0) return;
double averageHoldingPeriodReturn = holdingPeriodReturnList.Average();
double hprStdDev = StdDev(holdingPeriodReturnList);
SharpeRatio = (averageHoldingPeriodReturn - 1)/hprStdDev;
AverageHprPercent = 100*(averageHoldingPeriodReturn - 1);
GeometricHprPercent = 100*(Math.Pow((NetMoneyBalance/backtester.Profile.InitialAccount),
(1.0/holdingPeriodReturnList.Count)) - 1);
}
private void CalculateMaxConsecutiveLoses(IBacktester backtester)
{
maxConsecLoses = 0;
int sum = 0;
for (int pos = 0; pos < backtester.PositionsTotal; pos++)
{
Position position = backtester.PosFromNumb(pos);
if (position.Transaction != Transaction.Close &&
position.Transaction != Transaction.Reduce &&
position.Transaction != Transaction.Reverse)
continue; // There is no profit/loss taken.
if (position.ProfitLoss < -Epsilon)
sum++;
else if (position.ProfitLoss > Epsilon)
sum = 0;
if (sum > maxConsecLoses)
maxConsecLoses = sum;
}
}
private void CalculateProfitLoss(IBacktester backtester)
{
int countLoses = 0;
int countProfits = 0;
double grossProfit = 0;
double grossLoss = 0;
for (int pos = 0; pos < backtester.PositionsTotal; pos++)
{
Position position = backtester.PosFromNumb(pos);
if (position.Transaction != Transaction.Close &&
position.Transaction != Transaction.Reduce &&
position.Transaction != Transaction.Reverse)
continue; // There is no profit/loss taken.
double profit = position.ProfitLoss;
if (profit < -Epsilon)
{
countLoses++;
grossLoss += profit;
grossMoneyLoss += position.MoneyProfitLoss;
}
else if (profit > Epsilon)
{
countProfits++;
grossProfit += profit;
grossMoneyProfit += position.MoneyProfitLoss;
}
}
if (countProfits != 0)
averageMoneyProfit = grossMoneyProfit/countProfits;
if (countLoses != 0)
averageMoneyLoss = grossMoneyLoss/countLoses;
if (countProfits != 0)
averageProfit = (int) Math.Round(grossProfit/countProfits);
if (countLoses != 0)
averageLoss = (int) Math.Round(grossLoss/countLoses);
}
private double StdDev(IEnumerable values)
{
double mean = 0.0;
double sum = 0.0;
double stdDev = 0.0;
int n = 0;
foreach (double val in values)
{
n++;
double delta = val - mean;
mean += delta/n;
sum += delta*(val - mean);
}
if (1 < n)
stdDev = Math.Sqrt(sum/(n - 1));
return stdDev;
}
private double CalculateMaxSmoothBalanceDeviationPercent(IBacktester backtester)
{
int firstBar = backtester.Strategy.FirstBar;
int bars = backtester.DataSet.Bars;
const int checkPoints = 100;
double netBalance = NetMoneyBalance;
double startBalance = backtester.Profile.InitialAccount;
double maxDeviationPercent = 0;
for (int i = 1; i <= checkPoints; i++)
{
var bar = (int) (firstBar + i*((bars - firstBar)/(checkPoints + 1.0)));
double checkPointBalance = backtester.MoneyBalance(bar);
double targetBalance = startBalance + i*(netBalance - startBalance)/(checkPoints + 1.0);
double deviationPercent = Math.Abs((targetBalance - checkPointBalance)/targetBalance);
if (maxDeviationPercent < deviationPercent)
maxDeviationPercent = deviationPercent;
}
return maxDeviationPercent;
}
}
}
Risk warning: Forex, spread bets and CFD are leveraged products. They may not be suitable for you as they carry a high degree of risk to your capital and you can lose more than your initial investment. You should ensure you understand all of the risks.
Copyright © 2006 - 2024, Forex Software Ltd.;
Copyright © 2006 - 2024, Forex Software Ltd.;