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() { } /// /// 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 /// /// /// /// 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; } } }