Skip to content

Commit

Permalink
- Added support for sourcing and caching the full symbol list from IQ…
Browse files Browse the repository at this point in the history
…Feed website.

- Added IQFeed options support to toolbox: option chain universe, option symbol list, subscriptions, live prices
- Added LiveTradingDataFeed to support options live screaming
- Added IB live options trading support: trading orders, holdings, live option exercising
- Modified ISymbolMapper interface to support derivatives (options, futures)
- Fixed minor bugs with (introduced ealier) symbol changes for options
Tested with IQFeed version 5.2.4.2. IB TWS offline version 957.
  • Loading branch information
quant1729 committed Sep 14, 2016
1 parent 2c623c0 commit 0396841
Show file tree
Hide file tree
Showing 17 changed files with 467 additions and 67 deletions.
45 changes: 42 additions & 3 deletions Algorithm/QCAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -483,8 +483,7 @@ from kvp in UniverseManager
// add option underlying securities if not present
foreach (var option in Securities.Select(x => x.Value).OfType<Option>())
{
var sid = option.Symbol.ID;
var underlying = QuantConnect.Symbol.Create(sid.Symbol, SecurityType.Equity, sid.Market);
var underlying = option.Symbol.Underlying;
Security equity;
if (!Securities.TryGetValue(underlying, out equity))
{
Expand Down Expand Up @@ -1227,6 +1226,12 @@ public Security AddSecurity(SecurityType securityType, string symbol, Resolution
/// <param name="extendedMarketHours">ExtendedMarketHours send in data from 4am - 8pm, not used for FOREX</param>
public Security AddSecurity(SecurityType securityType, string symbol, Resolution resolution, string market, bool fillDataForward, decimal leverage, bool extendedMarketHours)
{
// if AddSecurity method is called to add an option, we delegate a call to AddOption methods
if (securityType == SecurityType.Option)
{
return AddOption(symbol, resolution, market, fillDataForward, leverage);
}

try
{
if (market == null)
Expand Down Expand Up @@ -1309,14 +1314,48 @@ public Option AddOption(string underlying, Resolution resolution = Resolution.Mi
Universe universe;
if (!UniverseManager.TryGetValue(canonicalSymbol, out universe))
{
var settings = new UniverseSettings(resolution, leverage, false, false, TimeSpan.Zero);
var settings = new UniverseSettings(resolution, leverage, true, false, TimeSpan.Zero);
universe = new OptionChainUniverse(canonicalSecurity, settings, SecurityInitializer);
UniverseManager.Add(canonicalSymbol, universe);
}

return canonicalSecurity;
}

/// <summary>
/// This is a private method that adds single option contract
/// </summary>
private Security AddSingleOption(string symbol, Resolution resolution, string market, bool fillDataForward, decimal leverage)
{
try
{
if (market == null)
{
if (!BrokerageModel.DefaultMarkets.TryGetValue(SecurityType.Option, out market))
{
throw new Exception("No default market set for security type: " + SecurityType.Option);
}
}

var sid = SecurityIdentifier.Parse(symbol);
var optionSymbol = QuantConnect.Symbol.CreateOption(sid.Underlying.Symbol, sid.Market, sid.OptionStyle, sid.OptionRight, sid.StrikePrice, sid.Date);

var marketHoursEntry = _marketHoursDatabase.GetEntry(market, optionSymbol.Underlying.Value, SecurityType.Option);
var symbolProperties = _symbolPropertiesDatabase.GetSymbolProperties(market, optionSymbol.Underlying.Value, SecurityType.Option, CashBook.AccountCurrency);
var security = (Option)SecurityManager.CreateSecurity(typeof(ZipEntryName), Portfolio, SubscriptionManager,
marketHoursEntry.ExchangeHours, marketHoursEntry.DataTimeZone, symbolProperties, SecurityInitializer, optionSymbol, resolution,
fillDataForward, leverage, false, false, false, true, false);

Securities.Add(security);
return security;
}
catch (Exception err)
{
Error("Algorithm.AddSingleOption(): " + err);
return null;
}
}

/// <summary>
/// Creates and adds a new <see cref="Forex"/> security to the algorithm
/// </summary>
Expand Down
5 changes: 4 additions & 1 deletion Brokerages/Fxcm/FxcmSymbolMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,11 @@ public string GetBrokerageSymbol(Symbol symbol)
/// <param name="brokerageSymbol">The FXCM symbol</param>
/// <param name="securityType">The security type</param>
/// <param name="market">The market</param>
/// <param name="expirationDate">Expiration date of the security(if applicable)</param>
/// <param name="strike">The strike of the security (if applicable)</param>
/// <param name="optionRight">The option right of the security (if applicable)</param>
/// <returns>A new Lean Symbol instance</returns>
public Symbol GetLeanSymbol(string brokerageSymbol, SecurityType securityType, string market)
public Symbol GetLeanSymbol(string brokerageSymbol, SecurityType securityType, string market, DateTime expirationDate = default(DateTime), decimal strike = 0, OptionRight optionRight = 0)
{
if (string.IsNullOrWhiteSpace(brokerageSymbol))
throw new ArgumentException("Invalid FXCM symbol: " + brokerageSymbol);
Expand Down
7 changes: 6 additions & 1 deletion Brokerages/ISymbolMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
* limitations under the License.
*/

using System;

namespace QuantConnect.Brokerages
{
/// <summary>
Expand All @@ -33,7 +35,10 @@ public interface ISymbolMapper
/// <param name="brokerageSymbol">The brokerage symbol</param>
/// <param name="securityType">The security type</param>
/// <param name="market">The market</param>
/// <param name="expirationDate">Expiration date of the security(if applicable)</param>
/// <param name="strike">The strike of the security (if applicable)</param>
/// <param name="optionRight">The option right of the security (if applicable)</param>
/// <returns>A new Lean Symbol instance</returns>
Symbol GetLeanSymbol(string brokerageSymbol, SecurityType securityType, string market);
Symbol GetLeanSymbol(string brokerageSymbol, SecurityType securityType, string market, DateTime expirationDate = default(DateTime), decimal strike = 0, OptionRight optionRight = 0);
}
}
26 changes: 22 additions & 4 deletions Brokerages/InteractiveBrokers/InteractiveBrokersBrokerage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -545,8 +545,15 @@ private void IBPlaceOrder(Order order, bool needsNewID, string exchange = null)
throw new ArgumentException("Expected order with populated BrokerId for updating orders.");
}

var ibOrder = ConvertOrder(order, contract, ibOrderID);
_client.PlaceOrder(ibOrder.OrderId, contract, ibOrder);
if (order.Type == OrderType.OptionExercise)
{
_client.ExerciseOptions(ibOrderID, contract, 1, order.Quantity, _account, 0);
}
else
{
var ibOrder = ConvertOrder(order, contract, ibOrderID);
_client.PlaceOrder(ibOrder.OrderId, contract, ibOrder);
}
}

private string GetPrimaryExchange(IB.Contract contract)
Expand Down Expand Up @@ -1053,7 +1060,7 @@ private IB.Contract CreateContract(Symbol symbol, string exchange = null)
contract.Expiry = symbol.ID.Date.ToString(DateFormat.EightCharacter);
contract.Right = symbol.ID.OptionRight == OptionRight.Call ? IB.RightType.Call : IB.RightType.Put;
contract.Strike = Convert.ToDouble(symbol.ID.StrikePrice);
contract.Symbol = symbol.ID.Symbol;
contract.Symbol = ibSymbol;
}

// some contracts require this, such as MSFT
Expand Down Expand Up @@ -1260,7 +1267,18 @@ private Symbol MapSymbol(IB.Contract contract)
var ibSymbol = securityType == SecurityType.Forex ? contract.Symbol + contract.Currency : contract.Symbol;
var market = securityType == SecurityType.Forex ? Market.FXCM : Market.USA;

return _symbolMapper.GetLeanSymbol(ibSymbol, securityType, market);
if (securityType == SecurityType.Option)
{
var expiryDate = DateTime.ParseExact(contract.Expiry, DateFormat.EightCharacter, CultureInfo.InvariantCulture);
var right = contract.Right == IB.RightType.Call ? OptionRight.Call : OptionRight.Put;
var strike = Convert.ToDecimal(contract.Strike);

return _symbolMapper.GetLeanSymbol(ibSymbol, securityType, market, expiryDate, strike, right);
}
else
{
return _symbolMapper.GetLeanSymbol(ibSymbol, securityType, market);
}
}

private decimal RoundPrice(decimal input, decimal minTick)
Expand Down
23 changes: 20 additions & 3 deletions Brokerages/InteractiveBrokers/InteractiveBrokersSymbolMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ public string GetBrokerageSymbol(Symbol symbol)
if (symbol.ID.SecurityType == SecurityType.Forex && symbol.Value.Length != 6)
throw new ArgumentException("Forex symbol length must be equal to 6: " + symbol.Value);

if (symbol.ID.SecurityType == SecurityType.Option)
{
return symbol.Underlying.Value;
}

return symbol.Value;
}

