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); }