book
Checkout our new book! Hands on AI Trading with Python, QuantConnect, and AWS Learn More arrow

Custom Universes

JSON Format Example

Introduction

This page explains how to import custom data for universe selection sourced in JSON format.

Data Format

You must create a file with data in JSON format. Ensure the data in the file is in chronological order.

[
    {
      "Date": "20170704",
      "Symbols": ["SPY", "QQQ", "FB", "AAPL", "IWM"]
    },
    {
      "Date": "20170706",
      "Symbols": ["QQQ", "AAPL", "IWM", "FB", "GOOGL"]
    },
    ...
    {
      "Date": "20170801",
      "Symbols": ["QQQ", "FB", "AAPL", "IWM", "GOOGL"]
    },
    {
      "Date": "20170802",
      "Symbols": ["QQQ", "IWM", "FB", "BAC", "GOOGL"]
    }
]

Define Custom Types

To define a custom data type, inherit the PythonData class and override the GetSource and Reader methods.

If you need to create multiple objects in your reader method from a single line, follow these steps:

  1. In the get_source method, pass FileFormat.UNFOLDING_COLLECTION as the third argument to the SubscriptionDataSource constructor.
  2. In the reader method, order the objects by their timestamp and then return a BaseDataCollection(end_time, config.symbol, objects) where objects is a list of your custom data objects.
Select Language:
class StockDataSource(PythonData):

    def get_source(self,
         config: SubscriptionDataConfig,
         date: datetime,
         is_live: bool) -> SubscriptionDataSource:
        if not is_live:
            return SubscriptionDataSource("<json_universe_key>", SubscriptionTransportMedium.OBJECT_STORE, FileFormat.UNFOLDING_COLLECTION)
        return SubscriptionDataSource("https://raw.githubusercontent.com/QuantConnect/Documentation/master/Resources/datasets/custom-data/universe-example.json", 
                                      SubscriptionTransportMedium.REMOTE_FILE,
                                      FileFormat.UNFOLDING_COLLECTION)

    def reader(self,
         config: SubscriptionDataConfig,
         line: str,
         date: datetime,
         is_live: bool) -> BaseData:
        
        objects = []
        data = json.loads(line)

        for datum in data:
            stocks = StockDataSource()
            stocks.symbol = config.symbol

            stocks.time = datetime.strptime(datum["Date"], "%Y%m%d")
            stocks.end_time = stocks.time + timedelta(1)
            stocks["Symbols"] = datum["Symbols"]
            objects.append(stocks)

        return BaseDataCollection(objects[-1].end_time, config.symbol, objects)

Initialize Universe

To perform a universe selection with custom data, in the initialize method, call the add_universe method.

Select Language:
class MyAlgorithm(QCAlgorithm): 
    def initialize(self) -> None:
        self.add_universe(StockDataSource, "my-stock-data-source", Resolution.DAILY, self._filter_function)
    

Receive Custom Data

As your data reader reads your custom data file, LEAN adds the data points into a List[StockDataSource]) object it passes to your algorithm's filter function. Your filter function needs to return a list of Symbol or str object. LEAN automatically subscribes to these new assets and adds them to your algorithm.

Select Language:
class MyAlgorithm(QCAlgorithm):
    def _filter_function(self, data: List[StockDataSource]) -> List[str]:
        symbols = []
        for item in data:
            for symbol in item["Symbols"]:
                symbols.append(symbol)
        return symbols
    

If you add custom properties to your data object in the reader method, LEAN adds them as members to the data object in your filter method. To ensure the property names you add in the reader method follow the convention of member names, LEAN applies the following changes to the property names you provide in the reader method:

  1. - and . characters are replaced with whitespace.
  2. The first letter is capitalized.
  3. Whitespace characters are removed.

For example, if you set a property name in the reader method to ['some-property.name'], you can access it in your filter method through the Somepropertyname member of your data object.

You can also see our Videos. You can also get in touch with us via Discord.

Did you find this page helpful?

Contribute to the documentation: