chore: import upstream snapshot with attribution
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 Seller’s 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 Seller’s 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 Center’s crossing session.
|
||||
/// </summary>
|
||||
[Description("Indicates that the trade resulted from a Market Center’s 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,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user