private BondSpreadResult ComputeCloseFutureBondSpread(BondAnalytics.Country eCountry, Future f, long Freq, Dictionary<DateTime, double> liborInterestCurveDict, Dictionary<DateTime, double> oisInterestCurveDict) { try { if (liborInterestCurveDict == null || oisInterestCurveDict == null) return null; var floas = BondAnalytics.SolveZSpread(eCountry, f.Delivery, f.BondPriceClose, DateTime.MinValue, DateTime.MinValue, f.Maturity, f.Coupon, Freq, liborInterestCurveDict.Keys.ToArray(), liborInterestCurveDict.Values.ToArray(), new List<DateTime>()); var foisoas = BondAnalytics.SolveZSpread(eCountry, f.Delivery, f.BondPriceClose, DateTime.MinValue, DateTime.MinValue, f.Maturity, f.Coupon, Freq, oisInterestCurveDict.Keys.ToArray(), oisInterestCurveDict.Values.ToArray(), new List<DateTime>()); floas = (floas == -1.0) ? double.NaN : -1 * floas * 10000; foisoas = (foisoas == -1.0) ? double.NaN : -1 * foisoas * 10000; var isFront = GetCTDFlagForBond(f.Contract) == CTDFlag.Front; var isBack = GetCTDFlagForBond(f.Contract) == CTDFlag.Back; return new BondSpreadResult { Ticker = f.Contract, Coupon = f.Coupon, IssueDate = f.IssueDate, EffectiveDate = f.Delivery, Maturity = f.Maturity, LOAS = floas, OISOAS = foisoas, FwdLOASFront = isFront ? floas : double.NaN, FwdLOASBack = isBack ? floas : double.NaN, FwdOISOASFront = isFront ? foisoas : double.NaN, FwdOISOASBack = isBack ? foisoas : double.NaN, Price = f.ClosePrice, BondPriceUsed = f.BondPriceClose, CtdBondCusipUsed = f.CTDReuters, InstrumentType = InstrumentType.BondFuture, Series = f.Header, IsLive = false, }; } catch (Exception e) { Log.Error(string.Format("Failed to compute future spread for {0}", f.Contract), e); return null; } }
public void EnrichFuture(Future futures, Dictionary<DateTime, double> oisCurveLive, Dictionary<DateTime, double> liborCurveLive, Dictionary<DateTime, double> oisCurve, Dictionary<DateTime, double> liborCurve, BondSpreadCalculator bondSpreadCalculator) { // can throw exceptions // default country to US var eCountry = BondAnalytics.Country.US; // obtain delivery date futures.Delivery = EnrichDeliveryDate(futures); RealTimeBondData bondData; // go to the calculator cache to obtain bond prices. bondSpreadCalculator.GetBondPrices().TryGetValue(futures.RTTicker, out bondData); futures.LivePrice = double.NaN; if (bondData != null) { futures.LivePrice = (bondData.Bid + bondData.Ask)/2; futures.LivePriceTimeUtc = bondData.UtcTime; } double closePrice; if (!histPriceCache.TryGetValue(futures.RTTicker, out closePrice)) { //**** only fetch closes from symmetry service for front and back month contract in order to reduce hammering the service too much // this logic could be removed when closes have migrated to carbon var futureDateCode = FutureContractCode(futures.Contract); if (futureDateCode == _futureBuilder.FrontTicker || futureDateCode == _futureBuilder.BackTicker) { //var histPriceObj = bondServiceModel.GetHistoryPrice(futures.Contract + " Comdty", _futureBuilder.AsOfDate, "Close"); //closePrice = histPriceObj != null ? histPriceObj.Price : double.NaN; closePrice = bondServiceModel.GetCarbonHistoryPrice(futures.Contract + " Comdty", _futureBuilder.AsOfDate); if (!double.IsNaN(closePrice)) histPriceCache[futures.RTTicker] = closePrice; } else { // not front and back contract, skip close Log.InfoFormat("close price skipped for future {0}", futures.Contract); closePrice = double.NaN; } } else closePrice = histPriceCache[futures.RTTicker]; futures.ClosePrice = closePrice; // get CTD static data RealTimeBondData ctdData=null; if (!string.IsNullOrEmpty(futures.CTDRic)) bondSpreadCalculator.GetBondPrices().TryGetValue(futures.CTDRic, out ctdData); else Log.ErrorFormat("no CTD ric found for future {0}, please ensure its setup in the BSServiceClient tool", futures.Contract); // either get from carbon or override var ctdRecord = FetchSavedCTDFromMongo(futures.Contract); // validation check // if no ctd override and carbon data, we cannot proceed further if ((ctdData == null || string.IsNullOrEmpty(ctdData.BKGD_REF)) && (ctdRecord == null || string.IsNullOrEmpty(ctdRecord.CTDCusip))) { Log.ErrorFormat("no ctd override and carbon data, we cannot proceed further for {0}", futures.Contract); return; } futures.CTDReuters =(ctdRecord == null || string.IsNullOrEmpty(ctdRecord.CTDCusip)) ? ctdData.BKGD_REF.TrimEnd('=') : ctdRecord.CTDCusip.TrimEnd('='); BondStatic ctdStatic; if (futureStaticCache.ContainsKey(futures.CTDReuters)) ctdStatic = futureStaticCache[futures.CTDReuters]; else { ctdStatic = bondServiceModel.GetSingleSecurityMetadata(futures.CTDReuters); futureStaticCache[futures.CTDReuters] = ctdStatic; } futures.CTD = ctdStatic.MLP; futures.IssueDate = DateTime.FromOADate(ctdStatic.IssueDate); futures.Maturity = DateTime.FromOADate(ctdStatic.Maturity); futures.Coupon = ctdStatic.Coupon; // beware that the delivery date used to compute conversion factor is from the long dated future, therefore we need to roll back 3 days for short dated futures var deliveryDateUsedForConvFactor = (futures.IsShortLong == ShortLongFuture.ShortDated) ? bondServiceModel.RollDate(futures.Delivery, -3, DateUnit.Bd , BusinessDayConvention.Following, "USNY").ContinueWith(a => a.Result.ToDateTime()).Result : futures.Delivery; futures.ConvFator = BondAnalytics.CalcBondCF(futures.Coupon, futures.IssueDate, DateTime.MinValue, futures.Maturity, deliveryDateUsedForConvFactor, futures.Header, 0.0); // ctd bond if (double.IsNaN(futures.LivePrice)) { futures.BondPriceLive = double.NaN; futures.FwdYieldLive = double.NaN; } else { futures.BondPriceLive = futures.LivePrice * futures.ConvFator; futures.FwdYieldLive = BondAnalytics.SolveYield(eCountry, futures.Delivery, futures.BondPriceLive, DateTime.MinValue, DateTime.MinValue, futures.Maturity, futures.Coupon, 6).First(); } futures.BondPriceClose = futures.ClosePrice*futures.ConvFator; futures.FwdYieldClose = BondAnalytics.SolveYield(eCountry, futures.Delivery, futures.BondPriceClose, DateTime.MinValue,DateTime.MinValue, futures.Maturity, futures.Coupon, 6).First(); if (liborCurveLive != null) { Log.DebugFormat("deliver{0} mat{1} oiscurve{2} libor{3}", futures.Delivery, futures.Maturity, oisCurve.Keys.Select(d => d.ToString("yyyy-MM-dd")).ToArray().JoinStrings(";"), liborCurveLive.Keys.Select(d => d.ToString("yyyy-MM-dd")).ToArray().JoinStrings(";")); futures.LMMSLive = BondAnalytics.CalcMMS(futures.Delivery, futures.Maturity, BondAnalytics.DayCountType.I30360, 6, 3, oisCurve.Keys.ToArray(),oisCurve.Values.ToArray(), liborCurveLive.Keys.ToArray(), liborCurveLive.Values.ToArray(), null, null, null, null, null, null, DateTime.MinValue); } futures.LMMSClose = BondAnalytics.CalcMMS(futures.Delivery, futures.Maturity, BondAnalytics.DayCountType.I30360, 6, 3, oisCurve.Keys.ToArray(), oisCurve.Values.ToArray(), liborCurve.Keys.ToArray(), liborCurve.Values.ToArray(), null, null, null, null, null, null, DateTime.MinValue); if (oisCurveLive != null) futures.OISMMSLive = BondAnalytics.CalcMMS(futures.Delivery, futures.Maturity, BondAnalytics.DayCountType.I30360, 12, 12, oisCurve.Keys.ToArray(), oisCurve.Values.ToArray(), oisCurveLive.Keys.ToArray(), oisCurveLive.Values.ToArray(), null, null, null, null, null, null, DateTime.MinValue); futures.OISMMSClose = BondAnalytics.CalcMMS(futures.Delivery, futures.Maturity, BondAnalytics.DayCountType.I30360, 12, 12, oisCurve.Keys.ToArray(), oisCurve.Values.ToArray(), oisCurve.Keys.ToArray(), oisCurve.Values.ToArray(), null, null, null, null, null, null, DateTime.MinValue); futures.CanComputeSpread = true; }
private DateTime EnrichDeliveryDate(Future futures) { ValididateFutureContract(futures); var futureDateCode = FutureContractCode(futures.Contract); if (futures.Header == "TU" || futures.Header == "FV") { if (futureDateCode == _futureBuilder.PrevFrontTicker) return _futureBuilder.PrevFrontRollShort; if (futureDateCode == _futureBuilder.FrontTicker) return _futureBuilder.FrontRollShort; if (futureDateCode == _futureBuilder.BackTicker) return _futureBuilder.BackRollShort; if (futureDateCode == _futureBuilder.Back2Ticker) return _futureBuilder.Back2RollShort; } else if (futures.Header == "TY" || futures.Header == "US" || futures.Header == "WN") { if (futureDateCode == _futureBuilder.PrevFrontTicker) return _futureBuilder.PrevFrontRollLong; if (futureDateCode == _futureBuilder.FrontTicker) return _futureBuilder.FrontRollLong; if (futureDateCode == _futureBuilder.BackTicker) return _futureBuilder.BackRollLong; if (futureDateCode == _futureBuilder.Back2Ticker) return _futureBuilder.Back2RollLong; } return DateTime.MinValue; }
private void ValididateFutureContract(Future futures) { var futureDateCode = FutureContractCode(futures.Contract); if(futureDateCode != _futureBuilder.PrevFrontTicker && futureDateCode != _futureBuilder.FrontTicker && futureDateCode != _futureBuilder.BackTicker && futureDateCode != _futureBuilder.Back2Ticker) throw new Exception("Invalid Future contract"); }
protected bool Equals(Future other) { return string.Equals(Contract, other.Contract) && string.Equals(RTTicker, other.RTTicker) && string.Equals(CTD, other.CTD) && string.Equals(CTDRic, other.CTDRic); }