/// <summary> /// Create a new <see cref="OHLCBarItem"/> using the specified properties. /// </summary> /// <param name="label">The _label that will appear in the legend.</param> /// <param name="points">An <see cref="IPointList"/> of double precision values that define /// the Date, Close, Open, High, and Low values for the curve. Note that this /// <see cref="IPointList" /> should contain <see cref="StockPt" /> items rather /// than <see cref="PointPair" /> items. /// </param> /// <param name="color"> /// The <see cref="System.Drawing.Color" /> to use for drawing the candlesticks.</param> public OHLCBarItem( string label, IPointList points, Color color ) : base(label, points) { _bar = new OHLCBar( color ); }
public static List<OHLCBar> ParseJson(JObject data, HistoricalDataRequest request) { var bars = new List<OHLCBar>(); var barTimeSpan = request.Frequency.ToTimeSpan(); Dictionary<int, InstrumentSession> sessionStartTimesByDay = request.Instrument.SessionStartTimesByDay(); var exchangeTz = TimeZoneInfo.FindSystemTimeZoneById(request.Instrument.Exchange.Timezone); JToken jsonBars = data["results"]; foreach (JToken jsonBar in jsonBars) { var bar = new OHLCBar { Open = decimal.Parse(jsonBar["open"].ToString()), High = decimal.Parse(jsonBar["high"].ToString()), Low = decimal.Parse(jsonBar["low"].ToString()), Close = decimal.Parse(jsonBar["close"].ToString()) }; long volume; if (long.TryParse(jsonBar.Value<string>("volume"), out volume)) { bar.Volume = volume; } if (request.Frequency < BarSize.OneDay) { //The timezone in which the data is delivered is NOT the exchange TZ //it seems to change depending on the whim of the api and/or location of user?!?! //anyway, we make sure the time is in the exchange's timezone bar.DTOpen = TimeZoneInfo.ConvertTime(DateTimeOffset.Parse(jsonBar["timestamp"].ToString()), exchangeTz).DateTime; bar.DT = bar.DTOpen.Value + barTimeSpan; //For intraday bars, the time is the bar's OPENING time //But it fails to "fit" properly...if you ask for hourly bars //it'll start at 9AM instead of 9:30AM open. So we use the instrument sessions to correct this. //closing time stays the same int dayOfWeek = (int)bar.DTOpen.Value.DayOfWeek -1; if (dayOfWeek < 0) dayOfWeek = 6; //small fix due to DayOfWeek vs DayOfTheWeek different format if (sessionStartTimesByDay.ContainsKey(dayOfWeek) && bar.DTOpen.Value.TimeOfDay < sessionStartTimesByDay[dayOfWeek].OpeningTime) { bar.DTOpen = bar.DTOpen.Value.Date.Add(sessionStartTimesByDay[dayOfWeek].OpeningTime); } } else { //daily bars or lower frequencies - here the time is provided as "2015-08-17T00:00:00-04:00", //i.e. just the day, not the actual closing time. We only need the date portion, so we parse the tradingDay field. bar.DT = DateTime.Parse(jsonBar["tradingDay"].ToString()); } string openInterest = jsonBar.Value<string>("openInterest"); if (!string.IsNullOrEmpty(openInterest)) { bar.OpenInterest = int.Parse(openInterest); } bars.Add(bar); } return bars; }
/// <summary> /// Constructor for deserializing objects /// </summary> /// <param name="info">A <see cref="SerializationInfo"/> instance that defines the serialized data /// </param> /// <param name="context">A <see cref="StreamingContext"/> instance that contains the serialized data /// </param> protected OHLCBarItem( SerializationInfo info, StreamingContext context ) : base(info, context) { // The schema value is just a file version parameter. You can use it to make future versions // backwards compatible as new member variables are added to classes int sch = info.GetInt32( "schema2" ); _bar = (OHLCBar)info.GetValue( "stick", typeof( OHLCBar ) ); }
/// <summary> /// Create a new <see cref="OHLCBarItem"/>, specifying only the legend label. /// </summary> /// <param name="label">The label that will appear in the legend.</param> public OHLCBarItem( string label ) : base(label) { _bar = new OHLCBar(); }
/// <summary> /// The Copy Constructor /// </summary> /// <param name="rhs">The <see cref="OHLCBarItem"/> object from which to copy</param> public OHLCBarItem( OHLCBarItem rhs ) : base(rhs) { _bar = rhs._bar.Clone(); }
/// <summary> /// Takes XML formated data from Quandl and turns it into a List of OHLCBars. /// </summary> public static List <OHLCBar> ParseXML(string data) { var doc = XDocument.Parse(data); var bars = new List <OHLCBar>(); //standard checks that the XML is what it should be if (doc.Root == null) { throw new Exception("XML Parse error: no root."); } if (doc.Root.Element("column-names") == null) { throw new Exception("Quandl: Column names element not found."); } //this simply gives us a list of column names, needed to parse the data correctly later on List <string> columns = doc.Root.Element("column-names").Elements("column-name").Select(x => x.Value).ToList(); //some columns are required.. if (!columns.Contains("Date")) { throw new Exception("Quandl: no date column, cannot parse data."); } var dataElement = doc.Root.Element("data"); if (dataElement == null) { throw new Exception("Quandl: No data present in XML file."); } bool skipBar, dateSet; //finally loop through each bar and try to parse it foreach (var barElements in dataElement.Elements("datum")) { skipBar = false; dateSet = false; decimal parsedValue; var bar = new OHLCBar(); int counter = 0; foreach (var price in barElements.Elements("datum")) { bool isNull = price.Attribute("nil") != null; //if the attribute "nil" exists, then the value of this field is null switch (columns[counter]) { case "Date": if (isNull) { skipBar = true; break; } bar.DT = DateTime.ParseExact(price.Value, "yyyy-MM-dd", CultureInfo.InvariantCulture); dateSet = true; break; case "Open": if (decimal.TryParse(price.Value, NumberStyles.Number | NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out parsedValue)) { bar.Open = parsedValue; } break; case "High": if (decimal.TryParse(price.Value, NumberStyles.Number | NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out parsedValue)) { bar.High = parsedValue; } break; case "Low": if (decimal.TryParse(price.Value, NumberStyles.Number | NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out parsedValue)) { bar.Low = parsedValue; } break; case "Close": if (decimal.TryParse(price.Value, NumberStyles.Number | NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out parsedValue)) { bar.Close = parsedValue; } break; case "Settle": //some futures data series have "settle" field instead of "close" if (!isNull && decimal.TryParse(price.Value, NumberStyles.Number | NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out parsedValue)) { bar.Close = parsedValue; } break; //volume and OI are not represented as ints for some reason case "Volume": double volume; if (!isNull && double.TryParse(price.Value, NumberStyles.Number | NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out volume)) { bar.Volume = (long)volume; } break; case "Open Interest": double openInterest; if (!isNull && double.TryParse(price.Value, NumberStyles.Number | NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out openInterest)) { bar.OpenInterest = (int)openInterest; } break; case "Adjusted Close": if (decimal.TryParse(price.Value, NumberStyles.Number | NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out parsedValue)) { bar.AdjClose = parsedValue; } break; } counter++; } //if the adjusted close is set, generate adjusted OHL if (bar.AdjClose.HasValue) { decimal adjFactor = bar.AdjClose.Value / bar.Close; bar.AdjOpen = bar.Open * adjFactor; bar.AdjHigh = bar.High * adjFactor; bar.AdjLow = bar.Low * adjFactor; } //make sure that the date has been set if (!dateSet) { skipBar = true; } if (!skipBar) { bars.Add(bar); } } return(bars); }
//Downloads data from yahoo. First dividends and splits, then actual price data private async Task <List <OHLCBar> > GetData(HistoricalDataRequest request) { var barSize = request.Frequency; var startDate = request.StartingDate; var endDate = request.EndingDate; var instrument = request.Instrument; var symbol = string.IsNullOrEmpty(instrument.DatasourceSymbol) ? instrument.Symbol : instrument.DatasourceSymbol; if (barSize < BarSize.OneDay) { throw new Exception("Bar size not supporterd"); //yahoo can't give us anything better than 1 day } if (startDate > endDate) { throw new Exception("Start date after end date"); //obvious } var data = new List <OHLCBar>(); //Splits string splitURL = string.Format(@"https://query1.finance.yahoo.com/v7/finance/download/{0}?period1={1}&period2={2}&interval=1d&events=split&crumb={3}", symbol, ToTimestamp(startDate), ToTimestamp(endDate), _crumb); var response = await _client.GetAsync(splitURL).ConfigureAwait(false); if (!response.IsSuccessStatusCode) { string errorMessage = string.Format("Error downloading split data from Yahoo, symbol {0}. Status code: {1} Url: ({2})", instrument.Symbol, response.StatusCode, splitURL); _logger.Log(LogLevel.Error, errorMessage); RaiseEvent(Error, this, new ErrorArgs(0, errorMessage)); return(new List <OHLCBar>()); } string contents = await response.Content.ReadAsStringAsync().ConfigureAwait(false); //stick dividends and splits into their respective dictionaries to be used later //the key is the date in yyyy-MM-dd format //Split format: //Date,Stock Splits //2014-06-09,7/1 var splits = new Dictionary <string, decimal>(); string[] rows = contents.Split("\n".ToCharArray()); for (int j = 1; j < rows.Length - 1; j++) //start at 1 because the first line's a header { string[] items = rows[j].Split(",".ToCharArray()); decimal splitNumerator, splitDenominator; string date = items[0]; string[] splitFactors = items[1].Trim().Split("/".ToCharArray()); if (decimal.TryParse(splitFactors[0], out splitNumerator) && decimal.TryParse(splitFactors[1], out splitDenominator)) { splits.Add(date, splitNumerator / splitDenominator); } } //Dividends string divURL = string.Format(@"https://query1.finance.yahoo.com/v7/finance/download/{0}?period1={1}&period2={2}&interval=1d&events=div&crumb={3}", symbol, ToTimestamp(startDate), ToTimestamp(endDate), _crumb); response = await _client.GetAsync(divURL).ConfigureAwait(false); if (!response.IsSuccessStatusCode) { string errorMessage = string.Format("Error downloading dividend data from Yahoo, symbol {0}. Status code: {1} Url: ({2})", instrument.Symbol, response.StatusCode, divURL); _logger.Log(LogLevel.Error, errorMessage); RaiseEvent(Error, this, new ErrorArgs(0, errorMessage)); return(new List <OHLCBar>()); } //Dividend Format //Date,Dividends //2014-11-06,0.47 contents = await response.Content.ReadAsStringAsync().ConfigureAwait(false); rows = contents.Split("\n".ToCharArray()); var dividends = new Dictionary <string, decimal>(); for (int j = 1; j < rows.Length - 1; j++) //start at 1 because the first line's a header { decimal dividend; string[] items = rows[j].Split(",".ToCharArray()); string date = items[0]; if (decimal.TryParse(items[1], out dividend)) { dividends.Add(date, dividend); } } //now download the actual price data //csv file comes in the following format: //Date,Open,High,Low,Close,Adj Close,Volume //2014-06-02,90.565712,90.690002,88.928574,628.650024,89.807144,92337700 string dataURL = string.Format(@"https://query1.finance.yahoo.com/v7/finance/download/{0}?period1={1}&period2={2}&interval=1d&events=history&crumb={3}", symbol, ToTimestamp(startDate), ToTimestamp(endDate), _crumb); response = await _client.GetAsync(dataURL).ConfigureAwait(false); if (!response.IsSuccessStatusCode) { string errorMessage = string.Format("Error downloading dividend data from Yahoo, symbol {0}. Status code: {1} Url: ({2})", instrument.Symbol, response.StatusCode, dataURL); _logger.Log(LogLevel.Error, errorMessage); RaiseEvent(Error, this, new ErrorArgs(0, errorMessage)); return(new List <OHLCBar>()); } contents = await response.Content.ReadAsStringAsync().ConfigureAwait(false); //parse the downloaded price data rows = contents.Split("\n".ToCharArray()); for (int j = 1; j < rows.Count() - 1; j++) //start at 1 because the first line's a header { string[] items = rows[j].Split(",".ToCharArray()); var bar = new OHLCBar(); if (dividends.ContainsKey(items[0])) { bar.Dividend = dividends[items[0]]; } if (splits.ContainsKey(items[0])) { bar.Split = splits[items[0]]; } //The OHL values are actually split adjusted, we want to turn them back try { var dt = DateTime.ParseExact(items[0], "yyyy-MM-dd", CultureInfo.InvariantCulture); bar.DT = dt; decimal adjRatio = decimal.Parse(items[4]) / decimal.Parse(items[5]); bar.Open = decimal.Parse(items[1]) * adjRatio; bar.High = decimal.Parse(items[2]) * adjRatio; bar.Low = decimal.Parse(items[3]) * adjRatio; bar.Close = decimal.Parse(items[4]); bar.Volume = long.Parse(items[6]); //set adj values so that in case they're not set later (eg if we only get one bar), they're still filled in bar.AdjOpen = bar.Open; bar.AdjHigh = bar.High; bar.AdjLow = bar.Low; bar.AdjClose = bar.Close; data.Add(bar); } catch { _logger.Error("Failed to parse line: " + rows[j]); } } //Note that due to the latest change, the adjusted close value is incorrect (doesn't include divs) //so we need to calc adj values ourselves PriceAdjuster.AdjustData(ref data); _logger.Log(LogLevel.Info, string.Format("Downloaded {0} bars from Yahoo, symbol {1}.", data.Count, instrument.Symbol)); return(data); }
public void AddDataAsync(OHLCBar data, Instrument instrument, BarSize frequency, bool overwrite = false) { AddData(new List <OHLCBar> { data }, instrument, frequency, overwrite); }
private OHLCBar ParseLine(string[] items, string[] columns, decimal priceMultiplier, int volumeMultiplier) { var bar = new OHLCBar(); TimeSpan?closingTime = null; bool success; for (int i = 0; i < items.Length; i++) { switch (columns[i]) { case "Date": DateTime tmpDate; success = DateTime.TryParseExact( items[i], DateFormatTextBox.Text, CultureInfo.InvariantCulture, DateTimeStyles.None, out tmpDate); if (!success) { throw new Exception("Incorrect date format."); } else { bar.DT = new DateTime(tmpDate.Ticks); } break; case "DateTime": DateTime tmpDT; success = DateTime.TryParseExact( items[i], DateTimeFormatTextBox.Text, CultureInfo.InvariantCulture, DateTimeStyles.None, out tmpDT); if (!success) { throw new Exception("Incorrect datetime format."); } else { bar.DT = new DateTime(tmpDT.Ticks); } break; case "DateTimeOpen": DateTime tmpDTOpen; success = DateTime.TryParseExact( items[i], DateTimeFormatTextBox.Text, CultureInfo.InvariantCulture, DateTimeStyles.None, out tmpDTOpen); if (!success) { throw new Exception("Incorrect datetime format."); } else { bar.DTOpen = new DateTime(tmpDTOpen.Ticks); } break; case "Time": DateTime tmpTS; success = DateTime.TryParseExact( items[i], TimeFormatTextBox.Text, CultureInfo.InvariantCulture, DateTimeStyles.None, out tmpTS); if (!success) { throw new Exception("Incorrect time format."); } else { closingTime = TimeSpan.FromSeconds(tmpTS.TimeOfDay.TotalSeconds); } break; case "Open": bar.Open = priceMultiplier * decimal.Parse(items[i]); break; case "High": bar.High = priceMultiplier * decimal.Parse(items[i]); break; case "Low": bar.Low = priceMultiplier * decimal.Parse(items[i]); break; case "Close": bar.Close = priceMultiplier * decimal.Parse(items[i]); break; case "AdjClose": bar.AdjClose = priceMultiplier * decimal.Parse(items[i]); break; case "Volume": bar.Volume = volumeMultiplier * long.Parse(items[i]); break; case "OpenInterest": bar.OpenInterest = int.Parse(items[i]); break; case "Dividends": bar.Dividend = decimal.Parse(items[i]); break; case "Splits": bar.Split = decimal.Parse(items[i]); break; } } //do the time addition if (closingTime != null) { bar.DT += closingTime.Value; } return(bar); }