Writing Algorithms
Initialization
Set Dates
To set the date range of backtests, call the SetStartDate
set_start_date
and SetEndDate
set_end_date
methods. The dates you provide are based in the algorithm time zone. By default, the end date is yesterday, one millisecond before midnight. In live trading, LEAN ignores the start and end dates.
// Set the start and end dates for the algorithm, defining the backtesting period. SetStartDate(2013, 1, 5); // Set the start date to January 5, 2013. SetEndDate(2015, 1, 5); // Set the end date to January 5, 2015. SetEndDate(DateTime.Now.Date.AddDays(-7)); // Set the end date to last week.
# Set the start and end dates for the algorithm, defining the backtesting period. self.set_start_date(2013, 1, 5) # Set the start date to January 5, 2013. self.set_end_date(2015, 1, 5) # Set the end date to January 5, 2015. self.set_end_date(datetime.now() - timedelta(7)) # Set the end date to last week.
Set Account Currency
The algorithm equity curve, benchmark, and performance statistics are denominated in the account currency. To set the account currency and your starting cash, call the SetAccountCurrency
set_account_currency
method. By default, the account currency is USD and your starting cash is $100,000. If you call the SetAccountCurrency
set_account_currency
method, you must call it before you call the SetCash
set_cash
method or add data. If you call the SetAccountCurrency
set_account_currency
method more than once, only the first call takes effect.
// Set the account currency and your starting cash. SetAccountCurrency("BTC"); // Set the account currency to Bitcoin and its quantity to 100,000 BTC. SetAccountCurrency("INR"); // Set the account currency to Indian Rupees and its quantity to 100,000 INR. SetAccountCurrency("BTC", 10); // Set the account currency to Bitcoin and its quantity to 10 BTC.
# Set the account currency and your starting cash. self.set_account_currency("BTC") # Set the account currency to Bitcoin and its quantity to 100,000 BTC. self.set_account_currency("INR") # Set the account currency to Indian Rupees and its quantity to 100,000 INR. self.set_account_currency("BTC", 10) # Set the account currency to Bitcoin and its quantity to 10 BTC.
Set Cash
To set your starting cash in backtests, call the SetCash
set_cash
method. By default, your starting cash is $100,000 USD. In live trading, LEAN ignores the SetCash
set_cash
method and uses the cash balances in your brokerage account instead.
// Set the initial cash balance for the account for supported currencies. SetCash(100000); // Set the quantity of the account currency to 100,000. SetCash("BTC", 10); // Set the Bitcoin quantity to 10. SetCash("EUR", 10000); // Set the EUR quantity to 10,000.
# Set the initial cash balance for the account for supported currencies. self.set_cash(100000) # Set the quantity of the account currency to 100,000. self.set_cash("BTC", 10) # Set the Bitcoin quantity to 10. self.set_cash("EUR", 10000) # Set the EUR quantity to 10,000.
Set Brokerage and Cash Model
We model your algorithm with margin modeling by default, but you can select a cash account type. Cash accounts don't allow leveraged trading, whereas Margin accounts can support leverage on your account value. To set your brokerage and account type, call the SetBrokerageModel
set_brokerage_model
method. For more information about each brokerage and the account types they support, see the brokerage integration documentation. For more information about the reality models that the brokerage models set, see Supported Models.
// Set the brokerage model and account type to model fees, supported order types, and margin available. SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin);
# Set the brokerage model and account type to model fees, supported order types, and margin available. self.set_brokerage_model(BrokerageName.INTERACTIVE_BROKERS_BROKERAGE, AccountType.CASH)
The AccountType
enumeration has the following members:
Set Universe Settings
The universe settings of your algorithm configure some properties of the universe constituents. The following table describes the properties of the UniverseSettings
object:
Property: |
Property: |
Property: |
Property: |
Property: |
Method: |
Property: |
Property: |
Property: |
Property: |
To set the UniverseSettings
universe_settings
, update the preceding properties in the Initialize
initialize
method before you add the universe. These settings are globals, so they apply to all universes you create.
// Request second resolution data. This will be slow! UniverseSettings.Resolution = Resolution.Second; AddUniverse(Universe.DollarVolume.Top(50));
# Request second resolution data. This will be slow! self.universe_settings.resolution = Resolution.SECOND self.add_universe(self.universe.dollar_volume.top(50))
For more information about universe settings, see the related documentation for classic and framework algorithms.
Set Security Initializer
Instead of configuring global universe settings, you can individually configure the settings of each security in the universe with a security initializer. Security initializers let you apply any security-level reality model or special data requests on a per-security basis. To set the security initializer, in the Initialize
initialize
method, call the SetSecurityInitializer
set_security_initializer
method and then define the security initializer.
// A custom security initializer can override default models such as // setting new fee and fill models for the security. SetSecurityInitializer(CustomSecurityInitializer); private void CustomSecurityInitializer(Security security) { security.SetFeeModel(new ConstantFeeModel(0, "USD")); }
# A custom security initializer can override default models such as # setting new fee and fill models for the security. self.set_security_initializer(self._custom_security_initializer) def _custom_security_initializer(self, security: Security) -> None: security.set_fee_model(ConstantFeeModel(0, "USD"))
For simple requests, you can use the functional implementation of the security initializer. This style lets you configure the security object with one line of code.
// Disable the trading fees for each security by passing a functional // implementation for the SetSecurityInitializer argument. SetSecurityInitializer(security => security.SetFeeModel(new ConstantFeeModel(0, "USD")));
# Disable the trading fees for each security by using lambda function # for the set_security_initializer argument. self.set_security_initializer(lambda security: security.set_fee_model(ConstantFeeModel(0, "USD")))
In some cases, you may want to trade a security in the same time loop that you create the security subscription. To avoid errors, use a security initializer to set the market price of each security to the last known price. The GetLastKnownPrices
get_last_known_prices
method seeds the security price by gathering the security data over the last 3 days. If there is no data during this period, the security price remains at 0.
// Gather the last 3 days of security prices by using GetLastKnowPrice as the seed in Initialize. var seeder = new FuncSecuritySeeder(GetLastKnownPrices); SetSecurityInitializer(security => seeder.SeedSecurity(security));
# Gather the last 3 days of security prices by using get_last_known_prices as the seed in initialize. seeder = FuncSecuritySeeder(self.get_last_known_prices) self.set_security_initializer(lambda security: seeder.seed_security(security))
If you call the SetSecurityInitializer
set_security_initializer
method, it overwrites the default security initializer. The default security initializer uses the security-level reality models of the brokerage model to set the following reality models of each security:
The default security initializer also sets the leverage of each security and intializes each security with a seeder function. To extend upon the default security initializer instead of overwriting it, create a custom BrokerageModelSecurityInitializer
.
public class BrokerageModelExampleAlgorithm : QCAlgorithm { public override void Initialize() { // In the Initialize method, set the security initializer to seed initial the prices and models of assets. SetSecurityInitializer(new MySecurityInitializer(BrokerageModel, new FuncSecuritySeeder(GetLastKnownPrices))); } } public class MySecurityInitializer : BrokerageModelSecurityInitializer { public MySecurityInitializer(IBrokerageModel brokerageModel, ISecuritySeeder securitySeeder) : base(brokerageModel, securitySeeder) {} public override void Initialize(Security security) { // First, call the superclass definition. // This method sets the reality models of each security using the default reality models of the brokerage model. base.Initialize(security); // Next, overwrite some of the reality models security.SetFeeModel(new ConstantFeeModel(0, "USD")); } }
class BrokerageModelExampleAlgorithm(QCAlgorithm): def initialize(self) -> None: # In the Initialize method, set the security initializer to seed initial the prices and models of assets. self.set_security_initializer(MySecurityInitializer(self.brokerage_model, FuncSecuritySeeder(self.get_last_known_prices))) # Outside of the algorithm class class MySecurityInitializer(BrokerageModelSecurityInitializer): def __init__(self, brokerage_model: IBrokerageModel, security_seeder: ISecuritySeeder) -> None: super().__init__(brokerage_model, security_seeder) def initialize(self, security: Security) -> None: # First, call the superclass definition. # This method sets the reality models of each security using the default reality models of the brokerage model. super().initialize(security) # Next, overwrite some of the reality models security.set_fee_model(ConstantFeeModel(0, "USD"))
To set a seeder function without overwriting the reality models of the brokerage, use the standard BrokerageModelSecurityInitializer
.
var seeder = new FuncSecuritySeeder(GetLastKnownPrices); SetSecurityInitializer(new BrokerageModelSecurityInitializer(BrokerageModel, seeder));
seeder = FuncSecuritySeeder(self.get_last_known_prices) self.set_security_initializer(BrokerageModelSecurityInitializer(self.brokerage_model, seeder))
Add Data
You can subscribe to asset, fundamental, alternative, and custom data. The Dataset Market provides 400TB of data that you can easily import into your algorithms.
Asset Data
To subscribe to asset data, call one of the asset subscription methods like AddEquity
add_equity
or AddForex
add_forex
. Each asset class has its own method to create subscriptions. For more information about how to create subscriptions for each asset class, see Asset Classes.
AddEquity("AAPL"); // Add Apple 1 minute bars (minute by default) AddForex("EURUSD", Resolution.Second); // Add EURUSD 1 second bars
self.add_equity("SPY") # Add Apple 1 minute bars (minute by default) self.add_forex("EURUSD", Resolution.SECOND) # Add EURUSD 1 second bars
In live trading, you define the securities you want, but LEAN also gets the securities in your live portfolio and sets their resolution to the lowest resolution of the subscriptions you made. For example, if you create subscriptions in your algorithm for securities with Second, Minute, and Hour resolutions, the assets in your live portfolio are given a resolution of Second.
Alternative Data
To add alternative datasets to your algorithms, call the AddData
add_data
method. For full examples, in the Datasets chapter, select a dataset and see the Requesting Data section.
Custom Data
To add custom data to your algorithms, call the AddData
add_data
method. For more information about custom data, see Importing Data.
Limitations
There is no official limit to how much data you can add to your algorithms, but there are practical resource limitations. Each security subscription requires about 5MB of RAM, so larger machines let you run algorithms with bigger universes. For more information about our cloud nodes, see Resources.
Set Indicators and Consolidators
You can create and warm-up indicators in the Initialize
initialize
method.
private Symbol _symbol; private SimpleMovingAverage _sma; _symbol = AddEquity("SPY").Symbol; _sma = SMA(_symbol, 20); WarmUpIndicator(_symbol, _sma);
self._symbol = self.add_equity("SPY").symbol self._sma = self.sma(self._symbol, 20) self.warm_up_indicator(self._symbol, self._sma)
Set Algorithm Settings
The following table describes the AlgorithmSettings
properties:
Property: |
Property: |
Property: |
Property: |
Property: |
Property: |
Property: |
Property: |
Property: |
Property: |
Property: |
Property: |
Property: |
To change the Settings
settings
, update some of the preceding properties.
// Set the algorithm behavior using the settings property on your algorithm. Settings.RebalancePortfolioOnSecurityChanges = false; Settings.TradingDaysPerYear = 365;
# Set the algorithm behavior using the settings property on your algorithm. self.settings.rebalance_portfolio_on_security_changes = False self.settings.trading_days_per_year = 365
To successfully update the FreePortfolioValue
free_portfolio_value
, you must update it after the Initialize
initialize
method.
Set Benchmark
The benchmark performance is input to calculate several statistics on your algorithm, including alpha and beta. To set a benchmark for your algorithm, call the SetBenchmark
set_benchmark
method. You can set the benchmark to a security, a constant value, or a value from a custom data source. If you don't set a brokerage model, the default benchmark is SPY. If you set a brokerage model, the model defines the default benchmark.
// Set the benchmark to IBM SetBenchmark("IBM"); // Set the benchmark to a constant value of 0 SetBenchmark(x => 0); // Set the benchmark to a value from a custom data source var symbol = AddData<CustomData>("CustomData", Resolution.Hour).Symbol; SetBenchmark(symbol);
# Set the benchmark to IBM self.set_benchmark("IBM") # Set the benchmark to a constant value of 0 self.set_benchmark(lambda x: 0) # Set the benchmark to a value from a custom data source symbol = self.add_data(CustomData, "CustomData", Resolution.HOUR).symbol self.set_benchmark(symbol)
If you pass a ticker to the SetBenchmark
set_benchmark
method, LEAN checks if you have a subscription for it. If you have a subscription for it, LEAN uses the security subscription. If you don't have a subscription for it, LEAN creates a US Equity subscription with the ticker. Since the ticker you pass may not reference a US Equity, we recommend you subscribe to the benchmark security before you call the SetBenchmark
set_benchmark
method.
Set Time Zone
LEAN supports international trading across multiple time zones and markets, so it needs a reference time zone for the algorithm to set the Time
time
. The default time zone is Eastern Time (ET), which is UTC-4 in summer and UTC-5 in winter. To set a different time zone, call the SetTimeZone
set_time_zone
method. This method accepts either a string following the IANA Time Zone database convention or a NodaTime.DateTimeZone object. If you pass a string, the method converts it to a NodaTime.DateTimeZone
object. The TimeZones
class provides the following helper attributes to create NodaTime.DateTimeZone
objects:
SetTimeZone("Europe/London"); SetTimeZone(NodaTime.DateTimeZone.Utc); SetTimeZone(TimeZones.Chicago);
self.set_time_zone("Europe/London") self.set_time_zone(TimeZones.CHICAGO)
The algorithm time zone may be different from the data time zone. If the time zones are different, it might appear like there is a lag between the algorithm time and the first bar of a history request, but this is just the difference in time zone. All the data is internally synchronized in Coordinated Universal Time (UTC) and arrives in the same Slice object. A slice is a sliver of time with all the data available for this moment.
To keep trades easy to compare between asset classes, we mark all orders in UTC time.
Set Warm Up Period
You may need some historical data at the start of your algorithm to prime technical indicators or populate historical data arrays. The warm-up period pumps data into your algorithm from before the start date. To set a warm-up period, call the SetWarmUp
set_warm_up
method. The warm-up feature uses the subscriptions you add in Initialize
initialize
to gather the historical data that warms up the algorithm. If you don't create security subscriptions in the Initialize
initialize
method, the warm-up won't occur.
// Wind time back 7 days from the start date SetWarmUp(TimeSpan.FromDays(7)); // Feed in 100 trading days worth of data before the start date SetWarmUp(100, Resolution.Daily); // If you don't provide a resolution argument, it uses the lowest resolution in your subscriptions SetWarmUp(100);
# Wind time back 7 days from the start date self.set_warm_up(timedelta(7)) # Feed in 100 trading days worth of data before the start date self.set_warm_up(100, Resolution.DAILY) # If you don't provide a resolution argument, it uses the lowest resolution in your subscriptions self.set_warm_up(100)
Set Name and Tags
You can categorize your backtest with a name and tags. To set the algorithm Name
name
, call the SetName
set_name
method.
// Set your backtest name and tags in the Backtest Result list to help categorize your results. SetName("Backtest Name");
# Set your backtest name and tags in the Backtest Result list to help categorize your results. self.set_name("Backtest Name")
The SetName
set_name
method overwrites the names of backtests of an optimization, which contains the parameter values. To keep these values, create a name that includes them.
// Setting backtest name to mix of parameters can help identify backtests in optimization results. var fastPeriod = GetParameter("fast_period", 100); var midPeriod = GetParameter("mid_period", 200); var slowPeriod = GetParameter("slow_period", 300); SetName($"Backtest Name ({fastPeriod},{midPeriod},{slowPeriod})");
# Setting backtest name to mix of parameters can help identify backtests in optimization results. fast_period = self.get_parameter("fast_period", 100); mid_period = self.get_parameter("mid_period", 200); slow_period = self.get_parameter("slow_period", 300); self.set_name(f"Backtest Name ({fast_period},{mid_period},{slow_period})");
To add tags to you algorithm, call the AddTag
add_tag
method.
// Add additional information to the algorithm for better organization in the backtest results. AddTag("Long Only"); AddTag("Momentum");
# Add additional information to the algorithm for better organization in the backtest results. self.add_tag("Long Only") self.add_tag("Momentum")
Your algorithm can add 20 tags. The name and each tag can have up to 200 characters or else they are truncated.
Post Initialization
After the Initialize
initialize
method, the PostInitialize
post_initialize
method performs post-initialization routines, so don't override it. To be notified when the algorithm is ready to begin trading, define an OnWarmupFinished
on_warmup_finished
method. This method executes even if you don't set a warm-up period.
// Notify yourself when the algorithm is ready to begin trading. public override void OnWarmupFinished() { Log("Algorithm Ready"); }
# Notify yourself when the algorithm is ready to begin trading. def on_warmup_finished(self) -> None: self.log("Algorithm Ready")
Examples
The following examples demonstrate some common practices for initializing algorithms.
Example 1: Set the Data Seed
Some data feeds might not have data on its first fetch, making the algorithm prone to error. To avoid issues, set a seed so the initial price of each asset is its last known price.
// Seed with the last known price of assets to prevent $0 security price errors. public override void Initialize() { SetSecurityInitializer(new BrokerageModelSecurityInitializer( BrokerageModel, new FuncSecuritySeeder(GetLastKnownPrices))); }
# Seed with the last known price of assets to prevent $0 security price errors. def initialize(self): self.set_security_initializer(BrokerageModelSecurityInitializer( self.brokerage_model, FuncSecuritySeeder(self.get_last_known_prices)))
Example 2: Enable Extended Market Hours
Some traders use pre-market data for filtering assets. To enable extended market hour data, adjust the universe settings.
// Request extended market hours to use pre/post market hours data for universe subscriptions. public override void Initialize() { UniverseSettings.ExtendedMarketHours = true; }
# Request extended market hours to use pre/post market hours data for universe subscriptions. def initialize(self): self.universe_settings.extended_market_hours = True
Example 3: Train Models When the Warm Up Finishes
After warming up indicators or rolling windows, you can train machine learning models with the data.
// Train machine learning models after the algorithm warm up is done, so your models // are ready as soon as the algorithm starts trading. public override void OnWarmupFinished() { Train(MyTrainingMethod); }
# Train machine learning models after the algorithm warm up is done, so your models # are ready as soon as the algorithm starts trading. def on_warmup_finished(self) -> None: self.train(self.my_training_method)
For more examples, see the following algorithms: