Files
marketdata/MarketDataLib/Generator/GainLoss/GainLossGeneratorCum.cs
2026-02-26 16:24:35 -05:00

188 lines
8.6 KiB
C#

using System;
using System.Linq;
using System.Collections.Generic;
using MarketData;
using MarketData.MarketDataModel;
using MarketData.MarketDataModel.GainLoss;
using MarketData.Utils;
using MarketData.DataAccess;
using MarketData.Cache;
namespace MarketData.Generator.GainLoss
{
public class GainLossGeneratorCum : ITotalGainLossGenerator
{
private DateGenerator dateGenerator=new DateGenerator();
public TotalGainLossCollection GenerateTotalGainLoss(PortfolioTrades portfolioTrades,DateTime? maxDateRef=null)
{
DateGenerator dateGenerator=new DateGenerator();
ModelPerformanceSeries performanceSeries=new ModelPerformanceSeries();
List<TotalGainLossItem> gainLossList=new List<TotalGainLossItem>();
GLPriceCache.GetInstance().Add(portfolioTrades);
try
{
if(!ValidatePortfolioTrades(portfolioTrades))return null;
DateTime minDate=portfolioTrades.GetMinTradeDate();
DateTime maxDate = GLPriceCache.GetInstance().GetLatestDate();
if(null!=maxDateRef) maxDate=maxDateRef.Value;
double prevGainLoss=double.NaN;
List<DateTime> historicalDates=dateGenerator.GenerateHistoricalDates(minDate,maxDate);
foreach(DateTime currentDate in historicalDates)
{
PortfolioTrades openPositions=portfolioTrades.GetOpenTradesOn(currentDate);
PortfolioTrades closedPositions=portfolioTrades.GetClosedTradesOn(currentDate);
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(!dateGenerator.IsMarketOpen(currentDate)) continue;
ModelPerformanceItem performanceItem=new ModelPerformanceItem();
foreach(PortfolioTrade openPosition in openPositions)
{
exposure+=openPosition.Shares*openPosition.Price;
Price price=GLPriceCache.GetInstance().GetPrice(openPosition.Symbol,currentDate);
if(null==price)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No price for {0} on {1}",openPosition.Symbol,currentDate.ToShortDateString()));
continue;
}
gainLoss+=((price.Close*openPosition.Shares)-(openPosition.Price*openPosition.Shares));
marketValue+=(price.Close*openPosition.Shares);
}
foreach(PortfolioTrade closedPosition in closedPositions)
{
double gainLossPosition=(closedPosition.SellPrice*closedPosition.Shares)-(closedPosition.Price*closedPosition.Shares);
gainLossClosedPositions+=gainLossPosition;
}
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;
}
performanceSeries.CalculatePerformance();
double totalGainLoss=double.NaN;
foreach(ModelPerformanceItem performanceItem in performanceSeries)
{
if(double.IsNaN(totalGainLoss))totalGainLoss=performanceItem.GainLossDOD;
else totalGainLoss+=performanceItem.GainLossDOD;
TotalGainLossItem totalGainLossItem=new TotalGainLossItem(
performanceItem.Date,
totalGainLoss,
performanceItem.CumProdMinusOne*100.00,
performanceItem.Exposure,
performanceItem.MarketValue);
gainLossList.Add(totalGainLossItem);
}
TotalGainLossCollection totalGainLossCollection= new TotalGainLossCollection(gainLossList);
totalGainLossCollection.Sort();
return totalGainLossCollection;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception: {0}",exception.ToString()));
return new TotalGainLossCollection(new List<TotalGainLossItem>());
}
}
public TotalGainLossCollection GenerateTotalGainLossWithDividends(PortfolioTrades portfolioTrades,DividendPayments dividendPayments,DateTime? maxDateRef=null)
{
DateGenerator dateGenerator=new DateGenerator();
ModelPerformanceSeries performanceSeries=new ModelPerformanceSeries();
List<TotalGainLossItem> gainLossList=new List<TotalGainLossItem>();
GLPriceCache.GetInstance().Add(portfolioTrades);
try
{
if(!ValidatePortfolioTrades(portfolioTrades)) return null;
DateTime minDate=portfolioTrades.Min(x => x.TradeDate);
DateTime maxDate = GLPriceCache.GetInstance().GetLatestDate();
double prevGainLoss=double.NaN;
List<DateTime> historicalDates=dateGenerator.GenerateHistoricalDates(minDate,maxDate);
foreach(DateTime currentDate in historicalDates)
{
PortfolioTrades openPositions=portfolioTrades.GetOpenTradesOn(currentDate);
PortfolioTrades closedPositions=portfolioTrades.GetClosedTradesOn(currentDate);
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(!dateGenerator.IsMarketOpen(currentDate)) continue;
ModelPerformanceItem performanceItem=new ModelPerformanceItem();
foreach(PortfolioTrade openPosition in openPositions)
{
exposure+=openPosition.Shares*openPosition.Price;
Price price=GLPriceCache.GetInstance().GetPrice(openPosition.Symbol,currentDate);
if(null==price)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No price for {0} on {1}",openPosition.Symbol,currentDate.ToShortDateString()));
}
gainLoss+=((price.Close*openPosition.Shares)-(openPosition.Price*openPosition.Shares));
marketValue+=(price.Close*openPosition.Shares);
}
foreach(PortfolioTrade closedPosition in closedPositions)
{
double gainLossPosition=(closedPosition.SellPrice*closedPosition.Shares)-(closedPosition.Price*closedPosition.Shares);
gainLossClosedPositions+=gainLossPosition;
}
double dividendPaymentsToDate=dividendPayments.GetDividendPaymentsToDate(currentDate);
marketValue+=dividendPaymentsToDate;
gainLoss+=dividendPaymentsToDate;
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;
}
performanceSeries.CalculatePerformance();
double totalGainLoss=double.NaN;
foreach(ModelPerformanceItem performanceItem in performanceSeries)
{
double dividendPaymentsToDate=dividendPayments.GetDividendPaymentsToDate(performanceItem.Date);
if(double.IsNaN(totalGainLoss))totalGainLoss=performanceItem.GainLossDOD;
else totalGainLoss+=performanceItem.GainLossDOD;
TotalGainLossItem totalGainLossItem=new TotalGainLossItem(
performanceItem.Date,
totalGainLoss,
performanceItem.CumProdMinusOne*100.00,
performanceItem.Exposure,
performanceItem.MarketValue,
dividendPaymentsToDate);
gainLossList.Add(totalGainLossItem);
}
TotalGainLossCollection totalGainLossCollection= new TotalGainLossCollection(gainLossList);
totalGainLossCollection.Sort();
return totalGainLossCollection;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception: {0}",exception.ToString()));
return new TotalGainLossCollection(new List<TotalGainLossItem>());
}
}
public bool ValidatePortfolioTrades(PortfolioTrades portfolioTrades)
{
foreach(PortfolioTrade portfolioTrade in portfolioTrades)
{
if(!dateGenerator.IsMarketOpen(portfolioTrade.TradeDate))return false;
if(!dateGenerator.IsMarketOpen(portfolioTrade.SellDate)) return false;
}
return true;
}
}
}