/*
* 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;
using QuantConnect.Data.Consolidators;
using QuantConnect.Data.Market;
using QuantConnect.Indicators;
using System;
using QuantConnect.Securities;
using NodaTime;
using System.Collections.Generic;
using System.Reflection.Emit;
using System.Reflection;
using QuantConnect.Python;
using Python.Runtime;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Data.Fundamental;
using System.Linq;
namespace QuantConnect.Algorithm
{
public partial class QCAlgorithm
{
private dynamic _pandas;
///
/// Sets pandas library
///
public void SetPandas()
{
try
{
using (Py.GIL())
{
_pandas = Py.Import("pandas");
}
}
catch (PythonException pythonException)
{
Error("QCAlgorithm.SetPandas(): Failed to import pandas module: " + pythonException);
}
}
///
/// AddData a new user defined data source, requiring only the minimum config options.
/// The data is added with a default time zone of NewYork (Eastern Daylight Savings Time)
///
/// Data source type
/// Key/Symbol for data
/// Resolution of the data
/// Generic type T must implement base data
public void AddData(PyObject type, string symbol, Resolution resolution = Resolution.Minute)
{
AddData(type, symbol, Resolution.Minute, TimeZones.NewYork, false, 1m);
}
///
/// AddData a new user defined data source, requiring only the minimum config options.
///
/// Data source type
/// Key/Symbol for data
/// Resolution of the Data Required
/// Specifies the time zone of the raw data
/// When no data available on a tradebar, return the last data that was generated
/// Custom leverage per security
public void AddData(PyObject type, string symbol, Resolution resolution, DateTimeZone timeZone, bool fillDataForward = false, decimal leverage = 1.0m)
{
AddData(CreateType(type), symbol, resolution, timeZone, fillDataForward, leverage);
}
///
/// AddData a new user defined data source, requiring only the minimum config options.
///
/// Data source type
/// Key/Symbol for data
/// Resolution of the Data Required
/// Specifies the time zone of the raw data
/// When no data available on a tradebar, return the last data that was generated
/// Custom leverage per security
public void AddData(Type T, string symbol, Resolution resolution, DateTimeZone timeZone, bool fillDataForward = false, decimal leverage = 1.0m)
{
var marketHoursDbEntry = _marketHoursDatabase.GetEntry(Market.USA, symbol, SecurityType.Base, timeZone);
//Add this to the data-feed subscriptions
var symbolObject = new Symbol(SecurityIdentifier.GenerateBase(symbol, Market.USA), symbol);
var symbolProperties = _symbolPropertiesDatabase.GetSymbolProperties(Market.USA, symbol, SecurityType.Base, CashBook.AccountCurrency);
//Add this new generic data as a tradeable security:
var security = SecurityManager.CreateSecurity(new List() { T }, Portfolio, SubscriptionManager, marketHoursDbEntry.ExchangeHours, marketHoursDbEntry.DataTimeZone,
symbolProperties, SecurityInitializer, symbolObject, resolution, fillDataForward, leverage, true, false, true, LiveMode);
AddToUserDefinedUniverse(security);
}
///
/// Creates a new universe and adds it to the algorithm. This is for coarse fundamental US Equity data and
/// will be executed on day changes in the NewYork time zone (
///
/// Defines an initial coarse selection
public void AddUniverse(PyObject pycoarse)
{
var coarse = ToFunc(pycoarse);
AddUniverse(coarse);
}
///
/// Creates a new universe and adds it to the algorithm. This is for coarse and fine fundamental US Equity data and
/// will be executed on day changes in the NewYork time zone (
///
/// Defines an initial coarse selection
/// Defines a more detailed selection with access to more data
public void AddUniverse(PyObject pycoarse, PyObject pyfine)
{
var coarse = ToFunc(pycoarse);
var fine = ToFunc(pyfine);
AddUniverse(coarse, fine);
}
///
/// Registers the consolidator to receive automatic updates as well as configures the indicator to receive updates
/// from the consolidator.
///
/// The symbol to register against
/// The indicator to receive data from the consolidator
/// The resolution at which to send data to the indicator, null to use the same resolution as the subscription
public void RegisterIndicator(Symbol symbol, IndicatorBase indicator, Resolution? resolution = null)
{
RegisterIndicator(symbol, indicator, resolution);
}
///
/// Registers the consolidator to receive automatic updates as well as configures the indicator to receive updates
/// from the consolidator.
///
/// The symbol to register against
/// The indicator to receive data from the consolidator
/// The resolution at which to send data to the indicator, null to use the same resolution as the subscription
public void RegisterIndicator(Symbol symbol, IndicatorBase indicator, Resolution? resolution = null)
{
RegisterIndicator(symbol, indicator, resolution);
}
///
/// Registers the consolidator to receive automatic updates as well as configures the indicator to receive updates
/// from the consolidator.
///
/// The symbol to register against
/// The indicator to receive data from the consolidator
/// The resolution at which to send data to the indicator, null to use the same resolution as the subscription
/// Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x)
public void RegisterIndicator(Symbol symbol, IndicatorBase indicator, Resolution? resolution, Func selector)
{
RegisterIndicator(symbol, indicator, resolution, selector);
}
///
/// Registers the consolidator to receive automatic updates as well as configures the indicator to receive updates
/// from the consolidator.
///
/// The symbol to register against
/// The indicator to receive data from the consolidator
/// The resolution at which to send data to the indicator, null to use the same resolution as the subscription
/// Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x)
public void RegisterIndicator(Symbol symbol, IndicatorBase indicator, Resolution? resolution, Func selector)
{
RegisterIndicator(symbol, indicator, resolution, selector);
}
///
/// Registers the consolidator to receive automatic updates as well as configures the indicator to receive updates
/// from the consolidator.
///
/// The symbol to register against
/// The indicator to receive data from the consolidator
/// The resolution at which to send data to the indicator, null to use the same resolution as the subscription
/// Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x)
public void RegisterIndicator(Symbol symbol, IndicatorBase indicator, TimeSpan? resolution, Func selector)
{
RegisterIndicator(symbol, indicator, resolution, selector);
}
///
/// Registers the consolidator to receive automatic updates as well as configures the indicator to receive updates
/// from the consolidator.
///
/// The symbol to register against
/// The indicator to receive data from the consolidator
/// The resolution at which to send data to the indicator, null to use the same resolution as the subscription
/// Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x)
public void RegisterIndicator(Symbol symbol, IndicatorBase indicator, TimeSpan? resolution, Func selector)
{
RegisterIndicator(symbol, indicator, resolution, selector);
}
///
/// Registers the consolidator to receive automatic updates as well as configures the indicator to receive updates
/// from the consolidator.
///
/// The symbol to register against
/// The indicator to receive data from the consolidator
/// The consolidator to receive raw subscription data
/// Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x)
public void RegisterIndicator(Symbol symbol, IndicatorBase indicator, IDataConsolidator consolidator, Func selector)
{
RegisterIndicator(symbol, indicator, consolidator, selector);
}
///
/// Registers the consolidator to receive automatic updates as well as configures the indicator to receive updates
/// from the consolidator.
///
/// The symbol to register against
/// The indicator to receive data from the consolidator
/// The consolidator to receive raw subscription data
/// Selects a value from the BaseData send into the indicator, if null defaults to a cast (x => (T)x)
public void RegisterIndicator(Symbol symbol, IndicatorBase indicator, IDataConsolidator consolidator, Func selector)
{
RegisterIndicator(symbol, indicator, consolidator, selector);
}
///
/// Plots the value of each indicator on the chart
///
/// The chart's name
/// The first indicator to plot
/// The second indicator to plot
/// The third indicator to plot
/// The fourth indicator to plot
///
public void Plot(string chart, Indicator first, Indicator second = null, Indicator third = null, Indicator fourth = null)
{
Plot(chart, new[] { first, second, third, fourth }.Where(x => x != null).ToArray());
}
///
/// Plots the value of each indicator on the chart
///
/// The chart's name
/// The first indicator to plot
/// The second indicator to plot
/// The third indicator to plot
/// The fourth indicator to plot
///
public void Plot(string chart, BarIndicator first, BarIndicator second = null, BarIndicator third = null, BarIndicator fourth = null)
{
Plot(chart, new[] { first, second, third, fourth }.Where(x => x != null).ToArray());
}
///
/// Plots the value of each indicator on the chart
///
/// The chart's name
/// The first indicator to plot
/// The second indicator to plot
/// The third indicator to plot
/// The fourth indicator to plot
///
public void Plot(string chart, TradeBarIndicator first, TradeBarIndicator second = null, TradeBarIndicator third = null, TradeBarIndicator fourth = null)
{
Plot(chart, new[] { first, second, third, fourth }.Where(x => x != null).ToArray());
}
///
/// Automatically plots each indicator when a new value is available
///
public void PlotIndicator(string chart, Indicator first, Indicator second = null, Indicator third = null, Indicator fourth = null)
{
PlotIndicator(chart, new[] { first, second, third, fourth }.Where(x => x != null).ToArray());
}
///
/// Automatically plots each indicator when a new value is available
///
public void PlotIndicator(string chart, BarIndicator first, BarIndicator second = null, BarIndicator third = null, BarIndicator fourth = null)
{
PlotIndicator(chart, new[] { first, second, third, fourth }.Where(x => x != null).ToArray());
}
///
/// Automatically plots each indicator when a new value is available
///
public void PlotIndicator(string chart, TradeBarIndicator first, TradeBarIndicator second = null, TradeBarIndicator third = null, TradeBarIndicator fourth = null)
{
PlotIndicator(chart, new[] { first, second, third, fourth }.Where(x => x != null).ToArray());
}
///
/// Automatically plots each indicator when a new value is available, optionally waiting for indicator.IsReady to return true
///
public void PlotIndicator(string chart, bool waitForReady, Indicator first, Indicator second = null, Indicator third = null, Indicator fourth = null)
{
PlotIndicator(chart, waitForReady, new[] { first, second, third, fourth }.Where(x => x != null).ToArray());
}
///
/// Automatically plots each indicator when a new value is available, optionally waiting for indicator.IsReady to return true
///
public void PlotIndicator(string chart, bool waitForReady, BarIndicator first, BarIndicator second = null, BarIndicator third = null, BarIndicator fourth = null)
{
PlotIndicator(chart, waitForReady, new[] { first, second, third, fourth }.Where(x => x != null).ToArray());
}
///
/// Automatically plots each indicator when a new value is available, optionally waiting for indicator.IsReady to return true
///
public void PlotIndicator(string chart, bool waitForReady, TradeBarIndicator first, TradeBarIndicator second = null, TradeBarIndicator third = null, TradeBarIndicator fourth = null)
{
PlotIndicator(chart, waitForReady, new[] { first, second, third, fourth }.Where(x => x != null).ToArray());
}
///
/// Gets the historical data for the specified symbol. The exact number of bars will be returned.
/// The symbol must exist in the Securities collection.
///
/// The symbols to retrieve historical data for
/// The number of bars to request
/// The resolution to request
/// A python dictionary with pandas DataFrame containing the requested historical data
public PyObject History(PyObject tickers, int periods, Resolution? resolution = null)
{
var symbols = GetSymbolsFromPyObject(tickers);
if (symbols == null) return null;
return CreatePandasDataFrame(symbols, History(symbols, periods, resolution));
}
///
/// Gets the historical data for the specified symbols over the requested span.
/// The symbols must exist in the Securities collection.
///
/// The symbols to retrieve historical data for
/// The span over which to retrieve recent historical data
/// The resolution to request
/// A python dictionary with pandas DataFrame containing the requested historical data
public PyObject History(PyObject tickers, TimeSpan span, Resolution? resolution = null)
{
var symbols = GetSymbolsFromPyObject(tickers);
if (symbols == null) return null;
return CreatePandasDataFrame(symbols, History(symbols, span, resolution));
}
///
/// Gets the historical data for the specified symbol between the specified dates. The symbol must exist in the Securities collection.
///
/// The symbols to retrieve historical data for
/// The start time in the algorithm's time zone
/// The end time in the algorithm's time zone
/// The resolution to request
/// A python dictionary with pandas DataFrame containing the requested historical data
public PyObject History(PyObject tickers, DateTime start, DateTime end, Resolution? resolution = null)
{
var symbols = GetSymbolsFromPyObject(tickers);
if (symbols == null) return null;
return CreatePandasDataFrame(symbols, History(symbols, start, end, resolution));
}
///
/// Creates a pandas DataFrame from an enumerable of slice containing the requested historical data
///
/// The symbols to retrieve historical data for
/// an enumerable of slice containing the requested historical data
/// A python dictionary with pandas DataFrame containing the requested historical data
private PyObject CreatePandasDataFrame(List symbols, IEnumerable history)
{
// If pandas is null (cound not be imported), return null
if (_pandas == null)
{
return null;
}
using (Py.GIL())
{
var pyDict = new PyDict();
foreach (var symbol in symbols)
{
var index = Securities[symbol].Type == SecurityType.Equity
? history.Get(symbol).Select(x => x.Time)
: history.Get(symbol).Select(x => x.Time);
var dataframe = new PyDict();
dataframe.SetItem("open", _pandas.Series(history.Get(symbol, Field.Open).ToList(), index));
dataframe.SetItem("high", _pandas.Series(history.Get(symbol, Field.High).ToList(), index));
dataframe.SetItem("low", _pandas.Series(history.Get(symbol, Field.Low).ToList(), index));
dataframe.SetItem("close", _pandas.Series(history.Get(symbol, Field.Close).ToList(), index));
dataframe.SetItem("volume", _pandas.Series(history.Get(symbol, Field.Volume).ToList(), index));
pyDict.SetItem(symbol.Value, _pandas.DataFrame(dataframe, columns: new[] { "open", "high", "low", "close", "volume" }.ToList()));
}
return pyDict;
}
}
///
/// Gets the symbols/string from a PyObject
///
/// PyObject containing symbols
/// List of symbols
private List GetSymbolsFromPyObject(PyObject pyObject)
{
using (Py.GIL())
{
if (PyString.IsStringType(pyObject))
{
Security security;
if (Securities.TryGetValue(pyObject.ToString(), out security))
{
return new List { security.Symbol };
}
return null;
}
var symbols = new List();
foreach (var item in pyObject)
{
Security security;
if (Securities.TryGetValue(item.ToString(), out security))
{
symbols.Add(security.Symbol);
}
}
return symbols.Count == 0 ? null : symbols;
}
}
///
/// Creates a type with a given name
///
/// Python object
/// Type object
private Type CreateType(PyObject type)
{
using (Py.GIL())
{
var an = new AssemblyName(type.Repr().Split('.')[1].Replace("\'>", ""));
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
return moduleBuilder.DefineType(an.Name,
TypeAttributes.Public |
TypeAttributes.Class |
TypeAttributes.AutoClass |
TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit |
TypeAttributes.AutoLayout,
// If the type has IsAuthCodeSet member, it is a PythonQuandl
type.HasAttr("IsAuthCodeSet") ? typeof(PythonQuandl) : typeof(PythonData))
.CreateType();
}
}
///
/// Encapsulates a python method with a
///
/// The data type
/// The python method
/// A that encapsulates the python method
private Func, IEnumerable> ToFunc(PyObject pyObject)
{
var testMod =
"from clr import AddReference\n" +
"AddReference(\"System\")\n" +
"AddReference(\"System.Collections\")\n" +
"AddReference(\"QuantConnect.Common\")\n" +
"from System import Func\n" +
"from System.Collections.Generic import IEnumerable\n" +
"from QuantConnect import Symbol\n" +
"from QuantConnect.Data.Fundamental import FineFundamental\n" +
"from QuantConnect.Data.UniverseSelection import CoarseFundamental\n" +
"def to_func(pyobject, type):\n" +
" return Func[IEnumerable[type], IEnumerable[Symbol]](pyobject)";
using (Py.GIL())
{
dynamic toFunc = PythonEngine.ModuleFromString("x", testMod).GetAttr("to_func");
return toFunc(pyObject, typeof(T))
.AsManagedObject(typeof(Func, IEnumerable>));
}
}
}
}