Skip to content

Commit

Permalink
Adds DynamicDataConsolidator and uses in ResolveConsolidator
Browse files Browse the repository at this point in the history
Also renamed example algorithm filenames to match the type name
  • Loading branch information
mchandschuh committed Mar 14, 2015
1 parent 47a2e64 commit ef04c41
Show file tree
Hide file tree
Showing 19 changed files with 514 additions and 81 deletions.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ namespace QuantConnect.Algorithm.Examples
/// </summary>
public class CustomDataBitcoinAlgorithm : QCAlgorithm
{


/// <summary>
/// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
/// </summary>
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
*/
using System;
using QuantConnect.Data.Custom;
using QuantConnect.Indicators;


namespace QuantConnect.Algorithm.Examples
Expand All @@ -28,6 +29,7 @@ namespace QuantConnect.Algorithm.Examples
/// </summary>
public class QCUQuandlImporter : QCAlgorithm
{
private SimpleMovingAverage sma;
string _quandlCode = "YAHOO/INDEX_SPY";

/// Initialize the data and resolution you require for your strategy:
Expand All @@ -41,10 +43,12 @@ public override void Initialize()
SetCash(25000);

//Add Generic Quandl Data:
AddData<Quandl>(_quandlCode);
AddData<Quandl>(_quandlCode, Resolution.Daily, true, true);

sma = SMA(_quandlCode, 14);
}

/// Data Event Handler: New data arrives here. "TradeBars" type is a dictionary of strings so you can access it by symbol.
/// Data Event Handler: New data arrives here. "TradeBars" type is a dictionary of strings so you can access it by symbol
public void OnData(Quandl data)
{
if (!Portfolio.HoldStock)
Expand All @@ -55,6 +59,8 @@ public void OnData(Quandl data)
//Debug sends messages to the user console: "Time" is the algorithm time keeper object
Debug("Purchased " + _quandlCode + " >> " + Time.ToShortDateString());
}

Plot("SPY", sma);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,4 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using System.Collections.Generic;
using System.Collections.Generic;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Interfaces;
Expand Down Expand Up @@ -62,7 +47,6 @@ public void OnData(Ticks data)
}
}
}

/// <summary>
/// Exchange filter class
/// </summary>
Expand Down Expand Up @@ -150,5 +134,4 @@ public bool Filter(Security asset, BaseData data)
}

}

}
13 changes: 9 additions & 4 deletions Algorithm/QCAlgorithm.Indicators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -390,14 +390,21 @@ protected IDataConsolidator ResolveConsolidator(string symbol, Resolution? resol
// we use IsAssignableFrom instead of IsSubclassOf so that we can account for types that are able to be cast to TradeBar
if (typeof(TradeBar).IsAssignableFrom(subscription.Type))
{
return TradeBarConsolidator.FromResolution(resolution.Value);
return new TradeBarConsolidator(resolution.Value.ToTimeSpan());
}

// if our type can be used as a tick then we'll use the tick consolidator
// we use IsAssignableFrom instead of IsSubclassOf so that we can account for types that are able to be cast to Tick
if (typeof (Tick).IsAssignableFrom(subscription.Type))
{
return TickConsolidator.FromResolution(resolution.Value);
return new TickConsolidator(resolution.Value.ToTimeSpan());
}

// if our type can be used as a DynamicData then we'll use the DynamicDataConsolidator, inspect
// the subscription to figure out the isTradeBar and hasVolume flags
if (typeof (DynamicData).IsAssignableFrom(subscription.Type))
{
return new DynamicDataConsolidator(resolution.Value.ToTimeSpan(), subscription.IsTradeBar, subscription.HasVolume);
}

// no matter what we can always consolidate based on the time-value pair of BaseData, later we'll need a check
Expand All @@ -413,8 +420,6 @@ protected IDataConsolidator ResolveConsolidator(string symbol, Resolution? resol
// this will happen if wedid not find the subscription, let's give the user a decent error message
throw new Exception("Please register to receive data for symbol '" + symbol + "' using the AddSecurity() function.");
}

throw new NotSupportedException("QCAlgorithm.ResolveConsolidator(): Currently this only supports TradeBar data.");
}

/// <summary>
Expand Down
23 changes: 20 additions & 3 deletions Algorithm/QCAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,22 @@ public void AddData<T>(string symbol, Resolution resolution = Resolution.Minute)
// Defaults:extended market hours" = true because we want events 24 hours,
// fillforward = false because only want to trigger when there's new custom data.
// leverage = 1 because no leverage on nonmarket data?
AddData<T>(symbol, resolution, fillDataForward: false, leverage: 1m);
AddData<T>(symbol, resolution, fillDataForward: false, leverage: 1m, isTradeBar: false, hasVolume: false);
}

