Custom Securities
CSV Format Example
Data Format
The following snippet shows an example CSV file:
Date,Open,High,Low,Close,SharesTraded,Tur11er(Rs.Cr) 1997-01-01,905.2,941.4,905.2,939.55,38948210,978.21 1997-01-02,941.95,944,925.05,927.05,49118380,1150.42 1997-01-03,924.3,932.6,919.55,931.65,35263845,866.74 ... 2014-07-24,7796.25,7835.65,7771.65,7830.6,117608370,6271.45 2014-07-25,7828.2,7840.95,7748.6,7790.45,153936037,7827.61 2014-07-28,7792.9,7799.9,7722.65,7748.7,116534670,6107.78
The data in the file must be in chronological order.
Define Custom Types
To define a custom data type, inherit the BaseData
PythonData
class and override the GetSource and Reader methods.
public class MyCustomDataType : BaseData { public decimal Open; public decimal High; public decimal Low; public decimal Close; public override SubscriptionDataSource GetSource( SubscriptionDataConfig config, DateTime date, bool isLiveMode) { if (!isLiveMode) { return new SubscriptionDataSource("<CustomDataKey>", SubscriptionTransportMedium.ObjectStore, FileFormat.Csv); } return new SubscriptionDataSource("https://raw.githubusercontent.com/QuantConnect/Documentation/master/Resources/datasets/custom-data/csv-data-example.csv", SubscriptionTransportMedium.RemoteFile); } public override BaseData Reader( SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode) { if (string.IsNullOrWhiteSpace(line.Trim())) { return null; } var index = new MyCustomDataType(); try { var data = line.Split(','); index.Symbol = config.Symbol; index.Time = DateTime.Parse(data[0], CultureInfo.InvariantCulture); index.EndTime = index.Time.AddDays(1); index.Open = Convert.ToDecimal(data[1], CultureInfo.InvariantCulture); index.High = Convert.ToDecimal(data[2], CultureInfo.InvariantCulture); index.Low = Convert.ToDecimal(data[3], CultureInfo.InvariantCulture); index.Close = Convert.ToDecimal(data[4], CultureInfo.InvariantCulture); index.Value = index.Close; } catch { } return index; } }
class MyCustomDataType(PythonData): def get_source(self, config: SubscriptionDataConfig, date: datetime, is_live: bool) -> SubscriptionDataSource: if not is_live: return SubscriptionDataSource("<custom_data_key>", SubscriptionTransportMedium.OBJECT_STORE, FileFormat.CSV) return SubscriptionDataSource("https://raw.githubusercontent.com/QuantConnect/Documentation/master/Resources/datasets/custom-data/csv-data-example.csv", SubscriptionTransportMedium.REMOTE_FILE) def reader(self, config: SubscriptionDataConfig, line: str, date: datetime, is_live: bool) -> BaseData: if not (line.strip()): return None index = MyCustomDataType() index.symbol = config.symbol try: data = line.split(',') index.time = datetime.strptime(data[0], "%Y-%m-%d") index.end_time = index.time + timedelta(days=1) index.value = data[4] index["open"] = float(data[1]) index["high"] = float(data[2]) index["low"] = float(data[3]) index["close"] = float(data[4]) except ValueError: # Do nothing return None return index
Create Subscriptions
To create a subscription for the custom type, in the Initialize
initialize
method, call the AddData
add_data
method. The AddData
add_data
method returns a Security
object, which contains a Symbol
. Save a reference to the Symbol
symbol
so you can use it in OnData
on_data
to access the security data in the Slice
.
public class MyAlgorithm : QCAlgorithm { private Symbol _symbol; public override void Initialize() { _symbol = AddData<MyCustomDataType>("MyCustomDataType", Resolution.Daily).Symbol; } }
class MyAlgorithm(QCAlgorithm): def initialize(self) -> None: self._symbol = self.add_data(MyCustomDataType, "MyCustomDataType", Resolution.DAILY).symbol
The AddData
add_data
method creates a subscription for a single custom data asset and adds it to your user-defined universe.
Receive Custom Data
As your data reader reads your custom data file, LEAN adds the data points in the Slice
it passes to your algorithm's OnData
on_data
method. To collect the custom data, use the Symbol
symbol
or name of your custom data subscription. You can access the Value
value
and custom properties of your custom data class from the Slice
. To access the custom properties, use the custom attributepass the property name to the GetProperty
get_property
method.
public class MyAlgorithm : QCAlgorithm { public override void OnData(Slice slice) { if (slice.ContainsKey(_symbol)) { var customData = slice[_symbol]; var close = customData.Close; } } // You can also get the data directly with OnData(<dataClass>) method public void OnData(MyCustomDataType customData) { var close = customData.Close; } }
class MyAlgorithm(QCAlgorithm): def on_data(self, slice: Slice) -> None: if slice.contains_key(self._symbol): custom_data = slice[self._symbol] close = custom_data.close
If you add custom properties to your data object in the Reader
reader
method, LEAN adds them as members to the data object in the Slice
. 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 the OnData
on_data
method through the Somepropertyname
member of your data object.