Custom Universes
JSON Format Example
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 BaseData
PythonData
class and override the GetSource and Reader methods.
If you need to create multiple objects in your Reader
reader
method from a single line
, follow these steps:
- In the
GetSource
get_source
method, passFileFormat.UnfoldingCollection
FileFormat.UNFOLDING_COLLECTION
as the third argument to theSubscriptionDataSource
constructor. - In the
Reader
reader
method, order the objects by their timestamp and then return aBaseDataCollection(endTime, config.Symbol, objects)
BaseDataCollection(end_time, config.symbol, objects)
whereobjects
is a list of your custom data objects.
using Newtonsoft.Json; public class StockDataSource : BaseData { [JsonProperty("Symbols")] public List<string> Symbols { get; set; } [JsonProperty("Date")] public string Date; public override DateTime EndTime => Time.AddDays(1); public override SubscriptionDataSource GetSource( SubscriptionDataConfig config, DateTime date, bool isLiveMode) { if (!isLiveMode) { return new SubscriptionDataSource("<jsonUniverseKey", SubscriptionTransportMedium.ObjectStore, FileFormat.UnfoldingCollection); } return new SubscriptionDataSource("https://raw.githubusercontent.com/QuantConnect/Documentation/master/Resources/datasets/custom-data/universe-example.json", SubscriptionTransportMedium.RemoteFile, FileFormat.UnfoldingCollection); } public override BaseData Reader( SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode) { var objects = JsonConvert.DeserializeObject<List<StockDataSource>>(line).Select(stocks => { stocks.Time = DateTime.ParseExact(stocks.Date, "yyyyMMdd", null); return stocks; }).ToList(); return new BaseDataCollection(objects.Last().EndTime, config.Symbol, objects); } }
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
initialize
method, call the AddUniverse
add_universe
method.
public class MyAlgorithm : QCAlgorithm { public override void Initialize() { AddUniverse<StockDataSource>("myStockDataSource", Resolution.Daily, FilterFunction); } }
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])
IEnumerable<StockDataSource>
object it passes to your algorithm's filter function. Your filter function needs to return a list of Symbol
or str
string
object. LEAN automatically subscribes to these new assets and adds them to your algorithm.
public class MyAlgorithm : QCAlgorithm { private IEnumerable<string> FilterFunction(IEnumerable<StockDataSource> stockDataSource) { return stockDataSource.SelectMany(x => x.Symbols); } }
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
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
reader
method follow the convention of member names, LEAN applies the following changes to the property names you provide in the Reader
reader
method:
-
and.
characters are replaced with whitespace.- The first letter is capitalized.
- Whitespace characters are removed.
For example, if you set a property name in the Reader
reader
method to ['some-property.name']
, you can access it in your filter method through the Somepropertyname
member of your data object.