115 lines
4.0 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|