/// <summary> /// Updates the specified statistics with the new scores /// </summary> /// <param name="context">Context whose alpha has just completed analysis</param> public void OnAlphaAnalysisCompleted(AlphaAnalysisContext context) { // increment analysis completed counter Statistics.TotalAlphasAnalysisCompleted += 1; foreach (var scoreType in AlphaManager.ScoreTypes) { var score = context.Score.GetScore(scoreType); var currentTime = context.CurrentValues.TimeUtc; // online population average var mean = Statistics.MeanPopulationScore.GetScore(scoreType); var newMean = mean + (score - mean) / Statistics.TotalAlphasAnalysisCompleted; Statistics.MeanPopulationScore.SetScore(scoreType, newMean, currentTime); var newEma = score; if (Statistics.TotalAlphasAnalysisCompleted > 1) { // compute the traditional ema var ema = Statistics.RollingAveragedPopulationScore.GetScore(scoreType); newEma = score * _smoothingFactor + ema * (1 - _smoothingFactor); } Statistics.RollingAveragedPopulationScore.SetScore(scoreType, newEma, currentTime); } }
/// <summary> /// Updates the specified statistics with the new scores /// </summary> /// <param name="statistics">Statistics to be updated</param> /// <param name="context">Context whose alpha has just completed analysis</param> public void OnAlphaAnalysisCompleted(AlphaRuntimeStatistics statistics, AlphaAnalysisContext context) { // increment analysis completed counter statistics.TotalAlphasAnalysisCompleted += 1; foreach (var scoreType in AlphaManager.ScoreTypes) { var score = context.Score.GetScore(scoreType); var currentTime = context.CurrentValues.TimeUtc; var mean = statistics.MeanPopulationScore.GetScore(scoreType); var newMean = mean + (score - mean) / _populationMeanSamples; statistics.MeanPopulationScore.SetScore(scoreType, newMean, currentTime); var newEma = score; if (_populationMeanSamples > 1) { var ema = statistics.RollingAveragedPopulationScore.GetScore(scoreType); newEma = score * _smoothingFactor + ema * (1 - _smoothingFactor); } statistics.RollingAveragedPopulationScore.SetScore(scoreType, newEma, currentTime); } _populationMeanSamples++; }
/// <summary> /// Handles the <see cref="IAlgorithm.AlphasGenerated"/> event /// Increments total, long and short counters. Updates long/short ratio /// </summary> /// <param name="context">The newly generated alpha context</param> public void OnAlphaGenerated(AlphaAnalysisContext context) { // incremement total alpha counter Statistics.TotalAlphasGenerated++; // update long/short ratio statistics if (context.Alpha.Direction == AlphaDirection.Up) { Statistics.LongCount++; } else if (context.Alpha.Direction == AlphaDirection.Down) { Statistics.ShortCount++; } }
/// <summary> /// Handles the <see cref="IAlgorithm.AlphasGenerated"/> event. /// Keep daily and total counts of alpha by symbol /// </summary> /// <param name="context">The newly generated alpha analysis context</param> public void OnAlphaGenerated(AlphaAnalysisContext context) { if (!_dailyAlphaCountPerSymbol.ContainsKey(context.Symbol)) { _alphaCountPerSymbol[context.Symbol] = 1; _dailyAlphaCountPerSymbol[context.Symbol] = 1; } else { // track total assets for life of backtest _alphaCountPerSymbol[context.Symbol] += 1; // track daily assets _dailyAlphaCountPerSymbol[context.Symbol] += 1; } }
/// <summary> /// Computes an estimated value for the alpha. This is intended to be invoked at the end of the /// alpha period, i.e, when now == alpha.GeneratedTimeUtc + alpha.Period; /// </summary> /// <param name="statistics">Statistics to be updated</param> /// <param name="context">Context whose alpha has just closed</param> public void OnAlphaPeriodClosed(AlphaRuntimeStatistics statistics, AlphaAnalysisContext context) { // tradable volume (purposefully includes fractional shares) var volume = _tradablePercentOfVolume * context.InitialValues.Volume; // value of the entering the trade in the account currency var enterValue = volume * context.InitialValues.Price * context.InitialValues.QuoteCurrencyConversionRate; // value of exiting the trade in the account currency var exitValue = volume * context.CurrentValues.Price * context.CurrentValues.QuoteCurrencyConversionRate; // total value delta between enter and exit values var alphaValue = (int)context.Alpha.Direction * (exitValue - enterValue); context.Alpha.EstimatedValue = alphaValue; statistics.TotalEstimatedAlphaValue += alphaValue; }
/// <summary> /// Updates statistics when a new alpha signal is received by the alpha manager /// </summary> /// <param name="statistics">Statistics to be updated</param> /// <param name="context">Context whose alpha was just generated</param> public void OnAlphaReceived(AlphaRuntimeStatistics statistics, AlphaAnalysisContext context) { // incremement total alpha counter statistics.TotalAlphasGenerated++; // update long/short ratio statistics if (context.Alpha.Direction == AlphaDirection.Up) { _longCount++; } else if (context.Alpha.Direction == AlphaDirection.Down) { _shortCount++; } statistics.LongShortRatio = _shortCount == 0 ? 1m : _longCount / _shortCount; }
/// <inheritdoc /> public double Evaluate(AlphaAnalysisContext context, AlphaScoreType scoreType) { var alpha = context.Alpha; var startingValue = context.InitialValues.Get(alpha.Type); var currentValue = context.CurrentValues.Get(alpha.Type); switch (alpha.Direction) { case AlphaDirection.Down: return(currentValue < startingValue ? 1 : 0); case AlphaDirection.Flat: // can't really do percent changes with zero if (startingValue == 0) { return(currentValue == startingValue ? 1 : 0); } // TODO : Re-evaluate flat predictions, potentially adding IAlpha.Tolerance to say 'how flat' var deltaPercent = Math.Abs(currentValue - startingValue) / startingValue; if (alpha.Magnitude.HasValue) { return(Math.Abs(deltaPercent) < (decimal)Math.Abs(alpha.Magnitude.Value) ? 1 : 0); } // this is pretty much impossible, I suppose unless the ticks are large and/or volumes are small return(currentValue == startingValue ? 1 : 0); case AlphaDirection.Up: return(currentValue > startingValue ? 1 : 0); default: throw new ArgumentOutOfRangeException(); } }
/// <summary> /// NOP - Charting is more concerned with population vs individual alphas /// </summary> /// <param name="context">Context whose alpha has just completed analysis</param> public void OnAlphaAnalysisCompleted(AlphaAnalysisContext context) { }
/// <summary> /// NOP - Charting is more concerned with population vs individual alphas /// </summary> /// <param name="context">Context whose alpha has just completed analysis</param> public void OnAlphaClosed(AlphaAnalysisContext context) { }