/// <summary>
/// AddData<typeparam name="T"/> a new user defined data source, requiring only the minimum config options.
/// </summary>
/// <param name="symbol">Key/Symbol for data</param>
/// <param name="resolution">Resolution of the data</param>
/// <param name="isTradeBar">Set to true if this data has Open, High, Low, and Close properties</param>
/// <param name="hasVolume">Set to true if this data has a Volume property</param>
/// <remarks>Generic type T must implement base data</remarks>
public void AddData<T>(string symbol, Resolution resolution, bool isTradeBar, bool hasVolume)
{
if (_locked) return;

AddData<T>(symbol, resolution, fillDataForward: false, leverage: 1m, isTradeBar: isTradeBar, hasVolume: hasVolume);
}


Expand All @@ -689,13 +704,15 @@ public void AddData<T>(string symbol, Resolution resolution = Resolution.Minute)
/// <param name="resolution">Resolution of the Data Required</param>
/// <param name="fillDataForward">When no data available on a tradebar, return the last data that was generated</param>
/// <param name="leverage">Custom leverage per security</param>
/// <param name="isTradeBar">Set to true if this data has Open, High, Low, and Close properties</param>
/// <param name="hasVolume">Set to true if this data has a Volume property</param>
/// <remarks>Generic type T must implement base data</remarks>
public void AddData<T>(string symbol, Resolution resolution, bool fillDataForward, decimal leverage = 1.0m)
public void AddData<T>(string symbol, Resolution resolution, bool fillDataForward, decimal leverage = 1.0m, bool isTradeBar = false, bool hasVolume = false)
{
if (_locked) return;

//Add this to the data-feed subscriptions
SubscriptionManager.Add(typeof(T), SecurityType.Base, symbol, resolution, fillDataForward, extendedMarketHours: true);
SubscriptionManager.Add(typeof(T), SecurityType.Base, symbol, resolution, fillDataForward, extendedMarketHours: true, isTradeBar: isTradeBar, hasVolume: hasVolume);

//Add this new generic data as a tradeable security:
Securities.Add(symbol, SecurityType.Base, resolution, fillDataForward, leverage, extendedMarketHours: true, isDynamicallyLoadedData: true);
Expand Down
12 changes: 6 additions & 6 deletions Algorithm/QuantConnect.Algorithm.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -74,21 +74,21 @@
<Reference Include="System.Numerics" />
</ItemGroup>
<ItemGroup>
<Compile Include="Examples\BubbleDetector.cs" />
<Compile Include="Examples\Bubble.cs" />
<Compile Include="Examples\CustomDataWithFillForward.cs" />
<Compile Include="Examples\QuandlImporter.cs" />
<Compile Include="Examples\QCUQuandlImporter.cs" />
<Compile Include="Examples\BasicTemplateAlgorithm.cs" />
<Compile Include="Examples\CustomData_NitfyINR.cs" />
<Compile Include="Examples\CustomDataNIFTYAlgorithm.cs" />
<Compile Include="Examples\DisplacedMovingAverageRibbon.cs" />
<Compile Include="Examples\ETFGlobalRotation.cs" />
<Compile Include="Examples\CustomData_Bitcoin.cs" />
<Compile Include="Examples\CustomCharting.cs" />
<Compile Include="Examples\CustomTickFilter.cs" />
<Compile Include="Examples\CustomDataBitcoinAlgorithm.cs" />
<Compile Include="Examples\CustomChartingAlgorithm.cs" />
<Compile Include="Examples\DataConsolidation.cs" />
<Compile Include="Examples\MACDTrendFollowing.cs" />
<Compile Include="Examples\MovingAverageCross.cs" />
<Compile Include="Examples\Stress_Symbols.cs" />
<Compile Include="Examples\LiveFeaturesAlgorithm.cs" />
<Compile Include="Examples\TickDataFilteringAlgorithm.cs" />
<Compile Include="QCAlgorithm.cs" />
<Compile Include="QCAlgorithm.Console.cs" />
<Compile Include="QCAlgorithm.Trading.cs" />
Expand Down
155 changes: 155 additions & 0 deletions Common/Data/Consolidators/DynamicDataConsolidator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*
* 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 System.Linq;
using QuantConnect.Data.Market;

namespace QuantConnect.Data.Consolidators
{
/// <summary>
/// A data csolidator that can make trade bars from DynamicData derived types. This is useful for
/// aggregating Quandl and other highly flexible dynamic custom data types.
/// </summary>
public class DynamicDataConsolidator : TradeBarConsolidatorBase<DynamicData>
{
private bool _first = true;
private readonly bool _hasVolume;
private readonly bool _isTradeBar;

/// <summary>
/// Creates a consolidator to produce a new 'TradeBar' representing the period. Setting both isTradeBar and hasVolume to
/// false will result in time-value aggregation only.
/// </summary>
/// <param name="period">The minimum span of time before emitting a consolidated bar</param>
/// <param name="isTradeBar">Set to true if the dynamic data has Open, High, Low, and Close properties defined</param>
/// <param name="hasVolume">Set to true if the dynamic data has Volume defined</param>
public DynamicDataConsolidator(TimeSpan period, bool isTradeBar, bool hasVolume)
: base(period)
{
_isTradeBar = isTradeBar;
_hasVolume = hasVolume;
}
/// <summary>
/// Creates a consolidator to produce a new 'TradeBar' representing the last count pieces of data. Setting both isTradeBar and hasVolume to
/// false will result in time-value aggregation only.
/// </summary>
/// <param name="maxCount">The number of pieces to accept before emiting a consolidated bar</param>
/// <param name="isTradeBar">Set to true if the dynamic data has Open, High, Low, and Close properties defined</param>
/// <param name="hasVolume">Set to true if the dynamic data has Volume defined</param>
public DynamicDataConsolidator(int maxCount, bool isTradeBar, bool hasVolume)
: base(maxCount)
{
_isTradeBar = isTradeBar;
_hasVolume = hasVolume;
}

/// <summary>
/// Creates a consolidator to produce a new 'TradeBar' representing the last count pieces of data or the period, whichever comes first.
/// Setting both isTradeBar and hasVolume to false will result in time-value aggregation only.
/// </summary>
/// <param name="maxCount">The number of pieces to accept before emiting a consolidated bar</param>
/// <param name="period">The minimum span of time before emitting a consolidated bar</param>
/// <param name="isTradeBar">Set to true if the dynamic data has Open, High, Low, and Close properties defined</param>
/// <param name="hasVolume">Set to true if the dynamic data has Volume defined</param>
public DynamicDataConsolidator(int maxCount, TimeSpan period, bool isTradeBar, bool hasVolume)
: base(maxCount, period)
{
_isTradeBar = isTradeBar;
_hasVolume = hasVolume;
}

protected override void AggregateBar(ref TradeBar workingBar, DynamicData data)
{
if (_first)
{
_first = false;
// the first time through we're going to inspect the data instance and verify
// the properties exist. We'll check them all first and throw a message containing
// all expected and all missing properties names.
VerifyDataShape(data);
}

decimal open, high, low, close;
long volume = 0;
if (_isTradeBar)
{
open = (decimal) Convert.ChangeType(data.GetProperty("Open"), typeof (decimal));
high = (decimal) Convert.ChangeType(data.GetProperty("High"), typeof (decimal));
low = (decimal) Convert.ChangeType(data.GetProperty("Low"), typeof (decimal));
close = (decimal) Convert.ChangeType(data.GetProperty("Close"), typeof (decimal));
}
else
{
// fall back on regular time-value aggregation
open = high = low = close = data.Value;
}
if (_hasVolume)
{
volume = (long) Convert.ChangeType(data.GetProperty("Volume"), typeof (long));
}

if (workingBar == null)
{
workingBar = new TradeBar
{
Symbol = data.Symbol,
Time = data.Time,
Open = open,
High = high,
Low = low,
Close = close,
Volume = volume
};
}
else
{
//Aggregate the working bar
workingBar.Close = close;
workingBar.Volume += volume;
if (low < workingBar.Low) workingBar.Low = low;
if (high > workingBar.High) workingBar.High = high;
}
}

private void VerifyDataShape(DynamicData data)
{
var expected = new List<string>();
if (_isTradeBar)
{
// expect OHLC data
expected.AddRange(new[]{"open", "high", "low", "close"});
}
if (_hasVolume)
{
expected.Add("volume");
}

var missing = expected.Where(propertyName => !data.HasProperty(propertyName)).ToList();
if (missing.Any())
{
var message = string.Format("Error in DynamicDataConsolidator while consolidating type '{0}' with symbol '{1}'. " +
"Expected property names: {2} but the following were missing: {3}",
data.GetType().Name,
data.Symbol,
string.Join(", ", expected),
string.Join(", ", missing)
);

throw new Exception(message);
}
}
}
}
10 changes: 0 additions & 10 deletions Common/Data/Consolidators/TickConsolidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,6 @@ namespace QuantConnect.Data.Consolidators
/// </summary>
public class TickConsolidator : TradeBarConsolidatorBase<Tick>
{
/// <summary>
/// Create a new TickConsolidator for the desired resolution
/// </summary>
/// <param name="resolution">The resoluton desired</param>
/// <returns>A consolidator that produces data on the resolution interval</returns>
public static TickConsolidator FromResolution(Resolution resolution)
{
return new TickConsolidator(resolution.ToTimeSpan());
}

/// <summary>
/// Creates a consolidator to produce a new 'TradeBar' representing the period
/// </summary>
Expand Down
1 change: 1 addition & 0 deletions Common/Data/Consolidators/TradeBarConsolidatorBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ namespace QuantConnect.Data.Consolidators
///
/// This type acts as the base for other consolidators that produce bars on a given time step or for a count of data.
/// </summary>
/// <typeparam name="T">The input type into the consolidator's Update method</typeparam>
public abstract class TradeBarConsolidatorBase<T> : DataConsolidator<T>
where T : BaseData
{
Expand Down

0 comments on commit ef04c41

Please sign in to comment.