chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:02:50 +08:00
commit 0fc60fdcb1
5008 changed files with 910633 additions and 0 deletions
+100
View File
@@ -0,0 +1,100 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
namespace QuantConnect.Data.Auxiliary
{
/// <summary>
/// Unique definition key for a collection of auxiliary data for a Market and SecurityType
/// </summary>
public class AuxiliaryDataKey
{
/// <summary>
/// USA equities market corporate actions key definition
/// </summary>
public static AuxiliaryDataKey EquityUsa { get; } = new (QuantConnect.Market.USA, SecurityType.Equity);
/// <summary>
/// The market associated with these corporate actions
/// </summary>
public string Market { get; }
/// <summary>
/// The associated security type
/// </summary>
public SecurityType SecurityType { get; }
/// <summary>
/// Creates a new instance
/// </summary>
public AuxiliaryDataKey(string market, SecurityType securityType)
{
Market = market;
SecurityType = securityType;
}
/// <summary>
/// Serves as a hash function for a particular type.
/// </summary>
public override int GetHashCode()
{
unchecked
{
var hashCode = Market.GetHashCode();
return (hashCode*397) ^ SecurityType.GetHashCode();
}
}
/// <summary>
/// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
/// </summary>
/// <returns>
/// true if the specified object is equal to the current object; otherwise, false.
/// </returns>
/// <param name="obj">The object to compare with the current object. </param><filterpriority>2</filterpriority>
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj)) return false;
if (obj.GetType() != GetType()) return false;
var other = (AuxiliaryDataKey)obj;
return other.Market == Market
&& other.SecurityType == SecurityType;
}
/// <summary>
/// Returns a string containing the market and security type
/// </summary>
public override string ToString()
{
return $"{Market}:{SecurityType}";
}
/// <summary>
/// Helper method to create a new instance from a Symbol
/// </summary>
public static AuxiliaryDataKey Create(Symbol symbol) => Create(symbol.HasUnderlying ? symbol.Underlying.ID : symbol.ID);
/// <summary>
/// Helper method to create a new instance from a SecurityIdentifier
/// </summary>
public static AuxiliaryDataKey Create(SecurityIdentifier securityIdentifier)
{
securityIdentifier = securityIdentifier.HasUnderlying ? securityIdentifier.Underlying : securityIdentifier;
return new AuxiliaryDataKey(securityIdentifier.Market, securityIdentifier.SecurityType);
}
}
}
@@ -0,0 +1,274 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
using System.Linq;
using QuantConnect.Util;
using QuantConnect.Logging;
using QuantConnect.Securities;
using QuantConnect.Data.Market;
using System.Collections.Generic;
namespace QuantConnect.Data.Auxiliary
{
/// <summary>
/// Corporate related factor provider. Factors based on splits and dividends
/// </summary>
public class CorporateFactorProvider : FactorFile<CorporateFactorRow>
{
/// <summary>
///Creates a new instance
/// </summary>
public CorporateFactorProvider(string permtick, IEnumerable<CorporateFactorRow> data, DateTime? factorFileMinimumDate = null) : base(permtick, data, factorFileMinimumDate)
{
}
/// <summary>
/// Gets the price scale factor that includes dividend and split adjustments for the specified search date
/// </summary>
public override decimal GetPriceFactor(DateTime searchDate, DataNormalizationMode dataNormalizationMode, DataMappingMode? dataMappingMode = null, uint contractOffset = 0)
{
if (dataNormalizationMode == DataNormalizationMode.Raw)
{
return 0;
}
var factor = 1m;
for (var i = 0; i < ReversedFactorFileDates.Count; i++)
{
var factorDate = ReversedFactorFileDates[i];
if (factorDate.Date < searchDate.Date)
{
break;
}
var factorFileRow = SortedFactorFileData[factorDate];
switch (dataNormalizationMode)
{
case DataNormalizationMode.TotalReturn:
case DataNormalizationMode.SplitAdjusted:
factor = factorFileRow.First().SplitFactor;
break;
case DataNormalizationMode.Adjusted:
case DataNormalizationMode.ScaledRaw:
factor = factorFileRow.First().PriceScaleFactor;
break;
default:
throw new ArgumentOutOfRangeException();
}
}
return factor;
}
/// <summary>
/// Gets price and split factors to be applied at the specified date
/// </summary>
public CorporateFactorRow GetScalingFactors(DateTime searchDate)
{
var factors = new CorporateFactorRow(searchDate, 1m, 1m, 0m);
// Iterate backwards to find the most recent factors
foreach (var splitDate in ReversedFactorFileDates)
{
if (splitDate.Date < searchDate.Date) break;
factors = SortedFactorFileData[splitDate][0];
}
return factors;
}
/// <summary>
/// Returns true if the specified date is the last trading day before a dividend event
/// is to be fired
/// </summary>
/// <remarks>
/// NOTE: The dividend event in the algorithm should be fired at the end or AFTER
/// this date. This is the date in the file that a factor is applied, so for example,
/// MSFT has a 31 cent dividend on 2015.02.17, but in the factor file the factor is applied
/// to 2015.02.13, which is the first trading day BEFORE the actual effective date.
/// </remarks>
/// <param name="date">The date to check the factor file for a dividend event</param>
/// <param name="priceFactorRatio">When this function returns true, this value will be populated
/// with the price factor ratio required to scale the closing value (pf_i/pf_i+1)</param>
/// <param name="referencePrice">When this function returns true, this value will be populated
/// with the reference raw price, which is the close of the provided date</param>
public bool HasDividendEventOnNextTradingDay(DateTime date, out decimal priceFactorRatio, out decimal referencePrice)
{
priceFactorRatio = 0;
referencePrice = 0;
var index = SortedFactorFileData.IndexOfKey(date);
if (index > -1 && index < SortedFactorFileData.Count - 1)
{
// grab the next key to ensure it's a dividend event
var thisRow = SortedFactorFileData.Values[index].First();
var nextRow = SortedFactorFileData.Values[index + 1].First();
// if the price factors have changed then it's a dividend event
if (thisRow.PriceFactor != nextRow.PriceFactor)
{
priceFactorRatio = thisRow.PriceFactor / nextRow.PriceFactor;
referencePrice = thisRow.ReferencePrice;
return true;
}
}
return false;
}
/// <summary>
/// Returns true if the specified date is the last trading day before a split event
/// is to be fired
/// </summary>
/// <remarks>
/// NOTE: The split event in the algorithm should be fired at the end or AFTER this
/// date. This is the date in the file that a factor is applied, so for example MSFT
/// has a split on 1999.03.29, but in the factor file the split factor is applied on
/// 1999.03.26, which is the first trading day BEFORE the actual split date.
/// </remarks>
/// <param name="date">The date to check the factor file for a split event</param>
/// <param name="splitFactor">When this function returns true, this value will be populated
/// with the split factor ratio required to scale the closing value</param>
/// <param name="referencePrice">When this function returns true, this value will be populated
/// with the reference raw price, which is the close of the provided date</param>
public bool HasSplitEventOnNextTradingDay(DateTime date, out decimal splitFactor, out decimal referencePrice)
{
splitFactor = 1;
referencePrice = 0;
var index = SortedFactorFileData.IndexOfKey(date);
if (index > -1 && index < SortedFactorFileData.Count - 1)
{
// grab the next key to ensure it's a split event
var thisRow = SortedFactorFileData.Values[index].First();
var nextRow = SortedFactorFileData.Values[index + 1].First();
// if the split factors have changed then it's a split event
if (thisRow.SplitFactor != nextRow.SplitFactor)
{
splitFactor = thisRow.SplitFactor / nextRow.SplitFactor;
referencePrice = thisRow.ReferencePrice;
return true;
}
}
return false;
}
/// <summary>
/// Gets all of the splits and dividends represented by this factor file
/// </summary>
/// <param name="symbol">The symbol to ues for the dividend and split objects</param>
/// <param name="exchangeHours">Exchange hours used for resolving the previous trading day</param>
/// <param name="decimalPlaces">The number of decimal places to round the dividend's distribution to, defaulting to 2</param>
/// <returns>All splits and dividends represented by this factor file in chronological order</returns>
public List<BaseData> GetSplitsAndDividends(Symbol symbol, SecurityExchangeHours exchangeHours, int decimalPlaces = 2)
{
var dividendsAndSplits = new List<BaseData>();
if (SortedFactorFileData.Count == 0)
{
Log.Trace($"{symbol} has no factors!");
return dividendsAndSplits;
}
var futureFactorFileRow = SortedFactorFileData.Last().Value.First();
for (var i = SortedFactorFileData.Count - 2; i >= 0; i--)
{
var row = SortedFactorFileData.Values[i].First();
var dividend = row.GetDividend(futureFactorFileRow, symbol, exchangeHours, decimalPlaces);
if (dividend.Distribution != 0m)
{
dividendsAndSplits.Add(dividend);
}
var split = row.GetSplit(futureFactorFileRow, symbol, exchangeHours);
if (split.SplitFactor != 1m)
{
dividendsAndSplits.Add(split);
}
futureFactorFileRow = row;
}
return dividendsAndSplits.OrderBy(d => d.Time.Date).ToList();
}
/// <summary>
/// Creates a new factor file with the specified data applied.
/// Only <see cref="Dividend"/> and <see cref="Split"/> data types
/// will be used.
/// </summary>
/// <param name="data">The data to apply</param>
/// <param name="exchangeHours">Exchange hours used for resolving the previous trading day</param>
/// <returns>A new factor file that incorporates the specified dividend</returns>
public CorporateFactorProvider Apply(List<BaseData> data, SecurityExchangeHours exchangeHours)
{
if (data.Count == 0)
{
return this;
}
var factorFileRows = new List<CorporateFactorRow>();
var firstEntry = SortedFactorFileData.First().Value.First();
var lastEntry = SortedFactorFileData.Last().Value.First();
factorFileRows.Add(lastEntry);
var splitsAndDividends = GetSplitsAndDividends(data[0].Symbol, exchangeHours);
var combinedData = splitsAndDividends.Concat(data)
.DistinctBy(e => $"{e.GetType().Name}{e.Time.ToStringInvariant(DateFormat.EightCharacter)}")
.OrderByDescending(d => d.Time.Date);
foreach (var datum in combinedData)
{
CorporateFactorRow nextEntry = null;
var split = datum as Split;
var dividend = datum as Dividend;
if (dividend != null)
{
nextEntry = lastEntry.Apply(dividend, exchangeHours);
lastEntry = nextEntry;
}
else if (split != null)
{
nextEntry = lastEntry.Apply(split, exchangeHours);
lastEntry = nextEntry;
}
if (nextEntry != null)
{
// overwrite the latest entry -- this handles splits/dividends on the same date
if (nextEntry.Date == factorFileRows.Last().Date)
{
factorFileRows[factorFileRows.Count - 1] = nextEntry;
}
else
{
factorFileRows.Add(nextEntry);
}
}
}
var firstFactorFileRow = new CorporateFactorRow(firstEntry.Date, factorFileRows.Last().PriceFactor, factorFileRows.Last().SplitFactor, firstEntry.ReferencePrice == 0 ? 0 : firstEntry.ReferencePrice);
var existing = factorFileRows.FindIndex(row => row.Date == firstFactorFileRow.Date);
if (existing == -1)
{
// only add it if not present
factorFileRows.Add(firstFactorFileRow);
}
return new CorporateFactorProvider(Permtick, factorFileRows, FactorFileMinimumDate);
}
}
}
+319
View File
@@ -0,0 +1,319 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
using System.Linq;
using System.Globalization;
using QuantConnect.Securities;
using QuantConnect.Data.Market;
using System.Collections.Generic;
using static QuantConnect.StringExtensions;
namespace QuantConnect.Data.Auxiliary
{
/// <summary>
/// Defines a single row in a factor_factor file. This is a csv file ordered as {date, price factor, split factor, reference price}
/// </summary>
public class CorporateFactorRow : IFactorRow
{
private decimal _splitFactor;
private decimal _priceFactor;
/// <summary>
/// Gets the date associated with this data
/// </summary>
public DateTime Date { get; private set; }
/// <summary>
/// Gets the price factor associated with this data
/// </summary>
public decimal PriceFactor
{
get
{
return _priceFactor;
}
set
{
_priceFactor = value;
UpdatePriceScaleFactor();
}
}
/// <summary>
/// Gets the split factor associated with the date
/// </summary>
public decimal SplitFactor
{
get
{
return _splitFactor;
}
set
{
_splitFactor = value;
UpdatePriceScaleFactor();
}
}
/// <summary>
/// Gets the combined factor used to create adjusted prices from raw prices
/// </summary>
public decimal PriceScaleFactor { get; private set; }
/// <summary>
/// Gets the raw closing value from the trading date before the updated factor takes effect
/// </summary>
public decimal ReferencePrice { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="CorporateFactorRow"/> class
/// </summary>
public CorporateFactorRow(DateTime date, decimal priceFactor, decimal splitFactor, decimal referencePrice = 0)
{
Date = date;
ReferencePrice = referencePrice;
PriceFactor = priceFactor;
SplitFactor = splitFactor;
}
/// <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<CorporateFactorRow> Parse(IEnumerable<string> lines, out DateTime? factorFileMinimumDate)
{
factorFileMinimumDate = null;
var rows = new List<CorporateFactorRow>();
// parse factor file lines
foreach (var line in lines)
{
// Exponential notation is treated as inf is because of the loss of precision. In
// all cases, the significant part has fewer decimals than the needed for a correct
// representation, E.g., 1.6e+6 when the correct factor is 1562500.
if (line.Contains("inf") || line.Contains("e+"))
{
continue;
}
var row = Parse(line);
// ignore zero factor rows
if (row.PriceScaleFactor > 0)
{
rows.Add(row);
}
}
if (rows.Count > 0)
{
factorFileMinimumDate = rows.Min(ffr => ffr.Date).AddDays(-1);
}
return rows;
}
/// <summary>
/// Applies the dividend to this factor file row.
/// This dividend date must be on or before the factor
/// file row date
/// </summary>
/// <param name="dividend">The dividend to apply with reference price and distribution specified</param>
/// <param name="exchangeHours">Exchange hours used for resolving the previous trading day</param>
/// <returns>A new factor file row that applies the dividend to this row's factors</returns>
public CorporateFactorRow Apply(Dividend dividend, SecurityExchangeHours exchangeHours)
{
if (dividend.ReferencePrice == 0m)
{
throw new ArgumentException("Unable to apply dividend with reference price of zero.");
}
var previousTradingDay = exchangeHours.GetPreviousTradingDay(dividend.Time);
// this instance must be chronologically at or in front of the dividend
// this is because the factors are defined working from current to past
if (Date < previousTradingDay)
{
throw new ArgumentException(Invariant(
$"Factor file row date '{Date:yyy-MM-dd}' is before dividend previous trading date '{previousTradingDay.Date:yyyy-MM-dd}'."
));
}
// pfi - new price factor pf(i+1) - this price factor D - distribution C - previous close
// pfi = pf(i+1) * (C-D)/C
var priceFactor = PriceFactor * (dividend.ReferencePrice - dividend.Distribution) / dividend.ReferencePrice;
return new CorporateFactorRow(
previousTradingDay,
priceFactor,
SplitFactor,
dividend.ReferencePrice
);
}
/// <summary>
/// Applies the split to this factor file row.
/// This split date must be on or before the factor
/// file row date
/// </summary>
/// <param name="split">The split to apply with reference price and split factor specified</param>
/// <param name="exchangeHours">Exchange hours used for resolving the previous trading day</param>
/// <returns>A new factor file row that applies the split to this row's factors</returns>
public CorporateFactorRow Apply(Split split, SecurityExchangeHours exchangeHours)
{
if (split.Type == SplitType.Warning)
{
throw new ArgumentException("Unable to apply split with type warning. Only actual splits may be applied");
}
if (split.ReferencePrice == 0m)
{
throw new ArgumentException("Unable to apply split with reference price of zero.");
}
var previousTradingDay = exchangeHours.GetPreviousTradingDay(split.Time);
// this instance must be chronologically at or in front of the split
// this is because the factors are defined working from current to past
if (Date < previousTradingDay)
{
throw new ArgumentException(Invariant(
$"Factor file row date '{Date:yyy-MM-dd}' is before split date '{split.Time.Date:yyyy-MM-dd}'."
));
}
return new CorporateFactorRow(
previousTradingDay,
PriceFactor,
SplitFactor * split.SplitFactor,
split.ReferencePrice
);
}
/// <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="nextCorporateFactorRow">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>
/// <param name="decimalPlaces">The number of decimal places to round the dividend's distribution to, defaulting to 2</param>
/// <returns>A new dividend instance</returns>
public Dividend GetDividend(CorporateFactorRow nextCorporateFactorRow, Symbol symbol, SecurityExchangeHours exchangeHours, int decimalPlaces=2)
{
if (nextCorporateFactorRow.PriceFactor == 0m)
{
throw new InvalidOperationException(Invariant(
$"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 / nextCorporateFactorRow.PriceFactor,
decimalPlaces
);
}
/// <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="nextCorporateFactorRow">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(CorporateFactorRow nextCorporateFactorRow, Symbol symbol, SecurityExchangeHours exchangeHours)
{
if (nextCorporateFactorRow.SplitFactor == 0m)
{
throw new InvalidOperationException(Invariant(
$"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 / nextCorporateFactorRow.SplitFactor,
SplitType.SplitOccurred
);
}
/// <summary>
/// Parses the specified line as a factor file row
/// </summary>
private static CorporateFactorRow Parse(string line)
{
var csv = line.Split(',');
return new CorporateFactorRow(
QuantConnect.Parse.DateTimeExact(csv[0], DateFormat.EightCharacter, DateTimeStyles.None),
QuantConnect.Parse.Decimal(csv[1]),
QuantConnect.Parse.Decimal(csv[2]),
csv.Length > 3 ? QuantConnect.Parse.Decimal(csv[3]) : 0m
);
}
/// <summary>
/// Writes factor file row into it's file format
/// </summary>
/// <remarks>CSV formatted</remarks>
public string GetFileFormat(string source = null)
{
source = source == null ? "" : $",{source}";
return $"{Date.ToStringInvariant(DateFormat.EightCharacter)}," +
Invariant($"{Math.Round(PriceFactor, 7)},") +
Invariant($"{Math.Round(SplitFactor, 8)},") +
Invariant($"{Math.Round(ReferencePrice, 4).Normalize()}") +
$"{source}";
}
/// <summary>
/// Returns a string that represents the current object.
/// </summary>
/// <returns>
/// A string that represents the current object.
/// </returns>
/// <filterpriority>2</filterpriority>
public override string ToString()
{
return Invariant($"{Date:yyyy-MM-dd}: {PriceScaleFactor:0.0000} {SplitFactor:0.0000}");
}
/// <summary>
/// For performance we update <see cref="PriceScaleFactor"/> when underlying
/// values are updated to avoid decimal multiplication on each get operation.
/// </summary>
private void UpdatePriceScaleFactor()
{
PriceScaleFactor = _priceFactor * _splitFactor;
}
}
}
+143
View File
@@ -0,0 +1,143 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
using System.IO;
using System.Linq;
using QuantConnect.Util;
using System.Collections;
using System.Collections.Generic;
namespace QuantConnect.Data.Auxiliary
{
/// <summary>
/// Represents an entire factor file for a specified symbol
/// </summary>
public abstract class FactorFile<T> : IFactorProvider
where T : IFactorRow
{
/// <summary>
/// Keeping a reversed version is more performant that reversing it each time we need it
/// </summary>
protected List<DateTime> ReversedFactorFileDates { get; }
/// <summary>
/// The factor file data rows sorted by date
/// </summary>
public SortedList<DateTime, List<T>> SortedFactorFileData { get; set; }
/// <summary>
/// The minimum tradeable date for the symbol
/// </summary>
/// <remarks>
/// Some factor files have INF split values, indicating that the stock has so many splits
/// that prices can't be calculated with correct numerical precision.
/// To allow backtesting these symbols, we need to move the starting date
/// forward when reading the data.
/// Known symbols: GBSN, JUNI, NEWL
/// </remarks>
public DateTime? FactorFileMinimumDate { get; set; }
/// <summary>
/// Gets the most recent factor change in the factor file
/// </summary>
public DateTime MostRecentFactorChange => ReversedFactorFileDates
.FirstOrDefault(time => time != Time.EndOfTime);
/// <summary>
/// Gets the symbol this factor file represents
/// </summary>
public string Permtick { get; }
/// <summary>
/// Initializes a new instance of the <see cref="FactorFile{T}"/> class.
/// </summary>
protected FactorFile(string permtick, IEnumerable<T> data, DateTime? factorFileMinimumDate = null)
{
Permtick = permtick.LazyToUpper();
SortedFactorFileData = new SortedList<DateTime, List<T>>();
foreach (var row in data)
{
if (!SortedFactorFileData.TryGetValue(row.Date, out var factorFileRows))
{
SortedFactorFileData[row.Date] = factorFileRows = new List<T>();
}
factorFileRows.Add(row);
}
ReversedFactorFileDates = new List<DateTime>(SortedFactorFileData.Count);
foreach (var time in SortedFactorFileData.Keys.Reverse())
{
ReversedFactorFileDates.Add(time);
}
FactorFileMinimumDate = factorFileMinimumDate;
}
/// <summary>
/// Gets the price scale factor for the specified search date
/// </summary>
public abstract decimal GetPriceFactor(
DateTime searchDate,
DataNormalizationMode dataNormalizationMode,
DataMappingMode? dataMappingMode = null,
uint contractOffset = 0
);
/// <summary>
/// Writes this factor file data to an enumerable of csv lines
/// </summary>
/// <returns>An enumerable of lines representing this factor file</returns>
public IEnumerable<string> GetFileFormat()
{
return SortedFactorFileData.SelectMany(kvp => kvp.Value.Select(row => row.GetFileFormat()));
}
/// <summary>
/// Write the factor file to the correct place in the default Data folder
/// </summary>
/// <param name="symbol">The symbol this factor file represents</param>
public void WriteToFile(Symbol symbol)
{
var filePath = LeanData.GenerateRelativeFactorFilePath(symbol);
File.WriteAllLines(filePath, GetFileFormat());
}
/// <summary>Returns an enumerator that iterates through the collection.</summary>
/// <returns>A <see cref="T:System.Collections.Generic.IEnumerator`1" /> that can be used to iterate through the collection.</returns>
/// <filterpriority>1</filterpriority>
public IEnumerator<IFactorRow> GetEnumerator()
{
foreach (var kvp in SortedFactorFileData)
{
foreach (var factorRow in kvp.Value)
{
yield return factorRow;
}
}
}
/// <summary>Returns an enumerator that iterates through a collection.</summary>
/// <returns>An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.</returns>
/// <filterpriority>2</filterpriority>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
@@ -0,0 +1,88 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using static QuantConnect.StringExtensions;
namespace QuantConnect.Data.Auxiliary
{
/// <summary>
/// Provides methods for reading factor file zips
/// </summary>
public static class FactorFileZipHelper
{
/// <summary>
/// Constructs the factor file path for the specified market and security type
/// </summary>
/// <param name="market">The market this symbol belongs to</param>
/// <param name="securityType">The security type</param>
/// <returns>The relative file path</returns>
public static string GetRelativeFactorFilePath(string market, SecurityType securityType)
{
return Invariant($"{securityType.SecurityTypeToLower()}/{market}/factor_files");
}
/// <summary>
/// Gets the factor file zip filename for the specified date
/// </summary>
public static string GetFactorFileZipFileName(string market, DateTime date, SecurityType securityType)
{
return Path.Combine(Globals.DataFolder, GetRelativeFactorFilePath(market, securityType), $"factor_files_{date:yyyyMMdd}.zip");
}
/// <summary>
/// Reads the zip bytes as text and parses as FactorFileRows to create FactorFiles
/// </summary>
public static IEnumerable<KeyValuePair<Symbol, IFactorProvider>> ReadFactorFileZip(Stream file, MapFileResolver mapFileResolver, string market, SecurityType securityType)
{
if (file == null || file.Length == 0)
{
return new Dictionary<Symbol, IFactorProvider>();
}
var keyValuePairs = (
from kvp in Compression.Unzip(file)
let filename = kvp.Key
let lines = kvp.Value
let factorFile = PriceScalingExtensions.SafeRead(Path.GetFileNameWithoutExtension(filename), lines, securityType)
let mapFile = mapFileResolver.GetByPermtick(factorFile.Permtick)
where mapFile != null
select new KeyValuePair<Symbol, IFactorProvider>(GetSymbol(mapFile, market, securityType), factorFile)
);
return keyValuePairs;
}
private static Symbol GetSymbol(MapFile mapFile, string market, SecurityType securityType)
{
SecurityIdentifier sid;
switch (securityType)
{
case SecurityType.Equity:
sid = SecurityIdentifier.GenerateEquity(mapFile.FirstDate, mapFile.FirstTicker, market);
break;
case SecurityType.Future:
sid = SecurityIdentifier.GenerateFuture(SecurityIdentifier.DefaultDate, mapFile.Permtick, market);
break;
default:
throw new ArgumentOutOfRangeException(nameof(securityType), securityType, null);
}
return new Symbol(sid, mapFile.Permtick);
}
}
}
+49
View File
@@ -0,0 +1,49 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
using System.Collections.Generic;
namespace QuantConnect.Data.Auxiliary
{
/// <summary>
/// Providers price scaling factors for a permanent tick
/// </summary>
public interface IFactorProvider : IEnumerable<IFactorRow>
{
/// <summary>
/// Gets the symbol this factor file represents
/// </summary>
public string Permtick { get; }
/// <summary>
/// The minimum tradeable date for the symbol
/// </summary>
/// <remarks>
/// Some factor files have INF split values, indicating that the stock has so many splits
/// that prices can't be calculated with correct numerical precision.
/// To allow backtesting these symbols, we need to move the starting date
/// forward when reading the data.
/// Known symbols: GBSN, JUNI, NEWL
/// </remarks>
public DateTime? FactorFileMinimumDate { get; set; }
/// <summary>
/// Gets the price factor for the specified search date
/// </summary>
decimal GetPriceFactor(DateTime searchDate, DataNormalizationMode dataNormalizationMode, DataMappingMode? dataMappingMode = null, uint contractOffset = 0);
}
}
+36
View File
@@ -0,0 +1,36 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
namespace QuantConnect.Data.Auxiliary
{
/// <summary>
/// Factor row abstraction. <see cref="IFactorProvider"/>
/// </summary>
public interface IFactorRow
{
/// <summary>
/// Gets the date associated with this data
/// </summary>
DateTime Date { get; }
/// <summary>
/// Writes factor file row into it's file format
/// </summary>
string GetFileFormat(string source = null);
}
}
@@ -0,0 +1,96 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System.IO;
using QuantConnect.Util;
using QuantConnect.Interfaces;
using System.Collections.Concurrent;
namespace QuantConnect.Data.Auxiliary
{
/// <summary>
/// Provides an implementation of <see cref="IFactorFileProvider"/> that searches the local disk
/// </summary>
public class LocalDiskFactorFileProvider : IFactorFileProvider
{
private IMapFileProvider _mapFileProvider;
private IDataProvider _dataProvider;
private readonly ConcurrentDictionary<Symbol, IFactorProvider> _cache;
/// <summary>
/// Creates a new instance of the <see cref="LocalDiskFactorFileProvider"/>
/// </summary>
public LocalDiskFactorFileProvider()
{
_cache = new ConcurrentDictionary<Symbol, IFactorProvider>();
}
/// <summary>
/// Initializes our FactorFileProvider by supplying our mapFileProvider
/// and dataProvider
/// </summary>
/// <param name="mapFileProvider">MapFileProvider to use</param>
/// <param name="dataProvider">DataProvider to use</param>
public void Initialize(IMapFileProvider mapFileProvider, IDataProvider dataProvider)
{
_mapFileProvider = mapFileProvider;
_dataProvider = dataProvider;
}
/// <summary>
/// Gets a <see cref="FactorFile{T}"/> instance for the specified symbol, or null if not found
/// </summary>
/// <param name="symbol">The security's symbol whose factor file we seek</param>
/// <returns>The resolved factor file, or null if not found</returns>
public IFactorProvider Get(Symbol symbol)
{
symbol = symbol.GetFactorFileSymbol();
IFactorProvider factorFile;
if (_cache.TryGetValue(symbol, out factorFile))
{
return factorFile;
}
// we first need to resolve the map file to get a permtick, that's how the factor files are stored
var mapFileResolver = _mapFileProvider.Get(AuxiliaryDataKey.Create(symbol));
if (mapFileResolver == null)
{
return GetFactorFile(symbol, symbol.Value);
}
var mapFile = mapFileResolver.ResolveMapFile(symbol);
if (mapFile.IsNullOrEmpty())
{
return GetFactorFile(symbol, symbol.Value);
}
return GetFactorFile(symbol, mapFile.Permtick);
}
/// <summary>
/// Checks that the factor file exists on disk, and if it does, loads it into memory
/// </summary>
private IFactorProvider GetFactorFile(Symbol symbol, string permtick)
{
var basePath = Globals.GetDataFolderPath(FactorFileZipHelper.GetRelativeFactorFilePath(symbol.ID.Market, symbol.SecurityType));
var path = Path.Combine(basePath, permtick.ToLowerInvariant() + ".csv");
var factorFile = PriceScalingExtensions.SafeRead(permtick, _dataProvider.ReadLines(path), symbol.SecurityType);
_cache.AddOrUpdate(symbol, factorFile, (s, c) => factorFile);
return factorFile;
}
}
}
@@ -0,0 +1,83 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System.IO;
using System.Threading;
using QuantConnect.Logging;
using QuantConnect.Interfaces;
using System.Collections.Concurrent;
namespace QuantConnect.Data.Auxiliary
{
/// <summary>
/// Provides a default implementation of <see cref="IMapFileProvider"/> that reads from
/// the local disk
/// </summary>
public class LocalDiskMapFileProvider : IMapFileProvider
{
private static int _wroteTraceStatement;
private readonly ConcurrentDictionary<AuxiliaryDataKey, MapFileResolver> _cache;
private IDataProvider _dataProvider;
/// <summary>
/// Creates a new instance of the <see cref="LocalDiskFactorFileProvider"/>
/// </summary>
public LocalDiskMapFileProvider()
{
_cache = new ConcurrentDictionary<AuxiliaryDataKey, MapFileResolver>();
}
/// <summary>
/// Initializes our MapFileProvider by supplying our dataProvider
/// </summary>
/// <param name="dataProvider">DataProvider to use</param>
public void Initialize(IDataProvider dataProvider)
{
_dataProvider = dataProvider;
}
/// <summary>
/// Gets a <see cref="MapFileResolver"/> representing all the map
/// files for the specified market
/// </summary>
/// <param name="auxiliaryDataKey">Key used to fetch a map file resolver. Specifying market and security type</param>
/// <returns>A <see cref="MapFileRow"/> containing all map files for the specified market</returns>
public MapFileResolver Get(AuxiliaryDataKey auxiliaryDataKey)
{
return _cache.GetOrAdd(auxiliaryDataKey, GetMapFileResolver);
}
private MapFileResolver GetMapFileResolver(AuxiliaryDataKey key)
{
var securityType = key.SecurityType;
var market = key.Market;
var mapFileDirectory = Globals.GetDataFolderPath(MapFile.GetRelativeMapFilePath(market, securityType));
if (!Directory.Exists(mapFileDirectory))
{
// only write this message once per application instance
if (Interlocked.CompareExchange(ref _wroteTraceStatement, 1, 0) == 0)
{
Log.Error($"LocalDiskMapFileProvider.GetMapFileResolver({market}): " +
$"The specified directory does not exist: {mapFileDirectory}"
);
}
return MapFileResolver.Empty;
}
return new MapFileResolver(MapFile.GetMapFiles(mapFileDirectory, market, securityType, _dataProvider));
}
}
}
@@ -0,0 +1,169 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using QuantConnect.Util;
using QuantConnect.Logging;
using System.Threading.Tasks;
using QuantConnect.Interfaces;
using System.Collections.Generic;
namespace QuantConnect.Data.Auxiliary
{
/// <summary>
/// Provides an implementation of <see cref="IFactorFileProvider"/> that searches the local disk for a zip file containing all factor files
/// </summary>
public class LocalZipFactorFileProvider : IFactorFileProvider
{
private readonly object _lock;
private IDataProvider _dataProvider;
private IMapFileProvider _mapFileProvider;
private Dictionary<AuxiliaryDataKey, bool> _seededMarket;
private readonly Dictionary<Symbol, IFactorProvider> _factorFiles;
/// <summary>
/// The cached refresh period for the factor files
/// </summary>
/// <remarks>Exposed for testing</remarks>
protected virtual TimeSpan CacheRefreshPeriod
{
get
{
var dueTime = Time.GetNextLiveAuxiliaryDataDueTime();
if (dueTime > TimeSpan.FromMinutes(10))
{
// Clear the cache before the auxiliary due time to avoid race conditions with consumers
return dueTime - TimeSpan.FromMinutes(10);
}
return dueTime;
}
}
/// <summary>
/// Creates a new instance of the <see cref="LocalZipFactorFileProvider"/> class.
/// </summary>
public LocalZipFactorFileProvider()
{
_factorFiles = new Dictionary<Symbol, IFactorProvider>();
_seededMarket = new Dictionary<AuxiliaryDataKey, bool>();
_lock = new object();
}
/// <summary>
/// Initializes our FactorFileProvider by supplying our mapFileProvider
/// and dataProvider
/// </summary>
/// <param name="mapFileProvider">MapFileProvider to use</param>
/// <param name="dataProvider">DataProvider to use</param>
public void Initialize(IMapFileProvider mapFileProvider, IDataProvider dataProvider)
{
if (_mapFileProvider != null || _dataProvider != null)
{
return;
}
_mapFileProvider = mapFileProvider;
_dataProvider = dataProvider;
StartExpirationTask();
}
/// <summary>
/// Gets a <see cref="FactorFile{T}"/> instance for the specified symbol, or null if not found
/// </summary>
/// <param name="symbol">The security's symbol whose factor file we seek</param>
/// <returns>The resolved factor file, or null if not found</returns>
public IFactorProvider Get(Symbol symbol)
{
symbol = symbol.GetFactorFileSymbol();
var key = AuxiliaryDataKey.Create(symbol);
lock (_lock)
{
if (!_seededMarket.ContainsKey(key))
{
HydrateFactorFileFromLatestZip(key);
_seededMarket[key] = true;
}
IFactorProvider factorFile;
if (!_factorFiles.TryGetValue(symbol, out factorFile))
{
// Could not find factor file for symbol
Log.Error($"LocalZipFactorFileProvider.Get({symbol}): No factor file found.");
_factorFiles[symbol] = factorFile = symbol.GetEmptyFactorFile();
}
return factorFile;
}
}
/// <summary>
/// Helper method that will clear any cached factor files in a daily basis, this is useful for live trading
/// </summary>
protected virtual void StartExpirationTask()
{
lock (_lock)
{
// we clear the seeded markets so they are reloaded
_seededMarket = new Dictionary<AuxiliaryDataKey, bool>();
}
_ = Task.Delay(CacheRefreshPeriod).ContinueWith(_ => StartExpirationTask());
}
/// Hydrate the <see cref="_factorFiles"/> from the latest zipped factor file on disk
private void HydrateFactorFileFromLatestZip(AuxiliaryDataKey key)
{
var market = key.Market;
// start the search with yesterday, today's file will be available tomorrow
var todayNewYork = DateTime.UtcNow.ConvertFromUtc(TimeZones.NewYork).Date;
var date = todayNewYork.AddDays(-1);
var count = 0;
do
{
var factorFilePath = FactorFileZipHelper.GetFactorFileZipFileName(market, date, key.SecurityType);
// Fetch a stream for our zip from our data provider
var stream = _dataProvider.Fetch(factorFilePath);
// If the file was found we can read the file
if (stream != null)
{
var mapFileResolver = _mapFileProvider.Get(key);
foreach (var keyValuePair in FactorFileZipHelper.ReadFactorFileZip(stream, mapFileResolver, market, key.SecurityType))
{
// we merge with existing, this will allow to hold multiple markets
_factorFiles[keyValuePair.Key] = keyValuePair.Value;
}
stream.DisposeSafely();
Log.Trace($"LocalZipFactorFileProvider.Get({market}): Fetched factor files for: {date.ToShortDateString()} NY");
return;
}
// Otherwise we will search back another day
Log.Debug($"LocalZipFactorFileProvider.Get({market}): No factor file found for date {date.ToShortDateString()}");
// prevent infinite recursion if something is wrong
if (count++ > 7)
{
throw new InvalidOperationException($"LocalZipFactorFileProvider.Get(): Could not find any factor files going all the way back to {date} for {market}");
}
date = date.AddDays(-1);
}
while (true);
}
}
}
@@ -0,0 +1,148 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using QuantConnect.Configuration;
using QuantConnect.Util;
using QuantConnect.Logging;
using System.Globalization;
using System.Threading.Tasks;
using QuantConnect.Interfaces;
using System.Collections.Generic;
namespace QuantConnect.Data.Auxiliary
{
/// <summary>
/// Provides an implementation of <see cref="IMapFileProvider"/> that reads from a local zip file
/// </summary>
public class LocalZipMapFileProvider : IMapFileProvider
{
private readonly DateTime? _lookupDate;
private Dictionary<AuxiliaryDataKey, MapFileResolver> _cache;
private IDataProvider _dataProvider;
private object _lock;
/// <summary>
/// The cached refresh period for the map files
/// </summary>
/// <remarks>Exposed for testing</remarks>
protected virtual TimeSpan CacheRefreshPeriod
{
get
{
var dueTime = Time.GetNextLiveAuxiliaryDataDueTime();
if (dueTime > TimeSpan.FromMinutes(10))
{
// Clear the cache before the auxiliary due time to avoid race conditions with consumers
return dueTime - TimeSpan.FromMinutes(10);
}
return dueTime;
}
}
/// <summary>
/// Creates a new instance of the <see cref="LocalZipMapFileProvider"/>
/// </summary>
public LocalZipMapFileProvider()
{
_lock = new object();
_cache = new Dictionary<AuxiliaryDataKey, MapFileResolver>();
var lookupDateConfig = Config.Get("map-file-provider-lookup-date");
if (DateTime.TryParseExact(lookupDateConfig, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var lookupDate))
{
_lookupDate = lookupDate;
}
}
/// <summary>
/// Initializes our MapFileProvider by supplying our dataProvider
/// </summary>
/// <param name="dataProvider">DataProvider to use</param>
public void Initialize(IDataProvider dataProvider)
{
if (_dataProvider != null)
{
return;
}
_dataProvider = dataProvider;
StartExpirationTask();
}
/// <summary>
/// Gets a <see cref="MapFileResolver"/> representing all the map files for the specified market
/// </summary>
/// <param name="auxiliaryDataKey">Key used to fetch a map file resolver. Specifying market and security type</param>
/// <returns>A <see cref="MapFileResolver"/> containing all map files for the specified market</returns>
public MapFileResolver Get(AuxiliaryDataKey auxiliaryDataKey)
{
MapFileResolver result;
// we use a lock so that only 1 thread loads the map file resolver while the rest wait
// else we could have multiple threads loading the map file resolver at the same time!
lock (_lock)
{
if (!_cache.TryGetValue(auxiliaryDataKey, out result))
{
_cache[auxiliaryDataKey] = result = GetMapFileResolver(auxiliaryDataKey);
}
}
return result;
}
/// <summary>
/// Helper method that will clear any cached factor files in a daily basis, this is useful for live trading
/// </summary>
protected virtual void StartExpirationTask()
{
lock (_lock)
{
// we clear the seeded markets so they are reloaded
_cache = new Dictionary<AuxiliaryDataKey, MapFileResolver>();
}
_ = Task.Delay(CacheRefreshPeriod).ContinueWith(_ => StartExpirationTask());
}
private MapFileResolver GetMapFileResolver(AuxiliaryDataKey auxiliaryDataKey)
{
var market = auxiliaryDataKey.Market;
var timestamp = DateTime.UtcNow.ConvertFromUtc(TimeZones.NewYork);
var todayNewYork = timestamp.Date;
// If a lookup date was provided, use it, otherwise use yesterday as the starting point for our search
var yesterdayNewYork = _lookupDate ?? todayNewYork.AddDays(-1);
// To prevent infinite recursion if something is wrong with the zip files, we look back a maximum of 30 days
var endDate = yesterdayNewYork.AddDays(_lookupDate.HasValue ? 0 : -30);
// start the search with yesterday, today's file will be available tomorrow
for (var date = yesterdayNewYork; date >= endDate; date = date.AddDays(-1))
{
var zipFileName = MapFileZipHelper.GetMapFileZipFileName(market, date, auxiliaryDataKey.SecurityType);
// Fetch a stream for our zip from our data provider
var stream = _dataProvider.Fetch(zipFileName);
// If we didn't find a file, continue to the next date
if (stream == null) continue;
Log.Trace($"LocalZipMapFileProvider.Get({market}): Fetched map files for: {date.ToShortDateString()} NY ({(date - todayNewYork).Days} days ago).");
var result = new MapFileResolver(MapFileZipHelper.ReadMapFileZip(stream, market, auxiliaryDataKey.SecurityType));
stream.DisposeSafely();
return result;
}
throw new InvalidOperationException($"LocalZipMapFileProvider couldn't find any map files going all the way back to {endDate.ToShortDateString()} for {market}");
}
}
}
+253
View File
@@ -0,0 +1,253 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using QuantConnect.Interfaces;
using QuantConnect.Logging;
using static QuantConnect.StringExtensions;
namespace QuantConnect.Data.Auxiliary
{
/// <summary>
/// Represents an entire map file for a specified symbol
/// </summary>
public class MapFile : IEnumerable<MapFileRow>
{
private readonly List<MapFileRow> _data;
/// <summary>
/// Gets the entity's unique symbol, i.e OIH.1
/// </summary>
public string Permtick { get; }
/// <summary>
/// Gets the last date in the map file which is indicative of a delisting event
/// </summary>
public DateTime DelistingDate { get; }
/// <summary>
/// Gets the first date in this map file
/// </summary>
public DateTime FirstDate { get; }
/// <summary>
/// Gets the first ticker for the security represented by this map file
/// </summary>
public string FirstTicker { get; }
/// <summary>
/// Initializes a new instance of the <see cref="MapFile"/> class.
/// </summary>
public MapFile(string permtick, IEnumerable<MapFileRow> data)
{
if (string.IsNullOrEmpty(permtick))
{
throw new ArgumentNullException(nameof(permtick), "Provided ticker is null or empty");
}
Permtick = permtick.LazyToUpper();
_data = data.Distinct().OrderBy(row => row.Date).ToList();
// for performance we set first and last date on ctr
if (_data.Count == 0)
{
FirstDate = Time.BeginningOfTime;
DelistingDate = Time.EndOfTime;
}
else
{
FirstDate = _data[0].Date;
DelistingDate = _data[_data.Count - 1].Date;
}
var firstTicker = GetMappedSymbol(FirstDate, Permtick);
if (char.IsDigit(firstTicker.Last()))
{
var dotIndex = firstTicker.LastIndexOf(".", StringComparison.Ordinal);
if (dotIndex > 0)
{
int value;
var number = firstTicker.AsSpan(dotIndex + 1);
if (int.TryParse(number, out value))
{
firstTicker = firstTicker.Substring(0, dotIndex);
}
}
}
FirstTicker = firstTicker;
}
/// <summary>
/// Memory overload search method for finding the mapped symbol for this date.
/// </summary>
/// <param name="searchDate">date for symbol we need to find.</param>
/// <param name="defaultReturnValue">Default return value if search was got no result.</param>
/// <param name="dataMappingMode">The mapping mode to use if any.</param>
/// <returns>Symbol on this date.</returns>
public string GetMappedSymbol(DateTime searchDate, string defaultReturnValue = "", DataMappingMode? dataMappingMode = null)
{
var mappedSymbol = defaultReturnValue;
//Iterate backwards to find the most recent factor:
for (var i = 0; i < _data.Count; i++)
{
var row = _data[i];
if (row.Date < searchDate || row.DataMappingMode.HasValue && row.DataMappingMode != dataMappingMode)
{
continue;
}
mappedSymbol = row.MappedSymbol;
break;
}
return mappedSymbol;
}
/// <summary>
/// Determines if there's data for the requested date
/// </summary>
public bool HasData(DateTime date)
{
// handle the case where we don't have any data
if (_data.Count == 0)
{
return true;
}
if (date < FirstDate || date > DelistingDate)
{
// don't even bother checking the disk if the map files state we don't have the data
return false;
}
return true;
}
/// <summary>
/// Reads and writes each <see cref="MapFileRow"/>
/// </summary>
/// <returns>Enumerable of csv lines</returns>
public IEnumerable<string> ToCsvLines()
{
return _data.Select(mapRow => mapRow.ToCsv());
}
/// <summary>
/// Writes the map file to a CSV file
/// </summary>
/// <param name="market">The market to save the MapFile to</param>
/// <param name="securityType">The map file security type</param>
public void WriteToCsv(string market, SecurityType securityType)
{
var filePath = Path.Combine(Globals.DataFolder, GetRelativeMapFilePath(market, securityType), Permtick.ToLowerInvariant() + ".csv");
var fileDir = Path.GetDirectoryName(filePath);
if (!Directory.Exists(fileDir))
{
Directory.CreateDirectory(fileDir);
Log.Trace($"Created directory for map file: {fileDir}");
}
File.WriteAllLines(filePath, ToCsvLines());
}
/// <summary>
/// Constructs the map file path for the specified market and symbol
/// </summary>
/// <param name="market">The market this symbol belongs to</param>
/// <param name="securityType">The map file security type</param>
/// <returns>The file path to the requested map file</returns>
public static string GetRelativeMapFilePath(string market, SecurityType securityType)
{
return Invariant($"{securityType.SecurityTypeToLower()}/{market}/map_files");
}
#region Implementation of IEnumerable
/// <summary>
/// Returns an enumerator that iterates through the collection.
/// </summary>
/// <returns>
/// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
/// </returns>
/// <filterpriority>1</filterpriority>
public IEnumerator<MapFileRow> GetEnumerator()
{
return _data.GetEnumerator();
}
/// <summary>
/// Returns an enumerator that iterates through a collection.
/// </summary>
/// <returns>
/// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
/// </returns>
/// <filterpriority>2</filterpriority>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
/// <summary>
/// Reads all the map files in the specified directory
/// </summary>
/// <param name="mapFileDirectory">The map file directory path</param>
/// <param name="market">The map file market</param>
/// <param name="securityType">The map file security type</param>
/// <param name="dataProvider">The data provider instance to use</param>
/// <returns>An enumerable of all map files</returns>
public static IEnumerable<MapFile> GetMapFiles(string mapFileDirectory, string market, SecurityType securityType, IDataProvider dataProvider)
{
var mapFiles = new List<MapFile>();
Parallel.ForEach(Directory.EnumerateFiles(mapFileDirectory), file =>
{
if (file.EndsWith(".csv"))
{
var permtick = Path.GetFileNameWithoutExtension(file);
var fileRead = SafeMapFileRowRead(file, market, securityType, dataProvider);
var mapFile = new MapFile(permtick, fileRead);
lock (mapFiles)
{
// just use a list + lock, not concurrent bag, avoid garbage it creates for features we don't need here. See https://github.com/dotnet/runtime/issues/23103
mapFiles.Add(mapFile);
}
}
});
return mapFiles;
}
/// <summary>
/// Reads in the map file at the specified path, returning null if any exceptions are encountered
/// </summary>
private static List<MapFileRow> SafeMapFileRowRead(string file, string market, SecurityType securityType, IDataProvider dataProvider)
{
try
{
return MapFileRow.Read(file, market, securityType, dataProvider).ToList();
}
catch (Exception err)
{
Log.Error(err, $"File: {file}");
return new List<MapFileRow>();
}
}
}
}
@@ -0,0 +1,63 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System.Collections.Concurrent;
using System.Linq;
using QuantConnect.Interfaces;
namespace QuantConnect.Data.Auxiliary
{
/// <summary>
/// Implementation of IPrimaryExchangeProvider from map files.
/// </summary>
public class MapFilePrimaryExchangeProvider : IPrimaryExchangeProvider
{
private readonly IMapFileProvider _mapFileProvider;
private readonly ConcurrentDictionary<SecurityIdentifier, Exchange> _primaryExchangeBySid;
/// <summary>
/// Constructor for Primary Exchange Provider from MapFiles
/// </summary>
/// <param name="mapFileProvider">MapFile to use</param>
public MapFilePrimaryExchangeProvider(IMapFileProvider mapFileProvider)
{
_mapFileProvider = mapFileProvider;
_primaryExchangeBySid = new ConcurrentDictionary<SecurityIdentifier, Exchange>();
}
/// <summary>
/// Gets the primary exchange for a given security identifier
/// </summary>
/// <param name="securityIdentifier">The security identifier to get the primary exchange for</param>
/// <returns>Returns the primary exchange or null if not found</returns>
public Exchange GetPrimaryExchange(SecurityIdentifier securityIdentifier)
{
Exchange primaryExchange;
if (!_primaryExchangeBySid.TryGetValue(securityIdentifier, out primaryExchange))
{
var mapFile = _mapFileProvider.Get(AuxiliaryDataKey.Create(securityIdentifier))
.ResolveMapFile(securityIdentifier.Symbol, securityIdentifier.Date);
if (mapFile != null && mapFile.Any())
{
primaryExchange = mapFile.Last().PrimaryExchange;
}
_primaryExchangeBySid[securityIdentifier] = primaryExchange;
}
return primaryExchange;
}
}
}
+230
View File
@@ -0,0 +1,230 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using QuantConnect.Interfaces;
using QuantConnect.Util;
namespace QuantConnect.Data.Auxiliary
{
/// <summary>
/// Provides a means of mapping a symbol at a point in time to the map file
/// containing that share class's mapping information
/// </summary>
public class MapFileResolver : IEnumerable<MapFile>
{
private readonly Dictionary<string, MapFile> _mapFilesByPermtick;
private readonly Dictionary<string, SortedList<DateTime, MapFileRowEntry>> _bySymbol;
/// <summary>
/// Gets an empty <see cref="MapFileResolver"/>, that is an instance that contains
/// zero mappings
/// </summary>
public static readonly MapFileResolver Empty = new MapFileResolver(Enumerable.Empty<MapFile>());
/// <summary>
/// Initializes a new instance of the <see cref="MapFileResolver"/> by reading
/// in all files in the specified directory.
/// </summary>
/// <param name="mapFiles">The data used to initialize this resolver.</param>
public MapFileResolver(IEnumerable<MapFile> mapFiles)
{
_mapFilesByPermtick = new Dictionary<string, MapFile>(StringComparer.InvariantCultureIgnoreCase);
_bySymbol = new Dictionary<string, SortedList<DateTime, MapFileRowEntry>>(StringComparer.InvariantCultureIgnoreCase);
foreach (var mapFile in mapFiles)
{
// add to our by path map
_mapFilesByPermtick.Add(mapFile.Permtick, mapFile);
foreach (var row in mapFile)
{
SortedList<DateTime, MapFileRowEntry> entries;
var mapFileRowEntry = new MapFileRowEntry(mapFile.Permtick, row);
if (!_bySymbol.TryGetValue(row.MappedSymbol, out entries))
{
entries = new SortedList<DateTime, MapFileRowEntry>();
_bySymbol[row.MappedSymbol] = entries;
}
if (entries.ContainsKey(mapFileRowEntry.MapFileRow.Date))
{
// check to verify it' the same data
if (!entries[mapFileRowEntry.MapFileRow.Date].Equals(mapFileRowEntry))
{
throw new DuplicateNameException("Attempted to assign different history for symbol.");
}
}
else
{
entries.Add(mapFileRowEntry.MapFileRow.Date, mapFileRowEntry);
}
}
}
}
/// <summary>
/// Gets the map file matching the specified permtick
/// </summary>
/// <param name="permtick">The permtick to match on</param>
/// <returns>The map file matching the permtick, or null if not found</returns>
public MapFile GetByPermtick(string permtick)
{
MapFile mapFile;
_mapFilesByPermtick.TryGetValue(permtick.LazyToUpper(), out mapFile);
return mapFile;
}
/// <summary>
/// Resolves the map file path containing the mapping information for the symbol defined at <paramref name="date"/>
/// </summary>
/// <param name="symbol">The symbol as of <paramref name="date"/> to be mapped</param>
/// <param name="date">The date associated with the <paramref name="symbol"/></param>
/// <returns>The map file responsible for mapping the symbol, if no map file is found, null is returned</returns>
public MapFile ResolveMapFile(string symbol, DateTime date)
{
// lookup the symbol's history
SortedList<DateTime, MapFileRowEntry> entries;
if (_bySymbol.TryGetValue(symbol, out entries))
{
if (entries.Count == 0)
{
return new MapFile(symbol, Enumerable.Empty<MapFileRow>());
}
// Return value of BinarySearch (from MSDN):
// The zero-based index of item in the sorted List<T>, if item is found;
// otherwise, a negative number that is the bitwise complement of the index of the next element that is larger than item
// or, if there is no larger element, the bitwise complement of Count.
var indexOf = entries.Keys.BinarySearch(date);
if (indexOf >= 0)
{
symbol = entries.Values[indexOf].EntitySymbol;
}
else
{
if (indexOf == ~entries.Keys.Count)
{
// the searched date is greater than the last date in the list, return the last entry
indexOf = entries.Keys.Count - 1;
}
else
{
// if negative, it's the bitwise complement of where it should be
indexOf = ~indexOf;
}
symbol = entries.Values[indexOf].EntitySymbol;
}
}
// secondary search for exact mapping, find path than ends with symbol.csv
MapFile mapFile;
if (!_mapFilesByPermtick.TryGetValue(symbol, out mapFile)
|| mapFile.FirstDate > date && date != SecurityIdentifier.DefaultDate)
{
return new MapFile(symbol, Enumerable.Empty<MapFileRow>());
}
return mapFile;
}
/// <summary>
/// Combines the map file row with the map file path that produced the row
/// </summary>
class MapFileRowEntry : IEquatable<MapFileRowEntry>
{
/// <summary>
/// Gets the map file row
/// </summary>
public MapFileRow MapFileRow { get; private set; }
/// <summary>
/// Gets the full path to the map file that produced this row
/// </summary>
public string EntitySymbol { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="MapFileRowEntry"/> class
/// </summary>
/// <param name="entitySymbol">The map file that produced this row</param>
/// <param name="mapFileRow">The map file row data</param>
public MapFileRowEntry(string entitySymbol, MapFileRow mapFileRow)
{
MapFileRow = mapFileRow;
EntitySymbol = entitySymbol;
}
/// <summary>
/// Indicates whether the current object is equal to another object of the same type.
/// </summary>
/// <returns>
/// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
/// </returns>
/// <param name="other">An object to compare with this object.</param>
public bool Equals(MapFileRowEntry other)
{
if (other == null) return false;
return other.MapFileRow.Date == MapFileRow.Date
&& other.MapFileRow.MappedSymbol == MapFileRow.MappedSymbol;
}
/// <summary>
/// Returns a string that represents the current object.
/// </summary>
/// <returns>
/// A string that represents the current object.
/// </returns>
/// <filterpriority>2</filterpriority>
public override string ToString()
{
return MapFileRow.Date + ": " + MapFileRow.MappedSymbol + ": " + EntitySymbol;
}
}
#region Implementation of IEnumerable
/// <summary>
/// Returns an enumerator that iterates through the collection.
/// </summary>
/// <returns>
/// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
/// </returns>
/// <filterpriority>1</filterpriority>
public IEnumerator<MapFile> GetEnumerator()
{
return _mapFilesByPermtick.Values.GetEnumerator();
}
/// <summary>
/// Returns an enumerator that iterates through a collection.
/// </summary>
/// <returns>
/// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
/// </returns>
/// <filterpriority>2</filterpriority>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
}
+213
View File
@@ -0,0 +1,213 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
using System.Linq;
using QuantConnect.Interfaces;
using System.Collections.Generic;
namespace QuantConnect.Data.Auxiliary
{
/// <summary>
/// Represents a single row in a map_file. This is a csv file ordered as {date, mapped symbol}
/// </summary>
public class MapFileRow : IEquatable<MapFileRow>
{
/// <summary>
/// Gets the date associated with this data
/// </summary>
public DateTime Date { get; }
/// <summary>
/// Gets the mapped symbol
/// </summary>
public string MappedSymbol { get; }
/// <summary>
/// Gets the mapped symbol
/// </summary>
public Exchange PrimaryExchange { get; }
/// <summary>
/// Gets the securities mapping mode associated to this mapping row
/// </summary>
public DataMappingMode? DataMappingMode { get; }
/// <summary>
/// Initializes a new instance of the <see cref="MapFileRow"/> class.
/// </summary>
public MapFileRow(DateTime date, string mappedSymbol, string primaryExchange,
string market = QuantConnect.Market.USA, SecurityType securityType = SecurityType.Equity, DataMappingMode? dataMappingMode = null)
: this(date, mappedSymbol, primaryExchange.GetPrimaryExchange(securityType, market), dataMappingMode)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="MapFileRow"/> class.
/// </summary>
public MapFileRow(DateTime date, string mappedSymbol, Exchange primaryExchange = null, DataMappingMode? dataMappingMode = null)
{
Date = date;
MappedSymbol = mappedSymbol.LazyToUpper();
PrimaryExchange = primaryExchange ?? Exchange.UNKNOWN;
DataMappingMode = dataMappingMode;
}
/// <summary>
/// Reads in the map_file for the specified equity symbol
/// </summary>
public static IEnumerable<MapFileRow> Read(string file, string market, SecurityType securityType, IDataProvider dataProvider)
{
return dataProvider.ReadLines(file)
.Where(l => !string.IsNullOrWhiteSpace(l))
.Select(s => {
try
{
return Parse(s, market, securityType);
}
catch (ArgumentException)
{
// skip unrecognized mapping modes for backwards compatibility
return null;
}
})
.Where(row => row != null);
}
/// <summary>
/// Parses the specified line into a MapFileRow
/// </summary>
public static MapFileRow Parse(string line, string market, SecurityType securityType)
{
var csv = line.Split(',');
var primaryExchange = Exchange.UNKNOWN;
DataMappingMode? mappingMode = null;
if (csv.Length >= 3)
{
primaryExchange = csv[2].GetPrimaryExchange(securityType, market);
}
if (csv.Length >= 4)
{
mappingMode = csv[3].ParseDataMappingMode();
}
return new MapFileRow(DateTime.ParseExact(csv[0], DateFormat.EightCharacter, null), csv[1], primaryExchange, mappingMode);
}
#region Equality members
/// <summary>
/// Indicates whether the current object is equal to another object of the same type.
/// </summary>
/// <returns>
/// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
/// </returns>
/// <param name="other">An object to compare with this object.</param>
public bool Equals(MapFileRow other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Date.Equals(other.Date) &&
string.Equals(MappedSymbol, other.MappedSymbol) &&
string.Equals(PrimaryExchange, other.PrimaryExchange) &&
DataMappingMode == other.DataMappingMode;
}
/// <summary>
/// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
/// </summary>
/// <returns>
/// true if the specified object is equal to the current object; otherwise, false.
/// </returns>
/// <param name="obj">The object to compare with the current object. </param><filterpriority>2</filterpriority>
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((MapFileRow)obj);
}
/// <summary>
/// Serves as a hash function for a particular type.
/// </summary>
/// <returns>
/// A hash code for the current <see cref="T:System.Object"/>.
/// </returns>
/// <filterpriority>2</filterpriority>
public override int GetHashCode()
{
unchecked
{
return (Date.GetHashCode() * 397) ^
(MappedSymbol != null ? MappedSymbol.GetHashCode() : 0) ^
(DataMappingMode != null ? DataMappingMode.GetHashCode() : 0) ^
(PrimaryExchange.GetHashCode());
}
}
/// <summary>
/// Determines whether or not the two instances are equal
/// </summary>
public static bool operator ==(MapFileRow left, MapFileRow right)
{
return Equals(left, right);
}
/// <summary>
/// Determines whether or not the two instances are not equal
/// </summary>
public static bool operator !=(MapFileRow left, MapFileRow right)
{
return !Equals(left, right);
}
#endregion
/// <summary>
/// Writes this row to csv format
/// </summary>
public string ToCsv()
{
var encodedExchange = string.Empty;
if (PrimaryExchange == Exchange.UNKNOWN)
{
if (DataMappingMode != null)
{
// be lazy, only add a comma if we have a mapping mode after
encodedExchange = ",";
}
}
else
{
encodedExchange = $",{PrimaryExchange.Code}";
}
var mappingMode = DataMappingMode != null ? $",{(int)DataMappingMode}" : string.Empty;
return $"{Date.ToStringInvariant(DateFormat.EightCharacter)},{MappedSymbol.ToLowerInvariant()}{encodedExchange}{mappingMode}";
}
/// <summary>
/// Convert this row into string form
/// </summary>
/// <returns>resulting string</returns>
public override string ToString()
{
var mainExchange = PrimaryExchange == Exchange.UNKNOWN ? string.Empty : $" - {PrimaryExchange}";
var mappingMode = DataMappingMode != null ? $" - {DataMappingMode}" : string.Empty;
return Date.ToShortDateString() + ": " + MappedSymbol + mainExchange + mappingMode;
}
}
}
+71
View File
@@ -0,0 +1,71 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
namespace QuantConnect.Data.Auxiliary
{
/// <summary>
/// Helper class for handling mapfile zip files
/// </summary>
public static class MapFileZipHelper
{
/// <summary>
/// Gets the mapfile zip filename for the specified date
/// </summary>
public static string GetMapFileZipFileName(string market, DateTime date, SecurityType securityType)
{
return Path.Combine(Globals.DataFolder, MapFile.GetRelativeMapFilePath(market, securityType), $"map_files_{date:yyyyMMdd}.zip");
}
/// <summary>
/// Reads the zip bytes as text and parses as MapFileRows to create MapFiles
/// </summary>
public static IEnumerable<MapFile> ReadMapFileZip(Stream file, string market, SecurityType securityType)
{
if (file == null || file.Length == 0)
{
return Enumerable.Empty<MapFile>();
}
var result = from kvp in Compression.Unzip(file)
let filename = kvp.Key
where filename.EndsWith(".csv", StringComparison.InvariantCultureIgnoreCase)
let lines = kvp.Value.Where(line => !string.IsNullOrEmpty(line))
let mapFile = SafeRead(filename, lines, market, securityType)
select mapFile;
return result;
}
/// <summary>
/// Parses the contents as a MapFile, if error returns a new empty map file
/// </summary>
private static MapFile SafeRead(string filename, IEnumerable<string> contents, string market, SecurityType securityType)
{
var permtick = Path.GetFileNameWithoutExtension(filename);
try
{
return new MapFile(permtick, contents.Select(s => MapFileRow.Parse(s, market, securityType)));
}
catch
{
return new MapFile(permtick, Enumerable.Empty<MapFileRow>());
}
}
}
}
@@ -0,0 +1,99 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
using System.Linq;
using System.Collections.Generic;
namespace QuantConnect.Data.Auxiliary
{
/// <summary>
/// Mapping related factor provider. Factors based on price differences on mapping dates
/// </summary>
public class MappingContractFactorProvider : FactorFile<MappingContractFactorRow>
{
/// <summary>
///Creates a new instance
/// </summary>
public MappingContractFactorProvider(string permtick, IEnumerable<MappingContractFactorRow> data, DateTime? factorFileMinimumDate = null)
: base(permtick, data, factorFileMinimumDate)
{
}
/// <summary>
/// Gets the price scale factor for the specified search date
/// </summary>
public override decimal GetPriceFactor(DateTime searchDate, DataNormalizationMode dataNormalizationMode, DataMappingMode? dataMappingMode = null, uint contractOffset = 0)
{
if (dataNormalizationMode == DataNormalizationMode.Raw)
{
return 0;
}
var factor = 1m;
if (dataNormalizationMode is DataNormalizationMode.BackwardsPanamaCanal or DataNormalizationMode.ForwardPanamaCanal)
{
// default value depends on the data mode
factor = 0;
}
for (var i = 0; i < ReversedFactorFileDates.Count; i++)
{
var factorDate = ReversedFactorFileDates[i];
if (factorDate.Date < searchDate.Date)
{
break;
}
var factorFileRow = SortedFactorFileData[factorDate];
switch (dataNormalizationMode)
{
case DataNormalizationMode.BackwardsRatio:
{
var row = factorFileRow.FirstOrDefault(row => row.DataMappingMode == dataMappingMode);
if (row != null && row.BackwardsRatioScale.Count > contractOffset)
{
factor = row.BackwardsRatioScale[(int)contractOffset];
}
break;
}
case DataNormalizationMode.BackwardsPanamaCanal:
{
var row = factorFileRow.FirstOrDefault(row => row.DataMappingMode == dataMappingMode);
if (row != null && row.BackwardsPanamaCanalScale.Count > contractOffset)
{
factor = row.BackwardsPanamaCanalScale[(int)contractOffset];
}
break;
}
case DataNormalizationMode.ForwardPanamaCanal:
{
var row = factorFileRow.FirstOrDefault(row => row.DataMappingMode == dataMappingMode);
if (row != null && row.ForwardPanamaCanalScale.Count > contractOffset)
{
factor = row.ForwardPanamaCanalScale[(int)contractOffset];
}
break;
}
default:
throw new ArgumentOutOfRangeException(nameof(dataNormalizationMode));
}
}
return factor;
}
}
}
@@ -0,0 +1,103 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
using System.Linq;
using Newtonsoft.Json;
using System.Collections.Generic;
namespace QuantConnect.Data.Auxiliary
{
/// <summary>
/// Collection of factors for continuous contracts and their back months contracts for a specific mapping mode <see cref="DataMappingMode"/> and date
/// </summary>
public class MappingContractFactorRow : IFactorRow
{
/// <summary>
/// Gets the date associated with this data
/// </summary>
public DateTime Date { get; set; }
/// <summary>
/// Backwards ratio price scaling factors for the front month [index 0] and it's 'i' back months [index 0 + i]
/// <see cref="DataNormalizationMode.BackwardsRatio"/>
/// </summary>
public IReadOnlyList<decimal> BackwardsRatioScale { get; set; } = new List<decimal>();
/// <summary>
/// Backwards Panama Canal price scaling factors for the front month [index 0] and it's 'i' back months [index 0 + i]
/// <see cref="DataNormalizationMode.BackwardsPanamaCanal"/>
/// </summary>
public IReadOnlyList<decimal> BackwardsPanamaCanalScale { get; set; } = new List<decimal>();
/// <summary>
/// Forward Panama Canal price scaling factors for the front month [index 0] and it's 'i' back months [index 0 + i]
/// <see cref="DataNormalizationMode.ForwardPanamaCanal"/>
/// </summary>
public IReadOnlyList<decimal> ForwardPanamaCanalScale { get; set; } = new List<decimal>();
/// <summary>
/// Allows the consumer to specify a desired mapping mode
/// </summary>
public DataMappingMode? DataMappingMode { get; set; }
/// <summary>
/// Empty constructor for json converter
/// </summary>
public MappingContractFactorRow()
{
}
/// <summary>
/// Writes factor file row into it's file format
/// </summary>
/// <remarks>Json formatted</remarks>
public string GetFileFormat(string source = null)
{
return JsonConvert.SerializeObject(this);
}
/// <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<MappingContractFactorRow> Parse(IEnumerable<string> lines, out DateTime? factorFileMinimumDate)
{
factorFileMinimumDate = null;
var rows = new List<MappingContractFactorRow>();
// parse factor file lines
foreach (var line in lines)
{
var row = JsonConvert.DeserializeObject<MappingContractFactorRow>(line);
if(!row.DataMappingMode.HasValue || Enum.IsDefined(typeof(DataMappingMode), row.DataMappingMode.Value))
{
rows.Add(row);
}
}
if (rows.Count > 0)
{
factorFileMinimumDate = rows.Min(ffr => ffr.Date).AddDays(-1);
}
return rows;
}
}
}
+201
View File
@@ -0,0 +1,201 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
using System.Linq;
using QuantConnect.Interfaces;
using System.Collections.Generic;
namespace QuantConnect.Data.Auxiliary
{
/// <summary>
/// Mapping extensions helper methods
/// </summary>
public static class MappingExtensions
{
/// <summary>
/// Helper method to resolve the mapping file to use.
/// </summary>
/// <remarks>This method is aware of the data type being added for <see cref="SecurityType.Base"/>
/// to the <see cref="SecurityIdentifier.Symbol"/> value</remarks>
/// <param name="mapFileProvider">The map file provider</param>
/// <param name="dataConfig">The configuration to fetch the map file for</param>
/// <returns>The mapping file to use</returns>
public static MapFile ResolveMapFile(this IMapFileProvider mapFileProvider, SubscriptionDataConfig dataConfig)
{
var resolver = MapFileResolver.Empty;
if (dataConfig.TickerShouldBeMapped())
{
resolver = mapFileProvider.Get(AuxiliaryDataKey.Create(dataConfig.Symbol));
}
return resolver.ResolveMapFile(dataConfig.Symbol, dataConfig.Type.Name);
}
/// <summary>
/// Helper method to resolve the mapping file to use.
/// </summary>
/// <remarks>This method is aware of the data type being added for <see cref="SecurityType.Base"/>
/// to the <see cref="SecurityIdentifier.Symbol"/> value</remarks>
/// <param name="mapFileResolver">The map file resolver</param>
/// <param name="symbol">The symbol that we want to map</param>
/// <param name="dataType">The string data type name if any</param>
/// <returns>The mapping file to use</returns>
public static MapFile ResolveMapFile(this MapFileResolver mapFileResolver,
Symbol symbol,
string dataType = null)
{
// Load the symbol and date to complete the mapFile checks in one statement
var symbolID = symbol.HasUnderlying ? symbol.Underlying.ID.Symbol : symbol.ID.Symbol;
if (dataType == null && symbol.SecurityType == SecurityType.Base)
{
SecurityIdentifier.TryGetCustomDataType(symbol.ID.Symbol, out dataType);
}
symbolID = symbol.SecurityType == SecurityType.Base && dataType != null ? symbolID.RemoveFromEnd($".{dataType}") : symbolID;
MapFile result;
if (ReferenceEquals(mapFileResolver, MapFileResolver.Empty))
{
result = mapFileResolver.ResolveMapFile(symbol.Value, Time.BeginningOfTime);
}
else
{
var date = symbol.HasUnderlying ? symbol.Underlying.ID.Date : symbol.ID.Date;
result = mapFileResolver.ResolveMapFile(symbolID, date);
}
return result;
}
/// <summary>
/// Some historical provider supports ancient data. In fact, the ticker could be restructured to new one.
/// </summary>
/// <param name="mapFileProvider">Provides instances of <see cref="MapFileResolver"/> at run time</param>
/// <param name="symbol">Represents a unique security identifier</param>
/// <param name="startDateTime">The date since we began our search for the historical name of the symbol.</param>
/// <param name="endDateTime">The end date and time of the historical data range.</param>
/// <returns>
/// An enumerable collection of tuples containing symbol ticker, start date and time, and end date and time
/// representing the historical definitions of the symbol within the specified time range.
/// </returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="mapFileProvider"/> is null.</exception>
/// <example>
/// For instances, get "GOOGL" since 2013 to 2018:
/// It returns: { ("GOOG", 2013, 2014), ("GOOGL", 2014, 2018) }
/// </example>
/// <remarks>
/// GOOGLE: IPO: August 19, 2004 Name = GOOG then it was restructured: from "GOOG" to "GOOGL" on April 2, 2014
/// </remarks>
public static IEnumerable<TickerDateRange> RetrieveSymbolHistoricalDefinitionsInDateRange
(this IMapFileProvider mapFileProvider, Symbol symbol, DateTime startDateTime, DateTime endDateTime)
{
if (mapFileProvider == null)
{
throw new ArgumentNullException(nameof(mapFileProvider));
}
var mapFileResolver = mapFileProvider.Get(AuxiliaryDataKey.Create(symbol));
var symbolMapFile = mapFileResolver.ResolveMapFile(symbol);
if (!symbolMapFile.Any())
{
yield break;
}
var newStartDateTime = startDateTime;
foreach (var mappedTicker in symbolMapFile.Skip(1)) // Skip: IPO Ticker's DateTime
{
if (mappedTicker.Date >= newStartDateTime)
{
// Shifts endDateTime by one day to include all data up to and including the endDateTime.
var newEndDateTime = mappedTicker.Date.AddDays(1);
if (newEndDateTime > endDateTime)
{
yield return new(mappedTicker.MappedSymbol, newStartDateTime, endDateTime);
// the request EndDateTime was achieved
yield break;
}
yield return new(mappedTicker.MappedSymbol, newStartDateTime, newEndDateTime);
// the end of the current request is the start of the next
newStartDateTime = newEndDateTime;
}
}
}
/// <summary>
/// Retrieves all Symbol from map files based on specific Symbol.
/// </summary>
/// <param name="mapFileProvider">The provider for map files containing ticker data.</param>
/// <param name="symbol">The symbol to get <see cref="MapFileResolver"/> and generate new Symbol.</param>
/// <returns>An enumerable collection of <see cref="SymbolDateRange"/></returns>
/// <exception cref="ArgumentException">Throw if <paramref name="mapFileProvider"/> is null.</exception>
public static IEnumerable<SymbolDateRange> RetrieveAllMappedSymbolInDateRange(this IMapFileProvider mapFileProvider, Symbol symbol)
{
if (mapFileProvider == null || symbol == null)
{
throw new ArgumentException($"The map file provider and symbol cannot be null. {(mapFileProvider == null ? nameof(mapFileProvider) : nameof(symbol))}");
}
var mapFileResolver = mapFileProvider.Get(AuxiliaryDataKey.Create(symbol));
var tickerUpperCase = symbol.HasUnderlying ? symbol.Underlying.Value.ToUpperInvariant() : symbol.Value.ToUpperInvariant();
var isOptionSymbol = symbol.SecurityType == SecurityType.Option;
foreach (var mapFile in mapFileResolver)
{
// Check if 'mapFile' contains the desired ticker symbol.
if (!mapFile.Any(mapFileRow => mapFileRow.MappedSymbol == tickerUpperCase))
{
continue;
}
foreach (var tickerDateRange in mapFile.GetTickerDateRanges(tickerUpperCase))
{
var sid = SecurityIdentifier.GenerateEquity(mapFile.FirstDate, mapFile.FirstTicker, symbol?.ID.Market);
var newSymbol = new Symbol(sid, tickerUpperCase);
if (isOptionSymbol)
{
newSymbol = Symbol.CreateCanonicalOption(newSymbol);
}
yield return new(newSymbol, tickerDateRange.StartDate, tickerDateRange.EndDate);
}
}
}
/// <summary>
/// Retrieves the date ranges associated with a specific ticker symbol from the provided map file.
/// </summary>
/// <param name="mapFile">The map file containing the data ranges for various ticker.</param>
/// <param name="ticker">The ticker for which to retrieve the date ranges.</param>
/// <returns>An enumerable collection of tuples representing the start and end dates for each date range associated with the specified ticker symbol.</returns>
private static IEnumerable<(DateTime StartDate, DateTime EndDate)> GetTickerDateRanges(this MapFile mapFile, string ticker)
{
var previousRowDate = mapFile.FirstOrDefault().Date;
foreach (var currentRow in mapFile.Skip(1))
{
if (ticker == currentRow.MappedSymbol)
{
yield return (previousRowDate, currentRow.Date.AddDays(1));
}
// MapFile maintains the latest date associated with each ticker name, except first Row
previousRowDate = currentRow.Date.AddDays(1);
}
}
}
}
@@ -0,0 +1,129 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
using System.Linq;
using System.Collections.Generic;
namespace QuantConnect.Data.Auxiliary
{
/// <summary>
/// Set of helper methods for factor files and price scaling operations
/// </summary>
public static class PriceScalingExtensions
{
/// <summary>
/// Resolves the price scale for a date given a factor file and required settings
/// </summary>
/// <param name="factorFile">The factor file to use</param>
/// <param name="dateTime">The date for the price scale lookup</param>
/// <param name="normalizationMode">The price normalization mode requested</param>
/// <param name="contractOffset">The contract offset, useful for continuous contracts</param>
/// <param name="dataMappingMode">The data mapping mode used, useful for continuous contracts</param>
/// <param name="endDateTime">The reference end date for scaling prices.</param>
/// <returns>The price scale to use</returns>
/// <exception cref="ArgumentException">
/// If <paramref name="normalizationMode"/> is <see cref="DataNormalizationMode.ScaledRaw"/> and <paramref name="endDateTime"/> is null
/// </exception>
/// <remarks>
/// For <see cref="DataNormalizationMode.ScaledRaw"/> normalization mode,
/// the prices are scaled to the prices on the <paramref name="endDateTime"/>
/// </remarks>
public static decimal GetPriceScale(
this IFactorProvider factorFile,
DateTime dateTime,
DataNormalizationMode normalizationMode,
uint contractOffset = 0,
DataMappingMode? dataMappingMode = null,
DateTime? endDateTime = null
)
{
if (factorFile == null)
{
if (normalizationMode is DataNormalizationMode.BackwardsPanamaCanal or DataNormalizationMode.ForwardPanamaCanal)
{
return 0;
}
return 1;
}
var endDateTimeFactor = 1m;
if (normalizationMode == DataNormalizationMode.ScaledRaw)
{
if (endDateTime == null)
{
throw new ArgumentException(
$"{nameof(DataNormalizationMode.ScaledRaw)} normalization mode requires an end date for price scaling.");
}
// For ScaledRaw, we need to get the price scale at the end date to adjust prices to that date instead of "today"
endDateTimeFactor = factorFile.GetPriceFactor(endDateTime.Value, normalizationMode, dataMappingMode, contractOffset);
}
return factorFile.GetPriceFactor(dateTime, normalizationMode, dataMappingMode, contractOffset) / endDateTimeFactor;
}
/// <summary>
/// Determines the symbol to use to fetch it's factor file
/// </summary>
/// <remarks>This is useful for futures where the symbol to use is the canonical</remarks>
public static Symbol GetFactorFileSymbol(this Symbol symbol)
{
return symbol.SecurityType == SecurityType.Future ? symbol.Canonical : symbol;
}
/// <summary>
/// Helper method to return an empty factor file
/// </summary>
public static IFactorProvider GetEmptyFactorFile(this Symbol symbol)
{
if (symbol.SecurityType == SecurityType.Future)
{
return new MappingContractFactorProvider(symbol.ID.Symbol, Enumerable.Empty<MappingContractFactorRow>());
}
return new CorporateFactorProvider(symbol.ID.Symbol, Enumerable.Empty<CorporateFactorRow>());
}
/// <summary>
/// Parses the contents as a FactorFile, if error returns a new empty factor file
/// </summary>
public static IFactorProvider SafeRead(string permtick, IEnumerable<string> contents, SecurityType securityType)
{
try
{
DateTime? minimumDate;
contents = contents.Distinct();
if (securityType == SecurityType.Future)
{
return new MappingContractFactorProvider(permtick, MappingContractFactorRow.Parse(contents, out minimumDate), 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 CorporateFactorProvider(permtick, CorporateFactorRow.Parse(contents, out minimumDate), minimumDate);
}
catch (Exception e)
{
if (securityType == SecurityType.Future)
{
return new MappingContractFactorProvider(permtick, Enumerable.Empty<MappingContractFactorRow>());
}
return new CorporateFactorProvider(permtick, Enumerable.Empty<CorporateFactorRow>());
}
}
}
}
@@ -0,0 +1,202 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.ComponentModel;
namespace QuantConnect.Data.Auxiliary
{
/// <summary>
/// Flag system for quote conditions
/// </summary>
[Flags]
public enum QuoteConditionFlags : long
{
/// <summary>
/// No Condition
/// </summary>
None = 0,
/// <summary>
/// This condition is used for the majority of quotes to indicate a normal trading environment.
/// </summary>
[Description("This condition is used for the majority of quotes to indicate a normal trading environment.")]
Regular = 1L << 0,
/// <summary>
/// This condition is used to indicate that the quote is a Slow Quote on both the Bid and Offer
/// sides due to a Set Slow List that includes High Price securities.
/// </summary>
[Description("This condition is used to indicate that the quote is a Slow Quote on both the Bid and Offer " +
"sides due to a Set Slow List that includes High Price securities.")]
Slow = 1L << 1,
/// <summary>
/// While in this mode, auto-execution is not eligible, the quote is then considered manual and non-firm in the Bid and Offer and
/// either or both sides can be traded through as per Regulation NMS.
/// </summary>
[Description("While in this mode, auto-execution is not eligible, the quote is then considered manual and non-firm in the Bid and Offer and " +
"either or both sides can be traded through as per Regulation NMS.")]
Gap = 1L << 2,
/// <summary>
/// This condition can be disseminated to indicate that this quote was the last quote for a security for that Participant.
/// </summary>
[Description("This condition can be disseminated to indicate that this quote was the last quote for a security for that Participant.")]
Closing = 1L << 3,
/// <summary>
/// This regulatory Opening Delay or Trading Halt is used when relevant news influencing the security is being disseminated.
/// Trading is suspended until the primary market determines that an adequate publication or disclosure of information has occurred.
/// </summary>
[Description("This regulatory Opening Delay or Trading Halt is used when relevant news influencing the security is being disseminated." +
"Trading is suspended until the primary market determines that an adequate publication or disclosure of information has occurred.")]
NewsDissemination = 1L << 4,
/// <summary>
/// This condition is used to indicate a regulatory Opening Delay or Trading Halt due to an expected news announcement,
/// which may influence the security. An Opening Delay or Trading Halt may be continued once the news has been disseminated.
/// </summary>
[Description("This condition is used to indicate a regulatory Opening Delay or Trading Halt due to an expected news announcement, " +
"which may influence the security. An Opening Delay or Trading Halt may be continued once the news has been disseminated.")]
NewsPending = 1L << 5,
/// <summary>
/// The condition is used to denote the probable trading range (bid and offer prices, no sizes) of a security that is not Opening Delayed or
/// Trading Halted. The Trading Range Indication is used prior to or after the opening of a security.
/// </summary>
[Description("The condition is used to denote the probable trading range (bid and offer prices, no sizes) of a security that is not Opening Delayed or" +
"Trading Halted. The Trading Range Indication is used prior to or after the opening of a security.")]
TradingRangeIndication = 1L << 6,
/// <summary>
/// This non-regulatory Opening Delay or Trading Halt is used when there is a significant imbalance of buy or sell orders.
/// </summary>
[Description("This non-regulatory Opening Delay or Trading Halt is used when there is a significant imbalance of buy or sell orders.")]
OrderImbalance = 1L << 7,
/// <summary>
/// This condition is disseminated by each individual FINRA Market Maker to signify either the last quote of the day or
/// the premature close of an individual Market Maker for the day.
/// </summary>
[Description("This condition is disseminated by each individual FINRA Market Maker to signify either the last quote of the day or" +
"the premature close of an individual Market Maker for the day.")]
ClosedMarketMaker = 1L << 8,
/// <summary>
/// This quote condition indicates a regulatory Opening Delay or Trading Halt due to conditions in which
/// a security experiences a 10 % or more change in price over a five minute period.
/// </summary>
[Description("This quote condition indicates a regulatory Opening Delay or Trading Halt due to conditions in which " +
"a security experiences a 10 % or more change in price over a five minute period.")]
VolatilityTradingPause = 1L << 9,
/// <summary>
/// This quote condition suspends a Participant's firm quote obligation for a quote for a security.
/// </summary>
[Description("This quote condition suspends a Participant's firm quote obligation for a quote for a security.")]
NonFirmQuote = 1L << 10,
/// <summary>
/// This condition can be disseminated to indicate that this quote was the opening quote for a security for that Participant.
/// </summary>
[Description("This condition can be disseminated to indicate that this quote was the opening quote for a security for that Participant.")]
OpeningQuote = 1L << 11,
/// <summary>
/// This non-regulatory Opening Delay or Trading Halt is used when events relating to one security will affect the price and performance of
/// another related security. This non-regulatory Opening Delay or Trading Halt is also used when non-regulatory halt reasons such as
/// Order Imbalance, Order Influx and Equipment Changeover are combined with Due to Related Security on CTS.
/// </summary>
[Description("This non-regulatory Opening Delay or Trading Halt is used when events relating to one security will affect the price and performance of " +
"another related security. This non-regulatory Opening Delay or Trading Halt is also used when non-regulatory halt reasons such as " +
"Order Imbalance, Order Influx and Equipment Changeover are combined with Due to Related Security on CTS.")]
DueToRelatedSecurity = 1L << 12,
/// <summary>
/// This quote condition along with zero-filled bid, offer and size fields is used to indicate that trading for a Participant is no longer
/// suspended in a security which had been Opening Delayed or Trading Halted.
/// </summary>
[Description("This quote condition along with zero-filled bid, offer and size fields is used to indicate that trading for a Participant is no longer " +
"suspended in a security which had been Opening Delayed or Trading Halted.")]
Resume = 1L << 13,
/// <summary>
/// This quote condition is used when matters affecting the common stock of a company affect the performance of the non-common
/// associated securities, e.g., warrants, rights, preferred, classes, etc.
/// </summary>
[Description("This quote condition is used when matters affecting the common stock of a company affect the performance of the non-common " +
"associated securities, e.g., warrants, rights, preferred, classes, etc.")]
InViewOfCommon = 1L << 14,
/// <summary>
/// This non-regulatory Opening Delay or Trading Halt is used when the ability to trade a security by a Participant is temporarily
/// inhibited due to a systems, equipment or communications facility problem or for other technical reasons.
/// </summary>
[Description("This non-regulatory Opening Delay or Trading Halt is used when the ability to trade a security by a Participant is temporarily " +
"inhibited due to a systems, equipment or communications facility problem or for other technical reasons.")]
EquipmentChangeover = 1L << 15,
/// <summary>
/// This non-regulatory Opening Delay or Trading Halt is used to indicate an Opening Delay or Trading Halt for a security whose price
/// may fall below $1.05, possibly leading to a sub-penny execution.
/// </summary>
[Description("This non-regulatory Opening Delay or Trading Halt is used to indicate an Opening Delay or Trading Halt for a security whose price" +
" may fall below $1.05, possibly leading to a sub-penny execution.")]
SubPennyTrading = 1L << 16,
/// <summary>
/// This quote condition is used to indicate that an Opening Delay or a Trading Halt is to be in effect for the rest
/// of the trading day in a security for a Participant.
/// </summary>
[Description("This quote condition is used to indicate that an Opening Delay or a Trading Halt is to be in effect for the rest " +
"of the trading day in a security for a Participant.")]
NoOpenNoResume = 1L << 17,
/// <summary>
/// This quote condition is used to indicate that a Limit Up-Limit Down Price Band is applicable for a security.
/// </summary>
[Description("This quote condition is used to indicate that a Limit Up-Limit Down Price Band is applicable for a security.")]
LimitUpLimitDownPriceBand = 1L << 18,
/// <summary>
/// This quote condition is used to indicate that a Limit Up-Limit Down Price Band that is being disseminated " +
/// is a republication of the latest Price Band for a security.
/// </summary>
[Description("This quote condition is used to indicate that a Limit Up-Limit Down Price Band that is being disseminated " +
"is a republication of the latest Price Band for a security.")]
RepublishedLimitUpLimitDownPriceBand = 1L << 19,
/// <summary>
/// This indicates that the market participant is in a manual mode on both the Bid and Ask. While in this mode,
/// automated execution is not eligible on the Bid and Ask side and can be traded through pursuant to Regulation NMS requirements.
/// </summary>
[Description("This indicates that the market participant is in a manual mode on both the Bid and Ask. While in this mode, " +
"automated execution is not eligible on the Bid and Ask side and can be traded through pursuant to Regulation NMS requirements.")]
Manual = 1L << 20,
/// <summary>
/// For extremely active periods of short duration. While in this mode, the UTP participant will enter quotations on a “best efforts” basis.
/// </summary>
[Description("For extremely active periods of short duration. While in this mode, the UTP participant will enter quotations on a “best efforts” basis.")]
FastTrading = 1L << 21,
/// <summary>
/// A halt condition used when there is a sudden order influx. To prevent a disorderly market, trading is temporarily suspended by the UTP participant.
/// </summary>
[Description("A halt condition used when there is a sudden order influx. To prevent a disorderly market, trading is temporarily suspended by the UTP participant.")]
OrderInflux = 1L << 22
}
}
+55
View File
@@ -0,0 +1,55 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace QuantConnect.Data.Auxiliary
{
/// <summary>
/// Represents security identifier within a date range.
/// </summary>
#pragma warning disable CA1815 // Override equals and operator equals on value types
public readonly struct SymbolDateRange
{
/// <summary>
/// Represents a unique security identifier.
/// </summary>
public Symbol Symbol { get; }
/// <summary>
/// Ticker Start Date Time in Local
/// </summary>
public DateTime StartDateTimeLocal { get; }
/// <summary>
/// Ticker End Date Time in Local
/// </summary>
public DateTime EndDateTimeLocal { get; }
/// <summary>
/// Create the instance of <see cref="SymbolDateRange"/> struct.
/// </summary>
/// <param name="symbol">The unique security identifier</param>
/// <param name="startDateTimeLocal">Start Date Time Local</param>
/// <param name="endDateTimeLocal">End Date Time Local</param>
public SymbolDateRange(Symbol symbol, DateTime startDateTimeLocal, DateTime endDateTimeLocal)
{
Symbol = symbol;
StartDateTimeLocal = startDateTimeLocal;
EndDateTimeLocal = endDateTimeLocal;
}
}
#pragma warning restore CA1815
}
+56
View File
@@ -0,0 +1,56 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace QuantConnect.Data.Auxiliary
{
/// <summary>
/// Represents stock data for a specific ticker within a date range.
/// </summary>
#pragma warning disable CA1815 // Override equals and operator equals on value types
public readonly struct TickerDateRange
{
/// <summary>
/// Ticker simple name of stock
/// </summary>
public string Ticker { get; }
/// <summary>
/// Ticker Start Date Time in Local
/// </summary>
public DateTime StartDateTimeLocal { get; }
/// <summary>
/// Ticker End Date Time in Local
/// </summary>
public DateTime EndDateTimeLocal { get; }
/// <summary>
/// Create the instance of <see cref="TickerDateRange"/> struct.
/// </summary>
/// <param name="ticker">Name of ticker</param>
/// <param name="startDateTimeLocal">Start Date Time Local</param>
/// <param name="endDateTimeLocal">End Date Time Local</param>
public TickerDateRange(string ticker, DateTime startDateTimeLocal, DateTime endDateTimeLocal)
{
Ticker = ticker;
StartDateTimeLocal = startDateTimeLocal;
EndDateTimeLocal = endDateTimeLocal;
}
}
#pragma warning restore CA1815
}
@@ -0,0 +1,256 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.ComponentModel;
namespace QuantConnect.Data.Auxiliary
{
/// <summary>
/// Flag system for trade conditions
/// </summary>
[Flags]
public enum TradeConditionFlags: long
{
/// <summary>
/// No Condition
/// </summary>
None = 0,
/// <summary>
/// A trade made without stated conditions is deemed regular way for settlement on the third business day following the transaction date.
/// </summary>
[Description("A trade made without stated conditions is deemed regular way for settlement on the third business day following the transaction date.")]
Regular = 1L << 0,
/// <summary>
/// A transaction which requires delivery of securities and payment on the same day the trade takes place.
/// </summary>
[Description("A transaction which requires delivery of securities and payment on the same day the trade takes place.")]
Cash = 1L << 1,
/// <summary>
/// A transaction that requires the delivery of securities on the first business day following the trade date.
/// </summary>
[Description("A transaction that requires the delivery of securities on the first business day following the trade date.")]
NextDay = 1L << 2,
/// <summary>
/// A Sellers Option transaction gives the seller the right to deliver the security at any time within a specific period,
/// ranging from not less than two calendar days, to not more than sixty calendar days.
/// </summary>
[Description("A Sellers Option transaction gives the seller the right to deliver the security at any time within a specific period, " +
"ranging from not less than two calendar days, to not more than sixty calendar days.")]
Seller = 1L << 3,
/// <summary>
/// Market Centers will have the ability to identify regular trades being reported during specific events as out of the ordinary
/// by appending a new sale condition code Yellow Flag (Y) on each transaction reported to the UTP SIP.
/// The new sale condition will be eligible to update all market center and consolidated statistics.
/// </summary>
[Description("Market Centers will have the ability to identify regular trades being reported during specific events as out of the ordinary " +
"by appending a new sale condition code Yellow Flag (Y) on each transaction reported to the UTP SIP." +
"The new sale condition will be eligible to update all market center and consolidated statistics.")]
YellowFlag = 1L << 4,
/// <summary>
/// The transaction that constituted the trade-through was the execution of an order identified as an Intermarket Sweep Order.
/// </summary>
[Description("The transaction that constituted the trade-through was the execution of an order identified as an Intermarket Sweep Order.")]
IntermarketSweep = 1L << 5,
/// <summary>
/// The trade that constituted the trade-through was a single priced opening transaction by the Market Center.
/// </summary>
[Description("The trade that constituted the trade-through was a single priced opening transaction by the Market Center.")]
OpeningPrints = 1L << 6,
/// <summary>
/// The transaction that constituted the trade-through was a single priced closing transaction by the Market Center.
/// </summary>
[Description("The transaction that constituted the trade-through was a single priced closing transaction by the Market Center.")]
ClosingPrints = 1L << 7,
/// <summary>
/// The trade that constituted the trade-through was a single priced reopening transaction by the Market Center.
/// </summary>
[Description("The trade that constituted the trade-through was a single priced reopening transaction by the Market Center.")]
ReOpeningPrints = 1L << 8,
/// <summary>
/// The transaction that constituted the trade-through was the execution of an order at a price that was not based, directly or indirectly,
/// on the quoted price of the security at the time of execution and for which the material terms were not reasonably determinable
/// at the time the commitment to execute the order was made.
/// </summary>
[Description("The transaction that constituted the trade-through was the execution of an order at a price that was not based, directly or indirectly, " +
"on the quoted price of the security at the time of execution and for which the material terms were not reasonably determinable " +
"at the time the commitment to execute the order was made.")]
DerivativelyPriced = 1L << 9,
/// <summary>
/// Trading in extended hours enables investors to react quickly to events that typically occur outside regular market hours, such as earnings reports.
/// However, liquidity may be constrained during such Form T trading, resulting in wide bid-ask spreads.
/// </summary>
[Description("Trading in extended hours enables investors to react quickly to events that typically occur outside regular market hours, such as earnings reports." +
"However, liquidity may be constrained during such Form T trading, resulting in wide bid-ask spreads.")]
FormT = 1L << 10,
/// <summary>
/// Sold Last is used when a trade prints in sequence but is reported late or printed in conformance to the One or Two Point Rule.
/// </summary>
[Description("Sold Last is used when a trade prints in sequence but is reported late or printed in conformance to the One or Two Point Rule.")]
Sold = 1L << 11,
/// <summary>
/// The transaction that constituted the trade-through was the execution by a trading center of an order for which, at the time
/// of receipt of the order, the execution at no worse than a specified price a 'stopped order'
/// </summary>
[Description("The transaction that constituted the trade-through was the execution by a trading center of an order for which, at the time" +
"of receipt of the order, the execution at no worse than a specified price a 'stopped order'")]
Stopped = 1L << 12,
/// <summary>
/// Identifies a trade that was executed outside of regular primary market hours and is reported as an extended hours trade.
/// </summary>
[Description("Identifies a trade that was executed outside of regular primary market hours and is reported as an extended hours trade.")]
ExtendedHours = 1L << 13,
/// <summary>
/// Identifies a trade that takes place outside of regular market hours.
/// </summary>
[Description("Identifies a trade that takes place outside of regular market hours.")]
OutOfSequence = 1L << 14,
/// <summary>
/// An execution in two markets when the specialist or Market Maker in the market first receiving the order agrees to execute a portion of it
/// at whatever price is realized in another market to which the balance of the order is forwarded for execution.
/// </summary>
[Description("An execution in two markets when the specialist or Market Maker in the market first receiving the order agrees to execute a portion of it " +
"at whatever price is realized in another market to which the balance of the order is forwarded for execution.")]
Split = 1L << 15,
/// <summary>
/// A transaction made on the Exchange as a result of an Exchange acquisition.
/// </summary>
[Description("A transaction made on the Exchange as a result of an Exchange acquisition.")]
Acquisition = 1L << 16,
/// <summary>
/// A trade representing an aggregate of two or more regular trades in a security occurring at the same price either simultaneously
/// or within the same 60-second period, with no individual trade exceeding 10,000 shares.
/// </summary>
[Description("A trade representing an aggregate of two or more regular trades in a security occurring at the same price either simultaneously " +
"or within the same 60-second period, with no individual trade exceeding 10,000 shares.")]
Bunched = 1L << 17,
/// <summary>
/// Stock-Option Trade is used to identify cash equity transactions which are related to options transactions and therefore
/// potentially subject to cancellation if market conditions of the options leg(s) prevent the execution of the stock-option
/// order at the price agreed upon.
/// </summary>
[Description("Stock-Option Trade is used to identify cash equity transactions which are related to options transactions and therefore" +
"potentially subject to cancellation if market conditions of the options leg(s) prevent the execution of the stock-option" +
"order at the price agreed upon.")]
StockOption = 1L << 18,
/// <summary>
/// Sale of a large block of stock in such a manner that the price is not adversely affected.
/// </summary>
[Description("Sale of a large block of stock in such a manner that the price is not adversely affected.")]
Distribution = 1L << 19,
/// <summary>
/// A trade where the price reported is based upon an average of the prices for transactions in a security during all or any portion of the trading day.
/// </summary>
[Description("A trade where the price reported is based upon an average of the prices for transactions in a security during all or any portion of the trading day.")]
AveragePrice = 1L << 20,
/// <summary>
/// Indicates that the trade resulted from a Market Centers crossing session.
/// </summary>
[Description("Indicates that the trade resulted from a Market Centers crossing session.")]
Cross = 1L << 21,
/// <summary>
/// Indicates a regular market session trade transaction that carries a price that is significantly away from the prevailing consolidated or primary market value at the time of the transaction.
/// </summary>
[Description("Indicates a regular market session trade transaction that carries a price that is significantly away from the prevailing consolidated or primary market value at the time of the transaction.")]
PriceVariation = 1L << 22,
/// <summary>
/// To qualify as a NYSE AMEX Rule 155
/// </summary>
[Description("To qualify as a NYSE AMEX Rule 155")]
Rule155 = 1L << 23,
/// <summary>
/// Indicates the Official closing value as determined by a Market Center. This transaction report will contain the market center generated closing price.
/// </summary>
[Description("Indicates the Official closing value as determined by a Market Center. This transaction report will contain the market center generated closing price.")]
OfficialClose = 1L << 24,
/// <summary>
/// A sale condition that identifies a trade based on a price at a prior point in time i.e. more than 90 seconds prior to the time of the trade report.
/// The execution time of the trade will be the time of the prior reference price.
/// </summary>
[Description("A sale condition that identifies a trade based on a price at a prior point in time i.e. more than 90 seconds prior to the time of the trade report. " +
"The execution time of the trade will be the time of the prior reference price.")]
PriorReferencePrice = 1L << 25,
/// <summary>
/// Indicates the Official open value as determined by a Market Center. This transaction report will contain the market
/// </summary>
[Description("Indicates the Official open value as determined by a Market Center. This transaction report will contain the market")]
OfficialOpen = 1L << 26,
/// <summary>
/// The CAP Election Trade highlights sales as a result of a sweep execution on the NYSE, whereby CAP orders have been elected and executed
/// outside the best price bid or offer and the orders appear as repeat trades at subsequent execution prices.
/// This indicator provides additional information to market participants that an automatic sweep transaction has occurred with repeat
/// trades as one continuous electronic transaction.
/// </summary>
[Description("The CAP Election Trade highlights sales as a result of a sweep execution on the NYSE, whereby CAP orders have been elected and executed " +
"outside the best price bid or offer and the orders appear as repeat trades at subsequent execution prices. " +
"This indicator provides additional information to market participants that an automatic sweep transaction has occurred with repeat " +
"trades as one continuous electronic transaction.")]
CapElection = 1L << 27,
/// <summary>
/// A sale condition code that identifies a NYSE trade that has been automatically executed without the potential benefit of price improvement.
/// </summary>
[Description("A sale condition code that identifies a NYSE trade that has been automatically executed without the potential benefit of price improvement.")]
AutoExecution = 1L << 28,
/// <summary>
/// Denotes whether or not a trade is exempt (Rule 611) and when used jointly with certain Sale Conditions,
/// will more fully describe the characteristics of a particular trade.
/// </summary>
[Description("Denotes whether or not a trade is exempt (Rule 611) and when used jointly with certain Sale Conditions, " +
"will more fully describe the characteristics of a particular trade.")]
TradeThroughExempt = 1L << 29,
/// <summary>
/// This flag is present in raw data, but AlgoSeek document does not describe it.
/// </summary>
[Description("This flag is present in raw data, but AlgoSeek document does not describe it.")]
UndocumentedFlag = 1L << 30,
/// <summary>
/// Denotes the trade is an odd lot less than a 100 shares.
/// </summary>
[Description("Denotes the trade is an odd lot less than a 100 shares.")]
OddLot = 1L << 31,
}
}