Expand All @@ -49,16 +54,28 @@ public string GetBrokerageSymbol(Symbol symbol)
/// <param name="brokerageSymbol">The InteractiveBrokers symbol</param>
/// <param name="securityType">The security type</param>
/// <param name="market">The market</param>
/// <param name="expirationDate">Expiration date of the security(if applicable)</param>
/// <param name="strike">The strike of the security (if applicable)</param>
/// <param name="optionRight">The option right of the security (if applicable)</param>
/// <returns>A new Lean Symbol instance</returns>
public Symbol GetLeanSymbol(string brokerageSymbol, SecurityType securityType, string market)
public Symbol GetLeanSymbol(string brokerageSymbol, SecurityType securityType, string market, DateTime expirationDate = default(DateTime), decimal strike = 0, OptionRight optionRight = 0)
{
if (string.IsNullOrWhiteSpace(brokerageSymbol))
throw new ArgumentException("Invalid symbol: " + brokerageSymbol);

if (securityType != SecurityType.Forex && securityType != SecurityType.Equity)
if (securityType != SecurityType.Forex &&
securityType != SecurityType.Equity &&
securityType != SecurityType.Option)
throw new ArgumentException("Invalid security type: " + securityType);

return Symbol.Create(brokerageSymbol, securityType, market);
if (securityType == SecurityType.Option)
{
return Symbol.CreateOption(brokerageSymbol, market, OptionStyle.American, optionRight, strike, expirationDate);
}
else
{
return Symbol.Create(brokerageSymbol, securityType, market);
}
}
}
}
5 changes: 4 additions & 1 deletion Brokerages/Oanda/OandaSymbolMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,11 @@ public string GetBrokerageSymbol(Symbol symbol)
/// <param name="brokerageSymbol">The Oanda symbol</param>
/// <param name="securityType">The security type</param>
/// <param name="market">The market</param>
/// <param name="expirationDate">Expiration date of the security(if applicable)</param>
/// <param name="strike">The strike of the security (if applicable)</param>
/// <param name="optionRight">The option right of the security (if applicable)</param>
/// <returns>A new Lean Symbol instance</returns>
public Symbol GetLeanSymbol(string brokerageSymbol, SecurityType securityType, string market)
public Symbol GetLeanSymbol(string brokerageSymbol, SecurityType securityType, string market, DateTime expirationDate = default(DateTime), decimal strike = 0, OptionRight optionRight = 0)
{
if (string.IsNullOrWhiteSpace(brokerageSymbol))
throw new ArgumentException("Invalid Oanda symbol: " + brokerageSymbol);
Expand Down
Original file line number Diff line number Diff line change
@@ -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 https://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 System.Linq;
using QuantConnect.Data;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Interfaces;
using QuantConnect.Data.Market;
using QuantConnect.Data.Auxiliary;
using System.Collections;

namespace QuantConnect.Lean.Engine.DataFeeds.Enumerators
{
/// <summary>
/// Provides an implementation of <see cref="ISubscriptionEnumeratorFactory"/> that reads
/// a live dataset provided by <see cref="IDataQueueUniverseProvider"/> into a <see cref="OptionChainUniverseDataCollection"/> enumerator
/// </summary>
public class DataQueueOptionChainUniverseDataCollectionEnumerator : IEnumerator<OptionChainUniverseDataCollection>
{
private readonly List<BaseData> _symbolUniverse;
private readonly IEnumerator<BaseData> _underlying;
private readonly Symbol _symbol;
OptionChainUniverseDataCollection _current;
bool _needNewCurrent;

/// <summary>
/// Initializes a new instance of the <see cref="DataQueueOptionChainUniverseDataCollectionEnumerator"/> class.
/// </summary>
/// <param name="symbol">Option contract symbol</param>
/// <param name="underlying">Underlying enumerator</param>
/// <param name="symbolUniverse">Symbol universe provider of the data queue</param>
public DataQueueOptionChainUniverseDataCollectionEnumerator(Symbol symbol, IEnumerator<BaseData> underlying, List<BaseData> symbolUniverse)
{
_symbolUniverse = symbolUniverse;
_underlying = underlying;
_needNewCurrent = true;
_symbol = symbol;

_current = new OptionChainUniverseDataCollection { Symbol = _symbol, Data = symbolUniverse };
}

/// <summary>
/// Returns current option chain enumerator position
/// </summary>
public OptionChainUniverseDataCollection Current
{
get
{
if (_underlying.Current == null)
{
return null;
}

// if we need to update the enumerator, we update it wrapping around the underlying enumerator
if (_needNewCurrent)
{
var current = (OptionChainUniverseDataCollection)_current.Clone();
current.Underlying = _underlying.Current;
current.Time = _underlying.Current.Time;
current.EndTime = _underlying.Current.EndTime;

_current = current;
_needNewCurrent = false;
}

return _current;
}
}

/// <summary>
/// Returns current option chain enumerator position
/// </summary>
object IEnumerator.Current
{
get
{
return Current;
}
}

/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
_underlying.Dispose();
}

/// <summary>
/// Advances the enumerator to the next element of the collection.
/// </summary>
/// <returns>
/// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.
/// </returns>
public bool MoveNext()
{
_needNewCurrent = _underlying.MoveNext();
return _needNewCurrent;
}

/// <summary>
/// Sets the enumerator to its initial position, which is before the first element in the collection.
/// </summary>
public void Reset()
{
_underlying.Reset();
_needNewCurrent = true;
}
}
}
Loading

0 comments on commit 0396841

Please sign in to comment.