// todo: consider to remove from here public DateAndNumberOfDaysUntil GetNumberOfDaysLeftUntilExpiry(DateTime expiry) { int delta = 0; EntityResponse <DateTime> utcMarketCloseTimeForDate = GetUtcMarketCloseTimeForDate(expiry); if (utcMarketCloseTimeForDate == null) { delta = -1; } if (expiry.Kind == DateTimeKind.Utc) { expiry = TimeZoneInfo.ConvertTimeFromUtc(expiry, CstTimeZone); } DateTime utcMarketCloseTime = TimeZoneInfo.ConvertTimeToUtc(expiry.Date.Add(SseCloseTime), CstTimeZone); TimeSpan diff = (utcMarketCloseTime - DateTime.UtcNow); DateAndNumberOfDaysUntil result = new DateAndNumberOfDaysUntil { FutureDate = expiry, TotalNumberOfDaysUntilExpiry = diff.TotalDays + delta }; return(result); }
private double?GetStrikePriceForLegOld(OptionChain chain, StrategyLeg leg, DateAndNumberOfDaysUntil date) { if (leg == null || leg.LegType == LegType.Security) { return(null); } IList <double> strikePrices = chain.GetStrikePrices(date); if (leg.BuyOrSell == BuyOrSell.Buy) { double strikePrice = leg.LegType == LegType.Call ? strikePrices.LastAndIndex(d => d < chain.UnderlyingCurrentPrice).Item2 : strikePrices.FirstAndIndex(d => d > chain.UnderlyingCurrentPrice).Item2; return(strikePrice); } else { List <DateAndStandardDeviation> stdDevs = _predictionAndStdDevService.GetExpiriesAndStandardDeviations(chain, chain.ExpirationDates, 1.0); DateAndStandardDeviation stdDevPricesForCurrentExpiry = stdDevs.Single(d => d.DateAndNumberOfDaysUntil.FutureDate.Equals(date.FutureDate)); double strikePrice = leg.LegType == LegType.Call ? strikePrices.GetClosest(stdDevPricesForCurrentExpiry.StdDev.UpPrice) : strikePrices.GetClosest(stdDevPricesForCurrentExpiry.StdDev.DownPrice); return(strikePrice); } }
public List <DateAndStandardDeviations> GetStandardDeviationsForDates(IBasicPredictionCalculationData data, IReadOnlyList <DateAndNumberOfDaysUntil> referencePoints, IReadOnlyList <double> deviations) { const int numberOfPointsBetween = 4; DateAndNumberOfDaysUntil expirationDateForToday = _marketWorkTimeService.GetNumberOfDaysLeftUntilExpiry(_marketWorkTimeService.NowInMarketTimeZone.Date); List <DateAndNumberOfDaysUntil> experies = referencePoints.Union(expirationDateForToday.Yield()).Distinct().OrderBy(t => t.FutureDate).ToList(); List <DateAndNumberOfDaysUntil> dates = new List <DateAndNumberOfDaysUntil>(experies); for (int i = 0; i < experies.Count - 1; i++) { DateAndNumberOfDaysUntil current = experies[i]; DateAndNumberOfDaysUntil next = experies[i + 1]; double range = next.TotalNumberOfDaysUntilExpiry - current.TotalNumberOfDaysUntilExpiry; if (range < 3) { continue; } double step = Math.Round(range / numberOfPointsBetween, 3, MidpointRounding.AwayFromZero); // uniformly inserts points between current date and next date. IEnumerable <int> increments = CollectionExtensions.Range(0, range - step, step).Where(d => (int)d != 0).Select(d => (int)d).Distinct(); foreach (int increment in increments) { DateTime date = current.FutureDate.AddDays(increment); dates.Add(_marketWorkTimeService.GetNumberOfDaysLeftUntilExpiry(date)); } } List <DateAndNumberOfDaysUntil> datesToBuildPredictionFor = dates.OrderBy(e => e.FutureDate).ToList(); Dictionary <DateAndNumberOfDaysUntil, Prediction> predictions = MakePredictionsForDates(data, datesToBuildPredictionFor); List <DateAndStandardDeviations> stdDevs = GetStandardDeviationsForDates(data, predictions, deviations); return(stdDevs); }
private static List <CoveredCall> GetCoveredCallsByLegType(OptionChain data, Dictionary <DateAndNumberOfDaysUntil, Prediction> predictions, /*EarningsCalendar calendar,*/ LegType legType, /*Signal volOfVol,*/ double?minimumPremiumParam, double?minimumReturnParam) { double minimumPremium = minimumPremiumParam ?? DefaultMinimumPremium; double minimumReturn = minimumReturnParam ?? DefaultMinimumReturn; HashSet <DateTime> optimalIsFound = new HashSet <DateTime>(); List <CoveredCall> coveredCalls = new List <CoveredCall>(); foreach (OptionPair optionChain in data) { Option option = legType == LegType.Call ? optionChain.CallOption : optionChain.PutOption; double bid = option.Bid; double currentPremium = bid; DateAndNumberOfDaysUntil expiry = optionChain.Expiry; double lastPrice = data.UnderlyingCurrentPrice; double strkePrice = optionChain.StrikePrice; List <OptionPair> chainsWithTheSameExpiry = data.Where(x => x.Expiry == expiry).ToList(); // todo: optionChain.OptionType //bool typeisSuitable = optionChain.OptionType == OptionType.Standard; double daysQuantity = expiry.TotalNumberOfDaysUntilExpiry; // Ignore expired options if (daysQuantity < 0 || daysQuantity.AlmostEqual(0) /*|| !typeisSuitable*/) { continue; } bool strikePriceIsSuitable = legType == LegType.Call ? optionChain.StrikePrice > lastPrice : optionChain.StrikePrice < lastPrice; if (!strikePriceIsSuitable) { // Check if fifth strike is suitable strikePriceIsSuitable = legType == LegType.Call ? chainsWithTheSameExpiry.Where(x => x.StrikePrice < lastPrice) .OrderBy(x => lastPrice - x.StrikePrice) .Take(5).Last().StrikePrice <optionChain.StrikePrice : chainsWithTheSameExpiry.Where(x => x.StrikePrice > lastPrice) .OrderBy(x => x.StrikePrice - lastPrice) .Take(5).Last().StrikePrice> optionChain.StrikePrice; } if (!strikePriceIsSuitable) { continue; } double intrinsicValue = legType == LegType.Call ? lastPrice > optionChain.StrikePrice ? lastPrice - optionChain.StrikePrice : 0 : lastPrice < optionChain.StrikePrice ? optionChain.StrikePrice - lastPrice : 0; double currentReturn = (Math.Pow(1 + (currentPremium - intrinsicValue) / lastPrice, 365.0 / daysQuantity) - 1) * 100; bool firstAboveBelowStdDev = false; Prediction prediction = predictions[optionChain.Expiry]; StdDevPrices stdDev = prediction.GetStdDevPrices(1); bool deviationIsSuitable = legType == LegType.Call ? strkePrice > stdDev.UpPrice : strkePrice < stdDev.DownPrice; if (deviationIsSuitable) { firstAboveBelowStdDev = legType == LegType.Call ? chainsWithTheSameExpiry .Where(x => x.StrikePrice >= stdDev.UpPrice) .Select(y => y.StrikePrice - stdDev.UpPrice) .Min() .AlmostEqual(strkePrice - stdDev.UpPrice) : chainsWithTheSameExpiry .Where(x => x.StrikePrice <= stdDev.DownPrice) .Select(y => stdDev.DownPrice - y.StrikePrice) .Min() .AlmostEqual(stdDev.DownPrice - optionChain.StrikePrice); } double probability = prediction.GetProbabilityByPrice(optionChain.StrikePrice) * 100; double probabilityInSigmas = MarketMath.GetSigmaProbabilityByDeviation(probability / 100); CoveredCall coveredCall = new CoveredCall(); coveredCall.Premium = currentPremium; coveredCall.Return = currentReturn; coveredCall.OptionNumber = option.OptionNumber; coveredCall.Probability = probability; coveredCall.ProbabilityInSigmas = probabilityInSigmas; // if (volOfVol != null) // { // coveredCall.VolOfVol = volOfVol.Value; // } coveredCall.PercentAboveBelowCurrentPrice = Math.Abs((optionChain.StrikePrice - lastPrice)) / lastPrice * 100; int numberOfStrikes = legType == LegType.Call ? chainsWithTheSameExpiry.Where(x => x.StrikePrice >= lastPrice) .OrderBy(x => x.StrikePrice) .ToList() .FindIndex(x => x.StrikePrice.AlmostEqual(optionChain.StrikePrice)) + 1 : chainsWithTheSameExpiry.Where(x => x.StrikePrice <= lastPrice) .OrderByDescending(x => x.StrikePrice) .ToList() .FindIndex(x => x.StrikePrice.AlmostEqual(optionChain.StrikePrice)) + 1; coveredCall.NumberOfStrikesBelowAboveCurrentPrice = numberOfStrikes; coveredCall.NumberOfSdBelowAboveCurrentPrice = probabilityInSigmas; // coveredCall.HasEarnings = calendar != null && calendar.Date >= DateTime.Now && calendar.Date <= optionChain.Expiry; // if (calendar != null) // { // coveredCall.DaysQuantityBeforeEarnings = (calendar.Date - DateTime.UtcNow).TotalDays; // } coveredCalls.Add(coveredCall); if (bid.AlmostEqual(0.0)) { coveredCall.RiskTolerance = RiskTolerance.NoBid; continue; } if (!deviationIsSuitable) { if (currentPremium < minimumPremium) { coveredCall.RiskTolerance = RiskTolerance.LowPremium; continue; } coveredCall.RiskTolerance = currentReturn >= minimumReturn ? RiskTolerance.Aggressive : RiskTolerance.LowReturn; continue; } if (currentPremium < minimumPremium) { coveredCall.RiskTolerance = RiskTolerance.LowPremium; continue; } if (currentReturn < minimumReturn) { coveredCall.RiskTolerance = RiskTolerance.LowReturn; continue; } if (!optimalIsFound.Contains(optionChain.Expiry.FutureDate) && daysQuantity >= 3 && daysQuantity <= 60 //&& (!coveredCall.HasEarnings || daysQuantity < coveredCall.DaysQuantityBeforeEarnings) && firstAboveBelowStdDev) { coveredCall.RiskTolerance = RiskTolerance.Optimal; optimalIsFound.Add(optionChain.Expiry.FutureDate); } else { coveredCall.RiskTolerance = RiskTolerance.Conservative; } } return(coveredCalls); }
public List <BasePortfolioItemGroupViewModel> GetPortfolioData(string customerCode, string accountCode, string tradeAccount) { #region Fetch data OptionPositionsArguments arguments = new OptionPositionsArguments { CustomerCode = customerCode, CustomerAccountCode = accountCode }; EntityResponse <List <OptionPositionInformation> > optionPositions = _portfolioManager.GetOptionPositions(arguments); EntityResponse <List <OptionableStockPositionInformation> > stockPositions = _portfolioManager.GetOptionalStockPositions(customerCode, accountCode, tradeAccount); if (!optionPositions.IsSuccess || !stockPositions.IsSuccess || optionPositions.Entity.Count == 0) { return(new List <BasePortfolioItemGroupViewModel>()); } IEnumerable <PortfolioOption> portfolioOptions = Mapper.Map <List <OptionPositionInformation>, List <PortfolioOption> >(optionPositions.Entity); IEnumerable <PortfolioStock> portfolioStocks = Mapper.Map <List <OptionableStockPositionInformation>, List <PortfolioStock> >(stockPositions.Entity); Dictionary <string, EntityResponse <OptionChain> > optionChains = new Dictionary <string, EntityResponse <OptionChain> >(); #endregion #region Fill additional information foreach (PortfolioOption portfolioItem in portfolioOptions) { EntityResponse <List <OptionBasicInformation> > optionBasicInformation = _marketDataProviderQueryable.GetOptionBasicInformation(optionNumber: portfolioItem.OptionNumber); if (optionBasicInformation.Entity == null || !optionBasicInformation.Entity.Any()) { continue; } OptionBasicInformation basicInfo = optionBasicInformation.Entity.Single(); DateTime expiryDate = basicInfo.ExpireDate; DateAndNumberOfDaysUntil expiry = _marketWorkTimeService.GetNumberOfDaysLeftUntilExpiry(expiryDate); portfolioItem.Expiry = expiry; portfolioItem.UnderlyingCode = basicInfo.OptionUnderlyingCode; portfolioItem.UnderlyingName = basicInfo.OptionUnderlyingName; portfolioItem.StrikePrice = basicInfo.StrikePrice; EntityResponse <OptionChain> optionChain; string underlying = portfolioItem.UnderlyingCode; if (optionChains.ContainsKey(underlying)) { optionChain = optionChains[underlying]; } else { optionChain = _marketDataService.GetOptionChain(underlying); optionChains.Add(underlying, optionChain); } if (optionChain == null) { continue; } Option option = optionChain.Entity[portfolioItem.OptionNumber]; if (option == null) { portfolioItem.UnderlyingCode = null; continue; } Greeks greeks = option.Greeks ?? new Greeks(); portfolioItem.LastPrice = (decimal)option.LatestTradedPrice; //optionChain.Entity.UnderlyingCurrentPrice; portfolioItem.PremiumMultiplier = option.RootPair.PremiumMultiplier; portfolioItem.Greeks = new PortfolioGreeks(portfolioItem.OptionAvailableQuantity, greeks, portfolioItem.OptionSide, portfolioItem.PremiumMultiplier); } portfolioOptions = portfolioOptions.Where(x => x.UnderlyingCode != null); foreach (PortfolioStock portfolioStock in portfolioStocks) { SecurityQuotation quote = _marketDataService.GetSecurityQuotation(portfolioStock.SecurityCode); portfolioStock.LastPrice = quote.LastPrice; portfolioStock.StockMarketValue = quote.LastPrice * portfolioStock.AvailableBalance; portfolioStock.Greeks = new PortfolioGreeks(portfolioStock.AdjustedAvailableQuantity, new Greeks() { Delta = 1 }, portfolioStock.OptionSide, 1); } #endregion IEnumerable <BasePortfolioItemGroup> groupedByStrategies = _strategyService.GetPortfolioItemsGroupedByStrategy(portfolioOptions, portfolioStocks); List <BasePortfolioItemGroupViewModel> result = Mapper.Map <IEnumerable <BasePortfolioItemGroup>, IEnumerable <BasePortfolioItemGroupViewModel> >(groupedByStrategies) .ToList(); return(result); }
public EntityResponse <OptionChain> GetOptionChain(string underlying) { EntityResponse <StockQuoteInfo> stockQuote = GetStockQuote(underlying); if (!stockQuote.IsSuccess) { return(EntityResponse <OptionChain> .Error(stockQuote)); } EntityResponse <List <OptionBasicInformation> > optionBasicInformationResponse = _marketDataProvider.GetOptionBasicInformation(underlying); if (!optionBasicInformationResponse.IsSuccess) { return(EntityResponse <OptionChain> .Error(optionBasicInformationResponse)); } double interestRate = _riskFreeRateProvider.GetRiskFreeRate(); decimal spotPrice = stockQuote.Entity.LastPrice == 0 ? (stockQuote.Entity.PreviousClose ?? 0) : (stockQuote.Entity.LastPrice ?? 0); //filtering OptionBasicInformation and get all not expired DateTime nowInmarketTimeZone = _marketWorkTimeService.NowInMarketTimeZone.Date; List <OptionBasicInformation> optionsBasicInformation = optionBasicInformationResponse.Entity .Where(item => item.ExpireDate.Date >= nowInmarketTimeZone.Date) .ToList(); List <string> optionNumbers = optionsBasicInformation.Select(item => item.OptionNumber).Distinct().ToList(); EntityResponse <List <OptionQuotation> > optionQuotesResponse = GetOptionQuotesByOptionNumbers(optionNumbers); if (!optionQuotesResponse.IsSuccess) { return(EntityResponse <OptionChain> .Error(optionQuotesResponse)); } // To filter the optionBasicInformation Dictionary <string, OptionQuotation> optionQuotesDict = new Dictionary <string, OptionQuotation>(); foreach (OptionQuotation itemOptionQuotation in optionQuotesResponse.Entity) { optionQuotesDict.Add(itemOptionQuotation.OptionNumber, itemOptionQuotation); } if (!MemoryCache.IsOptionChainCacheExpired(underlying)) { // memory cache working. MemoryCache.OptionChainCache[underlying].OptionChains.UpdateQuotation((double)spotPrice, interestRate, optionQuotesResponse.Entity); return(MemoryCache.OptionChainCache[underlying].OptionChains); } // todo: 4 requests here. Big problem with performance SecurityInformationCache securityInfo = _marketDataProvider .GetAllSecuritiesInformation().FirstOrDefault(s => s.SecurityCode == underlying); if (securityInfo == null) { return(ErrorCode.SZKingdomLibraryNoRecords); } HashSet <OptionPair> chains = new HashSet <OptionPair>(); foreach (OptionBasicInformation optionBasicInformation in optionsBasicInformation) { // Filter the option if the quotation of the specified options cannot be found if (!optionQuotesDict.ContainsKey(optionBasicInformation.OptionNumber)) { continue; } DateAndNumberOfDaysUntil expiry = _marketWorkTimeService .GetNumberOfDaysLeftUntilExpiry(optionBasicInformation.ExpireDate); OptionPair pair = new OptionPair { Expiry = expiry, StrikePrice = (double)optionBasicInformation.StrikePrice, PremiumMultiplier = optionBasicInformation.OptionUnit, SecurityCode = optionBasicInformation.OptionUnderlyingCode, SecurityName = optionBasicInformation.OptionUnderlyingName }; if (!chains.Contains(pair)) { chains.Add(pair); } else { pair = chains.Single(c => c.Equals(pair)); } Option op = new Option(pair, optionBasicInformation.OptionNumber, optionBasicInformation.OptionCode) { Name = optionBasicInformation.OptionName, OptionName = optionBasicInformation.OptionName, OpenInterest = optionBasicInformation.UncoveredPositionQuantity, SecurityCode = optionBasicInformation.OptionUnderlyingCode, PreviousClose = (double)optionBasicInformation.PreviousClosingPrice, Greeks = new Greeks() }; if (optionBasicInformation.OptionType == OptionType.Call) { op.LegType = LegType.Call; pair.CallOption = op; } else { op.LegType = LegType.Put; pair.PutOption = op; } } OptionChain chain = new OptionChain(chains, (double)spotPrice, interestRate, optionQuotesResponse.Entity); MemoryCache.AddOrUpdateOptionChainCache(underlying, chain); return(chain); }