using System;
using System.Collections.Generic;
using System.Linq;
using NT8.Core.Intelligence;
using NT8.Core.Logging;
namespace NT8.Core.Analytics
{
///
/// Grade-level aggregate analysis report.
///
public class GradePerformanceReport
{
///
/// Metrics by grade.
///
public Dictionary MetricsByGrade { get; set; }
///
/// Accuracy by grade.
///
public Dictionary GradeAccuracy { get; set; }
///
/// Suggested threshold.
///
public TradeGrade SuggestedThreshold { get; set; }
///
/// Creates a report instance.
///
public GradePerformanceReport()
{
MetricsByGrade = new Dictionary();
GradeAccuracy = new Dictionary();
SuggestedThreshold = TradeGrade.F;
}
}
///
/// Analyzes performance by confluence grade.
///
public class GradePerformanceAnalyzer
{
private readonly ILogger _logger;
private readonly PerformanceCalculator _calculator;
///
/// Initializes analyzer.
///
/// Logger dependency.
public GradePerformanceAnalyzer(ILogger logger)
{
if (logger == null)
throw new ArgumentNullException("logger");
_logger = logger;
_calculator = new PerformanceCalculator(logger);
}
///
/// Produces grade-level performance report.
///
/// Trade records.
/// Performance report.
public GradePerformanceReport AnalyzeByGrade(List trades)
{
if (trades == null)
throw new ArgumentNullException("trades");
try
{
var report = new GradePerformanceReport();
foreach (TradeGrade grade in Enum.GetValues(typeof(TradeGrade)))
{
var subset = trades.Where(t => t.Grade == grade).ToList();
report.MetricsByGrade[grade] = _calculator.Calculate(subset);
report.GradeAccuracy[grade] = CalculateGradeAccuracy(grade, trades);
}
report.SuggestedThreshold = FindOptimalThreshold(trades);
return report;
}
catch (Exception ex)
{
_logger.LogError("AnalyzeByGrade failed: {0}", ex.Message);
throw;
}
}
///
/// Calculates percentage of profitable trades for a grade.
///
/// Target grade.
/// Trade records.
/// Accuracy in range [0,1].
public double CalculateGradeAccuracy(TradeGrade grade, List trades)
{
if (trades == null)
throw new ArgumentNullException("trades");
try
{
var subset = trades.Where(t => t.Grade == grade).ToList();
if (subset.Count == 0)
return 0.0;
var winners = subset.Count(t => t.RealizedPnL > 0.0);
return (double)winners / subset.Count;
}
catch (Exception ex)
{
_logger.LogError("CalculateGradeAccuracy failed: {0}", ex.Message);
throw;
}
}
///
/// Finds threshold with best expectancy for accepted grades and above.
///
/// Trade records.
/// Suggested threshold grade.
public TradeGrade FindOptimalThreshold(List trades)
{
if (trades == null)
throw new ArgumentNullException("trades");
try
{
var ordered = new List
{
TradeGrade.APlus,
TradeGrade.A,
TradeGrade.B,
TradeGrade.C,
TradeGrade.D,
TradeGrade.F
};
var bestGrade = TradeGrade.F;
var bestExpectancy = double.MinValue;
foreach (var threshold in ordered)
{
var accepted = trades.Where(t => (int)t.Grade >= (int)threshold).ToList();
if (accepted.Count == 0)
continue;
var expectancy = _calculator.CalculateExpectancy(accepted);
if (expectancy > bestExpectancy)
{
bestExpectancy = expectancy;
bestGrade = threshold;
}
}
return bestGrade;
}
catch (Exception ex)
{
_logger.LogError("FindOptimalThreshold failed: {0}", ex.Message);
throw;
}
}
///
/// Gets metrics grouped by grade.
///
/// Trade records.
/// Metrics by grade.
public Dictionary GetMetricsByGrade(List trades)
{
if (trades == null)
throw new ArgumentNullException("trades");
try
{
var result = new Dictionary();
foreach (TradeGrade grade in Enum.GetValues(typeof(TradeGrade)))
{
var subset = trades.Where(t => t.Grade == grade).ToList();
result.Add(grade, _calculator.Calculate(subset));
}
return result;
}
catch (Exception ex)
{
_logger.LogError("GetMetricsByGrade failed: {0}", ex.Message);
throw;
}
}
}
}