Files
ARM64/MarketData/MarketDataLib/Generator/ParityGenerator.cs
2025-04-29 23:13:41 -04:00

143 lines
5.0 KiB
C#
Executable File

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MarketData.Generator.GainLoss;
using MarketData.DataAccess;
using MarketData.MarketDataModel;
using MarketData.Utils;
namespace MarketData.Generator
{
public class ParityGenerator
{
private enum Direction { Negative, Positive, None };
private ParityGenerator()
{
}
/// <summary>
/// Given PortfolioTrades for a symbol and corresponding latestPrice gives us the breakeven element.
/// This call avoid calling the database. Used by GetGainLossWithDetailByDate in the GainLossController
/// </summary>
/// <param name="symbolTrades"></param>
/// <param name="latestPrice"></param>
/// <returns></returns>
public static ParityElement GenerateBreakEven(PortfolioTrades symbolTrades,Price latestPrice)
{
try
{
ParityElement parityElement=new ParityElement();
Price zeroPrice=null;
if(null==symbolTrades||0==symbolTrades.Count)return null;
PortfolioTrades openTrades=symbolTrades.GetOpenTrades();
GainLossGenerator gainLossGenerator=new GainLossGenerator();
zeroPrice=ParityGenerator.GenerateGainLossValue(openTrades,latestPrice);
parityElement.ParityOffsetPrice=zeroPrice.Close;
parityElement.ParityOffsetPercent=((latestPrice.Close-zeroPrice.Close)/zeroPrice.Close);
parityElement.Symbol=latestPrice.Symbol;
parityElement.PricingDate=latestPrice.Date;
return parityElement;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0}",exception));
return null;
}
}
public static ParityElement GenerateBreakEven(String symbol)
{
try
{
ParityElement parityElement=new ParityElement();
Price zeroPrice=null;
PortfolioTrades portfolioTrades = PortfolioDA.GetTradesSymbol(symbol);
if(null==portfolioTrades||0==portfolioTrades.Count)return null;
PortfolioTrades openTrades=portfolioTrades.GetOpenTrades();
DateTime pricingDate = PricingDA.GetLatestDate(symbol);
Price latestPrice = PricingDA.GetPrice(symbol, pricingDate);
GainLossGenerator gainLossGenerator=new GainLossGenerator();
zeroPrice=ParityGenerator.GenerateGainLossValue(openTrades,latestPrice);
parityElement.ParityOffsetPrice=zeroPrice.Close;
parityElement.ParityOffsetPercent=((latestPrice.Close-zeroPrice.Close)/zeroPrice.Close);
parityElement.Symbol=symbol;
parityElement.PricingDate=pricingDate;
return parityElement;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0}",exception));
return null;
}
}
// generate gain/loss value for portfolio of like symbols
// This funtion will return the dollar price that the stock must either rise to (when G/L is negative) or fall to (when the G/L is positive)
// in order to produce a zero return (i.e.) a wash.
public static Price GenerateGainLossValue(PortfolioTrades portfolioTrades,Price givenPrice)
{
double? lowerClamp=null;
double? upperClamp=null;
double? gainLoss=null;
double? prevGuess=null;
Direction direction=Direction.None;
int iterations=0;
if(null==portfolioTrades||0==portfolioTrades.Count) return null;
Price price=givenPrice.Clone();
gainLoss=portfolioTrades.GetGainLoss(price);
if(gainLoss<0) direction=Direction.Negative;
else direction=Direction.Positive;
while(true)
{
if(IsZero(gainLoss)) break;
if(null==lowerClamp&&null==upperClamp&&gainLoss<0&&direction.Equals(Direction.Negative))
{
prevGuess=price.Close;
price.Close=price.Close+(price.Close/2.00);
iterations++;
}
else if(null==lowerClamp&&null==upperClamp&&gainLoss>=0&&direction.Equals(Direction.Positive))
{
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=portfolioTrades.GetGainLoss(price);
}
return price;
}
public static bool IsZero(double? value)
{
if(null==value)
{
return true;
}
int intValue=(int)(value*10000.00);
if(0==intValue) return true;
return false;
}
}
}