/// <summary> /// Reads in the factor file for the specified equity symbol /// </summary> public static IEnumerable <FactorFileRow> Read(string permtick, string market, out DateTime?factorFileMinimumDate) { factorFileMinimumDate = null; var path = Path.Combine(Globals.DataFolder, "equity", market, "factor_files", permtick.ToLower() + ".csv"); var lines = File.ReadAllLines(path).Where(l => !string.IsNullOrWhiteSpace(l)); var hasInfEntry = false; var rows = new List <FactorFileRow>(); // parse factor file lines foreach (var line in lines) { if (line.Contains("inf")) { hasInfEntry = true; continue; } var row = Parse(line); if (hasInfEntry && rows.Count == 0) { // special handling for INF values: set minimum date factorFileMinimumDate = row.Date.AddDays(1); row = new FactorFileRow(row.Date.AddDays(-1), row.PriceFactor, row.SplitFactor); } rows.Add(row); } return(rows); }
/// <summary> /// Parses the contents as a FactorFile, if error returns a new empty factor file /// </summary> public static FactorFile SafeRead(string filename, IEnumerable <string> contents) { var permtick = Path.GetFileNameWithoutExtension(filename); try { DateTime?minimumDate; // FactorFileRow.Parse handles entries with 'inf' and exponential notation and provides the associated minimum tradeable date for these cases // previously these cases were not handled causing an exception and returning an empty factor file return(new FactorFile(permtick, FactorFileRow.Parse(contents, out minimumDate), minimumDate)); } catch { return(new FactorFile(permtick, Enumerable.Empty <FactorFileRow>())); } }
/// <summary> /// Gets price and split factors to be applied at the specified date /// </summary> public FactorFileRow GetScalingFactors(DateTime searchDate) { var factors = new FactorFileRow(searchDate, 1m, 1m); // Iterate backwards to find the most recent factors foreach (var splitDate in SortedFactorFileData.Keys.Reverse()) { if (splitDate.Date < searchDate.Date) { break; } factors = SortedFactorFileData[splitDate]; } return(factors); }
/// <summary> /// Creates a new dividend from this factor file row and the one chronologically in front of it /// This dividend may have a distribution of zero if this row doesn't represent a dividend /// </summary> /// <param name="futureFactorFileRow">The next factor file row in time</param> /// <param name="symbol">The symbol to use for the dividend</param> /// <param name="exchangeHours">Exchange hours used for resolving the previous trading day</param> /// <returns>A new dividend instance</returns> public Dividend GetDividend(FactorFileRow futureFactorFileRow, Symbol symbol, SecurityExchangeHours exchangeHours) { if (futureFactorFileRow.PriceFactor == 0m) { throw new InvalidOperationException($"Unable to resolve dividend for '{symbol.ID}' at {Date:yyyy-MM-dd}. Price factor is zero."); } // find previous trading day var previousTradingDay = exchangeHours.GetNextTradingDay(Date); return(Dividend.Create( symbol, previousTradingDay, ReferencePrice, PriceFactor / futureFactorFileRow.PriceFactor )); }
/// <summary> /// Creates a new split from this factor file row and the one chronologically in front of it /// This split may have a split factor of one if this row doesn't represent a split /// </summary> /// <param name="futureFactorFileRow">The next factor file row in time</param> /// <param name="symbol">The symbol to use for the split</param> /// <param name="exchangeHours">Exchange hours used for resolving the previous trading day</param> /// <returns>A new split instance</returns> public Split GetSplit(FactorFileRow futureFactorFileRow, Symbol symbol, SecurityExchangeHours exchangeHours) { if (futureFactorFileRow.SplitFactor == 0m) { throw new InvalidOperationException($"Unable to resolve split for '{symbol.ID}' at {Date:yyyy-MM-dd}. Split factor is zero."); } // find previous trading day var previousTradingDay = exchangeHours.GetNextTradingDay(Date); return(new Split( symbol, previousTradingDay, ReferencePrice, SplitFactor / futureFactorFileRow.SplitFactor, SplitType.SplitOccurred )); }
/// <summary> /// Parses the lines as factor files rows while properly handling inf entries /// </summary> /// <param name="lines">The lines from the factor file to be parsed</param> /// <param name="factorFileMinimumDate">The minimum date from the factor file</param> /// <returns>An enumerable of factor file rows</returns> public static List <FactorFileRow> Parse(IEnumerable <string> lines, out DateTime?factorFileMinimumDate) { var hasInfEntry = false; factorFileMinimumDate = null; var rows = new List <FactorFileRow>(); // parse factor file lines foreach (var line in lines) { if (line.Contains("inf")) { hasInfEntry = true; continue; } var row = Parse(line); if (hasInfEntry && rows.Count == 0) { // special handling for INF values: set minimum date factorFileMinimumDate = row.Date.AddDays(1); row = new FactorFileRow(row.Date.AddDays(-1), row.PriceFactor, row.SplitFactor, row.ReferencePrice); } // ignore zero factor rows if (row.PriceScaleFactor > 0) { rows.Add(row); } } if (factorFileMinimumDate == null && rows.Count > 0) { factorFileMinimumDate = rows.Min(ffr => ffr.Date).AddDays(-1); } return(rows); }
/// <summary> /// Reads a FactorFile in from the <see cref="Globals.DataFolder"/>. /// </summary> public static FactorFile Read(string permtick, string market) { return(new FactorFile(permtick, FactorFileRow.Read(permtick, market))); }
/// <summary> /// Reads a FactorFile in from the <see cref="Globals.DataFolder"/>. /// </summary> public static FactorFile Read(string permtick, string market) { DateTime?factorFileMinimumDate; return(new FactorFile(permtick, FactorFileRow.Read(permtick, market, out factorFileMinimumDate), factorFileMinimumDate)); }
/// <summary> /// Calculates the split factor of a <see cref="Split"/> /// </summary> /// <param name="split">The next <see cref="Split"/></param> /// <param name="previousFactorFileRow">The previous <see cref="FactorFileRow"/> generated</param> /// <returns><see cref="FactorFileRow"/> that represents the split event</returns> private FactorFileRow CalculateNextSplitFactor(BaseData split, FactorFileRow previousFactorFileRow) { var eventDayData = GetDailyDataForDate(split.Time); // If you don't have the equity data nothing can be done if (eventDayData == null) return null; TradeBar previousClosingPrice = FindPreviousTradableDayClosingPrice(eventDayData.Time); return new FactorFileRow( previousClosingPrice.Time, previousFactorFileRow.PriceFactor, (previousFactorFileRow.SplitFactor * split.Value).RoundToSignificantDigits(6) ); }
/// <summary> /// Calculates the price factor of a <see cref="Dividend"/> /// </summary> /// <param name="dividend">The next dividend</param> /// <param name="previousFactorFileRow">The previous <see cref="FactorFileRow"/> generated</param> /// <returns><see cref="FactorFileRow"/> that represents the dividend event</returns> private FactorFileRow CalculateNextDividendFactor(BaseData dividend, FactorFileRow previousFactorFileRow) { var eventDayData = GetDailyDataForDate(dividend.Time); // If you don't have the equity data nothing can be calculated if (eventDayData == null) return null; TradeBar previousClosingPrice = FindPreviousTradableDayClosingPrice(eventDayData.Time); var priceFactor = previousFactorFileRow.PriceFactor - (dividend.Value / ((previousClosingPrice.Close) * previousFactorFileRow.SplitFactor)); return new FactorFileRow(previousClosingPrice.Time, priceFactor.RoundToSignificantDigits(7), previousFactorFileRow.SplitFactor); }
/// <summary> /// Generates the <see cref="FactorFileRow"/> that represents a intraday dividend split. /// Applies the dividend first. /// </summary> /// <param name="intraDayDividendSplit"><see cref="IntraDayDividendSplit"/> instance that holds the intraday dividend and split information</param> /// <param name="last">The last <see cref="FactorFileRow"/> generated recursivly</param> /// <returns><see cref="FactorFileRow"/> that represents an intraday dividend and split</returns> private FactorFileRow CalculateIntradayDividendSplit(IntraDayDividendSplit intraDayDividendSplit, FactorFileRow last) { var row = CalculateNextDividendFactor(intraDayDividendSplit.Dividend, last); return CalculateNextSplitFactor(intraDayDividendSplit.Split, row); }