chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:02:50 +08:00
commit 0fc60fdcb1
5008 changed files with 910633 additions and 0 deletions
+125
View File
@@ -0,0 +1,125 @@
/*
* 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.Runtime.CompilerServices;
using ProtoBuf;
namespace QuantConnect.Data.Market
{
/// <summary>
/// Base Bar Class: Open, High, Low, Close and Period.
/// </summary>
[ProtoContract(SkipConstructor = true)]
public class Bar : IBar
{
private bool _openSet;
/// <summary>
/// Opening price of the bar: Defined as the price at the start of the time period.
/// </summary>
[ProtoMember(1)]
public virtual decimal Open { get; set; }
/// <summary>
/// High price of the bar during the time period.
/// </summary>
[ProtoMember(2)]
public virtual decimal High { get; set; }
/// <summary>
/// Low price of the bar during the time period.
/// </summary>
[ProtoMember(3)]
public virtual decimal Low { get; set; }
/// <summary>
/// Closing price of the bar. Defined as the price at Start Time + TimeSpan.
/// </summary>
[ProtoMember(4)]
public virtual decimal Close { get; set; }
/// <summary>
/// Default initializer to setup an empty bar.
/// </summary>
public Bar()
{
}
/// <summary>
/// Initializer to setup a bar with a given information.
/// </summary>
/// <param name="open">Decimal Opening Price</param>
/// <param name="high">Decimal High Price of this bar</param>
/// <param name="low">Decimal Low Price of this bar</param>
/// <param name="close">Decimal Close price of this bar</param>
public Bar(decimal open, decimal high, decimal low, decimal close)
{
_openSet = open != 0;
Open = open;
High = high;
Low = low;
Close = close;
}
/// <summary>
/// Updates the bar with a new value. This will aggregate the OHLC bar
/// </summary>
/// <param name="value">The new value</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Update(decimal value)
{
Update(ref value);
}
/// <summary>
/// Updates the bar with a new value. This will aggregate the OHLC bar
/// </summary>
/// <param name="value">The new value</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Update(ref decimal value)
{
// Do not accept zero as a new value
if (value == 0) return;
if (!_openSet)
{
Open = High = Low = Close = value;
_openSet = true;
}
else if (value > High) High = value;
else if (value < Low) Low = value;
Close = value;
}
/// <summary>
/// Returns a clone of this bar
/// </summary>
public Bar Clone()
{
return new Bar(Open, High, Low, Close);
}
/// <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 $"O: {Open.SmartRounding()} " +
$"H: {High.SmartRounding()} " +
$"L: {Low.SmartRounding()} " +
$"C: {Close.SmartRounding()}";
}
}
}
+38
View File
@@ -0,0 +1,38 @@
/*
* 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.Market
{
/// <summary>
/// Enum for Bar Direction
/// </summary>
public enum BarDirection
{
/// <summary>
/// Rising bar (0)
/// </summary>
Rising,
/// <summary>
/// No change (1)
/// </summary>
NoDelta,
/// <summary>
/// Falling bar (2)
/// </summary>
Falling
}
}
+330
View File
@@ -0,0 +1,330 @@
/*
* 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.Linq;
using Python.Runtime;
using QuantConnect.Python;
using QuantConnect.Securities.Option;
using QuantConnect.Util;
namespace QuantConnect.Data.Market
{
/// <summary>
/// Base representation of an entire chain of contracts for a single underlying security.
/// This type is <see cref="IEnumerable{T}"/> where T is <see cref="OptionContract"/>, <see cref="FuturesContract"/>, etc.
/// </summary>
public class BaseChain<T, TContractsCollection> : BaseData, IEnumerable<T>
where T : BaseContract
where TContractsCollection : DataDictionary<T>, new()
{
private Dictionary<Type, Dictionary<Symbol, List<BaseData>>> _auxiliaryData;
private readonly Lazy<PyObject> _dataframe;
private readonly bool _flatten;
private Dictionary<Type, Dictionary<Symbol, List<BaseData>>> AuxiliaryData
{
get
{
if (_auxiliaryData == null)
{
_auxiliaryData = new Dictionary<Type, Dictionary<Symbol, List<BaseData>>>();
}
return _auxiliaryData;
}
}
/// <summary>
/// Gets the most recent trade information for the underlying. This may
/// be a <see cref="Tick"/> or a <see cref="TradeBar"/>
/// </summary>
[PandasIgnore]
public BaseData Underlying
{
get; internal set;
}
/// <summary>
/// Gets all ticks for every option contract in this chain, keyed by option symbol
/// </summary>
[PandasIgnore]
public Ticks Ticks
{
get; protected set;
}
/// <summary>
/// Gets all trade bars for every option contract in this chain, keyed by option symbol
/// </summary>
[PandasIgnore]
public TradeBars TradeBars
{
get; protected set;
}
/// <summary>
/// Gets all quote bars for every option contract in this chain, keyed by option symbol
/// </summary>
[PandasIgnore]
public QuoteBars QuoteBars
{
get; protected set;
}
/// <summary>
/// Gets all contracts in the chain, keyed by option symbol
/// </summary>
public TContractsCollection Contracts
{
get; private set;
}
/// <summary>
/// Gets the set of symbols that passed the <see cref="Option.ContractFilter"/>
/// </summary>
[PandasIgnore]
public HashSet<Symbol> FilteredContracts
{
get; protected set;
}
/// <summary>
/// The data frame representation of the option chain
/// </summary>
[PandasIgnore]
public PyObject DataFrame => _dataframe.Value;
/// <summary>
/// The number of contracts in this chain
/// </summary>
public int Count => Contracts.Count;
/// <summary>
/// Checks if the chain contains a contract with the specified symbol
/// </summary>
/// <param name="key">The symbol of the contract to check for</param>
/// <returns>True if the chain contains a contract with the specified symbol; otherwise, false.</returns>
public bool ContainsKey(Symbol key)
{
return Contracts.ContainsKey(key);
}
/// <summary>
/// Initializes a new default instance of the <see cref="BaseChain{T, TContractsCollection}"/> class
/// </summary>
protected BaseChain(MarketDataType dataType, bool flatten)
{
DataType = dataType;
_flatten = flatten;
_dataframe = new Lazy<PyObject>(
() =>
{
if (!PythonEngine.IsInitialized)
{
return null;
}
return new PandasConverter().GetDataFrame(new[] { this }, symbolOnlyIndex: true, flatten: _flatten);
},
isThreadSafe: false);
}
/// <summary>
/// Initializes a new instance of the <see cref="BaseChain{T, TContractsCollection}"/> class
/// </summary>
/// <param name="canonicalOptionSymbol">The symbol for this chain.</param>
/// <param name="time">The time of this chain</param>
/// <param name="flatten">Whether to flatten the data frame</param>
protected BaseChain(Symbol canonicalOptionSymbol, DateTime time, MarketDataType dataType, bool flatten = true)
: this(dataType, flatten)
{
Time = time;
Symbol = canonicalOptionSymbol;
Ticks = new Ticks(time);
TradeBars = new TradeBars(time);
QuoteBars = new QuoteBars(time);
FilteredContracts = new HashSet<Symbol>();
Underlying = new QuoteBar();
Contracts = new();
Contracts.Time = time;
}
/// <summary>
/// Initializes a new instance of the <see cref="BaseChain{T, TContractsCollection}"/> class as a copy of the specified chain
/// </summary>
protected BaseChain(BaseChain<T, TContractsCollection> other)
: this(other.DataType, other._flatten)
{
Symbol = other.Symbol;
Time = other.Time;
Value = other.Value;
Underlying = other.Underlying;
Ticks = other.Ticks;
QuoteBars = other.QuoteBars;
TradeBars = other.TradeBars;
Contracts = other.Contracts;
FilteredContracts = other.FilteredContracts;
}
/// <summary>
/// Gets the auxiliary data with the specified type and symbol
/// </summary>
/// <typeparam name="TAux">The type of auxiliary data</typeparam>
/// <param name="symbol">The symbol of the auxiliary data</param>
/// <returns>The last auxiliary data with the specified type and symbol</returns>
public TAux GetAux<TAux>(Symbol symbol)
{
List<BaseData> list;
Dictionary<Symbol, List<BaseData>> dictionary;
if (!AuxiliaryData.TryGetValue(typeof(TAux), out dictionary) || !dictionary.TryGetValue(symbol, out list))
{
return default;
}
return list.OfType<TAux>().LastOrDefault();
}
/// <summary>
/// Gets all auxiliary data of the specified type as a dictionary keyed by symbol
/// </summary>
/// <typeparam name="TAux">The type of auxiliary data</typeparam>
/// <returns>A dictionary containing all auxiliary data of the specified type</returns>
public DataDictionary<TAux> GetAux<TAux>()
{
Dictionary<Symbol, List<BaseData>> d;
if (!AuxiliaryData.TryGetValue(typeof(TAux), out d))
{
return new DataDictionary<TAux>();
}
var dictionary = new DataDictionary<TAux>();
foreach (var kvp in d)
{
var item = kvp.Value.OfType<TAux>().LastOrDefault();
if (item != null)
{
dictionary.Add(kvp.Key, item);
}
}
return dictionary;
}
/// <summary>
/// Gets all auxiliary data of the specified type as a dictionary keyed by symbol
/// </summary>
/// <typeparam name="TAux">The type of auxiliary data</typeparam>
/// <returns>A dictionary containing all auxiliary data of the specified type</returns>
public Dictionary<Symbol, List<BaseData>> GetAuxList<TAux>()
{
Dictionary<Symbol, List<BaseData>> dictionary;
if (!AuxiliaryData.TryGetValue(typeof(TAux), out dictionary))
{
return new Dictionary<Symbol, List<BaseData>>();
}
return dictionary;
}
/// <summary>
/// Gets a list of auxiliary data with the specified type and symbol
/// </summary>
/// <typeparam name="TAux">The type of auxiliary data</typeparam>
/// <param name="symbol">The symbol of the auxiliary data</param>
/// <returns>The list of auxiliary data with the specified type and symbol</returns>
public List<TAux> GetAuxList<TAux>(Symbol symbol)
{
List<BaseData> list;
Dictionary<Symbol, List<BaseData>> dictionary;
if (!AuxiliaryData.TryGetValue(typeof(TAux), out dictionary) || !dictionary.TryGetValue(symbol, out list))
{
return new List<TAux>();
}
return list.OfType<TAux>().ToList();
}
/// <summary>
/// Returns an enumerator that iterates through the collection.
/// </summary>
/// <returns>
/// An enumerator that can be used to iterate through the collection.
/// </returns>
public IEnumerator<T> GetEnumerator()
{
return Contracts.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>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
/// <summary>
/// Adds the specified data to this chain
/// </summary>
/// <param name="data">The data to be added</param>
internal void AddData(BaseData data)
{
switch (data)
{
case Tick tick:
Ticks.Add(tick.Symbol, tick);
break;
case TradeBar tradeBar:
TradeBars[tradeBar.Symbol] = tradeBar;
break;
case QuoteBar quoteBar:
QuoteBars[quoteBar.Symbol] = quoteBar;
break;
default:
if (data.DataType == MarketDataType.Base)
{
AddAuxData(data);
}
break;
}
}
/// <summary>
/// Adds the specified auxiliary data to this option chain
/// </summary>
/// <param name="baseData">The auxiliary data to be added</param>
private void AddAuxData(BaseData baseData)
{
var type = baseData.GetType();
Dictionary<Symbol, List<BaseData>> dictionary;
if (!AuxiliaryData.TryGetValue(type, out dictionary))
{
dictionary = new Dictionary<Symbol, List<BaseData>>();
AuxiliaryData[type] = dictionary;
}
List<BaseData> list;
if (!dictionary.TryGetValue(baseData.Symbol, out list))
{
list = new List<BaseData>();
dictionary[baseData.Symbol] = list;
}
list.Add(baseData);
}
}
}
+87
View File
@@ -0,0 +1,87 @@
/*
* 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 Python.Runtime;
using QuantConnect.Python;
using QuantConnect.Securities;
using System;
using System.Collections.Generic;
using System.Linq;
namespace QuantConnect.Data.Market
{
/// <summary>
/// Collection of <see cref="BaseChain{T, TContractsCollection}"/> keyed by canonical option symbol
/// </summary>
public class BaseChains<T, TContract, TContractsCollection> : DataDictionary<T>
where T : BaseChain<TContract, TContractsCollection>
where TContract : BaseContract
where TContractsCollection : DataDictionary<TContract>, new()
{
private static readonly IEnumerable<string> _flattenedDfIndexNames = new[] { "canonical", "symbol" };
private readonly Lazy<PyObject> _dataframe;
private readonly bool _flatten;
/// <summary>
/// The data frame representation of the option chains
/// </summary>
public PyObject DataFrame => _dataframe.Value;
/// <summary>
/// Creates a new instance of the <see cref="BaseChains{T, TContract, TContractsCollection}"/> dictionary
/// </summary>
protected BaseChains()
: this(default, true)
{
}
/// <summary>
/// Creates a new instance of the <see cref="BaseChains{T, TContract, TContractsCollection}"/> dictionary
/// </summary>
protected BaseChains(bool flatten)
: this(default, flatten)
{
}
/// <summary>
/// Creates a new instance of the <see cref="BaseChains{T, TContract, TContractsCollection}"/> dictionary
/// </summary>
protected BaseChains(DateTime time, bool flatten)
: base(time)
{
_flatten = flatten;
_dataframe = new Lazy<PyObject>(InitializeDataFrame, isThreadSafe: false);
}
private PyObject InitializeDataFrame()
{
if (!PythonEngine.IsInitialized)
{
return null;
}
var dataFrames = this.Select(kvp => kvp.Value.DataFrame).ToList();
if (_flatten)
{
var canonicalSymbols = this.Select(kvp => kvp.Key);
return PandasConverter.ConcatDataFrames(dataFrames, keys: canonicalSymbols, names: _flattenedDfIndexNames, sort: false);
}
return PandasConverter.ConcatDataFrames(dataFrames, sort: false);
}
}
}
+138
View File
@@ -0,0 +1,138 @@
/*
* 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 QuantConnect.Python;
using System;
namespace QuantConnect.Data.Market
{
/// <summary>
/// Defines a base for a single contract, like an option or future contract
/// </summary>
public abstract class BaseContract : ISymbolProvider
{
/// <summary>
/// Gets the contract's symbol
/// </summary>
[PandasIgnore]
public Symbol Symbol
{
get; set;
}
/// <summary>
/// The security identifier of the symbol
/// </summary>
[PandasIgnore]
public SecurityIdentifier ID => Symbol.ID;
/// <summary>
/// Gets the underlying security's symbol
/// </summary>
public Symbol UnderlyingSymbol => Symbol.Underlying;
/// <summary>
/// Gets the expiration date
/// </summary>
public DateTime Expiry => Symbol.ID.Date;
/// <summary>
/// Gets the local date time this contract's data was last updated
/// </summary>
[PandasIgnore]
public DateTime Time
{
get; set;
}
/// <summary>
/// Gets the open interest
/// </summary>
public virtual decimal OpenInterest { get; set; }
/// <summary>
/// Gets the last price this contract traded at
/// </summary>
public virtual decimal LastPrice { get; set; }
/// <summary>
/// Value representation of this contract, mimicking <see cref="BaseData.Value"/>.
/// Aliases <see cref="LastPrice"/>.
/// </summary>
[PandasIgnore]
public virtual decimal Value => LastPrice;
/// <summary>
/// Alias of value as price, mimicking <see cref="BaseData.Price"/>.
/// Aliases <see cref="LastPrice"/>.
/// </summary>
[PandasIgnore]
public virtual decimal Price => LastPrice;
/// <summary>
/// Closing price of this contract, mimicking <see cref="TradeBar.Close"/>.
/// Aliases <see cref="LastPrice"/>.
/// </summary>
[PandasIgnore]
public virtual decimal Close => LastPrice;
/// <summary>
/// Gets the last volume this contract traded at
/// </summary>
public virtual long Volume { get; set; }
/// <summary>
/// Gets the current bid price
/// </summary>
public virtual decimal BidPrice { get; set; }
/// <summary>
/// Get the current bid size
/// </summary>
public virtual long BidSize { get; set; }
/// <summary>
/// Gets the ask price
/// </summary>
public virtual decimal AskPrice { get; set; }
/// <summary>
/// Gets the current ask size
/// </summary>
public virtual long AskSize { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="BaseContract"/> class
/// </summary>
/// <param name="symbol">The contract symbol</param>
protected BaseContract(Symbol symbol)
{
Symbol = symbol;
}
/// <summary>
/// Returns a string that represents the current object.
/// </summary>
/// <returns>
/// A string that represents the current object.
/// </returns>
public override string ToString() => Symbol.Value;
/// <summary>
/// Updates the contract with the new data, which can be a <see cref="Tick"/> or <see cref="TradeBar"/> or <see cref="QuoteBar"/>
/// </summary>
internal abstract void Update(BaseData data);
}
}
+86
View File
@@ -0,0 +1,86 @@
/*
* 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.Market
{
/// <summary>
/// Represents a bar sectioned not by time, but by some amount of movement in a set field,
/// where:
/// - Open : Gets the opening value that started this bar
/// - Close : Gets the closing value or the current value if the bar has not yet closed.
/// - High : Gets the highest value encountered during this bar
/// - Low : Gets the lowest value encountered during this bar
/// </summary>
public abstract class BaseRenkoBar : TradeBar, IBaseDataBar
{
/// <summary>
/// Gets the kind of the bar
/// </summary>
public RenkoType Type { get; protected set; }
/// <summary>
/// The preset size of the consolidated bar
/// </summary>
public decimal BrickSize { get; protected set; }
/// <summary>
/// Gets the end time of this renko bar or the most recent update time if it <see cref="IsClosed"/>
/// </summary>
public override DateTime EndTime { get; set; }
/// <summary>
/// Gets the time this bar started
/// </summary>
public DateTime Start
{
get { return Time; }
protected set { Time = value; }
}
/// <summary>
/// Gets whether or not this bar is considered closed.
/// </summary>
public virtual bool IsClosed { get; protected set; }
/// <summary>
/// Reader Method :: using set of arguements we specify read out type. Enumerate
/// until the end of the data stream or file. E.g. Read CSV file line by line and convert
/// into data types.
/// </summary>
/// <returns>BaseData type set by Subscription Method.</returns>
/// <param name="config">Config.</param>
/// <param name="line">Line.</param>
/// <param name="date">Date.</param>
/// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)
{
throw new NotSupportedException("RenkoBar does not support the Reader function. This function should never be called on this type.");
}
/// <summary>
/// Return the URL string source of the file. This will be converted to a stream
/// </summary>
/// <param name="config">Configuration object</param>
/// <param name="date">Date of this source file</param>
/// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
/// <returns>String URL of source file.</returns>
public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
{
throw new NotSupportedException("RenkoBar does not support the GetSource function. This function should never be called on this type.");
}
}
}
+243
View File
@@ -0,0 +1,243 @@
/*
* 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 Common.Util;
using QuantConnect.Python;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
namespace QuantConnect.Data.Market
{
/// <summary>
/// Provides a base class for types holding base data instances keyed by symbol
/// </summary>
[PandasNonExpandable]
public class DataDictionary<T> : BaseExtendedDictionary<Symbol, T>
{
/// <summary>
/// Used to cache the sorted items in the dictionary.
/// We do this instead of using a SortedDictionary to keep the O(1) access time.
/// </summary>
private List<KeyValuePair<Symbol, T>> _items;
private List<Symbol> _keys;
private List<T> _values;
/// <summary>
/// Gets or sets the time associated with this collection of data
/// </summary>
public DateTime Time { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="QuantConnect.Data.Market.DataDictionary{T}"/> class.
/// </summary>
public DataDictionary() : base()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="QuantConnect.Data.Market.DataDictionary{T}"/> class
/// using the specified <paramref name="data"/> as a data source
/// </summary>
/// <param name="data">The data source for this data dictionary</param>
/// <param name="keySelector">Delegate used to select a key from the value</param>
public DataDictionary(IEnumerable<T> data, Func<T, Symbol> keySelector)
: base(data, keySelector)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="QuantConnect.Data.Market.DataDictionary{T}"/> class.
/// </summary>
/// <param name="time">The time this data was emitted.</param>
public DataDictionary(DateTime time) : base()
{
Time = time;
}
/// <summary>
/// Gets or sets the element with the specified key.
/// </summary>
public override T this[Symbol symbol]
{
get
{
T data;
if (TryGetValue(symbol, out data))
{
return data;
}
CheckForImplicitlyCreatedSymbol(symbol);
throw new KeyNotFoundException($"'{symbol}' wasn't found in the {GetType().GetBetterTypeName()} object, likely because there was no-data at this moment in time and it wasn't possible to fillforward historical data. Please check the data exists before accessing it with data.ContainsKey(\"{symbol}\")");
}
set
{
_items = null;
base[symbol] = value;
}
}
/// <summary>
/// Gets the value associated with the specified key.
/// </summary>
public virtual T GetValue(Symbol key)
{
T value;
TryGetValue(key, out value);
return value;
}
/// <summary>
/// Gets all the items in the dictionary
/// </summary>
/// <returns>All the items in the dictionary</returns>
public override IEnumerable<KeyValuePair<Symbol, T>> GetItems()
{
if (_items == null)
{
_items = base.GetItems().OrderBy(x => x.Key).ToList();
}
return _items;
}
/// <summary>
/// Gets a collection containing the keys of the dictionary
/// </summary>
public override ICollection<Symbol> Keys
{
get
{
if (_keys == null)
{
_keys = (_items == null ? base.Keys.OrderBy(x => x) : _items.Select(x => x.Key)).ToList();
}
return _keys;
}
}
/// <summary>
/// Gets a collection containing the values of the dictionary
/// </summary>
public override ICollection<T> Values
{
get
{
if (_values == null)
{
var items = _items == null
? base.GetItems().OrderBy(x => x.Key)
: (IEnumerable<KeyValuePair<Symbol, T>>)_items;
_values = items.Select(x => x.Value).ToList();
}
return _values;
}
}
/// <summary>
/// Gets a collection containing the keys in the dictionary
/// </summary>
protected override IEnumerable<Symbol> GetKeys => Keys;
/// <summary>
/// Gets a collection containing the values in the dictionary
/// </summary>
protected override IEnumerable<T> GetValues => Values;
/// <summary>
/// Returns an enumerator that iterates through the dictionary
/// </summary>
/// <returns>An enumerator for the dictionary</returns>
public override IEnumerator<KeyValuePair<Symbol, T>> GetEnumerator()
{
return GetItems().GetEnumerator();
}
/// <summary>
/// Removes all items from the dictionary
/// </summary>
public override void Clear()
{
ClearCache();
base.Clear();
}
/// <summary>
/// Removes the value with the specified key
/// </summary>
/// <param name="key">The key of the element to remove</param>
/// <returns>true if the element was successfully found and removed; otherwise, false</returns>
public override bool Remove(Symbol key)
{
ClearCache();
return base.Remove(key);
}
/// <summary>
/// Removes the first occurrence of a specific object from the dictionary
/// </summary>
/// <param name="item">The key-value pair to remove</param>
/// <returns>true if the key-value pair was successfully removed; otherwise, false</returns>
public override bool Remove(KeyValuePair<Symbol, T> item)
{
ClearCache();
return base.Remove(item);
}
/// <summary>
/// Adds an element with the provided key and value to the dictionary
/// </summary>
/// <param name="key">The key of the element to add</param>
/// <param name="value">The value of the element to add</param>
public override void Add(Symbol key, T value)
{
ClearCache();
base.Add(key, value);
}
/// <summary>
/// Adds an element with the provided key-value pair to the dictionary
/// </summary>
/// <param name="item">The key-value pair to add</param>
public override void Add(KeyValuePair<Symbol, T> item)
{
ClearCache();
base.Add(item);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ClearCache()
{
_items = null;
_keys = null;
_values = null;
}
}
/// <summary>
/// Provides extension methods for the DataDictionary class
/// </summary>
public static class DataDictionaryExtensions
{
/// <summary>
/// Provides a convenience method for adding a base data instance to our data dictionary
/// </summary>
public static void Add<T>(this DataDictionary<T> dictionary, T data)
where T : BaseData
{
dictionary.Add(data.Symbol, data);
}
}
}
+122
View File
@@ -0,0 +1,122 @@
/*
* 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 Newtonsoft.Json;
using QuantConnect.Orders;
namespace QuantConnect.Data.Market
{
/// <summary>
/// Delisting event of a security
/// </summary>
public class Delisting : BaseData
{
/// <summary>
/// Gets the type of delisting, warning or delisted
/// A <see cref="DelistingType.Warning"/> is sent
/// </summary>
[JsonProperty]
public DelistingType Type { get; private set; }
/// <summary>
/// Gets the <see cref="OrderTicket"/> that was submitted to liquidate this position
/// </summary>
public OrderTicket Ticket { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="Delisting"/> class
/// </summary>
public Delisting()
{
DataType = MarketDataType.Auxiliary;
Type = DelistingType.Delisted;
}
/// <summary>
/// Initializes a new instance of the <see cref="Delisting"/> class
/// </summary>
/// <param name="symbol">The delisted symbol</param>
/// <param name="date">The date the symbol was delisted</param>
/// <param name="price">The final price before delisting</param>
/// <param name="type">The type of delisting event</param>
public Delisting(Symbol symbol, DateTime date, decimal price, DelistingType type)
: this()
{
Symbol = symbol;
Time = date;
Value = price;
Type = type;
}
/// <summary>
/// Sets the <see cref="OrderTicket"/> used to liquidate this position
/// </summary>
/// <param name="ticket">The ticket that represents the order to liquidate this position</param>
public void SetOrderTicket(OrderTicket ticket)
{
Ticket = ticket;
}
/// <summary>
/// Reader converts each line of the data source into BaseData objects. Each data type creates its own factory method, and returns a new instance of the object
/// each time it is called.
/// </summary>
/// <param name="config">Subscription data config setup object</param>
/// <param name="line">Line of the source document</param>
/// <param name="date">Date of the requested data</param>
/// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
/// <returns>Instance of the T:BaseData object generated by this line of the CSV</returns>
public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)
{
throw new NotImplementedException("This method is not supposed to be called on the Delisting type.");
}
/// <summary>
/// Return the URL string source of the file. This will be converted to a stream
/// </summary>
/// <param name="config">Configuration object</param>
/// <param name="date">Date of this source file</param>
/// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
/// <returns>String URL of source file.</returns>
public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
{
return null;
}
/// <summary>
/// Return a new instance clone of this object, used in fill forward
/// </summary>
/// <remarks>
/// This base implementation uses reflection to copy all public fields and properties
/// </remarks>
/// <returns>A clone of the current object</returns>
public override BaseData Clone()
{
return new Delisting(Symbol, Time, Price, Type);
}
/// <summary>
/// Formats a string with the symbol and value.
/// </summary>
/// <returns>string - a string formatted as SPY: 167.753</returns>
public override string ToString()
{
var type = Type == DelistingType.Warning ? "Delisting Warning" : "Delisted";
return $"{type}: {Symbol} {EndTime}";
}
}
}
+42
View File
@@ -0,0 +1,42 @@
/*
* 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.Market
{
/// <summary>
/// Collections of <see cref="Delisting"/> keyed by <see cref="Symbol"/>
/// </summary>
public class Delistings : DataDictionary<Delisting>
{
/// <summary>
/// Initializes a new instance of the <see cref="Delistings"/> dictionary
/// </summary>
public Delistings()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Delistings"/> dictionary
/// </summary>
/// <param name="frontier">The time associated with the data in this dictionary</param>
public Delistings(DateTime frontier)
: base(frontier)
{
}
}
}
+158
View File
@@ -0,0 +1,158 @@
/*
* 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 ProtoBuf;
using static QuantConnect.StringExtensions;
namespace QuantConnect.Data.Market
{
/// <summary>
/// Dividend event from a security
/// </summary>
[ProtoContract(SkipConstructor = true)]
public class Dividend : BaseData
{
/// <summary>
/// Gets the dividend payment
/// </summary>
[ProtoMember(10)]
public decimal Distribution
{
get { return Value; }
set { Value = value; }
}
/// <summary>
/// Gets the price at which the dividend occurred.
/// This is typically the previous day's closing price
/// </summary>
[ProtoMember(11)]
public decimal ReferencePrice
{
get;
set;
}
/// <summary>
/// Initializes a new instance of the Dividend class
/// </summary>
public Dividend()
{
DataType = MarketDataType.Auxiliary;
}
/// <summary>
/// Initializes a new instance of the Dividend class
/// </summary>
/// <param name="symbol">The symbol</param>
/// <param name="date">The date</param>
/// <param name="distribution">The dividend amount</param>
/// <param name="referencePrice">The previous day's closing price</param>
public Dividend(Symbol symbol, DateTime date, decimal distribution, decimal referencePrice)
: this()
{
Symbol = symbol;
Time = date;
Distribution = distribution;
ReferencePrice = referencePrice;
}
/// <summary>
/// Initializes a new instance of the Dividend class
/// </summary>
/// <param name="symbol">The symbol</param>
/// <param name="date">The date</param>
/// <param name="referencePrice">The previous day's closing price</param>
/// <param name="priceFactorRatio">The ratio of the price factors, pf_i/pf_i+1</param>
/// <param name="decimalPlaces">The number of decimal places to round the dividend's distribution to, defaulting to 2</param>
public static Dividend Create(Symbol symbol, DateTime date, decimal referencePrice, decimal priceFactorRatio, int decimalPlaces = 2)
{
var distribution = ComputeDistribution(referencePrice, priceFactorRatio, decimalPlaces);
return new Dividend(symbol, date, distribution, referencePrice);
}
/// <summary>
/// Computes the price factor ratio given the previous day's closing price and the p
/// </summary>
/// <param name="close">Previous day's closing price</param>
/// <param name="priceFactorRatio">Price factor ratio pf_i/pf_i+1</param>
/// <param name="decimalPlaces">The number of decimal places to round the result to, defaulting to 2</param>
/// <returns>The distribution rounded to the specified number of decimal places, defaulting to 2</returns>
public static decimal ComputeDistribution(decimal close, decimal priceFactorRatio, int decimalPlaces)
{
return Math.Round(close - close * priceFactorRatio, decimalPlaces);
}
/// <summary>
/// Reader converts each line of the data source into BaseData objects. Each data type creates its own factory method, and returns a new instance of the object
/// each time it is called.
/// </summary>
/// <param name="config">Subscription data config setup object</param>
/// <param name="line">Line of the source document</param>
/// <param name="date">Date of the requested data</param>
/// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
/// <returns>Instance of the T:BaseData object generated by this line of the CSV</returns>
public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)
{
// this is implemented in the SubscriptionDataReader.CheckForDividend
throw new NotImplementedException("This method is not supposed to be called on the Dividend type.");
}
/// <summary>
/// Return the URL string source of the file. This will be converted to a stream
/// </summary>
/// <param name="config">Configuration object</param>
/// <param name="date">Date of this source file</param>
/// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
/// <returns>String URL of source file.</returns>
public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
{
// this data is derived from map files and factor files in backtesting
return null;
}
/// <summary>
/// Return a new instance clone of this object, used in fill forward
/// </summary>
/// <remarks>
/// This base implementation uses reflection to copy all public fields and properties
/// </remarks>
/// <returns>A clone of the current object</returns>
public override BaseData Clone()
{
return new Dividend
{
Time = Time,
Value = Value,
Symbol = Symbol,
EndTime = EndTime,
DataType = DataType,
Distribution = Distribution,
ReferencePrice = ReferencePrice
};
}
/// <summary>
/// Formats a string with the symbol and value.
/// </summary>
/// <returns>string - a string formatted as SPY: 167.753</returns>
public override string ToString()
{
return Invariant($"Dividend: {Symbol}: {Distribution} | {ReferencePrice}");
}
}
}
+42
View File
@@ -0,0 +1,42 @@
/*
* 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.Market
{
/// <summary>
/// Collection of dividends keyed by <see cref="Symbol"/>
/// </summary>
public class Dividends : DataDictionary<Dividend>
{
/// <summary>
/// Initializes a new instance of the <see cref="Dividends"/> dictionary
/// </summary>
public Dividends()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Dividends"/> dictionary
/// </summary>
/// <param name="frontier">The time associated with the data in this dictionary</param>
public Dividends(DateTime frontier)
: base(frontier)
{
}
}
}
+73
View File
@@ -0,0 +1,73 @@
/*
* 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;
using QuantConnect.Data.UniverseSelection;
namespace QuantConnect.Data.Market
{
/// <summary>
/// Represents an entire chain of futures contracts for a single underlying
/// This type is <see cref="IEnumerable{FuturesContract}"/>
/// </summary>
public class FuturesChain : BaseChain<FuturesContract, FuturesContracts>
{
/// <summary>
/// Initializes a new instance of the <see cref="FuturesChain"/> class
/// </summary>
/// <param name="canonicalFutureSymbol">The symbol for this chain.</param>
/// <param name="time">The time of this chain</param>
/// <param name="flatten">Whether to flatten the data frame</param>
public FuturesChain(Symbol canonicalFutureSymbol, DateTime time, bool flatten = true)
: base(canonicalFutureSymbol, time, MarketDataType.FuturesChain, flatten)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="FuturesChain"/> class
/// </summary>
/// <param name="canonicalFutureSymbol">The symbol for this chain.</param>
/// <param name="time">The time of this chain</param>
/// <param name="contracts">The list of contracts that form this chain</param>
/// <param name="flatten">Whether to flatten the data frame</param>
public FuturesChain(Symbol canonicalFutureSymbol, DateTime time, IEnumerable<FutureUniverse> contracts, bool flatten = true)
: this(canonicalFutureSymbol, time, flatten)
{
foreach (var contractData in contracts)
{
if (contractData.Symbol.ID.Date.Date < time.Date) continue;
Contracts[contractData.Symbol] = new FuturesContract(contractData);
}
}
/// <summary>
/// Initializes a new instance of the <see cref="FuturesChain"/> class as a clone of the specified instance
/// </summary>
private FuturesChain(FuturesChain other)
: base(other)
{
}
/// <summary>
/// Return a new instance clone of this object, used in fill forward
/// </summary>
/// <returns>A clone of the current object</returns>
public override BaseData Clone()
{
return new FuturesChain(this);
}
}
}
+48
View File
@@ -0,0 +1,48 @@
/*
* 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.Market
{
/// <summary>
/// Collection of <see cref="FuturesChain"/> keyed by canonical futures symbol
/// </summary>
public class FuturesChains : BaseChains<FuturesChain, FuturesContract, FuturesContracts>
{
/// <summary>
/// Creates a new instance of the <see cref="FuturesChains"/> dictionary
/// </summary>
public FuturesChains()
{
}
/// <summary>
/// Creates a new instance of the <see cref="FuturesChains"/> dictionary
/// </summary>
public FuturesChains(bool flatten)
: base(flatten)
{
}
/// <summary>
/// Creates a new instance of the <see cref="FuturesChains"/> dictionary
/// </summary>
public FuturesChains(DateTime time, bool flatten = true)
: base(time, flatten)
{
}
}
}
+247
View File
@@ -0,0 +1,247 @@
/*
* 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 QuantConnect.Data.UniverseSelection;
namespace QuantConnect.Data.Market
{
/// <summary>
/// Defines a single futures contract at a specific expiration
/// </summary>
public class FuturesContract : BaseContract
{
private FutureUniverse _universeData;
private TradeBar _tradeBar;
private QuoteBar _quoteBar;
private Tick _tradeTick;
private Tick _quoteTick;
private Tick _openInterest;
/// <summary>
/// Gets the open interest
/// </summary>
public override decimal OpenInterest
{
get
{
// Contract universe data is prioritized
if (_universeData != null)
{
return _universeData.OpenInterest;
}
return _openInterest?.Value ?? decimal.Zero;
}
}
/// <summary>
/// Gets the last price this contract traded at
/// </summary>
public override decimal LastPrice
{
get
{
if (_universeData != null)
{
return _universeData.Close;
}
if (_tradeBar == null && _tradeTick == null)
{
return decimal.Zero;
}
if (_tradeBar != null)
{
return _tradeTick != null && _tradeTick.EndTime > _tradeBar.EndTime ? _tradeTick.Price : _tradeBar.Close;
}
return _tradeTick.Price;
}
}
/// <summary>
/// Gets the last volume this contract traded at
/// </summary>
public override long Volume
{
get
{
if (_universeData != null)
{
return (long)_universeData.Volume;
}
if (_tradeBar == null && _tradeTick == null)
{
return 0L;
}
if (_tradeBar != null)
{
return (long)(_tradeTick != null && _tradeTick.EndTime > _tradeBar.EndTime ? _tradeTick.Quantity : _tradeBar.Volume);
}
return (long)_tradeTick.Quantity;
}
}
/// <summary>
/// Get the current bid price
/// </summary>
public override decimal BidPrice
{
get
{
if (_universeData != null)
{
return _universeData.Close;
}
if (_quoteBar == null && _quoteTick == null)
{
return decimal.Zero;
}
if (_quoteBar != null)
{
var quoteBarPrice = _quoteBar.Bid?.Close ?? decimal.Zero;
if (_quoteTick != null)
{
return _quoteTick.EndTime > _quoteBar.EndTime ? _quoteTick.BidPrice : quoteBarPrice;
}
return quoteBarPrice;
}
return _quoteTick.BidPrice;
}
}
/// <summary>
/// Get the current bid size
/// </summary>
public override long BidSize
{
get
{
if (_quoteBar == null && _quoteTick == null)
{
return 0;
}
if (_quoteBar != null)
{
return (long)(_quoteTick != null && _quoteTick.EndTime > _quoteBar.EndTime ? _quoteTick.BidSize : _quoteBar.LastBidSize);
}
return (long)_quoteTick.BidSize;
}
}
/// <summary>
/// Gets the current ask price
/// </summary>
public override decimal AskPrice
{
get
{
if (_universeData != null)
{
return _universeData.Close;
}
if (_quoteBar == null && _quoteTick == null)
{
return decimal.Zero;
}
if (_quoteBar != null)
{
var quoteBarPrice = _quoteBar.Ask?.Close ?? decimal.Zero;
if (_quoteTick != null)
{
return _quoteTick.EndTime > _quoteBar.EndTime ? _quoteTick.AskPrice : quoteBarPrice;
}
return quoteBarPrice;
}
return _quoteTick.AskPrice;
}
}
/// <summary>
/// Get the current ask size
/// </summary>
public override long AskSize
{
get
{
if (_quoteBar == null && _quoteTick == null)
{
return 0;
}
if (_quoteBar != null)
{
return (long)(_quoteTick != null && _quoteTick.EndTime > _quoteBar.EndTime ? _quoteTick.AskSize : _quoteBar.LastAskSize);
}
return (long)_quoteTick.AskSize;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="FuturesContract"/> class
/// </summary>
/// <param name="symbol">The futures contract symbol</param>
public FuturesContract(Symbol symbol)
: base(symbol)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="FuturesContract"/> class
/// </summary>
/// <param name="contractData">The contract universe data</param>
public FuturesContract(FutureUniverse contractData)
: base(contractData.Symbol)
{
_universeData = contractData;
}
/// <summary>
/// Implicit conversion into <see cref="Symbol"/>
/// </summary>
/// <param name="contract">The option contract to be converted</param>
public static implicit operator Symbol(FuturesContract contract)
{
return contract.Symbol;
}
/// <summary>
/// Updates the future contract with the new data, which can be a <see cref="Tick"/> or <see cref="TradeBar"/> or <see cref="QuoteBar"/>
/// </summary>
internal override void Update(BaseData data)
{
switch (data)
{
case TradeBar tradeBar:
_tradeBar = tradeBar;
break;
case QuoteBar quoteBar:
_quoteBar = quoteBar;
break;
case Tick tick when tick.TickType == TickType.Trade:
_tradeTick = tick;
break;
case Tick tick when tick.TickType == TickType.Quote:
_quoteTick = tick;
break;
case Tick tick when tick.TickType == TickType.OpenInterest:
_openInterest = tick;
break;
}
}
}
}
+40
View File
@@ -0,0 +1,40 @@
/*
* 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.Market
{
/// <summary>
/// Collection of <see cref="FuturesContract"/> keyed by futures symbol
/// </summary>
public class FuturesContracts : DataDictionary<FuturesContract>
{
/// <summary>
/// Creates a new instance of the <see cref="FuturesContracts"/> dictionary
/// </summary>
public FuturesContracts()
{
}
/// <summary>
/// Creates a new instance of the <see cref="FuturesContracts"/> dictionary
/// </summary>
public FuturesContracts(DateTime time)
: base(time)
{
}
}
}
+153
View File
@@ -0,0 +1,153 @@
/*
* 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.Python;
namespace QuantConnect.Data.Market
{
/// <summary>
/// Defines the greeks
/// </summary>
public class Greeks
{
/// <summary>
/// Gets the delta.
/// <para>
/// Delta measures the rate of change of the option value with respect to changes in
/// the underlying asset'sprice. (∂V/∂S)
/// </para>
/// </summary>
public virtual decimal Delta { get; set; }
/// <summary>
/// Gets the gamma.
/// <para>
/// Gamma measures the rate of change of Delta with respect to changes in
/// the underlying asset'sprice. (∂²V/∂S²)
/// </para>
/// </summary>
public virtual decimal Gamma { get; set; }
/// <summary>
/// Gets the vega.
/// <para>
/// Vega measures the rate of change of the option value with respect to changes in
/// the underlying's volatility. (∂V/∂σ)
/// </para>
/// </summary>
public virtual decimal Vega { get; set; }
/// <summary>
/// Gets the theta.
/// <para>
/// Theta measures the rate of change of the option value with respect to changes in
/// time. This is commonly known as the 'time decay.' (∂V/∂τ)
/// </para>
/// </summary>
public virtual decimal Theta { get; set; }
/// <summary>
/// Gets the rho.
/// <para>
/// Rho measures the rate of change of the option value with respect to changes in
/// the risk free interest rate. (∂V/∂r)
/// </para>
/// </summary>
public virtual decimal Rho { get; set; }
/// <summary>
/// Gets the lambda.
/// <para>
/// Lambda is the percentage change in option value per percentage change in the
/// underlying's price, a measure of leverage. Sometimes referred to as gearing.
/// (∂V/∂S ✕ S/V)
/// </para>
/// </summary>
[PandasIgnore]
public virtual decimal Lambda { get; set; }
/// <summary>
/// Gets the lambda.
/// <para>
/// Lambda is the percentage change in option value per percentage change in the
/// underlying's price, a measure of leverage. Sometimes referred to as gearing.
/// (∂V/∂S ✕ S/V)
/// </para>
/// </summary>
/// <remarks>
/// Alias for <see cref="Lambda"/> required for compatibility with Python when
/// PEP8 API is used (lambda is a reserved keyword in Python).
/// </remarks>
[PandasIgnore]
public virtual decimal Lambda_
{
get { return Lambda; }
set { Lambda = value; }
}
/// <summary>
/// Gets the theta per day.
/// <para>
/// Theta measures the rate of change of the option value with respect to changes in
/// time. This is commonly known as the 'time decay.' (∂V/∂τ)
/// </para>
/// </summary>
[PandasIgnore]
public virtual decimal ThetaPerDay
{
get { return Theta / 365m; }
set { Theta = value * 365m; }
}
/// <summary>
/// Calculates the annualized theta value based on a daily theta input.
/// </summary>
/// <param name="thetaPerDay">The theta value per day to be annualized.</param>
/// <returns>The annualized theta value, calculated as the daily theta multiplied by 365. Returns decimal.MaxValue or
/// decimal.MinValue if the result overflows.</returns>
public static decimal GetSafeTheta(decimal thetaPerDay)
{
try
{
return thetaPerDay * 365m;
}
catch (OverflowException)
{
return thetaPerDay < 0 ? decimal.MinValue : decimal.MaxValue;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="Greeks"/> class.
/// </summary>
public Greeks()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Greeks"/> class with specified values.
/// </summary>
public Greeks(decimal delta, decimal gamma, decimal vega, decimal theta, decimal rho, decimal lambda)
{
Delta = delta;
Gamma = gamma;
Vega = vega;
Theta = theta;
Rho = rho;
Lambda = lambda;
}
}
}
+43
View File
@@ -0,0 +1,43 @@
/*
* 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.Market
{
/// <summary>
/// Generic bar interface with Open, High, Low and Close.
/// </summary>
public interface IBar
{
/// <summary>
/// Opening price of the bar: Defined as the price at the start of the time period.
/// </summary>
decimal Open { get; }
/// <summary>
/// High price of the bar during the time period.
/// </summary>
decimal High { get; }
/// <summary>
/// Low price of the bar during the time period.
/// </summary>
decimal Low { get; }
/// <summary>
/// Closing price of the bar. Defined as the price at Start Time + TimeSpan.
/// </summary>
decimal Close { get; }
}
}
+24
View File
@@ -0,0 +1,24 @@
/*
* 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.Market
{
/// <summary>
/// Represents a type that is both a bar and base data
/// </summary>
public interface IBaseDataBar : IBaseData, IBar
{
}
}
+100
View File
@@ -0,0 +1,100 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
using NodaTime;
using System.IO;
using QuantConnect.Util;
namespace QuantConnect.Data.Market
{
/// <summary>
/// Margin interest rate data source
/// </summary>
/// <remarks>This is useful to model margin costs</remarks>
public class MarginInterestRate : BaseData
{
/// <summary>
/// The interest rate value
/// </summary>
public decimal InterestRate { get; set; }
/// <summary>
/// Creates a new instance
/// </summary>
public MarginInterestRate()
{
DataType = MarketDataType.Auxiliary;
}
/// <summary>
/// Reader converts each line of the data source into BaseData objects. Each data type creates its own factory method, and returns a new instance of the object
/// each time it is called. The returned object is assumed to be time stamped in the config.ExchangeTimeZone.
/// </summary>
/// <param name="config">Subscription data config setup object</param>
/// <param name="stream">The data stream</param>
/// <param name="date">Date of the requested data</param>
/// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
/// <returns>Instance of the T:BaseData object generated by this line of the CSV</returns>
[StubsIgnore]
public override BaseData Reader(SubscriptionDataConfig config, StreamReader stream, DateTime date, bool isLiveMode)
{
var dateTime = stream.GetDateTime("yyyyMMdd HH:mm:ss");
var interestRate = stream.GetDecimal();
return new MarginInterestRate {
Time = dateTime,
InterestRate = Value = interestRate,
Symbol = config.Symbol
};
}
/// <summary>
/// Return the URL string source of the file. This will be converted to a stream
/// </summary>
/// <param name="config">Configuration object</param>
/// <param name="date">Date of this source file</param>
/// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
/// <returns>String URL of source file.</returns>
public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
{
var identifier = config.Symbol.ID;
var source = Path.Combine(Globals.DataFolder,
identifier.SecurityType.SecurityTypeToLower(),
identifier.Market.ToLowerInvariant(),
"margin_interest",
$"{identifier.Symbol.ToLowerInvariant()}.csv"
);
return new SubscriptionDataSource(source, SubscriptionTransportMedium.LocalFile, FileFormat.Csv);
}
/// <summary>
/// Specifies the data time zone for this data type. This is useful for custom data types
/// </summary>
public override DateTimeZone DataTimeZone()
{
return TimeZones.Utc;
}
/// <summary>
/// Formats a string with the symbol and value.
/// </summary>
public override string ToString()
{
return $"{Symbol}: Rate {InterestRate}";
}
}
}
+42
View File
@@ -0,0 +1,42 @@
/*
* 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.Market
{
/// <summary>
/// Collection of dividends keyed by <see cref="Symbol"/>
/// </summary>
public class MarginInterestRates : DataDictionary<MarginInterestRate>
{
/// <summary>
/// Initializes a new instance of the <see cref="MarginInterestRate"/> dictionary
/// </summary>
public MarginInterestRates()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="MarginInterestRate"/> dictionary
/// </summary>
/// <param name="frontier">The time associated with the data in this dictionary</param>
public MarginInterestRates(DateTime frontier)
: base(frontier)
{
}
}
}
+75
View File
@@ -0,0 +1,75 @@
/*
* 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.Market
{
/// <summary>
/// Defines the greeks
/// </summary>
internal class ModeledGreeks : Greeks
{
private Lazy<decimal> _delta;
private Lazy<decimal> _gamma;
private Lazy<decimal> _vega;
private Lazy<decimal> _theta;
private Lazy<decimal> _rho;
private Lazy<decimal> _lambda;
/// <summary>
/// Gets the delta
/// </summary>
public override decimal Delta => _delta.Value;
/// <summary>
/// Gets the gamma
/// </summary>
public override decimal Gamma => _gamma.Value;
/// <summary>
/// Gets the vega
/// </summary>
public override decimal Vega => _vega.Value;
/// <summary>
/// Gets the theta
/// </summary>
public override decimal Theta => _theta.Value;
/// <summary>
/// Gets the rho
/// </summary>
public override decimal Rho => _rho.Value;
/// <summary>
/// Gets the lambda
/// </summary>
public override decimal Lambda => _lambda.Value;
/// <summary>
/// Initializes a new instance of the <see cref="ModeledGreeks"/> class
/// </summary>
public ModeledGreeks(Func<decimal> delta, Func<decimal> gamma, Func<decimal> vega, Func<decimal> theta, Func<decimal> rho, Func<decimal> lambda)
{
_delta = new Lazy<decimal>(delta, isThreadSafe: false);
_gamma = new Lazy<decimal>(gamma, isThreadSafe: false);
_vega = new Lazy<decimal>(vega, isThreadSafe: false);
_theta = new Lazy<decimal>(theta, isThreadSafe: false);
_rho = new Lazy<decimal>(rho, isThreadSafe: false);
_lambda = new Lazy<decimal>(lambda, isThreadSafe: false);
}
}
}
+62
View File
@@ -0,0 +1,62 @@
/*
* 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.Market
{
/// <summary>
/// Defines greeks that are all zero
/// </summary>
internal class NullGreeks : Greeks
{
/// <summary>
/// Singleton instance of <see cref="NullGreeks"/>
/// </summary>
public static readonly NullGreeks Instance = new NullGreeks();
/// <summary>
/// Gets the delta
/// </summary>
public override decimal Delta => decimal.Zero;
/// <summary>
/// Gets the gamma
/// </summary>
public override decimal Gamma => decimal.Zero;
/// <summary>
/// Gets the vega
/// </summary>
public override decimal Vega => decimal.Zero;
/// <summary>
/// Gets the theta
/// </summary>
public override decimal Theta => decimal.Zero;
/// <summary>
/// Gets the rho
/// </summary>
public override decimal Rho => decimal.Zero;
/// <summary>
/// Gets the lambda
/// </summary>
public override decimal Lambda => decimal.Zero;
private NullGreeks()
{
}
}
}
+157
View File
@@ -0,0 +1,157 @@
/*
* 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 QuantConnect.Util;
using System;
using ProtoBuf;
namespace QuantConnect.Data.Market
{
/// <summary>
/// Defines a data type that represents open interest for given security
/// </summary>
[ProtoContract(SkipConstructor = true)]
public class OpenInterest : Tick
{
/// <summary>
/// Initializes a new instance of the OpenInterest class
/// </summary>
public OpenInterest()
{
DataType = MarketDataType.Tick;
TickType = TickType.OpenInterest;
Value = 0;
Time = new DateTime();
Symbol = Symbol.Empty;
}
/// <summary>
/// Cloner constructor for fill forward engine implementation. Clone the original OI into this new one:
/// </summary>
/// <param name="original">Original OI we're cloning</param>
public OpenInterest(OpenInterest original)
{
DataType = MarketDataType.Tick;
TickType = TickType.OpenInterest;
Value = original.Value;
Time = original.Time;
Symbol = original.Symbol;
}
/// <summary>
/// Initializes a new instance of the OpenInterest class with data
/// </summary>
/// <param name="time">Full date and time</param>
/// <param name="symbol">Underlying equity security symbol</param>
/// <param name="openInterest">Open Interest value</param>
public OpenInterest(DateTime time, Symbol symbol, decimal openInterest)
{
DataType = MarketDataType.Tick;
TickType = TickType.OpenInterest;
Time = time;
Symbol = symbol;
Value = openInterest;
}
/// <summary>
/// Constructor for QuantConnect open interest data
/// </summary>
/// <param name="config">Subscription configuration</param>
/// <param name="symbol">Symbol for underlying asset</param>
/// <param name="line">CSV line of data from QC OI csv</param>
/// <param name="baseDate">The base date of the OI</param>
public OpenInterest(SubscriptionDataConfig config, Symbol symbol, string line, DateTime baseDate)
{
var csv = line.Split(',');
DataType = MarketDataType.Tick;
TickType = TickType.OpenInterest;
Symbol = symbol;
Time = (config.Resolution == Resolution.Daily || config.Resolution == Resolution.Hour) ?
// hourly and daily have different time format, and can use slow, robust c# parser.
DateTime.ParseExact(csv[0], DateFormat.TwelveCharacter,
System.Globalization.CultureInfo.InvariantCulture)
.ConvertTo(config.DataTimeZone, config.ExchangeTimeZone)
:
// Using custom "ToDecimal" conversion for speed on high resolution data.
baseDate.Date.AddMilliseconds(csv[0].ToInt32()).ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
Value = csv[1].ToDecimal();
}
/// <summary>
/// Parse an open interest data line from quantconnect zip source files.
/// </summary>
/// <param name="line">CSV source line of the compressed source</param>
/// <param name="date">Base date for the open interest (date is stored as int milliseconds since midnight)</param>
/// <param name="config">Subscription configuration object</param>
public OpenInterest(SubscriptionDataConfig config, string line, DateTime date):
this(config, config.Symbol, line, date)
{
}
/// <summary>
/// Tick implementation of reader method: read a line of data from the source and convert it to an open interest object.
/// </summary>
/// <param name="config">Subscription configuration object for algorithm</param>
/// <param name="line">Line from the datafeed source</param>
/// <param name="date">Date of this reader request</param>
/// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
/// <returns>New initialized open interest object</returns>
public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)
{
if (isLiveMode)
{
// currently OIs don't come through the reader function
return new OpenInterest();
}
return new OpenInterest(config, line, date);
}
/// <summary>
/// Get source for OI data feed - not used with QuantConnect data sources implementation.
/// </summary>
/// <param name="config">Configuration object</param>
/// <param name="date">Date of this source request if source spread across multiple files</param>
/// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
/// <returns>String source location of the file to be opened with a stream</returns>
public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
{
if (isLiveMode)
{
// this data type is streamed in live mode
return new SubscriptionDataSource(string.Empty, SubscriptionTransportMedium.Streaming);
}
var source = LeanData.GenerateZipFilePath(Globals.DataFolder, config.Symbol, date, config.Resolution, config.TickType);
if (config.SecurityType == SecurityType.Future || config.SecurityType.IsOption())
{
source += "#" + LeanData.GenerateZipEntryName(config.Symbol, date, config.Resolution, config.TickType);
}
return new SubscriptionDataSource(source, SubscriptionTransportMedium.LocalFile, FileFormat.Csv);
}
/// <summary>
/// Clone implementation for open interest class:
/// </summary>
/// <returns>New tick object clone of the current class values.</returns>
public override BaseData Clone()
{
return new OpenInterest(this);
}
}
}
+77
View File
@@ -0,0 +1,77 @@
/*
* 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;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Securities;
namespace QuantConnect.Data.Market
{
/// <summary>
/// Represents an entire chain of option contracts for a single underlying security.
/// This type is <see cref="IEnumerable{OptionContract}"/>
/// </summary>
public class OptionChain : BaseChain<OptionContract, OptionContracts>
{
/// <summary>
/// Initializes a new instance of the <see cref="OptionChain"/> class
/// </summary>
/// <param name="canonicalOptionSymbol">The symbol for this chain.</param>
/// <param name="time">The time of this chain</param>
/// <param name="flatten">Whether to flatten the data frame</param>
public OptionChain(Symbol canonicalOptionSymbol, DateTime time, bool flatten = true)
: base(canonicalOptionSymbol, time, MarketDataType.OptionChain, flatten)
{
}
/// <summary>
/// Initializes a new option chain for a list of contracts as <see cref="OptionUniverse"/> instances
/// </summary>
/// <param name="canonicalOptionSymbol">The canonical option symbol</param>
/// <param name="time">The time of this chain</param>
/// <param name="contracts">The list of contracts data</param>
/// <param name="symbolProperties">The option symbol properties</param>
/// <param name="flatten">Whether to flatten the data frame</param>
public OptionChain(Symbol canonicalOptionSymbol, DateTime time, IEnumerable<OptionUniverse> contracts, SymbolProperties symbolProperties,
bool flatten = true)
: this(canonicalOptionSymbol, time, flatten)
{
foreach (var contractData in contracts)
{
Underlying ??= contractData.Underlying;
if (contractData.Symbol.ID.Date.Date < time.Date) continue;
Contracts[contractData.Symbol] = OptionContract.Create(contractData, symbolProperties);
}
}
/// <summary>
/// Initializes a new instance of the <see cref="OptionChain"/> class as a clone of the specified instance
/// </summary>
private OptionChain(OptionChain other)
: base(other)
{
}
/// <summary>
/// Return a new instance clone of this object, used in fill forward
/// </summary>
/// <returns>A clone of the current object</returns>
public override BaseData Clone()
{
return new OptionChain(this);
}
}
}
+129
View File
@@ -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.Collections.Generic;
namespace QuantConnect.Data.Market
{
/// <summary>
/// Collection of <see cref="OptionChain"/> keyed by canonical option symbol
/// </summary>
public class OptionChains : BaseChains<OptionChain, OptionContract, OptionContracts>
{
/// <summary>
/// Creates a new instance of the <see cref="OptionChains"/> dictionary
/// </summary>
public OptionChains() : base()
{
}
/// <summary>
/// Creates a new instance of the <see cref="OptionChains"/> dictionary
/// </summary>
public OptionChains(bool flatten)
: base(flatten)
{
}
/// <summary>
/// Creates a new instance of the <see cref="OptionChains"/> dictionary
/// </summary>
public OptionChains(DateTime time, bool flatten = true)
: base(time, flatten)
{
}
/// <summary>
/// Gets or sets the <see cref="OptionChain"/> for the symbol, converting to canonical if needed.
/// </summary>
public override OptionChain this[Symbol symbol]
{
get => base[GetCanonicalOptionSymbol(symbol)];
set => base[GetCanonicalOptionSymbol(symbol)] = value;
}
/// <summary>
/// Tries to get the <see cref="OptionChain"/> for the given symbol.
/// Converts to the canonical option symbol if needed before attempting retrieval.
/// </summary>
public override bool TryGetValue(Symbol key, out OptionChain value)
{
var canonicalSymbol = GetCanonicalOptionSymbol(key);
return base.TryGetValue(canonicalSymbol, out value);
}
/// <summary>
/// Checks if an <see cref="OptionChain"/> exists for the given symbol.
/// Converts to the canonical option symbol first if needed.
/// </summary>
public override bool ContainsKey(Symbol key)
{
var canonicalSymbol = GetCanonicalOptionSymbol(key);
return base.ContainsKey(canonicalSymbol);
}
/// <summary>
/// Adds the specified symbol and chain to the dictionary, converting to canonical if needed.
/// </summary>
public override void Add(Symbol key, OptionChain value)
{
var canonicalSymbol = GetCanonicalOptionSymbol(key);
base.Add(canonicalSymbol, value);
}
/// <summary>
/// Removes the element with the specified key, converting to canonical if needed.
/// </summary>
public override bool Remove(Symbol key)
{
var canonicalSymbol = GetCanonicalOptionSymbol(key);
return base.Remove(canonicalSymbol);
}
/// <summary>
/// Determines if the dictionary contains the specific key-value pair, converting key to canonical if needed.
/// </summary>
public override bool Contains(KeyValuePair<Symbol, OptionChain> item)
{
var canonicalSymbol = GetCanonicalOptionSymbol(item.Key);
return base.Contains(new KeyValuePair<Symbol, OptionChain>(canonicalSymbol, item.Value));
}
/// <summary>
/// Removes the specific key-value pair, converting key to canonical if needed.
/// </summary>
public override bool Remove(KeyValuePair<Symbol, OptionChain> item)
{
var canonicalSymbol = GetCanonicalOptionSymbol(item.Key);
return base.Remove(new KeyValuePair<Symbol, OptionChain>(canonicalSymbol, item.Value));
}
private static Symbol GetCanonicalOptionSymbol(Symbol symbol)
{
if (symbol.SecurityType.HasOptions())
{
return Symbol.CreateCanonicalOption(symbol);
}
if (symbol.SecurityType.IsOption())
{
return symbol.Canonical;
}
return symbol;
}
}
}
+344
View File
@@ -0,0 +1,344 @@
/*
* 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 QuantConnect.Data.UniverseSelection;
using QuantConnect.Interfaces;
using QuantConnect.Securities;
using QuantConnect.Securities.Option;
using System;
namespace QuantConnect.Data.Market
{
/// <summary>
/// Defines a single option contract at a specific expiration and strike price
/// </summary>
public class OptionContract : BaseContract
{
private IOptionData _optionData = OptionPriceModelResultData.Null;
private readonly SymbolProperties _symbolProperties;
/// <summary>
/// Gets the strike price
/// </summary>
public decimal Strike => Symbol.ID.StrikePrice;
/// <summary>
/// Gets the strike price multiplied by the strike multiplier
/// </summary>
public decimal ScaledStrike => Strike * _symbolProperties.StrikeMultiplier;
/// <summary>
/// Gets the right being purchased (call [right to buy] or put [right to sell])
/// </summary>
public OptionRight Right => Symbol.ID.OptionRight;
/// <summary>
/// Gets the option style
/// </summary>
public OptionStyle Style => Symbol.ID.OptionStyle;
/// <summary>
/// Gets the theoretical price of this option contract as computed by the <see cref="IOptionPriceModel"/>
/// </summary>
public decimal TheoreticalPrice => _optionData.TheoreticalPrice;
/// <summary>
/// Gets the implied volatility of the option contract as computed by the <see cref="IOptionPriceModel"/>
/// </summary>
public decimal ImpliedVolatility => _optionData.ImpliedVolatility;
/// <summary>
/// Gets the greeks for this contract
/// </summary>
public Greeks Greeks => _optionData.Greeks;
/// <summary>
/// Gets the open interest
/// </summary>
public override decimal OpenInterest => _optionData.OpenInterest;
/// <summary>
/// Gets the last price this contract traded at
/// </summary>
public override decimal LastPrice => _optionData.LastPrice;
/// <summary>
/// Gets the last volume this contract traded at
/// </summary>
public override long Volume => _optionData.Volume;
/// <summary>
/// Gets the current bid price
/// </summary>
public override decimal BidPrice => _optionData.BidPrice;
/// <summary>
/// Get the current bid size
/// </summary>
public override long BidSize => _optionData.BidSize;
/// <summary>
/// Gets the ask price
/// </summary>
public override decimal AskPrice => _optionData.AskPrice;
/// <summary>
/// Gets the current ask size
/// </summary>
public override long AskSize => _optionData.AskSize;
/// <summary>
/// Gets the last price the underlying security traded at
/// </summary>
public decimal UnderlyingLastPrice => _optionData.UnderlyingLastPrice;
/// <summary>
/// Initializes a new instance of the <see cref="OptionContract"/> class
/// </summary>
/// <param name="security">The option contract security</param>
public OptionContract(ISecurityPrice security)
: base(security.Symbol)
{
_symbolProperties = security.SymbolProperties;
}
/// <summary>
/// Initializes a new option contract from a given <see cref="OptionUniverse"/> instance
/// </summary>
/// <param name="contractData">The option universe contract data to use as source for this contract</param>
/// <param name="symbolProperties">The contract symbol properties</param>
public OptionContract(OptionUniverse contractData, SymbolProperties symbolProperties)
: base(contractData.Symbol)
{
_symbolProperties = symbolProperties;
_optionData = new OptionUniverseData(contractData);
}
/// <summary>
/// Sets the option price model evaluator function to be used for this contract
/// </summary>
/// <param name="optionPriceModelEvaluator">Function delegate used to evaluate the option price model</param>
internal void SetOptionPriceModel(Func<OptionPriceModelResult> optionPriceModelEvaluator)
{
_optionData = new OptionPriceModelResultData(optionPriceModelEvaluator, _optionData as OptionPriceModelResultData);
}
/// <summary>
/// Creates a <see cref="OptionContract"/>
/// </summary>
/// <param name="baseData"></param>
/// <param name="security">Provides price properties for a <see cref="Security"/></param>
/// <param name="underlying">Last underlying security trade data</param>
/// <returns>Option contract</returns>
public static OptionContract Create(BaseData baseData, ISecurityPrice security, BaseData underlying)
=> Create(baseData.EndTime, security, underlying);
/// <summary>
/// Creates a <see cref="OptionContract"/>
/// </summary>
/// <param name="endTime">local date time this contract's data was last updated</param>
/// <param name="security">provides price properties for a <see cref="Security"/></param>
/// <param name="underlying">last underlying security trade data</param>
/// <returns>Option contract</returns>
public static OptionContract Create(DateTime endTime, ISecurityPrice security, BaseData underlying)
{
var contract = new OptionContract(security)
{
Time = endTime,
};
contract._optionData.SetUnderlying(underlying);
return contract;
}
/// <summary>
/// Creates a new option contract from a given <see cref="OptionUniverse"/> instance,
/// using its data to form a quote bar to source pricing data
/// </summary>
/// <param name="contractData">The option universe contract data to use as source for this contract</param>
/// <param name="symbolProperties">The contract symbol properties</param>
public static OptionContract Create(OptionUniverse contractData, SymbolProperties symbolProperties)
{
var contract = new OptionContract(contractData, symbolProperties)
{
Time = contractData.EndTime,
};
return contract;
}
/// <summary>
/// Implicit conversion into <see cref="Symbol"/>
/// </summary>
/// <param name="contract">The option contract to be converted</param>
public static implicit operator Symbol(OptionContract contract)
{
return contract.Symbol;
}
/// <summary>
/// Updates the option contract with the new data, which can be a <see cref="Tick"/> or <see cref="TradeBar"/> or <see cref="QuoteBar"/>
/// </summary>
internal override void Update(BaseData data)
{
if (data.Symbol.SecurityType.IsOption())
{
_optionData.Update(data);
}
else if (data.Symbol.SecurityType == Symbol.GetUnderlyingFromOptionType(Symbol.SecurityType))
{
_optionData.SetUnderlying(data);
}
}
#region Option Contract Data Handlers
private interface IOptionData
{
decimal LastPrice { get; }
decimal UnderlyingLastPrice { get; }
long Volume { get; }
decimal BidPrice { get; }
long BidSize { get; }
decimal AskPrice { get; }
long AskSize { get; }
decimal OpenInterest { get; }
decimal TheoreticalPrice { get; }
decimal ImpliedVolatility { get; }
Greeks Greeks { get; }
void Update(BaseData data);
void SetUnderlying(BaseData data);
}
/// <summary>
/// Handles option data for a contract from actual price data (trade, quote, open interest) and theoretical price model results
/// </summary>
private class OptionPriceModelResultData : IOptionData
{
public static readonly OptionPriceModelResultData Null = new(() => OptionPriceModelResult.None);
private readonly Lazy<OptionPriceModelResult> _optionPriceModelResult;
private TradeBar _tradeBar;
private QuoteBar _quoteBar;
private OpenInterest _openInterest;
private BaseData _underlying;
public decimal LastPrice => _tradeBar?.Close ?? decimal.Zero;
public decimal UnderlyingLastPrice => _underlying?.Price ?? decimal.Zero;
public long Volume => (long)(_tradeBar?.Volume ?? 0L);
public decimal BidPrice => _quoteBar?.Bid?.Close ?? decimal.Zero;
public long BidSize => (long)(_quoteBar?.LastBidSize ?? 0L);
public decimal AskPrice => _quoteBar?.Ask?.Close ?? decimal.Zero;
public long AskSize => (long)(_quoteBar?.LastAskSize ?? 0L);
public decimal OpenInterest => _openInterest?.Value ?? decimal.Zero;
public decimal TheoreticalPrice => _optionPriceModelResult.Value.TheoreticalPrice;
public decimal ImpliedVolatility => _optionPriceModelResult.Value.ImpliedVolatility;
public Greeks Greeks => _optionPriceModelResult.Value.Greeks;
public OptionPriceModelResultData(Func<OptionPriceModelResult> optionPriceModelEvaluator,
OptionPriceModelResultData previousOptionData = null)
{
_optionPriceModelResult = new(optionPriceModelEvaluator, isThreadSafe: false);
if (previousOptionData != null)
{
_tradeBar = previousOptionData._tradeBar;
_quoteBar = previousOptionData._quoteBar;
_openInterest = previousOptionData._openInterest;
_underlying = previousOptionData._underlying;
}
}
public void Update(BaseData data)
{
switch (data)
{
case TradeBar tradeBar:
_tradeBar = tradeBar;
break;
case QuoteBar quoteBar:
_quoteBar = quoteBar;
break;
case OpenInterest openInterest:
_openInterest = openInterest;
break;
}
}
public void SetUnderlying(BaseData data)
{
_underlying = data;
}
}
/// <summary>
/// Handles option data for a contract from a <see cref="OptionUniverse"/> instance
/// </summary>
private class OptionUniverseData : IOptionData
{
private readonly OptionUniverse _contractData;
public decimal LastPrice => _contractData.Close;
// TODO: Null check required for FOPs: since OptionUniverse does not support FOPs,
// these instances will by "synthetic" and will not have underlying data.
// Can be removed after FOPs are supported by OptionUniverse
public decimal UnderlyingLastPrice => _contractData?.Underlying?.Price ?? decimal.Zero;
public long Volume => (long)_contractData.Volume;
public decimal BidPrice => _contractData.Close;
public long BidSize => 0;
public decimal AskPrice => _contractData.Close;
public long AskSize => 0;
public decimal OpenInterest => _contractData.OpenInterest;
public decimal TheoreticalPrice => decimal.Zero;
public decimal ImpliedVolatility => _contractData.ImpliedVolatility;
public Greeks Greeks => _contractData.Greeks;
public OptionUniverseData(OptionUniverse contractData)
{
_contractData = contractData;
}
public void Update(BaseData data)
{
}
public void SetUnderlying(BaseData data)
{
}
}
#endregion
}
}
+40
View File
@@ -0,0 +1,40 @@
/*
* 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.Market
{
/// <summary>
/// Collection of <see cref="OptionContract"/> keyed by option symbol
/// </summary>
public class OptionContracts : DataDictionary<OptionContract>
{
/// <summary>
/// Creates a new instance of the <see cref="OptionContracts"/> dictionary
/// </summary>
public OptionContracts()
{
}
/// <summary>
/// Creates a new instance of the <see cref="OptionContracts"/> dictionary
/// </summary>
public OptionContracts(DateTime time)
: base(time)
{
}
}
}
+725
View File
@@ -0,0 +1,725 @@
/*
* 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.Globalization;
using System.IO;
using System.Runtime.CompilerServices;
using ProtoBuf;
using QuantConnect.Logging;
using QuantConnect.Python;
using QuantConnect.Util;
using static QuantConnect.StringExtensions;
namespace QuantConnect.Data.Market
{
/// <summary>
/// QuoteBar class for second and minute resolution data:
/// An OHLC implementation of the QuantConnect BaseData class with parameters for candles.
/// </summary>
[ProtoContract(SkipConstructor = true)]
public class QuoteBar : BaseData, IBaseDataBar
{
// scale factor used in QC equity/option/indexOption data files
private const decimal _scaleFactor = 1 / 10000m;
/// <summary>
/// Average bid size
/// </summary>
[ProtoMember(201)]
[PandasColumn("bidsize")]
public decimal LastBidSize { get; set; }
/// <summary>
/// Average ask size
/// </summary>
[ProtoMember(202)]
[PandasColumn("asksize")]
public decimal LastAskSize { get; set; }
/// <summary>
/// Bid OHLC
/// </summary>
[ProtoMember(203)]
public Bar Bid { get; set; }
/// <summary>
/// Ask OHLC
/// </summary>
[ProtoMember(204)]
public Bar Ask { get; set; }
/// <summary>
/// Opening price of the bar: Defined as the price at the start of the time period.
/// </summary>
public decimal Open
{
get
{
if (Bid != null && Ask != null)
{
if (Bid.Open != 0m && Ask.Open != 0m)
return (Bid.Open + Ask.Open) / 2m;
if (Bid.Open != 0)
return Bid.Open;
if (Ask.Open != 0)
return Ask.Open;
return 0m;
}
if (Bid != null)
{
return Bid.Open;
}
if (Ask != null)
{
return Ask.Open;
}
return 0m;
}
}
/// <summary>
/// High price of the QuoteBar during the time period.
/// </summary>
public decimal High
{
get
{
if (Bid != null && Ask != null)
{
if (Bid.High != 0m && Ask.High != 0m)
return (Bid.High + Ask.High) / 2m;
if (Bid.High != 0)
return Bid.High;
if (Ask.High != 0)
return Ask.High;
return 0m;
}
if (Bid != null)
{
return Bid.High;
}
if (Ask != null)
{
return Ask.High;
}
return 0m;
}
}
/// <summary>
/// Low price of the QuoteBar during the time period.
/// </summary>
public decimal Low
{
get
{
if (Bid != null && Ask != null)
{
if (Bid.Low != 0m && Ask.Low != 0m)
return (Bid.Low + Ask.Low) / 2m;
if (Bid.Low != 0)
return Bid.Low;
if (Ask.Low != 0)
return Ask.Low;
return 0m;
}
if (Bid != null)
{
return Bid.Low;
}
if (Ask != null)
{
return Ask.Low;
}
return 0m;
}
}
/// <summary>
/// Closing price of the QuoteBar. Defined as the price at Start Time + TimeSpan.
/// </summary>
public decimal Close
{
get
{
if (Bid != null && Ask != null)
{
if (Bid.Close != 0m && Ask.Close != 0m)
return (Bid.Close + Ask.Close) / 2m;
if (Bid.Close != 0)
return Bid.Close;
if (Ask.Close != 0)
return Ask.Close;
return 0m;
}
if (Bid != null)
{
return Bid.Close;
}
if (Ask != null)
{
return Ask.Close;
}
return Value;
}
}
/// <summary>
/// The closing time of this bar, computed via the Time and Period
/// </summary>
[PandasIgnore]
public override DateTime EndTime
{
get { return Time + Period; }
set { Period = value - Time; }
}
/// <summary>
/// The period of this quote bar, (second, minute, daily, ect...)
/// </summary>
[ProtoMember(205)]
[PandasIgnore]
public TimeSpan Period { get; set; }
/// <summary>
/// Default initializer to setup an empty quotebar.
/// </summary>
public QuoteBar()
: this(false)
{
}
/// <summary>
/// Default initializer to setup an empty quotebar.
/// </summary>
public QuoteBar(bool empty)
{
DataType = MarketDataType.QuoteBar;
if (empty)
{
return;
}
Symbol = Symbol.Empty;
Time = new DateTime();
Bid = new Bar();
Ask = new Bar();
Value = 0;
Period = QuantConnect.Time.OneMinute;
}
/// <summary>
/// Initialize Quote Bar with Bid(OHLC) and Ask(OHLC) Values:
/// </summary>
/// <param name="time">DateTime Timestamp of the bar</param>
/// <param name="symbol">Market MarketType Symbol</param>
/// <param name="bid">Bid OLHC bar</param>
/// <param name="lastBidSize">Average bid size over period</param>
/// <param name="ask">Ask OLHC bar</param>
/// <param name="lastAskSize">Average ask size over period</param>
/// <param name="period">The period of this bar, specify null for default of 1 minute</param>
public QuoteBar(DateTime time, Symbol symbol, IBar bid, decimal lastBidSize, IBar ask, decimal lastAskSize, TimeSpan? period = null)
{
Symbol = symbol;
Time = time;
Bid = bid == null ? null : new Bar(bid.Open, bid.High, bid.Low, bid.Close);
Ask = ask == null ? null : new Bar(ask.Open, ask.High, ask.Low, ask.Close);
if (Bid != null) LastBidSize = lastBidSize;
if (Ask != null) LastAskSize = lastAskSize;
Value = Close;
Period = period ?? QuantConnect.Time.OneMinute;
DataType = MarketDataType.QuoteBar;
}
/// <summary>
/// Update the quotebar - build the bar from this pricing information:
/// </summary>
/// <param name="lastTrade">The last trade price</param>
/// <param name="bidPrice">Current bid price</param>
/// <param name="askPrice">Current asking price</param>
/// <param name="volume">Volume of this trade</param>
/// <param name="bidSize">The size of the current bid, if available, if not, pass 0</param>
/// <param name="askSize">The size of the current ask, if available, if not, pass 0</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Update(decimal lastTrade, decimal bidPrice, decimal askPrice, decimal volume, decimal bidSize, decimal askSize)
{
// update our bid and ask bars - handle null values, this is to give good values for midpoint OHLC
if (Bid == null && bidPrice != 0) Bid = new Bar(bidPrice, bidPrice, bidPrice, bidPrice);
else if (Bid != null) Bid.Update(ref bidPrice);
if (Ask == null && askPrice != 0) Ask = new Bar(askPrice, askPrice, askPrice, askPrice);
else if (Ask != null) Ask.Update(ref askPrice);
if (bidSize > 0)
{
LastBidSize = bidSize;
}
if (askSize > 0)
{
LastAskSize = askSize;
}
// be prepared for updates without trades
if (lastTrade != 0) Value = lastTrade;
else if (askPrice != 0) Value = askPrice;
else if (bidPrice != 0) Value = bidPrice;
}
/// <summary>
/// QuoteBar Reader: Fetch the data from the QC storage and feed it line by line into the engine.
/// </summary>
/// <param name="config">Symbols, Resolution, DataType, </param>
/// <param name="stream">The file data stream</param>
/// <param name="date">Date of this reader request</param>
/// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
/// <returns>Enumerable iterator for returning each line of the required data.</returns>
[StubsIgnore]
public override BaseData Reader(SubscriptionDataConfig config, StreamReader stream, DateTime date, bool isLiveMode)
{
try
{
switch (config.SecurityType)
{
case SecurityType.Equity:
return ParseEquity(config, stream, date);
case SecurityType.Forex:
case SecurityType.Crypto:
case SecurityType.CryptoFuture:
return ParseForex(config, stream, date);
case SecurityType.Cfd:
return ParseCfd(config, stream, date);
case SecurityType.Option:
case SecurityType.FutureOption:
case SecurityType.IndexOption:
return ParseOption(config, stream, date);
case SecurityType.Future:
return ParseFuture(config, stream, date);
}
}
catch (Exception err)
{
Log.Error(Invariant($"QuoteBar.Reader(): Error parsing stream, Symbol: {config.Symbol.Value}, SecurityType: {config.SecurityType}, ") +
Invariant($"Resolution: {config.Resolution}, Date: {date.ToStringInvariant("yyyy-MM-dd")}, Message: {err}")
);
}
// we need to consume a line anyway, to advance the stream
stream.ReadLine();
// if we couldn't parse it above return a default instance
return new QuoteBar { Symbol = config.Symbol, Period = config.Increment };
}
/// <summary>
/// QuoteBar Reader: Fetch the data from the QC storage and feed it line by line into the engine.
/// </summary>
/// <param name="config">Symbols, Resolution, DataType, </param>
/// <param name="line">Line from the data file requested</param>
/// <param name="date">Date of this reader request</param>
/// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
/// <returns>Enumerable iterator for returning each line of the required data.</returns>
public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)
{
try
{
switch (config.SecurityType)
{
case SecurityType.Equity:
return ParseEquity(config, line, date);
case SecurityType.Forex:
case SecurityType.Crypto:
case SecurityType.CryptoFuture:
return ParseForex(config, line, date);
case SecurityType.Cfd:
return ParseCfd(config, line, date);
case SecurityType.Option:
case SecurityType.FutureOption:
case SecurityType.IndexOption:
return ParseOption(config, line, date);
case SecurityType.Future:
return ParseFuture(config, line, date);
}
}
catch (Exception err)
{
Log.Error(Invariant($"QuoteBar.Reader(): Error parsing line: '{line}', Symbol: {config.Symbol.Value}, SecurityType: {config.SecurityType}, ") +
Invariant($"Resolution: {config.Resolution}, Date: {date.ToStringInvariant("yyyy-MM-dd")}, Message: {err}")
);
}
// if we couldn't parse it above return a default instance
return new QuoteBar { Symbol = config.Symbol, Period = config.Increment };
}
/// <summary>
/// Parse a quotebar representing a future with a scaling factor
/// </summary>
/// <param name="config">Symbols, Resolution, DataType</param>
/// <param name="line">Line from the data file requested</param>
/// <param name="date">Date of this reader request</param>
/// <returns><see cref="QuoteBar"/> with the bid/ask set to same values</returns>
public QuoteBar ParseFuture(SubscriptionDataConfig config, string line, DateTime date)
{
return ParseQuote(config, date, line, false);
}
/// <summary>
/// Parse a quotebar representing a future with a scaling factor
/// </summary>
/// <param name="config">Symbols, Resolution, DataType</param>
/// <param name="streamReader">The data stream of the requested file</param>
/// <param name="date">Date of this reader request</param>
/// <returns><see cref="QuoteBar"/> with the bid/ask set to same values</returns>
public QuoteBar ParseFuture(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
{
return ParseQuote(config, date, streamReader, false);
}
/// <summary>
/// Parse a quotebar representing an option with a scaling factor
/// </summary>
/// <param name="config">Symbols, Resolution, DataType</param>
/// <param name="line">Line from the data file requested</param>
/// <param name="date">Date of this reader request</param>
/// <returns><see cref="QuoteBar"/> with the bid/ask set to same values</returns>
public QuoteBar ParseOption(SubscriptionDataConfig config, string line, DateTime date)
{
return ParseQuote(config, date, line, LeanData.OptionUseScaleFactor(config.Symbol));
}
/// <summary>
/// Parse a quotebar representing an option with a scaling factor
/// </summary>
/// <param name="config">Symbols, Resolution, DataType</param>
/// <param name="streamReader">The data stream of the requested file</param>
/// <param name="date">Date of this reader request</param>
/// <returns><see cref="QuoteBar"/> with the bid/ask set to same values</returns>
public QuoteBar ParseOption(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
{
// scale factor only applies for equity and index options
return ParseQuote(config, date, streamReader, useScaleFactor: LeanData.OptionUseScaleFactor(config.Symbol));
}
/// <summary>
/// Parse a quotebar representing a cfd without a scaling factor
/// </summary>
/// <param name="config">Symbols, Resolution, DataType</param>
/// <param name="line">Line from the data file requested</param>
/// <param name="date">Date of this reader request</param>
/// <returns><see cref="QuoteBar"/> with the bid/ask set to same values</returns>
public QuoteBar ParseCfd(SubscriptionDataConfig config, string line, DateTime date)
{
return ParseQuote(config, date, line, false);
}
/// <summary>
/// Parse a quotebar representing a cfd without a scaling factor
/// </summary>
/// <param name="config">Symbols, Resolution, DataType</param>
/// <param name="streamReader">The data stream of the requested file</param>
/// <param name="date">Date of this reader request</param>
/// <returns><see cref="QuoteBar"/> with the bid/ask set to same values</returns>
public QuoteBar ParseCfd(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
{
return ParseQuote(config, date, streamReader, false);
}
/// <summary>
/// Parse a quotebar representing a forex without a scaling factor
/// </summary>
/// <param name="config">Symbols, Resolution, DataType</param>
/// <param name="line">Line from the data file requested</param>
/// <param name="date">Date of this reader request</param>
/// <returns><see cref="QuoteBar"/> with the bid/ask set to same values</returns>
public QuoteBar ParseForex(SubscriptionDataConfig config, string line, DateTime date)
{
return ParseQuote(config, date, line, false);
}
/// <summary>
/// Parse a quotebar representing a forex without a scaling factor
/// </summary>
/// <param name="config">Symbols, Resolution, DataType</param>
/// <param name="streamReader">The data stream of the requested file</param>
/// <param name="date">Date of this reader request</param>
/// <returns><see cref="QuoteBar"/> with the bid/ask set to same values</returns>
public QuoteBar ParseForex(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
{
return ParseQuote(config, date, streamReader, false);
}
/// <summary>
/// Parse a quotebar representing an equity with a scaling factor
/// </summary>
/// <param name="config">Symbols, Resolution, DataType</param>
/// <param name="line">Line from the data file requested</param>
/// <param name="date">Date of this reader request</param>
/// <returns><see cref="QuoteBar"/> with the bid/ask set to same values</returns>
public QuoteBar ParseEquity(SubscriptionDataConfig config, string line, DateTime date)
{
return ParseQuote(config, date, line, true);
}
/// <summary>
/// Parse a quotebar representing an equity with a scaling factor
/// </summary>
/// <param name="config">Symbols, Resolution, DataType</param>
/// <param name="streamReader">The data stream of the requested file</param>
/// <param name="date">Date of this reader request</param>
/// <returns><see cref="QuoteBar"/> with the bid/ask set to same values</returns>
public QuoteBar ParseEquity(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
{
return ParseQuote(config, date, streamReader, true);
}
/// <summary>
/// "Scaffold" code - If the data being read is formatted as a QuoteBar, use this method to deserialize it
/// </summary>
/// <param name="config">Symbols, Resolution, DataType, </param>
/// <param name="streamReader">The data stream of the requested file</param>
/// <param name="date">Date of this reader request</param>
/// <param name="useScaleFactor">Whether the data has a scaling factor applied</param>
/// <returns><see cref="QuoteBar"/> with the bid/ask prices set appropriately</returns>
private QuoteBar ParseQuote(SubscriptionDataConfig config, DateTime date, StreamReader streamReader, bool useScaleFactor)
{
// Non-equity asset classes will not use scaling, including options that have a non-equity underlying asset class.
var scaleFactor = useScaleFactor
? _scaleFactor
: 1;
var quoteBar = new QuoteBar
{
Period = config.Increment,
Symbol = config.Symbol
};
if (config.Resolution == Resolution.Daily || config.Resolution == Resolution.Hour)
{
// hourly and daily have different time format, and can use slow, robust c# parser.
quoteBar.Time = streamReader.GetDateTime().ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
}
else
{
// Using custom int conversion for speed on high resolution data.
quoteBar.Time = date.Date.AddMilliseconds(streamReader.GetInt32()).ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
}
var open = streamReader.GetDecimal();
var high = streamReader.GetDecimal();
var low = streamReader.GetDecimal();
var close = streamReader.GetDecimal();
var lastSize = streamReader.GetDecimal();
// only create the bid if it exists in the file
if (open != 0 || high != 0 || low != 0 || close != 0)
{
// the Bid/Ask bars were already create above, we don't need to recreate them but just set their values
quoteBar.Bid.Open = open * scaleFactor;
quoteBar.Bid.High = high * scaleFactor;
quoteBar.Bid.Low = low * scaleFactor;
quoteBar.Bid.Close = close * scaleFactor;
quoteBar.LastBidSize = lastSize;
}
else
{
quoteBar.Bid = null;
}
open = streamReader.GetDecimal();
high = streamReader.GetDecimal();
low = streamReader.GetDecimal();
close = streamReader.GetDecimal();
lastSize = streamReader.GetDecimal();
// only create the ask if it exists in the file
if (open != 0 || high != 0 || low != 0 || close != 0)
{
// the Bid/Ask bars were already create above, we don't need to recreate them but just set their values
quoteBar.Ask.Open = open * scaleFactor;
quoteBar.Ask.High = high * scaleFactor;
quoteBar.Ask.Low = low * scaleFactor;
quoteBar.Ask.Close = close * scaleFactor;
quoteBar.LastAskSize = lastSize;
}
else
{
quoteBar.Ask = null;
}
quoteBar.Value = quoteBar.Close;
return quoteBar;
}
/// <summary>
/// "Scaffold" code - If the data being read is formatted as a QuoteBar, use this method to deserialize it
/// TODO: Once all Forex data refactored to use QuoteBar formatted data, use only this method
/// </summary>
/// <param name="config">Symbols, Resolution, DataType, </param>
/// <param name="line">Line from the data file requested</param>
/// <param name="date">Date of this reader request</param>
/// <param name="useScaleFactor">Whether the data has a scaling factor applied</param>
/// <returns><see cref="QuoteBar"/> with the bid/ask prices set appropriately</returns>
private QuoteBar ParseQuote(SubscriptionDataConfig config, DateTime date, string line, bool useScaleFactor)
{
var scaleFactor = useScaleFactor
? _scaleFactor
: 1;
var quoteBar = new QuoteBar
{
Period = config.Increment,
Symbol = config.Symbol
};
var csv = line.ToCsv(11);
if (config.Resolution == Resolution.Daily || config.Resolution == Resolution.Hour)
{
// hourly and daily have different time format, and can use slow, robust c# parser.
quoteBar.Time = DateTime.ParseExact(csv[0], DateFormat.TwelveCharacter, CultureInfo.InvariantCulture).ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
}
else
{
// Using custom "ToDecimal" conversion for speed on high resolution data.
quoteBar.Time = date.Date.AddMilliseconds((double)csv[0].ToDecimal()).ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
}
// only create the bid if it exists in the file
if (csv[1].Length != 0 || csv[2].Length != 0 || csv[3].Length != 0 || csv[4].Length != 0)
{
// the Bid/Ask bars were already create above, we don't need to recreate them but just set their values
quoteBar.Bid.Open = csv[1].ToDecimal() * scaleFactor;
quoteBar.Bid.High = csv[2].ToDecimal() * scaleFactor;
quoteBar.Bid.Low = csv[3].ToDecimal() * scaleFactor;
quoteBar.Bid.Close = csv[4].ToDecimal() * scaleFactor;
quoteBar.LastBidSize = csv[5].ToDecimal();
}
else
{
quoteBar.Bid = null;
}
// only create the ask if it exists in the file
if (csv[6].Length != 0 || csv[7].Length != 0 || csv[8].Length != 0 || csv[9].Length != 0)
{
// the Bid/Ask bars were already create above, we don't need to recreate them but just set their values
quoteBar.Ask.Open = csv[6].ToDecimal() * scaleFactor;
quoteBar.Ask.High = csv[7].ToDecimal() * scaleFactor;
quoteBar.Ask.Low = csv[8].ToDecimal() * scaleFactor;
quoteBar.Ask.Close = csv[9].ToDecimal() * scaleFactor;
quoteBar.LastAskSize = csv[10].ToDecimal();
}
else
{
quoteBar.Ask = null;
}
quoteBar.Value = quoteBar.Close;
return quoteBar;
}
/// <summary>
/// Get Source for Custom Data File
/// >> What source file location would you prefer for each type of usage:
/// </summary>
/// <param name="config">Configuration object</param>
/// <param name="date">Date of this source request if source spread across multiple files</param>
/// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
/// <returns>String source location of the file</returns>
public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
{
if (isLiveMode)
{
// this data type is streamed in live mode
return new SubscriptionDataSource(string.Empty, SubscriptionTransportMedium.Streaming);
}
var source = LeanData.GenerateZipFilePath(Globals.DataFolder, config.Symbol, date, config.Resolution, config.TickType);
if (config.SecurityType == SecurityType.Future || config.SecurityType.IsOption())
{
source += "#" + LeanData.GenerateZipEntryName(config.Symbol, date, config.Resolution, config.TickType);
}
return new SubscriptionDataSource(source, SubscriptionTransportMedium.LocalFile, FileFormat.Csv);
}
/// <summary>
/// Return a new instance clone of this quote bar, used in fill forward
/// </summary>
/// <returns>A clone of the current quote bar</returns>
public override BaseData Clone()
{
return new QuoteBar
{
Ask = Ask == null ? null : Ask.Clone(),
Bid = Bid == null ? null : Bid.Clone(),
LastAskSize = LastAskSize,
LastBidSize = LastBidSize,
Symbol = Symbol,
Time = Time,
Period = Period,
Value = Value,
DataType = DataType
};
}
/// <summary>
/// Collapses QuoteBars into TradeBars object when
/// algorithm requires FX data, but calls OnData(<see cref="TradeBars"/>)
/// TODO: (2017) Remove this method in favor of using OnData(<see cref="Slice"/>)
/// </summary>
/// <returns><see cref="TradeBars"/></returns>
public TradeBar Collapse()
{
return new TradeBar(Time, Symbol, Open, High, Low, Close, 0, Period);
}
/// <summary>
/// Convert this <see cref="QuoteBar"/> to string form.
/// </summary>
/// <returns>String representation of the <see cref="QuoteBar"/></returns>
public override string ToString()
{
return $"{Symbol}: " +
$"Bid: O: {Bid?.Open.SmartRounding()} " +
$"Bid: H: {Bid?.High.SmartRounding()} " +
$"Bid: L: {Bid?.Low.SmartRounding()} " +
$"Bid: C: {Bid?.Close.SmartRounding()} " +
$"Ask: O: {Ask?.Open.SmartRounding()} " +
$"Ask: H: {Ask?.High.SmartRounding()} " +
$"Ask: L: {Ask?.Low.SmartRounding()} " +
$"Ask: C: {Ask?.Close.SmartRounding()} ";
}
}
}
+40
View File
@@ -0,0 +1,40 @@
/*
* 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.Market
{
/// <summary>
/// Collection of <see cref="QuoteBar"/> keyed by symbol
/// </summary>
public class QuoteBars : DataDictionary<QuoteBar>
{
/// <summary>
/// Creates a new instance of the <see cref="QuoteBars"/> dictionary
/// </summary>
public QuoteBars()
{
}
/// <summary>
/// Creates a new instance of the <see cref="QuoteBars"/> dictionary
/// </summary>
public QuoteBars(DateTime time)
: base(time)
{
}
}
}
+134
View File
@@ -0,0 +1,134 @@
/*
* 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.Market
{
/// <summary>
/// Represents a bar sectioned not by time, but by some amount of movement in a value (for example, Closing price moving in $10 bar sizes)
/// </summary>
public class RangeBar: TradeBar
{
/// <summary>
/// Gets the range of the bar.
/// </summary>
public decimal RangeSize { get; private set; }
/// <summary>
/// Gets whether or not this bar is considered closed.
/// </summary>
public bool IsClosed { get; private set; }
/// <summary>
/// Initialize a new default instance of <see cref="RangeBar"/> class.
/// </summary>
public RangeBar()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="RangeBar"/> class with the specified values
/// </summary>
/// <param name="symbol">The symbol of this data</param>
/// <param name="endTime">The end time of the bar</param>
/// <param name="rangeSize">The size of each range bar</param>
/// <param name="open">The opening price for the new bar</param>
/// <param name="high">The high price for the new bar</param>
/// <param name="low">The low price for the new bar</param>
/// <param name="close">The closing price for the new bar</param>
/// <param name="volume">The volume value for the new bar</param>
public RangeBar(Symbol symbol, DateTime endTime,
decimal rangeSize, decimal open, decimal? high = null, decimal? low = null, decimal? close = null, decimal volume = 0)
{
Symbol = symbol;
EndTime = endTime;
RangeSize = rangeSize;
Open = open;
Close = close ?? open;
Volume = volume;
High = high ?? open;
Low = low ?? open;
}
/// <summary>
/// Updates this <see cref="RangeBar"/> with the specified values
/// </summary>
/// <param name="time">The current time</param>
/// <param name="currentValue">The current value</param>
/// <param name="volumeSinceLastUpdate">The volume since the last update called on this instance</param>
public void Update(DateTime time, decimal currentValue, decimal volumeSinceLastUpdate)
{
EndTime = time;
if (currentValue < Low)
{
if ((High - currentValue) > RangeSize)
{
IsClosed = true;
Low = High - RangeSize;
Close = Low;
return;
}
else
{
Low = currentValue;
}
}
else if (currentValue > High)
{
if ((currentValue - Low) > RangeSize)
{
IsClosed = true;
High = Low + RangeSize;
Close = High;
return;
}
else
{
High = currentValue;
}
}
Volume += volumeSinceLastUpdate;
}
/// <summary>
/// Return a new instance clone of this object, used in fill forward
/// </summary>
/// <remarks>
/// This base implementation uses reflection to copy all public fields and properties
/// </remarks>
/// <returns>A clone of the current object</returns>
public override BaseData Clone()
{
return new RangeBar
{
RangeSize = RangeSize,
Open = Open,
Volume = Volume,
Close = Close,
EndTime = EndTime,
High = High,
IsClosed = IsClosed,
Low = Low,
Time = Time,
Value = Value,
Symbol = Symbol,
DataType = DataType
};
}
}
}
+183
View File
@@ -0,0 +1,183 @@
/*
* 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.Market
{
/// <summary>
/// Represents a bar sectioned not by time, but by some amount of movement in a value (for example, Closing price moving in $10 bar sizes)
/// </summary>
public class RenkoBar : BaseRenkoBar
{
/// <summary>
/// Gets the end time of this renko bar or the most recent update time if it <see cref="BaseRenkoBar.IsClosed"/>
/// </summary>
[Obsolete("RenkoBar.End is obsolete. Please use RenkoBar.EndTime property instead.")]
public DateTime End
{
get { return EndTime; }
set { EndTime = value; }
}
/// <summary>
/// The trend of the bar (i.e. Rising, Falling or NoDelta)
/// </summary>
public BarDirection Direction
{
get
{
if (Open < Close)
return BarDirection.Rising;
else if (Open > Close)
return BarDirection.Falling;
else
return BarDirection.NoDelta;
}
}
/// <summary>
/// The "spread" of the bar
/// </summary>
public decimal Spread
{
get
{
return Math.Abs(Close - Open);
}
}
/// <summary>
/// Initializes a new default instance of the <see cref="RenkoBar"/> class.
/// </summary>
public RenkoBar()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="RenkoBar"/> class with the specified values
/// </summary>
/// <param name="symbol">The symbol of this data</param>
/// <param name="time">The start time of the bar</param>
/// <param name="brickSize">The size of each renko brick</param>
/// <param name="open">The opening price for the new bar</param>
/// <param name="volume">Any initial volume associated with the data</param>
public RenkoBar(Symbol symbol, DateTime time, decimal brickSize,
decimal open, decimal volume)
{
Type = RenkoType.Classic;
Symbol = symbol;
Start = time;
EndTime = time;
BrickSize = brickSize;
Open = open;
Close = open;
Volume = volume;
High = open;
Low = open;
}
/// <summary>
/// Initializes a new instance of the <see cref="RenkoBar"/> class with the specified values
/// </summary>
/// <param name="symbol">The symbol of this data</param>
/// <param name="start">The start time of the bar</param>
/// <param name="endTime">The end time of the bar</param>
/// <param name="brickSize">The size of each wicko brick</param>
/// <param name="open">The opening price for the new bar</param>
/// <param name="high">The high price for the new bar</param>
/// <param name="low">The low price for the new bar</param>
/// <param name="close">The closing price for the new bar</param>
public RenkoBar(Symbol symbol, DateTime start, DateTime endTime,
decimal brickSize, decimal open, decimal high, decimal low, decimal close)
{
Type = RenkoType.Wicked;
Symbol = symbol;
Start = start;
EndTime = endTime;
BrickSize = brickSize;
Open = open;
Close = close;
Volume = 0;
High = high;
Low = low;
}
/// <summary>
/// Updates this <see cref="RenkoBar"/> with the specified values and returns whether or not this bar is closed
/// </summary>
/// <param name="time">The current time</param>
/// <param name="currentValue">The current value</param>
/// <param name="volumeSinceLastUpdate">The volume since the last update called on this instance</param>
/// <returns>True if this bar <see cref="BaseRenkoBar.IsClosed"/></returns>
public bool Update(DateTime time, decimal currentValue, decimal volumeSinceLastUpdate)
{
if (Type == RenkoType.Wicked)
throw new InvalidOperationException("A \"Wicked\" RenkoBar cannot be updated!");
// can't update a closed renko bar
if (IsClosed) return true;
if (Start == DateTime.MinValue) Start = time;
EndTime = time;
// compute the min/max closes this renko bar can have
decimal lowClose = Open - BrickSize;
decimal highClose = Open + BrickSize;
Close = Math.Min(highClose, Math.Max(lowClose, currentValue));
Volume += volumeSinceLastUpdate;
// determine if this data caused the bar to close
if (currentValue <= lowClose || currentValue >= highClose)
{
IsClosed = true;
}
if (Close > High) High = Close;
if (Close < Low) Low = Close;
return IsClosed;
}
/// <summary>
/// Return a new instance clone of this object, used in fill forward
/// </summary>
/// <remarks>
/// This base implementation uses reflection to copy all public fields and properties
/// </remarks>
/// <returns>A clone of the current object</returns>
public override BaseData Clone()
{
return new RenkoBar
{
Type = Type,
BrickSize = BrickSize,
Open = Open,
Volume = Volume,
Close = Close,
EndTime = EndTime,
High = High,
IsClosed = IsClosed,
Low = Low,
Time = Time,
Value = Value,
Symbol = Symbol,
DataType = DataType
};
}
}
}
+49
View File
@@ -0,0 +1,49 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace QuantConnect.Data.Market
{
/// <summary>
/// The type of the RenkoBar being created.
/// Used by RenkoConsolidator, ClassicRenkoConsolidator and VolumeRenkoConsolidator
/// </summary>
/// <remarks>Classic implementation was not entirely accurate for Renko consolidator
/// so we have replaced it with a new implementation and maintain the classic
/// for backwards compatibility and comparison.</remarks>
public enum RenkoType
{
/// <summary>
/// Indicates that the RenkoConsolidator works in its
/// original implementation; Specifically:
/// - It only returns a single bar, at most, irrespective of tick movement
/// - It will emit consecutive bars side by side
/// - By default even bars are created
/// (0)
/// </summary>
/// <remarks>the Classic mode has only been retained for
/// backwards compatibility with existing code.</remarks>
Classic,
/// <summary>
/// Indicates that the RenkoConsolidator works properly;
/// Specifically:
/// - returns zero or more bars per tick, as appropriate.
/// - Will not emit consecutive bars side by side
/// - Creates
/// (1)
/// </summary>
Wicked
}
}
+165
View File
@@ -0,0 +1,165 @@
/*
* 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.Indicators;
using QuantConnect.Securities;
using Common.Data.Consolidators;
namespace QuantConnect.Data.Market
{
/// <summary>
/// Provides a rolling window of <see cref="SessionBar"/> with size 2,
/// where [0] contains the current session values in progress (OHLCV + OpenInterest),
/// and [1] contains the fully consolidated data of the previous trading day.
/// </summary>
public class Session : RollingWindow<SessionBar>, IBar
{
private readonly Symbol _symbol;
private readonly TickType _tickType;
private readonly SecurityExchangeHours _exchangeHours;
private SessionConsolidator _consolidator;
/// <summary>
/// Opening price of the session
/// </summary>
public decimal Open => _consolidator?.WorkingInstance.Open ?? 0;
/// <summary>
/// High price of the session
/// </summary>
public decimal High => _consolidator?.WorkingInstance.High ?? 0;
/// <summary>
/// Low price of the session
/// </summary>
public decimal Low => _consolidator?.WorkingInstance.Low ?? 0;
/// <summary>
/// Closing price of the session
/// </summary>
public decimal Close => _consolidator?.WorkingInstance.Close ?? 0;
/// <summary>
/// Volume traded during the session
/// </summary>
public decimal Volume => _consolidator?.WorkingInstance.Volume ?? 0;
/// <summary>
/// Open Interest of the session
/// </summary>
public decimal OpenInterest => _consolidator?.WorkingInstance.OpenInterest ?? 0;
/// <summary>
/// The symbol of the session
/// </summary>
public Symbol Symbol => _symbol;
/// <summary>
/// The end time of the session
/// </summary>
public DateTime EndTime => _consolidator?.WorkingInstance.EndTime ?? default;
/// <summary>
/// Gets the size of this window
/// </summary>
public override int Size
{
set
{
base.Size = value;
TryInitialize();
}
}
/// <summary>
/// Initializes a new instance of the <see cref="Session"/> class
/// </summary>
/// <param name="tickType">The tick type to use</param>
/// <param name="exchangeHours">The exchange hours</param>
/// <param name="symbol">The symbol</param>
/// <param name="size">The number of items to hold</param>
public Session(TickType tickType, SecurityExchangeHours exchangeHours, Symbol symbol, int size = 0)
: base(size)
{
_symbol = symbol;
_tickType = tickType;
_exchangeHours = exchangeHours;
TryInitialize();
}
/// <summary>
/// Updates the session with new market data
/// </summary>
/// <param name="data">The new data to update the session with</param>
public void Update(BaseData data)
{
_consolidator?.Update(data);
}
private void OnConsolidated(object sender, IBaseData consolidated)
{
// Finished current trading day
// Add the new working session bar at [0], this will shift the previous trading day's bar to [1]
Add(_consolidator.WorkingInstance);
}
/// <summary>
/// Scans the consolidator to see if it should emit a bar due to time passing
/// </summary>
public void Scan(DateTime currentLocalTime)
{
// Delegates the scan decision to the underlying consolidator.
_consolidator?.ValidateAndScan(currentLocalTime);
}
/// <summary>
/// Resets the session
/// </summary>
public override void Reset()
{
if (_consolidator != null)
{
base.Reset();
_consolidator.Reset();
// We need to add the working session bar at [0]
Add(_consolidator.WorkingInstance);
}
}
/// <summary>
/// Returns a string representation of current session bar with OHLCV and OpenInterest values formatted.
/// Example: "O: 101.00 H: 112.00 L: 95.00 C: 110.00 V: 1005.00 OI: 12"
/// </summary>
public override string ToString()
{
if (_consolidator != null)
{
return _consolidator.WorkingInstance.ToString();
}
return string.Empty;
}
private void TryInitialize()
{
if (base.Size > 0 && _consolidator == null)
{
_consolidator = new SessionConsolidator(_exchangeHours, _tickType, _symbol);
_consolidator.DataConsolidated += OnConsolidated;
Add(_consolidator.WorkingInstance);
}
}
}
}
+229
View File
@@ -0,0 +1,229 @@
/*
* 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.Market
{
/// <summary>
/// Contains OHLCV data for a single session
/// </summary>
public class SessionBar : TradeBar
{
private DateTime _lastVolumeTime = DateTime.MinValue;
private QuoteBar _bar;
private readonly TickType _sourceTickType;
/// <summary>
/// Open Interest:
/// </summary>
public decimal OpenInterest { get; set; }
/// <summary>
/// Opening price of the bar: Defined as the price at the start of the time period.
/// </summary>
public override decimal Open => _bar?.Open ?? 0m;
/// <summary>
/// High price of the TradeBar during the time period.
/// </summary>
public override decimal High => _bar?.High ?? 0m;
/// <summary>
/// Low price of the TradeBar during the time period.
/// </summary>
public override decimal Low => _bar?.Low ?? 0m;
/// <summary>
/// Closing price of the TradeBar. Defined as the price at Start Time + TimeSpan.
/// </summary>
public override decimal Close => _bar?.Close ?? 0m;
/// <summary>
/// The closing time of this bar, computed via the Time and Period
/// </summary>
public override DateTime EndTime
{
get
{
if (Time == DateTime.MaxValue)
{
// Prevent overflow from Time + Period when Time is DateTime.MaxValue
return Time;
}
return base.EndTime;
}
}
/// <summary>
/// The period of this session bar
/// </summary>
public override TimeSpan Period { get; set; }
/// <summary>
/// Initializes a new instance of SessionBar with default values
/// </summary>
public SessionBar()
{
Period = QuantConnect.Time.OneDay;
}
/// <summary>
/// Initializes a new instance of SessionBar with a specific tick type
/// </summary>
public SessionBar(TickType sourceTickType)
{
_sourceTickType = sourceTickType;
Period = QuantConnect.Time.OneDay;
}
/// <summary>
/// Updates the session bar with new market data and initializes the first bar if needed
/// </summary>
/// <param name="data">The new data to update the session with</param>
/// <param name="consolidated">The current consolidated session bar</param>
public void Update(BaseData data, IBaseData consolidated)
{
InitializeBar(data, consolidated);
if (data.Time < _bar.Time)
{
// This will prevent overlapping
return;
}
if (data.DataType == MarketDataType.TradeBar && data is TradeBar tradeBar)
{
if (_lastVolumeTime <= tradeBar.Time)
{
_lastVolumeTime = tradeBar.EndTime;
Volume += tradeBar.Volume;
}
if (_sourceTickType == TickType.Trade)
{
if (Initialized == 0)
{
Initialized = 1;
_bar.Bid.Open = tradeBar.Open;
}
_bar.Bid.Close = tradeBar.Close;
if (tradeBar.Low < _bar.Bid.Low) _bar.Bid.Low = tradeBar.Low;
if (tradeBar.High > _bar.Bid.High) _bar.Bid.High = tradeBar.High;
_bar.Time = tradeBar.EndTime;
}
}
else if (_sourceTickType == TickType.Quote && data.DataType == MarketDataType.QuoteBar)
{
var quoteBar = (QuoteBar)data;
var bid = quoteBar.Bid;
var ask = quoteBar.Ask;
// update the bid and ask
if (bid != null)
{
if (_bar.Bid == null)
{
_bar.Bid = bid.Clone();
}
else
{
_bar.Bid.Close = bid.Close;
if (_bar.Bid.High < bid.High) _bar.Bid.High = bid.High;
if (_bar.Bid.Low > bid.Low) _bar.Bid.Low = bid.Low;
}
}
if (ask != null)
{
if (_bar.Ask == null)
{
_bar.Ask = ask.Clone();
}
else
{
_bar.Ask.Close = ask.Close;
if (_bar.Ask.High < ask.High) _bar.Ask.High = ask.High;
if (_bar.Ask.Low > ask.Low) _bar.Ask.Low = ask.Low;
}
}
_bar.Value = data.Value;
_bar.Time = data.EndTime;
}
else if (data.DataType == MarketDataType.Tick)
{
var tick = (Tick)data;
if (_lastVolumeTime <= data.Time)
{
_lastVolumeTime = data.EndTime;
Volume += tick.Quantity;
}
// update the bid and ask
if (_sourceTickType == tick.TickType)
{
if (tick.TickType == TickType.Trade)
{
if (Initialized == 0)
{
Initialized = 1;
_bar.Bid.Open = tick.Value;
}
_bar.Bid.Close = tick.Value;
if (tick.Value < _bar.Bid.Low) _bar.Bid.Low = tick.Value;
if (tick.Value > _bar.Bid.High) _bar.Bid.High = tick.Value;
}
else
{
_bar.Update(decimal.Zero, tick.BidPrice, tick.AskPrice, decimal.Zero, tick.BidSize, tick.AskSize);
}
_bar.Time = data.EndTime;
}
}
}
private void InitializeBar(BaseData data, IBaseData consolidated)
{
if (_bar == null)
{
_bar = new QuoteBar(data.Time.Date, data.Symbol, null, 0, null, 0, Period);
if (_sourceTickType == TickType.Trade)
{
_bar.Bid = new Bar(0, 0, decimal.MaxValue, 0);
}
else if (consolidated != null)
{
var previousBar = ((SessionBar)consolidated)._bar;
_bar.Update(decimal.Zero, previousBar?.Bid?.Close ?? decimal.Zero, previousBar?.Ask?.Close ?? decimal.Zero, decimal.Zero, decimal.Zero, decimal.Zero);
}
}
}
/// <summary>
/// Returns a string representation of the session bar with OHLCV and OpenInterest values formatted.
/// Example: "O: 101.00 H: 112.00 L: 95.00 C: 110.00 V: 1005.00 OI: 12"
/// </summary>
public override string ToString()
{
return $"O: {Open.SmartRounding()} " +
$"H: {High.SmartRounding()} " +
$"L: {Low.SmartRounding()} " +
$"C: {Close.SmartRounding()} " +
$"V: {Volume.SmartRounding()} " +
$"OI: {OpenInterest}";
}
}
}
+138
View File
@@ -0,0 +1,138 @@
/*
* 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 Newtonsoft.Json;
using ProtoBuf;
using static QuantConnect.StringExtensions;
namespace QuantConnect.Data.Market
{
/// <summary>
/// Split event from a security
/// </summary>
[ProtoContract(SkipConstructor = true)]
public class Split : BaseData
{
/// <summary>
///Gets the type of split event, warning or split.
/// </summary>
[JsonProperty]
[ProtoMember(10)]
public SplitType Type
{
get; private set;
}
/// <summary>
/// Gets the split factor
/// </summary>
[JsonProperty]
[ProtoMember(11)]
public decimal SplitFactor
{
get; set;
}
/// <summary>
/// Gets the price at which the split occurred
/// This is typically the previous day's closing price
/// </summary>
[ProtoMember(12)]
public decimal ReferencePrice
{
get { return Value; }
set { Value = value; }
}
/// <summary>
/// Initializes a new instance of the Split class
/// </summary>
public Split()
{
Type = SplitType.SplitOccurred;
DataType = MarketDataType.Auxiliary;
}
/// <summary>
/// Initializes a new instance of the Split class
/// </summary>
/// <param name="symbol">The symbol</param>
/// <param name="date">The date</param>
/// <param name="price">The price at the time of the split</param>
/// <param name="splitFactor">The split factor to be applied to current holdings</param>
/// <param name="type">The type of split event, warning or split occurred</param>
public Split(Symbol symbol, DateTime date, decimal price, decimal splitFactor, SplitType type)
: this()
{
Type = type;
Time = date;
Symbol = symbol;
ReferencePrice = price;
SplitFactor = splitFactor;
}
/// <summary>
/// Reader converts each line of the data source into BaseData objects. Each data type creates its own factory method, and returns a new instance of the object
/// each time it is called.
/// </summary>
/// <param name="config">Subscription data config setup object</param>
/// <param name="line">Line of the source document</param>
/// <param name="date">Date of the requested data</param>
/// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
/// <returns>Instance of the T:BaseData object generated by this line of the CSV</returns>
public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)
{
// this is implemented in the SubscriptionDataReader.CheckForSplit
throw new NotImplementedException("This method is not supposed to be called on the Split type.");
}
/// <summary>
/// Return the URL string source of the file. This will be converted to a stream
/// </summary>
/// <param name="config">Configuration object</param>
/// <param name="date">Date of this source file</param>
/// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
/// <returns>String URL of source file.</returns>
public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
{
// this data is derived from map files and factor files in backtesting
return null;
}
/// <summary>
/// Formats a string with the symbol and value.
/// </summary>
/// <returns>string - a string formatted as SPY: 167.753</returns>
public override string ToString()
{
var type = Type == SplitType.Warning ? "Split Warning" : "Split";
return Invariant($"{type}: {Symbol}: {SplitFactor} | {ReferencePrice}");
}
/// <summary>
/// Return a new instance clone of this object, used in fill forward
/// </summary>
/// <remarks>
/// This base implementation uses reflection to copy all public fields and properties
/// </remarks>
/// <returns>A clone of the current object</returns>
public override BaseData Clone()
{
return new Split(Symbol, Time, Price, SplitFactor, Type);
}
}
}
+42
View File
@@ -0,0 +1,42 @@
/*
* 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.Market
{
/// <summary>
/// Collection of splits keyed by <see cref="Symbol"/>
/// </summary>
public class Splits : DataDictionary<Split>
{
/// <summary>
/// Initializes a new instance of the <see cref="Splits"/> dictionary
/// </summary>
public Splits()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Splits"/> dictionary
/// </summary>
/// <param name="frontier">The time associated with the data in this dictionary</param>
public Splits(DateTime frontier)
: base(frontier)
{
}
}
}
+93
View File
@@ -0,0 +1,93 @@
/*
* 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.Market
{
/// <summary>
/// Symbol changed event of a security. This is generated when a symbol is remapped for a given
/// security, for example, at EOD 2014.04.02 GOOG turned into GOOGL, but are the same
/// </summary>
public class SymbolChangedEvent : BaseData
{
/// <summary>
/// Gets the symbol before the change
/// </summary>
public string OldSymbol { get; private set; }
/// <summary>
/// Gets the symbol after the change
/// </summary>
public string NewSymbol { get; private set; }
/// <summary>
/// Initializes a new default instance of the <see cref="SymbolChangedEvent"/> class
/// </summary>
public SymbolChangedEvent()
{
DataType = MarketDataType.Auxiliary;
}
/// <summary>
/// Initializes a new instance of the <see cref="SymbolChangedEvent"/>
/// </summary>
/// <param name="requestedSymbol">The symbol that was originally requested</param>
/// <param name="date">The date/time this symbol remapping took place</param>
/// <param name="oldSymbol">The old symbol mapping</param>
/// <param name="newSymbol">The new symbol mapping</param>
public SymbolChangedEvent(Symbol requestedSymbol, DateTime date, string oldSymbol, string newSymbol)
: this()
{
Time = date;
Symbol = requestedSymbol;
OldSymbol = oldSymbol;
NewSymbol = newSymbol;
}
/// <summary>
/// Return the URL string source of the file. This will be converted to a stream
/// </summary>
/// <param name="config">Configuration object</param>
/// <param name="date">Date of this source file</param>
/// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
/// <returns>String URL of source file.</returns>
public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
{
return null;
}
/// <summary>
/// Return a new instance clone of this object, used in fill forward
/// </summary>
/// <remarks>
/// This base implementation uses reflection to copy all public fields and properties
/// </remarks>
/// <returns>A clone of the current object</returns>
public override BaseData Clone()
{
return new SymbolChangedEvent(Symbol, Time, OldSymbol, NewSymbol);
}
/// <summary>
/// Friendly string representation of this symbol changed event
/// </summary>
public override string ToString()
{
return $"{Time} {OldSymbol}->{NewSymbol}";
}
}
}
+42
View File
@@ -0,0 +1,42 @@
/*
* 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.Market
{
/// <summary>
/// Collection of <see cref="SymbolChangedEvent"/> keyed by the original, requested symbol
/// </summary>
public class SymbolChangedEvents : DataDictionary<SymbolChangedEvent>
{
/// <summary>
/// Initializes a new instance of the <see cref="SymbolChangedEvent"/> dictionary
/// </summary>
public SymbolChangedEvents()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SymbolChangedEvent"/> dictionary
/// </summary>
/// <param name="frontier">The time associated with the data in this dictionary</param>
public SymbolChangedEvents(DateTime frontier)
: base(frontier)
{
}
}
}
+855
View File
@@ -0,0 +1,855 @@
/*
* 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 ProtoBuf;
using System.IO;
using Newtonsoft.Json;
using QuantConnect.Util;
using QuantConnect.Logging;
using System.Globalization;
using System.Runtime.CompilerServices;
using QuantConnect.Python;
namespace QuantConnect.Data.Market
{
/// <summary>
/// Tick class is the base representation for tick data. It is grouped into a Ticks object
/// which implements IDictionary and passed into an OnData event handler.
/// </summary>
[ProtoContract(SkipConstructor = true)]
[ProtoInclude(1000, typeof(OpenInterest))]
public class Tick : BaseData
{
private Exchange _exchange = QuantConnect.Exchange.UNKNOWN;
private string _exchangeValue;
private uint? _parsedSaleCondition;
/// <summary>
/// Type of the Tick: Trade or Quote.
/// </summary>
[ProtoMember(10)]
[PandasIgnore]
public TickType TickType { get; set; } = TickType.Trade;
/// <summary>
/// Quantity exchanged in a trade.
/// </summary>
[ProtoMember(11)]
public decimal Quantity { get; set; }
/// <summary>
/// Exchange code this tick came from <see cref="Exchanges"/>
/// </summary>
[PandasIgnore]
public string ExchangeCode
{
get
{
if (_exchange == null)
{
_exchange = Symbol != null
? _exchangeValue.GetPrimaryExchange(Symbol.SecurityType, Symbol.ID.Market) : _exchangeValue.GetPrimaryExchange();
}
return _exchange.Code;
}
set
{
_exchangeValue = value;
_exchange = null;
}
}
/// <summary>
/// Exchange name this tick came from <see cref="Exchanges"/>
/// </summary>
[ProtoMember(12)]
public string Exchange
{
get
{
if (_exchange == null)
{
_exchange = Symbol != null
? _exchangeValue.GetPrimaryExchange(Symbol.SecurityType, Symbol.ID.Market) : _exchangeValue.GetPrimaryExchange();
}
return _exchange;
}
set
{
_exchangeValue = value;
_exchange = null;
}
}
/// <summary>
/// Sale condition for the tick.
/// </summary>
[PandasIgnore]
[ProtoMember(13)]
public string SaleCondition { get; set; } = string.Empty;
/// <summary>
/// For performance parsed sale condition for the tick.
/// </summary>
[JsonIgnore]
[PandasIgnore]
public uint ParsedSaleCondition
{
get
{
if (string.IsNullOrEmpty(SaleCondition))
{
return 0;
}
if (!_parsedSaleCondition.HasValue)
{
_parsedSaleCondition = uint.Parse(SaleCondition, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
}
return _parsedSaleCondition.Value;
}
set
{
_parsedSaleCondition = value;
}
}
/// <summary>
/// Bool whether this is a suspicious tick
/// </summary>
[ProtoMember(14)]
public bool Suspicious { get; set; }
/// <summary>
/// Bid Price for Tick
/// </summary>
[ProtoMember(15)]
public decimal BidPrice { get; set; }
/// <summary>
/// Asking price for the Tick quote.
/// </summary>
[ProtoMember(16)]
public decimal AskPrice { get; set; }
/// <summary>
/// Alias for "Value" - the last sale for this asset.
/// </summary>
public decimal LastPrice
{
get
{
return Value;
}
}
/// <summary>
/// Size of bid quote.
/// </summary>
[ProtoMember(17)]
public decimal BidSize { get; set; }
/// <summary>
/// Size of ask quote.
/// </summary>
[ProtoMember(18)]
public decimal AskSize { get; set; }
//In Base Class: Alias of Closing:
//public decimal Price;
//Symbol of Asset.
//In Base Class: public Symbol Symbol;
//In Base Class: DateTime Of this TradeBar
//public DateTime Time;
/// <summary>
/// Initialize tick class with a default constructor.
/// </summary>
public Tick()
{
Value = 0;
Time = new DateTime();
DataType = MarketDataType.Tick;
Symbol = Symbol.Empty;
TickType = TickType.Trade;
Quantity = 0;
_exchange = QuantConnect.Exchange.UNKNOWN;
SaleCondition = string.Empty;
Suspicious = false;
BidSize = 0;
AskSize = 0;
}
/// <summary>
/// Cloner constructor for fill forward engine implementation. Clone the original tick into this new tick:
/// </summary>
/// <param name="original">Original tick we're cloning</param>
public Tick(Tick original)
{
Symbol = original.Symbol;
Time = new DateTime(original.Time.Ticks);
Value = original.Value;
BidPrice = original.BidPrice;
AskPrice = original.AskPrice;
// directly set privates so we don't parse the exchange
_exchange = original._exchange;
_exchangeValue = original._exchangeValue;
SaleCondition = original.SaleCondition;
_parsedSaleCondition = original._parsedSaleCondition;
Quantity = original.Quantity;
Suspicious = original.Suspicious;
DataType = MarketDataType.Tick;
TickType = original.TickType;
BidSize = original.BidSize;
AskSize = original.AskSize;
}
/// <summary>
/// Constructor for a FOREX tick where there is no last sale price. The volume in FX is so high its rare to find FX trade data.
/// To fake this the tick contains bid-ask prices and the last price is the midpoint.
/// </summary>
/// <param name="time">Full date and time</param>
/// <param name="symbol">Underlying currency pair we're trading</param>
/// <param name="bid">FX tick bid value</param>
/// <param name="ask">FX tick ask value</param>
public Tick(DateTime time, Symbol symbol, decimal bid, decimal ask)
{
DataType = MarketDataType.Tick;
Time = time;
Symbol = symbol;
Value = (bid + ask) / 2;
TickType = TickType.Quote;
BidPrice = bid;
AskPrice = ask;
}
/// <summary>
/// Initializes a new instance of the <see cref="Tick"/> class to <see cref="TickType.OpenInterest"/>.
/// </summary>
/// <param name="time">The time at which the open interest tick occurred.</param>
/// <param name="symbol">The symbol associated with the open interest tick.</param>
/// <param name="openInterest">The value of the open interest for the specified symbol.</param>
public Tick(DateTime time, Symbol symbol, decimal openInterest)
{
Time = time;
Symbol = symbol;
Value = openInterest;
DataType = MarketDataType.Tick;
TickType = TickType.OpenInterest;
}
/// <summary>
/// Initializer for a last-trade equity tick with bid or ask prices.
/// </summary>
/// <param name="time">Full date and time</param>
/// <param name="symbol">Underlying equity security symbol</param>
/// <param name="bid">Bid value</param>
/// <param name="ask">Ask value</param>
/// <param name="last">Last trade price</param>
public Tick(DateTime time, Symbol symbol, decimal last, decimal bid, decimal ask)
{
DataType = MarketDataType.Tick;
Time = time;
Symbol = symbol;
Value = last;
TickType = TickType.Quote;
BidPrice = bid;
AskPrice = ask;
}
/// <summary>
/// Trade tick type constructor
/// </summary>
/// <param name="time">Full date and time</param>
/// <param name="symbol">Underlying equity security symbol</param>
/// <param name="saleCondition">The ticks sale condition</param>
/// <param name="exchange">The ticks exchange</param>
/// <param name="quantity">The quantity traded</param>
/// <param name="price">The price of the trade</param>
public Tick(DateTime time, Symbol symbol, string saleCondition, string exchange, decimal quantity, decimal price)
{
Value = price;
Time = time;
DataType = MarketDataType.Tick;
Symbol = symbol;
TickType = TickType.Trade;
Quantity = quantity;
Exchange = exchange;
SaleCondition = saleCondition;
Suspicious = false;
}
/// <summary>
/// Trade tick type constructor
/// </summary>
/// <param name="time">Full date and time</param>
/// <param name="symbol">Underlying equity security symbol</param>
/// <param name="saleCondition">The ticks sale condition</param>
/// <param name="exchange">The ticks exchange</param>
/// <param name="quantity">The quantity traded</param>
/// <param name="price">The price of the trade</param>
public Tick(DateTime time, Symbol symbol, string saleCondition, Exchange exchange, decimal quantity, decimal price)
: this(time, symbol, saleCondition, string.Empty, quantity, price)
{
// we were giving the exchange, set it directly
_exchange = exchange;
}
/// <summary>
/// Quote tick type constructor
/// </summary>
/// <param name="time">Full date and time</param>
/// <param name="symbol">Underlying equity security symbol</param>
/// <param name="saleCondition">The ticks sale condition</param>
/// <param name="exchange">The ticks exchange</param>
/// <param name="bidSize">The bid size</param>
/// <param name="bidPrice">The bid price</param>
/// <param name="askSize">The ask size</param>
/// <param name="askPrice">The ask price</param>
public Tick(DateTime time, Symbol symbol, string saleCondition, string exchange, decimal bidSize, decimal bidPrice, decimal askSize, decimal askPrice)
{
Time = time;
DataType = MarketDataType.Tick;
Symbol = symbol;
TickType = TickType.Quote;
Exchange = exchange;
SaleCondition = saleCondition;
Suspicious = false;
AskPrice = askPrice;
AskSize = askSize;
BidPrice = bidPrice;
BidSize = bidSize;
SetValue();
}
/// <summary>
/// Quote tick type constructor
/// </summary>
/// <param name="time">Full date and time</param>
/// <param name="symbol">Underlying equity security symbol</param>
/// <param name="bidSize">The bid size</param>
/// <param name="bidPrice">The bid price</param>
/// <param name="askSize">The ask size</param>
/// <param name="askPrice">The ask price</param>
public Tick(DateTime time, Symbol symbol, decimal bidSize, decimal bidPrice, decimal askSize, decimal askPrice)
: this(time, symbol, string.Empty, string.Empty, bidSize, bidPrice, askSize, askPrice)
{
}
/// <summary>
/// Quote tick type constructor
/// </summary>
/// <param name="time">Full date and time</param>
/// <param name="symbol">Underlying equity security symbol</param>
/// <param name="saleCondition">The ticks sale condition</param>
/// <param name="exchange">The ticks exchange</param>
/// <param name="bidSize">The bid size</param>
/// <param name="bidPrice">The bid price</param>
/// <param name="askSize">The ask size</param>
/// <param name="askPrice">The ask price</param>
public Tick(DateTime time, Symbol symbol, string saleCondition, Exchange exchange, decimal bidSize, decimal bidPrice, decimal askSize, decimal askPrice)
: this(time, symbol, saleCondition, string.Empty, bidSize, bidPrice, askSize, askPrice)
{
// we were giving the exchange, set it directly
_exchange = exchange;
}
/// <summary>
/// Constructor for QuantConnect FXCM Data source:
/// </summary>
/// <param name="symbol">Symbol for underlying asset</param>
/// <param name="line">CSV line of data from FXCM</param>
public Tick(Symbol symbol, string line)
{
var csv = line.Split(',');
DataType = MarketDataType.Tick;
Symbol = symbol;
Time = DateTime.ParseExact(csv[0], DateFormat.Forex, CultureInfo.InvariantCulture);
Value = (BidPrice + AskPrice) / 2;
TickType = TickType.Quote;
BidPrice = Convert.ToDecimal(csv[1], CultureInfo.InvariantCulture);
AskPrice = Convert.ToDecimal(csv[2], CultureInfo.InvariantCulture);
}
/// <summary>
/// Constructor for QuantConnect tick data
/// </summary>
/// <param name="symbol">Symbol for underlying asset</param>
/// <param name="line">CSV line of data from QC tick csv</param>
/// <param name="baseDate">The base date of the tick</param>
public Tick(Symbol symbol, string line, DateTime baseDate)
{
var csv = line.Split(',');
DataType = MarketDataType.Tick;
Symbol = symbol;
Time = baseDate.Date.AddTicks(Convert.ToInt64(10000 * csv[0].ToDecimal()));
Value = csv[1].ToDecimal() / GetScaleFactor(symbol);
TickType = TickType.Trade;
Quantity = csv[2].ToDecimal();
Exchange = csv[3].Trim();
SaleCondition = csv[4];
Suspicious = csv[5].ToInt32() == 1;
}
/// <summary>
/// Parse a tick data line from quantconnect zip source files.
/// </summary>
/// <param name="reader">The source stream reader</param>
/// <param name="date">Base date for the tick (ticks date is stored as int milliseconds since midnight)</param>
/// <param name="config">Subscription configuration object</param>
public Tick(SubscriptionDataConfig config, StreamReader reader, DateTime date)
{
try
{
DataType = MarketDataType.Tick;
Symbol = config.Symbol;
// Which security type is this data feed:
var scaleFactor = GetScaleFactor(config.Symbol);
switch (config.SecurityType)
{
case SecurityType.Equity:
{
TickType = config.TickType;
Time = date.Date.AddTicks(Convert.ToInt64(10000 * reader.GetDecimal())).ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
bool pastLineEnd;
if (TickType == TickType.Trade)
{
Value = reader.GetDecimal() / scaleFactor;
Quantity = reader.GetDecimal(out pastLineEnd);
if (!pastLineEnd)
{
Exchange = reader.GetString();
SaleCondition = reader.GetString();
Suspicious = reader.GetInt32() == 1;
}
}
else if (TickType == TickType.Quote)
{
BidPrice = reader.GetDecimal() / scaleFactor;
BidSize = reader.GetDecimal();
AskPrice = reader.GetDecimal() / scaleFactor;
AskSize = reader.GetDecimal(out pastLineEnd);
SetValue();
if (!pastLineEnd)
{
Exchange = reader.GetString();
SaleCondition = reader.GetString();
Suspicious = reader.GetInt32() == 1;
}
}
else
{
throw new InvalidOperationException($"Tick(): Unexpected tick type {TickType}");
}
break;
}
case SecurityType.Forex:
case SecurityType.Cfd:
{
TickType = TickType.Quote;
Time = date.Date.AddTicks(Convert.ToInt64(10000 * reader.GetDecimal()))
.ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
BidPrice = reader.GetDecimal();
AskPrice = reader.GetDecimal();
SetValue();
break;
}
case SecurityType.CryptoFuture:
case SecurityType.Crypto:
{
TickType = config.TickType;
Exchange = config.Market;
Time = date.Date.AddTicks(Convert.ToInt64(10000 * reader.GetDecimal()))
.ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
if (TickType == TickType.Trade)
{
Value = reader.GetDecimal();
Quantity = reader.GetDecimal(out var endOfLine);
Suspicious = !endOfLine && reader.GetInt32() == 1;
}
else if(TickType == TickType.Quote)
{
BidPrice = reader.GetDecimal();
BidSize = reader.GetDecimal();
AskPrice = reader.GetDecimal();
AskSize = reader.GetDecimal(out var endOfLine);
Suspicious = !endOfLine && reader.GetInt32() == 1;
SetValue();
}
break;
}
case SecurityType.Future:
case SecurityType.Option:
case SecurityType.FutureOption:
case SecurityType.IndexOption:
{
TickType = config.TickType;
Time = date.Date.AddTicks(Convert.ToInt64(10000 * reader.GetDecimal()))
.ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
if (TickType == TickType.Trade)
{
Value = reader.GetDecimal() / scaleFactor;
Quantity = reader.GetDecimal();
Exchange = reader.GetString();
SaleCondition = reader.GetString();
Suspicious = reader.GetInt32() == 1;
}
else if (TickType == TickType.OpenInterest)
{
Value = reader.GetDecimal();
}
else
{
BidPrice = reader.GetDecimal() / scaleFactor;
BidSize = reader.GetDecimal();
AskPrice = reader.GetDecimal() / scaleFactor;
AskSize = reader.GetDecimal();
Exchange = reader.GetString();
Suspicious = reader.GetInt32() == 1;
SetValue();
}
break;
}
}
}
catch (Exception err)
{
Log.Error(err);
}
}
/// <summary>
/// Parse a tick data line from quantconnect zip source files.
/// </summary>
/// <param name="line">CSV source line of the compressed source</param>
/// <param name="date">Base date for the tick (ticks date is stored as int milliseconds since midnight)</param>
/// <param name="config">Subscription configuration object</param>
public Tick(SubscriptionDataConfig config, string line, DateTime date)
{
try
{
DataType = MarketDataType.Tick;
Symbol = config.Symbol;
// Which security type is this data feed:
var scaleFactor = GetScaleFactor(config.Symbol);
switch (config.SecurityType)
{
case SecurityType.Equity:
{
var index = 0;
TickType = config.TickType;
var csv = line.ToCsv(TickType == TickType.Trade ? 6 : 8);
Time = date.Date.AddTicks(Convert.ToInt64(10000 * csv[index++].ToDecimal())).ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
if (TickType == TickType.Trade)
{
Value = csv[index++].ToDecimal() / scaleFactor;
Quantity = csv[index++].ToDecimal();
if (csv.Count > index)
{
Exchange = csv[index++];
SaleCondition = csv[index++];
Suspicious = (csv[index++] == "1");
}
}
else if (TickType == TickType.Quote)
{
BidPrice = csv[index++].ToDecimal() / scaleFactor;
BidSize = csv[index++].ToDecimal();
AskPrice = csv[index++].ToDecimal() / scaleFactor;
AskSize = csv[index++].ToDecimal();
SetValue();
if (csv.Count > index)
{
Exchange = csv[index++];
SaleCondition = csv[index++];
Suspicious = (csv[index++] == "1");
}
}
else
{
throw new InvalidOperationException($"Tick(): Unexpected tick type {TickType}");
}
break;
}
case SecurityType.Forex:
case SecurityType.Cfd:
{
var csv = line.ToCsv(3);
TickType = TickType.Quote;
var ticks = (long)(csv[0].ToDecimal() * TimeSpan.TicksPerMillisecond);
Time = date.Date.AddTicks(ticks)
.ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
BidPrice = csv[1].ToDecimal();
AskPrice = csv[2].ToDecimal();
SetValue();
break;
}
case SecurityType.Crypto:
case SecurityType.CryptoFuture:
{
TickType = config.TickType;
Exchange = config.Market;
if (TickType == TickType.Trade)
{
var csv = line.ToCsv(3);
Time = date.Date.AddTicks(Convert.ToInt64(10000 * csv[0].ToDecimal()))
.ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
Value = csv[1].ToDecimal();
Quantity = csv[2].ToDecimal();
Suspicious = csv.Count >= 4 && csv[3] == "1";
}
if (TickType == TickType.Quote)
{
var csv = line.ToCsv(6);
Time = date.Date.AddTicks(Convert.ToInt64(10000 * csv[0].ToDecimal()))
.ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
BidPrice = csv[1].ToDecimal();
BidSize = csv[2].ToDecimal();
AskPrice = csv[3].ToDecimal();
AskSize = csv[4].ToDecimal();
Suspicious = csv.Count >= 6 && csv[5] == "1";
SetValue();
}
break;
}
case SecurityType.Future:
case SecurityType.Option:
case SecurityType.FutureOption:
case SecurityType.IndexOption:
{
var csv = line.ToCsv(7);
TickType = config.TickType;
Time = date.Date.AddTicks(Convert.ToInt64(10000 * csv[0].ToDecimal()))
.ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
if (TickType == TickType.Trade)
{
Value = csv[1].ToDecimal()/scaleFactor;
Quantity = csv[2].ToDecimal();
Exchange = csv[3];
SaleCondition = csv[4];
Suspicious = csv[5] == "1";
}
else if (TickType == TickType.OpenInterest)
{
Value = csv[1].ToDecimal();
}
else
{
if (csv[1].Length != 0)
{
BidPrice = csv[1].ToDecimal()/scaleFactor;
BidSize = csv[2].ToDecimal();
}
if (csv[3].Length != 0)
{
AskPrice = csv[3].ToDecimal()/scaleFactor;
AskSize = csv[4].ToDecimal();
}
Exchange = csv[5];
Suspicious = csv[6] == "1";
SetValue();
}
break;
}
}
}
catch (Exception err)
{
Log.Error(err);
}
}
/// <summary>
/// Tick implementation of reader method: read a line of data from the source and convert it to a tick object.
/// </summary>
/// <param name="config">Subscription configuration object for algorithm</param>
/// <param name="line">Line from the datafeed source</param>
/// <param name="date">Date of this reader request</param>
/// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
/// <returns>New Initialized tick</returns>
public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)
{
if (isLiveMode)
{
// currently ticks don't come through the reader function
return new Tick();
}
return new Tick(config, line, date);
}
/// <summary>
/// Tick implementation of reader method: read a line of data from the source and convert it to a tick object.
/// </summary>
/// <param name="config">Subscription configuration object for algorithm</param>
/// <param name="stream">The source stream reader</param>
/// <param name="date">Date of this reader request</param>
/// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
/// <returns>New Initialized tick</returns>
[StubsIgnore]
public override BaseData Reader(SubscriptionDataConfig config, StreamReader stream, DateTime date, bool isLiveMode)
{
if (isLiveMode)
{
// currently ticks don't come through the reader function
return new Tick();
}
return new Tick(config, stream, date);
}
/// <summary>
/// Get source for tick data feed - not used with QuantConnect data sources implementation.
/// </summary>
/// <param name="config">Configuration object</param>
/// <param name="date">Date of this source request if source spread across multiple files</param>
/// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
/// <returns>String source location of the file to be opened with a stream</returns>
public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
{
if (isLiveMode)
{
// this data type is streamed in live mode
return new SubscriptionDataSource(string.Empty, SubscriptionTransportMedium.Streaming);
}
var source = LeanData.GenerateZipFilePath(Globals.DataFolder, config.Symbol, date, config.Resolution, config.TickType);
if (config.SecurityType == SecurityType.Future || config.SecurityType.IsOption())
{
source += "#" + LeanData.GenerateZipEntryName(config.Symbol, date, config.Resolution, config.TickType);
}
return new SubscriptionDataSource(source, SubscriptionTransportMedium.LocalFile, FileFormat.Csv);
}
/// <summary>
/// Update the tick price information - not used.
/// </summary>
/// <param name="lastTrade">This trade price</param>
/// <param name="bidPrice">Current bid price</param>
/// <param name="askPrice">Current asking price</param>
/// <param name="volume">Volume of this trade</param>
/// <param name="bidSize">The size of the current bid, if available</param>
/// <param name="askSize">The size of the current ask, if available</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Update(decimal lastTrade, decimal bidPrice, decimal askPrice, decimal volume, decimal bidSize, decimal askSize)
{
Value = lastTrade;
BidPrice = bidPrice;
AskPrice = askPrice;
BidSize = bidSize;
AskSize = askSize;
Quantity = Convert.ToDecimal(volume);
}
/// <summary>
/// Check if tick contains valid data (either a trade, or a bid or ask)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsValid()
{
// Indexes have zero volume in live trading, but is still a valid tick.
return (TickType == TickType.Trade && (LastPrice > 0.0m && (Quantity > 0 || Symbol.SecurityType == SecurityType.Index))) ||
(TickType == TickType.Quote && AskPrice > 0.0m && AskSize > 0) ||
(TickType == TickType.Quote && BidPrice > 0.0m && BidSize > 0) ||
(TickType == TickType.OpenInterest && Value > 0);
}
/// <summary>
/// Clone implementation for tick class:
/// </summary>
/// <returns>New tick object clone of the current class values.</returns>
public override BaseData Clone()
{
return new Tick(this);
}
/// <summary>
/// Formats a string with the symbol and value.
/// </summary>
/// <returns>string - a string formatted as SPY: 167.753</returns>
public override string ToString()
{
switch (TickType)
{
case TickType.Trade:
return $"{Symbol}: Price: {Price} Quantity: {Quantity}";
case TickType.Quote:
return $"{Symbol}: Bid: {BidSize}@{BidPrice} Ask: {AskSize}@{AskPrice}";
case TickType.OpenInterest:
return $"{Symbol}: OpenInterest: {Value}";
default:
throw new ArgumentOutOfRangeException();
}
}
/// <summary>
/// Sets the tick Value based on ask and bid price
/// </summary>
public void SetValue()
{
Value = BidPrice + AskPrice;
if (BidPrice * AskPrice != 0)
{
Value /= 2m;
}
}
/// <summary>
/// Gets the scaling factor according to the <see cref="SecurityType"/> of the <see cref="Symbol"/> provided.
/// Non-equity data will not be scaled, including options with an underlying non-equity asset class.
/// </summary>
/// <param name="symbol">Symbol to get scaling factor for</param>
/// <returns>Scaling factor</returns>
private static decimal GetScaleFactor(Symbol symbol)
{
return symbol.SecurityType == SecurityType.Equity || symbol.SecurityType == SecurityType.Option ? 10000m : 1;
}
}
}
+43
View File
@@ -0,0 +1,43 @@
/*
* 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.Market
{
/// <summary>
/// Ticks collection which implements an IDictionary-string-list of ticks. This way users can iterate over the string indexed ticks of the requested symbol.
/// </summary>
/// <remarks>Ticks are timestamped to the nearest second in QuantConnect</remarks>
public class Ticks : DataDictionary<List<Tick>>
{
/// <summary>
/// Initializes a new instance of the <see cref="Ticks"/> dictionary
/// </summary>
public Ticks()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Ticks"/> dictionary
/// </summary>
/// <param name="frontier">The time associated with the data in this dictionary</param>
public Ticks(DateTime frontier)
: base(frontier)
{
}
}
}
+883
View File
@@ -0,0 +1,883 @@
/*
* 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 ProtoBuf;
using System.IO;
using System.Threading;
using QuantConnect.Util;
using System.Globalization;
using QuantConnect.Logging;
using static QuantConnect.StringExtensions;
using QuantConnect.Python;
namespace QuantConnect.Data.Market
{
/// <summary>
/// TradeBar class for second and minute resolution data:
/// An OHLC implementation of the QuantConnect BaseData class with parameters for candles.
/// </summary>
[ProtoContract(SkipConstructor = true)]
public class TradeBar : BaseData, IBaseDataBar
{
// scale factor used in QC equity/forex data files
private const decimal _scaleFactor = 1 / 10000m;
protected int Initialized;
private decimal _open;
private decimal _high;
private decimal _low;
/// <summary>
/// Volume:
/// </summary>
[ProtoMember(101)]
public virtual decimal Volume { get; set; }
/// <summary>
/// Opening price of the bar: Defined as the price at the start of the time period.
/// </summary>
[ProtoMember(102)]
public virtual decimal Open
{
get { return _open; }
set
{
Initialize(value);
_open = value;
}
}
/// <summary>
/// High price of the TradeBar during the time period.
/// </summary>
[ProtoMember(103)]
public virtual decimal High
{
get { return _high; }
set
{
Initialize(value);
_high = value;
}
}
/// <summary>
/// Low price of the TradeBar during the time period.
/// </summary>
[ProtoMember(104)]
public virtual decimal Low
{
get { return _low; }
set
{
Initialize(value);
_low = value;
}
}
/// <summary>
/// Closing price of the TradeBar. Defined as the price at Start Time + TimeSpan.
/// </summary>
[ProtoMember(105)]
public virtual decimal Close
{
get { return Value; }
set
{
Initialize(value);
Value = value;
}
}
/// <summary>
/// The closing time of this bar, computed via the Time and Period
/// </summary>
[PandasIgnore]
public override DateTime EndTime
{
get { return Time + Period; }
set { Period = value - Time; }
}
/// <summary>
/// The period of this trade bar, (second, minute, daily, ect...)
/// </summary>
[ProtoMember(106)]
[PandasIgnore]
public virtual TimeSpan Period { get; set; }
//In Base Class: Alias of Closing:
//public decimal Price;
//Symbol of Asset.
//In Base Class: public Symbol Symbol;
//In Base Class: DateTime Of this TradeBar
//public DateTime Time;
/// <summary>
/// Default initializer to setup an empty tradebar.
/// </summary>
public TradeBar()
{
Symbol = Symbol.Empty;
DataType = MarketDataType.TradeBar;
Period = QuantConnect.Time.OneMinute;
}
/// <summary>
/// Cloner constructor for implementing fill forward.
/// Return a new instance with the same values as this original.
/// </summary>
/// <param name="original">Original tradebar object we seek to clone</param>
public TradeBar(TradeBar original)
{
DataType = MarketDataType.TradeBar;
Time = new DateTime(original.Time.Ticks);
Symbol = original.Symbol;
Value = original.Close;
Open = original.Open;
High = original.High;
Low = original.Low;
Close = original.Close;
Volume = original.Volume;
Period = original.Period;
Initialized = 1;
}
/// <summary>
/// Initialize Trade Bar with OHLC Values:
/// </summary>
/// <param name="time">DateTime Timestamp of the bar</param>
/// <param name="symbol">Market MarketType Symbol</param>
/// <param name="open">Decimal Opening Price</param>
/// <param name="high">Decimal High Price of this bar</param>
/// <param name="low">Decimal Low Price of this bar</param>
/// <param name="close">Decimal Close price of this bar</param>
/// <param name="volume">Volume sum over day</param>
/// <param name="period">The period of this bar, specify null for default of 1 minute</param>
public TradeBar(DateTime time, Symbol symbol, decimal open, decimal high, decimal low, decimal close, decimal volume, TimeSpan? period = null)
{
Time = time;
Symbol = symbol;
Value = close;
Open = open;
High = high;
Low = low;
Close = close;
Volume = volume;
Period = period ?? QuantConnect.Time.OneMinute;
DataType = MarketDataType.TradeBar;
Initialized = 1;
}
/// <summary>
/// TradeBar Reader: Fetch the data from the QC storage and feed it line by line into the engine.
/// </summary>
/// <param name="config">Symbols, Resolution, DataType, </param>
/// <param name="line">Line from the data file requested</param>
/// <param name="date">Date of this reader request</param>
/// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
/// <returns>Enumerable iterator for returning each line of the required data.</returns>
public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)
{
//Handle end of file:
if (line == null)
{
return null;
}
if (isLiveMode)
{
return new TradeBar();
}
try
{
switch (config.SecurityType)
{
//Equity File Data Format:
case SecurityType.Equity:
return ParseEquity(config, line, date);
//FOREX has a different data file format:
case SecurityType.Forex:
return ParseForex(config, line, date);
case SecurityType.Crypto:
case SecurityType.CryptoFuture:
return ParseCrypto(config, line, date);
case SecurityType.Cfd:
return ParseCfd(config, line, date);
case SecurityType.Index:
return ParseIndex(config, line, date);
case SecurityType.Option:
case SecurityType.FutureOption:
case SecurityType.IndexOption:
return ParseOption(config, line, date);
case SecurityType.Future:
return ParseFuture(config, line, date);
}
}
catch (Exception err)
{
Log.Error(Invariant($"TradeBar.Reader(): Error parsing line: '{line}', Symbol: {config.Symbol.Value}, SecurityType: ") +
Invariant($"{config.SecurityType}, Resolution: {config.Resolution}, Date: {date:yyyy-MM-dd}, Message: {err}")
);
}
// if we couldn't parse it above return a default instance
return new TradeBar { Symbol = config.Symbol, Period = config.Increment };
}
/// <summary>
/// TradeBar Reader: Fetch the data from the QC storage and feed it directly from the stream into the engine.
/// </summary>
/// <param name="config">Symbols, Resolution, DataType, </param>
/// <param name="stream">The file data stream</param>
/// <param name="date">Date of this reader request</param>
/// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
/// <returns>Enumerable iterator for returning each line of the required data.</returns>
[StubsIgnore]
public override BaseData Reader(SubscriptionDataConfig config, StreamReader stream, DateTime date, bool isLiveMode)
{
//Handle end of file:
if (stream == null || stream.EndOfStream)
{
return null;
}
if (isLiveMode)
{
return new TradeBar();
}
try
{
switch (config.SecurityType)
{
//Equity File Data Format:
case SecurityType.Equity:
return ParseEquity(config, stream, date);
//FOREX has a different data file format:
case SecurityType.Forex:
return ParseForex(config, stream, date);
case SecurityType.Crypto:
case SecurityType.CryptoFuture:
return ParseCrypto(config, stream, date);
case SecurityType.Index:
return ParseIndex(config, stream, date);
case SecurityType.Cfd:
return ParseCfd(config, stream, date);
case SecurityType.Option:
case SecurityType.FutureOption:
case SecurityType.IndexOption:
return ParseOption(config, stream, date);
case SecurityType.Future:
return ParseFuture(config, stream, date);
}
}
catch (Exception err)
{
Log.Error(Invariant($"TradeBar.Reader(): Error parsing stream, Symbol: {config.Symbol.Value}, SecurityType: ") +
Invariant($"{config.SecurityType}, Resolution: {config.Resolution}, Date: {date:yyyy-MM-dd}, Message: {err}")
);
}
// we need to consume a line anyway, to advance the stream
stream.ReadLine();
// if we couldn't parse it above return a default instance
return new TradeBar { Symbol = config.Symbol, Period = config.Increment };
}
/// <summary>
/// Parses the trade bar data line assuming QC data formats
/// </summary>
public static TradeBar Parse(SubscriptionDataConfig config, string line, DateTime baseDate)
{
switch (config.SecurityType)
{
case SecurityType.Equity:
return ParseEquity(config, line, baseDate);
case SecurityType.Forex:
case SecurityType.Crypto:
case SecurityType.CryptoFuture:
return ParseForex(config, line, baseDate);
case SecurityType.Cfd:
return ParseCfd(config, line, baseDate);
}
return null;
}
/// <summary>
/// Parses equity trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
/// </summary>
/// <typeparam name="T">The requested output type, must derive from TradeBar</typeparam>
/// <param name="config">Symbols, Resolution, DataType, </param>
/// <param name="line">Line from the data file requested</param>
/// <param name="date">Date of this reader request</param>
/// <returns></returns>
public static T ParseEquity<T>(SubscriptionDataConfig config, string line, DateTime date)
where T : TradeBar, new()
{
var tradeBar = new T
{
Symbol = config.Symbol,
Period = config.Increment
};
ParseEquity(tradeBar, config, line, date);
return tradeBar;
}
/// <summary>
/// Parses equity trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
/// </summary>
/// <param name="config">Symbols, Resolution, DataType, </param>
/// <param name="streamReader">The data stream of the requested file</param>
/// <param name="date">Date of this reader request</param>
/// <returns></returns>
public static TradeBar ParseEquity(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
{
var tradeBar = new TradeBar
{
Symbol = config.Symbol,
Period = config.Increment
};
StreamParseScale(config, streamReader, date, useScaleFactor: true, tradeBar, true);
return tradeBar;
}
private static void ParseEquity(TradeBar tradeBar, SubscriptionDataConfig config, string line, DateTime date)
{
LineParseScale(config, line, date, useScaleFactor: true, tradeBar, hasVolume: true);
}
/// <summary>
/// Parses equity trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
/// </summary>
/// <param name="config">Symbols, Resolution, DataType, </param>
/// <param name="line">Line from the data file requested</param>
/// <param name="date">Date of this reader request</param>
/// <returns></returns>
public static TradeBar ParseEquity(SubscriptionDataConfig config, string line, DateTime date)
{
var tradeBar = new TradeBar
{
Symbol = config.Symbol,
Period = config.Increment
};
ParseEquity(tradeBar, config, line, date);
return tradeBar;
}
/// <summary>
/// Parses forex trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
/// </summary>
/// <typeparam name="T">The requested output type, must derive from TradeBar</typeparam>
/// <param name="config">Symbols, Resolution, DataType, </param>
/// <param name="line">Line from the data file requested</param>
/// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
/// <returns></returns>
public static T ParseForex<T>(SubscriptionDataConfig config, string line, DateTime date)
where T : TradeBar, new()
{
var tradeBar = new T
{
Symbol = config.Symbol,
Period = config.Increment
};
LineParseNoScale(config, line, date, tradeBar, hasVolume: false);
return tradeBar;
}
/// <summary>
/// Parses crypto trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
/// </summary>
/// <typeparam name="T">The requested output type, must derive from TradeBar</typeparam>
/// <param name="config">Symbols, Resolution, DataType, </param>
/// <param name="line">Line from the data file requested</param>
/// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
public static T ParseCrypto<T>(SubscriptionDataConfig config, string line, DateTime date)
where T : TradeBar, new()
{
var tradeBar = new T
{
Symbol = config.Symbol,
Period = config.Increment
};
LineParseNoScale(config, line, date, tradeBar);
return tradeBar;
}
/// <summary>
/// Parses crypto trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
/// </summary>
/// <param name="config">Symbols, Resolution, DataType, </param>
/// <param name="line">Line from the data file requested</param>
/// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
public static TradeBar ParseCrypto(SubscriptionDataConfig config, string line, DateTime date)
{
return LineParseNoScale(config, line, date);
}
/// <summary>
/// Parses crypto trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
/// </summary>
/// <param name="config">Symbols, Resolution, DataType, </param>
/// <param name="streamReader">The data stream of the requested file</param>
/// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
public static TradeBar ParseCrypto(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
{
return StreamParseNoScale(config, streamReader, date);
}
/// <summary>
/// Parses forex trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
/// </summary>
/// <param name="config">Symbols, Resolution, DataType, </param>
/// <param name="line">Line from the data file requested</param>
/// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
/// <returns></returns>
public static TradeBar ParseForex(SubscriptionDataConfig config, string line, DateTime date)
{
return LineParseNoScale(config, line, date, hasVolume: false);
}
/// <summary>
/// Parses forex trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
/// </summary>
/// <param name="config">Symbols, Resolution, DataType, </param>
/// <param name="streamReader">The data stream of the requested file</param>
/// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
/// <returns></returns>
public static TradeBar ParseForex(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
{
return StreamParseNoScale(config, streamReader, date, hasVolume: false);
}
/// <summary>
/// Parses CFD trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
/// </summary>
/// <typeparam name="T">The requested output type, must derive from TradeBar</typeparam>
/// <param name="config">Symbols, Resolution, DataType, </param>
/// <param name="line">Line from the data file requested</param>
/// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
/// <returns></returns>
public static T ParseCfd<T>(SubscriptionDataConfig config, string line, DateTime date)
where T : TradeBar, new()
{
// CFD has the same data format as Forex
return ParseForex<T>(config, line, date);
}
/// <summary>
/// Parses CFD trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
/// </summary>
/// <param name="config">Symbols, Resolution, DataType, </param>
/// <param name="line">Line from the data file requested</param>
/// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
/// <returns></returns>
public static TradeBar ParseCfd(SubscriptionDataConfig config, string line, DateTime date)
{
// CFD has the same data format as Forex
return ParseForex(config, line, date);
}
/// <summary>
/// Parses CFD trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
/// </summary>
/// <param name="config">Symbols, Resolution, DataType, </param>
/// <param name="streamReader">The data stream of the requested file</param>
/// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
/// <returns></returns>
public static TradeBar ParseCfd(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
{
// CFD has the same data format as Forex
return ParseForex(config, streamReader, date);
}
/// <summary>
/// Parses Option trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
/// </summary>
/// <typeparam name="T">The requested output type, must derive from TradeBar</typeparam>
/// <param name="config">Symbols, Resolution, DataType, </param>
/// <param name="line">Line from the data file requested</param>
/// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
/// <returns></returns>
public static T ParseOption<T>(SubscriptionDataConfig config, string line, DateTime date)
where T : TradeBar, new()
{
var tradeBar = new T
{
Period = config.Increment,
Symbol = config.Symbol
};
LineParseScale(config, line, date, useScaleFactor: LeanData.OptionUseScaleFactor(config.Symbol), tradeBar, hasVolume: true);
return tradeBar;
}
/// <summary>
/// Parses Option trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
/// </summary>
/// <typeparam name="T">The requested output type, must derive from TradeBar</typeparam>
/// <param name="config">Symbols, Resolution, DataType, </param>
/// <param name="streamReader">The data stream of the requested file</param>
/// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
/// <returns></returns>
public static T ParseOption<T>(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
where T : TradeBar, new()
{
var tradeBar = new T
{
Period = config.Increment,
Symbol = config.Symbol
};
StreamParseScale(config, streamReader, date, useScaleFactor: LeanData.OptionUseScaleFactor(config.Symbol), tradeBar, true);
return tradeBar;
}
/// <summary>
/// Parses Future trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
/// </summary>
/// <typeparam name="T">The requested output type, must derive from TradeBar</typeparam>
/// <param name="config">Symbols, Resolution, DataType, </param>
/// <param name="streamReader">The data stream of the requested file</param>
/// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
/// <returns></returns>
public static T ParseFuture<T>(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
where T : TradeBar, new()
{
var tradeBar = new T
{
Period = config.Increment,
Symbol = config.Symbol
};
StreamParseNoScale(config, streamReader, date, tradeBar);
return tradeBar;
}
/// <summary>
/// Parses Future trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
/// </summary>
/// <typeparam name="T">The requested output type, must derive from TradeBar</typeparam>
/// <param name="config">Symbols, Resolution, DataType, </param>
/// <param name="line">Line from the data file requested</param>
/// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
/// <returns></returns>
public static T ParseFuture<T>(SubscriptionDataConfig config, string line, DateTime date)
where T : TradeBar, new()
{
var tradeBar = new T
{
Period = config.Increment,
Symbol = config.Symbol
};
LineParseNoScale(config, line, date, tradeBar);
return tradeBar;
}
/// <summary>
/// Parse an index bar from the LEAN disk format
/// </summary>
public static TradeBar ParseIndex(SubscriptionDataConfig config, string line, DateTime date)
{
return LineParseNoScale(config, line, date);
}
/// <summary>
/// Parse an index bar from the LEAN disk format
/// </summary>
private static TradeBar LineParseNoScale(SubscriptionDataConfig config, string line, DateTime date, TradeBar bar = null, bool hasVolume = true)
{
var tradeBar = bar ?? new TradeBar
{
Period = config.Increment,
Symbol = config.Symbol
};
var csv = line.ToCsv(hasVolume ? 6 : 5);
if (config.Resolution == Resolution.Daily || config.Resolution == Resolution.Hour)
{
// hourly and daily have different time format, and can use slow, robust c# parser.
tradeBar.Time = DateTime.ParseExact(csv[0], DateFormat.TwelveCharacter, CultureInfo.InvariantCulture).ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
}
else
{
// Using custom "ToDecimal" conversion for speed on high resolution data.
tradeBar.Time = date.Date.AddMilliseconds(csv[0].ToInt32()).ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
}
tradeBar.Open = csv[1].ToDecimal();
tradeBar.High = csv[2].ToDecimal();
tradeBar.Low = csv[3].ToDecimal();
tradeBar.Close = csv[4].ToDecimal();
if (hasVolume)
{
tradeBar.Volume = csv[5].ToDecimal();
}
return tradeBar;
}
/// <summary>
/// Parse an index bar from the LEAN disk format
/// </summary>
private static TradeBar StreamParseNoScale(SubscriptionDataConfig config, StreamReader streamReader, DateTime date, TradeBar bar = null, bool hasVolume = true)
{
var tradeBar = bar ?? new TradeBar
{
Period = config.Increment,
Symbol = config.Symbol
};
if (config.Resolution == Resolution.Daily || config.Resolution == Resolution.Hour)
{
// hourly and daily have different time format, and can use slow, robust c# parser.
tradeBar.Time = streamReader.GetDateTime().ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
}
else
{
// Using custom "ToDecimal" conversion for speed on high resolution data.
tradeBar.Time = date.Date.AddMilliseconds(streamReader.GetInt32()).ConvertTo(config.DataTimeZone, config.ExchangeTimeZone);
}
tradeBar.Open = streamReader.GetDecimal();
tradeBar.High = streamReader.GetDecimal();
tradeBar.Low = streamReader.GetDecimal();
tradeBar.Close = streamReader.GetDecimal();
if (hasVolume)
{
tradeBar.Volume = streamReader.GetDecimal();
}
return tradeBar;
}
private static TradeBar LineParseScale(SubscriptionDataConfig config, string line, DateTime date, bool useScaleFactor, TradeBar bar = null, bool hasVolume = true)
{
var tradeBar = bar ?? new TradeBar
{
Period = config.Increment,
Symbol = config.Symbol
};
LineParseNoScale(config, line, date, tradeBar, hasVolume);
if (useScaleFactor)
{
tradeBar.Open *= _scaleFactor;
tradeBar.High *= _scaleFactor;
tradeBar.Low *= _scaleFactor;
tradeBar.Close *= _scaleFactor;
}
return tradeBar;
}
private static TradeBar StreamParseScale(SubscriptionDataConfig config, StreamReader streamReader, DateTime date, bool useScaleFactor, TradeBar bar = null, bool hasVolume = true)
{
var tradeBar = bar ?? new TradeBar
{
Period = config.Increment,
Symbol = config.Symbol
};
StreamParseNoScale(config, streamReader, date, tradeBar, hasVolume);
if (useScaleFactor)
{
tradeBar.Open *= _scaleFactor;
tradeBar.High *= _scaleFactor;
tradeBar.Low *= _scaleFactor;
tradeBar.Close *= _scaleFactor;
}
return tradeBar;
}
/// <summary>
/// Parse an index bar from the LEAN disk format
/// </summary>
public static TradeBar ParseIndex(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
{
return StreamParseNoScale(config, streamReader, date);
}
/// <summary>
/// Parses Option trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
/// </summary>
/// <param name="config">Symbols, Resolution, DataType, </param>
/// <param name="line">Line from the data file requested</param>
/// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
/// <returns></returns>
public static TradeBar ParseOption(SubscriptionDataConfig config, string line, DateTime date)
{
return ParseOption<TradeBar>(config, line, date);
}
/// <summary>
/// Parses Option trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
/// </summary>
/// <param name="config">Symbols, Resolution, DataType, </param>
/// <param name="streamReader">The data stream of the requested file</param>
/// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
/// <returns></returns>
public static TradeBar ParseOption(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
{
return ParseOption<TradeBar>(config, streamReader, date);
}
/// <summary>
/// Parses Future trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
/// </summary>
/// <param name="config">Symbols, Resolution, DataType, </param>
/// <param name="line">Line from the data file requested</param>
/// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
/// <returns></returns>
public static TradeBar ParseFuture(SubscriptionDataConfig config, string line, DateTime date)
{
return ParseFuture<TradeBar>(config, line, date);
}
/// <summary>
/// Parses Future trade bar data into the specified tradebar type, useful for custom types with OHLCV data deriving from TradeBar
/// </summary>
/// <param name="config">Symbols, Resolution, DataType, </param>
/// <param name="streamReader">The data stream of the requested file</param>
/// <param name="date">The base data used to compute the time of the bar since the line specifies a milliseconds since midnight</param>
/// <returns></returns>
public static TradeBar ParseFuture(SubscriptionDataConfig config, StreamReader streamReader, DateTime date)
{
return ParseFuture<TradeBar>(config, streamReader, date);
}
/// <summary>
/// Update the tradebar - build the bar from this pricing information:
/// </summary>
/// <param name="lastTrade">This trade price</param>
/// <param name="bidPrice">Current bid price (not used) </param>
/// <param name="askPrice">Current asking price (not used) </param>
/// <param name="volume">Volume of this trade</param>
/// <param name="bidSize">The size of the current bid, if available</param>
/// <param name="askSize">The size of the current ask, if available</param>
public override void Update(decimal lastTrade, decimal bidPrice, decimal askPrice, decimal volume, decimal bidSize, decimal askSize)
{
Initialize(lastTrade);
if (lastTrade > High) High = lastTrade;
if (lastTrade < Low) Low = lastTrade;
//Volume is the total summed volume of trades in this bar:
Volume += volume;
//Always set the closing price;
Close = lastTrade;
}
/// <summary>
/// Get Source for Custom Data File
/// >> What source file location would you prefer for each type of usage:
/// </summary>
/// <param name="config">Configuration object</param>
/// <param name="date">Date of this source request if source spread across multiple files</param>
/// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
/// <returns>String source location of the file</returns>
public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
{
if (isLiveMode)
{
// this data type is streamed in live mode
return new SubscriptionDataSource(string.Empty, SubscriptionTransportMedium.Streaming);
}
var source = LeanData.GenerateZipFilePath(Globals.DataFolder, config.Symbol, date, config.Resolution, config.TickType);
if (config.SecurityType == SecurityType.Future || config.SecurityType.IsOption())
{
source += "#" + LeanData.GenerateZipEntryName(config.Symbol, date, config.Resolution, config.TickType);
}
return new SubscriptionDataSource(source, SubscriptionTransportMedium.LocalFile, FileFormat.Csv);
}
/// <summary>
/// Return a new instance clone of this object, used in fill forward
/// </summary>
/// <param name="fillForward">True if this is a fill forward clone</param>
/// <returns>A clone of the current object</returns>
public override BaseData Clone(bool fillForward)
{
var clone = base.Clone(fillForward);
if (fillForward)
{
// zero volume out, since it would skew calculations in volume-based indicators
((TradeBar)clone).Volume = 0;
}
return clone;
}
/// <summary>
/// Return a new instance clone of this object
/// </summary>
public override BaseData Clone()
{
return (BaseData)MemberwiseClone();
}
/// <summary>
/// Formats a string with the symbol and value.
/// </summary>
/// <returns>string - a string formatted as SPY: 167.753</returns>
public override string ToString()
{
return $"{Symbol}: " +
$"O: {Open.SmartRounding()} " +
$"H: {High.SmartRounding()} " +
$"L: {Low.SmartRounding()} " +
$"C: {Close.SmartRounding()} " +
$"V: {Volume.SmartRounding()}";
}
/// <summary>
/// Initializes this bar with a first data point
/// </summary>
/// <param name="value">The seed value for this bar</param>
private void Initialize(decimal value)
{
if (Interlocked.CompareExchange(ref Initialized, 1, 0) == 0)
{
_open = value;
_low = value;
_high = value;
}
}
}
}
+41
View File
@@ -0,0 +1,41 @@
/*
* 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.Market
{
/// <summary>
/// Collection of TradeBars to create a data type for generic data handler:
/// </summary>
public class TradeBars : DataDictionary<TradeBar>
{
/// <summary>
/// Creates a new instance of the <see cref="TradeBars"/> dictionary
/// </summary>
public TradeBars()
{
}
/// <summary>
/// Creates a new instance of the <see cref="TradeBars"/> dictionary
/// </summary>
/// <param name="frontier">The time associated with the data in this dictionary</param>
public TradeBars(DateTime frontier)
: base(frontier)
{
}
}
}
+116
View File
@@ -0,0 +1,116 @@
/*
* 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.Market
{
/// <summary>
/// Represents a bar sectioned not by time, but by some amount of movement in volume
/// </summary>
public class VolumeRenkoBar : BaseRenkoBar
{
/// <summary>
/// Gets whether or not this bar is considered closed.
/// </summary>
public override bool IsClosed => Volume >= BrickSize;
/// <summary>
/// Initializes a new default instance of the <see cref="RenkoBar"/> class.
/// </summary>
public VolumeRenkoBar()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="VolumeRenkoBar"/> class with the specified values
/// </summary>
/// <param name="symbol">symbol of the data</param>
/// <param name="start">The current data start time</param>
/// <param name="endTime">The current data end time</param>
/// <param name="brickSize">The preset volume capacity of this bar</param>
/// <param name="open">The current data open value</param>
/// <param name="high">The current data high value</param>
/// <param name="low">The current data low value</param>
/// <param name="close">The current data close value</param>
/// <param name="volume">The current data volume</param>
public VolumeRenkoBar(Symbol symbol, DateTime start, DateTime endTime, decimal brickSize, decimal open, decimal high, decimal low, decimal close, decimal volume)
{
Type = RenkoType.Classic;
BrickSize = brickSize;
Symbol = symbol;
Start = start;
EndTime = endTime;
Open = open;
Close = close;
Volume = volume;
High = high;
Low = low;
}
/// <summary>
/// Updates this <see cref="VolumeRenkoBar"/> with the specified values and returns whether or not this bar is closed
/// </summary>
/// <param name="time">The current data end time</param>
/// <param name="high">The current data high value</param>
/// <param name="low">The current data low value</param>
/// <param name="close">The current data close value</param>
/// <param name="volume">The current data volume</param>
/// <returns>The excess volume that the current bar cannot absorb</returns>
public decimal Update(DateTime time, decimal high, decimal low, decimal close, decimal volume)
{
// can't update a closed renko bar
if (IsClosed) return 0m;
EndTime = time;
var excessVolume = Volume + volume - BrickSize;
if (excessVolume > 0)
{
Volume = BrickSize;
}
else
{
Volume += volume;
}
Close = close;
if (high > High) High = high;
if (low < Low) Low = low;
return excessVolume;
}
/// <summary>
/// Create a new <see cref="VolumeRenkoBar"/> with previous information rollover
/// </summary>
public VolumeRenkoBar Rollover()
{
return new VolumeRenkoBar
{
Type = Type,
BrickSize = BrickSize,
Symbol = Symbol,
Open = Close, // rollover open is the previous close
High = Close,
Low = Close,
Close = Close,
Start = EndTime, // rollover start time is the previous end time
EndTime = EndTime,
Volume = 0m
};
}
}
}