Files
marketdata/MarketDataLib/DividendRiskParity/EquilibriumGenerator.cs
2024-02-22 14:52:53 -05:00

115 lines
4.0 KiB
C#

using MarketData.MarketDataModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MarketData.DividendRiskParity
{
// Takes a collection of positions and calculates the price at which the security needs to be at in order to have a zero (parity) overall position
public class EquilibriumGenerator
{
private EquilibriumGenerator()
{
}
public static Price GenerateEquilibriumPrice(DividendRiskParityPositionCollection dividendRiskParityCollection, Price givenPrice)
{
double? lowerClamp = null;
double? upperClamp = null;
double? gainLoss = null;
double? prevGuess = null;
int direction = 0;
int iterations = 0;
if (null == dividendRiskParityCollection || 0 == dividendRiskParityCollection.Count) return null;
Price price = givenPrice.Clone();
gainLoss = GetGainLoss(price, dividendRiskParityCollection);
if (gainLoss < 0) direction = -1;
else direction = 1;
while (true)
{
if (IsZero(gainLoss)) break;
if (null == lowerClamp && null == upperClamp && gainLoss < 0 && direction.Equals(-1))
{
prevGuess = price.Close;
price.Close = price.Close + (price.Close / 2.00);
iterations++;
}
else if (null == lowerClamp && null == upperClamp && gainLoss >= 0 && direction.Equals(1))
{
prevGuess = price.Close;
price.Close = price.Close - (price.Close / 2.00);
iterations++;
}
else
{
if (gainLoss < 0)
{
lowerClamp = price.Close;
if (null == upperClamp) upperClamp = prevGuess;
price.Close = (lowerClamp.Value + upperClamp.Value) / 2.00;
prevGuess = price.Close;
iterations++;
}
else
{
upperClamp = price.Close;
if (null == lowerClamp) lowerClamp = prevGuess;
price.Close = (upperClamp.Value + lowerClamp.Value) / 2.00;
prevGuess = price.Close;
iterations++;
}
}
gainLoss = GetGainLoss(price, dividendRiskParityCollection);
}
price.Open = 0.00;
price.High = 0.00;
price.Low = 0.00;
price.AdjClose = 0.00;
price.Volume = 0;
return price;
}
private static double? GetGainLoss(Price price, DividendRiskParityPositionCollection dividendRiskParityCollection)
{
double? gainLoss = null;
List<String> symbols = (from DividendRiskParityPosition dividendRiskParityPosition in dividendRiskParityCollection select dividendRiskParityPosition.Symbol).Distinct().ToList();
if (1 != symbols.Count) return null;
foreach (DividendRiskParityPosition dividendRiskParityPosition in dividendRiskParityCollection)
{
if (null == gainLoss)
{
gainLoss = GetMarketValue(price, dividendRiskParityPosition) - GetExposure(price, dividendRiskParityPosition);
}
else
{
gainLoss += GetMarketValue(price, dividendRiskParityPosition) - GetExposure(price, dividendRiskParityPosition);
}
}
return gainLoss;
}
private static double? GetMarketValue(Price price, DividendRiskParityPosition dividendRiskParityPosition)
{
if (null == price) return null;
if (price.Date < dividendRiskParityPosition.TradeDate) return null;
return dividendRiskParityPosition.Shares * price.Close;
}
private static double? GetExposure(Price price, DividendRiskParityPosition dividendRiskParityPosition)
{
if (null == price) return null;
if (price.Date < dividendRiskParityPosition.TradeDate) return null;
return dividendRiskParityPosition.Shares * dividendRiskParityPosition.PurchasePrice;
}
private static bool IsZero(double? value)
{
if (null == value)
{
return true;
}
int intValue = (int)(value * 10000.00);
if (0 == intValue) return true;
return false;
}
}
}