/// <summary> /// Returns the list of the tickers currently supported by this market data provider. /// </summary> /// <param name="filter">The filter to use to choose which symbols to return.</param> /// <returns>The supported ticker array.</returns> /// <remarks> /// For Yahoo! Finance it's not possible to get the full list, so queries will return /// data only if filtered. /// </remarks> public ISymbolDefinition[] SupportedTickers(string filter = null) { // Normalization of =X to strings without them. Func <string, string> cleanCurrency = x => x.EndsWith("=X") ? x.Remove(x.Length - 2) : x; try { if (filter != null && filter.Length > 0) { List <ISymbolDefinition> symbols = new List <ISymbolDefinition>(); YahooFinanceAPI.GetTickersWithFilter(filter).ForEach(x => symbols.Add(new SymbolDefinition(cleanCurrency((string)x["symbol"]), "Yahoo! Finance " + (string)x["typeDisp"] + " (" + (string)x["name"] + ")"))); return(symbols.ToArray()); } } catch (Exception) { } return(new ISymbolDefinition[0]); }
/// <summary> /// Checks if the Yahoo! Finance service is reachable /// and answers to requests correctly. /// </summary> /// <remarks> /// This is done by requesting a well known quote and checking if data is returned, /// the sanity of the data is not checked. /// </remarks> /// <returns> /// A <see cref="Status"/> indicating if the /// Yahoo! Finance service is working. /// </returns> public Status TestConnectivity() { // Prepare the default result, in case everything will go well. Status state = new Status(); state.HasErrors = false; state.ErrorMessage = string.Empty; try { // Try simply requesting a single data series known to exist // and to produce 1 result (we use GOOG at 31 jan 2011). List <YahooHistoricalQuote> quotes = YahooFinanceAPI.GetHistoricalQuotes("GOOG", new DateTime(2011, 1, 31), new DateTime(2011, 1, 31)); if (quotes.Count != 1) { // If there is a number of results different than 1, // it means the service is not answering as expected, // so fail the test. state.HasErrors = true; state.ErrorMessage = "Data from Yahoo! Finance not available or unreliable."; } } catch (Exception e) { // If an exception was thrown during data fetching it means // there is a problem with the service (either timeout, // connection failure, or Yahoo! changed data format). state.HasErrors = true; state.ErrorMessage = "Unable to connect to Yahoo! Finance service: " + e.Message; } return(state); }
/// <summary> /// Retrieves available call and put options for a given ticker. /// </summary> /// <param name="mdq">The market data query.</param> /// <param name="marketData">The requested market data.</param> /// <returns>A <see cref="RefreshStatus"/> with the status of the result.</returns> private RefreshStatus GetCallPriceMarketData(MarketDataQuery mdq, out IMarketData marketData) { marketData = null; //Check if the market data is in cache string cachedName = Path.Combine(Path.GetTempPath(), "CallPrices" + mdq.Ticker + mdq.Date.Year + mdq.Date.Month + mdq.Date.Day); List <YahooOptionChain> optionChains = null; if (System.IO.File.Exists(cachedName)) { try { optionChains = (List <YahooOptionChain>)DVPLI.ObjectSerialization.ReadFromFile(cachedName); } catch { //Failed to read from cache } } //if not found in cache try to get from the Yahoo service if (optionChains == null) { //Yahoo returns only last traded options hence, we assume that the only //valid dates are Today and Yesterday DateTime tMax = DateTime.Today; DateTime tMin = tMax.AddDays(-1); if (tMax.DayOfWeek == DayOfWeek.Monday) { tMin = tMax.AddDays(-3); } if (mdq.Date.Date < tMin || mdq.Date.Date > tMax) { return(new RefreshStatus("Options are not available for the requested period. Set the valuation date to Yesterday")); } // Request options does not seems to give the option effectively // tradaded at a given date but the options that are still being traded. optionChains = YahooFinanceAPI.RequestOptions(mdq.Ticker); try { DVPLI.ObjectSerialization.WriteToFile(cachedName, optionChains); } catch { } } Fairmat.MarketData.CallPriceMarketData data = new Fairmat.MarketData.CallPriceMarketData(); // Extract a list of YahooOption from the YahooOptionChain List. List <YahooOption> options = new List <YahooOption>(); foreach (YahooOptionChain q in optionChains) { Console.WriteLine(q.Symbol + " " + q.Expiration); foreach (YahooOption o in q.Options) { // Loads into YahooOption the needed information. o.Maturity = q.Expiration; options.Add(o); } } // Populate the CallPriceMarketData data structure var status = OptionQuotesUtility.GetCallPriceMarketData(this, mdq, options.ConvertAll(x => (IOptionQuote)x), data); if (status.HasErrors) { return(status); } marketData = data; Console.WriteLine(data); return(status); }
/// <summary> /// Gets a series of Historical Market Data from the starting date /// to the end date. /// </summary> /// <param name="mdq"> /// A <see cref="MarketDataQuery"/> with the data request. /// </param> /// <param name="end"> /// A <see cref="DateTime"/> with the ending date of the period to fetch data from. /// </param> /// <param name="dates"> /// In case of success, a list of the dates data was fetched from in the requested period. /// </param> /// <param name="marketData"> /// In case of success, a list of the fetched market data day /// by day corresponding to <see cref="dates"/>. /// </param> /// <returns> /// A <see cref="RefreshStatus"/> indicating if the query was successful. /// </returns> public RefreshStatus GetTimeSeries(MarketDataQuery mdq, DateTime end, out DateTime[] dates, out IMarketData[] marketData) { RefreshStatus status = new RefreshStatus(); // Holds whathever we should take market close or market open values. bool closeRequest; // Holds the currency conversion target in case a market different than // US is choosen. This handles conversion from USD values provided by yahoo. string targetMarket = null; string ticker = TickerUtility.PreparseSymbol(mdq.Ticker); bool divisionTransformation = false; bool inverseTransformation = false; // Check if open or close value was requested. switch (mdq.Field) { case "open": { closeRequest = false; break; } case "close": { closeRequest = true; break; } default: { // In case the request is neither open or close return an error. marketData = null; dates = null; status.HasErrors = true; status.ErrorMessage += "GetTimeSeries: Market data not available (only " + "open and close values are available, " + mdq.Field + " was requested)."; return(status); } } // Gather in which currency to get the data, this will require // an additional data fetching for the target currency for the same time period. if (mdq.Market.Length > 0 && mdq.Market != "US") { // Try to convert the entries in the Fairmat drop down box, // for the rest rely on the user. switch (mdq.Market) { case "EU": { targetMarket = "EUR"; break; } case "GB": { targetMarket = "GBP"; break; } case "JP": { targetMarket = "JPY"; break; } case "CH": { targetMarket = "CHF"; break; } case "HK": { targetMarket = "HKD"; break; } default: { // In the fallback scenario just use directly the provided string. targetMarket = mdq.Market; break; } } } // Check for currencies and handle them in a special way. // Check the single currency only (eg: EUR for USDEUR). if (GetCurrencyList().Contains(ticker)) { ticker += "=X"; // Disable the feature for this for now. targetMarket = null; } else { // Attempt a more throughout parsing. Check for <currency><currency> formats. foreach (string currency in GetCurrencyList()) { if (ticker.StartsWith(currency)) { if (currency == "USD") { // If usd is in the ticker name it's a special case due to the way // Yahoo! Finance keeps currencies. ticker = ticker.Remove(0, 3) + "=X"; targetMarket = null; } else if (ticker.Remove(0, 3) == "USD") { // This is the inverse of the previous case. // From other currency to USD. Similarly to above it's a special case. ticker = currency + "=X"; targetMarket = null; inverseTransformation = true; } else { // Normal not USD to not USD currency conversions. targetMarket = currency; ticker = ticker.Remove(0, 3) + "=X"; divisionTransformation = true; } break; } } } // For now only Scalar requests are handled. if (mdq.MarketDataType == typeof(Scalar).ToString()) { List <YahooHistoricalQuote> quotes = null; Dictionary <DateTime, YahooHistoricalQuote> currencyQuotes = new Dictionary <DateTime, YahooHistoricalQuote>(); try { // Request the data to the Market Data Provider. quotes = YahooFinanceAPI.GetHistoricalQuotes(ticker, mdq.Date, end); if (targetMarket != null) { // If we need currency quotes in order to handle currency conversions // fetch them now. List <YahooHistoricalQuote> fetchedCurrencyQuotes = YahooFinanceAPI.GetHistoricalQuotes(targetMarket + "=X", mdq.Date, end); // Put all items in a dictionary for easy fetching. fetchedCurrencyQuotes.ForEach(x => currencyQuotes.Add(x.Date, x)); } } catch (Exception e) { // There can be conversion, server availability // and other exceptions during this request. marketData = null; dates = null; status.HasErrors = true; status.ErrorMessage += "GetTimeSeries: Market data not available due " + "to problems with Yahoo! Finance: " + e.Message; return(status); } // Check if there is at least one result. if (quotes.Count >= 1) { // Allocate the structures for the output. List <Scalar> readyMarketData = new List <Scalar>(); List <DateTime> readyDates = new List <DateTime>(); // Scan the list of quotes to prepare the data for Fairmat. for (int i = 0; i < quotes.Count; i++) { // Prepare the single scalar data. Scalar val = new Scalar(); val.TimeStamp = quotes[i].Date; val.Value = (closeRequest == true) ? quotes[i].Close : quotes[i].Open; // Handle currency conversions if needed. if (currencyQuotes != null) { // Check if the entry in a date exists also for the // currency conversion, if not discard the data. if (!currencyQuotes.ContainsKey(quotes[i].Date)) { // We skip this entry. continue; } YahooHistoricalQuote currencyQuote = currencyQuotes[quotes[i].Date]; //if (divisionTransformation) //{ // val.Value /= (closeRequest == true) ? currencyQuote.Close : currencyQuote.Open; //} //else //{ // val.Value *= (closeRequest == true) ? currencyQuote.Close : currencyQuote.Open; //} } // Apply an inverse transformation, used for currency values when going // from a not USD currency to USD. //if (inverseTransformation) //{ // val.Value = 1 / val.Value; //} // Put it in the output structure. readyMarketData.Add(val); // Fill the dates array from the date field of each quote. readyDates.Add(quotes[i].Date); } // Put in the output data. marketData = readyMarketData.ToArray(); dates = readyDates.ToArray(); return(status); } else { // If there isn't at least one result return an error. marketData = null; dates = null; status.HasErrors = true; status.ErrorMessage += "GetTimeSeries: Market data not available: " + "empty data set for the request."; return(status); } } else { // If control falls through here it means the request type was not supported. marketData = null; dates = null; status.HasErrors = true; status.ErrorMessage += "GetTimeSeries: Market data request type (" + mdq.MarketDataType + ") not supported by the Market Data Provider."; return(status); } }