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 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; } } }