/*
* 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.Algorithm;
using QuantConnect.Configuration;
using QuantConnect.Data;
using QuantConnect.Data.Fundamental;
using QuantConnect.Data.Market;
using QuantConnect.Interfaces;
using QuantConnect.Lean.Engine;
using QuantConnect.Lean.Engine.DataFeeds;
using QuantConnect.Lean.Engine.HistoricalData;
using QuantConnect.Securities;
using QuantConnect.Securities.Future;
using QuantConnect.Securities.Option;
using QuantConnect.Statistics;
using QuantConnect.Util;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using QuantConnect.Packets;
using System.Threading.Tasks;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Lean.Engine.Setup;
using QuantConnect.Indicators;
using QuantConnect.Scheduling;
namespace QuantConnect.Research
{
///
/// Provides access to data for quantitative analysis
///
public class QuantBook : QCAlgorithm
{
private dynamic _pandas;
private IDataCacheProvider _dataCacheProvider;
private IDataProvider _dataProvider;
private static bool _isPythonNotebook;
static QuantBook()
{
//Determine if we are in a Python Notebook
try
{
PythonEngine.Initialize();
using (Py.GIL())
{
var isPython = PyModule.FromString(Guid.NewGuid().ToString(),
"try:\n" +
" import IPython\n" +
" def IsPythonNotebook():\n" +
" return (IPython.get_ipython() != None)\n" +
"except:\n" +
" print('No IPython installed')\n" +
" def IsPythonNotebook():\n" +
" return false\n").GetAttr("IsPythonNotebook").Invoke();
isPython.TryConvert(out _isPythonNotebook);
}
}
catch
{
//Default to false
_isPythonNotebook = false;
Logging.Log.Error("QuantBook failed to determine Notebook kernel language");
}
RecycleMemory();
Logging.Log.Trace($"QuantBook started; Is Python: {_isPythonNotebook}");
}
///
/// constructor.
/// Provides access to data for quantitative analysis
///
public QuantBook() : base()
{
try
{
using (Py.GIL())
{
_pandas = Py.Import("pandas");
}
// Issue #4892 : Set start time relative to NY time
// when the data is available from the previous day
var newYorkTime = DateTime.UtcNow.ConvertFromUtc(TimeZones.NewYork);
var hourThreshold = Config.GetInt("qb-data-hour", 9);
// If it is after our hour threshold; then we can use today
if (newYorkTime.Hour >= hourThreshold)
{
SetStartDate(newYorkTime);
}
else
{
SetStartDate(newYorkTime - TimeSpan.FromDays(1));
}
// Sets PandasConverter
SetPandasConverter();
// Reset our composer; needed for re-creation of QuantBook
Composer.Instance.Reset();
var composer = Composer.Instance;
Config.Reset();
// Create our handlers with our composer instance
var systemHandlers = LeanEngineSystemHandlers.FromConfiguration(composer);
// init the API
systemHandlers.Initialize();
var algorithmHandlers = LeanEngineAlgorithmHandlers.FromConfiguration(composer, researchMode: true);
;
var algorithmPacket = new BacktestNodePacket
{
UserToken = Globals.UserToken,
UserId = Globals.UserId,
ProjectId = Globals.ProjectId,
OrganizationId = Globals.OrganizationID,
Version = Globals.Version
};
ProjectId = algorithmPacket.ProjectId;
systemHandlers.LeanManager.Initialize(systemHandlers,
algorithmHandlers,
algorithmPacket,
new AlgorithmManager(false));
systemHandlers.LeanManager.SetAlgorithm(this);
algorithmHandlers.DataPermissionsManager.Initialize(algorithmPacket);
algorithmHandlers.ObjectStore.Initialize(algorithmPacket.UserId,
algorithmPacket.ProjectId,
algorithmPacket.UserToken,
new Controls
{
// if <= 0 we disable periodic persistence and make it synchronous
PersistenceIntervalSeconds = -1,
StorageLimit = Config.GetValue("storage-limit", 10737418240L),
StorageFileCount = Config.GetInt("storage-file-count", 10000),
StorageAccess = Config.GetValue("storage-permissions", new Packets.StoragePermissions())
});
SetObjectStore(algorithmHandlers.ObjectStore);
_dataCacheProvider = new ZipDataCacheProvider(algorithmHandlers.DataProvider);
_dataProvider = algorithmHandlers.DataProvider;
var symbolPropertiesDataBase = SymbolPropertiesDatabase.FromDataFolder();
var registeredTypes = new RegisteredSecurityDataTypesProvider();
var securityService = new SecurityService(Portfolio.CashBook,
MarketHoursDatabase,
symbolPropertiesDataBase,
this,
registeredTypes,
new SecurityCacheProvider(Portfolio),
algorithm: this);
Securities.SetSecurityService(securityService);
SubscriptionManager.SetDataManager(
new DataManager(new NullDataFeed(),
new UniverseSelection(this, securityService, algorithmHandlers.DataPermissionsManager, algorithmHandlers.DataProvider),
this,
TimeKeeper,
MarketHoursDatabase,
false,
registeredTypes,
algorithmHandlers.DataPermissionsManager));
var mapFileProvider = algorithmHandlers.MapFileProvider;
HistoryProvider = new HistoryProviderManager();
HistoryProvider.Initialize(
new HistoryProviderInitializeParameters(
null,
null,
algorithmHandlers.DataProvider,
_dataCacheProvider,
mapFileProvider,
algorithmHandlers.FactorFileProvider,
null,
true,
algorithmHandlers.DataPermissionsManager,
ObjectStore,
Settings
)
);
var initParameters = new ChainProviderInitializeParameters(mapFileProvider, HistoryProvider);
var optionChainProvider = new BacktestingOptionChainProvider();
optionChainProvider.Initialize(initParameters);
var futureChainProvider = new BacktestingFutureChainProvider();
futureChainProvider.Initialize(initParameters);
SetOptionChainProvider(new CachingOptionChainProvider(optionChainProvider));
SetFutureChainProvider(new CachingFutureChainProvider(futureChainProvider));
SetAlgorithmMode(AlgorithmMode.Research);
SetDeploymentTarget(Config.GetValue("deployment-target", DeploymentTarget.LocalPlatform));
}
catch (Exception exception)
{
throw new Exception("QuantBook.Main(): " + exception);
}
}
///
/// Python implementation of GetFundamental, get fundamental data for input symbols or tickers
///
/// The symbols or tickers to retrieve fundamental data for
/// Selects a value from the Fundamental data to filter the request output
/// The start date of selected data
/// The end date of selected data
/// pandas DataFrame
[Obsolete("Please use the 'UniverseHistory()' API")]
public PyObject GetFundamental(PyObject input, string selector = null, DateTime? start = null, DateTime? end = null)
{
//Covert to symbols
var symbols = PythonUtil.ConvertToSymbols(input);
//Fetch the data
var fundamentalData = GetAllFundamental(symbols, selector, start, end);
using (Py.GIL())
{
var data = new PyDict();
foreach (var day in fundamentalData.OrderBy(x => x.Key))
{
var orderedValues = day.Value.OrderBy(x => x.Key.ID.ToString()).ToList();
var columns = orderedValues.Select(x => x.Key.ID.ToString());
var values = orderedValues.Select(x => x.Value);
var row = _pandas.Series(values, columns);
data.SetItem(day.Key.ToPython(), row);
}
return _pandas.DataFrame.from_dict(data, orient:"index");
}
}
///
/// Get fundamental data from given symbols
///
/// The symbols to retrieve fundamental data for
/// Selects a value from the Fundamental data to filter the request output
/// The start date of selected data
/// The end date of selected data
/// Enumerable collection of DataDictionaries, one dictionary for each day there is data
[Obsolete("Please use the 'UniverseHistory()' API")]
public IEnumerable> GetFundamental(IEnumerable symbols, string selector = null, DateTime? start = null, DateTime? end = null)
{
var data = GetAllFundamental(symbols, selector, start, end);
foreach (var kvp in data.OrderBy(kvp => kvp.Key))
{
yield return kvp.Value;
}
}
///
/// Get fundamental data for a given symbol
///
/// The symbol to retrieve fundamental data for
/// Selects a value from the Fundamental data to filter the request output
/// The start date of selected data
/// The end date of selected data
/// Enumerable collection of DataDictionaries, one Dictionary for each day there is data.
[Obsolete("Please use the 'UniverseHistory()' API")]
public IEnumerable> GetFundamental(Symbol symbol, string selector = null, DateTime? start = null, DateTime? end = null)
{
var list = new List
{
symbol
};
return GetFundamental(list, selector, start, end);
}
///
/// Get fundamental data for a given set of tickers
///
/// The tickers to retrieve fundamental data for
/// Selects a value from the Fundamental data to filter the request output
/// The start date of selected data
/// The end date of selected data
/// Enumerable collection of DataDictionaries, one dictionary for each day there is data.
[Obsolete("Please use the 'UniverseHistory()' API")]
public IEnumerable> GetFundamental(IEnumerable tickers, string selector = null, DateTime? start = null, DateTime? end = null)
{
var list = new List();
foreach (var ticker in tickers)
{
list.Add(QuantConnect.Symbol.Create(ticker, SecurityType.Equity, Market.USA));
}
return GetFundamental(list, selector, start, end);
}
///
/// Get fundamental data for a given ticker
///
/// The symbol to retrieve fundamental data for
/// Selects a value from the Fundamental data to filter the request output
/// The start date of selected data
/// The end date of selected data
/// Enumerable collection of DataDictionaries, one Dictionary for each day there is data.
[Obsolete("Please use the 'UniverseHistory()' API")]
public dynamic GetFundamental(string ticker, string selector = null, DateTime? start = null, DateTime? end = null)
{
//Check if its Python; PythonNet likes to convert the strings, but for python we want the DataFrame as the return object
//So we must route the function call to the Python version.
if (_isPythonNotebook)
{
return GetFundamental(ticker.ToPython(), selector, start, end);
}
var symbol = QuantConnect.Symbol.Create(ticker, SecurityType.Equity, Market.USA);
var list = new List
{
symbol
};
return GetFundamental(list, selector, start, end);
}
///
/// Gets object for a given symbol, date and resolution
///
/// The symbol to retrieve historical option data for
/// The target option ticker. This is useful when the option ticker does not match the underlying, e.g. SPX index and the SPXW weekly option. If null is provided will use underlying
/// The history request start time
/// The history request end time. Defaults to 1 day if null
/// The resolution to request
/// True to fill forward missing data, false otherwise
/// True to include extended market hours data, false otherwise
/// A object that contains historical option data.
public OptionHistory OptionHistory(Symbol symbol, string targetOption, DateTime start, DateTime? end = null, Resolution? resolution = null,
bool fillForward = true, bool extendedMarketHours = false)
{
symbol = GetOptionSymbolForHistoryRequest(symbol, targetOption, resolution, fillForward);
return OptionHistory(symbol, start, end, resolution, fillForward, extendedMarketHours);
}
///
/// Gets object for a given symbol, date and resolution
///
/// The symbol to retrieve historical option data for
/// The target option ticker. This is useful when the option ticker does not match the underlying, e.g. SPX index and the SPXW weekly option. If null is provided will use underlying
/// The history request start time
/// The history request end time. Defaults to 1 day if null
/// The resolution to request
/// True to fill forward missing data, false otherwise
/// True to include extended market hours data, false otherwise
/// A object that contains historical option data.
[Obsolete("Please use the 'OptionHistory()' API")]
public OptionHistory GetOptionHistory(Symbol symbol, string targetOption, DateTime start, DateTime? end = null, Resolution? resolution = null,
bool fillForward = true, bool extendedMarketHours = false)
{
return OptionHistory(symbol, targetOption, start, end, resolution, fillForward, extendedMarketHours);
}
///
/// Gets object for a given symbol, date and resolution
///
/// The symbol to retrieve historical option data for
/// The history request start time
/// The history request end time. Defaults to 1 day if null
/// The resolution to request
/// True to fill forward missing data, false otherwise
/// True to include extended market hours data, false otherwise
/// A object that contains historical option data.
public OptionHistory OptionHistory(Symbol symbol, DateTime start, DateTime? end = null, Resolution? resolution = null,
bool fillForward = true, bool extendedMarketHours = false)
{
if (!end.HasValue || end.Value == start)
{
end = start.AddDays(1);
}
// Load a canonical option Symbol if the user provides us with an underlying Symbol
symbol = GetOptionSymbolForHistoryRequest(symbol, null, resolution, fillForward);
IEnumerable symbols;
if (symbol.IsCanonical())
{
// canonical symbol, lets find the contracts
var option = Securities[symbol] as Option;
if (!Securities.ContainsKey(symbol.Underlying))
{
var resolutionToUseForUnderlying = resolution ?? SubscriptionManager.SubscriptionDataConfigService
.GetSubscriptionDataConfigs(symbol.Underlying)
.Select(x => (Resolution?)x.Resolution)
.DefaultIfEmpty(null)
.Min();
if (!resolutionToUseForUnderlying.HasValue && UniverseManager.TryGetValue(symbol, out var universe))
{
resolutionToUseForUnderlying = universe.UniverseSettings.Resolution;
}
if (symbol.Underlying.SecurityType == SecurityType.Equity)
{
// only add underlying if not present
AddEquity(symbol.Underlying.Value, resolutionToUseForUnderlying, fillForward: fillForward,
extendedMarketHours: extendedMarketHours);
}
else if (symbol.Underlying.SecurityType == SecurityType.Index)
{
// only add underlying if not present
AddIndex(symbol.Underlying.Value, resolutionToUseForUnderlying, fillForward: fillForward);
}
else if (symbol.Underlying.SecurityType == SecurityType.Future && symbol.Underlying.IsCanonical())
{
AddFuture(symbol.Underlying.ID.Symbol, resolutionToUseForUnderlying, fillForward: fillForward,
extendedMarketHours: extendedMarketHours);
}
else if (symbol.Underlying.SecurityType == SecurityType.Future)
{
AddFutureContract(symbol.Underlying, resolutionToUseForUnderlying, fillForward: fillForward,
extendedMarketHours: extendedMarketHours);
}
}
var allSymbols = new HashSet();
var optionFilterUniverse = new OptionFilterUniverse(option);
foreach (var (date, chainData, underlyingData) in GetChainHistory(option, start, end.Value, extendedMarketHours))
{
if (underlyingData is not null)
{
optionFilterUniverse.Refresh(chainData, underlyingData, underlyingData.EndTime);
allSymbols.UnionWith(option.ContractFilter.Filter(optionFilterUniverse).Select(x => x.Symbol));
}
}
var distinctSymbols = allSymbols.Distinct().Select(x => new OptionUniverse() { Symbol = x, Time = start });
symbols = allSymbols.Concat(new[] { symbol.Underlying });
}
else
{
// the symbol is a contract
symbols = new List{ symbol };
}
return new OptionHistory(History(symbols, start, end.Value, resolution, fillForward, extendedMarketHours));
}
///
/// Gets object for a given symbol, date and resolution
///
/// The symbol to retrieve historical option data for
/// The history request start time
/// The history request end time. Defaults to 1 day if null
/// The resolution to request
/// True to fill forward missing data, false otherwise
/// True to include extended market hours data, false otherwise
/// A object that contains historical option data.
[Obsolete("Please use the 'OptionHistory()' API")]
public OptionHistory GetOptionHistory(Symbol symbol, DateTime start, DateTime? end = null, Resolution? resolution = null,
bool fillForward = true, bool extendedMarketHours = false)
{
return OptionHistory(symbol, start, end, resolution, fillForward, extendedMarketHours);
}
///
/// Gets object for a given symbol, date and resolution
///
/// The symbol to retrieve historical future data for
/// The history request start time
/// The history request end time. Defaults to 1 day if null
/// The resolution to request
/// True to fill forward missing data, false otherwise
/// True to include extended market hours data, false otherwise
/// A object that contains historical future data.
public FutureHistory FutureHistory(Symbol symbol, DateTime start, DateTime? end = null, Resolution? resolution = null,
bool fillForward = true, bool extendedMarketHours = false)
{
if (!end.HasValue || end.Value == start)
{
end = start.AddDays(1);
}
var allSymbols = new HashSet();
if (symbol.IsCanonical())
{
// canonical symbol, lets find the contracts
var future = Securities[symbol] as Future;
foreach (var (date, chainData, underlyingData) in GetChainHistory(future, start, end.Value, extendedMarketHours))
{
allSymbols.UnionWith(future.ContractFilter.Filter(new FutureFilterUniverse(chainData, date)).Select(x => x.Symbol));
}
}
else
{
// the symbol is a contract
allSymbols.Add(symbol);
}
return new FutureHistory(History(allSymbols, start, end.Value, resolution, fillForward, extendedMarketHours));
}
///
/// Gets object for a given symbol, date and resolution
///
/// The symbol to retrieve historical future data for
/// The history request start time
/// The history request end time. Defaults to 1 day if null
/// The resolution to request
/// True to fill forward missing data, false otherwise
/// True to include extended market hours data, false otherwise
/// A object that contains historical future data.
[Obsolete("Please use the 'FutureHistory()' API")]
public FutureHistory GetFutureHistory(Symbol symbol, DateTime start, DateTime? end = null, Resolution? resolution = null,
bool fillForward = true, bool extendedMarketHours = false)
{
return FutureHistory(symbol, start, end, resolution, fillForward, extendedMarketHours);
}
///
/// Gets the historical data of an indicator for the specified symbol. The exact number of bars will be returned.
/// The symbol must exist in the Securities collection.
///
/// The symbol to retrieve historical data for
/// The number of bars to request
/// The resolution to request
/// Selects a value from the BaseData to send into the indicator, if null defaults to the Value property of BaseData (x => x.Value)
/// pandas.DataFrame of historical data of an indicator
[Obsolete("Please use the 'IndicatorHistory()', pandas dataframe available through '.DataFrame'")]
public PyObject Indicator(IndicatorBase indicator, Symbol symbol, int period, Resolution? resolution = null, Func selector = null)
{
var history = History(new[] { symbol }, period, resolution);
return IndicatorHistory(indicator, history, selector).DataFrame;
}
///
/// Gets the historical data of a bar indicator for the specified symbol. The exact number of bars will be returned.
/// The symbol must exist in the Securities collection.
///
/// The symbol to retrieve historical data for
/// The number of bars to request
/// The resolution to request
/// Selects a value from the BaseData to send into the indicator, if null defaults to the Value property of BaseData (x => x.Value)
/// pandas.DataFrame of historical data of a bar indicator
[Obsolete("Please use the 'IndicatorHistory()', pandas dataframe available through '.DataFrame'")]
public PyObject Indicator(IndicatorBase indicator, Symbol symbol, int period, Resolution? resolution = null, Func selector = null)
{
var history = History(new[] { symbol }, period, resolution);
return IndicatorHistory(indicator, history, selector).DataFrame;
}
///
/// Gets the historical data of a bar indicator for the specified symbol. The exact number of bars will be returned.
/// The symbol must exist in the Securities collection.
///
/// The symbol to retrieve historical data for
/// The number of bars to request
/// The resolution to request
/// Selects a value from the BaseData to send into the indicator, if null defaults to the Value property of BaseData (x => x.Value)
/// pandas.DataFrame of historical data of a bar indicator
[Obsolete("Please use the 'IndicatorHistory()', pandas dataframe available through '.DataFrame'")]
public PyObject Indicator(IndicatorBase indicator, Symbol symbol, int period, Resolution? resolution = null, Func selector = null)
{
var history = History(new[] { symbol }, period, resolution);
return IndicatorHistory(indicator, history, selector).DataFrame;
}
///
/// Gets the historical data of an indicator for the specified symbol. The exact number of bars will be returned.
/// The symbol must exist in the Securities collection.
///
/// Indicator
/// The symbol to retrieve historical data for
/// The span over which to retrieve recent historical data
/// The resolution to request
/// Selects a value from the BaseData to send into the indicator, if null defaults to the Value property of BaseData (x => x.Value)
/// pandas.DataFrame of historical data of an indicator
[Obsolete("Please use the 'IndicatorHistory()', pandas dataframe available through '.DataFrame'")]
public PyObject Indicator(IndicatorBase indicator, Symbol symbol, TimeSpan span, Resolution? resolution = null, Func selector = null)
{
var history = History(new[] { symbol }, span, resolution);
return IndicatorHistory(indicator, history, selector).DataFrame;
}
///
/// Gets the historical data of a bar indicator for the specified symbol. The exact number of bars will be returned.
/// The symbol must exist in the Securities collection.
///
/// Indicator
/// The symbol to retrieve historical data for
/// The span over which to retrieve recent historical data
/// The resolution to request
/// Selects a value from the BaseData to send into the indicator, if null defaults to the Value property of BaseData (x => x.Value)
/// pandas.DataFrame of historical data of a bar indicator
[Obsolete("Please use the 'IndicatorHistory()', pandas dataframe available through '.DataFrame'")]
public PyObject Indicator(IndicatorBase indicator, Symbol symbol, TimeSpan span, Resolution? resolution = null, Func selector = null)
{
var history = History(new[] { symbol }, span, resolution);
return IndicatorHistory(indicator, history, selector).DataFrame;
}
///
/// Gets the historical data of a bar indicator for the specified symbol. The exact number of bars will be returned.
/// The symbol must exist in the Securities collection.
///
/// Indicator
/// The symbol to retrieve historical data for
/// The span over which to retrieve recent historical data
/// The resolution to request
/// Selects a value from the BaseData to send into the indicator, if null defaults to the Value property of BaseData (x => x.Value)
/// pandas.DataFrame of historical data of a bar indicator
[Obsolete("Please use the 'IndicatorHistory()', pandas dataframe available through '.DataFrame'")]
public PyObject Indicator(IndicatorBase indicator, Symbol symbol, TimeSpan span, Resolution? resolution = null, Func selector = null)
{
var history = History(new[] { symbol }, span, resolution);
return IndicatorHistory(indicator, history, selector).DataFrame;
}
///
/// Gets the historical data of an indicator for the specified symbol. The exact number of bars will be returned.
/// The symbol must exist in the Securities collection.
///
/// Indicator
/// The symbol 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
/// Selects a value from the BaseData to send into the indicator, if null defaults to the Value property of BaseData (x => x.Value)
/// pandas.DataFrame of historical data of an indicator
[Obsolete("Please use the 'IndicatorHistory()', pandas dataframe available through '.DataFrame'")]
public PyObject Indicator(IndicatorBase indicator, Symbol symbol, DateTime start, DateTime end, Resolution? resolution = null, Func selector = null)
{
var history = History(new[] { symbol }, start, end, resolution);
return IndicatorHistory(indicator, history, selector).DataFrame;
}
///
/// Gets the historical data of a bar indicator for the specified symbol. The exact number of bars will be returned.
/// The symbol must exist in the Securities collection.
///
/// Indicator
/// The symbol 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
/// Selects a value from the BaseData to send into the indicator, if null defaults to the Value property of BaseData (x => x.Value)
/// pandas.DataFrame of historical data of a bar indicator
[Obsolete("Please use the 'IndicatorHistory()', pandas dataframe available through '.DataFrame'")]
public PyObject Indicator(IndicatorBase indicator, Symbol symbol, DateTime start, DateTime end, Resolution? resolution = null, Func selector = null)
{
var history = History(new[] { symbol }, start, end, resolution);
return IndicatorHistory(indicator, history, selector).DataFrame;
}
///
/// Gets the historical data of a bar indicator for the specified symbol. The exact number of bars will be returned.
/// The symbol must exist in the Securities collection.
///
/// Indicator
/// The symbol 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
/// Selects a value from the BaseData to send into the indicator, if null defaults to the Value property of BaseData (x => x.Value)
/// pandas.DataFrame of historical data of a bar indicator
[Obsolete("Please use the 'IndicatorHistory()', pandas dataframe available through '.DataFrame'")]
public PyObject Indicator(IndicatorBase indicator, Symbol symbol, DateTime start, DateTime end, Resolution? resolution = null, Func selector = null)
{
var history = History(new[] { symbol }, start, end, resolution);
return IndicatorHistory(indicator, history, selector).DataFrame;
}
///
/// Will return the universe selection data and will optionally perform selection
///
/// The universe selection universe data type, for example Fundamentals
/// The selection data type, for example Fundamental
/// The start date
/// Optionally the end date, will default to today
/// Optionally the universe selection function
/// Date rule to apply for the history data
/// Enumerable of universe selection data for each date, filtered if the func was provided
public IEnumerable> UniverseHistory(DateTime start, DateTime? end = null, Func, IEnumerable> func = null, IDateRule dateRule = null)
where T1 : BaseDataCollection
where T2 : IBaseData
{
var universeSymbol = ((BaseDataCollection)typeof(T1).GetBaseDataInstance()).UniverseSymbol();
var symbols = new[] { universeSymbol };
var endDate = end ?? DateTime.UtcNow.Date;
var requests = CreateDateRangeHistoryRequests(new[] { universeSymbol }, typeof(T1), start, endDate);
var history = GetDataTypedHistory(requests).Select(x => x.Values.Single());
HashSet filteredSymbols = null;
Func> castDataPoint = dataPoint =>
{
var castedType = dataPoint.Data.OfType();
if (func != null)
{
var selection = func(castedType);
if (!ReferenceEquals(selection, Universe.Unchanged))
{
filteredSymbols = selection.ToHashSet();
}
return castedType.Where(x => filteredSymbols == null || filteredSymbols.Contains(x.Symbol));
}
else
{
return castedType;
}
};
Func getTime = datapoint => datapoint.EndTime.Date;
return PerformSelection, BaseDataCollection>(history, castDataPoint, getTime, start, endDate, dateRule);
}
///
/// Will return the universe selection data and will optionally perform selection
///
/// The universe to fetch the data for
/// The start date
/// Optionally the end date, will default to today
/// Date rule to apply for the history data
/// Enumerable of universe selection data for each date, filtered if the func was provided
public IEnumerable> UniverseHistory(Universe universe, DateTime start, DateTime? end = null, IDateRule dateRule = null)
{
return RunUniverseSelection(universe, start, end, dateRule);
}
///
/// Will return the universe selection data and will optionally perform selection
///
/// The universe to fetch the data for
/// The start date
/// Optionally the end date, will default to today
/// Optionally the universe selection function
/// Date rule to apply for the history data
/// Whether to flatten the resulting data frame.
/// For universe data, the each row represents a day of data, and the data is stored in a list in a cell of the data frame.
/// If flatten is true, the resulting data frame will contain one row per universe constituent,
/// and each property of the constituent will be a column in the data frame.
/// Enumerable of universe selection data for each date, filtered if the func was provided
public PyObject UniverseHistory(PyObject universe, DateTime start, DateTime? end = null, PyObject func = null, IDateRule dateRule = null,
bool flatten = false)
{
if (universe.TryConvert(out var convertedUniverse))
{
if (func != null)
{
throw new ArgumentException($"When providing a universe, the selection func argument isn't supported. Please provider a universe or a type and a func");
}
var filteredUniverseSelectionData = RunUniverseSelection(convertedUniverse, start, end, dateRule);
return GetDataFrame(filteredUniverseSelectionData, flatten);
}
// for backwards compatibility
if (universe.TryConvert(out var convertedType) && convertedType.IsAssignableTo(typeof(BaseDataCollection)))
{
var endDate = end ?? DateTime.UtcNow.Date;
var universeSymbol = ((BaseDataCollection)convertedType.GetBaseDataInstance()).UniverseSymbol();
if (func == null)
{
return History(universe, universeSymbol, start, endDate, flatten: flatten);
}
var requests = CreateDateRangeHistoryRequests(new[] { universeSymbol }, convertedType, start, endDate);
var history = History(requests);
return GetDataFrame(GetFilteredSlice(history, func, start, endDate, dateRule), flatten, convertedType);
}
throw new ArgumentException($"Failed to convert given universe {universe}. Please provider a valid {nameof(Universe)}");
}
///
/// Gets Portfolio Statistics from a pandas.DataFrame with equity and benchmark values
///
/// pandas.DataFrame with the information required to compute the Portfolio statistics
/// object wrapped in a with the portfolio statistics.
public PyDict GetPortfolioStatistics(PyObject dataFrame)
{
var dictBenchmark = new SortedDictionary();
var dictEquity = new SortedDictionary();
var dictPL = new SortedDictionary();
using (Py.GIL())
{
var result = new PyDict();
try
{
// Converts the data from pandas.DataFrame into dictionaries keyed by time
var df = ((dynamic)dataFrame).dropna();
dictBenchmark = GetDictionaryFromSeries((PyObject)df["benchmark"]);
dictEquity = GetDictionaryFromSeries((PyObject)df["equity"]);
dictPL = GetDictionaryFromSeries((PyObject)df["equity"].pct_change());
}
catch (PythonException e)
{
result.SetItem("Runtime Error", e.Message.ToPython());
return result;
}
// Convert the double into decimal
var equity = new SortedDictionary(dictEquity.ToDictionary(kvp => kvp.Key, kvp => (decimal)kvp.Value));
var profitLoss = new SortedDictionary(dictPL.ToDictionary(kvp => kvp.Key, kvp => double.IsNaN(kvp.Value) ? 0 : (decimal)kvp.Value));
// Gets the last value of the day of the benchmark and equity
var listBenchmark = CalculateDailyRateOfChange(dictBenchmark);
var listPerformance = CalculateDailyRateOfChange(dictEquity);
// Gets the startting capital
var startingCapital = Convert.ToDecimal(dictEquity.FirstOrDefault().Value);
// call method to set tradingDayPerYear for Algorithm (use: backwards compatibility)
BaseSetupHandler.SetBrokerageTradingDayPerYear(algorithm: this);
// Compute portfolio statistics
var stats = new PortfolioStatistics(profitLoss, equity, new(), listPerformance, listBenchmark, startingCapital, RiskFreeInterestRateModel,
Settings.TradingDaysPerYear.Value);
result.SetItem("Average Win (%)", Convert.ToDouble(stats.AverageWinRate * 100).ToPython());
result.SetItem("Average Loss (%)", Convert.ToDouble(stats.AverageLossRate * 100).ToPython());
result.SetItem("Compounding Annual Return (%)", Convert.ToDouble(stats.CompoundingAnnualReturn * 100m).ToPython());
result.SetItem("Drawdown (%)", Convert.ToDouble(stats.Drawdown * 100).ToPython());
result.SetItem("Expectancy", Convert.ToDouble(stats.Expectancy).ToPython());
result.SetItem("Net Profit (%)", Convert.ToDouble(stats.TotalNetProfit * 100).ToPython());
result.SetItem("Sharpe Ratio", Convert.ToDouble(stats.SharpeRatio).ToPython());
result.SetItem("Win Rate (%)", Convert.ToDouble(stats.WinRate * 100).ToPython());
result.SetItem("Loss Rate (%)", Convert.ToDouble(stats.LossRate * 100).ToPython());
result.SetItem("Profit-Loss Ratio", Convert.ToDouble(stats.ProfitLossRatio).ToPython());
result.SetItem("Alpha", Convert.ToDouble(stats.Alpha).ToPython());
result.SetItem("Beta", Convert.ToDouble(stats.Beta).ToPython());
result.SetItem("Annual Standard Deviation", Convert.ToDouble(stats.AnnualStandardDeviation).ToPython());
result.SetItem("Annual Variance", Convert.ToDouble(stats.AnnualVariance).ToPython());
result.SetItem("Information Ratio", Convert.ToDouble(stats.InformationRatio).ToPython());
result.SetItem("Tracking Error", Convert.ToDouble(stats.TrackingError).ToPython());
result.SetItem("Treynor Ratio", Convert.ToDouble(stats.TreynorRatio).ToPython());
return result;
}
}
///
/// Get's the universe data for the specified date
///
private IEnumerable GetChainHistory(Symbol canonicalSymbol, DateTime date, out BaseData underlyingData)
where T : BaseChainUniverseData
{
// Use this GetEntry extension method since it's data type dependent, so we get the correct entry for the option universe
var marketHoursEntry = MarketHoursDatabase.GetEntry(canonicalSymbol, new[] { typeof(T) });
var startInExchangeTz = QuantConnect.Time.GetStartTimeForTradeBars(marketHoursEntry.ExchangeHours, date, QuantConnect.Time.OneDay, 1,
extendedMarketHours: false, marketHoursEntry.DataTimeZone);
var start = startInExchangeTz.ConvertTo(marketHoursEntry.ExchangeHours.TimeZone, TimeZone);
var end = date.ConvertTo(marketHoursEntry.ExchangeHours.TimeZone, TimeZone);
var universeData = History(canonicalSymbol, start, end).SingleOrDefault();
if (universeData is not null)
{
underlyingData = universeData.Underlying;
return universeData.Data.Cast();
}
underlyingData = null;
return Enumerable.Empty();
}
///
/// Helper method to get option/future chain historical data for a given date range
///
private IEnumerable<(DateTime Date, IEnumerable ChainData, BaseData UnderlyingData)> GetChainHistory(
Security security, DateTime start, DateTime end, bool extendedMarketHours)
where T : BaseChainUniverseData
{
foreach (var date in QuantConnect.Time.EachTradeableDay(security, start.Date, end.Date, extendedMarketHours))
{
var universeData = GetChainHistory(security.Symbol, date, out var underlyingData);
yield return (date, universeData, underlyingData);
}
}
///
/// Helper method to perform selection on the given data and filter it
///
private IEnumerable GetFilteredSlice(IEnumerable history, dynamic func, DateTime start, DateTime end, IDateRule dateRule = null)
{
HashSet filteredSymbols = null;
Func processSlice = slice =>
{
var filteredData = slice.AllData.OfType();
using (Py.GIL())
{
using PyObject selection = func(filteredData.SelectMany(baseData => baseData.Data));
if (!selection.TryConvert