Nasdaq
Data Link
Introduction
The Data Link dataset by Nasdaq, previously known as Quandl, is a collection of alternative data sets. It has indexed millions of time-series datasets from over 400 sources, which start in different years. This dataset is delivered on several frequencies, but the free data sets have often a daily frequency.
For more information about the Data Link dataset, including CLI commands and pricing, see the dataset listing.
About the Provider
"Nasdaq" was initially an acronym for the National Association of Securities Dealers Automated Quotations. It was founded in 1971 by the National Association of Securities Dealers (NASD), now known as the Financial Industry Regulatory Authority (FINRA), with the goal to provide financial services and operate stock exchanges.
On Dec. 4th, 2018, Nasdaq announced it had acquired Quandl, Inc., a leading provider of alternative and core financial data. Quandl was founded by Tammer Kamel in 2012, with goal of making it easy for anyone to find and use high-quality data effectively in their professional decision-making. In 2021, Quandl was replaced by Nasdaq Data Link.
Getting Started
The following snippet demonstrates how to request data from the Data Link dataset:
# For premium datasets, provide your API Key
# NasdaqDataLink.set_auth_code(self.get_parameter("nasdaq-data-link-api-key"))
self.bitcoin_chain_symbol = self.add_data(NasdaqDataLink, "QDL/BCHAIN", Resolution.DAILY).symbol
# This dataset has one data column ("Value")
self.bitfinex_exchange_rate_symbol = self.add_data(NasdaqCustomColumns, "QDL/BITFINEX", Resolution.DAILY).symbol
# This dataset has multiple data columns. Create a subclass to set the default value column.
class NasdaqCustomColumns(NasdaqDataLink):
def __init__(self) -> None:
# Select the column "mid".
self.value_column_name = "mid" // For premium datasets, provide your API Key
// NasdaqDataLink.SetAuthCode(GetParameter("nasdaq-data-link-api-key"));
_bitcoinChainSymbol = AddData<NasdaqDataLink>("QDL/BCHAIN", Resolution.Daily).Symbol;
// This dataset has one data column ("Value")
_bitfinexExchangeRateSymbol = AddData<NasdaqCustomColumns>("QDL/BITFINEX", Resolution.Daily).Symbol;
// This dataset has multiple data columns. Create a subclass to set the default value column.
public class NasdaqCustomColumns : NasdaqDataLink
{
// Select the column "mid"
public NasdaqCustomColumns() : base(valueColumnName: "mid") { }
}
Our Nasdaq Data Link integration supports any dataset from Nasdaq that has a URL starting with data.nasdaq.com/data/. To import a dataset from Data Link, you need to get the dataset code. Follow these steps to get the dataset code:
- Open the Data Link catalog.
- (Optional) Use the filters on the left side bar to narrow down the catalog results.
- Click one of the data products.
- On the data product page, click one of the table names.
- In the top-right corner of the dataset page, copy the Nasdaq Data Link Code.
Data Summary
The data summary depends on the specific Data Link dataset you use. Follow these steps to view the data summary of a dataset:
- Open the Data Link catalog.
- Click one of the data products.
- On the data product page, click one of the table names.
- View the data frequency and description in the left sidebar.
- Above the chart, click
Tableto view the start date, end date, and data point attributes.
Backward Compatibility
QuantConnect/LEAN has supported Quandl since 2015. On January, 12, 2022, we moved the implementation from the LEAN GitHub repository to the Lean.DataSource.NasdaqDataLink GitHub repository. Through this transition, we maintained backward compatibility. All of the preceding code snippets work if you replace NasdaqDataLink with Quandl.
Requesting Data
To add Data Link data to your algorithm, call the AddDataadd_data method. Save a reference to the dataset Symbol so you can access the data later in your algorithm. If there is more than one value column in the Data Link dataset, to set the Value property of the data objects, create a sublcass of the NasdaqDataLink class and set its ValueColumnNamevalue_column_name property to the column name.
class NasdaqDataLinkDataAlgorithm(QCAlgorithm):
def initialize(self) -> None:
self.set_start_date(2022, 1, 1)
self.set_end_date(2022, 7, 1)
self.set_cash(100000)
# For premium datasets, provide your API Key
# NasdaqDataLink.set_auth_code(self.get_parameter("nasdaq-data-link-api-key"))
self.bitcoin_chain_symbol = self.add_data(NasdaqDataLink, "QDL/BCHAIN", Resolution.DAILY).symbol
# This dataset has one data column ("Value")
# Source : https://data.nasdaq.com/databases/BCHAIN
self.bitfinex_exchange_rate_symbol = self.add_data(NasdaqCustomColumns, "QDL/BITFINEX", Resolution.DAILY).symbol
# This dataset has multiple data columns
# Source: https://data.nasdaq.com/databases/BITFINEX
class NasdaqCustomColumns(NasdaqDataLink):
def __init__(self) -> None:
# Select the column "mid".
self.value_column_name = "mid" public class NasdaqDataLinkDataAlgorithm : QCAlgorithm
{
private Symbol _bitcoinChainSymbol , _bitfinexExchangeRateSymbol ;
public override void Initialize()
{
SetStartDate(2022, 1, 1);
SetEndDate(2022, 7, 1);
SetCash(100000);
// For premium datasets, provide your API Key
// NasdaqDataLink.SetAuthCode(GetParameter("nasdaq-data-link-api-key"));
_bitcoinChainSymbol = AddData<NasdaqDataLink>("QDL/BCHAIN", Resolution.Daily).Symbol;
// This dataset has one data column ("Value")
// Source : https://data.nasdaq.com/databases/BCHAIN
_bitfinexExchangeRateSymbol = AddData<NasdaqCustomColumns>("QDL/BITFINEX", Resolution.Daily).Symbol;
// This dataset has multiple data columns
// Source: https://data.nasdaq.com/databases/BITFINEX
}
}
public class NasdaqCustomColumns : NasdaqDataLink
{
// Select the column "mid"
public NasdaqCustomColumns() : base(valueColumnName: "mid") { }
}
Accessing Data
To get the current Data Link data, index the current Slice with the dataset Symbol. Slice objects deliver unique events to your algorithm as they happen, but the Slice may not contain data for your dataset at every time step. To avoid issues, check if the Slice contains the data you want before you index it.
To get the default value of the dataset, use the Valuevalue property. To get the value of a specific column in the dataset, call the GetStorageDictionaryget_storage_dictionary method and index the result with the column name.
def on_data(self, slice: Slice) -> None:
point = slice.get(self.bitcoin_chain_symbol)
if point:
value = point.value
self.log(f"{self.bitcoin_chain_symbol} at {slice.time}: {value}")
point = slice.get(self.bitfinex_exchange_rate_symbol)
if point:
value = point.value
storage_dictionary = point.get_storage_dictionary()
volume = storage_dictionary["volume"]
self.log(f"{self.bitfinex_exchange_rate_symbol} Bitfinex exchange rate at {slice.time}: {value}") public override void OnData(Slice slice)
{
if (slice.TryGetValue(_bitcoinChainSymbol, out var point))
{
var value = point.Value;
Log($"{_bitcoinChainSymbol} value at {slice.Time}: {value}");
}
if (slice.TryGetValue(_bitfinexExchangeRateSymbol, out var point))
{
var value = point.Value;
var storageDictionary = point.GetStorageDictionary();
var volume = storageDictionary["volume"];
Log($"{_bitfinexExchangeRateSymbol} Bitfinex exchange rate at {slice.Time}: {value}");
}
}
To iterate through all of the dataset objects in the current Slice, call the Getget method.
def on_data(self, slice: Slice) -> None:
for dataset_symbol, data_point in slice.get(NasdaqDataLink).items():
self.log(f"{dataset_symbol} value at {slice.time}: {data_point.value}")
for dataset_symbol, data_point in slice.get(NasdaqCustomColumns).items():
self.log(f"{dataset_symbol} value at {slice.time}: {data_point.value}") public override void OnData(Slice slice)
{
foreach (var kvp in slice.Get<NasdaqDataLink>())
{
var datasetSymbol = kvp.Key;
var dataPoint = kvp.Value;
Log($"{datasetSymbol} value at {slice.Time}: {dataPoint.Value}");
}
foreach (var kvp in slice.Get<NasdaqCustomColumns>())
{
var datasetSymbol = kvp.Key;
var dataPoint = kvp.Value;
Log($"{datasetSymbol} value at {slice.Time}: {dataPoint.Value}");
}
}
Historical Data
The process to get historical Data Link data depends on your environment.
In Algorithms
To get historical Data Link data in an algorithm, call the Historyhistory method with the dataset Symbol. If there is no data in the period you request, the history result is empty.
# DataFrames bitcoin_chain_df = self.history(self.bitcoin_chain_symbol, 100, Resolution.DAILY) bitfinex_exchange_rate_df = self.history(self.bitfinex_exchange_rate_symbol, 100, Resolution.DAILY) # Dataset objects bitcoin_chain_history = self.history[NasdaqDataLink](self.bitcoin_chain_symbol, 100, Resolution.DAILY) bitfinex_exchange_rate_history = self.history[NasdaqCustomColumns](self.bitfinex_exchange_rate_symbol, 100, Resolution.DAILY)
var bitcoinChainHistory = History<NasdaqDataLink>(_bitcoinChainSymbol, 100, Resolution.Daily); var bitfinexExchangeRateHistory = History<NasdaqDataLink>(_bitfinexExchangeRateSymbol, 100, Resolution.Daily);
For more information about historical data, see History Requests.
In Research Notebooks
To get historical Data Link data in the Research Environment, call the Downloaddownload method with the data URL.
from io import StringIO
data = qb.download("https://data.nasdaq.com/api/v3/datatables/QDL/BCHAIN.csv?")
df = pd.read_csv(StringIO(data), index_col=0) var data = qb.Download("https://data.nasdaq.com/api/v3/datatables/QDL/BCHAIN.csv?");
To receive a notification when you can use the Historyhistory method in the Research Environment to get data from Data Link, subscribe to Lean.DataSource.NasdaqDataLink GitHub Issue #10.
Example Applications
The Nasdaq Data Link sources allow you to explore different kinds of data in their database to develop trading strategies. Examples include the following strategies:
- Using alternative data to regress market regime/asset price.
- Backtesting exotic derivatives or private Equity investments.
Classic Algorithm Example
The following example algorithm subscribes to the Bitfinex Crypto Exchange Rate datasets. The algorithm takes a long position in Bitfinex BTCUSD.
from AlgorithmImports import *
class NasdaqImporterAlgorithm(QCAlgorithm):
def initialize(self):
self.nasdaq_code = "QDL/BITFINEX"
## Optional argument - personal token necessary for restricted dataset
# NasdaqDataLink.set_auth_code(self.get_parameter("nasdaq-data-link-api-key"))
self.set_start_date(2024, 9, 1)
self.set_end_date(2024, 12, 31)
self.set_cash(25000)
# Request nasdaq data link data to trade
self.add_data(NasdaqCustomColumns, self.nasdaq_code, Resolution.DAILY, TimeZones.NEW_YORK)
# Create SMA indicator that feed with nasdaq data link data, which can be used to determine trend for trend following trade
self.sma = self.SMA(self.nasdaq_code, 14)
def on_data(self, data):
if not self.portfolio.hold_stock:
self.set_holdings(self.nasdaq_code, 1)
self.debug("Purchased {0} >> {1}".format(self.nasdaq_code, self.time))
self.plot(self.nasdaq_code, "PriceSMA", self.sma.current.value)
# NasdaqDataLink often doesn't use close columns so need to tell LEAN which is the "value" column.
class NasdaqCustomColumns(NasdaqDataLink):
def __init__(self):
# Define ValueColumnName: cannot be None, Empty or non-existant column name
super().__init__("mid")
self.value_column_name = "mid" public class NasdaqImporterAlgorithm : QCAlgorithm
{
private string nasdaqCode = "QDL/BITFINEX";
private SimpleMovingAverage sma;
public override void Initialize()
{
// Optional argument - personal token necessary for restricted dataset
// NasdaqDataLink.SetAuthCode(this.GetParameter("nasdaq-data-link-api-key"));
SetStartDate(2024, 9, 1);
SetEndDate(2024, 12, 31);
SetCash(25000);
// Request nasdaq data link data to trade
AddData<NasdaqCustomColumns>(nasdaqCode, Resolution.Daily, TimeZones.NewYork);
// Create SMA indicator that feed with nasdaq data link data, which can be used to determine trend for trend following trade
sma = SMA(nasdaqCode, 14);
}
public override void OnData(Slice data)
{
// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
if (!Portfolio.HoldStock)
{
SetHoldings(nasdaqCode, 1);
Debug("Purchased " + nasdaqCode + " >> " + Time);
}
Plot(nasdaqCode, "PriceSMA", sma.Current.Value);
}
}
// NasdaqDataLink often doesn't use close columns so need to tell LEAN which is the "value" column.
public class NasdaqCustomColumns : NasdaqDataLink
{
// Custom nasdaq data type for setting customized value column name.
// Value column is used for the primary trading calculations and charting.
public NasdaqCustomColumns() : base("mid")
{
ValueColumnName = "mid";
}
}
Framework Algorithm Example
The following example algorithm subscribes to the Bitcoin Chain dataset and Bitfinex Crypto Exchange Rate dataset. The algorithm takes a long position in Bitfinex BTCUSD.
from AlgorithmImports import *
class NasdaqDataLinkFrameworkAlgorithm(QCAlgorithm):
def initialize(self) -> None:
self.set_start_date(2024, 9, 1)
self.set_end_date(2024, 12, 31)
self.set_cash(1000000)
### Requesting Data
# For premium datasets, provide your API Key
# NasdaqDataLink.set_auth_code(self.get_parameter("nasdaq-data-link-api-key"))
# Add nasdaq data link data as trading vehicle
bitfinex_exchange_rate_symbol = self.add_data(NasdaqDataLink, 'QDL/BCHAIN', Resolution.DAILY).symbol
### Historical Data
history = self.history(bitfinex_exchange_rate_symbol, 1000, Resolution.DAILY)
self.debug(f"We got {len(history)} items from our history request for {bitfinex_exchange_rate_symbol} Nasdaq Data Link")
# Custom alpha model that emit insights based on updated nasdaq data link data
self.add_alpha(NasdaqDataLinkAlphaModel(self))
# Invest equally to evenly dissipate the capital concentration risk
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel(lambda time: None))
class NasdaqDataLinkAlphaModel(AlphaModel):
def __init__(self, algorithm: QCAlgorithm) -> None:
### Requesting NASDAQ data link custom data
self.bitfinex_exchange_rate_symbol = algorithm.add_data(NasdaqCustomColumns, "QDL/BITFINEX", Resolution.DAILY).symbol
# This dataset has multiple data columns, so create a custom class to set the value column name
### Historical Data
history = algorithm.history(self.bitfinex_exchange_rate_symbol, 100, Resolution.DAILY)
algorithm.debug(f"We got {len(history)} items from our history request for {self.bitfinex_exchange_rate_symbol} Nasdaq Data Link")
def update(self, algorithm: QCAlgorithm, slice: Slice) -> List[Insight]:
insights = []
# Trade only based on updated NASDAQ data link data
for dataset_symbol, data_point in slice.Get(NasdaqCustomColumns).items():
if dataset_symbol != self.bitfinex_exchange_rate_symbol or algorithm.portfolio[dataset_symbol].invested:
continue
insights.append(Insight.price(dataset_symbol, timedelta(days=6*22), InsightDirection.UP))
return insights
# NasdaqDataLink often doesn't use close columns so need to tell LEAN which is the "value" column.
class NasdaqCustomColumns(NasdaqDataLink):
def __init__(self) -> None:
# Select the column "mid".
self.value_column_name = "mid" public class NasdaqDataLinkFrameworkAlgorithm : QCAlgorithm
{
public override void Initialize()
{
SetStartDate(2024, 9, 1);
SetEndDate(2024, 12, 31);
SetCash(100000);
/// Requesting Data
// For premium datasets, provide your API Key
// NasdaqDataLink.SetAuthCode(GetParameter("nasdaq-data-link-api-key"));
// Add nasdaq data link data as trading vehicle
var bitcoinChainSymbol = AddData<NasdaqDataLink>("QDL/BCHAIN", Resolution.Daily).Symbol;
/// Historical Data
var history = History<NasdaqDataLink>(bitcoinChainSymbol, 1000, Resolution.Daily);
Debug($"We got {history.Count()} items from our history request for {bitcoinChainSymbol} Nasdaq Data Link");
// Custom alpha model that emit insights based on updated nasdaq data link data
AddAlpha(new NasdaqDataLinkAlphaModel(this));
// Invest equally to evenly dissipate the capital concentration risk
SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel(time => null));
}
}
public class NasdaqDataLinkAlphaModel : AlphaModel
{
private Symbol _bitfinexExchangeRateSymbol;
public NasdaqDataLinkAlphaModel(QCAlgorithm algorithm)
{
/// Requesting NASDAQ data link custom data
_bitfinexExchangeRateSymbol = algorithm.AddData<NasdaqCustomColumns>("QDL/BITFINEX", Resolution.Daily).Symbol;
// This dataset has multiple data columns, so create a custom class to set the value column name
/// Historical Data
var history = algorithm.History<NasdaqCustomColumns>(_bitfinexExchangeRateSymbol, 100, Resolution.Daily);
algorithm.Debug($"We got {history.Count()} items from our history request for {_bitfinexExchangeRateSymbol} Nasdaq Data Link");
}
public override List<Insight> Update(QCAlgorithm algorithm, Slice slice)
{
List<Insight> insights = new List<Insight>();
/// Trade only based on updated NASDAQ data link data
foreach (var kvp in slice.Get<NasdaqCustomColumns>())
{
var datasetSymbol = kvp.Key;
if (datasetSymbol != _bitfinexExchangeRateSymbol || algorithm.Portfolio[datasetSymbol].Invested)
{
continue;
}
insights.Add(Insight.Price(datasetSymbol, TimeSpan.FromDays(6*22), InsightDirection.Up));
}
return insights;
}
}
public class NasdaqCustomColumns : NasdaqDataLink
{
// Select the column "mid".
public NasdaqCustomColumns() : base(valueColumnName: "mid") { }
}