Files
TradeBlotter/Model/ModelPerformanceAggregator.cs
2025-03-31 15:37:29 -04:00

423 lines
25 KiB
C#

using MarketData;
using MarketData.Cache;
using MarketData.DataAccess;
using MarketData.MarketDataModel;
using MarketData.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TradeBlotter.Model
{
public class ModelPerformanceAggregator
{
private ModelPerformanceAggregator()
{
}
// ***********************************************************************************************************************************************************************************************************
// *********************************************************************************** M A R C M I N E R V I N I M O M E N T U M *************************************************************************
// ***********************************************************************************************************************************************************************************************************
// 2 versions : one handles MMTrend the other handes the CMTrend.
public static double CalculateCumulativeReturn(IEnumerable<CMTPositionModel> positions)
{
ModelPerformanceSeries performanceSeries=GetModelPerformance(positions);
if(null==performanceSeries) return double.NaN;
return performanceSeries[performanceSeries.Count-1].CumProdMinusOne;
}
public static ModelPerformanceSeries GetModelPerformance(IEnumerable<CMTPositionModel> positions)
{
ModelPerformanceSeries performanceSeries=new ModelPerformanceSeries();
try
{
DateTime minDate=positions.Min(x => x.PurchaseDate);
DateTime maxDate=PricingDA.GetLatestDate();
DateGenerator dateGenerator=new DateGenerator();
double cumulativeReturn=double.NaN;
double prevGainLoss=double.NaN;
LocalPriceCache.GetInstance().RemoveDate(maxDate);
List<DateTime> historicalDates=dateGenerator.GenerateHistoricalDates(minDate,maxDate);
foreach(DateTime currentDate in historicalDates)
{
IEnumerable<CMTPositionModel> openPositions=positions.Where(x => (x.PurchaseDate<=currentDate&&(!Utility.IsEpoch(x.SellDate)&&x.SellDate>currentDate))||(x.PurchaseDate<=currentDate&&Utility.IsEpoch(x.SellDate))).ToList();
IEnumerable<CMTPositionModel> closedPositions=positions.Where(x => (!Utility.IsEpoch(x.SellDate)&&x.SellDate.Equals(currentDate))).ToList();
if(0==openPositions.Count()&&0==closedPositions.Count()) continue;
double gainLoss=0.00;
double gainLossClosedPositions=0.00;
double exposure=0.00;
double marketValue=0.00;
// if(HolidayDA.IsMarketHoliday(currentDate)) continue;
ModelPerformanceItem performanceItem=new ModelPerformanceItem();
foreach(CMTPositionModel openPosition in openPositions)
{
exposure+=openPosition.Shares*openPosition.PurchasePrice;
if(!LocalPriceCache.GetInstance().ContainsPrice(openPosition.Symbol,currentDate))
{
Prices prices=PricingDA.GetPricesForward(openPosition.Symbol,currentDate,PricingDA.ForwardLookingDays);
LocalPriceCache.GetInstance().Add(prices);
}
Price price=LocalPriceCache.GetInstance().GetPrice(openPosition.Symbol,currentDate);
if(null==price)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("GetModelPerformance: No price for {0} on {1}",openPosition.Symbol,currentDate.ToShortDateString()));
continue;
}
gainLoss+=((price.Close*openPosition.Shares)-(openPosition.PurchasePrice*openPosition.Shares));
marketValue+=(price.Close*openPosition.Shares);
}
foreach(CMTPositionModel closedPosition in closedPositions)
{
double gainLossPosition=(closedPosition.CurrentPrice*closedPosition.Shares)-(closedPosition.PurchasePrice*closedPosition.Shares);
gainLossClosedPositions+=gainLossPosition;
}
double dailyReturn=0.00;
if(double.IsNaN(prevGainLoss)) dailyReturn=(marketValue-exposure)/Math.Abs(exposure);
else dailyReturn=((gainLoss-prevGainLoss)/Math.Abs(prevGainLoss))/100.00;
if(double.IsNaN(cumulativeReturn)) cumulativeReturn=1.00*(1.00+dailyReturn);
else cumulativeReturn=cumulativeReturn*(1.00+dailyReturn);
performanceItem.Date=currentDate;
performanceItem.Exposure=exposure;
performanceItem.MarketValue=marketValue;
performanceItem.GainLossDOD=double.IsNaN(prevGainLoss)?gainLoss:(gainLoss-prevGainLoss)+gainLossClosedPositions;
performanceItem.GainLoss=gainLoss+gainLossClosedPositions;
performanceItem.ClosedPositions=closedPositions.Count()>0?true:false;
performanceSeries.Add(performanceItem);
prevGainLoss=gainLoss;
}
for(int index=0;index<performanceSeries.Count;index++)
{
ModelPerformanceItem currentModelPerformanceItem=performanceSeries[index];
ModelPerformanceItem prevModelPerformanceItem=0==index?null:performanceSeries[index-1];
if(null==prevModelPerformanceItem)
{
currentModelPerformanceItem.CumulativeGainLoss=currentModelPerformanceItem.GainLossDOD;
currentModelPerformanceItem.R=(currentModelPerformanceItem.MarketValue-currentModelPerformanceItem.Exposure)/currentModelPerformanceItem.Exposure;
currentModelPerformanceItem.OnePlusR=1.00+currentModelPerformanceItem.R;
currentModelPerformanceItem.CumProd=currentModelPerformanceItem.OnePlusR;
currentModelPerformanceItem.CumProdMinusOne=currentModelPerformanceItem.CumProd-1.00;
}
else
{
currentModelPerformanceItem.CumulativeGainLoss=currentModelPerformanceItem.GainLossDOD+prevModelPerformanceItem.CumulativeGainLoss;
currentModelPerformanceItem.R=prevModelPerformanceItem.Exposure.Equals(currentModelPerformanceItem.Exposure)?(currentModelPerformanceItem.MarketValue-prevModelPerformanceItem.MarketValue)/prevModelPerformanceItem.MarketValue:0;
currentModelPerformanceItem.OnePlusR=1.00+currentModelPerformanceItem.R;
currentModelPerformanceItem.CumProd=currentModelPerformanceItem.OnePlusR*prevModelPerformanceItem.CumProd;
currentModelPerformanceItem.CumProdMinusOne=currentModelPerformanceItem.CumProd-1.00;
}
}
return performanceSeries;
}
catch(Exception)
{
return performanceSeries;
}
}
// ***********************************************************************************************************************************************************************************************************
// *********************************************************************************** C L E N O W M O M E N T U M (C M M M O M E N T U M )**************************************************************************************
// ***********************************************************************************************************************************************************************************************************
public static double CalculateCumulativeReturn(IEnumerable<CMPositionModel> positions)
{
ModelPerformanceSeries performanceSeries=GetModelPerformance(positions);
if(null==performanceSeries)return double.NaN;
return performanceSeries[performanceSeries.Count-1].CumProdMinusOne;
}
public static double CalculateCumulativeReturn(IEnumerable<MGPositionModel> positions)
{
ModelPerformanceSeries performanceSeries=GetModelPerformance(positions);
if(null==performanceSeries) return double.NaN;
return performanceSeries[performanceSeries.Count-1].CumProdMinusOne;
}
public static double CalculateCumulativeReturn(IEnumerable<MGSHPositionModel> positions)
{
ModelPerformanceSeries performanceSeries=GetModelPerformance(positions);
if(null==performanceSeries) return double.NaN;
return performanceSeries[performanceSeries.Count-1].CumProdMinusOne;
}
public static ModelPerformanceSeries GetModelPerformance(IEnumerable<CMPositionModel> positions)
{
ModelPerformanceSeries performanceSeries=new ModelPerformanceSeries();
try
{
DateTime minDate=positions.Min(x => x.PurchaseDate);
DateTime maxDate=PricingDA.GetLatestDate();
DateGenerator dateGenerator=new DateGenerator();
double cumulativeReturn=double.NaN;
double prevGainLoss=double.NaN;
LocalPriceCache.GetInstance().RemoveDate(maxDate);
List<DateTime> historicalDates=dateGenerator.GenerateHistoricalDates(minDate,maxDate);
foreach(DateTime currentDate in historicalDates)
{
IEnumerable<CMPositionModel> openPositions=positions.Where(x => (x.PurchaseDate<=currentDate&&(!Utility.IsEpoch(x.SellDate)&&x.SellDate>currentDate))||(x.PurchaseDate<=currentDate&&Utility.IsEpoch(x.SellDate))).ToList();
IEnumerable<CMPositionModel> closedPositions=positions.Where(x => (!Utility.IsEpoch(x.SellDate)&&x.SellDate.Equals(currentDate))).ToList();
if(0==openPositions.Count()&&0==closedPositions.Count()) continue;
double gainLoss=0.00;
double gainLossClosedPositions=0.00;
double exposure=0.00;
double marketValue=0.00;
// if(HolidayDA.IsMarketHoliday(currentDate)) continue;
ModelPerformanceItem performanceItem=new ModelPerformanceItem();
foreach(CMPositionModel openPosition in openPositions)
{
exposure+=openPosition.Shares*openPosition.PurchasePrice;
if(!LocalPriceCache.GetInstance().ContainsPrice(openPosition.Symbol,currentDate))
{
Prices prices=PricingDA.GetPricesForward(openPosition.Symbol,currentDate,PricingDA.ForwardLookingDays);
LocalPriceCache.GetInstance().Add(prices);
}
Price price=LocalPriceCache.GetInstance().GetPrice(openPosition.Symbol,currentDate);
if(null==price)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("GetModelPerformance: No price for {0} on {1}",openPosition.Symbol,currentDate.ToShortDateString()));
continue;
}
gainLoss+=((price.Close*openPosition.Shares)-(openPosition.PurchasePrice*openPosition.Shares));
marketValue+=(price.Close*openPosition.Shares);
}
foreach(CMPositionModel closedPosition in closedPositions)
{
double gainLossPosition=(closedPosition.CurrentPrice*closedPosition.Shares)-(closedPosition.PurchasePrice*closedPosition.Shares);
gainLossClosedPositions+=gainLossPosition;
}
double dailyReturn=0.00;
if(double.IsNaN(prevGainLoss)) dailyReturn=(marketValue-exposure)/Math.Abs(exposure);
else dailyReturn=((gainLoss-prevGainLoss)/Math.Abs(prevGainLoss))/100.00;
if(double.IsNaN(cumulativeReturn)) cumulativeReturn=1.00*(1.00+dailyReturn);
else cumulativeReturn=cumulativeReturn*(1.00+dailyReturn);
performanceItem.Date=currentDate;
performanceItem.Exposure=exposure;
performanceItem.MarketValue=marketValue;
performanceItem.GainLossDOD=double.IsNaN(prevGainLoss)?gainLoss:(gainLoss-prevGainLoss)+gainLossClosedPositions;
performanceItem.GainLoss=gainLoss+gainLossClosedPositions;
performanceItem.ClosedPositions=closedPositions.Count()>0?true:false;
performanceSeries.Add(performanceItem);
prevGainLoss=gainLoss;
}
for(int index=0;index<performanceSeries.Count;index++)
{
ModelPerformanceItem currentModelPerformanceItem=performanceSeries[index];
ModelPerformanceItem prevModelPerformanceItem=0==index?null:performanceSeries[index-1];
if(null==prevModelPerformanceItem)
{
currentModelPerformanceItem.CumulativeGainLoss=currentModelPerformanceItem.GainLossDOD;
currentModelPerformanceItem.R=(currentModelPerformanceItem.MarketValue-currentModelPerformanceItem.Exposure)/currentModelPerformanceItem.Exposure;
currentModelPerformanceItem.OnePlusR=1.00+currentModelPerformanceItem.R;
currentModelPerformanceItem.CumProd=currentModelPerformanceItem.OnePlusR;
currentModelPerformanceItem.CumProdMinusOne=currentModelPerformanceItem.CumProd-1.00;
}
else
{
currentModelPerformanceItem.CumulativeGainLoss=currentModelPerformanceItem.GainLossDOD+prevModelPerformanceItem.CumulativeGainLoss;
currentModelPerformanceItem.R=prevModelPerformanceItem.Exposure.Equals(currentModelPerformanceItem.Exposure)?(currentModelPerformanceItem.MarketValue-prevModelPerformanceItem.MarketValue)/prevModelPerformanceItem.MarketValue:0;
currentModelPerformanceItem.OnePlusR=1.00+currentModelPerformanceItem.R;
currentModelPerformanceItem.CumProd=currentModelPerformanceItem.OnePlusR*prevModelPerformanceItem.CumProd;
currentModelPerformanceItem.CumProdMinusOne=currentModelPerformanceItem.CumProd-1.00;
}
}
return performanceSeries;
}
catch(Exception)
{
return performanceSeries;
}
}
// ***********************************************************************************************************************************************************************************************************
// *********************************************************************************** Q U A N T U M M O M E N T U M **************************************************************************************
// ***********************************************************************************************************************************************************************************************************
public static ModelPerformanceSeries GetModelPerformance(IEnumerable<MGPositionModel> positions)
{
ModelPerformanceSeries performanceSeries=new ModelPerformanceSeries();
try
{
DateTime minDate=positions.Min(x => x.PurchaseDate);
DateTime maxDate=PricingDA.GetLatestDate();
DateGenerator dateGenerator=new DateGenerator();
double cumulativeReturn=double.NaN;
double prevGainLoss=double.NaN;
LocalPriceCache.GetInstance().RemoveDate(maxDate);
List<DateTime> historicalDates=dateGenerator.GenerateHistoricalDates(minDate,maxDate);
foreach(DateTime currentDate in historicalDates)
{
IEnumerable<MGPositionModel> openPositions=positions.Where(x => (x.PurchaseDate<=currentDate&&(!Utility.IsEpoch(x.SellDate)&&x.SellDate>currentDate))||(x.PurchaseDate<=currentDate&&Utility.IsEpoch(x.SellDate))).ToList();
IEnumerable<MGPositionModel> closedPositions=positions.Where(x => (!Utility.IsEpoch(x.SellDate)&&x.SellDate.Equals(currentDate))).ToList();
if(0==openPositions.Count()&&0==closedPositions.Count()) continue;
double gainLoss=0.00;
double gainLossClosedPositions=0.00;
double exposure=0.00;
double marketValue=0.00;
// if(HolidayDA.IsMarketHoliday(currentDate)) continue;
ModelPerformanceItem performanceItem=new ModelPerformanceItem();
foreach(MGPositionModel openPosition in openPositions)
{
exposure+=openPosition.Shares*openPosition.PurchasePrice;
if(!LocalPriceCache.GetInstance().ContainsPrice(openPosition.Symbol,currentDate))
{
Prices prices=PricingDA.GetPricesForward(openPosition.Symbol,currentDate,PricingDA.ForwardLookingDays);
LocalPriceCache.GetInstance().Add(prices);
}
Price price=LocalPriceCache.GetInstance().GetPrice(openPosition.Symbol,currentDate);
if(null==price)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("GetModelPerformance: No price for {0} on {1}",openPosition.Symbol,currentDate.ToShortDateString()));
continue;
}
gainLoss+=((price.Close*openPosition.Shares)-(openPosition.PurchasePrice*openPosition.Shares));
marketValue+=(price.Close*openPosition.Shares);
}
foreach(MGPositionModel closedPosition in closedPositions)
{
double gainLossPosition=(closedPosition.CurrentPrice*closedPosition.Shares)-(closedPosition.PurchasePrice*closedPosition.Shares);
gainLossClosedPositions+=gainLossPosition;
}
double dailyReturn=0.00;
if(double.IsNaN(prevGainLoss)) dailyReturn=(marketValue-exposure)/Math.Abs(exposure);
else dailyReturn=((gainLoss-prevGainLoss)/Math.Abs(prevGainLoss))/100.00;
if(double.IsNaN(cumulativeReturn)) cumulativeReturn=1.00*(1.00+dailyReturn);
else cumulativeReturn=cumulativeReturn*(1.00+dailyReturn);
performanceItem.Date=currentDate;
performanceItem.Exposure=exposure;
performanceItem.MarketValue=marketValue;
performanceItem.GainLossDOD=double.IsNaN(prevGainLoss)?gainLoss:(gainLoss-prevGainLoss)+gainLossClosedPositions;
performanceItem.GainLoss=gainLoss+gainLossClosedPositions;
performanceItem.ClosedPositions=closedPositions.Count()>0?true:false;
performanceSeries.Add(performanceItem);
prevGainLoss=gainLoss;
}
for(int index=0;index<performanceSeries.Count;index++)
{
ModelPerformanceItem currentModelPerformanceItem=performanceSeries[index];
ModelPerformanceItem prevModelPerformanceItem=0==index?null:performanceSeries[index-1];
if(null==prevModelPerformanceItem)
{
currentModelPerformanceItem.CumulativeGainLoss=currentModelPerformanceItem.GainLossDOD;
currentModelPerformanceItem.R=(currentModelPerformanceItem.MarketValue-currentModelPerformanceItem.Exposure)/currentModelPerformanceItem.Exposure;
currentModelPerformanceItem.OnePlusR=1.00+currentModelPerformanceItem.R;
currentModelPerformanceItem.CumProd=currentModelPerformanceItem.OnePlusR;
currentModelPerformanceItem.CumProdMinusOne=currentModelPerformanceItem.CumProd-1.00;
}
else
{
currentModelPerformanceItem.CumulativeGainLoss=currentModelPerformanceItem.GainLossDOD+prevModelPerformanceItem.CumulativeGainLoss;
currentModelPerformanceItem.R=prevModelPerformanceItem.Exposure.Equals(currentModelPerformanceItem.Exposure)?(currentModelPerformanceItem.MarketValue-prevModelPerformanceItem.MarketValue)/prevModelPerformanceItem.MarketValue:0;
currentModelPerformanceItem.OnePlusR=1.00+currentModelPerformanceItem.R;
currentModelPerformanceItem.CumProd=currentModelPerformanceItem.OnePlusR*prevModelPerformanceItem.CumProd;
currentModelPerformanceItem.CumProdMinusOne=currentModelPerformanceItem.CumProd-1.00;
}
}
return performanceSeries;
}
catch(Exception)
{
return null;
}
}
// ***********************************************************************************************************************************************************************************************************
// *********************************************************************************** M G S H Q U A N T U M M O M E N T U M **************************************************************************************
// ***********************************************************************************************************************************************************************************************************
public static ModelPerformanceSeries GetModelPerformance(IEnumerable<MGSHPositionModel> positions)
{
ModelPerformanceSeries performanceSeries=new ModelPerformanceSeries();
try
{
DateTime minDate=positions.Min(x => x.PurchaseDate);
DateTime maxDate=PricingDA.GetLatestDate();
DateGenerator dateGenerator=new DateGenerator();
double cumulativeReturn=double.NaN;
double prevGainLoss=double.NaN;
LocalPriceCache.GetInstance().RemoveDate(maxDate);
List<DateTime> historicalDates=dateGenerator.GenerateHistoricalDates(minDate,maxDate);
foreach(DateTime currentDate in historicalDates)
{
IEnumerable<MGSHPositionModel> openPositions=positions.Where(x => (x.PurchaseDate<=currentDate&&(!Utility.IsEpoch(x.SellDate)&&x.SellDate>currentDate))||(x.PurchaseDate<=currentDate&&Utility.IsEpoch(x.SellDate))).ToList();
IEnumerable<MGSHPositionModel> closedPositions=positions.Where(x => (!Utility.IsEpoch(x.SellDate)&&x.SellDate.Equals(currentDate))).ToList();
if(0==openPositions.Count()&&0==closedPositions.Count()) continue;
double gainLoss=0.00;
double gainLossClosedPositions=0.00;
double exposure=0.00;
double marketValue=0.00;
// if(HolidayDA.IsMarketHoliday(currentDate)) continue;
ModelPerformanceItem performanceItem=new ModelPerformanceItem();
foreach(MGSHPositionModel openPosition in openPositions)
{
exposure+=openPosition.Shares*openPosition.PurchasePrice;
if(!LocalPriceCache.GetInstance().ContainsPrice(openPosition.Symbol,currentDate))
{
Prices prices=PricingDA.GetPricesForward(openPosition.Symbol,currentDate,PricingDA.ForwardLookingDays);
LocalPriceCache.GetInstance().Add(prices);
}
Price price=LocalPriceCache.GetInstance().GetPrice(openPosition.Symbol,currentDate);
if(null==price)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("GetModelPerformance: No price for {0} on {1}",openPosition.Symbol,currentDate.ToShortDateString()));
continue;
}
gainLoss+=((price.Close*openPosition.Shares)-(openPosition.PurchasePrice*openPosition.Shares));
marketValue+=(price.Close*openPosition.Shares);
}
foreach(MGSHPositionModel closedPosition in closedPositions)
{
double gainLossPosition=(closedPosition.CurrentPrice*closedPosition.Shares)-(closedPosition.PurchasePrice*closedPosition.Shares);
gainLossClosedPositions+=gainLossPosition;
}
double dailyReturn=0.00;
if(double.IsNaN(prevGainLoss)) dailyReturn=(marketValue-exposure)/Math.Abs(exposure);
else dailyReturn=((gainLoss-prevGainLoss)/Math.Abs(prevGainLoss))/100.00;
if(double.IsNaN(cumulativeReturn)) cumulativeReturn=1.00*(1.00+dailyReturn);
else cumulativeReturn=cumulativeReturn*(1.00+dailyReturn);
performanceItem.Date=currentDate;
performanceItem.Exposure=exposure;
performanceItem.MarketValue=marketValue;
performanceItem.GainLossDOD=double.IsNaN(prevGainLoss)?gainLoss:(gainLoss-prevGainLoss)+gainLossClosedPositions;
performanceItem.GainLoss=gainLoss+gainLossClosedPositions;
performanceItem.ClosedPositions=closedPositions.Count()>0?true:false;
performanceSeries.Add(performanceItem);
prevGainLoss=gainLoss;
}
for(int index=0;index<performanceSeries.Count;index++)
{
ModelPerformanceItem currentModelPerformanceItem=performanceSeries[index];
ModelPerformanceItem prevModelPerformanceItem=0==index?null:performanceSeries[index-1];
if(null==prevModelPerformanceItem)
{
currentModelPerformanceItem.CumulativeGainLoss=currentModelPerformanceItem.GainLossDOD;
currentModelPerformanceItem.R=(currentModelPerformanceItem.MarketValue-currentModelPerformanceItem.Exposure)/currentModelPerformanceItem.Exposure;
currentModelPerformanceItem.OnePlusR=1.00+currentModelPerformanceItem.R;
currentModelPerformanceItem.CumProd=currentModelPerformanceItem.OnePlusR;
currentModelPerformanceItem.CumProdMinusOne=currentModelPerformanceItem.CumProd-1.00;
}
else
{
currentModelPerformanceItem.CumulativeGainLoss=currentModelPerformanceItem.GainLossDOD+prevModelPerformanceItem.CumulativeGainLoss;
currentModelPerformanceItem.R=prevModelPerformanceItem.Exposure.Equals(currentModelPerformanceItem.Exposure)?(currentModelPerformanceItem.MarketValue-prevModelPerformanceItem.MarketValue)/prevModelPerformanceItem.MarketValue:0;
currentModelPerformanceItem.OnePlusR=1.00+currentModelPerformanceItem.R;
currentModelPerformanceItem.CumProd=currentModelPerformanceItem.OnePlusR*prevModelPerformanceItem.CumProd;
currentModelPerformanceItem.CumProdMinusOne=currentModelPerformanceItem.CumProd-1.00;
}
}
return performanceSeries;
}
catch(Exception)
{
return null;
}
}
}
}