Implements History Requests returning pandas.DataFrame

Algorithms written in python can access to new overloads for the QCAlgorithm.History method that returns a dictionary with pandas.DataFrame
This commit is contained in:
AlexCatarino
2017-07-06 15:09:59 +01:00
parent 93941deac0
commit 3c23759efd
4 changed files with 146 additions and 1 deletions
+138
View File
@@ -33,6 +33,26 @@ namespace QuantConnect.Algorithm
{
public partial class QCAlgorithm
{
private dynamic _pandas;
/// <summary>
/// Sets pandas library
/// </summary>
public void SetPandas()
{
try
{
using (Py.GIL())
{
_pandas = Py.Import("pandas");
}
}
catch (PythonException pythonException)
{
Error("QCAlgorithm.SetPandas(): Failed to import pandas module: " + pythonException);
}
}
/// <summary>
/// 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)
@@ -302,6 +322,124 @@ namespace QuantConnect.Algorithm
PlotIndicator(chart, waitForReady, new[] { first, second, third, fourth }.Where(x => x != null).ToArray());
}
/// <summary>
/// Gets the historical data for the specified symbol. The exact number of bars will be returned.
/// The symbol must exist in the Securities collection.
/// </summary>
/// <param name="tickers">The symbols to retrieve historical data for</param>
/// <param name="periods">The number of bars to request</param>
/// <param name="resolution">The resolution to request</param>
/// <returns>A python dictionary with pandas DataFrame containing the requested historical data</returns>
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));
}
/// <summary>
/// Gets the historical data for the specified symbols over the requested span.
/// The symbols must exist in the Securities collection.
/// </summary>
/// <param name="tickers">The symbols to retrieve historical data for</param>
/// <param name="span">The span over which to retrieve recent historical data</param>
/// <param name="resolution">The resolution to request</param>
/// <returns>A python dictionary with pandas DataFrame containing the requested historical data</returns>
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));
}
/// <summary>
/// Gets the historical data for the specified symbol between the specified dates. The symbol must exist in the Securities collection.
/// </summary>
/// <param name="tickers">The symbols to retrieve historical data for</param>
/// <param name="start">The start time in the algorithm's time zone</param>
/// <param name="end">The end time in the algorithm's time zone</param>
/// <param name="resolution">The resolution to request</param>
/// <returns>A python dictionary with pandas DataFrame containing the requested historical data</returns>
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));
}
/// <summary>
/// Creates a pandas DataFrame from an enumerable of slice containing the requested historical data
/// </summary>
/// <param name="symbols">The symbols to retrieve historical data for</param>
/// <param name="history">an enumerable of slice containing the requested historical data</param>
/// <returns>A python dictionary with pandas DataFrame containing the requested historical data</returns>
private PyObject CreatePandasDataFrame(List<Symbol> symbols, IEnumerable<Slice> 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<TradeBar>(symbol).Select(x => x.Time)
: history.Get<QuoteBar>(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;
}
}
/// <summary>
/// Gets the symbols/string from a PyObject
/// </summary>
/// <param name="pyObject">PyObject containing symbols</param>
/// <returns>List of symbols</returns>
private List<Symbol> GetSymbolsFromPyObject(PyObject pyObject)
{
using (Py.GIL())
{
if (PyString.IsStringType(pyObject))
{
Security security;
if (Securities.TryGetValue(pyObject.ToString(), out security))
{
return new List<Symbol> { security.Symbol };
}
return null;
}
var symbols = new List<Symbol>();
foreach (var item in pyObject)
{
Security security;
if (Securities.TryGetValue(item.ToString(), out security))
{
symbols.Add(security.Symbol);
}
}
return symbols.Count == 0 ? null : symbols;
}
}
/// <summary>
/// Creates a type with a given name
/// </summary>