diff --git a/single-page/Quantconnect-Cloud-Platform.html b/single-page/Quantconnect-Cloud-Platform.html index 55dbcf4f7c..bffe711ba8 100644 --- a/single-page/Quantconnect-Cloud-Platform.html +++ b/single-page/Quantconnect-Cloud-Platform.html @@ -115,20 +115,21 @@

Table of Content

  • 10.2.4 Tastytrade
  • 10.2.5 Alpaca
  • 10.2.6 Charles Schwab
  • -
  • 10.2.7 Binance
  • -
  • 10.2.8 ByBit
  • -
  • 10.2.9 Tradier
  • -
  • 10.2.10 Kraken
  • -
  • 10.2.11 Coinbase
  • -
  • 10.2.12 Bitfinex
  • -
  • 10.2.13 dYdX
  • -
  • 10.2.14 Bloomberg EMSX
  • -
  • 10.2.15 SSC Eze
  • -
  • 10.2.16 Trading Technologies
  • -
  • 10.2.17 Wolverine
  • -
  • 10.2.18 FIX Connections
  • -
  • 10.2.19 CFD and FOREX Brokerages
  • -
  • 10.2.20 Unsupported Brokerages
  • +
  • 10.2.7 Webull
  • +
  • 10.2.8 Binance
  • +
  • 10.2.9 ByBit
  • +
  • 10.2.10 Tradier
  • +
  • 10.2.11 Kraken
  • +
  • 10.2.12 Coinbase
  • +
  • 10.2.13 Bitfinex
  • +
  • 10.2.14 dYdX
  • +
  • 10.2.15 Bloomberg EMSX
  • +
  • 10.2.16 SSC Eze
  • +
  • 10.2.17 Trading Technologies
  • +
  • 10.2.18 Wolverine
  • +
  • 10.2.19 FIX Connections
  • +
  • 10.2.20 CFD and FOREX Brokerages
  • +
  • 10.2.21 Unsupported Brokerages
  • 10.3 Deployment
  • 10.4 Notifications
  • 10.5 Results
  • @@ -142,94 +143,111 @@

    Table of Content

  • 11.4 Strategies
  • 11.5 Deployment
  • 11.6 Results
  • -
  • 12 Object Store
  • -
  • 13 Community
  • -
  • 13.1 Code of Conduct
  • -
  • 13.2 Forum
  • -
  • 13.3 Discord
  • -
  • 13.4 Profile
  • -
  • 13.5 Academic Grants
  • -
  • 13.6 Integration Partners
  • -
  • 13.7 Affiliates
  • -
  • 13.8 Research
  • -
  • 13.9 Strategies
  • -
  • 14 API Reference
  • -
  • 14.1 Authentication
  • -
  • 14.2 Project Management
  • -
  • 14.2.1 Create Project
  • -
  • 14.2.2 Read Project
  • -
  • 14.2.3 Update Project
  • -
  • 14.2.4 Delete Project
  • -
  • 14.2.5 Collaboration
  • -
  • 14.2.5.1 Create Project Collaborator
  • -
  • 14.2.5.2 Read Project Collaborators
  • -
  • 14.2.5.3 Update Project Collaborator
  • -
  • 14.2.5.4 Delete Project Collaborator
  • -
  • 14.2.5.5 Lock Project
  • -
  • 14.2.6 Nodes
  • -
  • 14.2.6.1 Read Project Nodes
  • -
  • 14.2.6.2 Update Project Nodes
  • -
  • 14.3 File Management
  • -
  • 14.3.1 Create File
  • -
  • 14.3.2 Read File
  • -
  • 14.3.3 Update File
  • -
  • 14.3.4 Delete File
  • -
  • 14.4 Compiling Code
  • -
  • 14.4.1 Create Compilation Job
  • -
  • 14.4.2 Read Compilation Result
  • -
  • 14.5 Backtest Management
  • -
  • 14.5.1 Create Backtest
  • -
  • 14.5.2 Read Backtest
  • -
  • 14.5.2.1 Backtest Statistics
  • -
  • 14.5.2.2 Charts
  • -
  • 14.5.2.3 Orders
  • -
  • 14.5.2.4 Insights
  • -
  • 14.5.3 Update Backtest
  • -
  • 14.5.4 Delete Backtest
  • -
  • 14.5.5 List Backtests
  • -
  • 14.6 Live Management
  • -
  • 14.6.1 Create Live Algorithm
  • -
  • 14.6.2 Read Live Algorithm
  • -
  • 14.6.2.1 Live Algorithm Statistics
  • -
  • 14.6.2.2 Charts
  • -
  • 14.6.2.3 Portfolio State
  • -
  • 14.6.2.4 Orders
  • -
  • 14.6.2.5 Insights
  • -
  • 14.6.2.6 Logs
  • -
  • 14.6.3 Update Live Algorithm
  • -
  • 14.6.3.1 Liquidate Live Portfolio
  • -
  • 14.6.3.2 Stop Live Algorithm
  • -
  • 14.6.4 List Live Algorithms
  • -
  • 14.6.5 Live Commands
  • -
  • 14.6.5.1 Create Live Command
  • -
  • 14.6.5.2 Broadcast Live Command
  • -
  • 14.7 Optimization Management
  • -
  • 14.7.1 Create Optimization
  • -
  • 14.7.2 Read Optimization
  • -
  • 14.7.3 Update Optimization
  • -
  • 14.7.4 Delete Optimization
  • -
  • 14.7.5 Abort Optimization
  • -
  • 14.7.6 List Optimization
  • -
  • 14.7.7 Estimate Optimization Cost
  • -
  • 14.8 Object Store Management
  • -
  • 14.8.1 Upload Object Store Files
  • -
  • 14.8.2 Get Object Store Metadata
  • -
  • 14.8.3 Get Object Store File
  • -
  • 14.8.4 Delete Object Store File
  • -
  • 14.8.5 List Object Store Files
  • -
  • 14.9 AI Assistance
  • -
  • 14.9.1 Tools
  • -
  • 14.9.1.1 Backtest Initialization
  • -
  • 14.9.1.2 Code Completion
  • -
  • 14.9.1.3 Error Enhancement
  • -
  • 14.9.1.4 PEP8 Conversion
  • -
  • 14.9.1.5 Syntax Check
  • -
  • 14.9.1.6 Search
  • -
  • 14.10 Reports
  • -
  • 14.10.1 Backtest Report
  • -
  • 14.11 Account
  • -
  • 14.12 Lean Version
  • -
  • 14.13 Examples
  • +
  • 12 Research Pipeline
  • +
  • 13 Object Store
  • +
  • 14 Community
  • +
  • 14.1 Code of Conduct
  • +
  • 14.2 Forum
  • +
  • 14.3 Discord
  • +
  • 14.4 Profile
  • +
  • 14.5 Academic Grants
  • +
  • 14.6 Integration Partners
  • +
  • 14.7 Affiliates
  • +
  • 14.8 Research
  • +
  • 14.9 Strategies
  • +
  • 15 API Reference
  • +
  • 15.1 Authentication
  • +
  • 15.2 Project Management
  • +
  • 15.2.1 Create Project
  • +
  • 15.2.2 Read Project
  • +
  • 15.2.3 Update Project
  • +
  • 15.2.4 Delete Project
  • +
  • 15.2.5 Collaboration
  • +
  • 15.2.5.1 Create Project Collaborator
  • +
  • 15.2.5.2 Read Project Collaborators
  • +
  • 15.2.5.3 Update Project Collaborator
  • +
  • 15.2.5.4 Delete Project Collaborator
  • +
  • 15.2.5.5 Lock Project
  • +
  • 15.2.6 Nodes
  • +
  • 15.2.6.1 Read Project Nodes
  • +
  • 15.2.6.2 Update Project Nodes
  • +
  • 15.3 File Management
  • +
  • 15.3.1 Create File
  • +
  • 15.3.2 Read File
  • +
  • 15.3.3 Update File
  • +
  • 15.3.4 Delete File
  • +
  • 15.4 Compiling Code
  • +
  • 15.4.1 Create Compilation Job
  • +
  • 15.4.2 Read Compilation Result
  • +
  • 15.5 Backtest Management
  • +
  • 15.5.1 Create Backtest
  • +
  • 15.5.2 Read Backtest
  • +
  • 15.5.2.1 Backtest Statistics
  • +
  • 15.5.2.2 Charts
  • +
  • 15.5.2.3 Orders
  • +
  • 15.5.2.4 Insights
  • +
  • 15.5.3 Update Backtest
  • +
  • 15.5.4 Delete Backtest
  • +
  • 15.5.5 List Backtests
  • +
  • 15.6 Live Management
  • +
  • 15.6.1 Create Live Algorithm
  • +
  • 15.6.2 Read Live Algorithm
  • +
  • 15.6.2.1 Live Algorithm Statistics
  • +
  • 15.6.2.2 Charts
  • +
  • 15.6.2.3 Portfolio State
  • +
  • 15.6.2.4 Orders
  • +
  • 15.6.2.5 Insights
  • +
  • 15.6.2.6 Logs
  • +
  • 15.6.3 Update Live Algorithm
  • +
  • 15.6.3.1 Liquidate Live Portfolio
  • +
  • 15.6.3.2 Stop Live Algorithm
  • +
  • 15.6.4 List Live Algorithms
  • +
  • 15.6.5 Live Commands
  • +
  • 15.6.5.1 Create Live Command
  • +
  • 15.6.5.2 Broadcast Live Command
  • +
  • 15.7 Optimization Management
  • +
  • 15.7.1 Create Optimization
  • +
  • 15.7.2 Read Optimization
  • +
  • 15.7.3 Update Optimization
  • +
  • 15.7.4 Delete Optimization
  • +
  • 15.7.5 Abort Optimization
  • +
  • 15.7.6 List Optimization
  • +
  • 15.7.7 Estimate Optimization Cost
  • +
  • 15.8 Object Store Management
  • +
  • 15.8.1 Upload Object Store Files
  • +
  • 15.8.2 Get Object Store Metadata
  • +
  • 15.8.3 Get Object Store File
  • +
  • 15.8.4 Delete Object Store File
  • +
  • 15.8.5 List Object Store Files
  • +
  • 15.9 AI Assistance
  • +
  • 15.9.1 Tools
  • +
  • 15.9.1.1 Backtest Initialization
  • +
  • 15.9.1.2 Code Completion
  • +
  • 15.9.1.3 Error Enhancement
  • +
  • 15.9.1.4 PEP8 Conversion
  • +
  • 15.9.1.5 Syntax Check
  • +
  • 15.9.1.6 Search
  • +
  • 15.10 Reports
  • +
  • 15.10.1 Backtest Report
  • +
  • 15.11 Account
  • +
  • 15.12 Lean Version
  • +
  • 15.13 Agent Management
  • +
  • 15.13.1 Agents
  • +
  • 15.13.2 Tasks
  • +
  • 15.13.2.1 Create Task
  • +
  • 15.13.2.2 Read Task
  • +
  • 15.13.2.3 Update Task
  • +
  • 15.13.2.4 Delete Task
  • +
  • 15.13.2.5 List Tasks
  • +
  • 15.13.3 Deployments
  • +
  • 15.13.3.1 Create Deployment
  • +
  • 15.13.3.2 Read Deployment
  • +
  • 15.13.3.3 Update Deployment
  • +
  • 15.13.3.4 List Deployments
  • +
  • 15.13.3.5 Stop Deployment
  • +
  • 15.13.3.6 Delete Deployment
  • +
  • 15.13.3.7 Read Conversation
  • +
  • 15.14 Examples
  •  

    @@ -565,7 +583,11 @@

    Physical access to our servers is limited to a few dedicated team members whom QuantConnect has vetted. Only those credentialed team members can access the physical servers, and we schedule all work in advance. - Work on the servers is always done in pairs to prevent single rogue actors from accessing the servers. We host our servers in a world-class security facility (Equinix) with security staff 24/7. + Work on the servers is always done in pairs to prevent single rogue actors from accessing the servers. We host our servers in a world-class security facility, on co-located servers racked in + + Equinix + + , with security staff 24/7.

    Information and Digital Security @@ -614,7 +636,7 @@

    Code Encryption

    -

    On-Premises Installations

    +

    On-Premise Installations

    @@ -1076,14 +1098,18 @@

    Trading Firm Tier

    The Trading Firm tier is designed for growing quantitative firms, prop desks, hedge funds, ETF companies, professional teams of quants, and sophisticated independent investors. It has special features for collaborating with consultants to protect the investor IP. If you are a company on QuantConnect, we recommend the Trading Firm Pack to make the most of QuantConnect.

    - The Trading Firm tier builds on the features included in the Team tier. Organizations on the Trading Firm tier can have an unlimited number of members and an unlimited number of collaborators simultaneously working on individual projects. The IP ownership of all the projects in these organizations remains within the organization. There is no limit on the number of backtesting, research, and live trading nodes these organizations can rent. They can produce 5MB of logs/backtest and 50MB of logs/day. Members in these organizations can have up to eight active coding sessions in the organization. Each live algorithm in a Trading Firm organization can send up to 240 Telegram, Email, or Webhook notifications per hour for free. + The Trading Firm tier builds on the features included in the Team tier. Organizations on the Trading Firm tier can have an unlimited number of members and up to 10 collaborators simultaneously working on an individual project. The IP ownership of all the projects in these organizations remains within the organization. There is no limit on the number of backtesting, research, and live trading nodes these organizations can rent. They can produce 5MB of logs/backtest and 50MB of logs/day. Members in these organizations can have up to eight active coding sessions in the organization. Each live algorithm in a Trading Firm organization can send up to 240 Telegram, Email, or Webhook notifications per hour for free. These organizations can also + + schedule AI assistant tasks + + to run automatically.

    The owner of a Trading Firm organization can grant various permissions - to the organization’s members, including designating a member to manage the organization's billing. These organizations have access to custom lean builds, so they can use feature branches or historical master branches to run their strategies. An example of this could be granting only a few members of your team live trading deployment access. + to the organization's members, including designating a member to manage the organization's billing. These organizations have access to custom lean builds, so they can use feature branches or historical master branches to run their strategies. An example of this could be granting only a few members of your team live trading deployment access.

    In addition to the brokerages and data providers available to Team organizations, Trading Firm organizations can use @@ -1481,7 +1507,11 @@

    Live Trading Nodes

    - Live trading nodes enable you to deploy live algorithms to our professionally-managed, co-located servers. + Live trading nodes enable you to deploy live algorithms to our professionally-managed, co-located servers racked in + + Equinix + + . You need a live trading node for each algorithm that you deploy to our co-located servers. Several models of live trading nodes are available. More powerful live trading nodes allow you to run algorithms with larger universes and give you @@ -1639,6 +1669,134 @@

    Live Trading Nodes

    +

    Assistant Nodes

    + + +

    + Assistant nodes are the servers that run your assistants—a harness for quant finance that wraps your AI models in the tools, data, and context they need to be productive. + Assistant nodes enable you to deploy assistant tasks. + The more assistant nodes your organization has, the more concurrent assistant tasks that you can run. + More powerful assistant nodes have more cores and RAM to handle larger, more demanding tasks. + The following table shows the specifications of the assistant node models: +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Name + + Number of Cores + + RAM (GB) + + Agents +
    + A-MICRO + + 1 + + 1 + + 1 +
    + A1-1 + + 1 + + 1 + + 1 +
    + A4-8 + + 4 + + 8 + + 4 +
    + A16-32 + + 16 + + 32 + + 16 +
    + +

    + Refer to the + + Pricing + + page to see the price of each assistant node model. + Your first organization includes one free A-MICRO assistant node, which is capped at 100,000 tokens per month. + The token cap is lifted and the node is replaced when you + + upgrade your organization to a paid tier + + and + + add a new assistant node + + . +

    +

    + To view the status of all of your organization's nodes, see the + + Resources panel + + of the IDE. + When you launch an assistant task, it uses the best-performing resource by default, but you can + + select a specific resource to use + + . +

    + + +

    Sharing Resources

    @@ -3036,7 +3194,7 @@

    Introduction

    There are three tiers of support seats and each tier provides different services. You can file support tickets to get private assistance with issues, but support tickets should not replace your efforts of performing your own research and reading through the documentation. If you need further assistance than what our Support Team offers, consider hiring an - + Integration Partner . @@ -3059,11 +3217,11 @@

    email , - + Discord , or the - + forum . We will review your submission. If we confirm you've found a bug, we will create a GitHub Issue to have it resolved. Subscribe to the GitHub Issue to track our progress in fixing the bug. @@ -3156,8 +3314,8 @@

    For more information on this feature, see - - Strategy Development + + AI Assistance .

    @@ -3470,7 +3628,7 @@

    LEAN CLI , and the - + QuantConnect REST API , which includes installation and subjects that don't apply to the Cloud Platform. @@ -4052,7 +4210,7 @@

    Ticket Quotas

    Chat with - + Mia for immediate assistance. We trained it on hundreds of algorithms and thousands of documentation pages to provide contextual assistance for most issues you may encounter when developing a strategy. @@ -5756,7 +5914,7 @@

    Introduction

    We created QuantConnect Credit (QCC) to enable micropayments on QuantConnect. - You can use QCC to optimize parameters, download datasets, gift to members in the forum, and chat with our agentic AI assistant. + You can use QCC to optimize parameters, download datasets, gift to members in the forum, and cover live notification overages (such as SMS and extra Telegram, email, and webhook notifications above the per-hour free allowance). You can purchase QCC in the Algorithm Lab at a rate of 1 QCC = $0.01 USD. Since QCC is owned by organizations instead of members, all of the members within your organization have the ability to spend the QCC balance.

    @@ -5801,7 +5959,7 @@

    Giving to Others

    To show your appreciation for contributions in the forum, - + give some QuantConnect Credit (QCC) rewards . The following table shows the available QCC rewards: @@ -6108,52 +6266,6 @@

    Giving to Others

    -

    Powering Agentic AI

    - - -

    - The - - support plans - - include some AI tokens you can use to power our agentic AI assistant, Mia. -

    - Cloud Platform view with Mia implementing a momentum strategy for US Equities. - Cloud Platform view with Mia implementing a momentum strategy for US Equities. -

    - Each message you send and recieve through the Ask Mia panel of the IDE consumes some of your - - support seat's AI tokens - - . - If you exhaust your - - monthly quota - - , you can - - purchase some QCC - - to continue using Mia before your monthly allocation of AI tokens resets. - To enable Mia to consume your QCC, open the - - Billing - - page, scroll down to the - - Organization Credit (QCC) - - section, and then select the - - Automatically consume QCC for AI usage - - check box. - QCC tokens are consumed at approximately the same rate as the top model from major providers. - We provide this value-added wrapper on the major providers so we can offer powerful tools without violating our data licenses, enabling you to easily use the major models and still have the full benefits of QuantConnect. -

    - - -

    Purchase QCC

    @@ -7020,7 +7132,7 @@

    Backtesting
    - + Sharing Backtests
    @@ -7407,9 +7519,12 @@

    Delete a parameter
  • Remove the - + GetParameter + + get_parameter + calls that were associated with the parameter from your code files.
  • @@ -7420,7 +7535,19 @@

    Delete Projects

    - You can delete a project when it is open or closed. + You can delete a project when it is open or closed. Deleted projects are moved to the + + Recycle Bin + + directory on the + + My Projects + + page, where you can recover them if you delete one by mistake. Projects in the + + Recycle Bin + + are permanently deleted after 30 days.

    Delete Open Projects @@ -7466,6 +7593,57 @@

    "Project deleted" displays.

    +

    + Recover Deleted Projects +

    +

    + When you delete a project, it moves to the + + Recycle Bin + + directory instead of being permanently removed. Projects in the + + Recycle Bin + + are permanently deleted after 30 days, so recover them within that window. Follow these steps to recover a project you deleted by mistake: +

    +
      +
    1. + Open the + + My Projects + + page. +
    2. +
    3. + Click the + + Recycle Bin + + directory. +
    4. +
    5. + Click the project to open it. +
    6. +
    7. + + Rename the project + + to remove the + + Recycle Bin/ + + prefix from its path (for example, rename + + Recycle Bin/My Legacy Project + + to + + My Legacy Project + + ). +
    8. +
    @@ -8413,7 +8591,8 @@

    AI Chats

    The Ask Mia panel is where you can privately have an agentic conversation with Mia, an AI assistant we trained on hundreds of algorithms and thousands of documentation pages. - Mia has your project context so the agent can directly edit your project files, run backtests, and deploy your algorithm to our live trading servers. + Mia has your project context so the agent can directly edit your project files, run backtests, and deploy your algorithm to our live trading servers. + She runs on our Assistant servers—a harness for quant finance that wraps the underlying AI model in these tools, data, and context to make it far more productive. Agentic conversations like this unlock the ability to create, research, and deploy algorithmic trading strategies without being an expert programmer.

    Cloud Platform view with Mia implementing a momentum strategy for US Equities. @@ -9333,6 +9512,87 @@

    +

    Order Tagging

    + + +

    + You can add + + tags + + to your orders to track why each trade was placed. This is useful for debugging your algorithm's trading logic because you can include the indicator values or conditions that triggered the order. +

    +
    +
    public class OrderTaggingAlgorithm : QCAlgorithm
    +{
    +    private ExponentialMovingAverage _emaShort;
    +    private ExponentialMovingAverage _emaLong;
    +    private Symbol _symbol;
    +
    +    public override void Initialize()
    +    {
    +        SetStartDate(2024, 9, 1);
    +        SetEndDate(2024, 12, 31);
    +        SetCash(100000);
    +
    +        _symbol = AddEquity("SPY", Resolution.Daily).Symbol;
    +        _emaShort = EMA(_symbol, 10);
    +        _emaLong = EMA(_symbol, 30);
    +    }
    +
    +    public override void OnData(Slice data)
    +    {
    +        if (!_emaShort.IsReady || !_emaLong.IsReady) return;
    +
    +        if (_emaShort > _emaLong && !Portfolio[_symbol].IsLong)
    +        {
    +            MarketOrder(_symbol, 100, tag: $"BUY: ema-short: {_emaShort:F4} > ema-long: {_emaLong:F4}");
    +        }
    +        else if (_emaShort < _emaLong && !Portfolio[_symbol].IsShort)
    +        {
    +            MarketOrder(_symbol, -100, tag: $"SELL: ema-short: {_emaShort:F4} < ema-long: {_emaLong:F4}");
    +        }
    +    }
    +}
    +
    class OrderTaggingAlgorithm(QCAlgorithm):
    +    def initialize(self) -> None:
    +        self.set_start_date(2024, 9, 1)
    +        self.set_end_date(2024, 12, 31)
    +        self.set_cash(100000)
    +
    +        self._symbol = self.add_equity("SPY", Resolution.DAILY).symbol
    +        self._ema_short = self.ema(self._symbol, 10)
    +        self._ema_long = self.ema(self._symbol, 30)
    +
    +    def on_data(self, data: Slice) -> None:
    +        if not self._ema_short.is_ready or not self._ema_long.is_ready:
    +            return
    +
    +        ema_short = self._ema_short.current.value
    +        ema_long = self._ema_long.current.value
    +        if ema_short > ema_long and not self.portfolio[self._symbol].is_long:
    +            self.market_order(self._symbol, 100, tag=f'BUY: ema-short: {ema_short:.4f} > ema-long: {ema_long:.4f}')
    +        elif ema_short < ema_long and not self.portfolio[self._symbol].is_short:
    +            self.market_order(self._symbol, -100, tag=f'SELL: ema-short: {ema_short:.4f} < ema-long: {ema_long:.4f}')
    +
    +

    + The tag is saved to the + + result files + + that you can download for local analysis. Unlike + + logging statements + + , order tags are attached directly to each order, so you don't need to search through logs to match a message to a specific trade, and they don't consume your + + log quota + + . +

    + + +

    Charting

    @@ -9370,7 +9630,7 @@

    Object Store

    The Object Store is a key-value data store for low-latency information storage and retrieval. - During a backtest, you can build large objects you’d like to analyze and write them for later analysis. + During a backtest, you can build large objects you'd like to analyze and write them for later analysis. This workflow can be helpful when the objects are large and plotting is impossible or when you want to perform analysis across many backtests.

    @@ -9452,10 +9712,21 @@

    Add Team Members

  • Click the - Select User... + Select User or Entire Organization... field and then click a member from the drop-down menu.
  • +

    + The drop-down menu also contains an entry with your organization's logo, labeled + + Entire + + organizationName + + Organization + + . This entry shares the project with all the members of your organization and counts as one collaborator slot. +

  • If you want to give the member @@ -9492,11 +9763,11 @@

    Collaborator Quotas

    - The number of members you can add to a project depends on your + The number of collaborators you can add to a project depends on your organization's tier - . The following table shows the number of collaborators each tier can have per project: + . This quota is separate from the number of members your organization can have. The following table shows the number of collaborators each tier can have per project:

    @@ -9540,7 +9811,7 @@

    Collaborator Quotas

    Trading Firm @@ -10592,8 +10863,10 @@

    Example

    bbdf[['price', 'lowerband', 'middleband', 'upperband']].plot();
    -
    #load "../QuantConnect.csx"
    -using QuantConnect;
    +   
    #load "../QuantConnect.csx"
    +
    +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
    @@ -11667,7 +11940,7 @@ 

    Out of Sample Period

    -

    On-Premises Backtests

    +

    On-Premise Backtests

    @@ -11735,11 +12008,11 @@

    Share Backtests

    To attach the embedded backtest result to a forum discussion, see - + Create Discussions or - + Post Comments . @@ -14174,7 +14447,7 @@

    method or the - + /backtests/orders/read endpoint. @@ -14391,11 +14664,11 @@

    Share Results

    To attach the embedded backtest result to a forum discussion, see - + Create Discussions or - + Post Comments . @@ -18483,7 +18756,11 @@

    Delivery

    - Most live trading algorithms run on co-located servers racked in Equinix. Co-location reduces several factors that can interfere with your algorithm, including downtime from internet outages, equipment repairs, and natural disasters. + Most live trading algorithms run on co-located servers racked in + + Equinix + + . Co-location reduces several factors that can interfere with your algorithm, including downtime from internet outages, equipment repairs, and natural disasters.

    Live data takes time to travel from the source to your algorithm. @@ -18665,7 +18942,11 @@

    Delivery

    - Most live trading algorithms run on co-located servers racked in Equinix. Co-location reduces several factors that can interfere with your algorithm, including downtime from internet outages, equipment repairs, and natural disasters. + Most live trading algorithms run on co-located servers racked in + + Equinix + + . Co-location reduces several factors that can interfere with your algorithm, including downtime from internet outages, equipment repairs, and natural disasters.

    Live data takes time to travel from the source to your algorithm. @@ -18813,7 +19094,11 @@

    Delivery

    - Most live trading algorithms run on co-located servers racked in Equinix. Co-location reduces several factors that can interfere with your algorithm, including downtime from internet outages, equipment repairs, and natural disasters. + Most live trading algorithms run on co-located servers racked in + + Equinix + + . Co-location reduces several factors that can interfere with your algorithm, including downtime from internet outages, equipment repairs, and natural disasters.

    Live data takes time to travel from the source to your algorithm. @@ -18948,7 +19233,11 @@

    Delivery

    - Most live trading algorithms run on co-located servers racked in Equinix. Co-location reduces several factors that can interfere with your algorithm, including downtime from internet outages, equipment repairs, and natural disasters. + Most live trading algorithms run on co-located servers racked in + + Equinix + + . Co-location reduces several factors that can interfere with your algorithm, including downtime from internet outages, equipment repairs, and natural disasters.

    Live data takes time to travel from the source to your algorithm. @@ -19083,7 +19372,11 @@

    Delivery

    - Most live trading algorithms run on co-located servers racked in Equinix. Co-location reduces several factors that can interfere with your algorithm, including downtime from internet outages, equipment repairs, and natural disasters. + Most live trading algorithms run on co-located servers racked in + + Equinix + + . Co-location reduces several factors that can interfere with your algorithm, including downtime from internet outages, equipment repairs, and natural disasters.

    Live data takes time to travel from the source to your algorithm. @@ -19249,7 +19542,11 @@

    Delivery

    - Most live trading algorithms run on co-located servers racked in Equinix. Co-location reduces several factors that can interfere with your algorithm, including downtime from internet outages, equipment repairs, and natural disasters. + Most live trading algorithms run on co-located servers racked in + + Equinix + + . Co-location reduces several factors that can interfere with your algorithm, including downtime from internet outages, equipment repairs, and natural disasters.

    Live data takes time to travel from the source to your algorithm. @@ -19389,7 +19686,11 @@

    Delivery

    , include a live stream. In these cases, we deliver the data as a live stream to your algorithm.

    - Most live trading algorithms run on co-located servers racked in Equinix. Co-location reduces several factors that can interfere with your algorithm, including downtime from internet outages, equipment repairs, and natural disasters. + Most live trading algorithms run on co-located servers racked in + + Equinix + + . Co-location reduces several factors that can interfere with your algorithm, including downtime from internet outages, equipment repairs, and natural disasters.

    Live data takes time to travel from the source to your algorithm. The latency of the alternative data depends on the specific dataset you're using. @@ -19931,7 +20232,7 @@

    Introduction

    TradeStation data provider serves Equity, Equity Option, and Futures prices directly from - + TradeStation's MarketData API . @@ -19948,11 +20249,7 @@

    Sourcing

    TradeStation - data provider sources data directly from - - TradeStation's MarketData API - - . + data provider sources data directly from TradeStation's MarketData API. If you use this data provider, TradeStation only provides the security price data. QuantConnect Cloud provides the @@ -20045,11 +20342,7 @@

    Pricing

    - To view the latest prices, see the - - Data - - page on the + To view the latest prices, see the Data page on the TradeStation @@ -20415,7 +20708,7 @@

    Hybrid Data Provider

    When you - + deploy a live algorithm with the Charles Schwab brokerage , you can use a third-party data provider, the Charles Schwab data provider, or both. @@ -21666,7 +21959,11 @@

    Introduction

    - A live algorithm is an algorithm that trades in real-time with real market data. QuantConnect enables you to run your algorithms in live mode with real-time market data. Deploy your algorithms using QuantConnect because our infrastructure is battle-tested. The algorithms that our members create are run on co-located servers and the trading infrastructure is maintained at all times by our team of engineers. It's common for members to achieve 6-months of uptime with no interruptions. + A live algorithm is an algorithm that trades in real-time with real market data. QuantConnect enables you to run your algorithms in live mode with real-time market data. Deploy your algorithms using QuantConnect because our infrastructure is battle-tested. The algorithms that our members create are run on co-located servers racked in + + Equinix + + and the trading infrastructure is maintained at all times by our team of engineers. It's common for members to achieve 6-months of uptime with no interruptions.

    @@ -22037,7 +22334,7 @@

    Clear Live Algorithms History

    -

    On-Premises Live Algorithms

    +

    On-Premise Live Algorithms

    @@ -22173,8 +22470,20 @@

    +

    - Unlimited + 10
    - - - + @@ -28538,6 +29172,9 @@

    + + + + + + + + + +
    + Order Type - Crypto + + Equity - Crypto Futures + + Equity Options + + Index Options
    green check + green check +
    @@ -28551,14 +29188,21 @@

    green check + green check +
    - Stop Market + Stop market + green check + + green check green check @@ -28576,25 +29220,41 @@

    green check + green check +
    + + Trailing stop + + + green check + + +

    Order Properties -

    - We model custom order properties from the Binance and Binance US APIs. The following table describes the members of the + We model custom order properties from the Webull API. The following table describes the members of the - BinanceOrderProperties + WebullOrderProperties - object that you can set to customize order execution: + object that you can set to customize order execution.

    @@ -28651,15 +29311,22 @@

    GOOD_TIL_CANCELED -
  • - - GoodTilDate - - - good_til_date - -
  • + Market orders support only + + Day + + + DAY + + , which the brokerage sets automatically. Option and Index Option sell orders also support only + + Day + + + DAY + + .

    @@ -28696,12 +29369,60 @@

    Updates

    - We model the Binance and Binance US APIs by not supporting order updates, but you can cancel an existing order and then create a new order with the desired arguments. For more information about this workaround, see the - - Workaround for Brokerages That Don’t Support Updates + We model the Webull API by supporting + + order updates .

    +

    + Handling Splits +

    +

    + If you're using raw + + data normalization + + and you have active orders with a limit, stop, or trigger price in the market for a US Equity when a + + stock split + + occurs, the following properties of your orders automatically adjust to reflect the stock split: +

    +
      +
    • + Quantity +
    • +
    • + Limit price +
    • +
    • + Stop price +
    • +
    • + Trigger price +
    • +
    + + + +

    Rate Limit

    + + +

    + Webull enforces the following rate limits on its trading API: +

    +
      +
    • + Order requests (place, replace, and cancel): 600 requests per 60 seconds. +
    • +
    • + Account and order queries (balance, positions, open orders, and order history): 2 requests per 2 seconds. +
    • +
    +

    + To avoid hitting these limits, design your algorithm to issue orders sparingly. +

    @@ -28709,19 +29430,15 @@

    Fees

    - To view the Binance or Binance US trading fees, see the - - Trading Fees - - page on the Binance.com website or the - - Fee Structure + Webull trading for Equity and Equity Options is commission-free. Index Options incur per-contract exchange fees. To view the Webull trading fees, see the + + Pricing - page on the Binance.us website. To view how we model their fees, see - + page on the Webull website. To view how we model their fees, see + Fees - . The Binance Spot Test Network does not charge order fees. + .

    @@ -28731,16 +29448,20 @@

    Margin

    We model - + buying power and margin calls - to ensure your algorithm stays within the margin requirements. If you trade Crypto Perpetual Futures, we model the margin cost and payments of your Crypto Future holdings by directly adjusting your portfolio cash. For more information about Futures margin interest modeling, see the - - Binance Futures Model + to ensure your algorithm stays within the margin requirements. If you have more than $25,000 in your brokerage account, you can use the + + PatternDayTradingMarginModel + + to make use of the 4x intraday leverage and 2x overnight leverage available on most brokerages from the + + PDT rule .

    @@ -28751,15 +29472,15 @@

    Slippage

    - Orders through Binance and Binance US do not experience slippage in backtests and + Orders through Webull do not experience slippage in backtests and QuantConnect Paper Trading . In live trading, your orders may experience slippage.

    - To view how we model Binance and Binance US slippage, see - + To view how we model Webull slippage, see + Slippage . @@ -28778,8 +29499,8 @@

    Fills

    . In live trading, if the quantity of your market orders exceeds the quantity available at the top of the order book, your orders are filled according to what is available in the order book.

    - To view how we model Binance and Binance US order fills, see - + To view how we model Webull order fills, see + Fills . @@ -28791,11 +29512,11 @@

    Settlements

    - Trades settle immediately after the transaction + If you trade with a margin account, trades settle immediately

    - To view how we model settlement for Binance and Binance US trades, see - + To view how we model settlement for Webull trades, see + Settlement . @@ -28807,7 +29528,7 @@

    Security and Stability

    - When you deploy live algorithms with Binance or Binance US, we don't save your credentials. + When you deploy live algorithms with Webull, we don't save your credentials.

    @@ -28820,6 +29541,9 @@

    Deposits and Withdrawals

    account. We sync the algorithm's cash holdings with the cash holdings in your brokerage account every day at 7:45 AM Eastern Time (ET).

    +

    + Funds are available for API trading 24 hours after the deposit. +

    @@ -28827,66 +29551,12 @@

    Demo Algorithm

    - The following algorithm demonstrates the functionality of the Binance and Binance US brokerages: + The following algorithm demonstrates the functionality of the Webull brokerage:

    -

    - Binance -

    -
    -
    // Demonstrate Binance brokerage functionality with an EMA crossover strategy.
    -public class BinanceDemoAlgorithm : QCAlgorithm
    -{
    -    private Symbol _symbol;
    -    private ExponentialMovingAverage _fast;
    -    private ExponentialMovingAverage _slow;
    -
    -    public override void Initialize()
    -    {
    -        SetStartDate(2024, 9, 1);
    -        SetEndDate(2024, 12, 31);
    -        SetCash(100000);
    -        SetBrokerageModel(BrokerageName.Binance, AccountType.Cash);
    -        _symbol = AddCrypto("BTCUSDT", Resolution.Daily, Market.Binance).Symbol;
    -        _fast = EMA(_symbol, 10, Resolution.Daily);
    -        _slow = EMA(_symbol, 50, Resolution.Daily);
    -    }
    -
    -    public override void OnData(Slice slice)
    -    {
    -        if (!_slow.IsReady) return;
    -        if (_fast > _slow && !Portfolio.Invested)
    -            SetHoldings(_symbol, 1);
    -        else if (_fast < _slow && Portfolio.Invested)
    -            Liquidate();
    -    }
    -}
    -
    # Demonstrate Binance brokerage functionality with an EMA crossover strategy.
    -class BinanceDemoAlgorithm(QCAlgorithm):
    -    def initialize(self) -> None:
    -        self.set_start_date(2024, 9, 1)
    -        self.set_end_date(2024, 12, 31)
    -        self.set_cash(100000)
    -        self.set_brokerage_model(BrokerageName.BINANCE, AccountType.CASH)
    -        self._symbol = self.add_crypto("BTCUSDT", Resolution.DAILY, Market.BINANCE).symbol
    -        self._fast = self.ema(self._symbol, 10, Resolution.DAILY)
    -        self._slow = self.ema(self._symbol, 50, Resolution.DAILY)
    -
    -    def on_data(self, slice: Slice) -> None:
    -        if not self._slow.is_ready:
    -            return
    -        if self._fast.current.value > self._slow.current.value and not self.portfolio.invested:
    -            self.set_holdings(self._symbol, 1)
    -        elif self._fast.current.value < self._slow.current.value and self.portfolio.invested:
    -            self.liquidate()
    -
    -

    - Binance US -

    -
    // Demonstrate Binance US brokerage functionality with an EMA crossover strategy.
    -public class BinanceUsDemoAlgorithm : QCAlgorithm
    +   
    // Demonstrate Webull brokerage functionality with an EMA crossover strategy on SPY.
    +public class WebullDemoAlgorithm : QCAlgorithm
     {
    -    private Symbol _symbol;
         private ExponentialMovingAverage _fast;
         private ExponentialMovingAverage _slow;
     
    @@ -28895,56 +29565,43 @@ 

    SetStartDate(2024, 9, 1); SetEndDate(2024, 12, 31); SetCash(100000); - SetBrokerageModel(BrokerageName.BinanceUS, AccountType.Cash); - _symbol = AddCrypto("BTCUSD", Resolution.Daily, Market.BinanceUS).Symbol; - _fast = EMA(_symbol, 10, Resolution.Daily); - _slow = EMA(_symbol, 50, Resolution.Daily); + SetBrokerageModel(BrokerageName.Webull, AccountType.Margin); + var symbol = AddEquity("SPY", Resolution.Daily).Symbol; + _fast = EMA(symbol, 10, Resolution.Daily); + _slow = EMA(symbol, 50, Resolution.Daily); } public override void OnData(Slice slice) { if (!_slow.IsReady) return; if (_fast > _slow && !Portfolio.Invested) - SetHoldings(_symbol, 1); + SetHoldings("SPY", 1); else if (_fast < _slow && Portfolio.Invested) Liquidate(); } }

    -
    # Demonstrate Binance US brokerage functionality with an EMA crossover strategy.
    -class BinanceUsDemoAlgorithm(QCAlgorithm):
    +   
    # Demonstrate Webull brokerage functionality with an EMA crossover strategy on SPY.
    +class WebullDemoAlgorithm(QCAlgorithm):
         def initialize(self) -> None:
             self.set_start_date(2024, 9, 1)
             self.set_end_date(2024, 12, 31)
             self.set_cash(100000)
    -        self.set_brokerage_model(BrokerageName.BINANCE_US, AccountType.CASH)
    -        self._symbol = self.add_crypto("BTCUSD", Resolution.DAILY, Market.BINANCE_US).symbol
    -        self._fast = self.ema(self._symbol, 10, Resolution.DAILY)
    -        self._slow = self.ema(self._symbol, 50, Resolution.DAILY)
    +        self.set_brokerage_model(BrokerageName.WEBULL, AccountType.MARGIN)
    +        symbol = self.add_equity("SPY", Resolution.DAILY).symbol
    +        self._fast = self.ema(symbol, 10, Resolution.DAILY)
    +        self._slow = self.ema(symbol, 50, Resolution.DAILY)
     
         def on_data(self, slice: Slice) -> None:
             if not self._slow.is_ready:
                 return
             if self._fast.current.value > self._slow.current.value and not self.portfolio.invested:
    -            self.set_holdings(self._symbol, 1)
    +            self.set_holdings("SPY", 1)
             elif self._fast.current.value < self._slow.current.value and self.portfolio.invested:
                 self.liquidate()
    -

    Virtual Pairs

    - - -

    - All fiat and Crypto currencies are individual assets. When you buy a pair like BTCUSD, you trade USD for BTC. In this case, LEAN removes some USD from your portfolio - - cashbook - - and adds some BTC. The virtual pair BTCUSD represents your position in that trade, but the virtual pair doesn't actually exist. It simply represents an open trade. When you deploy a live algorithm, LEAN populates your cashbook with the quantity of each currency, but it can't get your position of each virtual pair. -

    - - -

    Deploy Live Algorithms

    @@ -28980,61 +29637,20 @@

    Deploy Live Algorithms

    field and then click - Binance Exchange + Webull from the drop-down menu.
  • - Enter your API key and secret. + Enter your Webull App Key, App Secret, and account ID.
  • To generate your API credentials, see - + Account Types . Your account details are not saved on QuantConnect.

    -
  • - Click on the - - Environment - - field and then click one of the environments. -
  • -

    - The following table shows the supported environments: -

    -
    @@ -28673,10 +29340,10 @@

    - PostOnly + OutsideRegularTradingHours - post_only + outside_regular_trading_hours @@ -28685,9 +29352,15 @@

    - A flag to signal that the order must only add liquidity to the order book and not take liquidity from the order book. If part of the order results in taking liquidity rather than providing liquidity, the order is rejected without any part of it being filled. + If set to true, allows orders to also trigger or fill outside of regular trading hours. This property applies to Equity orders only and isn't supported for market orders. + + false + + + False +
    - - - - - - - - - - - - - - - - -
    - Environment - - Description -
    - Real - - Trade with real money -
    - Demo - - Trade with paper money through the Binance Global brokerage -
    -
  • Click the @@ -29056,66 +29672,13 @@

    Deploy Live Algorithms

    and change the data provider or add additional providers.
  • -
  • - If your brokerage account has existing cash holdings, follow these steps ( - - see video - - ): -
  • -
      -
    1. - In the - - Algorithm Cash State - - section, click - - Show - - . -
    2. -
    3. - Click - - Add Currency - - . -
    4. -
    5. - Enter the currency ticker (for example, USD or CAD) and a quantity. -
    6. -
    -
  • - If your brokerage account has existing position holdings, follow these steps ( - - see video +

    + Webull doesn't provide a live data feed, so use the + + QuantConnect data provider - ): -

  • -
      -
    1. - In the - - Algorithm Holdings State - - section, click - - Show - - . -
    2. -
    3. - Click - - Add Holding - - . -
    4. -
    5. - Enter the symbol ID, symbol, quantity, and average price. -
    6. -
    + or another data provider for the securities you trade. +

  • (Optional) @@ -29157,12 +29720,70 @@

    Deploy Live Algorithms

    +

    Troubleshooting

    + + +

    + The following table describes errors you may see when deploying to Webull: +

    + + + + + + + + + + + + + + + + + +
    + Error Message(s) + + Possible Cause and Fix +
    +
    + Invalid signatureAuthentication failed +
    +
    + Your App Key or App Secret may be incorrect or no longer active. Log in to the + + Webull Developer Portal + + , verify your credentials are enabled for trading, and regenerate them if necessary. +
    +
    + Account not found +
    +
    + The account ID you provided isn't associated with your API credentials. Check the account ID in the + + Webull Developer Portal + + and confirm it matches the account you want to trade. +
    +

    + If you need further support, + + open a new support ticket + + and add the live deployment with the error. +

    + + +

     

    - +

    Brokerages

    -

    ByBit

    +

    Binance

    Introduction

    @@ -29172,24 +29793,12 @@

    Introduction

    QuantConnect enables you to run your algorithms in live mode with real-time market data.

    - Bybit was co-founded by Ben Zhou in March 2018 with the goal to offer a professional platform where Crypto traders can find an ultra-fast matching engine, excellent customer service, and multilingual community support. Bybit provides access to trading Crypto and Crypto Futures for clients outside of - - excluded jurisdictions - - with low minimum deposits to set up an account. For more information about - - Crypto - - and - - fiat deposits - - , see the Bybit documentation. Bybit also provides Crypto staking, initial DEX offerings, and community airdrops. + Binance was founded by Changpeng Zhao in 2017 with the goal to "increase the freedom of money globally". Binance provides access to trading Crypto through spot markets and perpetual Futures. They serve clients with no minimum deposit when depositing Crypto. Binance also provides an NFT marketplace, a mining pool, and services to deposit Crypto coins in liquidity pools to earn rewards.

    - To view the implementation of the Bybit brokerage integration, see the - - Lean.Brokerages.Bybit repository + To view the implementation of the Binance brokerage integration, see the + + Lean.Brokerages.Binance repository .

    @@ -29200,9 +29809,9 @@

    Account Types

    - Bybit supports cash and margin accounts. To set the account type in an algorithm, see the - - Bybit brokerage model documentation + Binance supports cash and margin accounts for spot trades, but only supports margin accounts for Futures trades. Binance US only supports cash accounts. To set the account type in an algorithm, see the + + Binance brokerage model documentation .

    @@ -29210,90 +29819,90 @@

    Create an Account

    - Follow the - - How to Register an Account + Follow the account creation wizard on the + + Binance.com - tutorial on the Bybit website to create a Bybit account. + or + + Binance.us + + website to create a Binance account.

    - You will need API credentials to deploy live algorithms. After you have an account, - + You will need API credentials to deploy live algorithms with your brokerage account. After you open your account, + create API credentials and store them somewhere safe. As you create credentials, make the following configurations:

    • - Under API Key Usage, select - - API Transaction - - . -
    • -
    • - Choose a name for the API key. -
    • -
    • - Under API Key Permissions, select - - Read-Write - - . + Choose system-generated API key type which works using HMAC symmetric encryption.
    • Select the - Only IPs with permissions granted are allowed to access the OpenAPI + Restrict access to trusted IPs only - check box and then enter our IP addresses, 146.59.85.21, 57.128.231.46 and 57.128.233.143. + check box and then enter our IP addresses, 146.59.85.21, 57.128.231.46 and 57.128.233.143. For Binance.us, enter 207.182.16.137.
    • - Select the + If you are going to trade Crypto Futures, select the - Unified Trading + Enable Futures check box.
    • -

      - Unified trading enables read-write permissions for the following queries: -

      -
        -
      • - Order -
      • -
      • - Positions -
      • -
      • - Trade -
      • -

    Paper Trading

    - Our integration doesn't support paper trading through the Bybit Demo Trading environment, but you can follow these steps to simulate it with QuantConnect: + Binance supports paper trading through the Binance Spot Test Network. You don't need a Binance account to create API credentials for the Spot Test Network. +

    +

    + Follow these steps to set up paper trading with the Binance Spot Test Network:

      +
    1. + Log in to the + + Binance Spot Test Network + + with your GitHub credentials. +
    2. In the - - Initialize - - - initialize - - method of your algorithm, set the Bybit brokerage model. + + API Keys + + section, click + + Generate HMAC_SHA256 Key + + .
    3. - - Deploy your algorithm with the QuantConnect Paper Trading brokerage - + Enter a description and then click + + Generate + .
    4. +
    5. + Store your API key and API key secret somewhere safe. +
    +

    + Paper trading Binance Crypto Futures or with Binance US isn't currently available. +

    +

    + Sub-Accounts +

    +

    + Our Binance and Binance US integrations don't support trading with sub-accounts. You must use your main account. +

    @@ -29301,7 +29910,7 @@

    Asset Classes

    - Our Bybit integration supports trading + Our Binance integration supports trading Crypto @@ -29311,6 +29920,13 @@

    Asset Classes

    .

    +

    + Our Binance US integration supports trading + + Crypto + + . +

    @@ -29331,7 +29947,7 @@

    Orders

    - We model the Bybit API by supporting several order types, order properties, and order updates. When you deploy live algorithms, you can + We model the Binance and Binance US APIs by supporting several order types, supporting order properties, and not supporting order updates. When you deploy live algorithms, you can place manual orders @@ -29341,7 +29957,7 @@

    Order Types

    - The following table describes the available order types for each asset class that our Bybit integration supports: + The following table describes the available order types for each asset class that our Binance and Binance US integrations support:

    @@ -29387,11 +30003,10 @@

    - - - - - - @@ -29555,52 +30144,7 @@

    Updates

    - We model the Bybit API by supporting - - order updates - - for Crypto Future assets that have one of the following - - order states - - : -

    -
      -
    • - - OrderStatus.New - - - OrderStatus.NEW - -
    • -
    • - - OrderStatus.PartiallyFilled - - - OrderStatus.PARTIALLY_FILLED - -
    • -
    • - - OrderStatus.Submitted - - - OrderStatus.SUBMITTED - -
    • -
    • - - OrderStatus.UpdateSubmitted - - - OrderStatus.FILLED - -
    • -
    -

    - In cases where you can't update an order, you can cancel the existing order and then create a new order with the desired arguments. For more information about this workaround, see the + We model the Binance and Binance US APIs by not supporting order updates, but you can cancel an existing order and then create a new order with the desired arguments. For more information about this workaround, see the Workaround for Brokerages That Don’t Support Updates @@ -29613,15 +30157,19 @@

    Fees

    - To view the Bybit trading fees, see the - - Trading Fees Schedule + To view the Binance or Binance US trading fees, see the + + Trading Fees - page on the Bybit website. To view how we model their fees, see - + page on the Binance.com website or the + + Fee Structure + + page on the Binance.us website. To view how we model their fees, see + Fees - . + . The Binance Spot Test Network does not charge order fees.

    @@ -29631,14 +30179,18 @@

    Margin

    We model - + buying power and margin calls - to ensure your algorithm stays within the margin requirements. + to ensure your algorithm stays within the margin requirements. If you trade Crypto Perpetual Futures, we model the margin cost and payments of your Crypto Future holdings by directly adjusting your portfolio cash. For more information about Futures margin interest modeling, see the + + Binance Futures Model + + .

    @@ -29647,11 +30199,15 @@

    Slippage

    - Orders through Bybit do not experience slippage in backtests. In Bybit paper trading and live trading, your orders may experience slippage. + Orders through Binance and Binance US do not experience slippage in backtests and + + QuantConnect Paper Trading + + . In live trading, your orders may experience slippage.

    - To view how we model Bybit slippage, see - + To view how we model Binance and Binance US slippage, see + Slippage . @@ -29663,11 +30219,15 @@

    Fills

    - QuantConnect fills market orders immediately and completely in backtests. In Bybit paper trading and live trading, if the quantity of your market orders exceeds the quantity available at the top of the order book, your orders are filled according to what is available in the order book. + QuantConnect fills market orders immediately and completely in backtests and + + QuantConnect Paper Trading + + . In live trading, if the quantity of your market orders exceeds the quantity available at the top of the order book, your orders are filled according to what is available in the order book.

    - To view how we model Bybit order fills, see - + To view how we model Binance and Binance US order fills, see + Fills . @@ -29682,8 +30242,8 @@

    Settlements

    Trades settle immediately after the transaction

    - To view how we model settlement for Bybit trades, see - + To view how we model settlement for Binance and Binance US trades, see + Settlement . @@ -29695,7 +30255,7 @@

    Security and Stability

    - When you deploy live algorithms with Bybit, we don't save your credentials. + When you deploy live algorithms with Binance or Binance US, we don't save your credentials.

    @@ -29715,11 +30275,14 @@

    Demo Algorithm

    - The following algorithm demonstrates the functionality of the Bybit brokerage: + The following algorithm demonstrates the functionality of the Binance and Binance US brokerages:

    +

    + Binance +

    -
    // Demonstrate Bybit brokerage functionality with an EMA crossover strategy.
    -public class BybitDemoAlgorithm : QCAlgorithm
    +   
    // Demonstrate Binance brokerage functionality with an EMA crossover strategy.
    +public class BinanceDemoAlgorithm : QCAlgorithm
     {
         private Symbol _symbol;
         private ExponentialMovingAverage _fast;
    @@ -29730,8 +30293,8 @@ 

    Demo Algorithm

    SetStartDate(2024, 9, 1); SetEndDate(2024, 12, 31); SetCash(100000); - SetBrokerageModel(BrokerageName.Bybit, AccountType.Cash); - _symbol = AddCrypto("BTCUSDT", Resolution.Daily, Market.Bybit).Symbol; + SetBrokerageModel(BrokerageName.Binance, AccountType.Cash); + _symbol = AddCrypto("BTCUSDT", Resolution.Daily, Market.Binance).Symbol; _fast = EMA(_symbol, 10, Resolution.Daily); _slow = EMA(_symbol, 50, Resolution.Daily); } @@ -29745,14 +30308,64 @@

    Demo Algorithm

    Liquidate(); } }
    -
    # Demonstrate Bybit brokerage functionality with an EMA crossover strategy.
    -class BybitDemoAlgorithm(QCAlgorithm):
    +   
    # Demonstrate Binance brokerage functionality with an EMA crossover strategy.
    +class BinanceDemoAlgorithm(QCAlgorithm):
         def initialize(self) -> None:
             self.set_start_date(2024, 9, 1)
             self.set_end_date(2024, 12, 31)
             self.set_cash(100000)
    -        self.set_brokerage_model(BrokerageName.BYBIT, AccountType.CASH)
    -        self._symbol = self.add_crypto("BTCUSDT", Resolution.DAILY, Market.BYBIT).symbol
    +        self.set_brokerage_model(BrokerageName.BINANCE, AccountType.CASH)
    +        self._symbol = self.add_crypto("BTCUSDT", Resolution.DAILY, Market.BINANCE).symbol
    +        self._fast = self.ema(self._symbol, 10, Resolution.DAILY)
    +        self._slow = self.ema(self._symbol, 50, Resolution.DAILY)
    +
    +    def on_data(self, slice: Slice) -> None:
    +        if not self._slow.is_ready:
    +            return
    +        if self._fast.current.value > self._slow.current.value and not self.portfolio.invested:
    +            self.set_holdings(self._symbol, 1)
    +        elif self._fast.current.value < self._slow.current.value and self.portfolio.invested:
    +            self.liquidate()
    +
    +

    + Binance US +

    +
    +
    // Demonstrate Binance US brokerage functionality with an EMA crossover strategy.
    +public class BinanceUsDemoAlgorithm : QCAlgorithm
    +{
    +    private Symbol _symbol;
    +    private ExponentialMovingAverage _fast;
    +    private ExponentialMovingAverage _slow;
    +
    +    public override void Initialize()
    +    {
    +        SetStartDate(2024, 9, 1);
    +        SetEndDate(2024, 12, 31);
    +        SetCash(100000);
    +        SetBrokerageModel(BrokerageName.BinanceUS, AccountType.Cash);
    +        _symbol = AddCrypto("BTCUSD", Resolution.Daily, Market.BinanceUS).Symbol;
    +        _fast = EMA(_symbol, 10, Resolution.Daily);
    +        _slow = EMA(_symbol, 50, Resolution.Daily);
    +    }
    +
    +    public override void OnData(Slice slice)
    +    {
    +        if (!_slow.IsReady) return;
    +        if (_fast > _slow && !Portfolio.Invested)
    +            SetHoldings(_symbol, 1);
    +        else if (_fast < _slow && Portfolio.Invested)
    +            Liquidate();
    +    }
    +}
    +
    # Demonstrate Binance US brokerage functionality with an EMA crossover strategy.
    +class BinanceUsDemoAlgorithm(QCAlgorithm):
    +    def initialize(self) -> None:
    +        self.set_start_date(2024, 9, 1)
    +        self.set_end_date(2024, 12, 31)
    +        self.set_cash(100000)
    +        self.set_brokerage_model(BrokerageName.BINANCE_US, AccountType.CASH)
    +        self._symbol = self.add_crypto("BTCUSD", Resolution.DAILY, Market.BINANCE_US).symbol
             self._fast = self.ema(self._symbol, 10, Resolution.DAILY)
             self._slow = self.ema(self._symbol, 50, Resolution.DAILY)
     
    @@ -29815,7 +30428,7 @@ 

    Deploy Live Algorithms

    field and then click - Bybit Exchange + Binance Exchange from the drop-down menu. @@ -29824,25 +30437,52 @@

    Deploy Live Algorithms

    To generate your API credentials, see - + Account Types . Your account details are not saved on QuantConnect.

  • - Click the + Click on the - VIP Level + Environment - field and then click your level from the drop-down menu. + field and then click one of the environments.
  • - For more information about VIP levels, see - - FAQ — Bybit VIP Program - - on the Bybit website. + The following table shows the supported environments:

    +
    - Stop market + Stop Market - green check green check @@ -29420,11 +30035,12 @@

    Order Properties +

    - We model custom order properties from the Bybit API. The following table describes the members of the + We model custom order properties from the Binance and Binance US APIs. The following table describes the members of the - BybitBrokerageModel + BinanceOrderProperties object that you can set to customize order execution:

    @@ -29517,34 +30133,7 @@

    - A flag to signal that the order must only add liquidity to the order book and not take liquidity from the order book. If part of the order results in taking liquidity rather than providing liquidity, the order is rejected without any part of it being filled. This order property is only available for limit orders. - -
    - - ReduceOnly - - - reduce_only - - - - bool? - - - bool/NoneType - - - A flag to signal that the order must only reduce your current position size. For more information about this order property, see - - Reduce-Only Order - - on the Bybit website. + A flag to signal that the order must only add liquidity to the order book and not take liquidity from the order book. If part of the order results in taking liquidity rather than providing liquidity, the order is rejected without any part of it being filled.
    + + + + + + + + + + + + + + + + +
    + Environment + + Description +
    + Real + + Trade with real money +
    + Demo + + Trade with paper money through the Binance Global brokerage +
    +
  • Click the @@ -29864,6 +30504,36 @@

    Deploy Live Algorithms

    and change the data provider or add additional providers.
  • +
  • + If your brokerage account has existing cash holdings, follow these steps ( + + see video + + ): +
  • +
      +
    1. + In the + + Algorithm Cash State + + section, click + + Show + + . +
    2. +
    3. + Click + + Add Currency + + . +
    4. +
    5. + Enter the currency ticker (for example, USD or CAD) and a quantity. +
    6. +
  • If your brokerage account has existing position holdings, follow these steps ( @@ -29935,74 +30605,12 @@

    Deploy Live Algorithms

    -

    Troubleshooting

    - - -

    - The following table describes errors you may see when deploying to Bybit: -

    - - - - - - - - - - - - - - - - - -
    - Error Message(s) - - Possible Cause and Fix -
    -
    - Invalid API-key, IP, or permissions for action. -
    -
    - The credentials you provided are incorrect. Typically, the API key and API Secret contains - leading and/or trailing white spaces. Copy the API key and API Secret to a text editor to ensure - their are correct. -
    - Your API keys do not have read-write permissions. For more information, see - - Account Types - - . -
    -
    - You haven't enabled Cross Margin Trading yet. -
    -
    - Your account uses isolated margin. To enable cross margin, please head to the PC trading site or the Bybit app. For more information, see - - Isolated Margin/Cross Margin - - . -
    -

    - If you need further support, - - open a new support ticker - - and add the live deployment with the error. -

    - - -

     

    - +

    Brokerages

    -

    Tradier

    +

    ByBit

    Introduction

    @@ -30012,16 +30620,24 @@

    Introduction

    QuantConnect enables you to run your algorithms in live mode with real-time market data.

    - Tradier was founded by Dan Raju, Peter Laptewicz, Jason Barry, Jeyashree Chidambaram, and Steve Agalloco in 2012 with the goal to "deliver a choice of low-cost, high-value brokerage services to traders". Tradier provides access to trading Equities and Options for clients in over 250 countries and territories with - - no minimum deposit for cash accounts + Bybit was co-founded by Ben Zhou in March 2018 with the goal to offer a professional platform where Crypto traders can find an ultra-fast matching engine, excellent customer service, and multilingual community support. Bybit provides access to trading Crypto and Crypto Futures for clients outside of + + excluded jurisdictions - . Tradier also delivers custody, clearing, execution, and billing on behalf of registered advisors. + with low minimum deposits to set up an account. For more information about + + Crypto + + and + + fiat deposits + + , see the Bybit documentation. Bybit also provides Crypto staking, initial DEX offerings, and community airdrops.

    - To view the implementation of the Tradier brokerage integration, see the - - Lean.Brokerages.Tradier repository + To view the implementation of the Bybit brokerage integration, see the + + Lean.Brokerages.Bybit repository .

    @@ -30032,9 +30648,9 @@

    Account Types

    - Tradier supports cash and margin accounts. To set the account type in an algorithm, see the - - Tradier brokerage model documentation + Bybit supports cash and margin accounts. To set the account type in an algorithm, see the + + Bybit brokerage model documentation .

    @@ -30043,57 +30659,122 @@

    Follow the - - account creation wizard + + How to Register an Account - on the Tradier website to create a Tradier account. + tutorial on the Bybit website to create a Bybit account.

    - You will need your account ID and access token to deploy live algorithms. After you have an account, get your account ID and token from the - - Settings > API Access + You will need API credentials to deploy live algorithms. After you have an account, + + create API credentials - page on the Tradier website. Your account ID is the alpha-numeric code in a drop-down field on the page. -

    -

    - Paper Trading -

    -

    - Tradier supports paper trading, but with the following caveats: + and store them somewhere safe. As you create credentials, make the following configurations:

    • - Account activity is unavailable since this information is populated from Tradier's clearing firm. + Under API Key Usage, select + + API Transaction + + .
    • - Streaming Tradier market data is unavailable due to exchange restrictions related to delayed data. + Choose a name for the API key. +
    • +
    • + Under API Key Permissions, select + + Read-Write + + . +
    • +
    • + Select the + + Only IPs with permissions granted are allowed to access the OpenAPI + + check box and then enter our IP addresses, 146.59.85.21, 57.128.231.46 and 57.128.233.143. +
    • +
    • + Select the + + Unified Trading + + check box.
    • +

      + Unified trading enables read-write permissions for the following queries: +

      +
        +
      • + Order +
      • +
      • + Positions +
      • +
      • + Trade +
      • +
    +

    + Paper Trading +

    - To get your paper trading account number and access token, open the - - API Access + Bybit supports paper trading through two environments: + + Bybit Demo Trading - page on the Tradier website and then scroll down to the - - Sandbox Account Access (Paper Trading) - - section. + and the + + Bybit Testnet + + . Each environment uses a separate set of API credentials, so you can't reuse your live account keys.

    - If you trade Equities, you can use the - - QuantConnect data provider - - to get real-time data. - If you trade Options, you must use delayed data from the - - Tradier data provider - - . - If you trade Equities and Options, use both data providers. - However, if you trade with the demo environment, Tradier doesn't offer streaming market data due to exchange restrictions related to delayed data, so you must use our data provider. + Follow these steps to set up paper trading with Bybit Demo Trading: +

    +
      +
    1. + Log in to your + + Bybit account + + . +
    2. +
    3. + From your account menu, switch to + + Demo Trading + + . +
    4. +
    5. + + Create API credentials + + in the Demo Trading environment and store them somewhere safe. +
    6. +
    +

    + Follow these steps to set up paper trading with the Bybit Testnet:

    +
      +
    1. + Log in to the + + Bybit Testnet + + . +
    2. +
    3. + + Create API credentials + + on the Testnet and store them somewhere safe. +
    4. +
    @@ -30101,19 +30782,16 @@

    Asset Classes

    - Our Tradier integration supports trading - - US Equities + Our Bybit integration supports trading + + Crypto and - - Equity Options + + Crypto Futures .

    -

    - You may not be able to trade all assets with Tradier. For example, if you live in the EU, you can't trade US ETFs. Check with your local regulators to know which assets you are allowed to trade. You may need to adjust settings in your brokerage account to live trade some assets. -

    @@ -30121,15 +30799,11 @@

    Data Providers

    - You might need to purchase a - - Tradier market data - - subscription for your trading. For more information about live data providers, see - - Datasets + The + + QuantConnect data provider - . + provides Crypto data during live trading.

    @@ -30138,11 +30812,7 @@

    Orders

    - We model the Tradier API by supporting several order types and the - - TimeInForce - - order property. Tradier partially supports order updates, but does not support trading during extended market hours. When you deploy live algorithms, you can + We model the Bybit API by supporting several order types, order properties, and order updates. When you deploy live algorithms, you can place manual orders @@ -30152,7 +30822,7 @@

    Order Types

    - The following table describes the available order types for each asset class that our Tradier integration supports: + The following table describes the available order types for each asset class that our Bybit integration supports:

    @@ -30160,11 +30830,11 @@

    - - @@ -30233,11 +30903,11 @@

    Order Properties

    - We model the Tradier API. The following table describes the members of the + We model custom order properties from the Bybit API. The following table describes the members of the - TradierOrderProperties + BybitBrokerageModel - object that you can set to customize order execution. + object that you can set to customize order execution:

    Order Type - Equity + + Crypto - Equity Options + + Crypto Futures
    @@ -30316,10 +30986,10 @@

    + + + + + +
    - OutsideRegularTradingHours + PostOnly - outside_regular_trading_hours + post_only @@ -30328,16 +30998,37 @@

    - A flag to signal that the order may be triggered and filled outside of regular trading hours. + A flag to signal that the order must only add liquidity to the order book and not take liquidity from the order book. If part of the order results in taking liquidity rather than providing liquidity, the order is rejected without any part of it being filled. This order property is only available for limit orders. +
    - false + ReduceOnly - False + reduce_only + + + + bool? + + + bool/NoneType + A flag to signal that the order must only reduce your current position size. For more information about this order property, see + + Reduce-Only Order + + on the Bybit website. + +
    @@ -30345,37 +31036,56 @@

    Updates

    - We model the Tradier API by supporting most + We model the Bybit API by supporting order updates - . To update the quantity of an order, cancel the order and then submit a new order with the desired quantity. For more information about this workaround, see the - - Workaround for Brokerages That Don’t Support Updates + for Crypto Future assets that have one of the following + + order states - . -

    -

    - Extended Market Hours -

    -

    - Tradier doesn't support extended market hours trading. If you place an order outside of regular trading hours, the order will be processed at market open. -

    -

    - Automatic Cancellations -

    -

    - If you have open orders for a security when it performs a reverse split, Tradier automatically cancels your orders. + :

    -

    - Errors -

    +
      +
    • + + OrderStatus.New + + + OrderStatus.NEW + +
    • +
    • + + OrderStatus.PartiallyFilled + + + OrderStatus.PARTIALLY_FILLED + +
    • +
    • + + OrderStatus.Submitted + + + OrderStatus.SUBMITTED + +
    • +
    • + + OrderStatus.UpdateSubmitted + + + OrderStatus.FILLED + +
    • +

    - To view the order-related error codes from Tradier, see - - Error Responses + In cases where you can't update an order, you can cancel the existing order and then create a new order with the desired arguments. For more information about this workaround, see the + + Workaround for Brokerages That Don’t Support Updates - in their documentation. + .

    @@ -30384,12 +31094,12 @@

    Fees

    - To view the Tradier trading fees, see the - - Pricing + To view the Bybit trading fees, see the + + Trading Fees Schedule - page on the Tradier website. To view how we model their fees, see - + page on the Bybit website. To view how we model their fees, see + Fees . @@ -30402,22 +31112,14 @@

    Margin

    We model - + buying power and margin calls - to ensure your algorithm stays within the margin requirements. If you have more than $25,000 in your brokerage account, you can use the - - PatternDayTradingMarginModel - - to make use of the 4x intraday leverage and 2x overnight leverage available on most brokerages from the - - PDT rule - - . + to ensure your algorithm stays within the margin requirements.

    @@ -30426,15 +31128,11 @@

    Slippage

    - Orders through Tradier do not experience slippage in backtests and - - QuantConnect Paper Trading - - . In live trading, your orders may experience slippage. + Orders through Bybit do not experience slippage in backtests. In Bybit paper trading and live trading, your orders may experience slippage.

    - To view how we model Tradier slippage, see - + To view how we model Bybit slippage, see + Slippage . @@ -30446,15 +31144,11 @@

    Fills

    - QuantConnect fills market orders immediately and completely in backtests and - - QuantConnect Paper Trading - - . In live trading, if the quantity of your market orders exceeds the quantity available at the top of the order book, your orders are filled according to what is available in the order book. + QuantConnect fills market orders immediately and completely in backtests. In Bybit paper trading and live trading, if the quantity of your market orders exceeds the quantity available at the top of the order book, your orders are filled according to what is available in the order book.

    - To view how we model Tradier order fills, see - + To view how we model Bybit order fills, see + Fills . @@ -30466,11 +31160,11 @@

    Settlements

    - If you trade with a margin account, trades settle immediately + Trades settle immediately after the transaction

    - To view how we model settlement for Tradier trades, see - + To view how we model settlement for Bybit trades, see + Settlement . @@ -30482,23 +31176,7 @@

    Security and Stability

    - Note the following security and stability aspects of our Tradier integration. -

    -

    - Account Credentials -

    -

    - When you deploy live algorithms with Tradier, we don't save your credentials. -

    -

    - API Outages -

    -

    - We call the Tradier API to place live trades. Sometimes the API may be down. Check the - - Tradier status page - - to see if the API is currently working. + When you deploy live algorithms with Bybit, we don't save your credentials.

    @@ -30518,12 +31196,13 @@

    Demo Algorithm

    - The following algorithm demonstrates the functionality of the Tradier brokerage: + The following algorithm demonstrates the functionality of the Bybit brokerage:

    -
    // Demonstrate Tradier brokerage functionality with an EMA crossover strategy on SPY.
    -public class TradierDemoAlgorithm : QCAlgorithm
    +   
    // Demonstrate Bybit brokerage functionality with an EMA crossover strategy.
    +public class BybitDemoAlgorithm : QCAlgorithm
     {
    +    private Symbol _symbol;
         private ExponentialMovingAverage _fast;
         private ExponentialMovingAverage _slow;
     
    @@ -30532,43 +31211,56 @@ 

    Demo Algorithm

    SetStartDate(2024, 9, 1); SetEndDate(2024, 12, 31); SetCash(100000); - SetBrokerageModel(BrokerageName.TradierBrokerage, AccountType.Margin); - var symbol = AddEquity("SPY", Resolution.Daily).Symbol; - _fast = EMA(symbol, 10, Resolution.Daily); - _slow = EMA(symbol, 50, Resolution.Daily); + SetBrokerageModel(BrokerageName.Bybit, AccountType.Cash); + _symbol = AddCrypto("BTCUSDT", Resolution.Daily, Market.Bybit).Symbol; + _fast = EMA(_symbol, 10, Resolution.Daily); + _slow = EMA(_symbol, 50, Resolution.Daily); } public override void OnData(Slice slice) { if (!_slow.IsReady) return; if (_fast > _slow && !Portfolio.Invested) - SetHoldings("SPY", 1); + SetHoldings(_symbol, 1); else if (_fast < _slow && Portfolio.Invested) Liquidate(); } }
    -
    # Demonstrate Tradier brokerage functionality with an EMA crossover strategy on SPY.
    -class TradierDemoAlgorithm(QCAlgorithm):
    +   
    # Demonstrate Bybit brokerage functionality with an EMA crossover strategy.
    +class BybitDemoAlgorithm(QCAlgorithm):
         def initialize(self) -> None:
             self.set_start_date(2024, 9, 1)
             self.set_end_date(2024, 12, 31)
             self.set_cash(100000)
    -        self.set_brokerage_model(BrokerageName.TRADIER_BROKERAGE, AccountType.MARGIN)
    -        symbol = self.add_equity("SPY", Resolution.DAILY).symbol
    -        self._fast = self.ema(symbol, 10, Resolution.DAILY)
    -        self._slow = self.ema(symbol, 50, Resolution.DAILY)
    +        self.set_brokerage_model(BrokerageName.BYBIT, AccountType.CASH)
    +        self._symbol = self.add_crypto("BTCUSDT", Resolution.DAILY, Market.BYBIT).symbol
    +        self._fast = self.ema(self._symbol, 10, Resolution.DAILY)
    +        self._slow = self.ema(self._symbol, 50, Resolution.DAILY)
     
         def on_data(self, slice: Slice) -> None:
             if not self._slow.is_ready:
                 return
             if self._fast.current.value > self._slow.current.value and not self.portfolio.invested:
    -            self.set_holdings("SPY", 1)
    +            self.set_holdings(self._symbol, 1)
             elif self._fast.current.value < self._slow.current.value and self.portfolio.invested:
                 self.liquidate()
    +

    Virtual Pairs

    + + +

    + All fiat and Crypto currencies are individual assets. When you buy a pair like BTCUSD, you trade USD for BTC. In this case, LEAN removes some USD from your portfolio + + cashbook + + and adds some BTC. The virtual pair BTCUSD represents your position in that trade, but the virtual pair doesn't actually exist. It simply represents an open trade. When you deploy a live algorithm, LEAN populates your cashbook with the quantity of each currency, but it can't get your position of each virtual pair. +

    + + +

    Deploy Live Algorithms

    @@ -30604,31 +31296,40 @@

    Deploy Live Algorithms

    field and then click - Tradier + Bybit Exchange from the drop-down menu.
  • - Enter your Tradier account Id and token. + Enter your API key and secret.
  • - To get your account ID and token, see the - - Create an Account - - section in the - + To generate your API credentials, see + Account Types - documentation. Your account details are not saved on QuantConnect. -
    + . Your account details are not saved on QuantConnect.

  • Click the + + VIP Level + + field and then click your level from the drop-down menu. +
  • +

    + For more information about VIP levels, see + + FAQ — Bybit VIP Program + + on the Bybit website. +

    +
  • + Click on the Environment - field and then click one of the environments from the drop-down menu. + field and then click one of the environments.
  • The following table shows the supported environments: @@ -30636,7 +31337,7 @@

    Deploy Live Algorithms

    - + + + + @@ -30684,17 +31400,36 @@

    Deploy Live Algorithms

    and change the data provider or add additional providers. -

    - In most cases, we suggest using the - - QuantConnect data provider - - , the - - Tradier data provider +

  • + If your brokerage account has existing position holdings, follow these steps ( + + see video - , or both. The order you set them in the deployment wizard defines their order of precedence in Lean. In the demo environment, Tradier doesn't offer streaming market data due to exchange restrictions related to delayed data. -

    + ): +
  • +
      +
    1. + In the + + Algorithm Holdings State + + section, click + + Show + + . +
    2. +
    3. + Click + + Add Holding + + . +
    4. +
    5. + Enter the symbol ID, symbol, quantity, and average price. +
    6. +
  • (Optional) @@ -30736,12 +31471,74 @@

    Deploy Live Algorithms

    +

    Troubleshooting

    + + +

    + The following table describes errors you may see when deploying to Bybit: +

    +
  • + Environment @@ -30647,7 +31348,7 @@

    Deploy Live Algorithms

    - Real + Live Trade with real money @@ -30658,7 +31359,22 @@

    Deploy Live Algorithms

    Demo
    - Trade with paper money + Trade with paper money through the + + Bybit Demo Trading + + environment +
    + Testnet + + Trade with paper money through the + + Bybit Testnet +
    + + + + + + + + + + + + + + + + +
    + Error Message(s) + + Possible Cause and Fix +
    +
    + Invalid API-key, IP, or permissions for action. +
    +
    + The credentials you provided are incorrect. Typically, the API key and API Secret contains + leading and/or trailing white spaces. Copy the API key and API Secret to a text editor to ensure + their are correct. +
    + Your API keys do not have read-write permissions. For more information, see + + Account Types + + . +
    +
    + You haven't enabled Cross Margin Trading yet. +
    +
    + Your account uses isolated margin. To enable cross margin, please head to the PC trading site or the Bybit app. For more information, see + + Isolated Margin/Cross Margin + + . +
    +

    + If you need further support, + + open a new support ticker + + and add the live deployment with the error. +

    + + +

     

    - +

    Brokerages

    -

    Kraken

    +

    Tradier

    Introduction

    @@ -30751,23 +31548,16 @@

    Introduction

    QuantConnect enables you to run your algorithms in live mode with real-time market data.

    - - Kraken - - was founded by Jesse Powell in 2011 with the goal to "accelerate the adoption of cryptocurrency so that you and the rest of the world can achieve financial freedom and inclusion". Kraken provides access to trading Crypto through spot and Futures markets for clients with a minimum deposit of around $0-$150 USD for - - currency - - and - - Crypto deposits + Tradier was founded by Dan Raju, Peter Laptewicz, Jason Barry, Jeyashree Chidambaram, and Steve Agalloco in 2012 with the goal to "deliver a choice of low-cost, high-value brokerage services to traders". Tradier provides access to trading Equities and Options for clients in over 250 countries and territories with + + no minimum deposit for cash accounts - . Kraken also provides staking services, educational content, and a developer grant program. + . Tradier also delivers custody, clearing, execution, and billing on behalf of registered advisors.

    - To view the implementation of the Kraken brokerage integration, see the - - Lean.Brokerages.Kraken repository + To view the implementation of the Tradier brokerage integration, see the + + Lean.Brokerages.Tradier repository .

    @@ -30778,9 +31568,9 @@

    Account Types

    - Kraken supports cash and margin accounts. To set the account type in an algorithm, see the - - Kraken brokerage model documentation + Tradier supports cash and margin accounts. To set the account type in an algorithm, see the + + Tradier brokerage model documentation .

    @@ -30789,55 +31579,56 @@

    Follow the - + account creation wizard - on the Kraken website to create a Kraken account. + on the Tradier website to create a Tradier account.

    - You will need API credentials to deploy live algorithms with your brokerage account. After you open your account, - - create API credentials + You will need your account ID and access token to deploy live algorithms. After you have an account, get your account ID and token from the + + Settings > API Access - and store them somewhere safe. + page on the Tradier website. Your account ID is the alpha-numeric code in a drop-down field on the page.

    Paper Trading

    - The Kraken brokerage doesn't support paper trading, but you can follow these steps to simulate it with QuantConnect: + Tradier supports paper trading, but with the following caveats:

    -
      +
    -

    - Rewards Program -

    +

    - QuantConnect doesn't account for assets you enable in Kraken's - - rewards program + To get your paper trading account number and access token, open the + + API Access - . + page on the Tradier website and then scroll down to the + + Sandbox Account Access (Paper Trading) + + section. +

    +

    + If you trade Equities, you can use the + + QuantConnect data provider + + to get real-time data. + If you trade Options, you must use delayed data from the + + Tradier data provider + + . + If you trade Equities and Options, use both data providers. + However, if you trade with the demo environment, Tradier doesn't offer streaming market data due to exchange restrictions related to delayed data, so you must use our data provider.

    @@ -30846,12 +31637,19 @@

    Asset Classes

    - Our Kraken integration supports trading - - Crypto + Our Tradier integration supports trading + + US Equities + + and + + Equity Options .

    +

    + You may not be able to trade all assets with Tradier. For example, if you live in the EU, you can't trade US ETFs. Check with your local regulators to know which assets you are allowed to trade. You may need to adjust settings in your brokerage account to live trade some assets. +

    @@ -30859,11 +31657,15 @@

    Data Providers

    - The - - QuantConnect data provider + You might need to purchase a + + Tradier market data - provides Crypto data during live trading. + subscription for your trading. For more information about live data providers, see + + Datasets + + .

    @@ -30872,7 +31674,11 @@

    Orders

    - We model the Kraken API by supporting several order types, supporting order properties, and not supporting order updates. When you deploy live algorithms, you can + We model the Tradier API by supporting several order types and the + + TimeInForce + + order property. Tradier partially supports order updates, but does not support trading during extended market hours. When you deploy live algorithms, you can place manual orders @@ -30882,7 +31688,7 @@

    Order Types

    - The following table describes the available order types for each asset class that our Kraken integration supports: + The following table describes the available order types for each asset class that our Tradier integration supports:

    @@ -30890,8 +31696,11 @@

    - + @@ -30905,6 +31714,9 @@

    + - - - @@ -30935,6 +31740,9 @@

    + - + +
    Order Type - Crypto + + Equity + + Equity Options
    green check + green check +
    @@ -30915,13 +31727,6 @@

    green check
    - - Limit if touched - - green check green check + green check +
    @@ -30945,7 +31753,10 @@

    green check
    + green check +
    -

    Set Up SAPI

    +

    Security and Stability

    - The following few sections explain how to download the Bloomberg™ Server API (SAPI), install it on a cloud server, and add firewall rules so it can connect to QuantConnect Cloud. + Note the following security and stability aspects of our dYdX integration.

    - Download SAPI + Account Credentials

    - Follow these steps to download the SAPI: + When you deploy live algorithms with dYdX, we don't save your credentials.

    -
      -
    1. - - Install the Bloomberg™ Terminal - - . -
    2. -
    3. - - Create a Bloomberg™ Terminal account - - . -
    4. -
    5. - In the Bloomberg™ Terminal, run - - WAPI<GO> - - . -
    6. -
    7. - On the API Developer's Help Site, click - - EMSX API - - . -
    8. - -
    9. - On the EMSX API page, under the - - Server API Process - - section, click - - Link - - . -
    10. - -
    11. - On the Server API Software Install page, click the correct - - download - - icons. -
    12. - -
    13. - Click - - System Requirements - - . -
    14. - -

    - Install the SAPI + API Outages

    - Follow these steps to install the SAPI: + We call the dYdX API to place live trades. Sometimes the API may be down. Check the + + dYdX status page + + to see if the API is currently working.

    -
      -
    1. - Spin up an E12x9 AWS instance or higher that your organization controls. -
    2. -
    3. - Run the SAPI installer on the cloud server. -
    4. -

      - For more information about this step, see - - How to install serverapi.exe - - in the EMSX API Programmers Guide. At the end of the installion, you get a registration key. -

      -
    5. - Ask Bloomberg™ Support to activate your registration key. -
    6. -
    7. - Start the serverapi program. -
    8. -

      - On Windows, the default location is - - C: \ BLP \ ServerApi \ bin \ serverapi.exe - - . -

      -
    -

    - Set Up Your Account -

    + + + +

    Deposits and Withdrawals

    + +

    - Follow these steps to set up your SAPI account: + You can deposit and withdraw cash while you run an algorithm that's connected to a dYdX account. + We sync the algorithm's cash holdings with the cash holdings in dYdX every day at 7:45 AM Eastern Time (ET).

    -
      -
    1. - - Contact Bloomberg Support - - and ask them to enable the Server Side EMSX API. -
    2. -
    3. - Ask Bloomberg Support for your unique user identifier (UUID). -
    4. -

      - Save it somewhere safe. You will need it when you deploy live algorithms. -

      -
    5. - Contact the EMSX brokerage you plan to use and give them your UUID. -
    6. -
    -

    - Add Firewall Rules -

    + + + +

    Demo Algorithm

    + +

    - Follow these steps to configure the firewall rules on the AWS instance so that the SAPI can connect to QuantConnect Cloud: + The following algorithm demonstrates the functionality of the dYdX integration:

    -
      -
    1. - Click - - Start - - . -
    2. -
    3. - Enter - - Windows Defender Firewall with Advanced Security - - and then press - - Enter - - . -
    4. -
    5. - In the left panel, click - - Inbound Rules - - . -
    6. -
    7. - In the right panel, click - - New Rule... - - . -
    8. -
    9. - Follow the prompts to create a program rule for the serverapi. -
    10. -
    11. - In the Windows Defender Firewall with Advanced Security window, double-click the serverapi row. -
    12. -
    13. - In the serverapi window, click the - - Scope - - tab. -
    14. -
    15. - In the - - Remote IP address - - section, add the QuantConnect Cloud IP address, 207.182.16.137. -
    16. -
    17. - Click - - OK - - . -
    18. -
    19. - Add the QuantConnect Cloud IP address to the other row in the table that has the serverapi name. -
    20. -
    +
    +
    // Demonstrate dYdX brokerage functionality with an EMA crossover strategy.
    +public class DydxDemoAlgorithm : QCAlgorithm
    +{
    +    private Symbol _symbol;
    +    private ExponentialMovingAverage _fast;
    +    private ExponentialMovingAverage _slow;
    +
    +    public override void Initialize()
    +    {
    +        SetStartDate(2024, 9, 1);
    +        SetEndDate(2024, 12, 31);
    +        SetCash(100000);
    +        SetBrokerageModel(BrokerageName.DYDX, AccountType.Margin);
    +        _symbol = AddCryptoFuture("BTCUSD", Resolution.Daily, Market.DYDX).Symbol;
    +        _fast = EMA(_symbol, 10, Resolution.Daily);
    +        _slow = EMA(_symbol, 50, Resolution.Daily);
    +    }
    +
    +    public override void OnData(Slice slice)
    +    {
    +        if (!_slow.IsReady) return;
    +        if (_fast > _slow && !Portfolio.Invested)
    +            SetHoldings(_symbol, 1);
    +        else if (_fast < _slow && Portfolio.Invested)
    +            Liquidate();
    +    }
    +}
    +
    # Demonstrate dYdX brokerage functionality with an EMA crossover strategy.
    +class DydxDemoAlgorithm(QCAlgorithm):
    +    def initialize(self) -> None:
    +        self.set_start_date(2024, 9, 1)
    +        self.set_end_date(2024, 12, 31)
    +        self.set_cash(100000)
    +        self.set_brokerage_model(BrokerageName.DYDX, AccountType.MARGIN)
    +        self._symbol = self.add_crypto_future("BTCUSD", Resolution.DAILY, Market.DYDX).symbol
    +        self._fast = self.ema(self._symbol, 10, Resolution.DAILY)
    +        self._slow = self.ema(self._symbol, 50, Resolution.DAILY)
    +
    +    def on_data(self, slice: Slice) -> None:
    +        if not self._slow.is_ready:
    +            return
    +        if self._fast.current.value > self._slow.current.value and not self.portfolio.invested:
    +            self.set_holdings(self._symbol, 1)
    +        elif self._fast.current.value < self._slow.current.value and self.portfolio.invested:
    +            self.liquidate()
    +
    @@ -34751,11 +35148,7 @@

    Deploy Live Algorithms

    - You need to - - set up the Bloomberg SAPI - - before you can deploy cloud algorithms with Terminal Link. + dYdX is not a brokerage, it is a decentralised, disintermediated and permissionless protocol. However many parts of the QuantConnect platform include dYdX in a brokerage list for simple user-experience.

    You must have an available @@ -34789,97 +35182,35 @@

    Deploy Live Algorithms

    field and then click - Terminal Link + dYdX Exchange from the drop-down menu. +
  • + Enter API your private key, wallet address, and sub-account number +
  • +

    + To generate your API credentials, see the + + Create an Account + + section in the + + Account Types + + documentation. Your account details are not saved on QuantConnect. +

  • Click the - Connection Type - - field and then click - - SAPI + Node - from the drop-down menu. + field and then click the live trading node that you want to use from the drop-down menu.
  • - In the - - Server Auth Id - - field, enter your unique user identifier (UUID). -
  • -

    - The UUID is a unique integer identifier that's assigned to each Bloomberg Anywhere user. If you don't know your UUID, contact Bloomberg. -

    -
  • - In the - - EMSX Broker - - field, enter the EMSX broker to use. -
  • -
  • - In the - - Server Port - - field, enter the port where SAPI is listening. -
  • -

    - The default port is 8194. -

    -
  • - In the - - Server Host - - field, enter the public IP address of the SAPI AWS server. -
  • -
  • - In the - - EMSX Account - - field, enter the account to which LEAN should route orders. -
  • -
  • - In the - - EMSX Team - - field, enter the team account to receive events of your team's orders. -
  • -

    - The default value is empty, which means LEAN disregards these notifications. -

    -
  • - In the - - OpenFIGI Api Key - - field, enter your API key. -
  • -
  • - Click the - - Environment - - field and then click one of the options from the drop-down menu. -
  • -
  • - Click the - - Node - - field and then click the live trading node that you want to use from the drop-down menu. -
  • -
  • - - (Optional) - + + (Optional) + In the Data Provider @@ -34890,66 +35221,6 @@

    Deploy Live Algorithms

    and change the data provider or add additional providers.
  • -
  • - If your brokerage account has existing cash holdings, follow these steps ( - - see video - - ): -
  • -
      -
    1. - In the - - Algorithm Cash State - - section, click - - Show - - . -
    2. -
    3. - Click - - Add Currency - - . -
    4. -
    5. - Enter the currency ticker (for example, USD or CAD) and a quantity. -
    6. -
    -
  • - If your brokerage account has existing position holdings, follow these steps ( - - see video - - ): -
  • -
      -
    1. - In the - - Algorithm Holdings State - - section, click - - Show - - . -
    2. -
    3. - Click - - Add Holding - - . -
    4. -
    5. - Enter the symbol ID, symbol, quantity, and average price. -
    6. -
  • (Optional) @@ -34992,11 +35263,11 @@

    Deploy Live Algorithms

     

    - +

    Brokerages

    -

    SSC Eze

    +

    Bloomberg EMSX

    Introduction

    @@ -35005,11 +35276,16 @@

    Introduction

    QuantConnect enables you to run your algorithms in live mode with real-time market data.

    + Terminal link icon

    - - SS&C Eze + QuantConnect can integrate with the Bloomberg™ Server API (SAPI) or Desktop API (DAPI) in different cloud environments. This integration allows research, backtesting, opitimization, and live trading through the Bloomberg APIs. Terminal Link is in no way affiliated with or endorsed by Bloomberg™; it is simply an add-on. Add Terminal link to your organization to access the 1,300+ prime brokerages in the Bloomberg Execution Management System network. +

    +

    + QuantConnect Cloud only supports routing trades to the Bloomberg™ Server API. In this environment, you can route orders to any of the prime brokerages that Bloomberg supports and you get to leverage the data, server management, and data management from QuantConnect, giving you the best of both worlds. To use Terminal Link, you need to be a member of an organization on the Trading Firm or Institution + + tier - , former Eze Software, was founded by Sean McLaughlin in 1995. SS&C Eze provides a multi-asset and multi-broker execution management system (EMS) that provides fast, seamless, and centralized access to extensive liquidity across global equities, bonds, futures, options, and digital assets. + .

    @@ -35018,46 +35294,8 @@

    Account Types

    - SS&C Eze supports order routing via their EMS (execution management system). It's a margin account, where you set the buying power in the wizard when you're deploying to a professional prime brokerage account. -

    -

    - Create an Account -

    -

    - Contact SS&C Eze's - - sales team - - to create an account. -

    -

    - Paper Trading -

    -

    - The SS&C Eze doesn't support paper trading, but you can follow these steps to simulate it with QuantConnect: + Terminal Link supports order routing via the Bloomberg™ EMSX network. It's a margin account that's similiar to a FIX API, where you set the buying power in the wizard when you're deploying to a professional prime brokerage account.

    -
      -
    1. - In the - - Initialize - - - initialize - - method of your algorithm, - - set the SS&C Eze brokerage model and your account type - - . -
    2. -
    3. - - Deploy your algorithm with the QuantConnect Paper Trading brokerage - - . -
    4. -
    @@ -35065,16 +35303,12 @@

    Asset Classes

    - Our - - SS&C Eze - - integration supports the following asset classes: + Terminal Link supports trading the following asset classes:

    -

    - You may not be able to trade all assets with SS&C Eze. For example, if you live in the EU, you can't trade US ETFs. Check with your local regulators to know which assets you are allowed to trade. You may need to adjust settings in your brokerage account to live trade some assets. -

    @@ -35113,15 +35329,15 @@

    Data Providers

    - You might need to purchase a - - SS&C Eze market data + The Bloomberg™ Server API (SAPI) does not provide data, you must use + + QuantConnect data provider - subscription for your trading. For more information about live data providers, see - - Datasets + , a third-party data provider such as a + + Polygon - . + , or a broker that provides data.

    @@ -35130,47 +35346,29 @@

    Orders

    - We model the Eze API by supporting several order types, the - - TimeInForce - - order property, and order updates. When you deploy live algorithms, you can - - place manual orders - - through the IDE. + Terminal Link enables you to create and manage Bloomberg™ orders.

    Order Types

    - The following table describes the available order types for each asset class that our - - SS&C Eze - - integration supports: + The following table describes the available order types for each asset class that Terminal Link supports:

    - - - - - - @@ -35189,39 +35387,25 @@

    - - - - - - - - - - - - - - - - - - @@ -35310,11 +35462,11 @@

    Order Properties

    - We model the SS&C Eze API. The following table describes the members of the + We model custom order properties from the Bloomberg EMSX API. The following table describes the members of the - EzeOrderProperties + TerminalLinkOrderProperties - object that you can set to customize order execution. + object that you can set to customize order execution:

    + Order Type + Equity + Equity Options + Futures - Future Options - - Index Options -
    green check - green check - - green check -
    - - Limit + + Market on open green check - green check - - green check - - green check - green check
    - - Stop market + + Limit @@ -35233,17 +35417,11 @@

    green check - green check - - green check -
    - - Stop limit + + Stop market @@ -35255,47 +35433,21 @@

    green check - green check - - green check -
    - - Market on Open + + Stop limit green check - - - -
    - - Market on Close - - green check - - - + green check
    @@ -35393,10 +35545,10 @@

    @@ -35416,10 +35568,10 @@

    @@ -35443,10 +35591,10 @@

    @@ -35466,10 +35618,10 @@

    - -
    - Account + Notes - account + notes @@ -35408,7 +35560,7 @@

    - Sets a semi-colon separated list of trade or neutral accounts the user has permission for, e.g., "TAL;TEST;USER1;TRADE" or "TAL;TEST;USER2;NEUTRAL". + The free form instructions that may be sent to the broker.
    - AccountType + HandlingInstruction - account_type + handling_instruction @@ -35431,11 +35583,7 @@

    - Sets the account type for the order. E.g., - - "119" - - for margin orders in Eze EMS. + The instructions for handling the order or route. The values can be preconfigured or a value customized by the broker.
    - Notes + CustomNotes1 - notes + custom_notes_1 @@ -35458,7 +35606,11 @@

    - Sets the user message or notes. + Custom user order notes 1. For more information about custom order notes, see + + Custom Notes & Free Text Fields + + in the EMSX API documentation
    - Route + CustomNotes2 - route + custom_notes_2 @@ -35481,650 +35633,408 @@

    - Sets the route name as shown in SS&C Eze EMS. + Custom user order notes 2.
    -

    - Updates -

    -

    - We model the SS&C Eze API by supporting - - order updates - - . -

    -

    - Handling Splits -

    -

    - If you're using raw - - data normalization - - and you have active orders with a limit, stop, or trigger price in the market for a US Equity when a - - stock split - - occurs, the following properties of your orders automatically adjust to reflect the stock split: -

    -
      -
    • - Quantity -
    • -
    • - Limit price -
    • -
    • - Stop price -
    • -
    • - Trigger price -
    • -
    - - - -

    Fees

    - - -

    - Orders filled with SS&C Eze are subject to the fees of the SS&C Eze Execution Management System and your prime brokerage destination. To view how we model their fees, see - - Fees - - . -

    - - - -

    Margin

    - - -

    - We model - - buying power - - and - - margin calls - - to ensure your algorithm stays within the margin requirements. If you have more than $25,000 in your brokerage account, you can use the - - PatternDayTradingMarginModel - - to make use of the 4x intraday leverage and 2x overnight leverage available on most brokerages from the - - PDT rule - - . -

    - - - -

    Slippage

    - - -

    - Orders through SS&C Eze do not experience slippage in backtests and - - QuantConnect Paper Trading - - . In live trading, your orders may experience slippage. -

    -

    - To view how we model SS&C Eze slippage, see - - Slippage - - . -

    - - - -

    Fills

    - - -

    - QuantConnect fills market orders immediately and completely in backtests and - - QuantConnect Paper Trading - - . In live trading, if the quantity of your market orders exceeds the quantity available at the top of the order book, your orders are filled according to what is available in the order book. -

    -

    - To view how we model Eze order fills, see - - Fills - - . -

    - - - -

    Settlements

    - - -

    - If you trade with a margin account, trades settle immediately -

    -

    - To view how we model settlement for SS&C Eze trades, see - - Settlement - - . -

    - - - -

    Security and Stability

    - - -

    - When you deploy live algorithms with SS&C Eze, we don't save your credentials. -

    - - - -

    Deposits and Withdrawals

    - - -

    - You can deposit and withdraw cash from your brokerage account while you run an algorithm that's connected to the - account. We sync the algorithm's cash holdings with the cash holdings in your brokerage account every day at 7:45 AM - Eastern Time (ET). -

    - - - -

    Demo Algorithm

    - - -

    - The following algorithm demonstrates the functionality of the SS&C Eze brokerage: -

    -
    -
    // Demonstrate SS&C Eze brokerage functionality with an EMA crossover strategy on SPY.
    -public class SSCEzeDemoAlgorithm : QCAlgorithm
    -{
    -    private ExponentialMovingAverage _fast;
    -    private ExponentialMovingAverage _slow;
    -
    -    public override void Initialize()
    -    {
    -        SetStartDate(2024, 9, 1);
    -        SetEndDate(2024, 12, 31);
    -        SetCash(100000);
    -        SetBrokerageModel(BrokerageName.Eze, AccountType.Margin);
    -        var symbol = AddEquity("SPY", Resolution.Daily).Symbol;
    -        _fast = EMA(symbol, 10, Resolution.Daily);
    -        _slow = EMA(symbol, 50, Resolution.Daily);
    -    }
    -
    -    public override void OnData(Slice slice)
    -    {
    -        if (!_slow.IsReady) return;
    -        if (_fast > _slow && !Portfolio.Invested)
    -            SetHoldings("SPY", 1);
    -        else if (_fast < _slow && Portfolio.Invested)
    -            Liquidate();
    -    }
    -}
    -
    # Demonstrate SS&C Eze brokerage functionality with an EMA crossover strategy on SPY.
    -class SSCEzeDemoAlgorithm(QCAlgorithm):
    -    def initialize(self) -> None:
    -        self.set_start_date(2024, 9, 1)
    -        self.set_end_date(2024, 12, 31)
    -        self.set_cash(100000)
    -        self.set_brokerage_model(BrokerageName.EZE, AccountType.MARGIN)
    -        symbol = self.add_equity("SPY", Resolution.DAILY).symbol
    -        self._fast = self.ema(symbol, 10, Resolution.DAILY)
    -        self._slow = self.ema(symbol, 50, Resolution.DAILY)
    -
    -    def on_data(self, slice: Slice) -> None:
    -        if not self._slow.is_ready:
    -            return
    -        if self._fast.current.value > self._slow.current.value and not self.portfolio.invested:
    -            self.set_holdings("SPY", 1)
    -        elif self._fast.current.value < self._slow.current.value and self.portfolio.invested:
    -            self.liquidate()
    -
    - - - -

    Deploy Live Algorithms

    - - -

    - You must have an available - - live trading node - - for each live trading algorithm you deploy. -

    -

    - Follow these steps to deploy a live algorithm: -

    -
      -
    1. - - Open the project - - you want to deploy. -
    2. -
    3. - Click the - Lightning icon - - Deploy Live - - icon. -
    4. -
    5. - On the Deploy Live page, click the - - Brokerage - - field and then click - - SS&C Eze - - from the drop-down menu. -
    6. -
    7. - Enter your - - SS&C Eze - - username and password. -
    8. -
    9. - Enter the Trading Account in BBCD (BANK;BRANCH;CUSTOMER;DEPOSIT) format, the Domain, and the select the Locale. -
    10. -

      - Your account details are not saved on QuantConnect. -

      -
    11. - Click the - - Node - - field and then click the live trading node that you want to use from the drop-down menu. -
    12. -
    13. - - (Optional) - - In the - - Data Provider - - section, click - - Show - - and change the data provider or add additional providers. -
    14. -

      - In most cases, we suggest using the - - QuantConnect data provider - - , a third-party data provider such as - - Polygon data provider - - , or both. The order you set them in the deployment wizard defines their order of precedence in Lean. -

      -
    15. - If your brokerage account has existing cash holdings, follow these steps ( - - see video - - ): -
    16. -
        -
      1. - In the - - Algorithm Cash State - - section, click - - Show - - . -
      2. -
      3. - Click - - Add Currency - - . -
      4. -
      5. - Enter the currency ticker (for example, USD or CAD) and a quantity. -
      6. -
      -
    17. - - (Optional) - - - Set up notifications - - . -
    18. -
    19. - Configure the - - Automatically restart algorithm - - setting. -
    20. -

      - By enabling - - automatic restarts - - , the algorithm will use best efforts to restart the algorithm if it fails due to a runtime error. This can help improve the algorithm's resilience to temporary outages such as a brokerage API disconnection. -

      -
    21. - Click - - Deploy - - . -
    22. -
    -

    - The deployment process can take up to 5 minutes. When the algorithm deploys, the - - live results page - - displays. If you know your brokerage positions before you deployed, you can verify they have been loaded properly by checking your equity value in the runtime statistics, your cashbook holdings, and your position holdings. -

    - - - -

     

    - -
    -
    -

    Brokerages

    -

    Trading Technologies

    -
    -
    -

    Introduction

    - - -

    - QuantConnect enables you to run your algorithms in live mode with real-time market data. -

    -

    - Trading Technologies (TT) was founded by Gary Kemp in 1994 with the goal to create professional trading software, infrastructure, and data solutions for a wide variety of users. TT provides access to trading Futures, Options, and Crypto. TT also provides a charting platform, infrastructure services, and risk management tools. TT is not actually a brokerage. The firm is a brokerage router with access to more than 30 execution destinations. -

    -

    - To view the implementation of the TT integration, see the - - Lean.Brokerages.TradingTechnologies repository - - . -

    - - - -

    Account Types

    - - -

    - The - - TradingTechnologiesBrokerageModel - - does not have specific modeling for fees and slippage because TT is an order router and can execute on many exchanges and brokerages. To set the brokerage model and account type in an algorithm, see the - - TT brokerage model documentation - - . In live trading, TT reports the total fees of your orders after each order fill. Pass a different - - BrokerageName - - to - - SetBrokerageModel - - - set_brokerage_model - - to backtest your algorithm with fee and slippage modeling. The brokerage model you set should support the asset classes and orders in your algorithm. -

    -

    - Create an Account -

    -

    - Follow the - - account creation wizard - - on the TT website to create a TT account. -

    -

    - Paper Trading -

    -

    - Trading Technologies provides a separate - - - User Acceptance Testing (UAT) Certification environment at - - uat.trade.tt - - . The TT UAT environment connects to actual exchange certification environments for both market data and order routing. -

    -

    - To create a UAT account, follow the - - UAT account creation wizard - - on the TT website. -

    -

    - Create Deployment Credentials -

    -

    - After you create your TT account, see - - Create TT Users - - to create your live deployment credentials. -

    - - - -

    Asset Classes

    - - -

    - Our TT integration supports trading - - Futures - - . -

    - - - -

    Data Providers

    - - -

    - The - - QuantConnect data provider - - provides Futures data during live trading. If you want to use Trading Technologies data, you might need to purchase a - - Trading Technologies market data - - subscription for your trading. For more information about live data providers, see - - Datasets - - . -

    - - - -

    Orders

    - - -

    - We model the TT API by supporting several order types, the - - TimeInForce - - order property, and order updates. When you deploy live algorithms, you can - - place manual orders - - through the IDE. -

    -

    - Order Types -

    -

    - The following table describes the available order types for each asset class that our TT integration supports: -

    - - - - + + + + - - + + + + + + + + - -
    - Order Type - - Futures - + + CustomNotes3 + + + custom_notes_3 + + + + string + + + str + + + Custom user order notes 3. + +
    - - Market - + + CustomNotes4 + + + custom_notes_4 + + + + string + + + str + + + Custom user order notes 4. - green check
    - - Limit - + + CustomNotes5 + + + custom_notes_5 + + + + string + + + str + + + Custom user order notes 5. - green check
    - - Stop market - + + Account + + + account + + + + string + + + str + + + The EMSX account. - green check
    - - Stop limit - + + Broker + + + broker + + + + string + + + str + + + The EMSX broker code. - green check
    - -

    - TT enforces the following order rules: -

    -
      -
    • - If you are buying (selling) with a - - StopMarketOrder - - - stop_market_order - - or a - - StopLimitOrder - - - stop_limit_order - - , the stop price of the order must be greater (less) than the current security price. -
    • -
    • - If you are buying (selling) with a - - StopLimitOrder - - - stop_limit_order - - , the limit price of the order must be greater (less) than the stop price. -
    • -
    + + + + LocateBroker + + + locate_broker + + + + + string + + + str + + + + The EMSX locate broker code that identifies the counterparty the shares are borrowed from for a short equity sale (for example, + + "BMTB" + + ). + Maps to the + + LocBrkr + + field on the EMSX trading ticket. + Setting this property (or + + LocateId + + + locate_id + + ) on a short equity sale causes the brokerage to emit + + EMSX_LOCATE_REQ = "Y" + + alongside. + + + + + + + + LocateId + + + locate_id + + + + + string + + + str + + + + The EMSX locate confirmation/ticket Id that the lending broker returns. Maps to the + + LocId + + field on the EMSX trading ticket. + + + + + + + + Strategy + + + strategy + + + + + StrategyParameters + + + + An object that represents the EMSX order strategy details. You must append strategy parameters in the order that the EMSX API expects. + The following strategy names are supported: "DMA", "DESK", "VWAP", "TWAP", "FLOAT", "HIDDEN", "VOLUMEINLINE", "CUSTOM", "TAP", "CUSTOM2", "WORKSTRIKE", "TAPNOW", "TIMED", "LIMITTICK", "STRIKE" + + + + + + ExecutionInstruction + + + execution_instruction + + + + + string + + + str + + + + The execution instruction field. + + + + + + + + AutomaticPositionSides + + + automatic_position_sides + + + + + bool + + + + A flag that determines whether to automatically include the position side in the order direction (buy-to-open, sell-to-close, etc.) instead of the default (buy, sell). + + + + + + + + PositionSide + + + position_side + + + + + OrderPosition? + + + OrderPosition/NoneType + + + + An + + OrderPosition + + object that specifies the position side in the order direction (buy-to-open, sell-to-close, etc.) instead of the default (buy, sell). + This member has precedence over + + AutomaticPositionSides + + + automatic_position_sides + + . + + + + + + + + Exchange + + + exchange + + + + + Exchange + + + + Defines the exchange name for sending the order to. + + + + + + +

    + For more information about the format that the Bloomberg EMSX API expects, see + + Create Order and Route Extended Request + + in the EMSX API documentation and the + + createOrderAndRouteWithStrat documentation + + on the MathWorks website. +

    - Time In Force + Get Open Orders

    - We model the TT API by supporting the - - Day - - - DAY - - and - - GoodTilCanceled - - - GOOD_TIL_CANCELED - - - TimeInForce + Terminal Link lets you + + access open orders - order properties. + . +

    +

    + Monitor Fills +

    +

    + Terminal Link allows you to monitor orders as they fill through + + order events + + .

    Updates

    - We model the TT API by supporting + Terminal Link doesn't support order updates + , but you can cancel an existing order and then create a new order with the desired arguments. For more information about this workaround, see the + + Workaround for Brokerages That Don’t Support Updates + + . +

    +

    + Cancellations +

    +

    + Terminal Link enables you to + + cancel open orders + + . +

    +

    + Handling Splits +

    +

    + If you're using raw + + data normalization + + and you have active orders with a limit, stop, or trigger price in the market for a US Equity when a + + stock split + + occurs, the following properties of your orders automatically adjust to reflect the stock split: +

    +
      +
    • + Quantity +
    • +
    • + Limit price +
    • +
    • + Stop price +
    • +
    • + Trigger price +
    • +
    +

    + Brokerage-Side Orders +

    +

    + By default, your algorithm doesn't record orders that you submit to your account by third-parties instead of through LEAN. + To accept these orders, create a + + custom brokerage message handler + .

    @@ -36134,12 +36044,8 @@

    Fees

    - To view the TT trading fees, see the - - Pricing - - page on the TT website. To view how we model their fees, see - + Orders filled with Terminal Link are subject to the fees of the Bloomberg™ Execution Management System and your prime brokerage destination. To view how we model their fees, see + Fees . @@ -36151,8 +36057,8 @@

    Margin

    - We model - + Set your cash and holdings state in the wizard when you deploy to the Bloomberg™ EMSX environment. We use these states to model + buying power and @@ -36164,729 +36070,523 @@

    Margin

    -

    Slippage

    +

    Fills

    - Orders through TT do not experience slippage in backtests and - - QuantConnect Paper Trading - - . In live trading, your orders may experience slippage. + In live trading, LEAN routes your orders to the exchange or prime brokerage you select. The order fills and then routes back to you.

    - To view how we model TT slippage, see - - Slippage + To view how we model Bloomberg™ Execution Management System order fills, see + + Fills .

    -

    Fills

    +

    Compliance

    - QuantConnect fills market orders immediately and completely in backtests and - - QuantConnect Paper Trading - - . In live trading, if the quantity of your market orders exceeds the quantity available at the top of the order book, your orders are filled according to what is available in the order book. + Bloomberg™ is not affiliated with QuantConnect, nor does it endorse Terminal Link. A Bloomberg™ SAPI permission and EMSX permission is required to use this brokerage connection, along with a Trading Firm or Institutional subscription on QuantConnect.

    - To view how we model TT order fills, see - - Fills - - . + The following rules apply: +

    +
      +
    • + All users of the integration must hold a Bloomberg License to be defined as an "Entitled User". +
    • +
    • + The Bloomberg SAPI will only be used for order routing and no data is permitted. The Bloomberg SAPI cannot be used for black-box trading. +
    • +
    +

    + The following table shows the activities each of the Bloomberg technologies support:

    + + + + + + + + + + + + + + + + + + + +
    + Technology + + Research + + Backtesting + + Paper UAT Trading + + Live Trading +
    + Server API + + green check + + green check + + green check + + green check +
    + -

    Security and Stability

    +

    Set Up SAPI

    - Note the following security and stability aspects of our TT integration. + The following few sections explain how to download the Bloomberg™ Server API (SAPI), install it on a cloud server, and add firewall rules so it can connect to QuantConnect Cloud.

    - Account Credentials + Download SAPI

    - When you deploy live algorithms with TT, we don't save your credentials. + Follow these steps to download the SAPI:

    +
      +
    1. + + Install the Bloomberg™ Terminal + + . +
    2. +
    3. + + Create a Bloomberg™ Terminal account + + . +
    4. +
    5. + In the Bloomberg™ Terminal, run + + WAPI<GO> + + . +
    6. +
    7. + On the API Developer's Help Site, click + + EMSX API + + . +
    8. + +
    9. + On the EMSX API page, under the + + Server API Process + + section, click + + Link + + . +
    10. + +
    11. + On the Server API Software Install page, click the correct + + download + + icons. +
    12. + +
    13. + Click + + System Requirements + + . +
    14. + +

    - API Outages + Install the SAPI

    - We call the TT API to place live trades. Sometimes the API may be down. Check the - - TT status page - - to see if the API is currently working. -

    - - - -

    Deposits and Withdrawals

    - - -

    - You can deposit and withdraw cash from your brokerage account while you run an algorithm that's connected to the - account. We sync the algorithm's cash holdings with the cash holdings in your brokerage account every day at 7:45 AM - Eastern Time (ET). + Follow these steps to install the SAPI:

    - - - -

    Demo Algorithm

    - - +
      +
    1. + Spin up an E12x9 AWS instance or higher that your organization controls. +
    2. +
    3. + Run the SAPI installer on the cloud server. +
    4. +

      + For more information about this step, see + + How to install serverapi.exe + + in the EMSX API Programmers Guide. At the end of the installion, you get a registration key. +

      +
    5. + Ask Bloomberg™ Support to activate your registration key. +
    6. +
    7. + Start the serverapi program. +
    8. +

      + On Windows, the default location is + + C: \ BLP \ ServerApi \ bin \ serverapi.exe + + . +

      +
    +

    + Set Up Your Account +

    - The following algorithm demonstrates the functionality of the TT brokerage: + Follow these steps to set up your SAPI account:

    -
    -
    // Demonstrate Trading Technologies brokerage functionality with an EMA crossover strategy on ES Futures.
    -public class TradingTechnologiesDemoAlgorithm : QCAlgorithm
    -{
    -    private Symbol _symbol;
    -    private ExponentialMovingAverage _fast;
    -    private ExponentialMovingAverage _slow;
    -
    -    public override void Initialize()
    -    {
    -        SetStartDate(2024, 9, 1);
    -        SetEndDate(2024, 12, 31);
    -        SetCash(100000);
    -        SetBrokerageModel(BrokerageName.TradingTechnologies, AccountType.Margin);
    -        var future = AddFuture(Futures.Indices.SP500EMini, Resolution.Daily);
    -        future.SetFilter(0, 90);
    -    }
    -
    -    public override void OnData(Slice slice)
    -    {
    -        if (Portfolio.Invested) return;
    -        foreach (var chain in slice.FutureChains)
    -        {
    -            var contract = chain.Value.OrderBy(c => c.Expiry).FirstOrDefault();
    -            if (contract == null) continue;
    -            if (_fast == null)
    -            {
    -                _symbol = contract.Symbol;
    -                _fast = EMA(_symbol, 10, Resolution.Daily);
    -                _slow = EMA(_symbol, 50, Resolution.Daily);
    -                return;
    -            }
    -            if (!_slow.IsReady) return;
    -            if (_fast > _slow)
    -                MarketOrder(_symbol, 1);
    -        }
    -    }
    -}
    -
    # Demonstrate Trading Technologies brokerage functionality with an EMA crossover strategy on ES Futures.
    -class TradingTechnologiesDemoAlgorithm(QCAlgorithm):
    -    def initialize(self) -> None:
    -        self.set_start_date(2024, 9, 1)
    -        self.set_end_date(2024, 12, 31)
    -        self.set_cash(100000)
    -        self.set_brokerage_model(BrokerageName.TRADING_TECHNOLOGIES, AccountType.MARGIN)
    -        future = self.add_future(Futures.Indices.SP_500_E_MINI, Resolution.DAILY)
    -        future.set_filter(0, 90)
    -        self._symbol = None
    -        self._fast = None
    -        self._slow = None
    -
    -    def on_data(self, slice: Slice) -> None:
    -        if self.portfolio.invested:
    -            return
    -        for chain in slice.future_chains:
    -            contracts = sorted(chain.Value, key=lambda c: c.expiry)
    -            if not contracts:
    -                continue
    -            contract = contracts[0]
    -            if self._fast is None:
    -                self._symbol = contract.symbol
    -                self._fast = self.ema(self._symbol, 10, Resolution.DAILY)
    -                self._slow = self.ema(self._symbol, 50, Resolution.DAILY)
    -                return
    -            if not self._slow.is_ready:
    -                return
    -            if self._fast.current.value > self._slow.current.value:
    -                self.market_order(self._symbol, 1)
    -
    - - - -

    Create TT Users

    - - -

    - Follow these steps to create your TT user name, account name, remote comp id, session password, app key, and app secret: -

    -
    - -
    - +
      +
    1. + + Contact Bloomberg Support + + and ask them to enable the Server Side EMSX API. +
    2. +
    3. + Ask Bloomberg Support for your unique user identifier (UUID). +
    4. +

      + Save it somewhere safe. You will need it when you deploy live algorithms. +

      +
    5. + Contact the EMSX brokerage you plan to use and give them your UUID. +
    6. +

    - Part 1: Sign in + Add Firewall Rules

    - On the TT website, sign in to your - - TT account - - or your - - TT UAT account - - . If you don't have an account, see - - Account Types - - to create one. + Follow these steps to configure the firewall rules on the AWS instance so that the SAPI can connect to QuantConnect Cloud:

    -

    - Part 2: Add a New User -

    1. - In the top navigation bar, click + Click - Setup + Start .
    2. - On the Setup page, click - - +New User + Enter + + Windows Defender Firewall with Advanced Security + + and then press + + Enter .
    3. - In the - - New User + In the left panel, click + + Inbound Rules - window, click the radio button that corresponds to user's employment status in your company and then click + . +
    4. +
    5. + In the right panel, click - Continue + New Rule... .
    6. - On the New User page, fill in the form with information about the new user. + Follow the prompts to create a program rule for the serverapi.
    7. -

      - The name that you put in the - - Username - - field is the username you will need when deploying live algorithms with LEAN. -

      -

      - In the - - Status - - section, click the - - Trade Mode - - field and then click - - TT Pro +

    8. + In the Windows Defender Firewall with Advanced Security window, double-click the serverapi row. +
    9. +
    10. + In the serverapi window, click the + + Scope - from the drop-down menu. -

      -

      + tab. +

    11. +
    12. In the - Advanced Settings - - section, select the - - Can create TT Rest API Key - - and - - Can create TT.NET SDK Client Side key + Remote IP address - check boxes. -

      + section, add the QuantConnect Cloud IP address, 207.182.16.137. +
    13. Click - Create + OK .
    14. - On the Setup page, click the new user in the table and then click - - Send Invitation - - . + Add the QuantConnect Cloud IP address to the other row in the table that has the serverapi name.
    -

    - Part 3: Create an App Key for the New User -

    + + + +

    Deploy Live Algorithms

    + + +

    + You need to + + set up the Bloomberg SAPI + + before you can deploy cloud algorithms with Terminal Link. +

    +

    + You must have an available + + live trading node + + for each live trading algorithm you deploy. +

    +

    + Follow these steps to deploy a live algorithm: +

    1. - On the Setup page, click the new user in the table and then click the - - App Keys + + Open the project + + you want to deploy. +
    2. +
    3. + Click the + Lightning icon + + Deploy Live - tab. + icon.
    4. - In the - - App Keys + On the Deploy Live page, click the + + Brokerage - section, click + field and then click - New + Terminal Link - . + from the drop-down menu.
    5. - In the - - Create New Application Key - - window, enter an application key name (for example, - - qb_alex_app_key - - ), select - - TT REST API + Click the + + Connection Type - for application key type and then click + field and then click - Create + SAPI - . + from the drop-down menu.
    6. - Click - - Copy Secret to Clipboard + In the + + Server Auth Id - and save it somewhere safe, like a text editor. + field, enter your unique user identifier (UUID).
    7. - The text you copy contains the App Key and App Secret separated by a colon. For example, a79b17df-b249-45a1-9d12-d5bbd2964626:424cc666-42aa-4125-a48e-13f630afd5a1. + The UUID is a unique integer identifier that's assigned to each Bloomberg Anywhere user. If you don't know your UUID, contact Bloomberg.

    8. - Click - - OK + In the + + EMSX Broker - . + field, enter the EMSX broker to use.
    9. -
    -

    - Part 4: Create an Account for the New User -

    -
    1. - On the Setup page, in the left navigation bar, click - - Accounts + In the + + Server Port - . + field, enter the port where SAPI is listening.
    2. +

      + The default port is 8194. +

    3. - On the Accounts page, click - - +New Account + In the + + Server Host - . -
    4. -
    5. - On the New User page, fill in the form with the following information: + field, enter the public IP address of the SAPI AWS server.
    6. -
        -
      • - Set the account name to something representative (for example, QC). -
      • -
      • - If there is a parent account, select it in the - - Parent - - field. -
      • -
      • - In the - - Parent - - field, select - - Routing (internal sub-account) - - . -
      • -
    7. - Click - - Create + In the + + EMSX Account - . + field, enter the account to which LEAN should route orders.
    8. - On the Account page, click the new account in the table and then click the - - Users + In the + + EMSX Team - tab. + field, enter the team account to receive events of your team's orders.
    9. +

      + The default value is empty, which means LEAN disregards these notifications. +

    10. - In the Users section, click - - +Add + In the + + OpenFIGI Api Key - . + field, enter your API key.
    11. - In the Select Users window, click the new user and then click - - Select + Click the + + Environment - . + field and then click one of the options from the drop-down menu.
    12. - Click - - Save Changes + Click the + + Node - . + field and then click the live trading node that you want to use from the drop-down menu.
    13. -
    -

    - Part 5: Create a FIX Session for the New User -

    -
    1. - On the Setup page, in the left navigation bar, click - - FIX Sessions + + (Optional) - . -
    2. -
    3. - On the FIX Sessions page, click + In the + + Data Provider + + section, click - +New FIX Session + Show - . + and change the data provider or add additional providers.
    4. - On the New FIX Sessions page, fill in the form with the following information: + If your brokerage account has existing cash holdings, follow these steps ( + + see video + + ):
    5. -
        -
      • - Set the - - FIX Session Name - - to something representative (for example, QC). -
      • +
        1. In the - - FIX Type + + Algorithm Cash State - field, select + section, click - FIX Order Routing + Show .
        2. - Set the - - Remote Comp ID + Click + + Add Currency - to something representative (for example, qc_alex_id). + .
        3. -

          - This Id is the remote comp id you will use when deploying live algorithms with LEAN. -

          -

          - The session password you set in this form is the password you will use when deploying live algorithms with LEAN. -

        4. - Select the - - Send unsolicited order and fill message - - check box. + Enter the currency ticker (for example, USD or CAD) and a quantity.
        5. +
        +
      • + If your brokerage account has existing position holdings, follow these steps ( + + see video + + ): +
      • +
        1. In the - Status + Algorithm Holdings State - section, deselect the - - Inactive + section, click + + Show - check box. + .
        2. -
      +
    6. + Click + + Add Holding + + . +
    7. +
    8. + Enter the symbol ID, symbol, quantity, and average price. +
    9. +
  • - Click - - Create + + (Optional) + + Set up notifications + .
  • - On the FIX Sessions page, click the new FIX session in the table and then click the - - Users - - tab. -
  • -
  • - In the Users section, click - - +Add + Configure the + + Automatically restart algorithm - . + setting.
  • +

    + By enabling + + automatic restarts + + , the algorithm will use best efforts to restart the algorithm if it fails due to a runtime error. This can help improve the algorithm's resilience to temporary outages such as a brokerage API disconnection. +

  • - In the Select Users window, click the new user and then click + Click - Select - - . -
  • -
  • - Click - - Save Changes - - . -
  • - -

    - Part 6: Verify the User Credentials -

    -
      -
    1. - On the Setup page, in the left navigation bar, click - - Users - - . -
    2. -
    3. - On the Users page, click the new user in the table and then review the information under the - - FIX Sessions - - and - - App Keys - - tabs. -
    4. -
    - - - -

    Deploy Live Algorithms

    - - -

    - You must have an available - - live trading node - - for each live trading algorithm you deploy. -

    -

    - Follow these steps to deploy a live algorithm: -

    -
      -
    1. - - Open the project - - you want to deploy. -
    2. -
    3. - Click the - Lightning icon - - Deploy Live - - icon. -
    4. -
    5. - On the Deploy Live page, click the - - Brokerage - - field and then click - - Trading Technologies - - from the drop-down menu. -
    6. -
    7. - Enter your TT user name, account name, remote comp id, session password, app key, and app secret. -
    8. -

      - To get your credentials, see - - Create TT Users - - . -

      -

      - Our TT integration routes orders via the TT FIX 4.4 Connection. - - Contact your TT representative - - to set the exchange where you would like your orders sent. Your account details are not saved on QuantConnect. -
      -

      -

      - Our integration fetches your positions using the REST endpoint, so the app key and app secret are your REST App credentials. -

      -
    9. - Click the - - Environment - - field and then click one of the environments from the drop-down menu. -
    10. -

      - The following table shows the supported environments: -

      - - - - - - - - - - - - - - - - - -
      - Environment - - Description -
      - Live - - Trade in the production environment -
      - UAT - - Trade in the - - User Acceptance Testing - - environment -
      -
    11. - Click the - - Node - - field and then click the live trading node that you want to use from the drop-down menu. -
    12. -
    13. - - (Optional) - - In the - - Data Provider - - section, click - - Show - - and change the data provider or add additional providers. -
    14. -
    15. - If your brokerage account has existing cash holdings, follow these steps ( - - see video - - ): -
    16. -
        -
      1. - In the - - Algorithm Cash State - - section, click - - Show - - . -
      2. -
      3. - Click - - Add Currency - - . -
      4. -
      5. - Enter the currency ticker (for example, USD or CAD) and a quantity. -
      6. -
      -
    17. - - (Optional) - - - Set up notifications - - . -
    18. -
    19. - Configure the - - Automatically restart algorithm - - setting. -
    20. -

      - By enabling - - automatic restarts - - , the algorithm will use best efforts to restart the algorithm if it fails due to a runtime error. This can help improve the algorithm's resilience to temporary outages such as a brokerage API disconnection. -

      -
    21. - Click - - Deploy + Deploy .
    22. @@ -36902,11 +36602,11 @@

      Deploy Live Algorithms

       

      - +
      -
      +

      Brokerages

      -

      Wolverine

      +

      SSC Eze

      Introduction

      @@ -36916,14 +36616,10 @@

      Introduction

      QuantConnect enables you to run your algorithms in live mode with real-time market data.

      - Wolverine Execution Services is a diversified financial institution specializing in proprietary trading, asset management, order execution services, and technology solutions. They are recognized as a market leader in derivatives valuation, trading, and value-added order execution across global Equity, Options, and Futures markets. Their focus on innovation, achievement, and integrity serves the interests of their clients and colleagues. Wolverine Execution Services is headquartered in Chicago, with branch offices in New York, San Francisco, and London. They serve funds that have at least $5M assets under management. -

      -

      - To view the implementation of the Wolverine Execution Services brokerage integration, see the - - Lean.Brokerages.Wolverine repository + + SS&C Eze - . + , former Eze Software, was founded by Sean McLaughlin in 1995. SS&C Eze provides a multi-asset and multi-broker execution management system (EMS) that provides fast, seamless, and centralized access to extensive liquidity across global equities, bonds, futures, options, and digital assets.

      @@ -36932,27 +36628,23 @@

      Account Types

      - Wolverine Execution Services supports cash and margin accounts. To set the account type in an algorithm, see the - - Wolverine brokerage model documentation - - . + SS&C Eze supports order routing via their EMS (execution management system). It's a margin account, where you set the buying power in the wizard when you're deploying to a professional prime brokerage account.

      Create an Account

      - To create a Wolverine Execution Services account, - - contact their staff + Contact SS&C Eze's + + sales team - through the TradeWex website. + to create an account.

      Paper Trading

      - Wolverine Execution Services doesn't support paper trading, but you can follow these steps to simulate it with QuantConnect: + The SS&C Eze doesn't support paper trading, but you can follow these steps to simulate it with QuantConnect:

      1. @@ -36964,8 +36656,8 @@

        initialize method of your algorithm, - - set the Wolverine brokerage model and your account type + + set the SS&C Eze brokerage model and your account type .

      2. @@ -36983,14 +36675,46 @@

        Asset Classes

        - Our Wolverine Execution Services integration supports trading - - US Equities + Our + + SS&C Eze - . + integration supports the following asset classes:

        +

        - You may not be able to trade all assets with Wolverine. For example, if you live in the EU, you can't trade US ETFs. Check with your local regulators to know which assets you are allowed to trade. You may need to adjust settings in your brokerage account to live trade some assets. + You may not be able to trade all assets with SS&C Eze. For example, if you live in the EU, you can't trade US ETFs. Check with your local regulators to know which assets you are allowed to trade. You may need to adjust settings in your brokerage account to live trade some assets.

        @@ -36999,11 +36723,15 @@

        Data Providers

        - The - - QuantConnect data provider + You might need to purchase a + + SS&C Eze market data - provides US Equities data during live trading. + subscription for your trading. For more information about live data providers, see + + Datasets + + .

        @@ -37012,7 +36740,11 @@

        Orders

        - We model the Wolverine Execution Services API by supporting order types, but not order updates or extended market hours trading. When you deploy live algorithms, you can + We model the Eze API by supporting several order types, the + + TimeInForce + + order property, and order updates. When you deploy live algorithms, you can place manual orders @@ -37022,17 +36754,33 @@

        Order Types

        - The following table describes the available order types for each asset class that our Wolverine Execution Services integration supports: + The following table describes the available order types for each asset class that our + + SS&C Eze + + integration supports:

        - - + + + + @@ -37045,12 +36793,14 @@

        - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + Order Type + Equity + Equity Options + + Futures + + Future Options + + Index Options +
        green check
        - - Market on Close - + green check + + green check + + green check green check @@ -37065,6 +36815,18 @@

        green check + green check + + green check + + green check + + green check +
        @@ -37075,6 +36837,18 @@

        green check + green check + + green check + + green check + + green check +
        @@ -37085,6 +36859,54 @@

        green check + green check + + green check + + green check + + green check +
        + + Market on Open + + + green check + + + + +
        + + Market on Close + + + green check + + + + +
        @@ -37094,37 +36916,31 @@

        text-align: center; } -

        - Updates -

        -

        - We model the Wolverine Execution Services API by not supporting order updates. -

        -

        - Extended Market Hours -

        -

        - Wolverine Execution Services doesn't support extended market hours trading. If you place an order outside of regular trading hours, the order is invalid. -

        Order Properties

        - We model custom order properties from the Wolverine API. The following table describes the members of the + We model the SS&C Eze API. The following table describes the members of the - WolverineOrderProperties + EzeOrderProperties - object that you can set to customize order execution: + object that you can set to customize order execution.

        - - + + @@ -37165,6 +36981,14 @@

        GOOD_TIL_CANCELED +
      3. + + GoodTilDate + + + good_til_date + +
      4. @@ -37203,10 +37026,10 @@

        @@ -37237,10 +37053,10 @@

        @@ -37260,54 +37076,66 @@

        + Property + + Data Type + Description + Default Value +
        @@ -37179,23 +37003,22 @@

        - Exchange + Account - exchange + account - - Exchange + + string + + + str - Defines the exchange name for a particular market. For example, - - Exchange.SMART - - . + Sets a semi-colon separated list of trade or neutral accounts the user has permission for, e.g., "TAL;TEST;USER1;TRADE" or "TAL;TEST;USER2;NEUTRAL".
        - ExchangePostFix + AccountType - exchange_post_fix + account_type @@ -37218,18 +37041,11 @@

        - The exchange post fix to apply if any. For example, if you set - - Exchange - - - exchange - - to - - Exchange.SMART - - , then "-INCA-TX" yields "SMART-INCA-TX". + Sets the account type for the order. E.g., + + "119" + + for margin orders in Eze EMS.
        - LocateBroker + Notes - locate_broker + notes @@ -37252,7 +37068,7 @@

        - Identifies the broker source for borrowed stock. + Sets the user message or notes.
        - PositionSide + Route - position_side + route - OrderPosition? + string - OrderPosition | NoneType + str - Specify the position side in the order direction (buy-to-open, sell-to-close, etc.) instead of the default handling + Sets the route name as shown in SS&C Eze EMS. - - null - - - None -
        +

        + Updates +

        - If the - - PositionSide - - - position_side - - is not specified, the engine will determine it from the holdings and the order quantity. For example, if it's a sell short and will send - - PositionSide - - - SellShort - - - SELL_SHORT - + We model the SS&C Eze API by supporting + + order updates + .

        +

        + Handling Splits +

        +

        + If you're using raw + + data normalization + + and you have active orders with a limit, stop, or trigger price in the market for a US Equity when a + + stock split + + occurs, the following properties of your orders automatically adjust to reflect the stock split: +

        +
          +
        • + Quantity +
        • +
        • + Limit price +
        • +
        • + Stop price +
        • +
        • + Trigger price +
        • +
        @@ -37315,8 +37143,8 @@

        Fees

        - Wolverine Execution Services charge $0.005 per share you trade. To view how we model their fees, see - + Orders filled with SS&C Eze are subject to the fees of the SS&C Eze Execution Management System and your prime brokerage destination. To view how we model their fees, see + Fees . @@ -37329,7 +37157,7 @@

        Margin

        We model - + buying power and @@ -37353,15 +37181,15 @@

        Slippage

        - Orders through Wolverine Execution Services do not experience slippage in backtests and + Orders through SS&C Eze do not experience slippage in backtests and QuantConnect Paper Trading . In live trading, your orders may experience slippage.

        - To view how we model Wolverine slippage, see - + To view how we model SS&C Eze slippage, see + Slippage . @@ -37380,8 +37208,8 @@

        Fills

        . In live trading, if the quantity of your market orders exceeds the quantity available at the top of the order book, your orders are filled according to what is available in the order book.

        - To view how we model Wolverine Execution Services order fills, see - + To view how we model Eze order fills, see + Fills . @@ -37396,8 +37224,8 @@

        Settlements

        If you trade with a margin account, trades settle immediately

        - To view how we model settlement for Wolverine trades, see - + To view how we model settlement for SS&C Eze trades, see + Settlement . @@ -37409,7 +37237,7 @@

        Security and Stability

        - When you deploy live algorithms with Wolverine Execution Services, we don't save your credentials. + When you deploy live algorithms with SS&C Eze, we don't save your credentials.

        @@ -37429,11 +37257,11 @@

        Demo Algorithm

        - The following algorithm demonstrates the functionality of the Wolverine Execution Services brokerage: + The following algorithm demonstrates the functionality of the SS&C Eze brokerage:

        -
        // Demonstrate Wolverine Execution Services brokerage functionality with an EMA crossover strategy on SPY.
        -public class WolverineDemoAlgorithm : QCAlgorithm
        +   
        // Demonstrate SS&C Eze brokerage functionality with an EMA crossover strategy on SPY.
        +public class SSCEzeDemoAlgorithm : QCAlgorithm
         {
             private ExponentialMovingAverage _fast;
             private ExponentialMovingAverage _slow;
        @@ -37443,7 +37271,7 @@ 

        Demo Algorithm

        SetStartDate(2024, 9, 1); SetEndDate(2024, 12, 31); SetCash(100000); - SetBrokerageModel(BrokerageName.Wolverine, AccountType.Margin); + SetBrokerageModel(BrokerageName.Eze, AccountType.Margin); var symbol = AddEquity("SPY", Resolution.Daily).Symbol; _fast = EMA(symbol, 10, Resolution.Daily); _slow = EMA(symbol, 50, Resolution.Daily); @@ -37458,13 +37286,13 @@

        Demo Algorithm

        Liquidate(); } }
        -
        # Demonstrate Wolverine Execution Services brokerage functionality with an EMA crossover strategy on SPY.
        -class WolverineDemoAlgorithm(QCAlgorithm):
        +   
        # Demonstrate SS&C Eze brokerage functionality with an EMA crossover strategy on SPY.
        +class SSCEzeDemoAlgorithm(QCAlgorithm):
             def initialize(self) -> None:
                 self.set_start_date(2024, 9, 1)
                 self.set_end_date(2024, 12, 31)
                 self.set_cash(100000)
        -        self.set_brokerage_model(BrokerageName.WOLVERINE, AccountType.MARGIN)
        +        self.set_brokerage_model(BrokerageName.EZE, AccountType.MARGIN)
                 symbol = self.add_equity("SPY", Resolution.DAILY).symbol
                 self._fast = self.ema(symbol, 10, Resolution.DAILY)
                 self._slow = self.ema(symbol, 50, Resolution.DAILY)
        @@ -37515,12 +37343,19 @@ 

        Deploy Live Algorithms

        field and then click - Wolverine Execution Services + SS&C Eze from the drop-down menu.
      5. - Enter your Wolverine Execution Services credentials. + Enter your + + SS&C Eze + + username and password. +
      6. +
      7. + Enter the Trading Account in BBCD (BANK;BRANCH;CUSTOMER;DEPOSIT) format, the Domain, and the select the Locale.
      8. Your account details are not saved on QuantConnect. @@ -37546,6 +37381,17 @@

        Deploy Live Algorithms

        and change the data provider or add additional providers. +

        + In most cases, we suggest using the + + QuantConnect data provider + + , a third-party data provider such as + + Polygon data provider + + , or both. The order you set them in the deployment wizard defines their order of precedence in Lean. +

      9. If your brokerage account has existing cash holdings, follow these steps ( @@ -37576,36 +37422,6 @@

        Deploy Live Algorithms

        Enter the currency ticker (for example, USD or CAD) and a quantity.
      -
    23. - If your brokerage account has existing position holdings, follow these steps ( - - see video - - ): -
    24. -
        -
      1. - In the - - Algorithm Holdings State - - section, click - - Show - - . -
      2. -
      3. - Click - - Add Holding - - . -
      4. -
      5. - Enter the symbol ID, symbol, quantity, and average price. -
      6. -
    25. (Optional) @@ -37648,155 +37464,11 @@

      Deploy Live Algorithms

       

      - -
      -
      -

      Brokerages

      -

      FIX Connections

      -
      -
      -

      Introduction

      - - -

      - The Financial Information eXchange (FIX) is the standard electronic communications protocol for front-office messaging. The FIX community includes about 300 firms, including major investment banks. -

      - - - -

      Supported Connections

      - - -

      - The following FIX connections are available on QuantConnect: -

      - - - - - - - - - - - - - - - - - - - - -
      - Name - - Integration Implementation - - Model Implementation -
      - Raiffeisen Bank International - - - Lean.Brokerages.RaiffeisenBankInternational - - - - RBIBrokerageModel.cs - -
      - - Wolverine - - - - Lean.Brokerages.Wolverine - - - - WolverineBrokerageModel.cs - -
      - - - -

      Portfolio State

      - - -

      - FIX connections do not support fetching the portfolio state. You must provide the cash and holdings on deployment. -

      -

      - If your brokerage account has existing cash holdings, follow these steps ( - - see video - - ): -

      -
        -
      1. - In the - - Algorithm Cash State - - section, click - - Show - - . -
      2. -
      3. - Click - - Add Currency - - . -
      4. -
      5. - Enter the currency ticker (for example, USD or CAD) and a quantity. -
      6. -
      -

      - If your brokerage account has existing position holdings, follow these steps ( - - see video - - ): -

      -
        -
      1. - In the - - Algorithm Holdings State - - section, click - - Show - - . -
      2. -
      3. - Click - - Add Holding - - . -
      4. -
      5. - Enter the symbol ID, symbol, quantity, and average price. -
      6. -
      - - - -

       

      - +
      -
      +

      Brokerages

      -

      CFD and FOREX Brokerages

      +

      Trading Technologies

      Introduction

      @@ -37806,20 +37478,12 @@

      Introduction

      QuantConnect enables you to run your algorithms in live mode with real-time market data.

      - QuantConnect integrates with - - OANDA - - for CFD and FOREX trading. OANDA was founded by Dr. Michael Stumm and Dr. Richard Olsen in 1995 with the goal to "transform all aspects of how the world interacts with currencies, whether that be trading or utilizing currency data and information". OANDA provides access to trading Forex and CFDs for clients in over 240 countries and territories with - - no minimum deposit - - . OANDA also provides demo accounts, advanced charting tools, and educational content + Trading Technologies (TT) was founded by Gary Kemp in 1994 with the goal to create professional trading software, infrastructure, and data solutions for a wide variety of users. TT provides access to trading Futures, Options, and Crypto. TT also provides a charting platform, infrastructure services, and risk management tools. TT is not actually a brokerage. The firm is a brokerage router with access to more than 30 execution destinations.

      - To view the implementation of the OANDA brokerage integration, see the - - Lean.Brokerages.OANDA repository + To view the implementation of the TT integration, see the + + Lean.Brokerages.TradingTechnologies repository .

      @@ -37830,115 +37494,67 @@

      Account Types

      - OANDA supports margin accounts. To set the account type in an algorithm, see the - - OANDA brokerage model documentation + The + + TradingTechnologiesBrokerageModel + + does not have specific modeling for fees and slippage because TT is an order router and can execute on many exchanges and brokerages. To set the brokerage model and account type in an algorithm, see the + + TT brokerage model documentation - . + . In live trading, TT reports the total fees of your orders after each order fill. Pass a different + + BrokerageName + + to + + SetBrokerageModel + + + set_brokerage_model + + to backtest your algorithm with fee and slippage modeling. The brokerage model you set should support the asset classes and orders in your algorithm.

      Create an Account

      Follow the - - How to open an account + + account creation wizard - page on the OANDA website to open an OANDA account. + on the TT website to create a TT account.

      +

      + Paper Trading +

      - You will need your account number and access token to deploy live algorithms. To get your account number, open the - - Account Statement + Trading Technologies provides a separate + - page on the OANDA website. Your account number is formatted as - - ###-###-######-### - - . To get your access token, open the - - Manage API Access + User Acceptance Testing (UAT) Certification environment at + + uat.trade.tt - on the OANDA website. + . The TT UAT environment connects to actual exchange certification environments for both market data and order routing. +

      +

      + To create a UAT account, follow the + + UAT account creation wizard + + on the TT website.

      -
      -

      - Important note for European Union residents: On March 17th, 2023, OANDA Europe Markets Ltd. ("OEML") closed operations and transferred accounts to OANDA TMS Brokers S.A. ("OANDA TMS") - - - 1 - - - . OANDA TWS does not offer REST API endpoints for live trading. EU residents can trade with OANDA if they can open an account with another member of the OANDA Group, for example, US citizens. -

      -

      - Paper Trading + Create Deployment Credentials

      - OANDA supports paper trading. Follow these steps to set up an OANDA paper trading account: + After you create your TT account, see + + Create TT Users + + to create your live deployment credentials.

      -
        -
      1. - - Create an OANDA demo account - - . -
      2. -
      3. - Log in to your demo account. -
      4. -
      5. - On the Account page, in the - - My Services - - section, click - - Manage API Access - - . -
      6. -
      7. - On the Your key to OANDA's API page, click - - Generate - - . -
      8. -

        - Your access token displays. Store it somewhere safe. You need your access token to deploy an algorithm with your paper trading account. -
        -

        -
      9. - In the top navigation bar, click - - My Account - - . -
      10. -
      11. - On the Account page, in the - - Manage Funds - - section, click - - View - - . -
      12. -
      13. - On the My Funds page, in the - - Account Summary - - section, note your v20 Account Number. -
      14. -

        - You need your v20 Account Number to deploy an algorithm with your paper trading account. -

        -
      @@ -37946,13 +37562,9 @@

      Asset Classes

      - Our OANDA integration supports trading - - Forex - - and - - CFDs + Our TT integration supports trading + + Futures .

      @@ -37963,15 +37575,19 @@

      Data Providers

      - The QuantConnect data provider providers - - Forex + The + + QuantConnect data provider - and - - CFD + provides Futures data during live trading. If you want to use Trading Technologies data, you might need to purchase a + + Trading Technologies market data - trading data during live trading. + subscription for your trading. For more information about live data providers, see + + Datasets + + .

      @@ -37980,11 +37596,11 @@

      Orders

      - We model the OANDA API by supporting several order types, a + We model the TT API by supporting several order types, the TimeInForce - order instruction, and order updates. When you deploy live algorithms, you can + order property, and order updates. When you deploy live algorithms, you can place manual orders @@ -37994,7 +37610,7 @@

      Order Types

      - The following table describes the available order types for each asset class that our OANDA integration supports: + The following table describes the available order types for each asset class that our TT integration supports:

      @@ -38002,11 +37618,8 @@

      - - @@ -38020,9 +37633,6 @@

      - - - -
      Order Type - Forex - - CFD + + Futures
      green check - green check -
      @@ -38033,9 +37643,6 @@

      green check - green check -
      @@ -38046,9 +37653,6 @@

      green check - green check -
      @@ -38059,9 +37663,6 @@

      green check - green check -
      @@ -38071,27 +37672,66 @@

      text-align: center; } -

      - Time In Force -

      - We model the - - GoodTilCanceled - - - GOOD_TIL_CANCELED - - - TimeInForce - - from the OANDA API. + TT enforces the following order rules: +

      +
        +
      • + If you are buying (selling) with a + + StopMarketOrder + + + stop_market_order + + or a + + StopLimitOrder + + + stop_limit_order + + , the stop price of the order must be greater (less) than the current security price. +
      • +
      • + If you are buying (selling) with a + + StopLimitOrder + + + stop_limit_order + + , the limit price of the order must be greater (less) than the stop price. +
      • +
      +

      + Time In Force +

      +

      + We model the TT API by supporting the + + Day + + + DAY + + and + + GoodTilCanceled + + + GOOD_TIL_CANCELED + + + TimeInForce + + order properties.

      Updates

      - We model the OANDA API by supporting + We model the TT API by supporting order updates @@ -38104,12 +37744,12 @@

      Fees

      - To view the OANDA trading fees, see the - - Our Charges and Fees + To view the TT trading fees, see the + + Pricing - page on the OANDA website. To view how we model their fees, see - + page on the TT website. To view how we model their fees, see + Fees . @@ -38122,7 +37762,7 @@

      Margin

      We model - + buying power and @@ -38138,11 +37778,15 @@

      Slippage

      - Orders through OANDA do not experience slippage in backtests. In OANDA paper trading and live trading, your orders may experience slippage. + Orders through TT do not experience slippage in backtests and + + QuantConnect Paper Trading + + . In live trading, your orders may experience slippage.

      - To view how we model OANDA slippage, see - + To view how we model TT slippage, see + Slippage . @@ -38154,25 +37798,16 @@

      Fills

      - To view how we model OANDA order fills, see - - Fills + QuantConnect fills market orders immediately and completely in backtests and + + QuantConnect Paper Trading - . -

      - - - -

      Settlements

      - - -

      - Trades settle immediately after the transaction + . In live trading, if the quantity of your market orders exceeds the quantity available at the top of the order book, your orders are filled according to what is available in the order book.

      - To view how we model settlement for OANDA trades, see - - Settlement + To view how we model TT order fills, see + + Fills .

      @@ -38183,21 +37818,21 @@

      Security and Stability

      - Note the following security and stability aspects of our OANDA integration. + Note the following security and stability aspects of our TT integration.

      Account Credentials

      - When you deploy live algorithms with OANDA, we don't save your credentials. + When you deploy live algorithms with TT, we don't save your credentials.

      API Outages

      - We call the OANDA API to place live trades. Sometimes the API may be down. Check the - - OANDA status page + We call the TT API to place live trades. Sometimes the API may be down. Check the + + TT status page to see if the API is currently working.

      @@ -38219,11 +37854,11 @@

      Demo Algorithm

      - The following algorithm demonstrates the functionality of the OANDA brokerage: + The following algorithm demonstrates the functionality of the TT brokerage:

      -
      // Demonstrate OANDA brokerage functionality with an EMA crossover strategy on EURUSD.
      -public class OandaDemoAlgorithm : QCAlgorithm
      +   
      // Demonstrate Trading Technologies brokerage functionality with an EMA crossover strategy on ES Futures.
      +public class TradingTechnologiesDemoAlgorithm : QCAlgorithm
       {
           private Symbol _symbol;
           private ExponentialMovingAverage _fast;
      @@ -38234,836 +37869,527 @@ 

      Demo Algorithm

      SetStartDate(2024, 9, 1); SetEndDate(2024, 12, 31); SetCash(100000); - SetBrokerageModel(BrokerageName.OandaBrokerage, AccountType.Margin); - _symbol = AddForex("EURUSD", Resolution.Daily, Market.Oanda).Symbol; - _fast = EMA(_symbol, 10, Resolution.Daily); - _slow = EMA(_symbol, 50, Resolution.Daily); + SetBrokerageModel(BrokerageName.TradingTechnologies, AccountType.Margin); + var future = AddFuture(Futures.Indices.SP500EMini, Resolution.Daily); + future.SetFilter(0, 90); } public override void OnData(Slice slice) { - if (!_slow.IsReady) return; - if (_fast > _slow && !Portfolio.Invested) - SetHoldings(_symbol, 1); - else if (_fast < _slow && Portfolio.Invested) - Liquidate(); + if (Portfolio.Invested) return; + foreach (var chain in slice.FutureChains) + { + var contract = chain.Value.OrderBy(c => c.Expiry).FirstOrDefault(); + if (contract == null) continue; + if (_fast == null) + { + _symbol = contract.Symbol; + _fast = EMA(_symbol, 10, Resolution.Daily); + _slow = EMA(_symbol, 50, Resolution.Daily); + return; + } + if (!_slow.IsReady) return; + if (_fast > _slow) + MarketOrder(_symbol, 1); + } } }
      -
      # Demonstrate OANDA brokerage functionality with an EMA crossover strategy on EURUSD.
      -class OandaDemoAlgorithm(QCAlgorithm):
      +   
      # Demonstrate Trading Technologies brokerage functionality with an EMA crossover strategy on ES Futures.
      +class TradingTechnologiesDemoAlgorithm(QCAlgorithm):
           def initialize(self) -> None:
               self.set_start_date(2024, 9, 1)
               self.set_end_date(2024, 12, 31)
               self.set_cash(100000)
      -        self.set_brokerage_model(BrokerageName.OANDA_BROKERAGE, AccountType.MARGIN)
      -        self._symbol = self.add_forex("EURUSD", Resolution.DAILY, Market.OANDA).symbol
      -        self._fast = self.ema(self._symbol, 10, Resolution.DAILY)
      -        self._slow = self.ema(self._symbol, 50, Resolution.DAILY)
      +        self.set_brokerage_model(BrokerageName.TRADING_TECHNOLOGIES, AccountType.MARGIN)
      +        future = self.add_future(Futures.Indices.SP_500_E_MINI, Resolution.DAILY)
      +        future.set_filter(0, 90)
      +        self._symbol = None
      +        self._fast = None
      +        self._slow = None
       
           def on_data(self, slice: Slice) -> None:
      -        if not self._slow.is_ready:
      +        if self.portfolio.invested:
                   return
      -        if self._fast.current.value > self._slow.current.value and not self.portfolio.invested:
      -            self.set_holdings(self._symbol, 1)
      -        elif self._fast.current.value < self._slow.current.value and self.portfolio.invested:
      -            self.liquidate()
      + for chain in slice.future_chains: + contracts = sorted(chain.value, key=lambda c: c.expiry) + if not contracts: + continue + contract = contracts[0] + if self._fast is None: + self._symbol = contract.symbol + self._fast = self.ema(self._symbol, 10, Resolution.DAILY) + self._slow = self.ema(self._symbol, 50, Resolution.DAILY) + return + if not self._slow.is_ready: + return + if self._fast.current.value > self._slow.current.value: + self.market_order(self._symbol, 1)
      -

      Deploy Live Algorithms

      +

      Create TT Users

      - You must have an available - - live trading node - - for each live trading algorithm you deploy. + Follow these steps to create your TT user name, account name, remote comp id, session password, app key, and app secret:

      +
      + +
      + +

      + Part 1: Sign in +

      - Follow these steps to deploy a live algorithm: + On the TT website, sign in to your + + TT account + + or your + + TT UAT account + + . If you don't have an account, see + + Account Types + + to create one.

      +

      + Part 2: Add a New User +

      1. - - Open the project - - you want to deploy. + In the top navigation bar, click + + Setup + + .
      2. - Click the - Lightning icon - - Deploy Live + On the Setup page, click + + +New User - icon. + .
      3. - On the Deploy Live page, click the - - Brokerage + In the + + New User - field and then click + window, click the radio button that corresponds to user's employment status in your company and then click - OANDA + Continue - from the drop-down menu. + .
      4. - Enter your OANDA account Id and access token. + On the New User page, fill in the form with information about the new user.
      5. - To get your account ID and access token, see the - - Create an Account + The name that you put in the + + Username - section in the - - Account Types - - documentation. Your account details are not saved on QuantConnect. -
        + field is the username you will need when deploying live algorithms with LEAN.

        -
      6. - Click the +

        + In the + + Status + + section, click the - Environment + Trade Mode - field and then click one of the environments. -

      7. + field and then click + + TT Pro + + from the drop-down menu. +

        - The following table shows the supported environments: + In the + + Advanced Settings + + section, select the + + Can create TT Rest API Key + + and + + Can create TT.NET SDK Client Side key + + check boxes.

        - - - - - - - - - - - - - - - - - -
        - Environment - - Description -
        - Real - - Trade real money with fxTrade -
        - Demo - - Trade paper money with fxTrade Practice -
      8. - Click the - - Node + Click + + Create - field and then click the live trading node that you want to use from the drop-down menu. + .
      9. - - (Optional) + On the Setup page, click the new user in the table and then click + + Send Invitation + + . +
      10. +
      +

      + Part 3: Create an App Key for the New User +

      +
        +
      1. + On the Setup page, click the new user in the table and then click the + + App Keys + tab. +
      2. +
      3. In the - Data Provider + App Keys section, click - Show + New - and change the data provider or add additional providers. + .
      4. - - (Optional) + In the + + Create New Application Key + + window, enter an application key name (for example, + + qb_alex_app_key + + ), select + + TT REST API + + for application key type and then click + + Create - - Set up notifications - .
      5. - Configure the - - Automatically restart algorithm + Click + + Copy Secret to Clipboard - setting. + and save it somewhere safe, like a text editor.
      6. - By enabling - - automatic restarts - - , the algorithm will use best efforts to restart the algorithm if it fails due to a runtime error. This can help improve the algorithm's resilience to temporary outages such as a brokerage API disconnection. + The text you copy contains the App Key and App Secret separated by a colon. For example, a79b17df-b249-45a1-9d12-d5bbd2964626:424cc666-42aa-4125-a48e-13f630afd5a1.

      7. Click - Deploy + OK .
      -

      - The deployment process can take up to 5 minutes. When the algorithm deploys, the - - live results page - - displays. If you know your brokerage positions before you deployed, you can verify they have been loaded properly by checking your equity value in the runtime statistics, your cashbook holdings, and your position holdings. -

      - - - -

       

      - -
      -
      -

      Brokerages

      -

      Unsupported Brokerages

      -
      -
      -

      Introduction

      - - -

      - New brokerages can be added if the brokerage has an API that is popular, stable, and officially supported by the brokerage. To add a new brokerage to the platform, - - contact us - - . -

      - - - -

       

      - -
      -
      -

      Live Trading

      -

      Deployment

      -
      -
      -

      Introduction

      - - -

      - Deploy your trading algorithms live to receive real-time market data and submit orders on our co-located servers. As your algorithms run, you can view their performance in the Algorithm Lab. Since the algorithms run in QuantConnect Cloud, you can close the IDE without interrupting the execution of your algorithms. Deploying your algorithms to live trading through QuantConnect is cheaper than purchasing server space, setting up data feeds, and maintaining the software on your own. To deploy your algorithms on QuantConnect, you just need to follow the - - Deploy Live Algorithms - - section in the - - guide of your brokerage - - . -

      - - - -

      Resources

      - - -

      - Live trading nodes enable you to deploy live algorithms to our professionally-managed, co-located servers. - You need a live trading node for each algorithm that you deploy to our co-located servers. - Several models of live trading nodes are available. - More powerful live trading nodes allow you to run algorithms with larger universes and give you - - more time for machine learning training - - . - Each security subscription requires about 5MB of RAM. The following table shows the specifications of the live trading node models: -

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      - Name - - Number of Cores - - Processing Speed (GHz) - - RAM (GB) - - GPU -
      - L-MICRO - - 1 - - 2.6 - - 0.5 - - 0 -
      - L1-1 - - 1 - - 2.6 - - 1 - - 0 -
      - L1-2 - - 1 - - 2.6 - - 2 - - 0 -
      - L2-4 - - 2 - - 2.6 - - 4 - - 0 -
      - L8-16-GPU - - 8 - - 3.1 - - 16 - - 1/2 -
      - -

      - Refer to the - - Pricing - - page to see the price of each live trading node model. -

      -

      - To view the status of all of your organization's nodes, see the - - Resources panel - - of the IDE. - When you deploy an algorithm, it uses the best-performing resource by default, but you can - - select a specific resource to use - - . -

      -

      - The CPU nodes are available on a fair usage basis while the GPU nodes can be shared with a maximum of two members. - Depending on the server load, you may use all of the GPU's processing power. - GPU nodes perform best on repetitive and highly-parallel tasks like training machine learning models. - It takes time to transfer the data to the GPU for computation, so if your algorithm doesn't train machine learning models, the extra time it takes to transfer the data can make it appear that GPU nodes run slower than CPU nodes. -

      - - - -

      Node Quotas

      - - -

      - You need a live trading node for each simultaneous algorithm that you deploy. We do not support sub algorithms or sharing a server with multiple algorithms. The tier of your organization determines the number of live trading nodes the organization can have. The following number of live trading nodes are available for each tier: -

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      - Tier - - Node Quota -
      - Free - - 0 -
      - Quant Researcher - - 2 -
      - Team - - 10 -
      - Trading Firm - - Unlimited -
      - Institution - - Unlimited -
      - -

      - To deploy multiple algorithms using a single brokerage, create sub-accounts in your brokerage account so that each algorithm has its own set of brokerage connection credentials. -

      - - - -

      Ram Allocations

      - - -

      - Members often use 8-32GB of RAM in backtesting and are concerned that their algorithms will not work in live trading since live trading nodes have 512MB to 4GB of RAM. Backtesting nodes have more RAM because data is injected into your algorithm roughly 100,000x faster during backtests than live trading. You use more RAM in backtesting because many data objects are cached to achieve such fast speed. In live trading, 512MB to 4GB of RAM is sufficient for almost all use cases. -

      - - - -

      Wizard

      - - -

      - Use the deployment wizard in the Algorithm Lab to - - deploy your algorithms to live trading - - . The deployment wizard lets you select a brokerage, enter your brokerage credentials, select a data provider, select a live trading node, set up notifications, and configure automatic algorithm restarts. -

      - Deploy live wizard interface -

      - Most of the brokerages automatically load your cash holdings, position holdings, and submitted orders so that you can view your portfolio state on the - - live results page - - . For brokerages that don't automatically load your holdings, you can enter your cash and position holdings in the deployment wizard. -

      - - - -

      Unsupported Assets

      - - -

      - If you have unsupported assets in your brokerage account when you deploy, Lean can't calculate the portfolio value correctly, so margin calculations are wrong. To avoid issues, if your account has unsupported assets, Lean automatically exits on deployment. For a list of supported assets, see the asset class - - dataset listing - - . -

      - - - -

      Automatic Restarts

      - - -

      - Automatic restarts use best efforts to restart your algorithm if it fails due to a runtime error or an API disconnection. Automatic restarts reduce the risk of your algorithm missing a trade during periods of downtime. If you enable automatic restarts when you deploy your algorithm and your algorithm fails, your algorithm will try five times to restart. After five unsuccessful restarts, your algorithm won't attempt to restart again. To prevent restarts due to coding bugs, algorithms only automatically restart if they have been running for at least five minutes. -

      - - - -

      Security

      - - -

      - Your code is stored in a database, isolated from the internet. When the code leaves the database, it is compiled and - obfuscated before being deployed to the cloud. If the cloud servers were compromised, this process makes it - difficult to read your strategy. -

      -

      - As we've seen over recent years, there can never be any guarantee of security with online websites. However, we - deploy all modern and common security procedures. We deploy nightly software updates to keep the server up to date - with the latest security patches. We also use SSH key login to avoid reliance on passwords. Internally, we use - processes to ensure only a handful of people have access to the database and we always restrict logins to never use - root credentials. -

      -

      - See our - - Security and IP - - documentation for more information. -

      - - - -

      Automate Deployments

      - - -

      - If you have multiple deployments, use a notebook in the Research Enviroment to - - programmatically deploy, stop or liquidate - - algorithms. -

      - - - -

      Best Practices

      - - -

      - When you have a strategy that shows promising backtest results, consider paper trading the strategy before deploying it with real money. - Many of our - - brokerage integrations - - support a demo environment for paper trading. - If your brokerage supports a demo live environment, deploy a live algorithm that uses it. - Otherwise, - - set the brokerage model - - to your brokerage and then - - deploy your algorithm with the QuantConnect Paper Trading brokerage - - . - The demo environment and reality model of your brokerage provide the most accurate results for live trading. -

      -

      - While paper trading, perform the following stress tests to ensure your algorithm can handle interference: -

      -
        -
      • - Restart your algorithm when the market is open and closed. -
      • +

        + Part 4: Create an Account for the New User +

        +
        1. - - Update and redeploy the algorithm - + On the Setup page, in the left navigation bar, click + + Accounts + .
        2. - - Clone the project - - and deploy the cloned version. + On the Accounts page, click + + +New Account + + .
        3. -
      -

      - If the preceding stress tests pass, load a small amount of money into your real money brokerage account for final validation. - When you're ready to transition to live trading, load the rest of your trading capital into the account that you already validated. -

      - - - -

       

      - -
      -
      -

      Live Trading

      -

      Notifications

      -
      -
      -

      Introduction

      - - -

      - Set up some live trading notifications so that you are notified of market events and your algorithm's performance. We support email, SMS, webhooks, and Telegram notifications. If you set up notifications in the deployment wizard, we will notify you when your algorithm places orders or emits insights. To be notified at other moments in your algorithm, - - create notifications in your code files - - with the - - NotificationManager - - . Lean ignores notifications during backtests. To view the number of notification you can send for free, see the - - Live Trading Notification Quotas - - . -

      - - - -

      Email

      - - -

      - Email notifications can include up to 10KB of text content in the message body. These notifications can be slow since they go through your email provider. - If you don't receive an email notification that you're expecting, check your junk folders. -

      -

      - Follow these steps to set up email notifications in the deployment wizard: -

      -
      1. - On the Deploy Live page, enable at least one of the notification types. + On the New User page, fill in the form with the following information:
      2. -

        - The following table shows the supported notification types: -

        - - - - - - - - - - - - - - - - - -
        - Notification Type - - Description -
        - Order Events - - Notifications for when the algorithm receives - - OrderEvent - - objects -
        - Insights - - Notifications for when the algorithm emits - - Insight - - objects -
        +
          +
        • + Set the account name to something representative (for example, QC). +
        • +
        • + If there is a parent account, select it in the + + Parent + + field. +
        • +
        • + In the + + Parent + + field, select + + Routing (internal sub-account) + + . +
        • +
      3. Click - Email + Create .
      4. - Enter an email address. + On the Account page, click the new account in the table and then click the + + Users + + tab.
      5. - Enter a subject. + In the Users section, click + + +Add + + .
      6. - Click + In the Select Users window, click the new user and then click - Add + Select .
      7. -

        - To add more email notifications, click +

      8. + Click - Add Notification + Save Changes - and then continue from step 2. -

        + . +
      - - - -

      SMS

      - - -

      - SMS notifications are the only type of notification that you don't need an internet connection to receive. They can include up to 1,600 characters of text content in the message body. -

      -

      - Follow these steps to set up SMS notifications in the deployment wizard: -

      +

      + Part 5: Create a FIX Session for the New User +

      1. - On the Deploy Live page, enable at least one of the notification types. + On the Setup page, in the left navigation bar, click + + FIX Sessions + + .
      2. -

        - The following table shows the supported notification types: -

        - - - - - - - - - - - - - - - - - -
        - Notification Type - - Description -
        - Order Events - - Notifications for when the algorithm receives - - OrderEvent - - objects -
        - Insights - - Notifications for when the algorithm emits - - Insight - - objects -
      3. - Click + On the FIX Sessions page, click - SMS + +New FIX Session .
      4. - Enter a phone number. + On the New FIX Sessions page, fill in the form with the following information:
      5. +
          +
        • + Set the + + FIX Session Name + + to something representative (for example, QC). +
        • +
        • + In the + + FIX Type + + field, select + + FIX Order Routing + + . +
        • +
        • + Set the + + Remote Comp ID + + to something representative (for example, qc_alex_id). +
        • +

          + This Id is the remote comp id you will use when deploying live algorithms with LEAN. +

          +

          + The session password you set in this form is the password you will use when deploying live algorithms with LEAN. +

          +
        • + Select the + + Send unsolicited order and fill message + + check box. +
        • +
        • + In the + + Status + + section, deselect the + + Inactive + + check box. +
        • +
      6. Click - Add + Create .
      7. -

        - To add more SMS notifications, click +

      8. + On the FIX Sessions page, click the new FIX session in the table and then click the + + Users + + tab. +
      9. +
      10. + In the Users section, click - Add Notification + +Add - and then continue from step 2. -

        + . +
      11. +
      12. + In the Select Users window, click the new user and then click + + Select + + . +
      13. +
      14. + Click + + Save Changes + + . +
      15. +
      +

      + Part 6: Verify the User Credentials +

      +
        +
      1. + On the Setup page, in the left navigation bar, click + + Users + + . +
      2. +
      3. + On the Users page, click the new user in the table and then review the information under the + + FIX Sessions + + and + + App Keys + + tabs. +
      -

      Telegram

      +

      Deploy Live Algorithms

      - Telegram notifications are automated messages to a Telegram group. + You must have an available + + live trading node + + for each live trading algorithm you deploy.

      - Follow these steps to set up Telegram notifications in the deployment wizard: + Follow these steps to deploy a live algorithm:

      1. - On the Deploy Live page, enable at least one of the notification types. + + Open the project + + you want to deploy. +
      2. +
      3. + Click the + Lightning icon + + Deploy Live + + icon. +
      4. +
      5. + On the Deploy Live page, click the + + Brokerage + + field and then click + + Trading Technologies + + from the drop-down menu. +
      6. +
      7. + Enter your TT user name, account name, remote comp id, session password, app key, and app secret.
      8. - The following table shows the supported notification types: + To get your credentials, see + + Create TT Users + + . +

        +

        + Our TT integration routes orders via the TT FIX 4.4 Connection. + + Contact your TT representative + + to set the exchange where you would like your orders sent. Your account details are not saved on QuantConnect. +
        +

        +

        + Our integration fetches your positions using the REST endpoint, so the app key and app secret are your REST App credentials. +

        +
      9. + Click the + + Environment + + field and then click one of the environments from the drop-down menu. +
      10. +

        + The following table shows the supported environments:

        - Notification Type + Environment Description @@ -39073,283 +38399,340 @@

        Telegram

        - Order Events + Live - Notifications for when the algorithm receives - - OrderEvent - - objects + Trade in the production environment
        - Insights + UAT - Notifications for when the algorithm emits - - Insight - - objects + Trade in the + + User Acceptance Testing + + environment
      11. - Create a new Telegram group. + Click the + + Node + + field and then click the live trading node that you want to use from the drop-down menu.
      12. - Add a bot to your Telegram group. + + (Optional) + + In the + + Data Provider + + section, click + + Show + + and change the data provider or add additional providers.
      13. -

        - To create a bot, chat with @BotFather and follow its instructions. If you want to use our bot, the username is @quantconnect_notifications_bot. -

      14. - On the live deployment wizard, click - - Telegram + If your brokerage account has existing cash holdings, follow these steps ( + + see video + + ): +
      15. +
          +
        1. + In the + + Algorithm Cash State + + section, click + + Show + + . +
        2. +
        3. + Click + + Add Currency + + . +
        4. +
        5. + Enter the currency ticker (for example, USD or CAD) and a quantity. +
        6. +
        +
      16. + + (Optional) + + Set up notifications + .
      17. - Enter your user Id or group Id. + Configure the + + Automatically restart algorithm + + setting.
      18. - Your group Id is in the URL when you open your group chat in the Telegram web interface. For example, the group Id of - - web.telegram.org/z/#-503016366 - - is -503016366. + By enabling + + automatic restarts + + , the algorithm will use best efforts to restart the algorithm if it fails due to a runtime error. This can help improve the algorithm's resilience to temporary outages such as a brokerage API disconnection.

        -
      19. - If you are not using our notification bot, enter the token of your bot. -
      20. Click - Add + Deploy .
      21. -

        - To add more Telegram notifications, click - - Add Notification - - and then continue from step 2. -

      +

      + The deployment process can take up to 5 minutes. When the algorithm deploys, the + + live results page + + displays. If you know your brokerage positions before you deployed, you can verify they have been loaded properly by checking your equity value in the runtime statistics, your cashbook holdings, and your position holdings. +

      -

      Webhooks

      +

       

      + +
      +
      +

      Brokerages

      +

      Wolverine

      +
      +
      +

      Introduction

      - Webhook notifications are an HTTP-POST request to a URL you provide. The request is sent with a timeout of 300s. - You can process these notifications on your web server however you want. For instance, you can inject the content of the - notifications into your server's database or use it to create other notifications on your own server. + QuantConnect enables you to run your algorithms in live mode with real-time market data.

      - Follow these steps to set up webhook notifications in the deployment wizard: + Wolverine Execution Services is a diversified financial institution specializing in proprietary trading, asset management, order execution services, and technology solutions. They are recognized as a market leader in derivatives valuation, trading, and value-added order execution across global Equity, Options, and Futures markets. Their focus on innovation, achievement, and integrity serves the interests of their clients and colleagues. Wolverine Execution Services is headquartered in Chicago, with branch offices in New York, San Francisco, and London. They serve funds that have at least $5M assets under management. +

      +

      + To view the implementation of the Wolverine Execution Services brokerage integration, see the + + Lean.Brokerages.Wolverine repository + + . +

      + + + +

      Account Types

      + + +

      + Wolverine Execution Services supports cash and margin accounts. To set the account type in an algorithm, see the + + Wolverine brokerage model documentation + + . +

      +

      + Create an Account +

      +

      + To create a Wolverine Execution Services account, + + contact their staff + + through the TradeWex website. +

      +

      + Paper Trading +

      +

      + Wolverine Execution Services doesn't support paper trading, but you can follow these steps to simulate it with QuantConnect:

      1. - On the Deploy Live page, enable at least one of the notification types. -
      2. -

        - The following table shows the supported notification types: -

        - - - - - - - - - - - - - - - - - -
        - Notification Type - - Description -
        - Order Events - - Notifications for when the algorithm receives - - OrderEvent - - objects -
        - Insights - - Notifications for when the algorithm emits - - Insight - - objects -
        -
      3. - Click - - Webhook - + In the + + Initialize + + + initialize + + method of your algorithm, + + set the Wolverine brokerage model and your account type + .
      4. - Enter a URL. -
      5. -
      6. - If you want to add header information, click - - Add Header - - and then enter a key and value. -
      7. -

        - Repeat this step to add multiple header keys and values. -

        -
      8. - Click - - Add - + + Deploy your algorithm with the QuantConnect Paper Trading brokerage + .
      9. -

        - To add more webhook notifications, click - - Add Notification - - and then continue from step 2. -

      + + + +

      Asset Classes

      + + +

      + Our Wolverine Execution Services integration supports trading + + US Equities + + . +

      +

      + You may not be able to trade all assets with Wolverine. For example, if you live in the EU, you can't trade US ETFs. Check with your local regulators to know which assets you are allowed to trade. You may need to adjust settings in your brokerage account to live trade some assets. +

      + + + +

      Data Providers

      + + +

      + The + + QuantConnect data provider + + provides US Equities data during live trading. +

      + + + +

      Orders

      + + +

      + We model the Wolverine Execution Services API by supporting order types, but not order updates or extended market hours trading. When you deploy live algorithms, you can + + place manual orders + + through the IDE. +

      - JSON Payload Schema + Order Types

      - The webhook HTTP-POST request sends a JSON payload with the following schema: + The following table describes the available order types for each asset class that our Wolverine Execution Services integration supports:

      - +
      -
      - Property + + Order Type - Description + Equity
      - - ProjectName - + + Market + - - string - -
      - Name of the project. + green check
      - - ProjectId - + + Market on Close + - - integer - -
      - Id of the project. + green check
      - - Insights - + + Limit + - - Insight Array - -
      - Collection of insights emitted since the last notification. See - - Insight - - for the schema of each object. + green check
      - - OrderEvents - + + Stop market + - - OrderEvent Array - -
      - Collection of order events since the last notification. For the conceptual model, see - - OrderEvent in the API Reference - - . The webhook serialization differs from the API response. See the Order Event Schema table below for the webhook-specific schema. + green check
      - - Portfolio - + + Stop limit + - - Portfolio Array - -
      - Current portfolio holdings. See the Portfolio Schema table below for the schema of each object. + green check
      +

      - Order Event Schema + Updates

      - Each object in the + We model the Wolverine Execution Services API by not supporting order updates. +

      +

      + Extended Market Hours +

      +

      + Wolverine Execution Services doesn't support extended market hours trading. If you place an order outside of regular trading hours, the order is invalid. +

      +

      + Order Properties +

      +

      + We model custom order properties from the Wolverine API. The following table describes the members of the - OrderEvents + WolverineOrderProperties - array has the following properties: + object that you can set to customize order execution:

      - - @@ -39357,3667 +38740,2685 @@

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      + Property + Description
      - - OrderId - - - - integer + + TimeInForce -
      - Id of the order. -
      - - Id + + time_in_force - integer + TimeInForce -
      - Id of the order event.
      - - Symbol - + A + + TimeInForce + + instruction to apply to the order. The following instructions are supported: +
        +
      • + + Day + + + DAY + +
      • +
      • + + GoodTilCanceled + + + GOOD_TIL_CANCELED + +
      • +
      - - object - -
      - Symbol information with - - value - - (ticker), - - id + + TimeInForce.GoodTilCanceled - (symbol Id), and - - permtick + + TimeInForce.GOOD_TIL_CANCELED - (permanent ticker) properties.
      - - UtcTime - - - - string + + Exchange -
      - UTC time of the order event in ISO 8601 format. -
      - - Status + + exchange - integer + Exchange -
      - Order status. 1 = Submitted, 2 = PartiallyFilled, 3 = Filled, 5 = Canceled, 6 = Invalid, 7 = CancelPending, 8 = UpdateSubmitted.
      + Defines the exchange name for a particular market. For example, - OrderFee + Exchange.SMART + . - - object - -
      - Order fee with a - - Value - - object containing - - Amount - - (number) and - - Currency - - (string) properties.
      - - FillPrice - - - - number + + ExchangePostFix -
      - Price at which the order was filled. -
      - - FillPriceCurrency + + exchange_post_fix - + string -
      - Currency of the fill price. -
      - - FillQuantity + + str - - number + The exchange post fix to apply if any. For example, if you set + + Exchange -
      - Quantity filled in this event. -
      + + exchange + + to - Direction + Exchange.SMART + , then "-INCA-TX" yields "SMART-INCA-TX". - - integer - -
      - Order direction. 0 = Buy, 1 = Sell, 2 = Hold.
      - - IsAssignment + + LocateBroker - - - boolean + + locate_broker -
      - Whether the order is an option assignment.
      - - Quantity + + string - - - number + + str -
      - Total quantity of the order.
      -

      - Portfolio Schema -

      -

      - Each object in the - - Portfolio - - array has the following properties: -

      - - - - - - - - - - - - - - -
      - Property - - Description -
      - - Ticker - + Identifies the broker source for borrowed stock. - - string - -
      - Ticker of the holding.
      - - Quantity + + PositionSide - - - number + + position_side -
      - Quantity held.
      - - AveragePrice + + OrderPosition? - - - number + + OrderPosition | NoneType -
      - Average price of the holding. This property is not present for cash holdings.
      - - UnreazliedProfit - + Specify the position side in the order direction (buy-to-open, sell-to-close, etc.) instead of the default handling - - number + + null + + + None -
      - Unrealized profit of the holding. This property is not present for cash holdings.
      -

      - Example -

      - The following JSON shows an example webhook payload: + If the + + PositionSide + + + position_side + + is not specified, the engine will determine it from the holdings and the order quantity. For example, if it's a sell short and will send + + PositionSide + + + SellShort + + + SELL_SHORT + + .

      -
      -
      -{
      -  "ProjectName": "My Algorithm",
      -  "ProjectId": 29064060,
      -  "Insights": [
      -    {
      -      "Id": "a0af97846797499dbd515753bdb73a0c",
      -      "GroupId": null,
      -      "SourceModel": "4a834e9d-f10f-4ebe-9fd9-df5fc509b0f8",
      -      "GeneratedTime": 1773767160.2239125,
      -      "CreatedTime": 1773767160.2239125,
      -      "CloseTime": 1777482360.2239125,
      -      "Symbol": "SPY R735QTJ8XC9X",
      -      "Ticker": "SPY",
      -      "Type": "price",
      -      "reference": 671.77,
      -      "ReferenceValueFinal": 0,
      -      "Direction": "up",
      -      "Period": 2592000,
      -      "Magnitude": null,
      -      "Confidence": null,
      -      "Weight": null,
      -      "ScoreIsFinal": false,
      -      "ScoreMagnitude": "0",
      -      "ScoreDirection": "0",
      -      "EstimatedValue": "0",
      -      "Tag": ""
      -    }
      -  ],
      -  "OrderEvents": [
      -    {
      -      "OrderId": 1,
      -      "Id": 1,
      -      "Symbol": {
      -        "value": "AAPL",
      -        "id": "AAPL R735QTJ8XC9X",
      -        "permtick": "AAPL"
      -      },
      -      "UtcTime": "2026-03-17T17:06:00.2239123Z",
      -      "Status": 3,
      -      "OrderFee": {
      -        "Value": {
      -          "Amount": 1,
      -          "Currency": "USD"
      -        }
      -      },
      -      "FillPrice": 254.05,
      -      "FillPriceCurrency": "USD",
      -      "FillQuantity": 130,
      -      "Direction": 0,
      -      "IsAssignment": false,
      -      "Quantity": 130
      -    }
      -  ],
      -  "Portfolio": [
      -    {
      -      "Ticker": "AAPL",
      -      "Quantity": 130,
      -      "AveragePrice": 254.05,
      -      "UnreazliedProfit": -4.9
      -    },
      -    {
      -      "Ticker": "USD",
      -      "Quantity": 34053.79
      -    }
      -  ]
      -}
      -
      -
      -

      Quotas

      +

      Fees

      - The number of email, Telegram, or webhook notifications you can send in each live algorithm for free depends on the tier of your organization. The following table shows the hourly quotas: -

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      - Tier - - Number of Notifications Per Hour -
      - Free - - N/A -
      - Quant Researcher - - 20 -
      - Team - - 60 -
      - Trading Firm - - 240 -
      - Institution - - 3,600 -
      - -

      - If you exceed the hourly quota, each additional email, Telegram, or webhook notification costs 1 - - QuantConnect Credit + Wolverine Execution Services charge $0.005 per share you trade. To view how we model their fees, see + + Fees - (QCC). -

      -

      - Each SMS notification you send to a US or Canadian phone number costs 1 QCC. Each SMS notification you send to an international phone number costs 10 QCC. + .

      -

      Terms of Use

      +

      Margin

      - The notification system can't be used for data distribution. -

      - - - -

       

      - -
      -
      -

      Live Trading

      -

      Results

      -
      -
      -

      Introduction

      - - -

      - The live results page shows your algorithm's live trading performance. Review the results page to see how your algorithm has been performing and to investigate ways to improve it. + We model + + buying power + + and + + margin calls + + to ensure your algorithm stays within the margin requirements. If you have more than $25,000 in your brokerage account, you can use the + + PatternDayTradingMarginModel + + to make use of the 4x intraday leverage and 2x overnight leverage available on most brokerages from the + + PDT rule + + .

      -

      View Live Results

      +

      Slippage

      - The live results page automatically displays when you - - deploy a live algorithm + Orders through Wolverine Execution Services do not experience slippage in backtests and + + QuantConnect Paper Trading - . The page presents the algorithm's equity curve, holdings, trades, logs, server statistics, and much more information. + . In live trading, your orders may experience slippage.

      - Live result interface

      - The content in the live results page updates as your algorithm executes. You can close or refresh the window without interrupting the algorithm because the live trading node processes on our servers. If you close the page, you can - - view all of your live projects + To view how we model Wolverine slippage, see + + Slippage - to open the page again. + .

      -

      Runtime Statistics

      +

      Fills

      - The banner at the top of the live results page displays the performance statistics of your algorithm. -

      - Live runtime statistics -

      - The following table describes the default runtime statistics: -

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      - Statistic - - Description -
      - Equity - - The total portfolio value if all of the holdings were sold at current market rates. Equity equals the sum of cash and the market value of all open positions. -
      - Fees - - The total quantity of fees paid for all the transactions during the algorithm's trading period. Total fees include brokerage commissions, exchange fees, and other transaction costs. -
      - Holdings - - The absolute sum of the items in the portfolio. Holdings represent the total market value of all positions, regardless of whether they are long or short. -
      - Net Profit - - The dollar-value return across the entire trading period. -
      - PSR - - The probability that the estimated Sharpe ratio of an algorithm is greater than a benchmark. -
      - Return - - The rate of return across the entire trading period. -
      - Unrealized - - The amount of profit a portfolio would capture if it liquidated all open positions and paid the fees for transacting and crossing the spread. Unrealized profit becomes realized profit when the positions are closed. -
      - Volume - - The total value of assets traded for all of an algorithm's transactions during the trading period. -
      -

      - To add a custom runtime statistic, see - - Add Statistics + QuantConnect fills market orders immediately and completely in backtests and + + QuantConnect Paper Trading - . + . In live trading, if the quantity of your market orders exceeds the quantity available at the top of the order book, your orders are filled according to what is available in the order book.

      - If you - - stop + To view how we model Wolverine Execution Services order fills, see + + Fills - and redeploy a live algorithm, the runtime statistics are reset. + .

      -

      Built-in Charts

      +

      Settlements

      - The live results page displays the equity curve of your algorithm so that you can analyze its performance in real-time. + If you trade with a margin account, trades settle immediately

      - Live strategy equity candle chart

      - The following table describes the series in the Strategy Equity chart: + To view how we model settlement for Wolverine trades, see + + Settlement + + .

      - - - - - - - - - - - - - - - - - - - - - -
      - Series - - Description -
      - Equity - - The live equity curve of your algorithm. -
      - Out of Sample Backtest - - The - - backtest equity curve - - of your algorithm during the live trading period. -
      - Meta - - Points in time when you deployed your algorithm, stopped your algorithm, and when your algorithm encountered a runtime error. -
      + + + +

      Security and Stability

      + +

      - The following table describes the other charts displayed on the page: + When you deploy live algorithms with Wolverine Execution Services, we don't save your credentials.

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      - Chart -
      -
      - Description -
      - Drawdown - - A time series of equity peak-to-trough value. -
      - Benchmark - - A time series of the benchmark closing price (SPY, by default). -
      - Exposure - - A time series of long and short exposure ratios. -
      - Assets Sales Volume - - A chart showing the proportion of total volume for each traded security. -
      - Portfolio Turnover - - A time series of the portfolio turnover rate. -
      - Portfolio Margin - - A stacked area chart of the portfolio margin usage. For more information about this chart, see - - Portfolio Margin Plots - - . -
      - Asset Plot - - A time series of an asset's price with order event annotations. For more information about these charts, see - - Asset Plots - - . -
      -

      Asset Plots

      +

      Deposits and Withdrawals

      - Asset plots display the trade prices of an asset and the following - - order events - - you have for the asset: + You can deposit and withdraw cash from your brokerage account while you run an algorithm that's connected to the + account. We sync the algorithm's cash holdings with the cash holdings in your brokerage account every day at 7:45 AM + Eastern Time (ET).

      - - - - - - - - - - - - - - - - - - - - - - - - - -
      - Order Event - - Icon -
      - Submissions - - Gray circle -
      - Updates - - Blue circle -
      - Cancellations - - Gray square -
      - Fills and partial fills - - Green (buys) or red (sells) arrows -
      + + + +

      Demo Algorithm

      + +

      - The following image shows an example asset plot for AAPL: + The following algorithm demonstrates the functionality of the Wolverine Execution Services brokerage:

      - AAPL stock price with order events overlaid +
      +
      // Demonstrate Wolverine Execution Services brokerage functionality with an EMA crossover strategy on SPY.
      +public class WolverineDemoAlgorithm : QCAlgorithm
      +{
      +    private ExponentialMovingAverage _fast;
      +    private ExponentialMovingAverage _slow;
      +
      +    public override void Initialize()
      +    {
      +        SetStartDate(2024, 9, 1);
      +        SetEndDate(2024, 12, 31);
      +        SetCash(100000);
      +        SetBrokerageModel(BrokerageName.Wolverine, AccountType.Margin);
      +        var symbol = AddEquity("SPY", Resolution.Daily).Symbol;
      +        _fast = EMA(symbol, 10, Resolution.Daily);
      +        _slow = EMA(symbol, 50, Resolution.Daily);
      +    }
      +
      +    public override void OnData(Slice slice)
      +    {
      +        if (!_slow.IsReady) return;
      +        if (_fast > _slow && !Portfolio.Invested)
      +            SetHoldings("SPY", 1);
      +        else if (_fast < _slow && Portfolio.Invested)
      +            Liquidate();
      +    }
      +}
      +
      # Demonstrate Wolverine Execution Services brokerage functionality with an EMA crossover strategy on SPY.
      +class WolverineDemoAlgorithm(QCAlgorithm):
      +    def initialize(self) -> None:
      +        self.set_start_date(2024, 9, 1)
      +        self.set_end_date(2024, 12, 31)
      +        self.set_cash(100000)
      +        self.set_brokerage_model(BrokerageName.WOLVERINE, AccountType.MARGIN)
      +        symbol = self.add_equity("SPY", Resolution.DAILY).symbol
      +        self._fast = self.ema(symbol, 10, Resolution.DAILY)
      +        self._slow = self.ema(symbol, 50, Resolution.DAILY)
      +
      +    def on_data(self, slice: Slice) -> None:
      +        if not self._slow.is_ready:
      +            return
      +        if self._fast.current.value > self._slow.current.value and not self.portfolio.invested:
      +            self.set_holdings("SPY", 1)
      +        elif self._fast.current.value < self._slow.current.value and self.portfolio.invested:
      +            self.liquidate()
      +
      + + + +

      Deploy Live Algorithms

      + +

      - The order submission icons aren't visible by default. + You must have an available + + live trading node + + for each live trading algorithm you deploy.

      -

      - View Plots -

      - Follow these steps to open an asset plot: + Follow these steps to deploy a live algorithm:

      1. - Open the live results page. + + Open the project + + you want to deploy.
      2. Click the - - Orders + Lightning icon + + Deploy Live - tab. + icon.
      3. - Click the - - - Asset Plot + On the Deploy Live page, click the + + Brokerage - icon that's next to the asset Symbol in the Orders table. + field and then click + + Wolverine Execution Services + + from the drop-down menu.
      4. -
      -

      - Tool Tips -

      -

      - When you hover over one of the order events in the table, the asset plot highlights the order event, displays the asset price at the time of the event, and displays the - - tag - - associated with the event. Consider adding helpful tags to each order event to help with debugging your algorithm. For example, when you cancel an order, you can add a tag that explains the reason for cancelling it. -

      -

      - Adjust the Display Period -

      -

      - The resolution of the asset price time series in the plot doesn't necessarily match the resolution you set when you subscribed to the asset in your algorithm. If you are displaying the entire price series, the series usually displays the daily closing price. However, when you zoom in, the chart will adjust its display period and may use higher resolution data. To zoom in and out, perform either of the following actions: -

      -
        +
      • + Enter your Wolverine Execution Services credentials. +
      • +

        + Your account details are not saved on QuantConnect. +

      • Click the - - 1m + + Node - , - - 3m + field and then click the live trading node that you want to use from the drop-down menu. +
      • +
      • + + (Optional) - , - - 1y + In the + + Data Provider - , or + section, click - All + Show - period in the top-right corner of the chart. + and change the data provider or add additional providers.
      • - Click a point on the chart and drag your mouse horizontally to highlight a specific period of time in the chart. + If your brokerage account has existing cash holdings, follow these steps ( + + see video + + ):
      • -
      - gif that shows the price of AAPL while zooming in and out -

      - If you have multiple order events in a single day and you zoom out on the chart so that it displays the daily closing prices, the plot aggregates the order event icons together as the price on that day. -

      -

      - Order Fill Prices -

      -

      - The plot displays fill order events at the actual fill price of your orders. The fill price is usually not equal to the asset price that displays because of the following reasons: -

      - - - - -

      Custom Charts

      - - -

      - The results page shows the custom charts that you create. -

      -

      - Supported Chart Types -

      -

      - We support the following types of charts: -

      -
      -
      -

      - If you use - - SeriesType.Candle - - and plot enough values, the plot displays candlesticks. However, the - - Plot - - - plot - - method only accepts one numerical value per time step, so you can't plot candles that represent the open, high, low, and close values of each bar in your algorithm. The charting software automatically groups the data points you provide to create the candlesticks, so you can't control the period of time that each candlestick represents. -

      -

      - To create other types of charts, save the plot data in the Object Store and then load it into the Research Environment. In the Research Environment, you can - - create other types of charts with third-party charting packages - - . -

      -

      - Supported Markers -

      -

      - When you create scatter plots, you can set a marker symbol. We support the following marker symbols: -

      -
      -
      -

      - Chart Sampling -

      -

      - Charts are sampled every one and ten minutes. If you create 1-minute resolution custom charts, the IDE charting will downgrade the granularity and display the 10-minutes sampling after a certain amount of samples. -

      -

      - Demonstration -

      +

    - For more information about creating custom charts, see - - Charting + The deployment process can take up to 5 minutes. When the algorithm deploys, the + + live results page - . + displays. If you know your brokerage positions before you deployed, you can verify they have been loaded properly by checking your equity value in the runtime statistics, your cashbook holdings, and your position holdings.

    -

    Adjust Charts

    +

     

    + +
    +
    +

    Brokerages

    +

    FIX Connections

    +
    +
    +

    Introduction

    - You can manipulate the charts displayed on the live results page. -

    -
    - -
    - -
    -
    -

    - Toggle Charts -

    -

    - To display and hide a chart on the live results page, in the - - Select Chart - - section, click the name of a chart. -

    -

    - Toggle Chart Series -

    -

    - To display and hide a series on a chart on the live results page, click the name of a series at the top of a chart. -

    - Demostration of toggling series displays on charts -

    - Adjust the Display Period -

    -

    - To zoom in and out of a time series chart on the live results page, perform either of the following actions: -

    - -

    - If you adjust the zoom on a chart, it affects all of the charts. -

    -

    - After you zoom in on a chart, slide the horizontal bar at the bottom of the chart to adjust the time frame that displays. -

    - Demostration of scrolling for time period on charts -

    - Resize Charts -

    -

    - To resize a chart on the live results page, hover over the bottom-right corner of the chart. When the resize cursor appears, hold the left mouse button and then drag to the desired size. -

    -

    - Move Charts -

    -

    - To move a chart on the live results page, click, hold, and drag the chart title. -

    -

    - Refresh Charts -

    -

    - Refreshing the charts on the live results page resets the zoom level on all the charts. If you refresh the charts while your algorithm is executing, only the data that was seen by the Lean engine after you refreshed the charts is displayed. To refresh the charts, in the - - Select Chart - - section, click the - - reset - - icon. + The Financial Information eXchange (FIX) is the standard electronic communications protocol for front-office messaging. The FIX community includes about 300 firms, including major investment banks.

    -

    Holdings

    +

    Supported Connections

    - The - - Holdings - - tab on the live results page displays your positions and cash. -

    - Live holdings table -

    - The following table describes the properties that display for each of your positions: + The following FIX connections are available on QuantConnect:

    - +
    - - + - - - - - - - - - -
    - Property + + Name - Description + + Integration Implementation + + Model Implementation
    - - Symbol - - - The ticker of the security. -
    - - Average Price - - - The average price that you paid for the position. + Raiffeisen Bank International
    - - Quantity - + + Lean.Brokerages.RaiffeisenBankInternational + - The size of your position. + + RBIBrokerageModel.cs +
    - - Market Value - - - The value of your position if sold with market orders. + + Wolverine +
    - - Unrealized - + + Lean.Brokerages.Wolverine + - The unrealized profit of your position, including fees and spread costs. + + WolverineBrokerageModel.cs +
    -

    - The values in the positions section update as new data points are injected into your algorithm. The cash section displays the quantity of each currency in your algorithm's - - CashBook - - . View the - - Holdings - - tab to see your holdings, - - add security subscriptions - - , and - - place manual orders - - . To view all of your current holdings and active data subscriptions, enable the - - Show All Portfolio - - check box. -

    -

    Orders

    +

    Portfolio State

    - The live results page displays the orders of your algorithm and you can download them to your local machine. + FIX connections do not support fetching the portfolio state. You must provide the cash and holdings on deployment.

    -

    - View in the GUI -

    - To see the orders that your algorithm created, open the live results page and then click the - - Orders - - tab. If there are more than 10 orders, use the pagination tools at the bottom of the Orders Summary table to see all of the orders. Click on an individual order in the Orders Summary table to reveal all of the - - order events + If your brokerage account has existing cash holdings, follow these steps ( + + see video - , which include: + ):

    - -

    - The timestamps in the Order Summary table are based in Eastern Time (ET). -

    -

    - Access the Order Summary CSV -

    + + + + +

     

    + +
    +
    +

    Brokerages

    +

    CFD and FOREX Brokerages

    +
    +
    +

    Introduction

    + +

    - To view the orders data in CSV format, open the live results page, click the - - Orders - - tab, and then click - - Download Orders - - . The content of the CSV file is the content displayed in the Orders Summary table when the table rows are collapsed. The timestamps in the CSV file are based in Coordinated Universal Time (UTC). + QuantConnect enables you to run your algorithms in live mode with real-time market data.

    -

    - Access in Jupyter Notebooks -

    - To programmatically analyze orders, call the - - - ReadBacktestOrders - - - read_live_orders - + QuantConnect integrates with + + OANDA - method or the - - /live/orders/read + for CFD and FOREX trading. OANDA was founded by Dr. Michael Stumm and Dr. Richard Olsen in 1995 with the goal to "transform all aspects of how the world interacts with currencies, whether that be trading or utilizing currency data and information". OANDA provides access to trading Forex and CFDs for clients in over 240 countries and territories with + + no minimum deposit - endpoint. + . OANDA also provides demo accounts, advanced charting tools, and educational content

    + To view the implementation of the OANDA brokerage integration, see the + + Lean.Brokerages.OANDA repository + + .

    -

    Insights

    +

    Account Types

    - The live results page displays the insights of your algorithm and you can download them to your local machine. + OANDA supports margin accounts. To set the account type in an algorithm, see the + + OANDA brokerage model documentation + + .

    - View in the GUI + Create an Account

    - To see the insights your algorithm emit, open the live result page and then click the - - Insights - - tab. If there are more than 10 insights, use the pagination tools at the bottom of the Insights Summary table to see all of the insights. The timestamps in the Insights Summary table are based in Eastern Time (ET). + Follow the + + How to open an account + + page on the OANDA website to open an OANDA account.

    -

    - Download JSON -

    - To view the insights in JSON format, open the live result page, click the - - Insights - - tab, and then click - - Download Insights + You will need your account number and access token to deploy live algorithms. To get your account number, open the + + Account Statement + + page on the OANDA website. Your account number is formatted as + + ###-###-######-### - . The timestamps in the CSV file are based in Coordinated Universal Time (UTC). + . To get your access token, open the + + Manage API Access + + on the OANDA website. +

    +
    +

    + Important note for European Union residents: On March 17th, 2023, OANDA Europe Markets Ltd. ("OEML") closed operations and transferred accounts to OANDA TMS Brokers S.A. ("OANDA TMS") + + + 1 + + + . OANDA TWS does not offer REST API endpoints for live trading. EU residents can trade with OANDA if they can open an account with another member of the OANDA Group, for example, US citizens. +

    +
    +

    + Paper Trading +

    +

    + OANDA supports paper trading. Follow these steps to set up an OANDA paper trading account: +

    +
      +
    1. + + Create an OANDA demo account + + . +
    2. +
    3. + Log in to your demo account. +
    4. +
    5. + On the Account page, in the + + My Services + + section, click + + Manage API Access + + . +
    6. +
    7. + On the Your key to OANDA's API page, click + + Generate + + . +
    8. +

      + Your access token displays. Store it somewhere safe. You need your access token to deploy an algorithm with your paper trading account. +
      +

      +
    9. + In the top navigation bar, click + + My Account + + . +
    10. +
    11. + On the Account page, in the + + Manage Funds + + section, click + + View + + . +
    12. +
    13. + On the My Funds page, in the + + Account Summary + + section, note your v20 Account Number. +
    14. +

      + You need your v20 Account Number to deploy an algorithm with your paper trading account. +

      +
    + + + +

    Asset Classes

    + + +

    + Our OANDA integration supports trading + + Forex + + and + + CFDs + + .

    -

    Logs

    +

    Data Providers

    - The - - Logs - - tab on the live results page displays all of the - - logging statements + The QuantConnect data provider providers + + Forex - and status messages your algorithm creates. Their timestamps in the log file are in Coordinated Universal Time (UTC). The status messages include all of the points in time when your algorithm deployed, encountered an error, sent an order, or quit executing. It's good practice to add logs in live algorithms because then you can see what is happening while it executes. If you stop and redeploy your algorithm, the logs are retained. You can view the log file on the live results page or download them to your local machine. + and + + CFD + + trading data during live trading. +

    + + + +

    Orders

    + + +

    + We model the OANDA API by supporting several order types, a + + TimeInForce + + order instruction, and order updates. When you deploy live algorithms, you can + + place manual orders + + through the IDE.

    - View in the GUI + Order Types

    - To see the log file your algorithm has created, open the live results page and then click the - - Logs - - tab. + The following table describes the available order types for each asset class that our OANDA integration supports:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Order Type + + Forex + + CFD +
    + + Market + + + green check + + green check +
    + + Limit + + + green check + + green check +
    + + Stop market + + + green check + + green check +
    + + Stop limit + + + green check + + green check +
    + +

    + Time In Force +

    - To filter the logs, enter a search string in the - - Filter logs - - field. + We model the + + GoodTilCanceled + + + GOOD_TIL_CANCELED + + + TimeInForce + + from the OANDA API.

    -

    - Download Log File + Updates

    - To download the log file, open the live result page, click the - - Logs - - tab, and then click - - Download Logs - + We model the OANDA API by supporting + + order updates + .

    -

    Project Files

    +

    Fees

    - The live results page displays the project files used to deploy the algorithm. To view the files, click the - - Code - - tab. By default, the - - main.py - - - Main.cs - - file displays. To view other files in the project, click the file name and then select a different file from the drop-down menu. + To view the OANDA trading fees, see the + + Our Charges and Fees + + page on the OANDA website. To view how we model their fees, see + + Fees + + .

    - Algorithm code snippets + + + +

    Margin

    + +

    - To create a new project with the project files used to deploy the algorithm, click - - Clone Algorithm - + We model + + buying power + + and + + margin calls + + to ensure your algorithm stays within the margin requirements. +

    + + + +

    Slippage

    + + +

    + Orders through OANDA do not experience slippage in backtests. In OANDA paper trading and live trading, your orders may experience slippage. +

    +

    + To view how we model OANDA slippage, see + + Slippage + .

    -

    View All Live Projects

    +

    Fills

    - The - - Your Strategies - - section of the - - Strategy Explorer + To view how we model OANDA order fills, see + + Fills - page displays the status of all the live algorithms in your organizations. To view the page, log in to the Algorithm Lab and then, in the left navigation bar, click - - Strategy Explorer - .

    - View live project in algorithm lab -

    Errors

    +

    Settlements

    - If your live algorithm throws a runtime error, it stops executing and we send you an email. If you enabled - - automatic restarts + Trades settle immediately after the transaction +

    +

    + To view how we model settlement for OANDA trades, see + + Settlement - when you deployed your algorithm, your algorithm will try five times to restart. + .

    -

    Share Results

    +

    Security and Stability

    - You can share your live results with anyone, even if they don't have a QuantConnect account. - To share your results, follow these steps: + Note the following security and stability aspects of our OANDA integration.

    -
      -
    1. - In the Share Results section of the live results page, click - - Make Public - - . -
    2. -
    3. - To get a URL that you can share with others, click - - Live Stream - - . -
    4. -
    5. - To get an iframe that you can embed on a website, click - - Embed Code - - . -
    6. -
    +

    + Account Credentials +

    - The - - theme - - parameter of the URL defines the color theme. Valid values are - - darkly - - (dark) or - - chrome - - (light). + When you deploy live algorithms with OANDA, we don't save your credentials.

    +

    + API Outages +

    - To stop sharing your live results, in the Share Results section of the live results page, click - - Make Private - - . + We call the OANDA API to place live trades. Sometimes the API may be down. Check the + + OANDA status page + + to see if the API is currently working.

    -

     

    - -
    -
    -

    Live Trading

    -

    Algorithm Control

    -
    -
    -

    Introduction

    +

    Deposits and Withdrawals

    - The algorithm control features on the live results page let you adjust your algorithm while it is executing live so that you can perform actions that are not written in the project files. The control features let you intervene in the execution of your algorithm and make adjustments. For instance, you can create security subscriptions, place trades, stop the algorithm, and update the algorithm. + You can deposit and withdraw cash from your brokerage account while you run an algorithm that's connected to the + account. We sync the algorithm's cash holdings with the cash holdings in your brokerage account every day at 7:45 AM + Eastern Time (ET).

    -

    Add Security Subscriptions

    +

    Demo Algorithm

    - The live results page enables you to manually create security subscriptions for your algorithm instead of calling the - - Add - - securityType - - - methods in your code files. If you add security subscriptions to your algorithm, you can place manual trades through the IDE without having to edit and redeploy the algorithm. Follow these steps to add security subscriptions: + The following algorithm demonstrates the functionality of the OANDA brokerage: +

    +
    +
    // Demonstrate OANDA brokerage functionality with an EMA crossover strategy on EURUSD.
    +public class OandaDemoAlgorithm : QCAlgorithm
    +{
    +    private Symbol _symbol;
    +    private ExponentialMovingAverage _fast;
    +    private ExponentialMovingAverage _slow;
    +
    +    public override void Initialize()
    +    {
    +        SetStartDate(2024, 9, 1);
    +        SetEndDate(2024, 12, 31);
    +        SetCash(100000);
    +        SetBrokerageModel(BrokerageName.OandaBrokerage, AccountType.Margin);
    +        _symbol = AddForex("EURUSD", Resolution.Daily, Market.Oanda).Symbol;
    +        _fast = EMA(_symbol, 10, Resolution.Daily);
    +        _slow = EMA(_symbol, 50, Resolution.Daily);
    +    }
    +
    +    public override void OnData(Slice slice)
    +    {
    +        if (!_slow.IsReady) return;
    +        if (_fast > _slow && !Portfolio.Invested)
    +            SetHoldings(_symbol, 1);
    +        else if (_fast < _slow && Portfolio.Invested)
    +            Liquidate();
    +    }
    +}
    +
    # Demonstrate OANDA brokerage functionality with an EMA crossover strategy on EURUSD.
    +class OandaDemoAlgorithm(QCAlgorithm):
    +    def initialize(self) -> None:
    +        self.set_start_date(2024, 9, 1)
    +        self.set_end_date(2024, 12, 31)
    +        self.set_cash(100000)
    +        self.set_brokerage_model(BrokerageName.OANDA_BROKERAGE, AccountType.MARGIN)
    +        self._symbol = self.add_forex("EURUSD", Resolution.DAILY, Market.OANDA).symbol
    +        self._fast = self.ema(self._symbol, 10, Resolution.DAILY)
    +        self._slow = self.ema(self._symbol, 50, Resolution.DAILY)
    +
    +    def on_data(self, slice: Slice) -> None:
    +        if not self._slow.is_ready:
    +            return
    +        if self._fast.current.value > self._slow.current.value and not self.portfolio.invested:
    +            self.set_holdings(self._symbol, 1)
    +        elif self._fast.current.value < self._slow.current.value and self.portfolio.invested:
    +            self.liquidate()
    +
    + + + +

    Deploy Live Algorithms

    + + +

    + You must have an available + + live trading node + + for each live trading algorithm you deploy. +

    +

    + Follow these steps to deploy a live algorithm:

    1. - Open your algorithm's - - live results page + + Open the project - . + you want to deploy.
    2. - In the - - Holdings + Click the + Lightning icon + + Deploy Live - tab, click - - Add Security - - . -
    3. -
    4. - Enter the symbol, security type, resolution, leverage, and market of the security you want to add. -
    5. -
    6. - If you want the data for the security to be filled-forward, check the - - Fill Forward - - check box. + icon.
    7. - If you want to subscribe to extended market hours for the security, check the - - Extended Market Hours + On the Deploy Live page, click the + + Brokerage - check box. -
    8. -
    9. - Click + field and then click - Add Security + OANDA - . -
    10. -
    -

    - You can't manually remove security subscriptions from the IDE. -

    - - - -

    Place Manual Trades

    - - -

    - The live results page lets you manually place orders instead of calling the automated methods in your project files. You can use any order type that is supported by the brokerage that you used when deploying the algorithm. To view the supported order types of your brokerage, see the - - Orders - - section of your - - brokerage model - - . Some example situations where it may be helpful to place manual orders instead of stopping and redeploying the algorithm include the following: -

    - -

    - Note that it's not currently possible to cancel manual orders. -

    -

    - Follow these steps to place manual orders: -

    -
    1. - Open your algorithm's - - live results page - - . + Enter your OANDA account Id and access token.
    2. -
    3. - In the - - Holdings - - tab, if the security you want to trade isn't listed, click - - Show All Portfolio +

      + To get your account ID and access token, see the + + Create an Account - . -

    4. -
    5. - If the security you want to trade still isn't listed, - - subscribe to the security + section in the + + Account Types - . -
    6. -
    7. - Click the security you want to trade. -
    8. -
    9. - Click - - Create Order - - or - - Liquidate - - . -
    10. + documentation. Your account details are not saved on QuantConnect. +
      +

    11. - If you clicked - - Create Order + Click the + + Environment - , enter an order quantity. + field and then click one of the environments.
    12. +

      + The following table shows the supported environments: +

      + + + + + + + + + + + + + + + + + +
      + Environment + + Description +
      + Real + + Trade real money with fxTrade +
      + Demo + + Trade paper money with fxTrade Practice +
    13. Click the - Type + Node - field and then click an order type from the drop-down menu. + field and then click the live trading node that you want to use from the drop-down menu.
    14. - Click - - Submit Order + + (Optional) - . -
    15. -
    - - - -

    Liquidate Positions

    - - -

    - The live results page has a - - Liquidate - - button that acts as a "kill switch" to sell all of your portfolio holdings. If your algorithm has a bug in it that caused it to purchase a lot of securities that you didn't want, this button let's you easily liquidate your portfolio instead of placing many manual trades. When you click the - - Liquidate - - button, if the market is open for an asset you hold, the algorithm liquidates it with market orders. If the market is not open, the algorithm places market on open orders. After the algorithm submits the liquidation orders, it stops executing. -

    -

    - Follow these steps to liquidate your positions: -

    -
      -
    1. - Open your algorithm's - - live results page - - . -
    2. -
    3. - Click - - Liquidate + In the + + Data Provider - . -
    4. -
    5. - Click + section, click - Liquidate + Show - again. -
    6. -
    - - - -

    Stop the Algorithm

    - - -

    - The live trading results page has a - - Stop - - button to immediately stop your algorithm from executing. -When you stop a live algorithm, your portfolio holdings are retained. Stop your algorithm if you want to perform any of the following actions: -

    - -

    - Furthermore, if you receive new securities in your portfolio because of a reverse merger, you also need to stop and redeploy the algorithm. -

    -

    - LEAN actively terminates live algorithms when it detects interference outside of the algorithm's control to avoid conflicting race conditions between the owner of the account and the algorithm, so avoid manipulating your brokerage account and placing manual orders on your brokerage account while your algorithm is running. If you need to adjust your brokerage account holdings, stop the algorithm, manually place your trades, and then redeploy the algorithm. -

    -

    - Follow these steps to stop your algorithm: -

    -
    1. - Open your algorithm's - - live results page + + (Optional) + + + Set up notifications .
    2. - Click - - Stop + Configure the + + Automatically restart algorithm - . + setting.
    3. +

      + By enabling + + automatic restarts + + , the algorithm will use best efforts to restart the algorithm if it fails due to a runtime error. This can help improve the algorithm's resilience to temporary outages such as a brokerage API disconnection. +

    4. Click - Stop + Deploy - again. + .
    - - - -

    Update the Algorithm

    - -

    - If you need to adjust your algorithm's project files or - - parameter values - - , stop your algorithm, make your changes, and then redeploy your algorithm. You can't adjust your algorithm's code or parameter values while your algorithm executes. -

    -

    - When you stop and redeploy a live algorithm, your project's + The deployment process can take up to 5 minutes. When the algorithm deploys, the - live results - - is retained between the deployments. To clear the live results history, - - clone the project - - and then redeploy the cloned version of the project. -

    -

    - To update parameters in live mode, add a - - Schedule Event - - that - - downloads + live results page - a remote file and uses its contents to update the parameter values. + displays. If you know your brokerage positions before you deployed, you can verify they have been loaded properly by checking your equity value in the runtime statistics, your cashbook holdings, and your position holdings.

    -
    -
    private Dictionary _parameters = new();
    -public override void Initialize()
    -{
    -    if (LiveMode)
    -    {
    -        Schedule.On(
    -            DateRules.EveryDay(),
    -            TimeRules.Every(TimeSpan.FromMinutes(1)),
    -            ()=>
    -            {
    -                var content = Download(urlToRemoteFile);
    -                // Convert content to _parameters
    -            });
    -    }
    -}
    -
    def initialize(self):
    -    self.parameters = { }
    -    if self.live_mode:
    -        def download_parameters():
    -            content = self.download(url_to_remote_file)
    -            # Convert content to self.parameters
    -
    -        self.schedule.on(self.date_rules.every_day(), self.time_rules.every(timedelta(minutes=1)), download_parameters)
    -
    -
    -

    Receive Commands

    +

     

    + +
    +
    +

    Brokerages

    +

    Unsupported Brokerages

    +
    +
    +

    Introduction

    - Commands enable you to manually call methods in your live algorithm as it runs. - You can command live projects to update your algorithm state, place orders, or run any other logic. - Commands are different from - - notifications - - because - notifications enable you to send information - - out - - of your algorithm while commands enable you to send information - - into - - your algorithm. -

    -

    - For more information, see the - - Commands + New brokerages can be added if the brokerage has an API that is popular, stable, and officially supported by the brokerage. To add a new brokerage to the platform, + + contact us - section of the Writing Algorithms documentation. + .

     

    - +
    -
    +

    Live Trading

    -

    Reconciliation

    +

    Deployment

    Introduction

    - Algorithms usually perform differently between backtesting and live trading over the same time period. Backtests are simulations where we model reality as close as possible, but the modeling isn't always perfect. To measure the performance differences, we run an out-of-sample (OSS) backtest in parallel to all of your live trading deployments. The - - live results page + Deploy your trading algorithms live to receive real-time market data and submit orders on our co-located servers racked in + + Equinix - displays the live equity curve and the OOS backtest equity curve of your algorithms. -

    - The live and OSS backtest equity curves of an Alpha -

    - If your algorithm is perfectly reconciled, it has an exact overlap between its live and OOS backtest equity curves. Deviations mean that the performance of your algorithm has differed between the two execution modes. Several factors can contribute to the deviations. + . As your algorithms run, you can view their performance in the Algorithm Lab. Since the algorithms run in QuantConnect Cloud, you can close the IDE without interrupting the execution of your algorithms. Deploying your algorithms to live trading through QuantConnect is cheaper than purchasing server space, setting up data feeds, and maintaining the software on your own. To deploy your algorithms on QuantConnect, you just need to follow the + + Deploy Live Algorithms + + section in the + + guide of your brokerage + + .

    -

    Differences From Data

    +

    Resources

    - The data that your algorithm uses can cause differences between backtesting and live trading performance. -

    -

    - Data Providers -

    -

    - QuantConnect provides high-quality historical data for - - all supported asset classes - - . However, - - our live data provider - - lacks data feeds for equity options, index options, and futures options. If your algorithm trades these asset classes, you must rely on brokerage or third-party data feeds. -

    -

    - Look-Ahead Bias -

    -

    - The - - Time Frontier + Live trading nodes enable you to deploy live algorithms to our professionally-managed, co-located servers racked in + + Equinix - minimizes the risk of look-ahead bias in backtests, but it does not completely eliminate the risk of look-ahead bias. For instance, if you use a - - custom dataset + . + You need a live trading node for each algorithm that you deploy to our co-located servers. + Several models of live trading nodes are available. + More powerful live trading nodes allow you to run algorithms with larger universes and give you + + more time for machine learning training - that contains look-ahead bias, your algorithm's live and backtest equity curves may deviate. To avoid look-ahead bias with custom datasets, set a - - Period - - - period - - on your custom data points so that your algorithm receives the data points after the - - Time + Period - - - time + period - . + Each security subscription requires about 5MB of RAM. The following table shows the specifications of the live trading node models:

    -

    - Discrete Time Steps -

    -

    - In backtests, we inject data into your algorithm at predictable times, according to the data resolution. In live trading, we inject data into your algorithm when new data is available. Therefore, if your algorithm has a condition with a specific time (i.e. time is 9:30:15), the condition may work in backtests but it will always fail in live trading since live data has microsecond precision. To avoid issues, either use a time range in your condition (i.e. 9:30:10 < time < 9:30:20), use a rounded time, or use a Scheduled Event. -

    -

    - Custom Data Emission Times -

    -

    - Custom data is often timestamped to midnight, but the data point may not be available in reality until several days after that point. If your custom dataset is prone to this delay, your backtest may not fetch the same data at the same time or frequency that your live trading algorithm receives the data, leading to deviations between backtesting and live trading. To avoid issues, ensure the timestamps of your custom dataset are the times when the data points would be available in reality. -

    -

    - In backtesting, LEAN and custom data are perfectly synchonized. In live trading, daily and hourly data from a custom data source are not because of the frequency that LEAN checks the data source depends on the - - resolution - - argument. The following table shows the polling frequency of each resolution: -

    - +
    - + + + - - + - - + + + + + + + + + + + + + + + + + +
    - Resolution + + Name - Update Frequency + Number of Cores + + Processing Speed (GHz) + + RAM (GB) + + GPU
    - Daily + L-MICRO - Every 30 minutes + 1
    - Hour + 2.6 - Every 30 minutes + 0.5 + + 0
    - Minute + L1-1 - Every minute + 1
    - Second + 2.6 - Every second + 1 + + 0
    - Tick + L1-2 - Constantly checks for new data + 1 + + 2.6 + + 2 + + 0 +
    + L2-4 + + 2 + + 2.6 + + 4 + + 0 +
    + L8-16-GPU + + 8 + + 3.1 + + 16 + + 1/2
    -

    - Split Adjustment of Indicators -

    +

    - Backtests use adjusted price data by default. Therefore, if you don't change the - - data normalization mode - - , the indicators in your backtests are updated with adjusted price data. In contrast, if a split or dividend occurs in live trading, your indicators will temporarily contain price data from before the corporate event and price data from after the corporate event. If this occurs, your indicators will produce different signals in your backtests compared to your live trading deployment. To avoid issues, - - reset and warm up your indicators + Refer to the + + Pricing - when your algorithm receives a corporate event. -

    -

    - Tick Slice Sizes -

    -

    - In backtesting, we collect ticks into slices that span 1 millisecond before injecting them into your algorithm. In live trading, we collect ticks into slices that span up to 70 milliseconds before injecting them into your algorithm. This difference in slice sizes can cause deviations between your algorithm's live and OOS backtest equity curves. To avoid issues, ensure your strategy logic is compatible with both slice sizes. -

    -

    - Opening and Closing Auctions -

    -

    - The opening and closing price of the day is set by very specific opening and closing auction ticks. When a stock like Apple is - listed, it’s listed on Nasdaq. The open auction tick on Nasdaq is the price that’s used as the official open of the - day. NYSE, BATS, and other exchanges also have opening auctions, but the only official opening price for Apple is the - opening auction on the exchange where it was listed. + page to see the price of each live trading node model.

    - We set the opening and closing prices of the first and last bars of the day to the official auction prices. This - process is used for second, minute, hour, and daily bars for the 9:30 AM and 4:00 PM Eastern Time (ET) prices. - In contrast, other platforms might not be using the correct opening and closing prices. + To view the status of all of your organization's nodes, see the + + Resources panel + + of the IDE. + When you deploy an algorithm, it uses the best-performing resource by default, but you can + + select a specific resource to use + + .

    - The official auction prices are usually emitted 2-30 seconds after the market open and close. We do our best - to use the official opening and closing prices in the bars we build, but the delay can be so large that there - isn't enough time to update the opening and closing price of the bar before it's injected into your algorithms. For example, - if you subscribe to second resolution data, we wait until the end of the second for the opening price but most - second resolution data won’t get the official opening price. If you subscribe to minute resolution data, we wait until - the end of the minute for the opening auction price. Most of the time, you’ll get the actual opening auction price - with minute resolution data, but there are always exceptions. Nasdaq and NYSE can have delays in publishing the - opening auction price, but we don’t have control over those issues and we have to emit the data on time so that you - get the bar you are expecting. + The CPU nodes are available on a fair usage basis while the GPU nodes can be shared with a maximum of two members. + Depending on the server load, you may use all of the GPU's processing power. + GPU nodes perform best on repetitive and highly-parallel tasks like training machine learning models. + It takes time to transfer the data to the GPU for computation, so if your algorithm doesn't train machine learning models, the extra time it takes to transfer the data can make it appear that GPU nodes run slower than CPU nodes.

    -

    - Data Updates -

    + + + +

    Node Quotas

    + +

    - Data issues are incorrect or missing values in a dataset. These issues are generally a result of human error or from mistakes in the data collection process. Data issues can be reported by any QuantConnect member. When data issues are reported and verified, our Data Team works to quickly resolve them. Thanks to the communal efforts of the QuantConnect members, the QuantConnect data is reviewed and fixed by over 250,000 people, giving you a very high-quality source of data. + You need a live trading node for each simultaneous algorithm that you deploy. We do not support sub algorithms or sharing a server with multiple algorithms. The tier of your organization determines the number of live trading nodes the organization can have. The following number of live trading nodes are available for each tier:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Tier + + Node Quota +
    + Free + + 0 +
    + Quant Researcher + + 2 +
    + Team + + 10 +
    + Trading Firm + + Unlimited +
    + Institution + + Unlimited +
    +

    - Data updates change backtest results and, consequently, cause differences between backtesting running with fixed data and live trading. For more information on common data issues, please refer to the - - data issues documentation - - . + To deploy multiple algorithms using a single brokerage, create sub-accounts in your brokerage account so that each algorithm has its own set of brokerage connection credentials.

    -

    Differences From Modeling

    +

    Ram Allocations

    - The modeling that your algorithm uses can cause differences between backtesting and live trading performance. -

    -

    - Reality Modeling Error -

    -

    - We provide - - brokerage models - - to model fees, slippage, and order fills in backtests. However, these model predictions may not always match the fees that your live algorithm incurs, leading to deviations between backtesting and live trading. You can adjust the reality models that your algorithm uses to more accurately reflect the specific assets that you're trading. For more information about reality models, see - - Reality Modeling - - . + Members often use 8-32GB of RAM in backtesting and are concerned that their algorithms will not work in live trading since live trading nodes have 512MB to 4GB of RAM. Backtesting nodes have more RAM because data is injected into your algorithm roughly 100,000x faster during backtests than live trading. You use more RAM in backtesting because many data objects are cached to achieve such fast speed. In live trading, 512MB to 4GB of RAM is sufficient for almost all use cases.

    -

    - Market Impact -

    + + + +

    Wizard

    + +

    - We don't currently model market impact. So, if you are trading large orders, your fill prices can be better during backtesting than live trading, causing deviations between backtesting and live trading. To avoid issues, implement a - - custom fill model + Use the deployment wizard in the Algorithm Lab to + + deploy your algorithms to live trading - in your backtests that incorporates market impact. -

    -

    - Fills -

    -

    - In backtests, orders fill immediately. In live trading, they are sent to your brokerage and take about half a second to execute. If you fill an order in a backtest with stale data, deviations between backtesting and live trading can occur because the order is filled at a price that is likely different from the real market price. Stale order fills commonly occur in backtests when you create a Scheduled Event with an incompatible data resolution. For instance, if you subscribe to hourly data, place a Scheduled Event for 11:15 AM, and fill an order during the Scheduled Event, the order will fill at a stale price because the data between 11:00 AM and 11:15 AM is missing. To avoid stale fills, only place orders when your algorithm receives price data. -
    -

    -

    - In live trading, your brokerage provides the fill price of your orders. Since the backtesting brokerage models do not know the price at which live orders are filled, the fill price of backtest orders is based on - the best price available in the current backtesting data. Similarly, limit orders can fill at different prices between backtesting and live trading. In backtesting, limit orders fill as soon as the limit price is hit. In live trading, your brokerage may fill the same limit order at a different price or fail to fill the order, depending on the position of your order in their order book. -
    + . The deployment wizard lets you select a brokerage, enter your brokerage credentials, select a data provider, select a live trading node, set up notifications, and configure automatic algorithm restarts.

    -

    - Borrowing Costs -

    + Deploy live wizard interface

    - By default, LEAN doesn't simulate the - - cost of borrowing + Most of the brokerages automatically load your cash holdings, position holdings, and submitted orders so that you can view your portfolio state on the + + live results page - shorts in backtests, but you can enable them by setting a margin interest model. + . For brokerages that don't automatically load your holdings, you can enter your cash and position holdings in the deployment wizard.

    -

    Differences From Brokerage

    +

    Unsupported Assets

    - The brokerage that your algorithm uses can cause differences between backtesting and live trading performance. -

    -

    - Portfolio Allocations on Small Accounts -

    -

    - If you trade a small portfolio, it's difficult to achieve accurate portfolio allocations because shares are usually sold in whole numbers. For instance, you likely can't allocate exactly 10% of your portfolio to a security. You can use fractional shares to achieve accurate portfolio allocations, but not all brokerages support fractional shares. To get the closest results when backtesting and live trading over the same period, ensure both algorithms have the same starting cash balance. -

    -

    - Different Backtest Parameters -

    -

    - If you don't start your backtest and live deployment on the same date with the same holdings, deviations can occur between backtesting and live trading. To avoid issues, ensure your backtest parameters are the same as your live deployment. -

    -

    - Non-deterministic State From Algorithm Restarts -

    -

    - If you stop and redeploy your live trading algorithm, it needs to restart in a stateful way or else deviations can occur between backtesting and live trading. To avoid issues, redeploy your algorithm in a stateful way using the - - SetWarmUp - - - set_warm_up - - and - - History - - - history - - methods. Furthermore, use the Object Store to save state information between your live trading deployments. -

    -

    - Existing Portfolio Securities -

    -

    - If you deploy your algorithm to live trading with a brokerage account that has existing holdings, your live trading equity curve reflects your existing positions, but the backtesting curve won't. Therefore, if you have existing positions in your brokerage account when you deploy your algorithm to live trading, deviations will occur between backtesting and live trading. To avoid issues, deploy your algorithm to live trading using a separate brokerage account or subaccount that does not have existing positions. -
    -

    -

    - Brokerage Limitations -

    -

    - We provide brokerage models that support specific order types and model your buying power. In backtesting, we simulate your orders  with the brokerage model you select. In live trading, we send your orders to your brokerage for execution. If the brokerage model that you use in backtesting is not the same brokerage that you use in live trading, deviations may occur between backtesting and live trading. The deviations can occur if your live brokerage doesn't support the order types that you use or if the backtesting brokerage models your buying power with a different methodology than the real brokerage. To avoid brokerage model issues, set the - - brokerage model + If you have unsupported assets in your brokerage account when you deploy, Lean can't calculate the portfolio value correctly, so margin calculations are wrong. To avoid issues, if your account has unsupported assets, Lean automatically exits on deployment. For a list of supported assets, see the asset class + + dataset listing - in your backtest to the same brokerage that you use in live trading. -
    + .

    -

     

    - -
    -
    -

    Live Trading

    -

    Risks

    -
    -
    -

    Introduction

    +

    Automatic Restarts

    - There are risks associated with deploying your algorithms to live trading. Strategy, portfolio, market, counterparty, operational, and error risks can cause you to lose capital. Some of these risks can be out of your control, but there are ways that you can mitigate them. + Automatic restarts use best efforts to restart your algorithm if it fails due to a runtime error or an API disconnection. Automatic restarts reduce the risk of your algorithm missing a trade during periods of downtime. If you enable automatic restarts when you deploy your algorithm and your algorithm fails, your algorithm will try five times to restart. After five unsuccessful restarts, your algorithm won't attempt to restart again. To prevent restarts due to coding bugs, algorithms only automatically restart if they have been running for at least five minutes.

    -

    Strategy

    +

    Security

    - Strategy risk is the risk that results from designing a strategy based on a statistical model. If you ignore the underlying assumptions of the statistical model, you are exposed to strategy risk. Even if you test that the model assumptions are held, if the market environment changes, the new environment may violate the underlying assumptions of the model after you have deployed it to live trading. Additionally, your strategy development process may be prone to overfitting, survivorship bias, or - - look-ahead bias + Your code is stored in a database, isolated from the internet. When the code leaves the database, it is compiled and + obfuscated before being deployed to the cloud. If the cloud servers were compromised, this process makes it + difficult to read your strategy. +

    +

    + As we've seen over recent years, there can never be any guarantee of security with online websites. However, we + deploy all modern and common security procedures. We deploy nightly software updates to keep the server up to date + with the latest security patches. We also use SSH key login to avoid reliance on passwords. Internally, we use + processes to ensure only a handful of people have access to the database and we always restrict logins to never use + root credentials. +

    +

    + See our + + Security and IP - , which increases your exposure to strategy risk. To address strategy risk, use rolling parameters when training your statistical models and perform the required statistical tests before training such models. + documentation for more information.

    -

    Portfolio

    +

    Automate Deployments

    - Portfolio risk is the risk associated with your portfolio as a whole. For instance, you're exposed to portfolio risk if you allocate too much of your portfolio to a particular factor, the - - capacity + If you have multiple deployments, use a notebook in the Research Enviroment to + + programmatically deploy, stop or liquidate - of your trading strategies reduces, or the correlation of the strategies in your portfolio increases. To address portfolio risk, diversify your portfolio among multiple factors, monitor the rolling capacity of your trading strategies, and frequently check the correlation of your trading strategies. -
    + algorithms.

    -

    Market

    +

    Best Practices

    - Market risk, also known as systematic risk, is the risk that the value of your portfolio will decrease due to the value of the entire market decreasing. Market risk is caused by changes in interest rates, changes in currency exchange rates, geopolitical events, natural disasters, wars, terrorist attacks, and economic recessions. Additionally, central bank announcements and changes to monetary policy can increase overall market volatility and market risk. To address market risk, you can increase diversification, reduce your portfolio - - beta + When you have a strategy that shows promising backtest results, consider paper trading the strategy before deploying it with real money. + Many of our + + brokerage integrations - , hedge your positions with put Options, or hedge against volatility with volatility index securities. + support a demo environment for paper trading. + If your brokerage supports a demo live environment, deploy a live algorithm that uses it. + Otherwise, + + set the brokerage model + + to your brokerage and then + + deploy your algorithm with the QuantConnect Paper Trading brokerage + + . + The demo environment and reality model of your brokerage provide the most accurate results for live trading. +

    +

    + While paper trading, perform the following stress tests to ensure your algorithm can handle interference: +

    + +

    + If the preceding stress tests pass, load a small amount of money into your real money brokerage account for final validation. + When you're ready to transition to live trading, load the rest of your trading capital into the account that you already validated.

    -

    Counterparty

    +

     

    + +
    +
    +

    Live Trading

    +

    Notifications

    +
    +
    +

    Introduction

    - Counterparty risk is the risk that a counterparty with which you engage won't pay an obligation that they have made with you. Most commonly, counterparty risk is associated with the risk that your brokerage goes out of business without returning the trading capital that you have in your brokerage account. Brokerages can go bankrupt just like any other business. To address counterparty risk, diversify your portfolio across multiple brokers that have a strong reputation. If you allocate your capital across multiple brokers and one of them goes out of business, you won't lose all of your trading capital. + Set up some live trading notifications so that you are notified of market events and your algorithm's performance. We support email, SMS, webhooks, and Telegram notifications. If you set up notifications in the deployment wizard, we will notify you when your algorithm places orders or emits insights. To be notified at other moments in your algorithm, + + create notifications in your code files + + with the + + NotificationManager + + . Lean ignores notifications during backtests. To view the number of notification you can send for free, see the + + Live Trading Notification Quotas + + .

    -

    Operational

    +

    Email

    - Operational risks are the risks within your fund that relate to business operations, such as business risks, regulatory risks, trading infrastructure risks, and the risks of employees committing fraud or quitting. Operational risks are a result of the nature of a trading business, having employees, and regulatory changes. To address operational risks, stay up to date on potential regulatory changes, only hire employees that have signed contracts that protect your firm, use open-source trading infrastructure that's maintained by experts (Lean), and use co-located servers so that you don't need to tend to hardware failures and internet outages. + Email notifications can include up to 10KB of text content in the message body. These notifications can be slow since they go through your email provider. + If you don't receive an email notification that you're expecting, check your junk folders. +

    +

    + Follow these steps to set up email notifications in the deployment wizard:

    +
      +
    1. + On the Deploy Live page, enable at least one of the notification types. +
    2. +

      + The following table shows the supported notification types: +

      + + + + + + + + + + + + + + + + + +
      + Notification Type + + Description +
      + Order Events + + Notifications for when the algorithm receives + + OrderEvent + + objects +
      + Insights + + Notifications for when the algorithm emits + + Insight + + objects +
      +
    3. + Click + + Email + + . +
    4. +
    5. + Enter an email address. +
    6. +
    7. + Enter a subject. +
    8. +
    9. + Click + + Add + + . +
    10. +

      + To add more email notifications, click + + Add Notification + + and then continue from step 2. +

      +
    -

    Error

    +

    SMS

    - Error risk is the risk associated with errors occurring in your strategy logic or trading infrastructure. Error risks occur because bugs naturally arise in trading algorithms and the underlying engine that the algorithms use to execute. The Lean trading engine has been under constant development for over 10 years, but there are always more improvements that can be implemented. To address error risk, backtest your trading algorithm before deploying it live to test if it has coding errors, stay up to date on the - - Lean GitHub Issues - - , and have close access to your email at all times. If your trading algorithm fails, we notify you through email. You can also enable - - automatic restarts - - when you deploy algorithms. -

    - - - -

     

    - -
    -
    -

    Optimization

    - -
    -
    - - -
    -
    -

    - Parameter optimization is the process of finding the optimal algorithm parameters to maximize or minimize an objective function. For instance, you can optimize your indicator parameters to maximize the - - Sharpe ratio - - that your algorithm achieves over a backtest. Optimization can help you adjust your strategy to achieve better backtesting performance, but be wary of overfitting. If you select parameter values that model the past too closely, your algorithm may not be robust enough to perform well using out-of-sample data. -

    -
    - - -
    - - - -

     

    - -
    -
    -

    Optimization

    -

    Getting Started

    -
    -
    -

    Introduction

    - - -

    - Parameter optimization is the process of finding the optimal algorithm parameters to maximize or minimize an objective function. For instance, you can optimize your indicator parameters to maximize the - - Sharpe ratio - - that your algorithm achieves over a backtest. Optimization can help you adjust your strategy to achieve better backtesting performance, but be wary of overfitting. If you select parameter values that model the past too closely, your algorithm may not be robust enough to perform well using out-of-sample data. -

    - - - -

    Launch Optimization Jobs

    - - -

    - The following video demonstrates how to launch an optimization job: + SMS notifications are the only type of notification that you don't need an internet connection to receive. They can include up to 1,600 characters of text content in the message body.

    - Launch an optimization job

    - You need the following to optimize parameters: + Follow these steps to set up SMS notifications in the deployment wizard:

    - +

    + To add more SMS notifications, click + + Add Notification + + and then continue from step 2. +

    + + + + +

    Telegram

    + +

    - Follow these steps to optimize parameters: + Telegram notifications are automated messages to a Telegram group. +

    +

    + Follow these steps to set up Telegram notifications in the deployment wizard:

    1. - - Open the project - - that contains the parameters you want to optimize. -
    2. -
    3. - In the top-right corner of the IDE, click the - Cloud optimization icon - - Optimize - - icon. -
    4. -
    5. - On the Optimization page, in the - - Parameter & Constraints - - section, enter the name of the parameter to optimize. + On the Deploy Live page, enable at least one of the notification types.
    6. - The parameter name must match a parameter name in the Project panel. + The following table shows the supported notification types:

      + + + + + + + + + + + + + + + + + +
      + Notification Type + + Description +
      + Order Events + + Notifications for when the algorithm receives + + OrderEvent + + objects +
      + Insights + + Notifications for when the algorithm emits + + Insight + + objects +
    7. - Enter the minimum and maximum parameter values. -
    8. -
    9. - Click the - - gear - - icon next to the parameter and then enter a step size. + Create a new Telegram group.
    10. - If you want to add another parameter to optimize, click - - Add Parameter - - . + Add a bot to your Telegram group.
    11. - You can optimize a maximum of three parameters. To optimize more parameters, - - run local optimizations with the CLI - - . + To create a bot, chat with @BotFather and follow its instructions. If you want to use our bot, the username is @quantconnect_notifications_bot.

    12. - If you want to add - - optimization constraints - - , follow these steps: -
    13. -
        -
      1. - Click - - Add Constraint - - . -
      2. -
      3. - Click the - - target - - field and then select a - - target - - from the drop-down menu. -
      4. -
      5. - Click the - - operation - - field and then an operation from the drop-down menu. -
      6. -
      7. - Enter a constraint value. -
      8. -
      -
    14. - In the - - Estimated Number and Cost of Backtests - - section, click an - - optimization node - - and then select a maximum number of nodes to use. -
    15. -
    16. - In the - - Strategy & Target - - section, click the - - Choose Optimization Strategy + On the live deployment wizard, click + + Telegram - field and then select a - - strategy - - from the drop-down menu. + .
    17. - Click the - - Select Target - - field and then select a target from the drop-down menu. + Enter your user Id or group Id.
    18. - The target (also known as objective) is the performance metric the optimizer uses to compare the backtest performance of different parameter values. + Your group Id is in the URL when you open your group chat in the Telegram web interface. For example, the group Id of + + web.telegram.org/z/#-503016366 + + is -503016366.

    19. - Click - - Maximize - - or - - Minimize - - to maximize or minimize the optimization target, respectively. + If you are not using our notification bot, enter the token of your bot.
    20. Click - Launch Optimization + Add .
    21. - The - - optimization results page - - displays. As the optimization job runs, you can close or refresh the window without interrupting the job because the nodes are processing on our servers. -

      -

      - To abort a running optimization job, in the Status panel, click - - Abort - - and then click + To add more Telegram notifications, click - Yes + Add Notification - . + and then continue from step 2.

    -

    View Individual Backtest Results

    +

    Webhooks

    - The optimization results page displays a Backtests table that includes all of the backtests that ran during the optimization job. The table lists the parameter values of the backtests in the optimization job and their resulting values for the objectives. -

    - Individual backtest result result -

    - Open the Backtest Results Page -

    -

    - To open the - - backtest result page - - of one of the backtests in the optimization job, click a backtest in the table. -

    -

    - Download the Table -

    -

    - To download the table, right-click one of the rows, and then click - - Export > CSV Export - - . + Webhook notifications are an HTTP-POST request to a URL you provide. The request is sent with a timeout of 300s. + You can process these notifications on your web server however you want. For instance, you can inject the content of the + notifications into your server's database or use it to create other notifications on your own server.

    -

    - Filter the Table -

    - Follow these steps to apply filters to the Backtests table: + Follow these steps to set up webhook notifications in the deployment wizard:

    1. - On the right edge of the Backtests table, click + On the Deploy Live page, enable at least one of the notification types. +
    2. +

      + The following table shows the supported notification types: +

      + + + + + + + + + + + + + + + + + +
      + Notification Type + + Description +
      + Order Events + + Notifications for when the algorithm receives + + OrderEvent + + objects +
      + Insights + + Notifications for when the algorithm emits + + Insight + + objects +
      +
    3. + Click - Filters + Webhook .
    4. - Click the name of the column to which you want the filter to be applied. + Enter a URL.
    5. - If the column you selected is numerical, click the - - operation - - field and then select one of the operations from the drop-down menu. -
    6. -
    7. - Fill the fields below the operation you selected. -
    8. - Optimization results table -
    -

    - Toggle Table Columns -

    -

    - Follow these steps to hide and show columns in the Backtests table: -

    -
      -
    1. - On the right edge of the Backtests table, click + If you want to add header information, click - Columns - - . -
    2. -
    3. - Select the columns you want to include in the Backtests table and deselect the columns you want to exclude. -
    4. -
    -

    - Sort the Table Columns -

    -

    - In the Backtests table, click one of the column names to sort the table by that column. -

    - - - -

    View All Optimizations

    - - -

    - Follow these steps to view all of the optimization results of a project: -

    -
      -
    1. - - Open the project - - that contains the optimization results you want to view. -
    2. -
    3. - At the top of the IDE, click the - - - Results + Add Header - icon. + and then enter a key and value.
    4. - A table containing all of the backtest and optimization results for the project is displayed. If there is a - - play - - icon to the left of the name, it's a - - backtest result - - . If there is a - - fast-forward - - icon next to the name, it's an - - optimization result - - . -
      + Repeat this step to add multiple header keys and values.

      - All backtest table view -
    5. - - (Optional) - - In the top-right corner, select the - - Show - - field and then select one of the options from the drop-down menu to filter the table by backtest or optimization results. -
    6. -
    7. - - (Optional) - - In the bottom-right corner, click the - - Hide Error - - check box to remove backtest and optimization results from the table that had a runtime error. -
    8. -
    9. - - (Optional) - - Use the pagination tools at the bottom to change the page. -
    10. -
    11. - - (Optional) - - Click a column name to sort the table by that column. -
    12. -
    13. - Click a row in the table to open the results page of that backtest or optimization. -
    14. -
    -

    - Rename Optimizations -

    -

    - We give an arbitrary name (for example, "Smooth Apricot Chicken") to your optimization result files, but you can follow these steps to rename them: -

    -
      -
    1. - Hover over the optimization you want to rename and then click the - - pencil - - icon that appears. -
    2. - Rename optimization instance -
    3. - Enter the new name and then press - - Enter - - . -
    4. -
    -

    - Delete Optimizations -

    -

    - Hover over the optimization you want to delete and then click the - - trash can - - icon that appears to delete the optimization result. -

    - Delete optimization result - - - -

    On-Premises Optimizations

    - - -

    - For information about on-premises optimizations with - - Local Platform - - , see - - Getting Started - - . -

    - - - -

    Get Optimization Id

    - - -

    - To get the optimization Id, - - open the optimization result page - - and then scroll down to the table that shows the - - individual backtest results - - . The optimization Id is at the top of the table. An example optimization Id is O-696d861d6dbbed45a8442659bd24e59f. -

    - - - -

     

    - -
    -
    -

    Optimization

    -

    Parameters

    -
    -
    - - -
    - -
    - -
    -
    - - - -

    Introduction

    - - -

    - Parameters are project variables that your algorithm uses to define the value of internal variables like indicator arguments or the length of lookback windows. -

    -

    - Parameters are stored outside of your algorithm code, but we inject the values of the parameters into your algorithm when you - - launch an optimization job - - . The optimizer adjusts the value of your - - project parameters - - across a range and step size that you define to minimize or maximize an objective function. To optimize some parameters, add some parameters to your project and add the - - GetParameter - - - get_parameter - - method to your code files. -

    - - - -

    Set Parameters

    - - -

    - Algorithm parameters are hard-coded values for variables in your project that are set outside of the code files. Add parameters to your projects to remove hard-coded values from your code files and to perform parameter optimizations. You can add parameters, set default parameter values, and remove parameters from your projects. -

    -

    - Add Parameters -

    -

    - Follow these steps to add an algorithm parameter to a project: -

    -
      -
    1. - - Open the project - - . -
    2. - In the Project panel, click + Click - Add New Parameter + Add .
    3. -
    4. - Enter the parameter name. -
    5. - The parameter name must be unique in the project. -

      -
    6. - Enter the default value. -
    7. -
    8. - Click + To add more webhook notifications, click - Create Parameter + Add Notification - . -
    9. + and then continue from step 2. +

    -

    - To get the parameter values into your algorithm, see - - Get Parameters - - . -

    - Set Default Parameter Values + JSON Payload Schema

    - Follow these steps to set the default value of an algorithm parameter in a project: + The webhook HTTP-POST request sends a JSON payload with the following schema:

    -
      -
    1. - - Open the project - - . -
    2. -
    3. - In the Project panel, hover over the algorithm parameter and then click the - - pencil - - icon that appears. -
    4. - Edit default parameter -
    5. - Enter a default value for the parameter and then click - - Save - - . -
    6. -

      - The Project panel displays the default parameter value next to the parameter name. -

      -
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Property + + Description +
    + + ProjectName + + + + string + +
    + Name of the project. +
    + + ProjectId + + + + integer + +
    + Id of the project. +
    + + Insights + + + + Insight Array + +
    + Collection of insights emitted since the last notification. See + + Insight + + for the schema of each object. +
    + + OrderEvents + + + + OrderEvent Array + +
    + Collection of order events since the last notification. For the conceptual model, see + + OrderEvent in the API Reference + + . The webhook serialization differs from the API response. See the Order Event Schema table below for the webhook-specific schema. +
    + + Portfolio + + + + Portfolio Array + +
    + Current portfolio holdings. See the Portfolio Schema table below for the schema of each object. +

    - Delete Parameters + Order Event Schema

    - Follow these steps to delete an algorithm parameter in a project: -

    -
      -
    1. - - Open the project - - . -
    2. -
    3. - In the Project panel, hover over the algorithm parameter and then click the - - trash can - - icon that appears. -
    4. - Delete a parameter -
    5. - Remove the - - GetParameter - - calls that were associated with the parameter from your code files. -
    6. -
    - - - -

    Get Parameters

    - - -

    - To get the parameter values from the Project panel into your algorithm, see - - Get Parameters - - . -

    - - - -

    Number of Parameters

    - - -

    - The cloud optimizer can optimize up to three parameters. There are several reasons for this quota. First, the optimizer only supports the - - grid search strategy - - , which is very inefficient. This strategy tests every permutation of parameter values, so the number of backtests that the optimization job must run explodes as you add more parameters. Second, the - - parameter charts - - that display the optimization results are limited to three dimensions. Third, if you optimize with many variables, it increases the likelihood of - - overfitting - - to historical data. -

    -

    - To optimize more than three parameters, - - run local optimizations with the CLI - - . -

    - - - -

     

    - -
    -
    -

    Optimization

    -

    Objectives

    -
    -
    -

    Introduction

    - - -

    - An optimization objective is the performance metric that's used to compare the backtest performance of different parameter values. The optimizer currently supports the compound annual growth rate (CAGR), drawdown, Sharpe ratio, and Probabilistic Sharpe ratio (PSR) as optimization objectives. When the optimization job finishes, the results page displays the value of the objective with respect to the parameter values. -

    - - - -

    CAGR

    - - -

    - The annual percentage return that would be required to grow a portfolio from its starting value to its ending value. -

    -

    - It is calculated as + Each object in the + + OrderEvents + + array has the following properties:

    - $$ -\text{CAGR} = \left(\frac{e}{s}\right)^{\frac{1}{y}} - 1 -$$ -

    - where $s$ is starting equity, $e$ is ending equity, and $y$ is the number of years in the backtest period. -

    - - -

    - The benefit of using CAGR as the objective is that it maximizes the return of your algorithm over the entire backtest. The drawback of using CAGR is that it may cause your algorithm to have more volatile returns, which increases the difficulty of keeping your algorithm deployed in live mode. -

    - - - -

    Drawdown

    - - -

    - The largest peak to trough decline in an algorithm's equity curve. -

    -

    - It is calculated as -

    - $$ -1 - \frac{v^{t \ge s}_{\text{min}}}{v^s_{\text{max}}} -$$ -

    - where $v^s_{\text{max}}$ is the maximum equity value up to time $s$ and $v^{t \ge s}_{\text{min}}$ is the minimum equity value at time $t$ where $t \ge s$. -

    - - -

    - The following image illustrates how the max drawdown is calculated: -

    - An equity curve that has the drawdown periods highlighted -

    - During the first highlighted period in the preceding image, the equity curve dropped from 106,027 to 93,949 (11.4%). During the second highlighted period, the equity curve dropped from 112,848 to 99,576 (11.8%). Since 11.8% > 11.4%, the max drawdown of the equity curve is 11.8%. -

    -

    - The benefit of using drawdown as the objective is that it's psychologically easier to keep an algorithm deployed in live mode if the algorithm doesn't experience large drawdowns. The drawback of using drawdown is that it may limit the potential returns of your algorithm. -

    - - - -

    Sharpe

    - - -

    - A measure of the risk-adjusted return, developed by William Sharpe. -

    -

    - It is calculated as -

    - $$ -SR = \frac{E[R_p - R_b]}{\sigma_p} -$$ -

    - where $R_p$ is the return of the portfolio, $R_b$ is the return of the benchmark, and $\sigma_p$ is the standard deviation of the portfolio's excess returns. By default, LEAN uses a 0% risk-free rate, so $R_b = 0$. For more information about the Sharpe ratio, see - - Sharpe (1994) - - . -

    - - -

    - The benefit of using the Sharpe ratio as the objective is that it maximizes returns while minimizing the return volatility. It's usually psychologically easier to keep a live algorithm deployed if it has minimal swings in equity than if it has large swings in equity. The drawback of using the Sharpe ratio is that it may limit your potential returns in favor of a less volatile equity curve. -

    - - - -

    PSR

    - - -

    - The probability that the estimated Sharpe ratio of an algorithm is greater than a benchmark. -

    -

    - It is calculated as -

    - \[ P\left(\hat{SR} > SR^{\ast}\right) = CDF\left(\frac{(\hat{SR} - SR^{\ast})\sqrt{n-1}}{\sqrt{1 - \hat{\gamma}_{3}\hat{SR} + \frac{\hat{\gamma}_{4}-1}{4}\hat{SR}^{2}}}\right) \] -

    - where $SR^{\ast}$ is the Sharpe ratio of the benchmark, $\hat{SR}$ is the Sharpe ratio of the algorithm, $n$ is the number of trading days, $\hat{\gamma}_{3}$ is the skewness of the algorithm's returns, $\hat{\gamma}_{4}$ is the kurtosis of the algorithm's returns, and $CDF$ is the normal cumulative distribution function. For more information about the PSR, see - - Bailey and López de Prado (2012) - - . -

    - - -

    - The benefit of using the PSR as the objective is that it maximizes the probability of your algorithm's Sharpe ratio outperforming the benchmark Sharpe ratio. The drawback of using the PSR is that, like the Sharpe ratio objective, optimizing the PSR may limit your potential returns in favor of a less volatile equity curve. -

    - - - -

    Constraints

    - - -

    - Constraints filter out backtests from your optimization results that do not conform to your desired range of statistical results. Constraints consist of a target, operator, and a numerical value. For instance, you can add a constraint that the optimization backtests must have a Sharpe ratio >= 1 to be included in the optimization results. Constraints are optional, but you can use them to incorporate multiple objective functions into a single optimization job. -

    - - - -

     

    - -
    -
    -

    Optimization

    -

    Strategies

    -
    -
    -

    Introduction

    - - -

    - Optimization strategies control how the optimizer adjusts parameters for each new backtest that's run in the optimization job. Grid search is the only strategy currently available, but you can contribute new optimization strategies. -

    - - - -

    Grid Search

    - - -

    - Grid search is the most complete but the most expensive strategy because it takes a brute force approach and tests all the combinations of parameter values. If you are optimizing one parameter, the grid search strategy selects the values of the parameters based on the starting value, ending value, and step size that you provide. If you optimize two parameters, the grid search strategy searches the Cartesian product of possible values for each parameter. The following animation shows the process of using grid search to optimize two parameters: -

    - Grid search animation -

    - In the preceding animation, grid search tests all of the parameter combinations. The axes represent the possible values of each parameter. Gray squares represent backtests in the optimization queue, orange squares represent successful backtests, and black squares represent failed backtests. In this example, several squares are colored at the same time because the optimization job is using multiple - - optimization nodes - - . -

    -

    - Why Backtests Fail -

    -

    - When backtests fail during optimization, it is usually because the selected parameter values cause a divide-by-zero error or an index out-of-range exception. -

    -

    - Strategy Benefit and Drawback -

    -

    - The benefit of the grid search strategy is that it is the most comprehensive optimization strategy. The drawback of the strategy is it can be an expensive option because of the curse of dimensionality. -

    - - - -

    Contribute Strategies

    - - -

    - You can contribute any optimization strategy that is popular in the literature and is not already implemented. To view the optimization strategies that are already implemented, see - - our GitHub repository - - . If you contribute a strategy, you'll receive some - - QuantConnect Credit - - , you'll be shown as a contributor to Lean on your GitHub profile, and your work will be used in the Algorithm Lab by our community of over 250,000 quants. -

    -

    - To contribute optimization strategies, submit a pull request to the - - Lean GitHub repository - - . In your pull request, provide an explanation of the strategy and some relevant resources so that we can add the strategy to our documentation. For an example implementation, see the - - GridSearchOptimizationStrategy - - . -

    - - - -

     

    - -
    -
    -

    Optimization

    -

    Deployment

    -
    -
    -

    Introduction

    - - -

    - Deploy optimization jobs for your trading algorithms to optimize your algorithm parameters for the objective that you specify. The optimizer runs concurrent backtests to optimize your algorithm parameter using up to 24 nodes. As the optimization runs, the results are displayed and updated in real-time. -

    - - - -

    Resources

    - - -

    - The optimization nodes that backtest your algorithm are not the - - backtesting nodes - - in your organization. The optimization nodes are a cluster of nodes that exclusively run optimization jobs. The optimization can concurrently run multiple backtests if you use multiple nodes, but the maximum number of nodes you can use depends on the node type. The following table describes the node types: -

    - +
    - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + +
    - Type + + Property Description - Number of Cores - - RAM (GB) - - Max Cluster Size -
    - O2-8 + + OrderId + - Relatively simple strategies with less than 100 assets + + integer + +
    + Id of the order.
    - 2 + + Id + - 8 + + integer + +
    + Id of the order event.
    - 6 + + Symbol + + + + object + +
    + Symbol information with + + value + + (ticker), + + id + + (symbol Id), and + + permtick + + (permanent ticker) properties.
    - O4-12 + + UtcTime + - Strategies with less than 500 assets and simple universe selections + + string + +
    + UTC time of the order event in ISO 8601 format.
    - 4 + + Status + - 12 + + integer + +
    + Order status. 1 = Submitted, 2 = PartiallyFilled, 3 = Filled, 5 = Canceled, 6 = Invalid, 7 = CancelPending, 8 = UpdateSubmitted.
    - 4 + + OrderFee + + + + object + +
    + Order fee with a + + Value + + object containing + + Amount + + (number) and + + Currency + + (string) properties.
    - O8-16 + + FillPrice + - Complex strategies and machine learning + + number + +
    + Price at which the order was filled.
    - 8 + + FillPriceCurrency + - 16 + + string + +
    + Currency of the fill price.
    - 4 + + FillQuantity + + + + number + +
    + Quantity filled in this event. +
    + + Direction + + + + integer + +
    + Order direction. 0 = Buy, 1 = Sell, 2 = Hold. +
    + + IsAssignment + + + + boolean + +
    + Whether the order is an option assignment. +
    + + Quantity + + + + number + +
    + Total quantity of the order.
    +

    + Portfolio Schema +

    - The following table shows the - - training quotas - - of the optimization node types: + Each object in the + + Portfolio + + array has the following properties:

    - +
    - - - - + + +
    - Type - - Capacity (min) + + Property - Refill Rate (min/day) + Description
    - O2-8 - - 30 + + Ticker + - 5 + + string + +
    + Ticker of the holding.
    - O4-12 - - 60 + + Quantity + - 10 + + number + +
    + Quantity held.
    - O8-16 + + AveragePrice + - 90 + + number + +
    + Average price of the holding. This property is not present for cash holdings. +
    + + UnreazliedProfit + - 15 + + number + +
    + Unrealized profit of the holding. This property is not present for cash holdings.
    - - - - -

    Cost

    - - +

    + Example +

    - You can rent optimization nodes on a time basis. The deployment wizard estimates the total cost of your optimization job based on the results of the last successful backtest of your algorithm, the number of - - parameters - - , and the - - optimization strategy - - . Therefore, you must - - run a backtest - - of your algorithm before the deployment wizard can estimate the cost of the optimization job. The final cost that you pay can vary from the estimate. For instance, if your backtest used parameters that were favorable for speedy execution, the estimate can be lower than the final cost. -
    + The following JSON shows an example webhook payload:

    +
    +
    +{
    +  "ProjectName": "My Algorithm",
    +  "ProjectId": 29064060,
    +  "Insights": [
    +    {
    +      "Id": "a0af97846797499dbd515753bdb73a0c",
    +      "GroupId": null,
    +      "SourceModel": "4a834e9d-f10f-4ebe-9fd9-df5fc509b0f8",
    +      "GeneratedTime": 1773767160.2239125,
    +      "CreatedTime": 1773767160.2239125,
    +      "CloseTime": 1777482360.2239125,
    +      "Symbol": "SPY R735QTJ8XC9X",
    +      "Ticker": "SPY",
    +      "Type": "price",
    +      "reference": 671.77,
    +      "ReferenceValueFinal": 0,
    +      "Direction": "up",
    +      "Period": 2592000,
    +      "Magnitude": null,
    +      "Confidence": null,
    +      "Weight": null,
    +      "ScoreIsFinal": false,
    +      "ScoreMagnitude": "0",
    +      "ScoreDirection": "0",
    +      "EstimatedValue": "0",
    +      "Tag": ""
    +    }
    +  ],
    +  "OrderEvents": [
    +    {
    +      "OrderId": 1,
    +      "Id": 1,
    +      "Symbol": {
    +        "value": "AAPL",
    +        "id": "AAPL R735QTJ8XC9X",
    +        "permtick": "AAPL"
    +      },
    +      "UtcTime": "2026-03-17T17:06:00.2239123Z",
    +      "Status": 3,
    +      "OrderFee": {
    +        "Value": {
    +          "Amount": 1,
    +          "Currency": "USD"
    +        }
    +      },
    +      "FillPrice": 254.05,
    +      "FillPriceCurrency": "USD",
    +      "FillQuantity": 130,
    +      "Direction": 0,
    +      "IsAssignment": false,
    +      "Quantity": 130
    +    }
    +  ],
    +  "Portfolio": [
    +    {
    +      "Ticker": "AAPL",
    +      "Quantity": 130,
    +      "AveragePrice": 254.05,
    +      "UnreazliedProfit": -4.9
    +    },
    +    {
    +      "Ticker": "USD",
    +      "Quantity": 34053.79
    +    }
    +  ]
    +}
    +
    +
    + + + +

    Quotas

    + +

    - You can use multiple nodes to speed up the optimization job without the job costing more because you use each node for a shorter period of time. However, there is a spin-up time of roughly 15 seconds on each backtest, so it can sometimes cost more to use many nodes when you factor in the spin-up time. You pay for optimizations with your organization's - - QuantConnect Credit - - balance. If you have your own hardware, you can - - run local optimizations - - with your own data and hardware. + The number of email, Telegram, or webhook notifications you can send in each live algorithm for free depends on the tier of your organization. The following table shows the hourly quotas:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Tier + + Number of Notifications Per Hour +
    + Free + + N/A +
    + Quant Researcher + + 20 +
    + Team + + 60 +
    + Trading Firm + + 240 +
    + Institution + + 3,600 +
    +

    - The optimization job reads your + If you exceed the hourly quota, each additional email, Telegram, or webhook notification costs 1 QuantConnect Credit - balance on submission, deducts the cost from your balance when each backtest completes, and stops when all credit is consumed. Running optimization jobs don't account for credit you add after submission. Ensure you have sufficient credit, for example, 50% more than the estimate, before deploying. + (QCC). +

    +

    + Each SMS notification you send to a US or Canadian phone number costs 1 QCC. Each SMS notification you send to an international phone number costs 10 QCC.

    -

    Launch Optimization Jobs

    +

    Terms of Use

    - You need the following to optimize parameters: -

    - -

    - Follow these steps to optimize parameters: + The notification system can't be used for data distribution.

    -
      -
    1. - - Open the project - - that contains the parameters you want to optimize. -
    2. -
    3. - In the top-right corner of the IDE, click the - Cloud optimization icon - - Optimize - - icon. -
    4. -
    5. - On the Optimization page, in the - - Parameter & Constraints - - section, enter the name of the parameter to optimize. -
    6. -

      - The parameter name must match a parameter name in the Project panel. -

      -
    7. - Enter the minimum and maximum parameter values. -
    8. -
    9. - Click the - - gear - - icon next to the parameter and then enter a step size. -
    10. -
    11. - If you want to add another parameter to optimize, click - - Add Parameter - - . -
    12. -

      - You can optimize a maximum of three parameters. To optimize more parameters, - - run local optimizations with the CLI - - . -

      -
    13. - If you want to add - - optimization constraints - - , follow these steps: -
    14. -
        -
      1. - Click - - Add Constraint - - . -
      2. -
      3. - Click the - - target - - field and then select a - - target - - from the drop-down menu. -
      4. -
      5. - Click the - - operation - - field and then an operation from the drop-down menu. -
      6. -
      7. - Enter a constraint value. -
      8. -
      -
    15. - In the - - Estimated Number and Cost of Backtests - - section, click an - - optimization node - - and then select a maximum number of nodes to use. -
    16. -
    17. - In the - - Strategy & Target - - section, click the - - Choose Optimization Strategy - - field and then select a - - strategy - - from the drop-down menu. -
    18. -
    19. - Click the - - Select Target - - field and then select a target from the drop-down menu. -
    20. -

      - The target (also known as objective) is the performance metric the optimizer uses to compare the backtest performance of different parameter values. -

      -
    21. - Click - - Maximize - - or - - Minimize - - to maximize or minimize the optimization target, respectively. -
    22. -
    23. - Click - - Launch Optimization - - . -
    24. -

      - The - - optimization results page - - displays. As the optimization job runs, you can close or refresh the window without interrupting the job because the nodes are processing on our servers. -

      -

      - To abort a running optimization job, in the Status panel, click - - Abort - - and then click - - Yes - - . -

      -

     

    - +
    -
    -

    Optimization

    +
    +

    Live Trading

    Results

    @@ -43025,26 +41426,26 @@

    Introduction

    - The optimization results page shows your algorithm's performance with the various parameter values. Review the results page to see how your algorithm has performed during the backtests and to investigate how you might improve your algorithm before live trading. + The live results page shows your algorithm's live trading performance. Review the results page to see how your algorithm has been performing and to investigate ways to improve it.

    -

    View Optimization Results

    +

    View Live Results

    - The optimization results page automatically displays when you - - launch an optimization job + The live results page automatically displays when you + + deploy a live algorithm - . The page presents the algorithm's equity curves, parameters, target values, server statistics, and much more information. + . The page presents the algorithm's equity curve, holdings, trades, logs, server statistics, and much more information.

    - Optimization result interface + Live result interface

    - The content in the optimization results page updates as your optimization job executes. You can close or refresh the window without interrupting the job because the optimization nodes process on our servers. If you close the page, you can - - view all of the project's optimizations + The content in the live results page updates as your algorithm executes. You can close or refresh the window without interrupting the algorithm because the live trading node processes on our servers. If you close the page, you can + + view all of your live projects to open the page again.

    @@ -43055,13 +41456,13 @@

    Runtime Statistics

    - The banner at the top of the optimization results page displays the performance statistics of the optimization job. + The banner at the top of the live results page displays the performance statistics of your algorithm.

    - Optimization result banner + Live runtime statistics

    - The banner updates in real-time as the optimization job progresses on our servers. The following table describes the runtime statistics: + The following table describes the default runtime statistics:

    - +
    @@ -43075,403 +41476,148 @@

    Runtime Statistics

    - Completed + Equity - The number of backtests that have successfully completed + The total portfolio value if all of the holdings were sold at current market rates. Equity equals the sum of cash and the market value of all open positions.
    - Failed + Fees - The number of backtests that have failed during execution + The total quantity of fees paid for all the transactions during the algorithm's trading period. Total fees include brokerage commissions, exchange fees, and other transaction costs.
    - Running + Holdings - The number of backtests that are currently running + The absolute sum of the items in the portfolio. Holdings represent the total market value of all positions, regardless of whether they are long or short.
    - In Queue + Net Profit - The number of backtests that are waiting to start + The dollar-value return across the entire trading period.
    - Average Length + PSR - The average amount of time to complete one of the backtests + The probability that the estimated Sharpe ratio of an algorithm is greater than a benchmark.
    - Total Runtime + Return - The total runtime of the optimization job + The rate of return across the entire trading period.
    - Total + Unrealized - The total number of backtests run in the optimization job + The amount of profit a portfolio would capture if it liquidated all open positions and paid the fees for transacting and crossing the spread. Unrealized profit becomes realized profit when the positions are closed.
    - Consumed + Volume - The amount of - - QuantConnect Credit - - that was used to perform the optimization + The total value of assets traded for all of an algorithm's transactions during the trading period.
    - - - -

    Equity Curves

    - -

    - The optimization results page displays a Strategy Equities chart so that you can analyze the equity curves of the individual backtests in the optimization job. + To add a custom runtime statistic, see + + Add Statistics + + .

    - Optimization result equity curves

    - The equity curves of the backtests update in real-time as the optimization job runs. View the Strategy Equities chart to see how the parameter values affect the equity of your algorithm, to see how sensitive the returns are to the range of parameters selected by the optimizer, and to take a closer look at specific times in the backtest history. + If you + + stop + + and redeploy a live algorithm, the runtime statistics are reset.

    -

    Parameter Charts

    +

    Built-in Charts

    - The optimization results page displays parameter charts to show the relationship between the - - parameter - - value(s) selected by the optimizer and the value of several objectives. If your optimization job has one parameter, the result page displays a -scatter plot for each objective. If your optimization job has two parameters, the result page displays a heat map for each objective. + The live results page displays the equity curve of your algorithm so that you can analyze its performance in real-time.

    - Optimization result charts + Live strategy equity candle chart

    - If your optimization job has three parameters, the result page displays a 3-dimensional plot for each objective. To analyze the results of 3-dimensional plots, you can rotate them and apply cuttoff values. To get the objective value for a combination of parameters, hover over the dots in the plot. + The following table describes the series in the Strategy Equity chart:

    - Demo of 3 dimensional charts -

    - Parameter Stability -

    + + + + + + + + + + + + + + + + + + + + + +
    + Series + + Description +
    + Equity + + The live equity curve of your algorithm. +
    + Out of Sample Backtest + + The + + backtest equity curve + + of your algorithm during the live trading period. +
    + Meta + + Points in time when you deployed your algorithm, stopped your algorithm, and when your algorithm encountered a runtime error. +

    - Zones in the heatmap where the color of adjacent cells are relatively consistent represent areas where the objectives are stable. In these areas, the value of the objectives is not significantly influenced by the parameter values. The following image shows the parameter chart of an optimization job. The highlighted area identifies combinations of parameter values that stabilize the objective function. + The following table describes the other charts displayed on the page:

    - Stable objective function -

    - Supported Objectives -

    -

    - You can add parameter charts for the following objectives: -

    - -

    - Add Parameter Charts -

    -

    - Follow these steps to add a parameter chart to the optimization results page: -

    -
      -
    1. - In the Parameter Chart panel, click the - - plus - - icon. -
    2. -
    3. - Click the - - Objective - - field and then select an objective from the drop-down menu. -
    4. -
    5. - Click the - - Parameter 1 - - field and then select a parameter from the drop-down menu. -
    6. -
    7. - If there are multiple parameters in the optimization, click the - - Parameter 2 - - field and then select a parameter from the drop-down menu. -
    8. -
    9. - If there are three parameters in the optimization, click the - - Parameter 3 - - field and then select a parameter from the drop-down menu. -
    10. -
    11. - Click - - Create Chart - - . -
    12. -

      - The optimization results page displays the new chart. -

      -
    - - - -

    Individual Backtest Results

    - - -

    - The optimization results page displays a Backtests table that includes all of the backtests that ran during the optimization job. The table lists the parameter values of the backtests in the optimization job and their resulting values for the objectives. -

    - Individual backtest result result -

    - Open the Backtest Results Page -

    -

    - To open the - - backtest result page - - of one of the backtests in the optimization job, click a backtest in the table. -

    -

    - Download the Table -

    -

    - To download the table, right-click one of the rows, and then click - - Export > CSV Export - - . -

    -

    - Filter the Table -

    -

    - Follow these steps to apply filters to the Backtests table: -

    -
      -
    1. - On the right edge of the Backtests table, click - - Filters - - . -
    2. -
    3. - Click the name of the column to which you want the filter to be applied. -
    4. -
    5. - If the column you selected is numerical, click the - - operation - - field and then select one of the operations from the drop-down menu. -
    6. -
    7. - Fill the fields below the operation you selected. -
    8. - Optimization results table -
    -

    - Toggle Table Columns -

    -

    - Follow these steps to hide and show columns in the Backtests table: -

    -
      -
    1. - On the right edge of the Backtests table, click - - Columns - - . -
    2. -
    3. - Select the columns you want to include in the Backtests table and deselect the columns you want to exclude. -
    4. -
    -

    - Sort the Table Columns -

    -

    - In the Backtests table, click one of the column names to sort the table by that column. -

    - - - -

    Server Stats

    - - -

    - The optimization results page displays a Server Statistics section to show the status of the nodes running the optimization job. -

    -
    - -
    - -
    -
    -

    - The following image shows an example of the Server Statistics section: -

    - Optimization server statstics -

    - The following table describes the information that the Server Statistics section displays: -

    - +
    + + + + + + + + + + + + + +
    - Property + Chart +
    Description @@ -43481,3296 +41627,3996 @@

    Server Stats

    - CPU + Drawdown - The total CPU usage and the CPU usage of each node + A time series of equity peak-to-trough value.
    - RAM + Benchmark - The total RAM usage of the RAM usage of each node + A time series of the benchmark closing price (SPY, by default).
    - HOST + Exposure - The node model and the number of nodes used to run the optimization + A time series of long and short exposure ratios.
    - Uptime + Assets Sales Volume - The length of time that the optimization job has ran + A chart showing the proportion of total volume for each traded security. +
    + Portfolio Turnover + + A time series of the portfolio turnover rate. +
    + Portfolio Margin + + A stacked area chart of the portfolio margin usage. For more information about this chart, see + + Portfolio Margin Plots + + . +
    + Asset Plot + + A time series of an asset's price with order event annotations. For more information about these charts, see + + Asset Plots + + .
    -

    - View the Server Statistics section to see the amount of CPU power and RAM the optimization job demands. If your algorithm is demanding a lot of resources, use more powerful nodes on the next optimization job or improve the efficiency of your algorithm. -

    -

    Errors

    +

    Asset Plots

    - The following table describes common optimization errors: + Asset plots display the trade prices of an asset and the following + + order events + + you have for the asset:

    - +
    - - + + + + + + + +
    - Error + + Order Event - Description + + Icon
    - Runtime Errors + Submissions - If a backtest in your optimization job throws a runtime error, the backtest will not complete but you will still be charged. + Gray circle
    - Data Overload + Updates - If a backtest in your optimization job produces more than 700MB of data, then Lean can't upload the results and the optimization job appears to never be complete. + Blue circle +
    + Cancellations + + Gray square +
    + Fills and partial fills + + Green (buys) or red (sells) arrows
    - - - -

    View All Optimizations

    - -

    - Follow these steps to view all of the optimization results of a project: + The following image shows an example asset plot for AAPL: +

    + AAPL stock price with order events overlaid +

    + The order submission icons aren't visible by default. +

    +

    + View Plots +

    +

    + Follow these steps to open an asset plot:

    1. - - Open the project - - that contains the optimization results you want to view. + Open the live results page.
    2. - At the top of the IDE, click the - - - Results + Click the + + Orders - icon. + tab.
    3. -

      - A table containing all of the backtest and optimization results for the project is displayed. If there is a - - play - - icon to the left of the name, it's a - - backtest result - - . If there is a - - fast-forward - - icon next to the name, it's an - - optimization result - - . -
      -

      - All backtest table view
    4. - - (Optional) - - In the top-right corner, select the - - Show + Click the + + + Asset Plot - field and then select one of the options from the drop-down menu to filter the table by backtest or optimization results. + icon that's next to the asset Symbol in the Orders table.
    5. +
    +

    + Tool Tips +

    +

    + When you hover over one of the order events in the table, the asset plot highlights the order event, displays the asset price at the time of the event, and displays the + + tag + + associated with the event. Consider adding helpful tags to each order event to help with debugging your algorithm. For example, when you cancel an order, you can add a tag that explains the reason for cancelling it. +

    +

    + Adjust the Display Period +

    +

    + The resolution of the asset price time series in the plot doesn't necessarily match the resolution you set when you subscribed to the asset in your algorithm. If you are displaying the entire price series, the series usually displays the daily closing price. However, when you zoom in, the chart will adjust its display period and may use higher resolution data. To zoom in and out, perform either of the following actions: +

    + + gif that shows the price of AAPL while zooming in and out +

    + If you have multiple order events in a single day and you zoom out on the chart so that it displays the daily closing prices, the plot aggregates the order event icons together as the price on that day. +

    - Rename Optimizations + Order Fill Prices

    - We give an arbitrary name (for example, "Smooth Apricot Chicken") to your optimization result files, but you can follow these steps to rename them: + The plot displays fill order events at the actual fill price of your orders. The fill price is usually not equal to the asset price that displays because of the following reasons:

    -
      +
      • - Hover over the optimization you want to rename and then click the - - pencil - - icon that appears. -
      • - Rename optimization instance -
      • - Enter the new name and then press - - Enter - + Your order experiences + + slippage + .
      • -
    -

    - Delete Optimizations -

    -

    - Hover over the optimization you want to delete and then click the - - trash can - - icon that appears to delete the optimization result. -

    - Delete optimization result - - - -

     

    - -
    -
    -

    Object Store

    - -
    -
    -

    Introduction

    - - -

    - The Object Store is an organization-specific key-value storage location to save and retrieve data in QuantConnect's cache. Similar to a dictionary or hash table, a key-value store is a storage system that saves and retrieves objects by using keys. A key is a unique string that is associated with a single record in the key-value store and a value is an object being stored. Some common use cases of the Object Store include the following: -

    - -

    - The Object Store is shared across the entire organization. Using the same key, you can access data across all projects in an organization. -

    -

    View Storage

    +

    Custom Charts

    - The Object Store page shows all the data your organization has in the Object Store. To view the page, log in to the Algorithm Lab and then, in the left navigation bar, click - - Organization > Object Store + The results page shows the custom charts that you create. +

    +

    + Supported Chart Types +

    +

    + We support the following types of charts: +

    +
    +
    +

    + If you use + + SeriesType.Candle + + and plot enough values, the plot displays candlesticks. However, the + + Plot + + + plot + + method only accepts one numerical value per time step, so you can't plot candles that represent the open, high, low, and close values of each bar in your algorithm. The charting software automatically groups the data points you provide to create the candlesticks, so you can't control the period of time that each candlestick represents. +

    +

    + To create other types of charts, save the plot data in the Object Store and then load it into the Research Environment. In the Research Environment, you can + + create other types of charts with third-party charting packages .

    - Table of files and their sizes +

    + Supported Markers +

    - To view the metadata of a file (including it's path, size, and a content preview), click one of the files in the table. + When you create scatter plots, you can set a marker symbol. We support the following marker symbols:

    - Panel of metadata - - - -

    Upload Files

    - - +
    +
    +

    + Chart Sampling +

    - Follow these steps to upload files to the Object Store: + Charts are sampled every one and ten minutes. If you create 1-minute resolution custom charts, the IDE charting will downgrade the granularity and display the 10-minutes sampling after a certain amount of samples.

    -
      -
    1. - Open the - - Object Store - - page. -
    2. -
    3. - Navigate to the directory in the Object Store where you want to upload files. -
    4. -
    5. - Click - - Upload - - . -
    6. -
    7. - Drag and drop the files you want to upload. -
    8. -
    +

    + Demonstration +

    - Alternatively, you can - - add data to the Object Store in an algorithm - - or - - notebook + For more information about creating custom charts, see + + Charting .

    -

    Download Files

    +

    Adjust Charts

    - Permissioned - - Institutional - - clients can build derivative data such as machine learning models and download it from the Object Store. - - Contact us - - to unlock this feature for your account. + You can manipulate the charts displayed on the live results page.

    +
    + +
    + +
    +
    +

    + Toggle Charts +

    - Follow these steps to download files and directories from the Object Store: + To display and hide a chart on the live results page, in the + + Select Chart + + section, click the name of a chart.

    -
      -
    1. - Open the - - Object Store - - page. -
    2. -
    3. - Navigate to the directory in the Object Store where you want to download files and directories. -
    4. -
    5. - Select the file(s) and directory(ies) to download -
    6. +

      + Toggle Chart Series +

      +

      + To display and hide a series on a chart on the live results page, click the name of a series at the top of a chart. +

      + Demostration of toggling series displays on charts +

      + Adjust the Display Period +

      +

      + To zoom in and out of a time series chart on the live results page, perform either of the following actions: +

      +
      • - Click + Click the - Download + 1m - . -
      • -
      • - Wait while QuantConnect processes the request. -
      • -
      • - Click the + , - Download + 3m - link that appears. + , + + 1y + + , or + + All + + period in the top-right corner of the chart.
      • -
    +
  • + Click a point on the chart and drag your mouse horizontally to highlight a specific period of time in the chart. +
  • + Demostration of zooming in for time period on charts +

    - If you can't download files from the Object Store, you can log data to the Object Store during a backtest and analyze it in the Research Environment. For a full walkthrough, see - - Example for Logging - - . + If you adjust the zoom on a chart, it affects all of the charts. +

    +

    + After you zoom in on a chart, slide the horizontal bar at the bottom of the chart to adjust the time frame that displays. +

    + Demostration of scrolling for time period on charts +

    + Resize Charts +

    +

    + To resize a chart on the live results page, hover over the bottom-right corner of the chart. When the resize cursor appears, hold the left mouse button and then drag to the desired size. +

    +

    + Move Charts +

    +

    + To move a chart on the live results page, click, hold, and drag the chart title. +

    +

    + Refresh Charts +

    +

    + Refreshing the charts on the live results page resets the zoom level on all the charts. If you refresh the charts while your algorithm is executing, only the data that was seen by the Lean engine after you refreshed the charts is displayed. To refresh the charts, in the + + Select Chart + + section, click the + + reset + + icon.

    -

    Storage Sizes

    +

    Holdings

    - All organizations get 50 MB of free storage in the Object Store. Paid organizations can subscribe to more storage space. The following table shows the cost of the supported storage sizes: + The + + Holdings + + tab on the live results page displays your positions and cash.

    - + Live holdings table +

    + The following table describes the properties that display for each of your positions: +

    +
    - - - - - - - -
    - Storage Size (GB) - - Storage Files (-) + + Property - Monthly Cost ($) + + Description
    - 0.05 - - 1,000 + + Symbol + - 0 + The ticker of the security.
    - 2 - - 20,000 + + Average Price + - 10 + The average price that you paid for the position.
    - 5 - - 50,000 + + Quantity + - 20 + The size of your position.
    - 10 - - 100,000 + + Market Value + - 50 + The value of your position if sold with market orders.
    - 50 - - 500,000 + + Unrealized + - 100 + The unrealized profit of your position, including fees and spread costs.
    - +

    + The values in the positions section update as new data points are injected into your algorithm. The cash section displays the quantity of each currency in your algorithm's + + CashBook + + . View the + + Holdings + + tab to see your holdings, + + add security subscriptions + + , and + + place manual orders + + . To view all of your current holdings and active data subscriptions, enable the + + Show All Portfolio + + check box. +

    -

    Delete Storage

    +

    Orders

    - Follow these steps to delete storage from the Object Store: + The live results page displays the orders of your algorithm and you can download them to your local machine.

    -
      +

      + View in the GUI +

      +

      + To see the orders that your algorithm created, open the live results page and then click the + + Orders + + tab. If there are more than 10 orders, use the pagination tools at the bottom of the Orders Summary table to see all of the orders. Click on an individual order in the Orders Summary table to reveal all of the + + order events + + , which include: +

      +
      • - Open the - - Object Store - - page. + Submissions
      • - Navigate to the directory in the Object Store where you want to delete files. + Fills
      • - Click the check box next to the files you want to delete. + Partial fills
      • - Click - - Actions - - and then click - - Delete - - from the drop-down menu. + Updates
      • - Click - - OK - - . + Cancellations
      • -
    +
  • + Option contract exercises and expiration +
  • +

    - Alternatively, you can - - delete storage in an algorithm - - or - - notebook - - . + The timestamps in the Order Summary table are based in Eastern Time (ET).

    - - - -

    Edit Storage Plan

    - - +

    + Access the Order Summary CSV +

    - You need - - storage billing permissions - - and a paid organization to edit the size of the organization's Object Store. + To view the orders data in CSV format, open the live results page, click the + + Orders + + tab, and then click + + Download Orders + + . The content of the CSV file is the content displayed in the Orders Summary table when the table rows are collapsed. The timestamps in the CSV file are based in Coordinated Universal Time (UTC).

    +

    + Access in Jupyter Notebooks +

    - Follow these steps to edit the amount of storage available in your organization's Object Store: -

    -
      -
    1. - Log in to the Algorithm Lab. -
    2. -
    3. - In the left navigation bar, click - - Organization > Resources - - . -
    4. -
    5. - On the Resources page, scroll down to the - - Storage Resources - - and then click - - Add Object Store Capacity + To programmatically analyze orders, call the + + + ReadLiveOrders - . -
    6. -
    7. - On the Pricing page, select a storage plan. -
    8. -
    9. - Click - - Proceed to Checkout + + read_live_orders - . -
    10. - Project Object Store limit panel -
    + + method or the + + /live/orders/read + + endpoint. +

    +

    +

    -

    Research to Live Considerations

    +

    Insights

    - When you deploy a live algorithm, you can access the data within minutes of modifying the Object Store. Ensure your algorithm is able to handle a changing dataset. -

    -

    - The live environment's access to the Object Store is much slower than in research and backtesting. Limit the individual objects to less than 50 MB to prevent live trading access issues. -

    - - - -

    Usage by Project

    - - -

    - The Resources page shows the total storage used in your organization and the storage used by individual projects so that you can easily manage your storage space. To view the page, log in to the Algorithm Lab and then, in the left navigation bar, click - - Organization > Resources - - . -

    - Organization Object Store usage panel - - - -

     

    - -
    -
    -

    Community

    - -
    -
    - - -
    -
    -

    - The QuantConnect community consists of over 250,000 quants and investors with diverse backgrounds. Our platform supports several channels of communication so that our members can discuss with the core team, other community members, and third-party contractors. Our community members are a great source for assistance when creating trading algorithms, but we recommend all users complete our base training material before requesting assistance from other members. -

    -
    - - -
    - - - -

     

    - -
    -
    -

    Community

    -

    Code of Conduct

    -
    -
    -

    Introduction

    - - -

    - The QuantConnect community consists of 250,000 quants and investors with diverse backgrounds. Our platform supports several channels of communication so that our members can discuss with the core team, other community members, and third-party contractors. Our community members are a great source for assistance when creating trading algorithms, but we recommend all users complete our base training material before requesting assistance from other members. + The live results page displays the insights of your algorithm and you can download them to your local machine.

    +

    + View in the GUI +

    - Our community forum was created to be a hub for sharing quality quantitative science insights, discussions on quantitative philosophies, and solving problems. Our goal is to embrace new ways of thinking while keeping a friendly, welcoming environment where people feel comfortable amongst their peers. Our community's exceptional and diverse range of opinions and experiences make us a unique platform. Coming together with mutual respect and courtesy can unite us in growing together as quants. + To see the insights your algorithm emit, open the live result page and then click the + + Insights + + tab. If there are more than 10 insights, use the pagination tools at the bottom of the Insights Summary table to see all of the insights. The timestamps in the Insights Summary table are based in Eastern Time (ET).

    +

    + Download JSON +

    - We ask that our users adhere to the community code of conduct to ensure QuantConnect remains a safe, healthy environment for high-quality quantitative trading discussions. + To view the insights in JSON format, open the live result page, click the + + Insights + + tab, and then click + + Download Insights + + . The timestamps in the CSV file are based in Coordinated Universal Time (UTC).

    -

    Expectations

    +

    Logs

    - If you're here to get help, make it as easy as possible for others to help you. Follow our guidelines and remember that our community is made possible by volunteers. -

    -

    - Be clear and constructive when giving feedback, and be open when receiving it. Edits, comments, and suggestions are healthy parts of our community. + The + + Logs + + tab on the live results page displays all of the + + logging statements + + and status messages your algorithm creates. Their timestamps in the log file are in Coordinated Universal Time (UTC). The status messages include all of the points in time when your algorithm deployed, encountered an error, sent an order, or quit executing. It's good practice to add logs in live algorithms because then you can see what is happening while it executes. If you stop and redeploy your algorithm, the logs are retained. You can view the log file on the live results page or download them to your local machine.

    +

    + View in the GUI +

    - If you're here to help others, be patient and welcoming. Learning how to participate in our community can be hard. Offer support if you see someone struggling or otherwise in need of help. + To see the log file your algorithm has created, open the live results page and then click the + + Logs + + tab.

    - Be inclusive and respectful. Avoid sarcasm and be careful with jokes — tone is hard to decipher online. Prefer gender-neutral language when uncertain. If a situation makes it hard to be friendly, stop participating and move on. + To filter the logs, enter a search string in the + + Filter logs + + field.

    + +

    + Download Log File +

    - The following table shows examples of friendly and unfriendly content: + To download the log file, open the live result page, click the + + Logs + + tab, and then click + + Download Logs + + .

    - - - - - - - - - - - - - - - - - - - - - - - - - -
    - Unfriendly - - Friendly -
    - res-cross - "Google is free!" - - res-cross - "I think googling this might provide you with more helpful information." -
    - res-cross - "Obviously that's wrong because..." - - res-cross - "I think I can help you with this! Try this..." -
    - res-cross - "Can you speak English?" - - res-cross - "I think you're trying to say ____. Is that correct?" -
    - res-cross - "Your strategy will never work because ___." - - res-cross - "Here are some suggestions for your strategy..." -
    -

    Policies

    +

    Project Files

    - Please follow our community policies. -

    -

    - - Item green tick + The live results page displays the project files used to deploy the algorithm. To view the files, click the + + Code - Respect -

    -

    - We want our forum to be a place of general respect for one another. Keep interactions constructive, but friendly and lighthearted. Remember — we're all here to help one another and keep our community strong. -

    -

    - - Item green tick + tab. By default, the + + main.py - Due Diligence -

    -

    - We have hundreds of quants posting their questions. Your question is important, but make sure to check a few places before posting. We've worked hard on providing comprehensive documentation and - - bootcamp tutorials - - — make sure to check there first. Next, try Googling the concept to see if you can get another perspective before posting. Furthermore, our Debugger is a great tool to identify bugs in the code logic. -

    -

    - - Item green tick + + Main.cs - Relevancy -

    -

    - Keep posts related to algorithmic trading or quantitative finance. + file displays. To view other files in the project, click the file name and then select a different file from the drop-down menu.

    -

    - - Item green tick - - Patience -

    + Algorithm code snippets

    - Be patient with the community responses to your questions. Keep in mind the community are volunteers contributing to your quantitative growth voluntarily. When possible, answer your own questions to leave a path for future readers. Avoid "bumps", "+1", "Any Update?", double posting, - - thread hijacking - - , or - - necro-bumping - - discussions. Contributions are often rewarded with - - QuantConnect Credit - - . -

    -

    - - Item red cross + To create a new project with the project files used to deploy the algorithm, click + + Clone Algorithm - Bigotry -

    -

    - QuantConnect is firmly rooted in our policy against bigotry in our community forum. We are vehemently against racist, sexist, xenophobic, homophobic, or otherwise discriminatory behavior in our community. Any language that may offend anyone based on race, sexual orientation, gender, religion, will not be tolerated. + .

    -

    - - Item red cross - - Harassment -

    + + + +

    View All Live Projects

    + +

    - Bullying is not a part of our core culture at QuantConnect. We do not tolerate bullying, sexual harassment, profanity, threats of violence or otherwise, or any sexual harassment in our community forum. -

    -

    - - Item red cross + The + + Your Strategies - Bug Reports / Data Issues -

    -

    - We request bug reports be sent to the - - Support Team - - . The forums are not an effective bug tracking tool and often the reported issue is simply confusion on how the platform operates. For data issues please report the specific dates, times, contracts, and type of issue to the - - Data Explorer Issues List + section of the + + Strategy Explorer - . This is a system we've designed to track, fix and notify users when issues are fixed. -

    -

    - - Item red cross - - Promotional Activity -

    -

    - Spam and other forms of promotional activity isn't permitted in the community forum. Posts that deliver immediate value to the readers are permitted such as sharing a well-performing algorithm with an attribution to the author's company in the code comments. -

    -

    - - Item red cross + page displays the status of all the live algorithms in your organizations. To view the page, log in to the Algorithm Lab and then, in the left navigation bar, click + + Strategy Explorer - Can Someone Make My Algorithm? -

    -

    - We understand people are at different stages of their quant-growth, but if you are soliciting assistance you should have completed - - Boot Camp - - , and attach a backtest or code snippet with your best attempt at building your algorithm. This shows respect for the reader's time. You will need to know how to code to use QuantConnect. + .

    + View live project in algorithm lab -

    Reporting Violations

    +

    Errors

    - We monitor our forum diligently, but if you see something unsavory, please - - message us + If your live algorithm throws a runtime error, it stops executing and we send you an email. If you enabled + + automatic restarts - to report the activity. + when you deployed your algorithm, your algorithm will try five times to restart.

    -

    About This Code of Conduct

    +

    Share Results

    - We aspire to have a welcoming community filled with high-quality discussions about quantitative and algorithmic trading. Since our founding in November 2012, we ran an informal code of conduct and evolved those principles to support the community. Starting January 1st, 2021 as the scale of community content surpassed our ability to review each post, we have sought to write down these guidelines to provide transparency and framework for productive discussions. We welcome your feedback and expect this code of conduct to evolve over time. + You can share your live results with anyone, even if they don't have a QuantConnect account. + To share your results, follow these steps: +

    +
      +
    1. + In the Share Results section of the live results page, click + + Make Public + + . +
    2. +
    3. + To get a URL that you can share with others, click + + Live Stream + + . +
    4. +
    5. + To get an iframe that you can embed on a website, click + + Embed Code + + . +
    6. +
    +

    + The + + theme + + parameter of the URL defines the color theme. Valid values are + + darkly + + (dark) or + + chrome + + (light). +

    +

    + To stop sharing your live results, in the Share Results section of the live results page, click + + Make Private + + .

     

    - +
    -
    -

    Community

    -

    Forum

    +
    +

    Live Trading

    +

    Algorithm Control

    Introduction

    - The QuantConnect forum is a place to discuss with other community members, Mia (our AI assistant), the core QuantConnect team, and our - - Integration Partners - - . Use the forum to spark interesting discussions, ask for assistance from other members, and voice your opinion on our products. You must complete 30% of the Bootcamp lessons in the - - Learning Center - - to unlock the ability to post to the forum. + The algorithm control features on the live results page let you adjust your algorithm while it is executing live so that you can perform actions that are not written in the project files. The control features let you intervene in the execution of your algorithm and make adjustments. For instance, you can create security subscriptions, place trades, stop the algorithm, and update the algorithm.

    -

    Discussions

    +

    Add Security Subscriptions

    - Discussions are a set of forum posts and comments about a targeted subject. We occasionally post to forum discussions to announce new features, tutorials, and examples. If you have completed 30% of the Bootcamp lessons, you can contribute to discussions. Create discussions to connect with other members or to ask for guidance on a specific problem that you are facing, but always perform your own research and tests before asking other members for assistance. -

    -

    - The forum supports the following functionality: + The live results page enables you to manually create security subscriptions for your algorithm instead of calling the + + Add + + securityType + + + methods in your code files. If you add security subscriptions to your algorithm, you can place manual trades through the IDE without having to edit and redeploy the algorithm. Follow these steps to add security subscriptions:

    - +

    + Add Parameter Charts +

    - Follow these steps to deactivate your account: + Follow these steps to add a parameter chart to the optimization results page:

    1. - Log in to your account. + In the Parameter Chart panel, click the + + plus + + icon.
    2. - In the top navigation bar, click - - - yourUsername - - > My Account - - . + Click the + + Objective + + field and then select an objective from the drop-down menu.
    3. - At the bottom of your Account page, click - - Deactivate Account + Click the + + Parameter 1 - . + field and then select a parameter from the drop-down menu.
    4. - Click - - Deactivate Account + If there are multiple parameters in the optimization, click the + + Parameter 2 - . + field and then select a parameter from the drop-down menu.
    5. - Enter your email address and then click + If there are three parameters in the optimization, click the + + Parameter 3 + + field and then select a parameter from the drop-down menu. +
    6. +
    7. + Click - Continue + Create Chart .
    8. - The QuantConnect homepage displays. Log in to reactivate your account. + The optimization results page displays the new chart.

    -

    Remove Account

    +

    Individual Backtest Results

    - - Contact us - - to remove your QuantConnect account from our servers. + The optimization results page displays a Backtests table that includes all of the backtests that ran during the optimization job. The table lists the parameter values of the backtests in the optimization job and their resulting values for the objectives.

    - - - -

     

    - -
    -
    -

    Community

    -

    Academic Grants

    -
    -
    -

    Introduction

    - - + Individual backtest result result +

    + Open the Backtest Results Page +

    - The Academic Grant program gives researchers publishing a paper up to a three-month subscription. With this grant, you can implement your strategy and generate public source code that anyone can use to reproduce your results with our open-source platform on a uniform dataset. You can use the grant to create a - - Team organization - - with multiple members - - collaborating + To open the + + backtest result page - on the research. + of one of the backtests in the optimization job, click a backtest in the table.

    - - - -

    Apply for Grants

    - - +

    + Download the Table +

    - To be eligible for this grant, you must meet the following criteria: + To download the table, right-click one of the rows, and then click + + Export > CSV Export + + . +

    +

    + Filter the Table +

    +

    + Follow these steps to apply filters to the Backtests table:

    1. - You must be a member of an academic or corporate institution with a track record of published papers in peer-reviewed journals of the quantitative finance field. + On the right edge of the Backtests table, click + + Filters + + .
    2. - You must prove that you are a member of the institution as a professor, researcher, or student. + Click the name of the column to which you want the filter to be applied.
    3. - You must be actively doing research with the goal of writing a research paper. + If the column you selected is numerical, click the + + operation + + field and then select one of the operations from the drop-down menu. +
    4. +
    5. + Fill the fields below the operation you selected.
    6. + Optimization results table
    +

    + Toggle Table Columns +

    - Your research should be achievable using the QuantConnect platform and data. For example, we don't currently accept research on bonds, stock warrants, and non-US stocks. You can only use external data sources from official government websites, like - - FRED - - , or data vendors with a good track record. To view the dataset currently available on QuantConnect, see the - - Dataset Market - - . + Follow these steps to hide and show columns in the Backtests table:

    +
      +
    1. + On the right edge of the Backtests table, click + + Columns + + . +
    2. +
    3. + Select the columns you want to include in the Backtests table and deselect the columns you want to exclude. +
    4. +
    +

    + Sort the Table Columns +

    - To apply for a grant, submit your research proposal based on a draft paper or thesis to be published to - - research@quantconnect.com - - , along with details of your academic program or corporate research program. + In the Backtests table, click one of the column names to sort the table by that column.

    -

    Republishing Permission

    +

    Server Stats

    - By accepting a grant, you agree to share your research with a write-up, including the code, with the - - QuantConnect Research forum - - . If applicable, your strategy may be deployed as an example (with full credit given) on the - - Strategies page - - . + The optimization results page displays a Server Statistics section to show the status of the nodes running the optimization job.

    - - - -

     

    - -
    -
    -

    Community

    -

    Integration Partners

    -
    -
    -

    Introduction

    - - +
    + +
    + +
    +

    - Our Integration Partners are hand-picked, independent consultants and companies with a solid track record of operational excellence with QuantConnect. They offer guidance, consultation, teaching, coding development services. Let them help take your idea to a fully implemented algorithmic trading strategy. + The following image shows an example of the Server Statistics section:

    - - - -

    Hire Integration Partners

    - - + Optimization server statstics

    - The Integration Partners are thoroughly vetted and must pass a test to provide their services to the community. They decide their pricing and the services that they provide. Their services range from beginner to advanced dives into the algorithm framework, strategy and project consultation, developing simple to complex algorithms, portfolio optimization, and more. Hire them on the - - Integration Partners - - page to connect with a QuantConnect expert, to progress your development skills, or to outsource your algorithm development. + The following table describes the information that the Server Statistics section displays:

    - - - -

    Join the Integration Partner Team

    - - -

    - To list your services on the - - Integration Partners - - page, - - contact us - - and pass our test. If you are accepted and community members hire you, you receive 100% of the revenue. As an Integration Partner, you set your own pricing, define your own services, pick your own hours, and get paid to work on your quant trading specialty. Our Integration Partner program is a great addition to resumes. + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Property + + Description +
    + CPU + + The total CPU usage and the CPU usage of each node +
    + RAM + + The total RAM usage of the RAM usage of each node +
    + HOST + + The node model and the number of nodes used to run the optimization +
    + Uptime + + The length of time that the optimization job has ran +
    +

    + View the Server Statistics section to see the amount of CPU power and RAM the optimization job demands. If your algorithm is demanding a lot of resources, use more powerful nodes on the next optimization job or improve the efficiency of your algorithm. +

    + + + +

    Errors

    + + +

    + The following table describes common optimization errors: +

    + + + + + + + + + + + + + + + + + +
    + Error + + Description +
    + Runtime Errors + + If a backtest in your optimization job throws a runtime error, the backtest will not complete but you will still be charged. +
    + Data Overload + + If a backtest in your optimization job produces more than 700MB of data, then Lean can't upload the results and the optimization job appears to never be complete. +
    + + + +

    View All Optimizations

    + + +

    + Follow these steps to view all of the optimization results of a project: +

    +
      +
    1. + + Open the project + + that contains the optimization results you want to view. +
    2. +
    3. + At the top of the IDE, click the + + + Results + + icon. +
    4. +

      + A table containing all of the backtest and optimization results for the project is displayed. If there is a + + play + + icon to the left of the name, it's a + + backtest result + + . If there is a + + fast-forward + + icon next to the name, it's an + + optimization result + + . +
      +

      + All backtest table view +
    5. + + (Optional) + + In the top-right corner, select the + + Show + + field and then select one of the options from the drop-down menu to filter the table by backtest or optimization results. +
    6. +
    7. + + (Optional) + + In the bottom-right corner, click the + + Hide Error + + check box to remove backtest and optimization results from the table that had a runtime error. +
    8. +
    9. + + (Optional) + + Use the pagination tools at the bottom to change the page. +
    10. +
    11. + + (Optional) + + Click a column name to sort the table by that column. +
    12. +
    13. + Click a row in the table to open the results page of that backtest or optimization. +
    14. +
    +

    + Rename Optimizations +

    +

    + We give an arbitrary name (for example, "Smooth Apricot Chicken") to your optimization result files, but you can follow these steps to rename them: +

    +
      +
    1. + Hover over the optimization you want to rename and then click the + + pencil + + icon that appears. +
    2. + Rename optimization instance +
    3. + Enter the new name and then press + + Enter + + . +
    4. +
    +

    + Delete Optimizations +

    +

    + Hover over the optimization you want to delete and then click the + + trash can + + icon that appears to delete the optimization result.

    + Delete optimization result

     

    - +
    -
    -

    Community

    -

    Affiliates

    +
    +

    Research Pipeline

    +

    Introduction

    - Our Affiliate Program gives influential quants, financial strategists, and traders an opportunity to use their platforms to share QuantConnect and subsequently share in profits from referrals. + The Research Pipeline is a kanban board that enables you to easily monitor the state of your organization. + Think of it as a structured approach to bringing your ideas through the stages of research validation, backtesting, paper trading, and ending with live trading. + Each card represents one project in QuantConnect Cloud. + Simply add projects to the board, then drag-and-drop them, to move them throughout the various stages of the pipeline as your team completes tasks. + To view the board, + + log in to the Algorithm Lab + + and then click the + + Research Pipeline + + tab. +

    + Research Pipeline kanban board with cards across Ideas, Research, Backtest, Paper Trading, and Live Trading lists. +

    + To supercharge your productivity, add some + + AI assistants + + to your Research Pipeline. + We offer a set of highly-skilled AI assistants that you can add to your organization. + They are each pre-configured to specialize in a particular phase of the Research Pipeline, but you can always define custom assistants to fit your organization's needs.

    -

    Earning Potential

    +

    Stage 0: Ideas

    - On a monthly basis, you'll receive 10% of sales that originate for your referral. You'll receive this for up to 12 months after the individual you refer subscribes to our services. For example, if you refer a customer who signs up for our $280/month plan and the custom stays with us for 12+ months, you'll receive 12 payments of $28 for a total of $336 over the course of the first year. If the customer leaves after five months, you'll receive compensation for those five months. + The + + Ideas + + list on the Research Pipeline should contain cards that describe new trading ideas for your team's researchers to explore. + To add ideas to the list, click the + + + Add card + + icon in the top-right corner of the list, enter a description of the trading idea, and then click + + Save + + . +

    +

    + If you need some help generating some trading ideas, an + + Ideas Assistant + + can add a steady stream of novel trading ideas to your organization's + + Ideas + + list. + This assistant analyzes recent financial news articles and trading blog posts. + It then adds a card to the list with a concise description of the strategy, including the underlying logic, the types of assets it would trade, and any specific indicators or datasets it would utilize.

    - Referral rebate -

    YouTube Case Study

    +

    Stage 1: Research

    - Chris is a YouTube host with an audience of more than 20,000 subscribers where he posts trading and finance-related content in an easy-to-digest format. He is often appreciated for his ability to simply present complex concepts. Chris originally organically posted a review of various algorithmic trading platforms that caught our eye, so we invited him to join the QuantConnect Affiliate Program. + The + + Research + + list on the Research Pipeline should contain a card for each project that your research team is currently investigating. + During the research stage, the goal is to perform preliminary analysis to assess the viability of the trading idea before committing resources to a formal backtest. + This stage helps filter out weak ideas early, refine hypotheses, and identify potential pitfalls.

    - Over a period of two months, Chris and the QuantConnect team collaborated to design a series of video topics that would be interesting for the broader community. Chris always had complete content ownership and artistic control over his content. He often sought a “content review” from the QuantConnect team in a private Slack channel to help maintain the technical accuracy and quality of the videos. We were mindful to update him about new features that might impact or improve his videos. Each time Chris published a video, we would re-share it on all our social media channels to increase his video reach. We were always motivated to get high-quality content to the community so they can learn more effectively. + To add cards to the + + Research + + list, drag-and-drop a card from the + + Ideas + + list. + Alternatively, click the + + + Add card + + icon in the top-right corner of the list, enter a description of the trading idea, and then click + + Save + + .

    - Through his channel and the partnership with QuantConnect, Chris was able to create a long-term passive income from his content. Each month, he drives approximately 500 users to QuantConnect and 10-15 subscribe to become long-term clients. Within six months, he has built a passive recurring revenue stream of $250/month. + If you need some help researching ideas, add + + Research + + and + + Research Validation Assistants + + to your organization. + The Research Assistant has access to tools that let it write, read, and execute research notebooks, and it produces a research report in the project that documents its process and findings — typically covering empirical evidence, statistical significance, and a deep analysis of the fundamental reason for the edge of the trading idea. +

    +

    + The Research Validation Assistant then pressure-tests that work, using the same notebook tools to check the model's residuals for stationarity, serial correlation, heteroscedasticity, and stability between training and testing windows. + Together, they decide whether an idea has earned its place in the backtesting queue or whether it should be sent back before any engineering time is spent on it.

    -

    Become a Partner

    +

    Stage 2: Backtest

    - To apply for the QuantConnect Affiliate Program, fill in the - - application form + The + + Backtest + + list on the Research Pipeline should contain a card for each project that your backtest team is currently investigating. + During the backtest stage, the goal is to implement the trading idea in an error-free, event-driven algorithm. + This stage helps filter out strategies that passed the research phase but have poor performance after running a backtest with realistic + + reality modeling .

    +

    + To add cards to the + + Backtest + + list, drag-and-drop a card from the + + Ideas + + or + + Research + + lists. + Alternatively, click the + + + Add card + + icon in the top-right corner of the list, enter a description of the trading idea, and then click + + Save + + . +

    +

    + If you need some help backtesting ideas, add a + + Backtest Assistant + + to your organization. + This assistant has access to tools that enable it to write algorithms, fix coding errors, run backtests, interpret backtest results, and optimize parameters. + The output of this assistant is an algorithm that's proven to underperform the market or an algorithm that's ready for testing in the paper trading environment. +

    -

     

    - -
    -
    -

    Community

    -

    Research

    -
    -
    -

    Introduction

    +

    Stage 3: Paper Trading

    The - - Research - - page contains articles from QuantConnect team members and community members that implement a particular trading strategy. - Review these articles to gain a better understanding of creating full trading algorithms with LEAN and the method to build them. - If you have an interesting strategy or research you want to share with the community, create a post and get featured on the Research page and distributed in the community mailing list. - Sharing research can be a great way to build a reputation within the QuantConnect community, reach potential employers, and earn - - QuantConnect Credit + + Paper Trading + + list on the Research Pipeline should contain a card for each project that your team is currently running live with the + + paper trading brokerage . + During the paper trading stage, the goal is to confirm the strategy works as expected in a live environment and diagnose any deviations. +

    +

    + To add cards to the + + Paper Trading + + list, drag-and-drop a card from the + + Backtest + + list. +

    +

    + If you need some help validating ideas with paper trading, add a + + Paper Testing Assistant + + to your organization. + This assistant has access to tools to deploy algorithms, analyze their live performance, and adjust the algorithm logic if necessary. + As it runs, it verifies the following questions: +

    + +

    + When any of these questions fail validation, the assistant reviews the code, debugs the algorithm, edits the logic, and redeploys the project. + If the assistant determines the strategy is not ready for live trading with real money, it stops the algorithm and sends you a report.

    -

    Submit Proposals

    +

    Stage 4: Live Trading

    - Before you investigate a trading idea, submit a proposal. - The proposal should describe the type of strategy you want to investigate, the universe and asset classes it's applied to, the - - datasets + The + + Live Trading + + list on the Research Pipeline should contain a card for each project that your organization is running live with real money. + During this final stage, the goal is to ensure the algorithm continues running and to intervene when necessary. + To add cards to the + + Live Trading + + list, drag-and-drop a card from the + + Paper Trading + + list. +

    +

    + If you need some help monitoring your live algorithms, add a + + Live Monitoring Assistant - it requires, results you expect to see, and any research papers you're using as a source. - Once you submit a proposal, we will review it and email you on whether it's approved or rejected. + to your organization. + This assistant has access to tools to deploy algorithms, read their current positions, and scan for material news events that affect the positions. + As it runs, it assesses the directional risk of your holdings given breaking news events. + When it detects an event that can significantly reduce the value of your holdings, it notifies you so you can act.

    + + + +

    Project Archive

    + +

    - We only approve research that is based on some financial concept, theory, or model. - We reject research that is just a combination of technical indicators and overfit parameters. - This approval process ensures you avoid spending time on research that won't end up being published. + The + + Archived Items + + section at the bottom of the Research Pipeline should contain a card for each project to which you're no longer allocating resources. + These may be ideas that failed the research validation stage, strategies that underperformed in the backtesting stage, or algorithms that lost their edge in live trading.

    - Follow these steps to submit a research proposal for the Research page: + To add projects to the archive, drag-and-drop a card from one of the stages of the Research Pipeline. + You can always recover projects from the archive later on. +

    + + + +

    Update Cards

    + + +

    + Follow these steps to update the title or body of a card on the Research Pipeline:

    1. - Open the - - Research - - page. + Click on a card. +

      + The + + Card + + tab shows the current title and description. +

      + Card tab showing the current title and description.
    2. Click - Share New Research + Edit Idea .
    3. - In the - - Title - - field, enter the title of your research. -
    4. -

      - The title must follow standard capitalization rules. For example, "Opening Range Breakout for Stocks in Play". -

      -
    5. - In the - - Content - - field, replace the placeholder text in the - - Introduction - - section of the template with the introduction of your submission. -
    6. -

      - During the proposal stage, the - - Introduction - - section should summarize your area of research. Explain what type of strategy it is, the universe and asset classes it's applied to, the - - datasets - - it requires, and results you expect to see. If the strategy is based on a research paper, reference the paper at the end of the introduction. -

      -
    7. - If you have references, list them in the - - References - - section. + Enter the new title and description.
    8. Click - Publish Research + Save .
    9. @@ -46778,797 +45624,7919 @@

      Submit Proposals

      -

      Publish Content

      +

      View Deployments

      - After you receive our email that your research proposal is approved, implement your strategy or research notebook and then follow these steps to add your findings to your research post: + When you assign a card to an Assistant and the Assistant is currently working on the task, the card displays “Running”. +

      + Card on the Research Pipeline displaying the Running status. +

      + To view the deployment details, click + + Running + + . + The deployment view page displays the output of the Assistant(s) assigned to the task. +

      + Deployment view showing the output of the Assistants assigned to the task. +

      + To open the project associated with the task, click the project link at the top of the deployment view. +

      +

      + To interrupt the assistant during its run, in the top-right corner of the deployment view, click the + + + stop + + icon. +

      +

      + To view all the current and historical tasks, follow these steps:

      1. - Open the - - Research + Log in to the + + Algorithm Lab - page. -
      2. -
      3. - Click on the draft of your research post. + .
      4. - On the discussion page that opens, in the top-right corner of the + In the left navigation bar, click - Introduction - - section, click the - - three dots - - icon and then click - - Edit + Organization > Assistants .
      5. - Update the text of the research post. -
      6. -
      7. - Attach a backtest or notebook. -
      8. -
      9. - Click - - Update + Click the + + Tasks - . + tab.
      + Tasks tab on the Assistants page showing current and historical tasks.

      - We will review your submission. - If your research follows our content guidelines and provides value to the community, we may publish it to the Research page. + From this view, you can click the + + Status + + column to open the deployment view of the task or click + + Delete + + to remove the task from the view.

      -

      Content Guidelines

      +

      Quotas

      - To get your research onto the Research page, it must respect by the following guidelines: + Assistants have a fair-use quota, but are essentially uncapped. + The number of agents you can concurrently run in your organization and the amount of tokens you can use depends on your team's + + Assistant Nodes + + .

      -
        -
      1. - The research is based on some financial concept, theory, or model. It’s not just a strategy of some technical indicators and overfit parameters. -
      2. -
      3. - The attached backtest or notebook is concise and contains plenty of comments. -
      4. -
      5. - If the code is Python, it follows the - - PEP8 style guide - - . -
      6. -
      7. - The text is well-written English without grammar or spelling errors. -
      8. + + + +

         

        + +
        +
        +

        Object Store

        + +
        +
        +

        Introduction

        + + +

        + The Object Store is an organization-specific key-value storage location to save and retrieve data in QuantConnect's cache. Similar to a dictionary or hash table, a key-value store is a storage system that saves and retrieves objects by using keys. A key is a unique string that is associated with a single record in the key-value store and a value is an object being stored. Some common use cases of the Object Store include the following: +

        +
        • - The text can't be generated by an LLM like ChatGPT. + Transporting data between the backtesting environment and the research environment.
        • - In-line code snippets (not code blocks) are in bold face. + Training machine learning models in the research environment before deploying them to live trading.
        • -
        • - If there are math symbols throughout the text, it uses LateX syntax (for example, - - \(x\) - - ). +
        +

        + The Object Store is shared across the entire organization. Using the same key, you can access data across all projects in an organization. +

        + + + +

        View Storage

        + + +

        + The Object Store page shows all the data your organization has in the Object Store. To view the page, log in to the Algorithm Lab and then, in the left navigation bar, click + + Organization > Object Store + + . +

        + Table of files and their sizes +

        + To view the metadata of a file (including it's path, size, and a content preview), click one of the files in the table. +

        + Panel of metadata + + + +

        Upload Files

        + + +

        + Follow these steps to upload files to the Object Store: +

        +
          +
        1. + Open the + + Object Store + + page.
        2. - Asset class names are capitalized (for example, “Equity”). + Navigate to the directory in the Object Store where you want to upload files.
        3. - The content has the following "Heading 1" sections: + Click + + Upload + + . +
        4. +
        5. + Drag and drop the files you want to upload.
        6. -
            -
          1. - - Introduction - - : This section summarizes your area of research. Explain what type of strategy it is, the universe and asset classes it’s applied to, the datasets it requires, and summarize results you found. If the strategy is based on a research paper, reference the paper at the end of the introduction. -
          2. -
          3. - - Background - - : This section provides background information on foundational concepts of the strategy, utilizing LateX syntax when necessary. For example, if the strategy creates low beta portfolios, this section should define what beta is and how it’s calculated. By the end of the Background section, readers should understand what the entire strategy is, the universe it’s applied to, the factors it uses, and the portfolio construction technique. -
          4. -
          5. - - Implementation - - : This section walks the reader through how to implement the strategy in LEAN. Explain each step in text and then include a short code snippet. When appropriate, link the content to relevant pages of the - - QuantConnect documentation - - so readers can learn more. -
          6. -
          7. - - Results - - : This section describes the backtest period, notes the Sharpe ratio, and explains if the strategy underperformed or outperformed the underlying benchmark. One of the paragraphs should analyze the parameter sensitivity and feature a screenshot of - - the Sharpe ratio heatmap from the optimization results page - - . End this section by discussing the results and some areas of further research. -
          8. -
          9. - - (Optional) - - - References - - : This section is a list of references in APA style to any source material like research papers. For more information on the APA style of reference lists, see - - Basic Principles of Reference List Entries - - on the APA Style website. -
          10. -
        +

        + Alternatively, you can + + add data to the Object Store in an algorithm + + or + + notebook + + . +

        -

        Examples

        +

        Download Files

        - Review the following research posts for examples that respect the content guidelines: + Permissioned + + Institutional + + clients can build derivative data such as machine learning models and download it from the Object Store. + + Contact us + + to unlock this feature for your account.

        - - - - -

         

        - -
        -
        -

        Community

        -

        Strategies

        -
        -
        -

        Introduction

        - - +
      9. + Click the + + Download + + link that appears. +
      10. +

      - The - - Strategies + If you can't download files from the Object Store, you can log data to the Object Store during a backtest and analyze it in the Research Environment. For a full walkthrough, see + + Example for Logging - page contains algorithms shared by community members for you to learn from or use for your investing. - QuantConnect backtests these strategies on behalf of the community each day, allowing you to see it it performs with out-of-sample data. - You can add strategies to your own watchlist, clone them into your own projects, and contribute your own strategies to the community. + .

      -

      Leaderboard

      +

      Storage Sizes

      - The Leaderboard lists top performing strategies ranked by performance metrics. -

      -

      - We rank strategies by their returns over the last three months daily. The chart shows the equity curve of the 10 highest ranked strategies for the aforementioned period, and the table below displays the relevant metrics: the three-month return, the one-year - - Sharpe ratio - - , and the score. -

      -

      - The score is the Sharpe ratio over one year with a penalty for strategies with less than one year of out-of-sample data. The penalty is proportional to the fraction of the year they have been in-of-sample. For example, a strategy that has been in-of-sample for six months will have its Sharpe ratio halved to calculate its score. A strategy submitted for over a year will have no penalty applied, and its score matches its Sharpe ratio. -

      -

      - - Submit your strategies - - to join the competition. + All organizations get 50 MB of free storage in the Object Store. Paid organizations can subscribe to more storage space. The following table shows the cost of the supported storage sizes:

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      + Storage Size (GB) + + Storage Files (-) + + Monthly Cost ($) +
      + 0.05 + + 1,000 + + 0 +
      + 2 + + 20,000 + + 10 +
      + 5 + + 50,000 + + 20 +
      + 10 + + 100,000 + + 50 +
      + 50 + + 500,000 + + 100 +
      + -

      Submit Your Strategies

      +

      Delete Storage

      - Follow these steps to submit a strategy for the Strategies page: + Follow these steps to delete storage from the Object Store:

      1. Open the - - Strategies + + Object Store page.
      2. - Click - - Publish Strategy - - . -
      3. -
      4. - In the - - Project - - field, select one of your projects. -
      5. -
      6. - In the - - Backtest - - field, select one of the backtests of the selected project. + Navigate to the directory in the Object Store where you want to delete files.
      7. -

        - The - - Strategy Name - - and - - Strategy Description - - fields are generated by Mia. -

      8. - In the - - Strategy Name - - field, change the generated name if needed. + Click the check box next to the files you want to delete.
      9. - In the - - Strategy Description + Click + + Actions - field, change the generated description if needed. -
      10. -
      11. - In the - - Version Notes + and then click + + Delete - field, describe any changes or updates in this version. + from the drop-down menu.
      12. Click - Publish + OK .
      +

      + Alternatively, you can + + delete storage in an algorithm + + or + + notebook + + . +

      -

      Submission Guidelines

      +

      Edit Storage Plan

      - To keep community strategies reliable, readable, and easy to maintain, respect the following guidelines when submitting or updating strategies: + You need + + storage billing permissions + + and a paid organization to edit the size of the organization's Object Store.

      -
        -
      • - The backtest must run without throwing buying power or runtime errors. -
      • -
      • - The code should not have - - try - - / - - except - - blocks. -
      • -
      • - If you use daily data and place a - - Scheduled Event - - to rebalance the portfolio, set the time rule to 8 AM. -
      • -
      • - Use current QuantConnect LEAN APIs and patterns; avoid deprecated methods. -
      • -
      • - Match scheduling cadence to data resolution (daily vs intraday vs monthly). -
      • +

        + Follow these steps to edit the amount of storage available in your organization's Object Store: +

        +
        1. - The backtest period must be the last 5 years - - self.set_start_date(self.end_date - timedelta(5*365)) - - backtest period. + Log in to the Algorithm Lab.
        2. - Follow - - PEP8-style + In the left navigation bar, click + + Organization > Resources - readability with a maximum line length of 120 characters. -
        3. -
        4. - Add a leading underscore to private class members. -
        5. -
        6. - Keep total backtest runtime under 1 hour. + .
        7. - Use the - - default models - + On the Resources page, scroll down to the + + Storage Resources + + and then click + + Add Object Store Capacity + . - Do not override models to artificially improve performance. For example: -
        8. - Strategies must not add or modify - - CashBook cash balances - - during a backtest (no cash deposits or withdrawals). - Initial capital must be set only via - - self.set_cash() - - or - - self.set_account_currency() - - . + On the Pricing page, select a storage plan.
        9. - If the algorithm has regular rebalances (weekly, monthly, quarterly, etc), call your rebalance logic in - - on_warmup_finished - - so the start of the equity curve isn't flat. + Click + + Proceed to Checkout + + .
        10. -
      + Project Object Store limit panel +
    -

     

    - -
    -
    -

    API Reference

    - -
    -
    +

    Research to Live Considerations

    -
    -
    -

    - The QuantConnect REST API lets you communicate with our cloud servers through URL endpoints. -

    -
    - @@ -28908,7 +28912,7 @@

    Data Provider

     

    -
    +

    Brokerages

    CFD and FOREX Brokerages

    @@ -29015,6 +29019,7 @@

    Deploy Cloud Algorithms

    17) Tastytrade 18) Eze 19) dYdX +20) Webull Enter an option: 4
  • @@ -29308,6 +29313,7 @@

    Deploy Local Algorithms

    17) Tastytrade 18) Eze 19) dYdX +20) Webull
  • @@ -29605,6 +29611,7 @@

    Deploy Local Algorithms

    17) Tastytrade 18) Eze 19) dYdX +20) Webull
    @@ -29758,6 +29765,7 @@

    Deploy Cloud Algorithms

    17) Tastytrade 18) Eze 19) dYdX +20) Webull Enter an option: 1
  • @@ -30058,6 +30066,7 @@

    Deploy Local Algorithms

    17) Tastytrade 18) Eze 19) dYdX +20) Webull
  • @@ -30331,6 +30340,7 @@

    Deploy Local Algorithms

    17) Tastytrade 18) Eze 19) dYdX +20) Webull @@ -30486,6 +30496,7 @@

    Deploy Cloud Algorithms

    17) Tastytrade 18) Eze 19) dYdX +20) Webull Enter an option: 1
  • @@ -30796,6 +30807,7 @@

    Deploy Local Algorithms

    17) Tastytrade 18) Eze 19) dYdX +20) Webull
  • @@ -32945,6 +32957,94 @@

    Live Trading Considerations

    +

    Special Folders

    + + +

    + Your organization's Object Store includes a reserved + + .assistant + + folder that the + + QuantConnect AI assistants + + read. + It holds the custom + + skills + + , + + memories + + , and + + templates + + you define for your organization, which let the assistants apply your own expertise, remember your preferences, and start new projects from your own scaffolds. +

    +

    + The + + .assistant + + folder contains the following subfolders: +

    + + + + + + + + + + + + + + + + + + + + + +
    + Subfolder + + Purpose +
    + + skills + + + Reusable instructions and conventions the assistants apply. +
    + + memories + + + Facts the assistants remember across conversations. +
    + + templates + + + Your own project scaffolds for the assistants to initialize for new projects. +
    +

    + These files are shared across your organization. To add or update them, see + + Cloud Storage + + . +

    + + +

     

    @@ -34308,7 +34408,9 @@

    Options


    dYdX
    - Databento> + Databento +
    + Webull> @@ -34907,6 +35009,46 @@

    Options

    Your Databento.com API Key + + + + --webull-environment <enum: live|paper> + + + + Whether Live or Paper environment should be used + + + + + + --webull-app-key <string> + + + + Your Webull App Key + + + + + + --webull-app-secret <string> + + + + Your Webull App Secret + + + + + + --webull-account-id <string> + + + + Your Webull account id + + @@ -36794,7 +36936,9 @@

    Options


    Eze
    - dYdX> + dYdX +
    + Webull>
    @@ -37422,7 +37566,7 @@

    Options

    - --bybit-use-testnet <enum: live|paper> + --bybit-use-testnet <enum: live|paper|demo> @@ -37559,6 +37703,46 @@

    Options

    Whether the developer sandbox should be used + + + + --webull-environment <enum: live|paper> + + + + Whether Live or Paper environment should be used + + + + + + --webull-app-key <string> + + + + Your Webull App Key + + + + + + --webull-app-secret <string> + + + + Your Webull App Secret + + + + + + --webull-account-id <string> + + + + Your Webull account id + + @@ -40633,7 +40817,9 @@

    Options


    dYdX
    - Databento> + Databento +
    + Webull>
    @@ -41232,6 +41418,46 @@

    Options

    Your Databento.com API Key + + + + --webull-environment <enum: live|paper> + + + + Whether Live or Paper environment should be used + + + + + + --webull-app-key <string> + + + + Your Webull App Key + + + + + + --webull-app-secret <string> + + + + Your Webull App Secret + + + + + + --webull-account-id <string> + + + + Your Webull account id + + @@ -45248,7 +45474,9 @@

    Options


    Eze
    - dYdX> + dYdX +
    + Webull>
    @@ -45356,7 +45584,9 @@

    Options


    dYdX
    - Databento> + Databento +
    + Webull> @@ -46024,7 +46254,7 @@

    Options

    - --bybit-use-testnet <enum: live|paper> + --bybit-use-testnet <enum: live|paper|demo> @@ -46161,6 +46391,46 @@

    Options

    Whether the developer sandbox should be used + + + + --webull-environment <enum: live|paper> + + + + Whether Live or Paper environment should be used + + + + + + --webull-app-key <string> + + + + Your Webull App Key + + + + + + --webull-app-secret <string> + + + + Your Webull App Secret + + + + + + --webull-account-id <string> + + + + Your Webull account id + + @@ -49103,7 +49373,9 @@

    Options


    dYdX
    - Databento> + Databento +
    + Webull>
    @@ -49792,6 +50064,46 @@

    Options

    Your Databento.com API Key + + + + --webull-environment <enum: live|paper> + + + + Whether Live or Paper environment should be used + + + + + + --webull-app-key <string> + + + + Your Webull App Key + + + + + + --webull-app-secret <string> + + + + Your Webull App Secret + + + + + + --webull-account-id <string> + + + + Your Webull account id + + @@ -51607,7 +51919,9 @@

    Options


    dYdX
    - Databento> + Databento +
    + Webull>
    @@ -52206,6 +52520,46 @@

    Options

    Your Databento.com API Key + + + + --webull-environment <enum: live|paper> + + + + Whether Live or Paper environment should be used + + + + + + --webull-app-key <string> + + + + Your Webull App Key + + + + + + --webull-app-secret <string> + + + + Your Webull App Secret + + + + + + --webull-account-id <string> + + + + Your Webull account id + + diff --git a/single-page/Quantconnect-Lean-Engine.html b/single-page/Quantconnect-Lean-Engine.html index 11003705c2..744ea047b2 100644 --- a/single-page/Quantconnect-Lean-Engine.html +++ b/single-page/Quantconnect-Lean-Engine.html @@ -1621,7 +1621,7 @@

    Using Processing Framework

    map_file_provider = LocalZipMapFileProvider()
    -map_file_provider.Initialize(DefaultDataProvider())
    +map_file_provider.initialize(DefaultDataProvider())
  • Create a security identifier. @@ -2146,7 +2146,7 @@

    Using Processing Framework

  • map_file_provider = LocalZipMapFileProvider()
    -map_file_provider.Initialize(DefaultDataProvider())
    +map_file_provider.initialize(DefaultDataProvider())
  • Create a security identifier. diff --git a/single-page/Quantconnect-Local-Platform.html b/single-page/Quantconnect-Local-Platform.html index a8f95acd8f..03ecde3e82 100644 --- a/single-page/Quantconnect-Local-Platform.html +++ b/single-page/Quantconnect-Local-Platform.html @@ -396,7 +396,7 @@

    Custom LEAN Images

    -

    On-Premises Compute

    +

    On-Premise Compute

    @@ -2581,10 +2581,21 @@

    Add Team Members

  • Click the - Select User... + Select User or Entire Organization... field and then click a member from the drop-down menu.
  • +

    + The drop-down menu also contains an entry with your organization's logo, labeled + + Entire + + organizationName + + Organization + + . This entry shares the project with all the members of your organization and counts as one collaborator slot. +

  • If you want to give the member @@ -2621,11 +2632,11 @@

    Collaborator Quotas

    - The number of members you can add to a project depends on your - + The number of collaborators you can add to a project depends on your + organization's tier - . The following table shows the number of collaborators each tier can have per project: + . This quota is separate from the number of members your organization can have. The following table shows the number of collaborators each tier can have per project:

    @@ -2669,7 +2680,7 @@

    Collaborator Quotas

    Trading Firm @@ -4035,9 +4046,12 @@

    Delete a parameter
  • Remove the - + GetParameter + + get_parameter + calls that were associated with the parameter from your code files.
  • @@ -5141,7 +5155,11 @@

    the data providers we support in the cloud - . Your live algorithms run on our co-located servers that have 10 GB transfer speeds and low latency. + . Your live algorithms run on our co-located servers racked in + + Equinix + + that have 10 GB transfer speeds and low latency.

    @@ -8530,8 +8548,10 @@

    Example

    bbdf[['price', 'lowerband', 'middleband', 'upperband']].plot();
    -
    #load "../QuantConnect.csx"
    -using QuantConnect;
    +   
    #load "../QuantConnect.csx"
    +
    +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
    diff --git a/single-page/Quantconnect-Research-Environment.html b/single-page/Quantconnect-Research-Environment.html
    index 8d40b9665c..140936942b 100644
    --- a/single-page/Quantconnect-Research-Environment.html
    +++ b/single-page/Quantconnect-Research-Environment.html
    @@ -105,7 +105,8 @@ 

    Table of Content

  • 10.2 Backtest Analysis
  • 10.3 Optimization Analysis
  • 10.4 Live Analysis
  • -
  • 10.5 Live Deployment Automation
  • +
  • 10.5 Live Reconciliation
  • +
  • 10.6 Live Deployment Automation
  • 11 Applying Research
  • 11.1 Key Concepts
  • 11.2 Mean Reversion
  • @@ -206,8 +207,10 @@

    Example

    bbdf[['price', 'lowerband', 'middleband', 'upperband']].plot();
    -
    #load "../QuantConnect.csx"
    -using QuantConnect;
    +   
    #load "../QuantConnect.csx"
    +
    +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
    @@ -915,8 +918,8 @@ 

    Import C# Libraries

    #load "../QuantConnect.csx"
    -#r "../Plotly.NET.dll"
    -#r "../Plotly.NET.Interactive.dll"
    +#r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
     #r "../Deedle.dll"
  • @@ -6035,12 +6038,16 @@

    The following example studies the trend on the SP500 EMini Future contract. To study the short term supply-demand relationship, we consolidate the data into 5 minute bars and calculate the bid and ask dollar volume.

    -
    +
    // Load the required assembly files and data types in a separate cell.
    -#load "../Initialize.csx"
    -
    -#load "../QuantConnect.csx"
    -using QuantConnect;
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
    +#load "../QuantConnect.csx"
    +
    +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Data.Market;
     using QuantConnect.Data.Consolidators;
    @@ -6151,13 +6158,17 @@ 

    Create Subscriptions

    #load "../Initialize.csx"
  • - Import the data types. + Load the necessary assembly files.
  • #load "../QuantConnect.csx"
    -#r "../Microsoft.Data.Analysis.dll"
    -
    -using QuantConnect;
    +#r "../Microsoft.Data.Analysis.dll"
    +
    +
  • + Import the data types. +
  • +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
    @@ -6505,14 +6516,26 @@ 

    Resolutions

  • - - @@ -6524,15 +6547,27 @@

    Resolutions

    SECOND - - @@ -6544,15 +6579,27 @@

    Resolutions

    MINUTE - - @@ -6564,14 +6611,26 @@

    Resolutions

    HOUR - @@ -6583,28 +6642,48 @@

    Resolutions

    DAILY -
    - Unlimited + 10
    + + - green check + + + ✓ - green check + + + ✓
    - green check + + + ✓ - green check + + + ✓ + +
    - green check + + + ✓ - green check + + + ✓ + +
    - green check + + + ✓ + + +
    - green check + + + ✓ + + +
    @@ -9578,7 +9657,7 @@

    To select a particular column of the DataFrame, index it with the column name.

    -
    +
    df["SPY close"]
    @@ -9797,7 +9876,7 @@

    .

    -
    +
    foreach (var slice in allHistorySlice) {
         if (slice.Bars.ContainsKey(spy))
         {
    @@ -9968,8 +10047,8 @@ 

    }

    for trade_bars in all_history_trade_bars:
         for kvp in trade_bars:
    -        symbol = kvp.Key
    -        trade_bar = kvp.Value
    + symbol = kvp.key + trade_bar = kvp.value

    @@ -10244,8 +10323,12 @@

  • import plotly.graph_objects as go
    -
    #r "../Plotly.NET.dll"
    -using Plotly.NET;
    +    
    #r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    using Plotly.NET;
    +using Plotly.NET.Interactive;
     using Plotly.NET.LayoutObjects;
  • @@ -10320,7 +10403,7 @@

  • fig.show()
    -
    HTML(GenericChart.toChartHTML(chart))
    +
    display(chart)

    Candlestick charts display the open, high, low, and close prices of the security. @@ -10420,7 +10503,7 @@

    plt.show()
    -
    HTML(GenericChart.toChartHTML(chart))
    +
    display(chart)

    Line charts display the value of the property you selected in a time series. @@ -10461,12 +10544,18 @@

    The following example studies the candlestick pattern of the SPY. To study the short term pattern, we consolidate the data into 5 minute bars and plot the 5-minute candlestick plot.

    -
    +
    // Load the required assembly files and data types in a separate cell.
    -#load "../Initialize.csx"
    -
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
     #load "../QuantConnect.csx"
    -using System;
    +#r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    using System;
     using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Data.Market;
    @@ -10476,9 +10565,8 @@ 

    using QuantConnect.Research; using QuantConnect.Securities; -// Import Plotly for plotting. -#r "../Plotly.NET.dll" using Plotly.NET; +using Plotly.NET.Interactive; using Plotly.NET.LayoutObjects; // Create a QuantBook. @@ -10529,7 +10617,7 @@

    // Assign the Layout to the chart. chart.WithLayout(layout); // Display the plot. -HTML(GenericChart.toChartHTML(chart))

    +display(chart)
    # Import plotly library for plotting.
     import plotly.graph_objects as go
     
    @@ -10608,13 +10696,17 @@ 

    Create Subscriptions

    #load "../Initialize.csx"
  • - Import the data types. + Load the necessary assembly files.
  • #load "../QuantConnect.csx"
    -#r "../Microsoft.Data.Analysis.dll"
    -
    -using QuantConnect;
    +#r "../Microsoft.Data.Analysis.dll"
    +
    +
  • + Import the data types. +
  • +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
    @@ -11163,10 +11255,11 @@ 

    Plot Data

    package.
    -
    #r "../Plotly.NET.dll"
    -#r "../Plotly.NET.Interactive.dll"
    -
    -using Plotly.NET;
    +    
    #r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    using Plotly.NET;
     using Plotly.NET.Interactive;
     using Plotly.NET.LayoutObjects;
    @@ -11256,7 +11349,7 @@

    Plot Data

    plt.show()
    -
    HTML(GenericChart.toChartHTML(chart))
    +
    display(chart)

    Line charts display the value of the property you selected in a time series. @@ -11279,20 +11372,25 @@

    The following example studies the trend of PE Ratio of AAPL using a line chart.

    -
    +
    // Load the required assembly files and data types in a separate cell.
    -#load "../Initialize.csx"
    -
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
     #load "../QuantConnect.csx"
    -using QuantConnect;
    +#r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Data.Fundamental;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
     
    -// Import Plotly for plotting.
    -#r "../Plotly.NET.dll"
     using Plotly.NET;
    +using Plotly.NET.Interactive;
     using Plotly.NET.LayoutObjects;
     
     // Create a QuantBook.
    @@ -11331,7 +11429,7 @@ 

    chart.WithLayout(layout); // Display the plot. -HTML(GenericChart.toChartHTML(chart))

    +display(chart)

    # Create a QuantBook
     qb = QuantBook()
     
    @@ -11612,13 +11710,17 @@ 

    Create Subscriptions

    #load "../Initialize.csx"
  • - Import the data types. + Load the necessary assembly files.
  • #load "../QuantConnect.csx"
    -#r "../Microsoft.Data.Analysis.dll"
    -
    -using QuantConnect;
    +#r "../Microsoft.Data.Analysis.dll"
    +
    +
  • + Import the data types. +
  • +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
    @@ -11879,12 +11981,18 @@ 

    The following example plots a line chart on the implied volatility curve of the closest expiring calls.

    -
    +
    // Load the required assembly files and data types in a separate cell.
    -#load "../Initialize.csx"
    -
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
     #load "../QuantConnect.csx"
    -using System;
    +#r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    using System;
     using System.Collections.Generic;
     using QuantConnect;
     using QuantConnect.Data;
    @@ -11893,9 +12001,8 @@ 

    using QuantConnect.Algorithm; using QuantConnect.Research; -// Import Plotly for plotting. -#r "../Plotly.NET.dll" using Plotly.NET; +using Plotly.NET.Interactive; using Plotly.NET.LayoutObjects; // Create a QuantBook. @@ -11946,7 +12053,7 @@

    chart.WithLayout(layout); // Display the plot. -HTML(GenericChart.toChartHTML(chart))

    +display(chart)

    # Instantiate a QuantBook instance.
     qb = QuantBook()
     # Set the date being studied.
    @@ -12139,13 +12246,17 @@ 

    Create Subscriptions

    #load "../Initialize.csx"
  • - Import the data types. + Load the necessary assembly files.
  • #load "../QuantConnect.csx"
    -#r "../Microsoft.Data.Analysis.dll"
    -
    -using QuantConnect;
    +#r "../Microsoft.Data.Analysis.dll"
    +
    +
  • + Import the data types. +
  • +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
    @@ -12577,7 +12688,7 @@ 

    Greeks and IV History

    mirror_contract_symbol = Symbol.create_option(
         option_contract.underlying.symbol,     contract_symbol.id.market,
         option_contract.style, 
    -    OptionRight.Call if option_contract.right == OptionRight.PUT else OptionRight.PUT,
    +    OptionRight.CALL if option_contract.right == OptionRight.PUT else OptionRight.PUT,
         option_contract.strike_price,
         option_contract.expiry
     )
    @@ -12759,10 +12870,13 @@

    xaxis_rangeslider_visible=False ) ).show()

    -
    #load "../QuantConnect.csx"
    -#r "../Plotly.NET.dll"
    -
    -using QuantConnect;
    +   
    // Load the necessary assembly files.
    +#load "../QuantConnect.csx"
    +#r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
    @@ -12770,6 +12884,7 @@ 

    using QuantConnect.Data.Market; using QuantConnect.Securities.Option; using Plotly.NET; +using Plotly.NET.Interactive; using Plotly.NET.LayoutObjects; // Get the SPY Option chain for January 1, 2024. @@ -12781,10 +12896,10 @@

    // Select a contract from the chain. var expiry = chain.Select(contract => contract.Expiry).Min(); var contractSymbol = chain - .Where(contract => - contract.Expiry == expiry && + .Where(contract => + contract.Expiry == expiry && contract.Right == OptionRight.Call && - contract.Greeks.Delta > 0.3m && + contract.Greeks.Delta > 0.3m && contract.Greeks.Delta < 0.7m ) .OrderByDescending(contract => contract.OpenInterest) @@ -12815,7 +12930,7 @@

    layout.SetValue("yaxis", yAxis); layout.SetValue("title", title); chart.WithLayout(layout); -HTML(GenericChart.toChartHTML(chart))

    +display(chart)
    Candlestick plot of the prices for a SPY Option contract Candlestick plot of the prices for a SPY Option contract @@ -12859,10 +12974,13 @@

    # Plot the open interest history. history.plot(title=f'{contract_symbol.value} Open Interest') plt.show() -
    #load "../QuantConnect.csx"
    -#r "../Plotly.NET.dll"
    -
    -using QuantConnect;
    +   
    // Load the necessary assembly files.
    +#load "../QuantConnect.csx"
    +#r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +

    +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
    @@ -12870,6 +12988,7 @@ 

    using QuantConnect.Data.Market; using QuantConnect.Securities.Option; using Plotly.NET; +using Plotly.NET.Interactive; using Plotly.NET.LayoutObjects; // Get the TSLA Option chain for January 1, 2024. @@ -12909,7 +13028,7 @@

    layout.SetValue("yaxis", yAxis); layout.SetValue("title", title); chart.WithLayout(layout); -HTML(GenericChart.toChartHTML(chart)) +display(chart)

    Line plot of the open interest for a TSLA Option contract @@ -12948,13 +13067,17 @@

    Create Subscriptions

    #load "../Initialize.csx"
  • - Import the data types. + Load the necessary assembly files.
  • #load "../QuantConnect.csx"
    -#r "../Microsoft.Data.Analysis.dll"
    -
    -using QuantConnect;
    +#r "../Microsoft.Data.Analysis.dll"
    +
    +
  • + Import the data types. +
  • +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
    @@ -13017,8 +13140,8 @@ 

    Create Subscriptions

    Supported Assets section of the - - CoinAPI dataset listings + + QuantConnect dataset listings .

    @@ -13184,9 +13307,12 @@

    To get historical data for a specific period of time, call the - + History + + history + method with the Symbol @@ -15570,7 +15696,7 @@

    To select a particular column of the DataFrame, index it with the column name.

    -
    +
    df["BTCUSD close"]
    @@ -15789,7 +15915,7 @@

    .

    -
    +
    foreach (var slice in allHistorySlice) {
         if (slice.Bars.ContainsKey(btcusd))
         {
    @@ -15960,8 +16086,8 @@ 

    }

    for trade_bars in all_history_trade_bars:
         for kvp in trade_bars:
    -        symbol = kvp.Key
    -        trade_bar = kvp.Value
    + symbol = kvp.key + trade_bar = kvp.value

    @@ -16236,8 +16362,12 @@

    import plotly.graph_objects as go
    -
    #r "../Plotly.NET.dll"
    -using Plotly.NET;
    +    
    #r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    using Plotly.NET;
    +using Plotly.NET.Interactive;
     using Plotly.NET.LayoutObjects;
  • @@ -16318,7 +16448,7 @@

  • fig.show()
    -
    HTML(GenericChart.toChartHTML(chart))
    +
    display(chart)

    Candlestick charts display the open, high, low, and close prices of the security. @@ -16426,7 +16556,7 @@

    plt.show()
    -
    HTML(GenericChart.toChartHTML(chart))
    +
    display(chart)

    Line charts display the value of the property you selected in a time series. @@ -16449,12 +16579,18 @@

    The following example studies the candlestick pattern of the BTCUSD. To study the short term pattern, we consolidate the data into 5 minute bars and plot the 5-minute candlestick plot.

    -
    +
    // Load the required assembly files and data types in a separate cell.
    -#load "../Initialize.csx"
    -
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
     #load "../QuantConnect.csx"
    -using System;
    +#r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    using System;
     using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Data.Market;
    @@ -16464,9 +16600,8 @@ 

    using QuantConnect.Research; using QuantConnect.Securities; -// Import Plotly for plotting. -#r "../Plotly.NET.dll" using Plotly.NET; +using Plotly.NET.Interactive; using Plotly.NET.LayoutObjects; // Create a QuantBook. @@ -16517,7 +16652,7 @@

    // Assign the Layout to the chart. chart.WithLayout(layout); // Display the plot. -HTML(GenericChart.toChartHTML(chart))

    +display(chart)
    # Import plotly library for plotting.
     import plotly.graph_objects as go
     
    @@ -16592,13 +16727,17 @@ 

    Create Subscriptions

    #load "../Initialize.csx"
  • - Import the data types. + Load the necessary assembly files.
  • #load "../QuantConnect.csx"
    -#r "../Microsoft.Data.Analysis.dll"
    -
    -using QuantConnect;
    +#r "../Microsoft.Data.Analysis.dll"
    +
    +
  • + Import the data types. +
  • +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
    @@ -19211,7 +19350,7 @@ 

    To select a particular column of the DataFrame, index it with the column name.

    -
    +
    df["BTCUSD close"]
    @@ -19430,7 +19569,7 @@

    .

    -
    +
    foreach (var slice in allHistorySlice) {
         if (slice.Bars.ContainsKey(btcusd))
         {
    @@ -19601,8 +19740,8 @@ 

    }

    for trade_bars in all_history_trade_bars:
         for kvp in trade_bars:
    -        symbol = kvp.Key
    -        trade_bar = kvp.Value
    + symbol = kvp.key + trade_bar = kvp.value

    @@ -19877,8 +20016,12 @@

    import plotly.graph_objects as go
    -
    #r "../Plotly.NET.dll"
    -using Plotly.NET;
    +    
    #r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    using Plotly.NET;
    +using Plotly.NET.Interactive;
     using Plotly.NET.LayoutObjects;
  • @@ -19959,7 +20102,7 @@

  • fig.show()
    -
    HTML(GenericChart.toChartHTML(chart))
    +
    display(chart)

    Candlestick charts display the open, high, low, and close prices of the security. @@ -19989,7 +20132,7 @@

    history = qb.history([btcusd, ethusd], datetime(2021, 11, 23), datetime(2021, 12, 8), Resolution.DAILY)
    -
    var history = qb.history(new List<Symbol> { btcusd, ethusd }, new DateTime(2021, 11, 23), new DateTime(2021, 12, 8), Resolution.DAILY);
    +
    var history = qb.history(new List<Symbol> { btcusd, ethusd }, new DateTime(2021, 11, 23), new DateTime(2021, 12, 8), Resolution.DAILY);
  • Select the data to plot. @@ -20067,7 +20210,7 @@

  • plt.show()
    -
    HTML(GenericChart.toChartHTML(chart))
    +
    display(chart)

    Line charts display the value of the property you selected in a time series. @@ -20090,12 +20233,18 @@

    The following example studies the candlestick pattern of the BTCUSDT Future. To study the short term pattern, we consolidate the data into 5 minute bars and plot the 5-minute candlestick plot.

    -
    +
    // Load the required assembly files and data types in a separate cell.
    -#load "../Initialize.csx"
    -
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
     #load "../QuantConnect.csx"
    -using System;
    +#r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    using System;
     using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Data.Market;
    @@ -20105,9 +20254,8 @@ 

    using QuantConnect.Research; using QuantConnect.Securities; -// Import Plotly for plotting. -#r "../Plotly.NET.dll" using Plotly.NET; +using Plotly.NET.Interactive; using Plotly.NET.LayoutObjects; // Create a QuantBook. @@ -20158,7 +20306,7 @@

    // Assign the Layout to the chart. chart.WithLayout(layout); // Display the plot. -HTML(GenericChart.toChartHTML(chart))

    +display(chart)
    # Import plotly library for plotting.
     import plotly.graph_objects as go
     
    @@ -20520,13 +20668,17 @@ 

    Create Subscriptions

    #load "../Initialize.csx"
  • - Import the data types. + Load the necessary assembly files.
  • #load "../QuantConnect.csx"
    -#r "../Microsoft.Data.Analysis.dll"
    -
    -using QuantConnect;
    +#r "../Microsoft.Data.Analysis.dll"
    +
    +
  • + Import the data types. +
  • +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
    @@ -20643,9 +20795,12 @@ 

    Create Subscriptions

    - + DataMappingMode.OpenInterest + + DataMappingMode.OPEN_INTEREST + @@ -20728,7 +20883,7 @@

    Contract Price History

    );
    # Set the contract filter to select contracts that expire within 180 days.
     history = qb.future_history(
    -    future.Symbol, datetime(2025, 4, 1), datetime(2025, 4, 3), Resolution.MINUTE, 
    +    future.symbol, datetime(2025, 4, 1), datetime(2025, 4, 3), Resolution.MINUTE,
         fill_forward=False, extended_market_hours=False
     )
    @@ -22233,12 +22388,18 @@

    The following example studies the candlestick pattern of the ES Future. To study the short term pattern, we consolidate the data into 5 minute bars and plot the 5-minute candlestick plot.

    -
    +
    // Load the required assembly files and data types in a separate cell.
    -#load "../Initialize.csx"
    -
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
     #load "../QuantConnect.csx"
    -using System;
    +#r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    using System;
     using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Data.Market;
    @@ -22248,9 +22409,8 @@ 

    using QuantConnect.Research; using QuantConnect.Securities; -// Import Plotly for plotting. -#r "../Plotly.NET.dll" using Plotly.NET; +using Plotly.NET.Interactive; using Plotly.NET.LayoutObjects; // Create a QuantBook. @@ -22306,7 +22466,7 @@

    // Assign the Layout to the chart. chart.WithLayout(layout); // Display the plot. -HTML(GenericChart.toChartHTML(chart))

    +display(chart)
    # Import plotly library for plotting.
     import plotly.graph_objects as go
     
    @@ -22394,13 +22554,17 @@ 

    Create Subscriptions

    #load "../Initialize.csx"
  • - Import the data types. + Load the necessary assembly files.
  • #load "../QuantConnect.csx"
    -#r "../Microsoft.Data.Analysis.dll"
    -
    -using QuantConnect;
    +#r "../Microsoft.Data.Analysis.dll"
    +
    +
  • + Import the data types. +
  • +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
    @@ -22981,18 +23145,21 @@ 

    #load "../Initialize.csx"

    -
    #r "../Plotly.NET.dll"
    -using Plotly.NET;
    -using Plotly.NET.LayoutObjects;
    +
    // Load the necessary assembly files.
    +#load "../QuantConnect.csx"
    +#r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    -
    #load "../QuantConnect.csx"
    -using QuantConnect;
    +   
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Research;
     using QuantConnect.Securities;
     using QuantConnect.Data.Market;
    -    
    +using Plotly.NET;
    +using Plotly.NET.Interactive;
    +using Plotly.NET.LayoutObjects;
    +
     var qb = new QuantBook();
     // Get the front-month ES contract as of December 31, 2021.
     var future = qb.AddFuture(Futures.Indices.SP500EMini);
    @@ -23021,7 +23188,7 @@ 

    layout.SetValue("title", title); chart.WithLayout(layout); -HTML(GenericChart.toChartHTML(chart))

    +display(chart)
    Candlestick plot of ES18H22 OHLC Candlestick plot of ES18H22 OHLC @@ -23063,18 +23230,21 @@

    #load "../Initialize.csx"

    -
    #r "../Plotly.NET.dll"
    -using Plotly.NET;
    -using Plotly.NET.LayoutObjects;
    +
    // Load the necessary assembly files.
    +#load "../QuantConnect.csx"
    +#r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    -
    #load "../QuantConnect.csx"
    -using QuantConnect;
    +   
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Research;
     using QuantConnect.Securities;
     using QuantConnect.Data.Market;
    -    
    +using Plotly.NET;
    +using Plotly.NET.Interactive;
    +using Plotly.NET.LayoutObjects;
    +
     var qb = new QuantBook();
     // Get the front-month ES contract as of December 31, 2021.
     var future = qb.AddFuture(Futures.Indices.SP500EMini);
    @@ -23100,7 +23270,7 @@ 

    layout.SetValue("title", title); chart.WithLayout(layout); -HTML(GenericChart.toChartHTML(chart))

    +display(chart)
    Line chart of close price of Future contracts Line chart of open interest of ES18H22 @@ -23345,13 +23515,17 @@

    Create Subscriptions

    #load "../Initialize.csx"
  • - Import the data types. + Load the necessary assembly files.
  • #load "../QuantConnect.csx"
    -#r "../Microsoft.Data.Analysis.dll"
    -
    -using QuantConnect;
    +#r "../Microsoft.Data.Analysis.dll"
    +
    +
  • + Import the data types. +
  • +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
    @@ -24027,12 +24201,18 @@ 

    The following example plots a line chart on the implied volatility curve of the closest expiring calls.

    -
    +
    // Load the required assembly files and data types in a separate cell.
    -#load "../Initialize.csx"
    -
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
     #load "../QuantConnect.csx"
    -using System;
    +#r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    using System;
     using System.Collections.Generic;
     using QuantConnect;
     using QuantConnect.Data;
    @@ -24041,9 +24221,8 @@ 

    using QuantConnect.Algorithm; using QuantConnect.Research; -// Import Plotly for plotting. -#r "../Plotly.NET.dll" using Plotly.NET; +using Plotly.NET.Interactive; using Plotly.NET.LayoutObjects; // Create a QuantBook. @@ -24098,7 +24277,7 @@

    chart.WithLayout(layout); // Display the plot. -HTML(GenericChart.toChartHTML(chart))

    +display(chart)

    # Instantiate a QuantBook instance.
     qb = QuantBook()
     # Set the date being studied.
    @@ -24115,7 +24294,7 @@ 

    fill_forward=False, extended_market_hours=False ) -chain = list(option_history)[-1].OptionChains.values()[0] +chain = list(option_history)[-1].option_chains.values()[0] # Study the closest expiring contracts. expiry = min(x.expiry for x in chain) # Filter for the closest expiring calls to study only. @@ -24164,13 +24343,17 @@

    Create Subscriptions

    #load "../Initialize.csx"
  • - Import the data types. + Load the necessary assembly files.
  • #load "../QuantConnect.csx"
    -#r "../Microsoft.Data.Analysis.dll"
    -
    -using QuantConnect;
    +#r "../Microsoft.Data.Analysis.dll"
    +
    +
  • + Import the data types. +
  • +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
    @@ -24585,7 +24768,7 @@ 

    Greeks and IV History

    mirror_contract_symbol = Symbol.create_option(
         option_contract.underlying.symbol,     fop_contract_symbol.id.market,
         option_contract.style, 
    -    OptionRight.Call if option_contract.right == OptionRight.PUT else OptionRight.PUT,
    +    OptionRight.CALL if option_contract.right == OptionRight.PUT else OptionRight.PUT,
         option_contract.strike_price,
         option_contract.expiry
     )
    @@ -24771,10 +24954,13 @@

    xaxis_rangeslider_visible=False ) ).show()

    -
    #load "../QuantConnect.csx"
    -#r "../Plotly.NET.dll"
    -
    -using QuantConnect;
    +   
    // Load the necessary assembly files.
    +#load "../QuantConnect.csx"
    +#r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
    @@ -24782,6 +24968,7 @@ 

    using QuantConnect.Data.Market; using QuantConnect.Securities; using Plotly.NET; +using Plotly.NET.Interactive; using Plotly.NET.LayoutObjects; // Add the underlying Future contract @@ -24829,7 +25016,7 @@

    layout.SetValue("yaxis", yAxis); layout.SetValue("title", title); chart.WithLayout(layout); -HTML(GenericChart.toChartHTML(chart))

    +display(chart)
    Candlestick plot of the prices for an ES Future Option contract Candlestick plot of the prices for an ES Future Option contract @@ -24867,13 +25054,17 @@

    Create Subscriptions

    #load "../Initialize.csx"

  • - Import the data types. + Load the necessary assembly files.
  • #load "../QuantConnect.csx"
    -#r "../Microsoft.Data.Analysis.dll"
    -
    -using QuantConnect;
    +#r "../Microsoft.Data.Analysis.dll"
    +
    +
  • + Import the data types. +
  • +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
    @@ -27209,7 +27400,7 @@ 

    To select a particular column of the DataFrame, index it with the column name.

    -
    +
    df["EURUSD close"]
    @@ -27428,7 +27619,7 @@

    .

    -
    +
    foreach (var slice in allHistorySlice) {
         if (slice.QuoteBars.ContainsKey(eurusd))
         {
    @@ -27761,8 +27952,12 @@ 

    import plotly.graph_objects as go
    -
    #r "../Plotly.NET.dll"
    -using Plotly.NET;
    +    
    #r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    using Plotly.NET;
    +using Plotly.NET.Interactive;
     using Plotly.NET.LayoutObjects;
  • @@ -27843,7 +28038,7 @@

  • fig.show()
    -
    HTML(GenericChart.toChartHTML(chart))
    +
    display(chart)

    Candlestick charts display the open, high, low, and close prices of the security. @@ -27951,7 +28146,7 @@

    plt.show()
    -
    HTML(GenericChart.toChartHTML(chart))
    +
    display(chart)

    Line charts display the value of the property you selected in a time series. @@ -27974,12 +28169,18 @@

    The following example studies the candlestick pattern of the USDJPY. To study the short term pattern, we consolidate the data into 5 minute bars and plot the 5-minute candlestick plot, using the mid prices.

    -
    +
    // Load the required assembly files and data types in a separate cell.
    -#load "../Initialize.csx"
    -
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
     #load "../QuantConnect.csx"
    -using System;
    +#r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    using System;
     using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Data.Market;
    @@ -27989,9 +28190,8 @@ 

    using QuantConnect.Research; using QuantConnect.Securities; -// Import Plotly for plotting. -#r "../Plotly.NET.dll" using Plotly.NET; +using Plotly.NET.Interactive; using Plotly.NET.LayoutObjects; // Create a QuantBook. @@ -28042,7 +28242,7 @@

    // Assign the Layout to the chart. chart.WithLayout(layout); // Display the plot. -HTML(GenericChart.toChartHTML(chart))

    +display(chart)

    # Import plotly library for plotting.
     import plotly.graph_objects as go
     
    @@ -28117,13 +28317,17 @@ 

    Create Subscriptions

    #load "../Initialize.csx"
  • - Import the data types. + Load the necessary assembly files.
  • #load "../QuantConnect.csx"
    -#r "../Microsoft.Data.Analysis.dll"
    -
    -using QuantConnect;
    +#r "../Microsoft.Data.Analysis.dll"
    +
    +
  • + Import the data types. +
  • +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
    @@ -30459,7 +30663,7 @@ 

    To select a particular column of the DataFrame, index it with the column name.

    -
    +
    df["SPX500USD close"]
    @@ -30678,7 +30882,7 @@

    .

    -
    +
    foreach (var slice in allHistorySlice) {
         if (slice.QuoteBars.ContainsKey(spx))
         {
    @@ -31011,8 +31215,12 @@ 

    import plotly.graph_objects as go
    -
    #r "../Plotly.NET.dll"
    -using Plotly.NET;
    +    
    #r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    using Plotly.NET;
    +using Plotly.NET.Interactive;
     using Plotly.NET.LayoutObjects;
  • @@ -31093,7 +31301,7 @@

  • fig.show()
    -
    HTML(GenericChart.toChartHTML(chart))
    +
    display(chart)

    Candlestick charts display the open, high, low, and close prices of the security. @@ -31201,7 +31409,7 @@

    plt.show()
    -
    HTML(GenericChart.toChartHTML(chart))
    +
    display(chart)

    Line charts display the value of the property you selected in a time series. @@ -31224,12 +31432,18 @@

    The following example studies the candlestick pattern of the XAUUSD. To study the short term pattern, we consolidate the data into 5 minute bars and plot the 5-minute candlestick plot, using the mid prices.

    -
    +
    // Load the required assembly files and data types in a separate cell.
    -#load "../Initialize.csx"
    -
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
     #load "../QuantConnect.csx"
    -using System;
    +#r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    using System;
     using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Data.Market;
    @@ -31239,9 +31453,8 @@ 

    using QuantConnect.Research; using QuantConnect.Securities; -// Import Plotly for plotting. -#r "../Plotly.NET.dll" using Plotly.NET; +using Plotly.NET.Interactive; using Plotly.NET.LayoutObjects; // Create a QuantBook. @@ -31292,7 +31505,7 @@

    // Assign the Layout to the chart. chart.WithLayout(layout); // Display the plot. -HTML(GenericChart.toChartHTML(chart))

    +display(chart)

    # Import plotly library for plotting.
     import plotly.graph_objects as go
     
    @@ -31367,13 +31580,17 @@ 

    Create Subscriptions

    #load "../Initialize.csx"
  • - Import the data types. + Load the necessary assembly files.
  • #load "../QuantConnect.csx"
    -#r "../Microsoft.Data.Analysis.dll"
    -
    -using QuantConnect;
    +#r "../Microsoft.Data.Analysis.dll"
    +
    +
  • + Import the data types. +
  • +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
    @@ -33081,7 +33298,7 @@ 

    To select a particular column of the DataFrame, index it with the column name.

    -
    +
    df["SPX close"]
    @@ -33300,7 +33517,7 @@

    .

    -
    +
    foreach (var slice in allHistorySlice) {
         if (slice.Bars.ContainsKey(spx))
         {
    @@ -33453,8 +33670,8 @@ 

    }

    for trade_bars in all_history_trade_bars:
         for kvp in trade_bars:
    -        symbol = kvp.Key
    -        trade_bar = kvp.Value
    + symbol = kvp.key + trade_bar = kvp.value

    @@ -33633,8 +33850,12 @@

    import plotly.graph_objects as go
    -
    #r "../Plotly.NET.dll"
    -using Plotly.NET;
    +    
    #r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    using Plotly.NET;
    +using Plotly.NET.Interactive;
     using Plotly.NET.LayoutObjects;
  • @@ -33715,7 +33936,7 @@

  • fig.show()
    -
    HTML(GenericChart.toChartHTML(chart))
    +
    display(chart)

    Candlestick charts display the open, high, low, and close prices of the security. @@ -33823,7 +34044,7 @@

    plt.show()
    -
    HTML(GenericChart.toChartHTML(chart))
    +
    display(chart)

    Line charts display the value of the property you selected in a time series. @@ -33846,12 +34067,18 @@

    The following example studies the candlestick pattern of the SPX. To study the short term pattern, we consolidate the data into 5 minute bars and plot the 5-minute candlestick plot.

    -
    +
    // Load the required assembly files and data types in a separate cell.
    -#load "../Initialize.csx"
    -
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
     #load "../QuantConnect.csx"
    -using System;
    +#r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    using System;
     using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Data.Market;
    @@ -33861,9 +34088,8 @@ 

    using QuantConnect.Research; using QuantConnect.Securities; -// Import Plotly for plotting. -#r "../Plotly.NET.dll" using Plotly.NET; +using Plotly.NET.Interactive; using Plotly.NET.LayoutObjects; // Create a QuantBook. @@ -33914,7 +34140,7 @@

    // Assign the Layout to the chart. chart.WithLayout(layout); // Display the plot. -HTML(GenericChart.toChartHTML(chart))

    +display(chart)

    # Import plotly library for plotting.
     import plotly.graph_objects as go
     
    @@ -34198,13 +34424,17 @@ 

    Create Subscriptions

    #load "../Initialize.csx"
  • - Import the data types. + Load the necessary assembly files.
  • #load "../QuantConnect.csx"
    -#r "../Microsoft.Data.Analysis.dll"
    -
    -using QuantConnect;
    +#r "../Microsoft.Data.Analysis.dll"
    +
    +
  • + Import the data types. +
  • +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
    @@ -34481,12 +34711,18 @@ 

    The following example plots a line chart on the implied volatility curve of the closest expiring calls.

    -
    +
    // Load the required assembly files and data types in a separate cell.
    -#load "../Initialize.csx"
    -
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
     #load "../QuantConnect.csx"
    -using System;
    +#r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    using System;
     using System.Collections.Generic;
     using QuantConnect;
     using QuantConnect.Data;
    @@ -34495,9 +34731,8 @@ 

    using QuantConnect.Algorithm; using QuantConnect.Research; -// Import Plotly for plotting. -#r "../Plotly.NET.dll" using Plotly.NET; +using Plotly.NET.Interactive; using Plotly.NET.LayoutObjects; // Create a QuantBook. @@ -34549,7 +34784,7 @@

    chart.WithLayout(layout); // Display the plot. -HTML(GenericChart.toChartHTML(chart))

    +display(chart)

    # Instantiate a QuantBook instance.
     qb = QuantBook()
     # Set the date being studied.
    @@ -34744,13 +34979,17 @@ 

    Create Subscriptions

    #load "../Initialize.csx"
  • - Import the data types. + Load the necessary assembly files.
  • #load "../QuantConnect.csx"
    -#r "../Microsoft.Data.Analysis.dll"
    -
    -using QuantConnect;
    +#r "../Microsoft.Data.Analysis.dll"
    +
    +
  • + Import the data types. +
  • +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
    @@ -35199,7 +35438,7 @@ 

    Greeks and IV History

    mirror_contract_symbol = Symbol.create_option(
         option_contract.underlying.symbol,  contract_symbol.id.symbol,     contract_symbol.id.market,
         option_contract.style, 
    -    OptionRight.Call if option_contract.right == OptionRight.PUT else OptionRight.PUT,
    +    OptionRight.CALL if option_contract.right == OptionRight.PUT else OptionRight.PUT,
         option_contract.strike_price,
         option_contract.expiry
     )
    @@ -35383,10 +35622,13 @@

    xaxis_rangeslider_visible=False ) ).show()

    -
    #load "../QuantConnect.csx"
    -#r "../Plotly.NET.dll"
    -
    -using QuantConnect;
    +   
    // Load the necessary assembly files.
    +#load "../QuantConnect.csx"
    +#r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
    @@ -35394,6 +35636,7 @@ 

    using QuantConnect.Data.Market; using QuantConnect.Securities.Option; using Plotly.NET; +using Plotly.NET.Interactive; using Plotly.NET.LayoutObjects; // Get the SPX Option chain for January 1, 2024. @@ -35441,7 +35684,7 @@

    layout.SetValue("yaxis", yAxis); layout.SetValue("title", title); chart.WithLayout(layout); -HTML(GenericChart.toChartHTML(chart))

    +display(chart)

    Candlestick plot of the prices for an SPX Index Option contract Candlestick plot of the prices for an SPX Index Option contract @@ -35487,10 +35730,13 @@

    # Plot the open interest history. history.plot(title=f'{contract_symbol.value} Open Interest') plt.show() -
    #load "../QuantConnect.csx"
    -#r "../Plotly.NET.dll"
    -
    -using QuantConnect;
    +   
    // Load the necessary assembly files.
    +#load "../QuantConnect.csx"
    +#r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    + +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
    @@ -35498,6 +35744,7 @@ 

    using QuantConnect.Data.Market; using QuantConnect.Securities.Option; using Plotly.NET; +using Plotly.NET.Interactive; using Plotly.NET.LayoutObjects; // Get the VIX weekly Option chain for January 1, 2024. @@ -35539,7 +35786,7 @@

    layout.SetValue("yaxis", yAxis); layout.SetValue("title", title); chart.WithLayout(layout); -HTML(GenericChart.toChartHTML(chart)) +display(chart)

    Line plot of the open interest for a VIWX Index Option contract @@ -35583,11 +35830,14 @@

    Create Subscriptions

    Load the required assembly files and data types.
    -
    #load "../Initialize.csx"
    -#load "../QuantConnect.csx"
    -#r "../Microsoft.Data.Analysis.dll"
    -
    -using QuantConnect;
    +    
    #load "../Initialize.csx"
    +
    +
    +
    #load "../QuantConnect.csx"
    +#r "../Microsoft.Data.Analysis.dll"
    +
    +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
    @@ -35704,16 +35954,16 @@ 

    var subsetHistoryDataObjects = qb.History<CBOE>(new[] {vix, v3m}, 10); var allHistoryDataObjects = qb.History<CBOE>(qb.Securities.Keys, 10);

    # DataFrame
    -single_history_df = qb.History(vix, 10)
    -subset_history_df = qb.History([vix, v3m], 10)
    -all_history_df = qb.History(qb.Securities.Keys, 10)
    +single_history_df = qb.history(vix, 10)
    +subset_history_df = qb.history([vix, v3m], 10)
    +all_history_df = qb.history(qb.securities.keys(), 10)
     
     # Slice objects
    -all_history_slice = qb.History(10)
    +all_history_slice = qb.history(10)
     
     # CBOE objects
    -single_history_data_objects = qb.History[CBOE](vix, 10)
    -subset_history_data_objects = qb.History[CBOE]([vix, v3m], 10)
    all_history_data_objects = qb.History[CBOE](qb.Securities.Keys, 10)
    +single_history_data_objects = qb.history[CBOE](vix, 10) +subset_history_data_objects = qb.history[CBOE]([vix, v3m], 10)
    all_history_data_objects = qb.history[CBOE](qb.securities.keys(), 10)

    The preceding calls return the most recent bars, excluding periods of time when the exchange was closed. @@ -35752,15 +36002,15 @@

    // CBOE objects var singleHistoryDataObjects = qb.History<CBOE>(vix, TimeSpan.FromDays(3));
    var subsetHistoryDataObjects = qb.History<CBOE>(new[] {vix, v3m}, TimeSpan.FromDays(3));
    var allHistoryDataObjects = qb.History<CBOE>(TimeSpan.FromDays(3));
    # DataFrame
    -single_history_df = qb.History(vix, timedelta(days=3))
    -subset_history_df = qb.History([vix, v3m], timedelta(days=3))
    -all_history_df = qb.History(qb.Securities.Keys, timedelta(days=3))
    +single_history_df = qb.history(vix, timedelta(days=3))
    +subset_history_df = qb.history([vix, v3m], timedelta(days=3))
    +all_history_df = qb.history(qb.securities.keys(), timedelta(days=3))
     
     # Slice objects
    -all_history_slice = qb.History(timedelta(days=3))
    +all_history_slice = qb.history(timedelta(days=3))
     
     # CBOE objects
    -single_history_data_objects = qb.History[CBOE](vix, timedelta(days=3))
    subset_history_data_objects = qb.History[CBOE]([vix, v3m], timedelta(days=3))
    all_history_data_objects = qb.History[CBOE](qb.Securities.Keys, timedelta(days=3))
    +single_history_data_objects = qb.history[CBOE](vix, timedelta(days=3))
    subset_history_data_objects = qb.history[CBOE]([vix, v3m], timedelta(days=3))
    all_history_data_objects = qb.history[CBOE](qb.securities.keys(), timedelta(days=3))

    The preceding calls return the most recent bars or ticks, excluding periods of time when the exchange was closed. @@ -35816,15 +36066,15 @@

    end_time = datetime(2021, 3, 1) # DataFrame -single_history_df = qb.History(vix, start_time, end_time) -subset_history_df = qb.History([vix, v3m], start_time, end_time) -all_history_df = qb.History(qb.Securities.Keys, start_time, end_time) +single_history_df = qb.history(vix, start_time, end_time) +subset_history_df = qb.history([vix, v3m], start_time, end_time) +all_history_df = qb.history(qb.securities.keys(), start_time, end_time) # Slice objects -all_history_slice = qb.History(start_time, end_time) +all_history_slice = qb.history(start_time, end_time) # CBOE objects -single_history_data_objects = qb.History[CBOE](vix, start_time, end_time)
    subset_history_data_objects = qb.History[CBOE]([vix, v3m], start_time, end_time)
    all_history_data_objects = qb.History[CBOE](qb.Securities.Keys, start_time, end_time)
    +single_history_data_objects = qb.history[CBOE](vix, start_time, end_time)
    subset_history_data_objects = qb.history[CBOE]([vix, v3m], start_time, end_time)
    all_history_data_objects = qb.history[CBOE](qb.securities.keys(), start_time, end_time)

    The preceding calls return the bars or ticks that have a timestamp within the defined period of time. @@ -37048,7 +37298,7 @@

    To select a particular column of the DataFrame, index it with the column name.

    -
    +
    df["VIX close"]
    @@ -37214,7 +37464,7 @@

    .

    -
    +
    foreach (var slice in allHistorySlice) {
         if (slice.ContainsKey(vix))
         {
    @@ -37293,8 +37543,12 @@ 

    import plotly.graph_objects as go
    -
    #r "../Plotly.NET.dll"
    -using Plotly.NET;
    +    
    #r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    using Plotly.NET;
    +using Plotly.NET.Interactive;
     using Plotly.NET.LayoutObjects;
  • @@ -37375,7 +37629,7 @@

  • fig.show()
    -
    HTML(GenericChart.toChartHTML(chart))
    +
    display(chart)

    Candlestick charts display the open, high, low, and close prices of the alternative data. @@ -37483,7 +37737,7 @@

    plt.show()
    -
    HTML(GenericChart.toChartHTML(chart))
    +
    display(chart)

    Line charts display the value of the property you selected in a time series. @@ -37506,20 +37760,25 @@

    The following example studies the trend of 10-year yield curve using a line chart.

    -
    +
    // Load the required assembly files and data types in a separate cell.
    -#load "../Initialize.csx"
    -
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
     #load "../QuantConnect.csx"
    -using QuantConnect;
    +#r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.DataSource;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
     
    -// Import Plotly for plotting.
    -#r "../Plotly.NET.dll"
     using Plotly.NET;
    +using Plotly.NET.Interactive;
     using Plotly.NET.LayoutObjects;
     
     // Create a QuantBook.
    @@ -37558,7 +37817,7 @@ 

    chart.WithLayout(layout); // Display the plot. -HTML(GenericChart.toChartHTML(chart))

    +display(chart)

    # Create a QuantBook
     qb = QuantBook()
     
    @@ -37622,12 +37881,15 @@ 

    Define Custom Data

    methods.

    +
    +
    #load "../Initialize.csx"
    +
    +
    +
    #load "../QuantConnect.csx"
    +#r "../Microsoft.Data.Analysis.dll"
    +
    -
    #load "../Initialize.csx"
    -#load "../QuantConnect.csx"
    -#r "../Microsoft.Data.Analysis.dll"
    -
    -using QuantConnect;
    +   
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
    @@ -38168,8 +38430,12 @@ 

    import plotly.graph_objects as go
    -
    #r "../Plotly.NET.dll"
    -using Plotly.NET;
    +    
    #r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    using Plotly.NET;
    +using Plotly.NET.Interactive;
     using Plotly.NET.LayoutObjects;
  • @@ -38250,7 +38516,7 @@

  • fig.show()
    -
    HTML(GenericChart.toChartHTML(chart))
    +
    display(chart)

    Candlestick charts display the open, high, low, and close prices of the security. @@ -38344,7 +38610,7 @@

    plt.show()
    -
    HTML(GenericChart.toChartHTML(chart))
    +
    display(chart)

    Line charts display the value of the property you selected in a time series. @@ -39968,21 +40234,23 @@

    // Load the assembly files and data types in their own cell.
    -#load "../Initialize.csx"
    -
    -// Load the necessary assembly files.
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
     #load "../QuantConnect.csx"
    -#r "../Plotly.NET.dll"
    -#r "../Plotly.NET.Interactive.dll"
    -
    -// Import the QuantConnect, Plotly.NET, and Accord packages for calculation and plotting.
    +#r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    // Import the QuantConnect, Plotly.NET, and Accord packages for calculation and plotting.
     using QuantConnect;
     using QuantConnect.Research;
    -            
    +
     using Plotly.NET;
     using Plotly.NET.Interactive;
     using Plotly.NET.LayoutObjects;
    -            
    +
     using Accord.Math;
     using Accord.Statistics;
    @@ -40054,7 +40322,7 @@

    Create Candlestick Chart

    chart.WithLayout(layout); // Show the plot. -HTML(GenericChart.toChartHTML(chart));
    +display(chart);

    The Jupyter Notebook displays the candlestick chart. @@ -40103,7 +40371,7 @@

    Create Line Chart

    chart.WithLayout(layout); // Show the plot. -HTML(GenericChart.toChartHTML(chart));
    +display(chart);

    The Jupyter Notebook displays the line chart. @@ -40152,7 +40420,7 @@

    Create Scatter Plot

    chart.WithLayout(layout); // Show the plot. -HTML(GenericChart.toChartHTML(chart)); +display(chart);

    The Jupyter Notebook displays the scatter plot. @@ -40213,7 +40481,7 @@

    Create Heat Map

    heatmap.WithLayout(layout); // Show the plot. -HTML(GenericChart.toChartHTML(heatmap)) +display(heatmap)

    The Jupyter Notebook displays the heat map. @@ -40268,7 +40536,7 @@

    Create 3D Chart

    chart.WithLayout(layout); // Show the plot. -HTML(GenericChart.toChartHTML(heatmap)) +display(chart)

    The Jupyter Notebook displays the scatter plot. @@ -40308,8 +40576,11 @@

    Get Universe Data

    method. The object that returns contains a universe data collection for each day. With this object, you can iterate through each day and then iterate through the universe data objects of each day to analyze the universe constituents.

    +

    + Fundamental Universes +

    - For example, follow these steps to get the US Equity Fundamental data for a specific universe: + Follow these steps to get the US Equity Fundamental data for a specific universe:

    1. @@ -40474,537 +40745,905 @@

      Get Universe Data

      }
    - - - -

    Available Universes

    - - - +

    + ETF Constituents Universes +

    - To get universe data for other types of universes, you usually just need to replace - - Fundamental - - in the preceding code snippets with the universe data type. - The following table shows the datasets that support universe selection and their respective data type. - For more information, about universe selection with these datasets and the data points you can use in the filter function, see the dataset's documentation. + Follow these steps to get the data of a specific ETF Constituents universe:

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
      +
    1. + Load the assembly files and data types in their own cell. +
    2. +
      +
      #load "../Initialize.csx"
      +
      +
    3. + Load the necessary assembly files. +
    4. +
      +
      #load "../QuantConnect.csx"
      +
      +
    5. + Import the + + QuantConnect + + package and the universe data type. +
    6. +
      +
      using QuantConnect;
      +using QuantConnect.Research;
      +using QuantConnect.Data.UniverseSelection;
      +
      +
    7. + Create a + + QuantBook + + . +
    8. +
      +
      var qb = new QuantBook();
      +
      qb = QuantBook()
      +
      +
    9. + Define a universe. +
    10. +

      + The following example defines a dynamic universe that contains the 3 Equities with the largest weight in the SPY ETF. + To see all the + + ETFConstituentUniverse + + attributes you can use to define a filter function for an ETF Constituents universe, see + + Data Point Attributes + + . + To create the universe, call the + + Universe.ETF + + + universe.etf + + method with the ETF ticker and the selection function, then pass the result to the + + AddUniverse + + + add_universe + + method. +

      +
      +
      var universe = qb.AddUniverse(
      +    qb.Universe.ETF("SPY", universeFilterFunc: constituents => constituents
      +        .Where(c => c.Weight != 0m)
      +        .OrderBy(c => c.Weight)
      +        .TakeLast(3)
      +        .Select(c => c.Symbol)
      +    )
      +);
      +
      def select_assets(constituents: List[ETFConstituentUniverse]) -> List[Symbol]:
      +    sorted_by_weight = sorted([c for c in constituents if c.weight], key=lambda c: c.weight)
      +    return [c.symbol for c in sorted_by_weight[-3:]]
      +
      +universe = qb.add_universe(qb.universe.etf('SPY', universe_filter_func=select_assets))
      +
      +
    11. + Call the + + UniverseHistory + + + universe_history + + method with the universe, a start date, and an end date. +
    12. +
      +
      var universeHistory = qb.UniverseHistory(universe, new DateTime(2025, 11, 6), new DateTime(2025, 11, 11));
      +
      universe_history = qb.universe_history(universe, datetime(2025, 11, 6), datetime(2025, 11, 11))
      +
      +

      + The end date arguments is optional. If you omit it, the method returns + + ETFConstituentUniverse + + data between the start date and the current day. +

      +

      + The + + universe_history + + method returns a Series where the multi-index is the universe + + Symbol + + and the time when universe selection would occur in a backtest. + Each row in the data column contains a list of + + ETFConstituentUniverse + + objects. + Here is an example of a Series: +

      +
      +
      time
      +2025-11-07    [AAPL R735QTJ8XC9X: ¤0.00, MSFT R735QTJ8XC9X: ...
      +2025-11-08    [AAPL R735QTJ8XC9X: ¤0.00, MSFT R735QTJ8XC9X: ...
      +2025-11-11    [AAPL R735QTJ8XC9X: ¤0.00, MSFT R735QTJ8XC9X: ...
      +Name: data, dtype: object
      +
      +

      + To get a flat DataFrame instead of a Series, set the + + flatten + + argument to + + True + + . +

      +
      +
      qb.universe_history(universe, datetime(2025, 11, 6), datetime(2025, 11, 11), flatten=True)
      +
      +
      +
    - Dataset Name - - Universe Type(s) - - Documentation -
    - International Future Universe - -
      -
    • - - FutureFilterUniverse - -
    • -
    • - - FutureUniverse - -
    • -
    -
    - - Learn more - -
    - US ETF Constituents - - - ETFConstituentUniverse - - - - Learn more - -
    - US Equity Option Universe - -
      -
    • - - OptionFilterUniverse - -
    • -
    • - - OptionUniverse - -
    • -
    -
    - - Learn more - -
    - US Future Option Universe - -
      -
    • - - OptionFilterUniverse - -
    • -
    • - - OptionUniverse - -
    • -
    -
    - - Learn more - -
    - US Future Universe - -
      -
    • - - FutureFilterUniverse - -
    • -
    • - - FutureUniverse - -
    • -
    -
    - - Learn more - -
    - US Index Option Universe - -
      -
    • - - OptionFilterUniverse - -
    • -
    • - - OptionUniverse - -
    • -
    -
    - - Learn more - -
    - Binance Crypto Price Data - - - CryptoUniverse - - - - Learn more - -
    - Binance US Crypto Price Data - - - CryptoUniverse - - - - Learn more - -
    - Bitfinex Crypto Price Data - - - CryptoUniverse - - - - Learn more - -
    - Bybit Crypto Price Data - - - CryptoUniverse - - - - Learn more - -
    - Coinbase Crypto Price Data - - - CryptoUniverse - - - - Learn more - -
    - Kraken Crypto Price Data - - - CryptoUniverse - - - - Learn more - -
    - Brain Language Metrics on Company Filings - - - BrainCompanyFilingLanguageMetricsUniverse - -
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + lastupdate + + period + + sharesheld + + weight +
    + time + + symbol + + + + +
    + 2025-11-07 + + AAPL R735QTJ8XC9X + + 2025-11-05 + + 1 days + + 178991437.0 + + 0.069116 +
    + MSFT R735QTJ8XC9X + + 2025-11-05 + + 1 days + + 89652295.0 + + 0.064993 +
    + NVDA RHM8UTD8DT2D + + 2025-11-05 + + 1 days + + 294289223.0 + + 0.082117 +
    + 2025-11-08 + + AAPL R735QTJ8XC9X + + 2025-11-06 + + 1 days + + 179242780.0 + + 0.069801 +
    + MSFT R735QTJ8XC9X + + 2025-11-06 + + 1 days + + 89778184.0 + + 0.064423 +
    + NVDA RHM8UTD8DT2D + + 2025-11-06 + + 1 days + + 294702444.0 + + 0.080012 +
    + 2025-11-11 + + AAPL R735QTJ8XC9X + + 2025-11-07 + + 1 days + + 178922101.0 + + 0.069303 +
    + MSFT R735QTJ8XC9X + + 2025-11-07 + + 1 days + + 89617567.0 + + 0.064299 +
    + NVDA RHM8UTD8DT2D + + 2025-11-07 + + 1 days + + 294175231.0 + + 0.079933 +
    + +

    + The + + UniverseHistory + + method returns an + + IEnumerable<IEnumerable<ETFConstituentUniverse>> + + object. +

    +
  • + Iterate through the Series to access the universe data. +
  • +
  • + Iterate through the result to access the universe data. +
  • +
    +
    for date, constituents in universe_history.droplevel('symbol', axis=0).items():
    +    for constituent in constituents:
    +        symbol = constituent.symbol
    +        weight = constituent.weight
    +
    foreach (var constituents in universeHistory)
    +{
    +    foreach (ETFConstituentUniverse constituent in constituents)
    +    {
    +        var symbol = constituent.Symbol;
    +        var weight = constituent.Weight;
    +    }
    +}
    +
    + + + + +

    Available Universes

    + + + +

    + The following table shows the datasets that support universe selection and their respective data type. + For more information, about universe selection with these datasets and the data points you can use in the filter function, see the dataset's documentation. +

    + + + + + + + + + + + + - -
    + Dataset Name + + Universe Type(s) + + Documentation +
    - + Binance Crypto Price Data + + + CryptoUniverse + + + Learn more
    - Brain ML Stock Ranking + Binance US Crypto Price Data - BrainStockRankingUniverse + CryptoUniverse - + Learn more
    - Brain Sentiment Indicator + Bitfinex Crypto Price Data - BrainSentimentIndicatorUniverse + CryptoUniverse - + Learn more
    - Crypto Market Cap + Bybit Crypto Price Data - CoinGeckoUniverse + CryptoUniverse - + Learn more
    - CNBC Trading + Coinbase Crypto Price Data - QuiverCNBCsUniverse + CryptoUniverse - + Learn more
    - Corporate Lobbying + International Future Universe - - QuiverLobbyingUniverse - +
      +
    • + + FutureFilterUniverse + +
    • +
    • + + FutureUniverse + +
    • +
    - + Learn more
    - Insider Trading + Kraken Crypto Price Data - QuiverInsiderTradingUniverse + CryptoUniverse - + Learn more
    - US Congress Trading + US ETF Constituents - QuiverQuantCongressUniverse + ETFConstituentUniverse - + Learn more
    - US Government Contracts + US Equity Option Universe - - QuiverGovernmentContractUniverse - +
      +
    • + + OptionFilterUniverse + +
    • +
    • + + OptionUniverse + +
    • +
    - + Learn more
    - WallStreetBets + US Future Option Universe - - QuiverWallStreetBetsUniverse - +
      +
    • + + OptionFilterUniverse + +
    • +
    • + + OptionUniverse + +
    • +
    - + Learn more
    - Corporate Buybacks + US Future Universe
    • - SmartInsiderIntentionUniverse + FutureFilterUniverse
    • - SmartInsiderTransactionUniverse + FutureUniverse
    - + Learn more
    -

    - To get universe data for Futures and Options, use the - - FutureHistory - - - future_history - - and - - OptionHistory - - - option_history - - methods, respectively. -

    - - - -

    Examples

    - - -

    - The following examples demonstrate some common practices for universe research. -

    -

    - Example 1: Top-Minus-Bottom PE Ratio -

    -

    - The below example studies the top-minus-bottom PE Ratio universe, in which the top 10 PE Ratio stocks are brought, and the bottom 10 are sold in equal weighting daily. We carry out a mini-backtest to analyze its performance. -

    -
    -
    // Load the required assembly files and data types.
    -#load "../Initialize.csx"
    -#load "../QuantConnect.csx"
    -
    -using QuantConnect;
    -using QuantConnect.Data;
    -using QuantConnect.Data.Market;
    -using QuantConnect.Algorithm;
    -using QuantConnect.Research;
    -using System;
    -using MathNet.Numerics.Distributions;
    -
    -// Instantiate the QuantBook instance for researching.
    -var qb = new QuantBook();
    -
    -// Set start and end dates of the research to avoid look-ahead bias.
    -var start = new DateTime(2021, 1, 1);
    -var end = new DateTime(2021, 4, 1);
    -
    -// Request data for research purposes.
    -// We are interested in the most liquid primary stocks.
    -var universe = qb.AddUniverse(
    -    (datum) => datum.OrderByDescending(x => x.DollarVolume)
    -        .Take(100)
    -        .Select(x => x.Symbol)
    -);
    -
    -// Historical data call for the data to be compared and tested.
    -var universeHistory = qb.UniverseHistory(universe, start, end);
    -
    -// Process the historical data to generate a signal and return it for research.
    -var bottomPeRatioDict = new Dictionary<DateTime, List<Symbol>>();
    -var topPeRatioDict = new Dictionary<DateTime, List<Symbol>>();
    -foreach (FundamentalUniverse fundamentals in universeHistory)
    -{
    -    var sortedByPeRatio = fundamentals.OrderBy(x => (x as Fundamental).ValuationRatios.PERatio).ToList();
    -    var bottomPeRatio = sortedByPeRatio.Take(10)
    -        .Select(x => x.Symbol)
    -        .ToList();
    -    var topPeRatio = sortedByPeRatio.TakeLast(10)
    -        .Select(x => x.Symbol)
    -        .ToList();
    -
    -    // Study 10 stocks with the top and bottom PE Ratios.
    -    var _time = fundamentals.First().Time;
    -    bottomPeRatioDict[_time.Date] = bottomPeRatio;
    -    topPeRatioDict[_time.Date] = topPeRatio;
    -}
    -
    -// Extract symbols from both dictionaries and remove duplicates
    -var allSymbols = bottomPeRatioDict.Values
    -    .SelectMany(symbols => symbols)
    -    .Concat(topPeRatioDict.Values.SelectMany(symbols => symbols))
    -    .Distinct()
    -    .ToList();
    -// All symbols' daily prices are for return comparison.
    -var history = qb.History<TradeBar>(allSymbols, start, end, Resolution.Daily).ToList();
    -
    -// Iterate the history to backtest the top minus bottom performance.
    -var time = new List<DateTime>() { start };
    -var equity = new List<decimal>() { 1m };
    -for (int i = 0; i < history.Count - 2; i++)
    -{
    -    var bar = history[i];
    -    var nextBar = history[i+1];
    -    var timeStamp = bar.Values.First().EndTime;
    +    
    +     
    +      US Index Option Universe
    +     
    +     
    +      
    +     
    +     
    +      
    +       Learn more
    +      
    +     
    +    
    +    
    +     
    +      Brain Language Metrics on Company Filings
    +     
    +     
    +      
    +       BrainCompanyFilingLanguageMetricsUniverse
    +      
    +     
    +     
    +      
    +       Learn more
    +      
    +     
    +    
    +    
    +     
    +      Brain ML Stock Ranking
    +     
    +     
    +      
    +       BrainStockRankingUniverse
    +      
    +     
    +     
    +      
    +       Learn more
    +      
    +     
    +    
    +    
    +     
    +      Brain Sentiment Indicator
    +     
    +     
    +      
    +       BrainSentimentIndicatorUniverse
    +      
    +     
    +     
    +      
    +       Learn more
    +      
    +     
    +    
    +    
    +     
    +      Crypto Market Cap
    +     
    +     
    +      
    +       CoinGeckoUniverse
    +      
    +     
    +     
    +      
    +       Learn more
    +      
    +     
    +    
    +    
    +     
    +      CNBC Trading
    +     
    +     
    +      
    +       QuiverCNBCsUniverse
    +      
    +     
    +     
    +      
    +       Learn more
    +      
    +     
    +    
    +    
    +     
    +      Corporate Lobbying
    +     
    +     
    +      
    +       QuiverLobbyingUniverse
    +      
    +     
    +     
    +      
    +       Learn more
    +      
    +     
    +    
    +    
    +     
    +      Insider Trading
    +     
    +     
    +      
    +       QuiverInsiderTradingUniverse
    +      
    +     
    +     
    +      
    +       Learn more
    +      
    +     
    +    
    +    
    +     
    +      US Congress Trading
    +     
    +     
    +      
    +       QuiverQuantCongressUniverse
    +      
    +     
    +     
    +      
    +       Learn more
    +      
    +     
    +    
    +    
    +     
    +      US Government Contracts
    +     
    +     
    +      
    +       QuiverGovernmentContractUniverse
    +      
    +     
    +     
    +      
    +       Learn more
    +      
    +     
    +    
    +    
    +     
    +      Corporate Buybacks
    +     
    +     
    +      
    +     
    +     
    +      
    +       Learn more
    +      
    +     
    +    
    +   
    +  
    +  

    + To get universe data for Futures and Options, use the + + FutureHistory + + + future_history + + and + + OptionHistory + + + option_history + + methods, respectively. +

    + + + +

    Examples

    + + +

    + The following examples demonstrate some common practices for universe research. +

    +

    + Example 1: Top-Minus-Bottom PE Ratio +

    +

    + The below example studies the top-minus-bottom PE Ratio universe, in which the top 10 PE Ratio stocks are brought, and the bottom 10 are sold in equal weighting daily. We carry out a mini-backtest to analyze its performance. +

    +
    +
    // Load the required assembly files and data types.
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
    +#load "../QuantConnect.csx"
    +#r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    using QuantConnect;
    +using QuantConnect.Data;
    +using QuantConnect.Data.Market;
    +using QuantConnect.Algorithm;
    +using QuantConnect.Research;
    +using System;
    +using MathNet.Numerics.Distributions;
    +
    +// Instantiate the QuantBook instance for researching.
    +var qb = new QuantBook();
    +
    +// Set start and end dates of the research to avoid look-ahead bias.
    +var start = new DateTime(2021, 1, 1);
    +var end = new DateTime(2021, 4, 1);
    +
    +// Request data for research purposes.
    +// We are interested in the most liquid primary stocks.
    +var universe = qb.AddUniverse(
    +    (datum) => datum.OrderByDescending(x => x.DollarVolume)
    +        .Take(100)
    +        .Select(x => x.Symbol)
    +);
    +
    +// Historical data call for the data to be compared and tested.
    +var universeHistory = qb.UniverseHistory(universe, start, end);
    +
    +// Process the historical data to generate a signal and return it for research.
    +var bottomPeRatioDict = new Dictionary<DateTime, List<Symbol>>();
    +var topPeRatioDict = new Dictionary<DateTime, List<Symbol>>();
    +foreach (FundamentalUniverse fundamentals in universeHistory)
    +{
    +    var sortedByPeRatio = fundamentals.OrderBy(x => (x as Fundamental).ValuationRatios.PERatio).ToList();
    +    var bottomPeRatio = sortedByPeRatio.Take(10)
    +        .Select(x => x.Symbol)
    +        .ToList();
    +    var topPeRatio = sortedByPeRatio.TakeLast(10)
    +        .Select(x => x.Symbol)
    +        .ToList();
    +
    +    // Study 10 stocks with the top and bottom PE Ratios.
    +    var _time = fundamentals.First().Time;
    +    bottomPeRatioDict[_time.Date] = bottomPeRatio;
    +    topPeRatioDict[_time.Date] = topPeRatio;
    +}
    +
    +// Extract symbols from both dictionaries and remove duplicates
    +var allSymbols = bottomPeRatioDict.Values
    +    .SelectMany(symbols => symbols)
    +    .Concat(topPeRatioDict.Values.SelectMany(symbols => symbols))
    +    .Distinct()
    +    .ToList();
    +// All symbols' daily prices are for return comparison.
    +var history = qb.History<TradeBar>(allSymbols, start, end, Resolution.Daily).ToList();
    +
    +// Iterate the history to backtest the top minus bottom performance.
    +var time = new List<DateTime>() { start };
    +var equity = new List<decimal>() { 1m };
    +for (int i = 0; i < history.Count - 2; i++)
    +{
    +    var bar = history[i];
    +    var nextBar = history[i+1];
    +    var timeStamp = bar.Values.First().EndTime;
         var bottomReturn = bottomPeRatioDict[timeStamp.Date].Sum(x => (nextBar[x].Close - bar[x].Close) / bar[x].Close * -0.1m);
         var topReturn = topPeRatioDict[timeStamp.Date].Sum(x => (nextBar[x].Close - bar[x].Close) / bar[x].Close * 0.1m);
         
    @@ -41034,7 +41673,7 @@ 

    chart.WithLayout(layout); // Display the plot. -HTML(GenericChart.toChartHTML(chart))

    +display(chart)
    # Instantiate the QuantBook instance for researching.
     qb = QuantBook()
     
    @@ -41466,16 +42105,18 @@ 

    The following example demonstrates a quick backtest to testify the effectiveness of a Bollinger Band mean-reversal under the research enviornment.

    -
    +
    // Load the assembly files and data types in their own cell. 
    -#load "../Initialize.csx"
    -
    -// Load the necessary assembly files. 
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files. 
     #load "../QuantConnect.csx"
     #r "nuget: Plotly.NET"
    -#r "nuget: Plotly.NET.Interactive"
    -
    -// Import the QuantConnect, Plotly.NET, and Accord packages for calculation and plotting.
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    // Import the QuantConnect, Plotly.NET, and Accord packages for calculation and plotting.
     using QuantConnect;
     using QuantConnect.Data.Market;
     using QuantConnect.Indicators;
    @@ -41774,16 +42415,18 @@ 

    The following example demonstrates a quick backtest to testify the effectiveness of a William Percent Ratio mean-reversal under the research enviornment.

    -
    +
    // Load the assembly files and data types in their own cell. 
    -#load "../Initialize.csx"
    -
    -// Load the necessary assembly files. 
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files. 
     #load "../QuantConnect.csx"
     #r "nuget: Plotly.NET"
    -#r "nuget: Plotly.NET.Interactive"
    -
    -// Import the QuantConnect, Plotly.NET, and Accord packages for calculation and plotting.
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    // Import the QuantConnect, Plotly.NET, and Accord packages for calculation and plotting.
     using QuantConnect;
     using QuantConnect.Data.Market;
     using QuantConnect.Indicators;
    @@ -42079,16 +42722,18 @@ 

    The following example demonstrates a quick backtest to testify the effectiveness of a Money Flow Index mean-reversal under the research enviornment.

    -
    +
    // Load the assembly files and data types in their own cell. 
    -#load "../Initialize.csx"
    -
    -// Load the necessary assembly files. 
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files. 
     #load "../QuantConnect.csx"
     #r "nuget: Plotly.NET"
    -#r "nuget: Plotly.NET.Interactive"
    -
    -// Import the QuantConnect, Plotly.NET, and Accord packages for calculation and plotting.
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    // Import the QuantConnect, Plotly.NET, and Accord packages for calculation and plotting.
     using QuantConnect;
     using QuantConnect.Data.Market;
     using QuantConnect.Indicators;
    @@ -42427,20 +43072,22 @@ 

    The following example demonstrates a quick backtest to testify the effectiveness of a Standard Deviation On Return mean-reversal under the research enviornment.

    -
    +
    // Load the assembly files and data types in their own cell.
    -#load "../Initialize.csx"
    -
    -// Load the necessary assembly files.
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
     #load "../QuantConnect.csx"
    -#r "../Plotly.NET.dll"
    -#r "../Plotly.NET.Interactive.dll"
    -
    -// Import the QuantConnect, Plotly.NET, and Accord packages for calculation and plotting.
    +#r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    // Import the QuantConnect, Plotly.NET, and Accord packages for calculation and plotting.
     using QuantConnect;
     using QuantConnect.Indicators;
     using QuantConnect.Research;
    -            
    +
     using Plotly.NET;
     using Plotly.NET.Interactive;
     using Plotly.NET.LayoutObjects;
    @@ -42491,7 +43138,7 @@ 

    chart.WithLayout(layout); // Display the plot. -HTML(GenericChart.toChartHTML(chart))

    +display(chart)

    # Instantiate the QuantBook instance for researching.
     qb = QuantBook()
     # Request SPY data to work with the indicator.
    @@ -42593,9 +43240,12 @@ 

    Create Indicator Timeseries

    PythonIndicator superclass inheritance, - + Value + + value + attribute, and Update @@ -42693,9 +43343,9 @@

    Create Indicator Timeseries

    # Method to update the indicator values. Note that it only receives 1 IBaseData object (Tick, TradeBar, QuoteBar) argument. def update(self, input: BaseData) -> bool: - count = self._window.Count + count = self._window.count - self._window.Add(input.Close) + self._window.add(input.close) # Update the Value and other attributes as the indicator current value. if count >= 2: @@ -42861,20 +43511,22 @@

    The following example demonstrates researching using a custom-constructed Expected Shortfall indicator. Expected Shortfall refers to the average of the N% worst-case return.

    -
    +
    // Load the assembly files and data types in their own cell.
    -#load "../Initialize.csx"
    -
    -// Load the necessary assembly files.
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
     #load "../QuantConnect.csx"
    -#r "../Plotly.NET.dll"
    -#r "../Plotly.NET.Interactive.dll"
    -
    -// Import the QuantConnect, Plotly.NET, and Accord packages for calculation and plotting.
    +#r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    // Import the QuantConnect, Plotly.NET, and Accord packages for calculation and plotting.
     using QuantConnect;
     using QuantConnect.Indicators;
     using QuantConnect.Research;
    -            
    +
     using Plotly.NET;
     using Plotly.NET.Interactive;
     using Plotly.NET.LayoutObjects;
    @@ -42976,7 +43628,7 @@ 

    chart.WithLayout(layout); // Display the plot. -HTML(GenericChart.toChartHTML(chart))

    +display(chart)

    # Instantiate the QuantBook instance for researching.
     qb = QuantBook()
     # Request SPY data to work with the indicator.
    @@ -43003,9 +43655,9 @@ 

    # Method to update the indicator values. Note that it only receives 1 IBaseData object (Tick, TradeBar, QuoteBar) argument. def update(self, input: BaseData) -> bool: - count = self._window.Count + count = self._window.count - self._window.Add(input.Close) + self._window.add(input.close) # Update the Value and other attributes as the indicator current value. if count >= 2: @@ -43336,20 +43988,22 @@

    The following example demonstrates a quick backtest to testify the effectiveness of a Bollinger Band mean-reversal, using 5-miunte bar under the research enviornment.

    -
    +
    // Load the assembly files and data types in their own cell.
    -#load "../Initialize.csx"
    -
    -// Load the necessary assembly files.
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
     #load "../QuantConnect.csx"
    -#r "../Plotly.NET.dll"
    -#r "../Plotly.NET.Interactive.dll"
    -
    -// Import the QuantConnect, Plotly.NET, and Accord packages for calculation and plotting.
    +#r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    // Import the QuantConnect, Plotly.NET, and Accord packages for calculation and plotting.
     using QuantConnect;
     using QuantConnect.Indicators;
     using QuantConnect.Research;
    -            
    +
     using Plotly.NET;
     using Plotly.NET.Interactive;
     using Plotly.NET.LayoutObjects;
    @@ -43442,7 +44096,7 @@ 

    chart.WithLayout(layout); // Display the plot. -HTML(GenericChart.toChartHTML(chart))

    +display(chart)

    # Instantiate the QuantBook instance for researching.
     qb = QuantBook()
     # Request SPY data to work with the indicator.
    @@ -43970,9 +44624,12 @@ 

    Cache Data

    To clear the cache, call the - + Clear + + clear + method.

    @@ -44265,13 +44922,16 @@

    Example for Plotting

    . -
    +
    // Execute the following command in first
    -#load "../Initialize.csx"
    -
    -// Create a QuantBook object
    -#load "../QuantConnect.csx"
    -using QuantConnect;
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
    +#load "../QuantConnect.csx"
    +
    +
    +
    using QuantConnect;
     using QuantConnect.Research;
     
     var qb = new QuantBook();
    @@ -44312,8 +44972,12 @@

    Example for Plotting

    packages.
    -
    #r "../Plotly.NET.dll"
    -using Plotly.NET;
    +    
    #r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    using Plotly.NET;
    +using Plotly.NET.Interactive;
     using Plotly.NET.LayoutObjects;
  • @@ -44383,15 +45047,11 @@

    Example for Plotting

    Line - object and create the - - HTML - - object. + object and display the chart.
  • chart.WithLayout(layout);
    -var result = HTML(GenericChart.toChartHTML(chart));
    +display(chart);
    @@ -44496,9 +45156,12 @@

    Example for Logging

  • In the - + OnData + + on_data + method, place market orders when the EMAs cross.
  • @@ -44508,11 +45171,11 @@

    Example for Logging

    if (_emaShort > _emaLong && !Portfolio[_symbol].IsLong) { - MarketOrder(_symbol, 100, tag: $"BUY: ema-short: {_emaShort:F4} > ema-long: {_emaLong:F4}"); + MarketOrder(_symbol, 100, tag: $"BUY: ema-short: {_emaShort:F4} > ema-long: {_emaLong:F4}"); } else if (_emaShort < _emaLong && !Portfolio[_symbol].IsShort) { - MarketOrder(_symbol, -100, tag: $"SELL: ema-short: {_emaShort:F4} < ema-long: {_emaLong:F4}"); + MarketOrder(_symbol, -100, tag: $"SELL: ema-short: {_emaShort:F4} < ema-long: {_emaLong:F4}"); } }

    def on_data(self, data: Slice):
    @@ -44522,15 +45185,18 @@ 

    Example for Logging

    ema_short = self._ema_short.current.value ema_long = self._ema_long.current.value if ema_short > ema_long and not self.portfolio[self._symbol].is_long: - self.market_order(self._symbol, 100, tag=f'BUY: ema-short: {ema_short:.4f} > ema-long: {ema_long:.4f}') + self.market_order(self._symbol, 100, tag=f'BUY: ema-short: {ema_short:.4f} > ema-long: {ema_long:.4f}') elif ema_short < ema_long and not self.portfolio[self._symbol].is_short: - self.market_order(self._symbol, -100, tag=f'SELL: ema-short: {ema_short:.4f} < ema-long: {ema_long:.4f}')
    + self.market_order(self._symbol, -100, tag=f'SELL: ema-short: {ema_short:.4f} < ema-long: {ema_long:.4f}')

  • In the - + OnOrderEvent + + on_order_event + method, log each fill to the content string.
  • @@ -44582,13 +45248,16 @@

    Example for Logging

    . -
    +
    // Execute the following command in first
    -#load "../Initialize.csx"
    -
    -// Create a QuantBook object
    -#load "../QuantConnect.csx"
    -using QuantConnect;
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
    +#load "../QuantConnect.csx"
    +
    +
    +
    using QuantConnect;
     using QuantConnect.Research;
     
     var qb = new QuantBook();
    @@ -44712,11 +45381,11 @@

    Example for Logging

    // Place a market order when the EMAs cross. if (_emaShort > _emaLong && !Portfolio[_symbol].IsLong) { - MarketOrder(_symbol, 100, tag: $"BUY: ema-short: {_emaShort:F4} > ema-long: {_emaLong:F4}"); + MarketOrder(_symbol, 100, tag: $"BUY: ema-short: {_emaShort:F4} > ema-long: {_emaLong:F4}"); } else if (_emaShort < _emaLong && !Portfolio[_symbol].IsShort) { - MarketOrder(_symbol, -100, tag: $"SELL: ema-short: {_emaShort:F4} < ema-long: {_emaLong:F4}"); + MarketOrder(_symbol, -100, tag: $"SELL: ema-short: {_emaShort:F4} < ema-long: {_emaLong:F4}"); } } @@ -44758,9 +45427,9 @@

    Example for Logging

    ema_short = self._ema_short.current.value ema_long = self._ema_long.current.value if ema_short > ema_long and not self.portfolio[self._symbol].is_long: - self.market_order(self._symbol, 100, tag=f'BUY: ema-short: {ema_short:.4f} > ema-long: {ema_long:.4f}') + self.market_order(self._symbol, 100, tag=f'BUY: ema-short: {ema_short:.4f} > ema-long: {ema_long:.4f}') elif ema_short < ema_long and not self.portfolio[self._symbol].is_short: - self.market_order(self._symbol, -100, tag=f'SELL: ema-short: {ema_short:.4f} < ema-long: {ema_long:.4f}') + self.market_order(self._symbol, -100, tag=f'SELL: ema-short: {ema_short:.4f} < ema-long: {ema_long:.4f}') def on_order_event(self, order_event: OrderEvent) -> None: if order_event.status != OrderStatus.FILLED: @@ -44784,6 +45453,102 @@

    Example for Logging

    +

    Special Folders

    + + +

    + Your organization's Object Store includes a reserved + + .assistant + + folder that the + + QuantConnect AI assistants + + read. + It holds the custom + + skills + + , + + memories + + , and + + templates + + you define for your organization, which let the assistants apply your own expertise, remember your preferences, and start new projects from your own scaffolds. +

    +

    + The + + .assistant + + folder contains the following subfolders: +

    + + + + + + + + + + + + + + + + + + + + + +
    + Subfolder + + Purpose +
    + + skills + + + Reusable instructions and conventions the assistants apply. +
    + + memories + + + Facts the assistants remember across conversations. +
    + + templates + + + Your own project scaffolds for the assistants to initialize for new projects. +
    +

    + These files are shared across your organization. To add or update them, upload files to the Object Store with the + + Algorithm Lab + + , + + CLI + + , or + + API + + . +

    + + +

     

    @@ -47219,7 +47984,6 @@

    Train Models

    # Model Structure def __init__(self): super(NeuralNetwork, self).__init__() - self.flatten = nn.Flatten() self.linear_relu_stack = nn.Sequential( nn.Linear(5, 5), # input size, output size of the layer nn.ReLU(), # Relu non-linear transformation @@ -47506,7 +48270,6 @@

    # Model Structure def __init__(self): super(NeuralNetwork, self).__init__() - self.flatten = nn.Flatten() self.linear_relu_stack = nn.Sequential( nn.Linear(5, 5), # input size, output size of the layer nn.ReLU(), # Relu non-linear transformation @@ -47899,9 +48662,12 @@

  • Call - + GetFilePath + + get_file_path + with the key.
  • @@ -50186,361 +50952,1395 @@

    icon.
  • - In the Run and Debug panel, hover over the - - Breakpoints - - section and then click the - - - Remove All Breakpoints - - icon. + In the Run and Debug panel, hover over the + + Breakpoints + + section and then click the + + + Remove All Breakpoints + + icon. +
  • + + + + +

    Launch Debugger

    + + +

    + Follow these steps to launch the debugger: +

    +
      +
    1. + + Open the project + + you want to debug. +
    2. +
    3. + + Open the notebook file + + in your project. +
    4. +
    5. + In a notebook cell, add at least one breakpoint. +
    6. +
    7. + In the top-left corner of the cell, click the drop-down arrow and then click + + Debug Cell + + . +
    8. +
    +

    + If the Run and Debug panel is not open, it opens when the first breakpoint is hit. +

    + + + +

    Control Debugger

    + + +

    + After you launch the debugger, you can use the following buttons to control it: +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Button + + Name + + Default Keyboard Shortcut + + Description +
    + Debugger continue icon + + Continue + + + Continue execution until the next breakpoint +
    + Debugger step over icon + + Step Over + + + Alt+F10 + + + Step to the next line of code in the current or parent scope +
    + Debugger step into icon + + Step Into + + + Alt+F11 + + + Step into the definition of the function call on the current line +
    + Debugger restart icon + + Restart + + + Shift+F11 + + + Restart the debugger +
    + Debugger disconnect icon + + Disconnect + + + Shift+F5 + + + Exit the debugger +
    + + + + +

    Inspect Variables

    + + +

    + After you launch the debugger, you can inspect the state of your notebook as it executes each line of code. You can inspect local variables or custom expressions. + + The values of variables in your notebook are formatted in the IDE to improve readability. For example, if you inspect a variable that references a DataFrame, the debugger represents the variable value as the following: + +

    + Dataframe in a debugger variable view +

    + Local Variables +

    +

    + The + + Variables + + section of the Run and Debug panel shows the local variables at the current breakpoint. If a variable in the panel is an object, click it to see its members. The panel updates as the notebook runs. +

    + Local variables in debugger view +

    + Follow these steps to update the value of a variable: +

    +
      +
    1. + In the Run and Debug panel, right-click a variable and then click + + Set Value + + . +
    2. +
    3. + Enter the new value and then press + + Enter + + . +
    4. +
    +

    + Custom Expressions +

    +

    + The + + Watch + + section of the Run and Debug panel shows any custom expressions you add. For example, you can add an expression to show a + + datetime + + object. +

    + Inspect custom variables in debugger view +

    + Follow these steps to add a custom expression: +

    +
      +
    1. + Hover over the + + Watch + + section and then click the + + plus + + icon that appears. +
    2. +
    3. + Enter an expression and then press + + Enter + + . +
    4. +
    + + + +

     

    + +
    +
    +

    Meta Analysis

    + +
    +
    + +
    +
    +

    Meta Analysis

    +

    Key Concepts

    +
    +
    +

    Introduction

    + + +

    + Understanding your strategy trades in detail is key to attributing performance, and determining areas to focus for improvement. This analysis can be done with the QuantConnect API. We enable you to load backtest, optimization, and live trading results into the Research Environment. +

    + + + +

    Backtest Analysis

    + + +

    + Load your backtest results into the Research Environment to analyze trades and easily compare them against the raw backtesting data. For more information on loading and manipulating backtest results, see + + Backtest Analysis + + . +

    + + + +

    Optimization Analysis

    + + +

    + Load your optimization results into the Research Environment to analyze how different combinations of parameters affect the algorithm's performance. For more information on loading and manipulating optimizations results, see + + Optimization Analysis + + . +

    + + + +

    Live Analysis

    + + +

    + Load your live trading results into the Research Environment to compare live trading performance against simulated backtest results, or analyze your trades to improve your slippage and fee models. For more information on loading and manipulating live trading results, see + + Live Analysis + + . +

    + + + +

     

    + +
    +
    +

    Meta Analysis

    +

    Backtest Analysis

    +
    +
    +

    Introduction

    + + +

    + Load your backtest results into the Research Environment to analyze trades and easily compare them against the raw backtesting data. Compare backtests from different projects to find uncorrelated strategies to combine for better performance. +

    +

    + Loading your backtest trades allows you to plot fills against detailed data, or locate the source of profits. Similarly you can search for periods of high churn to reduce turnover and trading fees. +

    + + + +

    Read Backtest Results

    + + +

    + To get the results of a backtest, call the + + ReadBacktest + + + read_backtest + + method with the project Id and backtest ID. +

    +
    +
    #load "../Initialize.csx"
    +
    +
    +
    #load "../QuantConnect.csx"
    +
    +
    +
    using QuantConnect;
    +using QuantConnect.Api;
    +
    +var backtest = api.ReadBacktest(projectId, backtestId);
    +
    backtest = api.read_backtest(project_id, backtest_id)
    +
    +

    + The following table provides links to documentation that explains how to get the project Id and backtest Id, depending on the platform you use: +

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Platform + + Project Id + + Backtest Id +
    + Cloud Platform + + + Get Project Id + + + + Get Backtest Id + +
    + Local Platform + + + Get Project Id + + + + Get Backtest Id + +
    + CLI + + + Get Project Id + + + + Get Backtest Id + +
    +

    + Note that this method returns a snapshot of the backtest at the current moment. If the backtest is still executing, the result won't include all of the backtest data. +

    +

    + The + + ReadBacktest + + + read_backtest + + method returns a + + Backtest + + object, which have the following attributes: +

    +
    +
    + + + +

    Plot Order Fills

    + + +

    + Follow these steps to plot the daily order fills of a backtest: +

    +
      +
    1. + Get the backtest orders. +
    2. +
      +
      orders = api.read_backtest_orders(project_id, backtest_id)
      +
      +

      + The following table provides links to documentation that explains how to get the project Id and backtest Id, depending on the platform you use: +

      + + + + + + + + + + + + + + + + + + + + + + + + + +
      + Platform + + Project Id + + Backtest Id +
      + Cloud Platform + + + Get Project Id + + + + Get Backtest Id + +
      + Local Platform + + + Get Project Id + + + + Get Backtest Id + +
      + CLI + + + Get Project Id + + + + Get Backtest Id + +
      +

      + The + + ReadBacktestOrders + + + read_backtest_orders + + method returns a list of + + ApiOrderResponse + + objects, which have the following properties: +

      +
      +
      +
    3. + Organize the trade times and prices for each security into a dictionary. +
      +
      class OrderData:
      +    def __init__(self):
      +        self.buy_fill_times = []
      +        self.buy_fill_prices = []
      +        self.sell_fill_times = []
      +        self.sell_fill_prices = []
      +
      +order_data_by_symbol = {}
      +for order in [x.order for x in orders]:
      +    if order.symbol not in order_data_by_symbol:
      +        order_data_by_symbol[order.symbol] = OrderData()
      +    order_data = order_data_by_symbol[order.symbol]
      +    is_buy = order.quantity > 0
      +    (order_data.buy_fill_times if is_buy else order_data.sell_fill_times).append(order.last_fill_time.date())
      +    (order_data.buy_fill_prices if is_buy else order_data.sell_fill_prices).append(order.price)
      +
      +
    4. +
    5. + Get the price history of each security you traded. +
      +
      qb = QuantBook()
      +start_date = datetime.max.date()
      +end_date = datetime.min.date()
      +for symbol, order_data in order_data_by_symbol.items():
      +    if order_data.buy_fill_times:
      +        start_date = min(start_date, min(order_data.buy_fill_times))
      +        end_date = max(end_date, max(order_data.buy_fill_times))
      +    if order_data.sell_fill_times:
      +        start_date = min(start_date, min(order_data.sell_fill_times))
      +        end_date = max(end_date, max(order_data.sell_fill_times))
      +start_date -= timedelta(days=3)
      +all_history = qb.history(list(order_data_by_symbol.keys()), start_date, end_date, Resolution.DAILY)
      +
      +
    6. +
    7. + Create a candlestick plot for each security and annotate each plot with buy and sell markers. +
      +
      import plotly.express as px
      +import plotly.graph_objects as go
      +
      +for symbol, order_data in order_data_by_symbol.items():
      +    history = all_history.loc[symbol]
      +
      +    # Plot security price candlesticks
      +    candlestick = go.Candlestick(x=history.index,
      +                                open=history['open'],
      +                                high=history['high'],
      +                                low=history['low'],
      +                                close=history['close'],
      +                                name='Price')
      +    layout = go.Layout(title=go.layout.Title(text=f'{symbol.value} Trades'),
      +                    xaxis_title='Date',
      +                    yaxis_title='Price',
      +                    xaxis_rangeslider_visible=False,
      +                    height=600)
      +    fig = go.Figure(data=[candlestick], layout=layout)
      +
      +    # Plot buys
      +    fig.add_trace(go.Scatter(
      +        x=order_data.buy_fill_times,
      +        y=order_data.buy_fill_prices,
      +        marker=go.scatter.Marker(color='aqua', symbol='triangle-up', size=10),
      +        mode='markers',
      +        name='Buys',
      +    ))
      +
      +    # Plot sells
      +    fig.add_trace(go.Scatter(
      +        x=order_data.sell_fill_times,
      +        y=order_data.sell_fill_prices,
      +        marker=go.scatter.Marker(color='indigo', symbol='triangle-down', size=10),
      +        mode='markers',
      +        name='Sells',
      +    ))
      +
      +fig.show()
      +
      +
    8. + Plot of AAPL price with buy/sell markers + Plot of SPY price with buy/sell markers +

      + Note: The preceding plots only show the last fill of each trade. If your trade has partial fills, the plots only display the last fill. +

      +
    + + + +

    Plot Metadata

    + + +

    + Follow these steps to plot the equity curve, benchmark, and drawdown of a backtest: +

    +
      +
    1. + Define the project Id, backtest Id, and read the "Strategy Equity", "Drawdown", and "Benchmark" charts. +
    2. +
      +
      from time import time
      +
      +project_id = 23034953
      +backtest_id = 'ff616bb2cbccf70f61ea431278e57728'
      +
      +def read_chart(project_id, backtest_id, chart_name, start=0, end=int(time()), count=500):
      +    return api.read_backtest_chart(
      +        project_id, chart_name, start, end, count, backtest_id
      +    ).chart
      +
      +strategy_equity = read_chart(project_id, backtest_id, 'Strategy Equity')
      +drawdown_chart = read_chart(project_id, backtest_id, 'Drawdown')
      +benchmark_chart = read_chart(project_id, backtest_id, 'Benchmark')
      +
      +

      + The following table provides links to documentation that explains how to get the project Id and backtest Id, depending on the platform you use: +

      + + + + + + + + + + + + + + + + + + + + + + + + + +
      + Platform + + Project Id + + Backtest Id +
      + Cloud Platform + + + Get Project Id + + + + Get Backtest Id + +
      + Local Platform + + + Get Project Id + + + + Get Backtest Id + +
      + CLI + + + Get Project Id + + + + Get Backtest Id + +
      +
    3. + Extract the series and create a + + pandas.DataFrame + + . +
    4. +
      +
      def to_series(chart, series_name, selector=lambda x: x.y):
      +    return pd.Series({v.time: selector(v) for v in chart.series[series_name].values})
      +
      +df = pd.DataFrame({
      +    "Equity": to_series(strategy_equity, 'Equity', selector=lambda x: x.close),
      +    "Return": to_series(strategy_equity, 'Return'),
      +    "Drawdown": to_series(drawdown_chart, 'Equity Drawdown'),
      +    "Benchmark": to_series(benchmark_chart, 'Benchmark')
      +}).ffill()
      +df.index = df.index.tz_localize('UTC').tz_convert('US/Eastern').tz_localize(None)
      +
      +
    5. + Plot the performance chart. +
    6. +
      +
      # Create subplots to plot series on same/different plots
      +fig, ax = plt.subplots(3, 1, figsize=(12, 16), sharex=True, gridspec_kw={'height_ratios': [2, 1, 1]})
      +
      +# Plot the equity curve
      +ax[0].plot(df.index, df["Equity"])
      +ax[0].set_title("Strategy Equity Curve")
      +ax[0].set_ylabel("Portfolio Value ($)")
      +
      +# Plot the benchmark on the same plot, scale by using another y-axis
      +ax2 = ax[0].twinx()
      +ax2.plot(df.index, df["Benchmark"], color="grey")
      +ax2.set_ylabel("Benchmark Price ($)", color="grey")
      +
      +# Plot the daily returns
      +ax[1].plot(df.index, df["Return"], color="blue")
      +ax[1].set_title("Daily Return")
      +ax[1].set_ylabel("%")
      +
      +# Plot the drawdown on another plot
      +ax[2].plot(df.index, df["Drawdown"], color="red")
      +ax[2].set_title("Drawdown")
      +ax[2].set_xlabel("Time")
      +ax[2].set_ylabel("%");
      +
      + api-equity-curve +
    +

    + The following table shows all the chart series you can plot: +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Chart + + Series + + Description +
    + Strategy Equity + + Equity + + Time series of the equity curve. This series may not update daily. +
    + Return + + Time series of daily returns. Use this series for daily updates. +
    + Daily Performance + + Time series of daily percentage change. This series has as many data points as "Equity". +
    + Capacity + + Strategy Capacity + + Time series of + + strategy capacity + + snapshots +
    + Drawdown + + Equity Drawdown + + Time series of equity peak-to-trough value +
    + Benchmark + + Benchmark + + Time series of the + + benchmark + + closing price (SPY, by default) +
    + Exposure + + + SecurityType + + - Long Ratio + + Time series of the overall ratio of + + SecurityType + + long positions of the whole portfolio if any + + SecurityType + + is ever in the universe +
    + + SecurityType + + - Short Ratio + + Time series of the overall ratio of + + SecurityType + + short position of the whole portfolio if any + + SecurityType + + is ever in the universe +
    + Assets Sales Volume + + Each + + ticker + + is one series + + A chart showing the proportion of total volume for each traded security. +
    + Portfolio Turnover + + Portfolio Turnover + + A time series of the portfolio turnover rate. +
    + Portfolio Margin + + Each + + ticker + + is one series + + A stacked area chart of the portfolio margin usage. For more information about this chart, see + + Portfolio Margin Plots + + . +
    + Asset Plot + + Price and Annotations + + A time series of an asset's price with order event annotations. For more information about these charts, see + + Asset Plots + + . +
    + Custom Chart + + Custom Series + + Time series of a + + Series + + in a + + custom chart + +
    + + + +

    Plot Insights

    + + +

    + Follow these steps to display the insights of each asset in a backtest: +

    +
      +
    1. + Get the insights. +
    2. +
      +
      insight_response = api.read_backtest_insights(project_id, backtest_id)
      +
      +

      + The following table provides links to documentation that explains how to get the project Id and backtest Id, depending on the platform you use: +

      + + + + + + + + + + + + + + + + + + + + + + + + + +
      + Platform + + Project Id + + Backtest Id +
      + Cloud Platform + + + Get Project Id + + + + Get Backtest Id + +
      + Local Platform + + + Get Project Id + + + + Get Backtest Id + +
      + CLI + + + Get Project Id + + + + Get Backtest Id + +
      +

      + The + + read_backtest_insights + + method returns an + + InsightResponse + + object, which have the following properties: +

      +
      +
      +
    3. + Organize the insights into a DataFrame. +
    4. +
      +
      import pytz
      +
      +def _eastern_time(unix_timestamp):
      +    return unix_timestamp.replace(tzinfo=pytz.utc)\
      +        .astimezone(pytz.timezone('US/Eastern')).replace(tzinfo=None)
      +
      +insight_df = pd.DataFrame(
      +    [
      +        {
      +            'Symbol': i.symbol,
      +            'Direction': i.direction,
      +            'Generated Time': _eastern_time(i.generated_time_utc),
      +            'Close Time': _eastern_time(i.close_time_utc),
      +            'Weight': i.weight
      +        }
      +        for i in insight_response.insights
      +    ]
      +)
      +
      +
    5. + Get the + + price history + + of each security that has an insight. +
    6. +
      +
      symbols = list(insight_df['Symbol'].unique())
      +qb = QuantBook()
      +history = qb.history(
      +    symbols, insight_df['Generated Time'].min()-timedelta(1), 
      +    insight_df['Close Time'].max(), Resolution.DAILY
      +)['close'].unstack(0)
      +
      +
    7. + Plot the price and insights of each asset.
    8. +
      +
      colors = ['yellow', 'green', 'red']
      +fig, axs = plt.subplots(len(symbols), 1, sharex=True)
      +for i, symbol in enumerate(symbols):
      +    ax = axs[i]
      +    history[symbol].plot(ax=ax)
      +    for _, insight in insight_df[insight_df['Symbol'] == symbol].iterrows():
      +        ax.axvspan(
      +            insight['Generated Time'], insight['Close Time'], 
      +            color=colors[insight['Direction']], alpha=0.3
      +        )
      +    ax.set_title(f'Insights for {symbol.value}')
      +    ax.set_xlabel('Date')
      +    ax.set_ylabel('Price')
      +plt.tight_layout()
      +plt.show()
      +
      +
    -

    Launch Debugger

    +

    Examples

    +

    + Example 1: Read Backtest Statistics +

    - Follow these steps to launch the debugger: + The following example reads the last completed backtest statistics in a jupyter notebook.

    -
      -
    1. - - Open the project - - you want to debug. -
    2. -
    3. - - Open the notebook file - - in your project. -
    4. -
    5. - In a notebook cell, add at least one breakpoint. -
    6. -
    7. - In the top-left corner of the cell, click the drop-down arrow and then click - - Debug Cell - - . -
    8. -
    +
    +
    // Load the necessary assemblies.
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
    +#load "../QuantConnect.csx"
    +
    +
    +
    using QuantConnect;
    +using QuantConnect.Api;
    +using QuantConnect.Research;
    +
    +// Instantiate QuantBook instance for researching.
    +var qb = new QuantBook();
    +
    +// Get backtest list in the current project.
    +var backtests = api.ListBacktests(qb.ProjectId)
    +// Get the last completed backtest to study.
    +var backtestId = backtests.Backtests
    +    .Where(x => x.Progress == 1m)
    +    .OrderByDescending(x => x.Created)
    +    .First()
    +    .BacktestId;
    +var backtest = api.ReadBacktest(qb.ProjectId, backtestId);
    +
    +// Obtain the backtest statistics.
    +Console.WriteLine(backtest.Statistics.ToString());
    +
    # Instantiate QuantBook instance for researching.
    +qb = QuantBook()
    +
    +# Get backtest list in the current project.
    +backtests = api.list_backtests(qb.project_id)
    +# Get the last completed backtest to study.
    +backtest_id = sorted(
    +    [x for x in backtests.backtests if x.progress == 1],
    +    key=lambda x: x.created,
    +    reverse=True
    +)[0].backtest_id
    +backtest = api.read_backtest(project_id, backtest_id)
    +
    +# Obtain the backtest statistics.
    +print(backtest.statistics)
    +
    + + + +

     

    + +
    +
    +

    Meta Analysis

    +

    Optimization Analysis

    +
    +
    +

    Introduction

    + +

    - If the Run and Debug panel is not open, it opens when the first breakpoint is hit. + Load your optimization results into the Research Environment to analyze how different combinations of parameters affect the algorithm's performance.

    -

    Control Debugger

    +

    Read Optimization Results

    - After you launch the debugger, you can use the following buttons to control it: + To get the results of an optimization, call the + + ReadOptimization + + + read_optimization + + method with the optimization Id.

    - +
    +
    var optimization = api.ReadOptimization(optimizationId);
    +
    optimization = api.read_optimization(optimization_id)
    +
    +

    + The following table provides links to documentation that explains how to get the optimization Id, depending on the platform you use: +

    +
    - - - - - - - - - - - - - - - - - - - - -
    - Button - - Name - - Default Keyboard Shortcut + + Platform - Description + Optimization Id
    - Debugger continue icon - - Continue - - - Continue execution until the next breakpoint -
    - Debugger step over icon - - Step Over - - - Alt+F10 - - - Step to the next line of code in the current or parent scope -
    - Debugger step into icon - - Step Into - - - Alt+F11 - + Cloud Platform - Step into the definition of the function call on the current line + + Get Optimization Id +
    - Debugger restart icon - - Restart - - - Shift+F11 - + Local Platform - Restart the debugger + + Get Optimization Id +
    - Debugger disconnect icon - - Disconnect - - - Shift+F5 - + CLI - Exit the debugger
    - - - - -

    Inspect Variables

    - - -

    - After you launch the debugger, you can inspect the state of your notebook as it executes each line of code. You can inspect local variables or custom expressions. - - The values of variables in your notebook are formatted in the IDE to improve readability. For example, if you inspect a variable that references a DataFrame, the debugger represents the variable value as the following: - -

    - Dataframe in a debugger variable view -

    - Local Variables -

    -

    - The - - Variables - - section of the Run and Debug panel shows the local variables at the current breakpoint. If a variable in the panel is an object, click it to see its members. The panel updates as the notebook runs. -

    - Local variables in debugger view -

    - Follow these steps to update the value of a variable: -

    -
      -
    1. - In the Run and Debug panel, right-click a variable and then click - - Set Value - - . -
    2. -
    3. - Enter the new value and then press - - Enter - - . -
    4. -
    -

    - Custom Expressions -

    The - - Watch - - section of the Run and Debug panel shows any custom expressions you add. For example, you can add an expression to show a + + ReadOptimization + + + read_optimization + + method returns an - datetime + Optimization - object. -

    - Inspect custom variables in debugger view -

    - Follow these steps to add a custom expression: + object, which have the following attributes:

    -
      -
    1. - Hover over the - - Watch - - section and then click the - - plus - - icon that appears. -
    2. -
    3. - Enter an expression and then press - - Enter - - . -
    4. -
    +
    +
    -

     

    - -
    -
    -

    Meta Analysis

    - -
    -
    - -
    -
    -

    Meta Analysis

    -

    Key Concepts

    -
    -
    -

    Introduction

    +

    Example

    +

    + Example 1: Read Optimization Results +

    - Understanding your strategy trades in detail is key to attributing performance, and determining areas to focus for improvement. This analysis can be done with the QuantConnect API. We enable you to load backtest, optimization, and live trading results into the Research Environment. + The following example reads the last completed optimization job and obtains the optimum paramteters in a jupyter notebook.

    - - +
    +
    // Load the necessary assemblies.
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
    +#load "../QuantConnect.csx"
    +
    +
    +
    using QuantConnect;
    +using QuantConnect.Api;
    +using QuantConnect.Research;
     
    -

    Backtest Analysis

    - - -

    - Load your backtest results into the Research Environment to analyze trades and easily compare them against the raw backtesting data. For more information on loading and manipulating backtest results, see - - Backtest Analysis - - . -

    - - +// Instantiate QuantBook instance for researching. +var qb = new QuantBook(); -

    Optimization Analysis

    - - -

    - Load your optimization results into the Research Environment to analyze how different combinations of parameters affect the algorithm's performance. For more information on loading and manipulating optimizations results, see - - Optimization Analysis - - . -

    - - +// Get optimization job list in the current project. +var optimizations = api.ListOptimizations(qb.ProjectId) +// Get the last completed optimizations to study. +var optimizationId = optimizations.Where(x => x.Status == OptimizationStatus.Completed) + .OrderByDescending(x => x.Created) + .First() + .OptimizationId; +var optimization = api.ReadOptimization(optimizationId); -

    Live Analysis

    - - -

    - Load your live trading results into the Research Environment to compare live trading performance against simulated backtest results, or analyze your trades to improve your slippage and fee models. For more information on loading and manipulating live trading results, see - - Live Analysis - - . -

    +// Obtain the backtest with the best Sharpe Ratio. +var bestBacktest = optimization.Backtests.Values.MaxBy(x => x.Statistics["SharpeRatio"]) +// Obtain the parameter set of the backtest with the best result. +var parameterSet = bestBacktest.ParameterSet; +Console.WriteLine(parameterSet.ToString());
    +
    # Instantiate QuantBook instance for researching.
    +qb = QuantBook()
    +
    +# Get optimization job list in the current project.
    +optimizations = api.list_optimizations(qb.project_id)
    +# Get the last completed optimizations to study.
    +optimization_id = sorted(
    +    [x for x in optimizations if x.status == OptimizationStatus.COMPLETED],
    +    key=lambda x: x.created,
    +    reverse=True
    +)[0].optimization_id
    +optimization = api.read_optimization(optimization_id)
    +
    +# Obtain the backtest with the best Sharpe Ratio.
    +best_backtest = max(optimization.backtests.values(), key=lambda x: x.statistics["SharpeRatio"])
    +# Obtain the parameter set of the backtest with the best result.
    +parameter_set = best_backtest.parameter_set
    +print(parameter_set)
    +

     

    - +
    -
    +

    Meta Analysis

    -

    Backtest Analysis

    +

    Live Analysis

    Introduction

    - Load your backtest results into the Research Environment to analyze trades and easily compare them against the raw backtesting data. Compare backtests from different projects to find uncorrelated strategies to combine for better performance. -

    -

    - Loading your backtest trades allows you to plot fills against detailed data, or locate the source of profits. Similarly you can search for periods of high churn to reduce turnover and trading fees. + Load your live trading results into the Research Environment to compare live trading performance against simulated backtest results.

    -

    Read Backtest Results

    +

    Read Live Results

    - To get the results of a backtest, call the + To get the results of a live algorithm, call the - ReadBacktest + ReadLiveAlgorithm - read_backtest + read_live_algorithm - method with the project Id and backtest ID. + method with the project Id and deployment ID.

    +
    +
    #load "../Initialize.csx"
    +
    +
    +
    #load "../QuantConnect.csx"
    +
    -
    #load "../Initialize.csx"
    -#load "../QuantConnect.csx"
    -
    -using QuantConnect;
    +   
    using QuantConnect;
     using QuantConnect.Api;
     
    -var backtest = api.ReadBacktest(projectId, backtestId);
    -
    backtest = api.read_backtest(project_id, backtest_id)
    +var liveAlgorithm = api.ReadLiveAlgorithm(projectId, deployId);
    +
    live_algorithm = api.read_live_algorithm(project_id, deploy_id)

    - The following table provides links to documentation that explains how to get the project Id and backtest Id, depending on the platform you use: + The following table provides links to documentation that explains how to get the project Id and deployment Id, depending on the platform you use:

    @@ -50552,7 +52352,7 @@

    Read Backtest Results

    Project Id @@ -50567,8 +52367,8 @@

    Read Backtest Results

    @@ -50582,8 +52382,8 @@

    Read Backtest Results

    @@ -50597,31 +52397,25 @@

    Read Backtest Results

    - Backtest Id + Deployment Id
    - - Get Backtest Id + + Get Deployment Id
    - - Get Backtest Id + + Get Deployment Id
    - - Get Backtest Id -
    -

    - Note that this method returns a snapshot of the backtest at the current moment. If the backtest is still executing, the result won't include all of the backtest data. -

    The - ReadBacktest + ReadLiveAlgorithm - read_backtest + read_live_algorithm method returns a - Backtest + LiveAlgorithmResults object, which have the following attributes:

    -
    +
    @@ -50630,30 +52424,27 @@

    Plot Order Fills

    - Follow these steps to plot the daily order fills of a backtest: + Follow these steps to plot the daily order fills of a live algorithm:

    1. - Get the backtest orders. + Get the live trading orders.
    2. -
      orders = api.read_backtest_orders(project_id, backtest_id)
      +
      orders = api.read_live_orders(project_id)

      - The following table provides links to documentation that explains how to get the project Id and backtest Id, depending on the platform you use: + The following table provides links to documentation that explains how to get the project Id, depending on the platform you use:

      - - @@ -50666,11 +52457,6 @@

      Plot Order Fills

      Get Project Id - - -
      + Platform Project Id - Backtest Id -
      - - Get Backtest Id - -
      @@ -50681,11 +52467,6 @@

      Plot Order Fills

      Get Project Id
      - - Get Backtest Id - -
      @@ -50696,29 +52477,53 @@

      Plot Order Fills

      Get Project Id
      - - Get Backtest Id - -
      +

      + By default, the orders with an ID between 0 and 100. To get orders with an ID greater than 100, pass + + start + + and + + end + + arguments to the + + ReadLiveOrders + + + read_live_orders + + method. Note that + + end + + - + + start + + must be less than 100. +

      +
      +
      orders = api.read_live_orders(project_id, 100, 150)
      +

      The - ReadBacktestOrders + ReadLiveOrders - read_backtest_orders + read_live_orders method returns a list of - ApiOrderResponse + Order objects, which have the following properties:

      -
      +
    3. Organize the trade times and prices for each security into a dictionary. @@ -50810,96 +52615,51 @@

      Plot Order Fills

      -

      Plot Metadata

      +

      Plot Charts

      - Follow these steps to plot the equity curve, benchmark, and drawdown of a backtest: + Follow these steps to plot the equity curve, benchmark, and drawdown of a live algorithm:

      1. - Define the project Id, backtest Id, and read the "Strategy Equity", "Drawdown", and "Benchmark" charts. + Define the project Id and read the "Strategy Equity", "Drawdown", and "Benchmark" charts.
      2. -
        from time import time
        +    
        from time import sleep, time
         
         project_id = 23034953
        -backtest_id = 'ff616bb2cbccf70f61ea431278e57728'
         
        -def read_chart(project_id, backtest_id, chart_name, start=0, end=int(time()), count=500):
        -    return api.read_backtest_chart(
        -        project_id, chart_name, start, end, count, backtest_id
        -    ).chart
        +def read_chart(project_id, chart_name, start=0, end=int(time()), count=500):
        +    # Retry up to 10 times if the chart data is still loading
        +    for attempt in range(10):
        +        result = api.read_live_chart(project_id, chart_name, start, end, count)
        +        if result.status == 'loading':
        +            print(f"Chart data is loading... (attempt {attempt + 1}/10)")
        +            sleep(10)
        +            continue
        +        break
        +    return result.chart
         
        -strategy_equity = read_chart(project_id, backtest_id, 'Strategy Equity')
        -drawdown_chart = read_chart(project_id, backtest_id, 'Drawdown')
        -benchmark_chart = read_chart(project_id, backtest_id, 'Benchmark')
        +strategy_equity = read_chart(project_id, 'Strategy Equity') +drawdown_chart = read_chart(project_id, 'Drawdown') +benchmark_chart = read_chart(project_id, 'Benchmark')

        - The following table provides links to documentation that explains how to get the project Id and backtest Id, depending on the platform you use: + The process to get your project Id depends on if you use the + + Cloud Platform + + , + + Local Platform + + , or + + CLI + + .

        - - - - - - - - - - - - - - - - - - - - - - - - - -
        - Platform - - Project Id - - Backtest Id -
        - Cloud Platform - - - Get Project Id - - - - Get Backtest Id - -
        - Local Platform - - - Get Project Id - - - - Get Backtest Id - -
        - CLI - - - Get Project Id - - - - Get Backtest Id - -
      3. Extract the series and create a @@ -50912,9 +52672,9 @@

        Plot Metadata

        return pd.Series({v.time: selector(v) for v in chart.series[series_name].values}) df = pd.DataFrame({ - "Equity": to_series(strategy_equity, 'Equity', selector=lambda x: x.close), - "Return": to_series(strategy_equity, 'Return'), - "Drawdown": to_series(drawdown_chart, 'Equity Drawdown'), + "Equity": to_series(strategy_equity, 'Equity', selector=lambda x: x.close), + "Return": to_series(strategy_equity, 'Return'), + "Drawdown": to_series(drawdown_chart, 'Equity Drawdown'), "Benchmark": to_series(benchmark_chart, 'Benchmark') }).ffill() df.index = df.index.tz_localize('UTC').tz_convert('US/Eastern').tz_localize(None)

    @@ -51029,436 +52789,133 @@

    Plot Metadata

    Time series of the - - benchmark - - closing price (SPY, by default) - - - - - Exposure - - - - SecurityType - - - Long Ratio - - - Time series of the overall ratio of - - SecurityType - - long positions of the whole portfolio if any - - SecurityType - - is ever in the universe - - - - - - SecurityType - - - Short Ratio - - - Time series of the overall ratio of - - SecurityType - - short position of the whole portfolio if any - - SecurityType - - is ever in the universe - - - - - Assets Sales Volume - - - Each - - ticker - - is one series - - - A chart showing the proportion of total volume for each traded security. - - - - - Portfolio Turnover - - - Portfolio Turnover - - - A time series of the portfolio turnover rate. - - - - - Portfolio Margin - - - Each - - ticker - - is one series - - - A stacked area chart of the portfolio margin usage. For more information about this chart, see - - Portfolio Margin Plots - - . - - - - - Asset Plot - - - Price and Annotations - - - A time series of an asset's price with order event annotations. For more information about these charts, see - - Asset Plots - - . - - - - - Custom Chart - - - Custom Series - - - Time series of a - - Series - - in a - - custom chart - - - - - - - - -

    Plot Insights

    - - -

    - Follow these steps to display the insights of each asset in a backtest: -

    -
      -
    1. - Get the insights. -
    2. -
      -
      insight_response = api.read_backtest_insights(project_id, backtest_id)
      -
      -

      - The following table provides links to documentation that explains how to get the project Id and backtest Id, depending on the platform you use: -

      - - - - - - - - - - - - - - - - - - - - - - - - - -
      - Platform - - Project Id - - Backtest Id -
      - Cloud Platform - - - Get Project Id - - - - Get Backtest Id - -
      - Local Platform - - - Get Project Id - - - - Get Backtest Id - -
      - CLI - - - Get Project Id - - - - Get Backtest Id - -
      -

      - The - - read_backtest_insights - - method returns an - - InsightResponse - - object, which have the following properties: -

      -
      -
      -
    3. - Organize the insights into a DataFrame. -
    4. -
      -
      import pytz
      -
      -def _eastern_time(unix_timestamp):
      -    return unix_timestamp.replace(tzinfo=pytz.utc)\
      -        .astimezone(pytz.timezone('US/Eastern')).replace(tzinfo=None)
      -
      -insight_df = pd.DataFrame(
      -    [
      -        {
      -            'Symbol': i.symbol,
      -            'Direction': i.direction,
      -            'Generated Time': _eastern_time(i.generated_time_utc),
      -            'Close Time': _eastern_time(i.close_time_utc),
      -            'Weight': i.weight
      -        }
      -        for i in insight_response.insights
      -    ]
      -)
      -
      -
    5. - Get the - - price history - - of each security that has an insight. -
    6. -
      -
      symbols = list(insight_df['Symbol'].unique())
      -qb = QuantBook()
      -history = qb.history(
      -    symbols, insight_df['Generated Time'].min()-timedelta(1), 
      -    insight_df['Close Time'].max(), Resolution.DAILY
      -)['close'].unstack(0)
      -
      -
    7. - Plot the price and insights of each asset. -
    8. -
      -
      colors = ['yellow', 'green', 'red']
      -fig, axs = plt.subplots(len(symbols), 1, sharex=True)
      -for i, symbol in enumerate(symbols):
      -    ax = axs[i]
      -    history[symbol].plot(ax=ax)
      -    for _, insight in insight_df[insight_df['Symbol'] == symbol].iterrows():
      -        ax.axvspan(
      -            insight['Generated Time'], insight['Close Time'], 
      -            color=colors[insight['Direction']], alpha=0.3
      -        )
      -    ax.set_title(f'Insights for {symbol.value}')
      -    ax.set_xlabel('Date')
      -    ax.set_ylabel('Price')
      -plt.tight_layout()
      -plt.show()
      -
      - -
    - - - -

    Examples

    - - -

    - Example 1: Read Backtest Statistics -

    -

    - The following example reads the last completed backtest statistics in a jupyter notebook. -

    -
    -
    // Load the necessary assemblies.
    -#load "../Initialize.csx"
    -#load "../QuantConnect.csx"
    -
    -using QuantConnect;
    -using QuantConnect.Api;
    -using QuantConnect.Research;
    -
    -// Instantiate QuantBook instance for researching.
    -var qb = new QuantBook();
    -
    -// Get backtest list in the current project.
    -var backtests = api.ListBacktests(qb.ProjectId)
    -// Get the last completed backtest to study.
    -var backtestId = backtests.Backtests
    -    .Where(x => x.Progress == 1m)
    -    .OrderByDescending(x => x.Created)
    -    .First()
    -    .BacktestId;
    -var backtest = api.ReadBacktest(qb.ProjectId, backtestId);
    -
    -// Obtain the backtest statistics.
    -Console.WriteLine(backtest.Statistics.ToString());
    -
    # Instantiate QuantBook instance for researching.
    -qb = QuantBook()
    -
    -# Get backtest list in the current project.
    -backtests = api.list_backtests(qb.project_id)
    -# Get the last completed backtest to study.
    -backtest_id = sorted(
    -    [x for x in backtests.backtests if x.progress == 1],
    -    key=lambda x: x.created,
    -    reverse=True
    -)[0].backtest_id
    -backtest = api.read_backtest(project_id, backtest_id)
    -
    -# Obtain the backtest statistics.
    -print(backtest.statistics)
    -
    - - - -

     

    - -
    -
    -

    Meta Analysis

    -

    Optimization Analysis

    -
    -
    -

    Introduction

    - - -

    - Load your optimization results into the Research Environment to analyze how different combinations of parameters affect the algorithm's performance. -

    - - - -

    Read Optimization Results

    - - -

    - To get the results of an optimization, call the - - ReadOptimization - - - read_optimization - - method with the optimization Id. -

    -
    -
    var optimization = api.ReadOptimization(optimizationId);
    -
    optimization = api.read_optimization(optimization_id)
    -
    -

    - The following table provides links to documentation that explains how to get the optimization Id, depending on the platform you use: -

    - - + + benchmark + + closing price (SPY, by default) + + - - + + + - - + + + + + + + + + + + + + + + + +
    - Platform - - Optimization Id - + Exposure + + + SecurityType + + - Long Ratio + + Time series of the overall ratio of + + SecurityType + + long positions of the whole portfolio if any + + SecurityType + + is ever in the universe +
    - Cloud Platform + + SecurityType + + - Short Ratio - - Get Optimization Id + Time series of the overall ratio of + + SecurityType + + short position of the whole portfolio if any + + SecurityType + + is ever in the universe +
    + Assets Sales Volume + + Each + + ticker + + is one series + + A chart showing the proportion of total volume for each traded security. +
    + Portfolio Turnover + + Portfolio Turnover + + A time series of the portfolio turnover rate. +
    + Portfolio Margin + + Each + + ticker + + is one series + + A stacked area chart of the portfolio margin usage. For more information about this chart, see + + Portfolio Margin Plots + .
    - Local Platform + Asset Plot - - Get Optimization Id + Price and Annotations + + A time series of an asset's price with order event annotations. For more information about these charts, see + + Asset Plots + .
    - CLI + Custom Chart + + Custom Series + Time series of a + + Series + + in a + + custom chart +
    -

    - The - - ReadOptimization - - - read_optimization - - method returns an - - Optimization - - object, which have the following attributes: -

    -
    -
    @@ -51466,183 +52923,66 @@

    Example

    - Example 1: Read Optimization Results + Example 1: Read Live Algorithm Statistics

    - The following example reads the last completed optimization job and obtains the optimum paramteters in a jupyter notebook. + The following example reads the current running live algorithm's statistics in a jupyter notebook.

    -
    +
    // Load the necessary assemblies.
    -#load "../Initialize.csx"
    -#load "../QuantConnect.csx"
    -
    -using QuantConnect;
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
    +#load "../QuantConnect.csx"
    +
    +
    +
    using QuantConnect;
     using QuantConnect.Api;
     using QuantConnect.Research;
     
     // Instantiate QuantBook instance for researching.
     var qb = new QuantBook();
     
    -// Get optimization job list in the current project.
    -var optimizations = api.ListOptimizations(qb.ProjectId)
    -// Get the last completed optimizations to study.
    -var optimizationId = optimizations.Where(x => x.Status == OptimizationStatus.Completed)
    -    .OrderByDescending(x => x.Created)
    -    .First()
    -    .OptimizationId;
    -var optimization = api.ReadOptimization(optimizationId);
    +// Get current running live algorithm list in the current project.
    +var liveAlgorithms = api.ListLiveAlgorithms(AlgorithmStatus.Running)
    +var deployId = liveAlgorithms.Algorithms
    +    .Single(x => x.ProjectId == qb.ProjectId)
    +    .DeployId;
    +var liveAlgorithm = api.ReadLiveAlgorithm(qb.ProjectId, deployId);
     
    -// Obtain the backtest with the best Sharpe Ratio.
    -var bestBacktest = optimization.Backtests.Values.MaxBy(x => x.Statistics["SharpeRatio"])
    -// Obtain the parameter set of the backtest with the best result.
    -var parameterSet = bestBacktest.ParameterSet;
    -Console.WriteLine(parameterSet.ToString());
    +// Obtain the live algorithm statistics. +Console.WriteLine(backtest.RuntimeStatistics.ToString());
    # Instantiate QuantBook instance for researching.
     qb = QuantBook()
     
    -# Get optimization job list in the current project.
    -optimizations = api.list_optimizations(qb.project_id)
    -# Get the last completed optimizations to study.
    -optimization_id = sorted(
    -    [x for x in optimizations if x.status == OptimizationStatus.COMPLETED],
    -    key=lambda x: x.created,
    -    reverse=True
    -)[0].optimization_id
    -optimization = api.read_optimization(optimization_id)
    +# Get current running live algorithm list in the current project.
    +live_algorithms = api.list_live_algorithms(AlgorithmStatus.RUNNING)
    +deploy_id = next(
    +    [x for x in live_algorithms.algorithms if x.project_id == qb.project_id]
    +).deploy_id
    +live_algorithm = api.read_live_algorithm(project_id, deploy_id)
     
    -# Obtain the backtest with the best Sharpe Ratio.
    -best_backtest = max(optimization.backtests.values(), key=lambda x: x.statistics["SharpeRatio"])
    -# Obtain the parameter set of the backtest with the best result.
    -parameter_set = best_backtest.parameter_set
    -print(parameter_set)
    +# Obtain the live algorithm statistics. +print(live_algorithm.runtime_statistics)

     

    - +
    -
    +

    Meta Analysis

    -

    Live Analysis

    +

    Live Reconciliation

    Introduction

    - Load your live trading results into the Research Environment to compare live trading performance against simulated backtest results. -

    - - - -

    Read Live Results

    - - -

    - To get the results of a live algorithm, call the - - ReadLiveAlgorithm - - - read_live_algorithm - - method with the project Id and deployment ID. -

    -
    -
    #load "../Initialize.csx"
    -#load "../QuantConnect.csx"
    -
    -using QuantConnect;
    -using QuantConnect.Api;
    -
    -var liveAlgorithm = api.ReadLiveAlgorithm(projectId, deployId);
    -
    live_algorithm = api.read_live_algorithm(project_id, deploy_id)
    -
    -

    - The following table provides links to documentation that explains how to get the project Id and deployment Id, depending on the platform you use: -

    - - - - - - - - - - - - - - - - - - - - - - - - - -
    - Platform - - Project Id - - Deployment Id -
    - Cloud Platform - - - Get Project Id - - - - Get Deployment Id - -
    - Local Platform - - - Get Project Id - - - - Get Deployment Id - -
    - CLI - - - Get Project Id - - -
    -

    - The - - ReadLiveAlgorithm - - - read_live_algorithm - - method returns a - - LiveAlgorithmResults - - object, which have the following attributes: + This page shows you how to generate the out-of-sample (OOS) backtest reconciliation curve from a live deployment in the Research Environment so you can quantitatively and visually compare live versus backtest performance. You read the live deployment's launch datetime and starting equity, run a backtest with matching parameters, and overlay the two "Strategy Equity" curves along with every order fill on a single chart per security.

    -
    -
    - - - -

    Reconciliation

    - -

    Reconciliation is a way to @@ -51654,16 +52994,18 @@

    Reconciliation

    Seeing the difference between live performance and OOS performance gives you a way to determine if the algorithm is making unrealistic assumptions, exploiting data differences, or merely exhibiting behavior that is impractical or impossible in live trading.

    - A perfectly reconciled algorithm has an exact overlap between its live equity and OOS backtest curves. Any deviation means that the performance of the algorithm has differed for some reason. Several factors can contribute to this, often stemming from the algorithm design. -

    -

    - Live Deployment Reconciliation - - + A perfectly reconciled algorithm has an exact overlap between its live equity and OOS backtest curves. Any deviation means that the performance of the algorithm has differed for some reason. Several factors can contribute to this, often stemming from the algorithm design. For a catalogue of common deviation causes (data, modeling, brokerage, third-party indicators, and real-time scheduled events), see + + Reconciliation + + in the Writing Algorithms documentation.

    + Live Deployment Reconciliation + +

    Reconciliation is scored using two metrics: returns correlation and dynamic time warping (DTW) distance.

    @@ -51708,18 +53050,19 @@

    -

    Plot Order Fills

    +

    Get Live Deployment Parameters

    - Follow these steps to plot the daily order fills of a live algorithm: + Follow these steps to read the live deployment's start datetime, starting equity, and end datetime — the three parameters the OOS backtest must match. All three values come from the live "Strategy Equity" chart, so you only need the project Id.

    1. - Get the live trading orders. + Define the project Id.
    2. -
      orders = api.read_live_orders(project_id)
      +
      var projectId = 23034953;
      +
      project_id = 23034953

      The following table provides links to documentation that explains how to get the project Id, depending on the platform you use: @@ -51768,487 +53111,930 @@

      Plot Order Fills

      -

      - By default, the orders with an ID between 0 and 100. To get orders with an ID greater than 100, pass +

    3. + Read the live "Strategy Equity" chart with the + + ReadLiveChart + + + read_live_chart + + method. The first and last - start + Equity + + points give you the start datetime, starting equity, and end datetime. +
    4. +
      +
      var nowSec = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds();
      +
      +Chart ReadLiveChartWithRetry(int projectId, string chartName)
      +{
      +    for (var attempt = 0; attempt < 10; attempt++)
      +    {
      +        var result = api.ReadLiveChart(projectId, chartName, 0, nowSec, 500);
      +        if (result.Success) return result.Chart;
      +        Console.WriteLine($"Chart data is loading... (attempt {attempt + 1}/10)");
      +        Thread.Sleep(10000);
      +    }
      +    throw new Exception($"Failed to read {chartName} chart after 10 attempts");
      +}
      +
      +var strategyEquity = ReadLiveChartWithRetry(projectId, "Strategy Equity");
      +// The first few points in the series can have a null close, so keep only
      +// the points with a valid close value before extracting start/end.
      +var validValues = strategyEquity.Series["Equity"].Values
      +    .OfType<Candlestick>()
      +    .Where(v => v.Close.HasValue)
      +    .ToList();
      +
      +// Start datetime and starting equity: first valid point.
      +var startDatetime = validValues.First().Time;
      +var startingCash = validValues.First().Close.Value;
      +// End datetime: last valid timestamp of the live Strategy Equity series.
      +// Uncomment the next line instead to reconcile up to "now" and see what
      +// would have happened had you not stopped the live algorithm:
      +// var endDatetime = DateTime.UtcNow;
      +var endDatetime = validValues.Last().Time;
      +
      +Console.WriteLine($"Start (UTC): {startDatetime}");
      +Console.WriteLine($"Starting equity: ${startingCash:N2}");
      +Console.WriteLine($"End (UTC): {endDatetime}");
      +
      from datetime import datetime
      +from time import sleep, time
      +
      +def read_chart(project_id, chart_name, start=0, end=int(time()), count=500):
      +    # Retry up to 10 times until the chart data finishes loading.
      +    for attempt in range(10):
      +        result = api.read_live_chart(project_id, chart_name, start, end, count)
      +        if result.success:
      +            return result.chart
      +        print(f"Chart data is loading... (attempt {attempt + 1}/10)")
      +        sleep(10)
      +    raise RuntimeError(f"Failed to read {chart_name} chart after 10 attempts")
      +
      +strategy_equity = read_chart(project_id, 'Strategy Equity')
      +# The first few points in the series can have a None close, so keep only
      +# the points with a valid close value before extracting start/end.
      +valid_values = [v for v in strategy_equity.series['Equity'].values if v.close is not None]
      +
      +# Start datetime and starting equity: first valid point.
      +start_datetime = valid_values[0].time
      +starting_cash = valid_values[0].close
      +# End datetime: last valid timestamp of the live Strategy Equity series.
      +# Uncomment the next line instead to reconcile up to "now" and see what
      +# would have happened had you not stopped the live algorithm:
      +# end_datetime = datetime.utcnow()
      +end_datetime = valid_values[-1].time
      +
      +print(f"Start (UTC): {start_datetime}")
      +print(f"Starting equity: ${starting_cash:,.2f}")
      +print(f"End (UTC): {end_datetime}")
      +
      +
    + + + +

    Run OOS Backtest

    + + +

    + Follow these steps to run an out-of-sample backtest that mirrors the live deployment. +

    +
      +
    1. + In the project's main algorithm file, set the start date and starting cash to match the values you read in the previous step. Use + + SetStartDate + + + set_start_date and - - end + + SetCash - arguments to the + + set_cash + + so the backtest begins at the same moment and with the same equity as the live deployment. You can either hard-code the values or expose them as + + parameters + + . +
    2. +
    3. + Compile the project by calling the - ReadLiveOrders + CreateCompile - read_live_orders + create_compile - method. Note that - - end + method, then poll + + ReadCompile - - + + read_compile + + until the compile state is - start + BuildSuccess - must be less than 100. -

      + . +
    4. -
      orders = api.read_live_orders(project_id, 100, 150)
      +
      var compilation = api.CreateCompile(projectId);
      +var compileId = compilation.CompileId;
      +
      +// Poll until the build succeeds.
      +for (var attempt = 0; attempt < 10; attempt++)
      +{
      +    var result = api.ReadCompile(projectId, compileId);
      +    if (result.State == CompileState.BuildSuccess) break;
      +    if (result.State == CompileState.BuildError)
      +    {
      +        throw new Exception($"Compilation failed: {string.Join(Environment.NewLine, result.Logs)}");
      +    }
      +    Console.WriteLine($"Compile in queue... (attempt {attempt + 1}/10)");
      +    Thread.Sleep(5000);
      +}
      +
      from time import sleep
      +
      +compilation = api.create_compile(project_id)
      +compile_id = compilation.compile_id
      +
      +# Poll until the build succeeds.
      +for attempt in range(10):
      +    result = api.read_compile(project_id, compile_id)
      +    if result.state == 'BuildSuccess':
      +        break
      +    if result.state == 'BuildError':
      +        raise Exception(f"Compilation failed: {result.logs}")
      +    print(f"Compile in queue... (attempt {attempt + 1}/10)")
      +    sleep(5)
      -

      - The +

    5. + Create the OOS backtest with the - ReadLiveOrders + CreateBacktest - read_live_orders + create_backtest - method returns a list of + method. +
    6. +
      +
      var backtest = api.CreateBacktest(projectId, compileId, "OOS Reconciliation");
      +var backtestId = backtest.BacktestId;
      +Console.WriteLine($"Backtest Id: {backtestId}");
      +
      backtest = api.create_backtest(project_id, compile_id, 'OOS Reconciliation')
      +backtest_id = backtest.backtest_id
      +print(f"Backtest Id: {backtest_id}")
      +
      +
    7. + Poll the + + ReadBacktest + + + read_backtest + + method until the - Order + completed - objects, which have the following properties: -

      -
      + flag is + + True + + . Log the + + progress + + attribute on each poll so you can watch the backtest advance. +
    8. +
      +
      var completed = false;
      +while (!completed)
      +{
      +    var result = api.ReadBacktest(projectId, backtestId);
      +    completed = result.Completed;
      +    Console.WriteLine($"Backtest running... {result.Progress:P2}");
      +    Thread.Sleep(10000);
      +}
      +Console.WriteLine("Backtest completed.");
      +
      completed = False
      +while not completed:
      +    result = api.read_backtest(project_id, backtest_id)
      +    completed = result.completed
      +    print(f"Backtest running... {result.progress:.2%}")
      +    sleep(10)
      +print("Backtest completed.")
      -
    9. - Organize the trade times and prices for each security into a dictionary. -
      -
      class OrderData:
      -    def __init__(self):
      -        self.buy_fill_times = []
      -        self.buy_fill_prices = []
      -        self.sell_fill_times = []
      -        self.sell_fill_prices = []
      +  
    + + -order_data_by_symbol = {} -for order in [x.order for x in orders]: - if order.symbol not in order_data_by_symbol: - order_data_by_symbol[order.symbol] = OrderData() - order_data = order_data_by_symbol[order.symbol] - is_buy = order.quantity > 0 - (order_data.buy_fill_times if is_buy else order_data.sell_fill_times).append(order.last_fill_time.date()) - (order_data.buy_fill_prices if is_buy else order_data.sell_fill_prices).append(order.price) -
    +

    Plot Equity Curves

    + + +

    + Follow these steps to plot the live and OOS backtest equity curves on the same axes. +

    +
      +
    1. + Read the live "Strategy Equity" chart using the retry helper from the previous step.
    2. +
      +
      var liveEquityChart = ReadLiveChartWithRetry(projectId, "Strategy Equity");
      +
      live_equity_chart = read_chart(project_id, 'Strategy Equity')
      +
    3. - Get the price history of each security you traded. -
      -
      qb = QuantBook()
      -start_date = datetime.max.date()
      -end_date = datetime.min.date()
      -for symbol, order_data in order_data_by_symbol.items():
      -    if order_data.buy_fill_times:
      -        start_date = min(start_date, min(order_data.buy_fill_times))
      -        end_date = max(end_date, max(order_data.buy_fill_times))
      -    if order_data.sell_fill_times:
      -        start_date = min(start_date, min(order_data.sell_fill_times))
      -        end_date = max(end_date, max(order_data.sell_fill_times))
      -start_date -= timedelta(days=3)
      -all_history = qb.history(list(order_data_by_symbol.keys()), start_date, end_date, Resolution.DAILY)
      -
      + Read the backtest "Strategy Equity" chart by calling the + + ReadBacktestChart + + + read_backtest_chart + + method with the same retry pattern.
    4. +
      +
      Chart ReadBacktestChartWithRetry(int projectId, string backtestId, string chartName)
      +{
      +    for (var attempt = 0; attempt < 10; attempt++)
      +    {
      +        var result = api.ReadBacktestChart(projectId, chartName, 0, nowSec, 500, backtestId);
      +        if (result.Success) return result.Chart;
      +        Console.WriteLine($"Chart data is loading... (attempt {attempt + 1}/10)");
      +        Thread.Sleep(10000);
      +    }
      +    throw new Exception($"Failed to read backtest {chartName} chart after 10 attempts");
      +}
      +
      +var backtestEquityChart = ReadBacktestChartWithRetry(projectId, backtestId, "Strategy Equity");
      +
      def read_backtest_chart(project_id, backtest_id, chart_name, start=0, end=int(time()), count=500):
      +    for attempt in range(10):
      +        result = api.read_backtest_chart(project_id, chart_name, start, end, count, backtest_id)
      +        if result.success:
      +            return result.chart
      +        print(f"Chart data is loading... (attempt {attempt + 1}/10)")
      +        sleep(10)
      +    raise RuntimeError(f"Failed to read backtest {chart_name} chart after 10 attempts")
      +
      +backtest_equity_chart = read_backtest_chart(project_id, backtest_id, 'Strategy Equity')
      +
    5. - Create a candlestick plot for each security and annotate each plot with buy and sell markers. -
      -
      import plotly.express as px
      -import plotly.graph_objects as go
      +    Extract the
      +    
      +     Equity
      +    
      +    series from each chart, filtering out points with a null close. Python uses a
      +    
      +     pandas.Series
      +    
      +    indexed by timestamp so the two curves can be aligned on the union of their timestamps; C# keeps two lists of
      +    
      +     Candlestick
      +    
      +    points and lets Plotly.NET align them on the same x-axis.
      +   
    6. +
      +
      var liveValues = liveEquityChart.Series["Equity"].Values
      +    .OfType<Candlestick>()
      +    .Where(v => v.Close.HasValue)
      +    .ToList();
      +var backtestValues = backtestEquityChart.Series["Equity"].Values
      +    .OfType<Candlestick>()
      +    .Where(v => v.Close.HasValue)
      +    .ToList();
      +
      import pandas as pd
      +
      +def to_naive(t):
      +    ts = pd.Timestamp(t)
      +    return ts.tz_convert('UTC').tz_localize(None) if ts.tzinfo else ts
      +
      +def to_series(chart, series_name='Equity'):
      +    values = [v for v in chart.series[series_name].values if v.close is not None]
      +    return pd.Series(
      +        [v.close for v in values],
      +        index=pd.DatetimeIndex([to_naive(v.time) for v in values])
      +    )
      +
      +live_series = to_series(live_equity_chart)
      +backtest_series = to_series(backtest_equity_chart)
      +
      +# Keep every timestamp from both sources; align and forward-fill on the union.
      +df = pd.concat([live_series.rename('Live'), backtest_series.rename('OOS Backtest')], axis=1).sort_index().ffill()
      +
      +
    7. + Plot both curves on the same axis. Python uses + + matplotlib + + ; C# uses + + Plotly.NET + + — load + + Plotly.NET + + and + + Plotly.NET.Interactive + + from NuGet and alias + + Plotly.NET.Chart + + to avoid ambiguity with + + QuantConnect.Chart + + . +
    8. +
      +
      #r "nuget: Plotly.NET"
      +#r "nuget: Plotly.NET.Interactive"
      +using PlotlyChart = Plotly.NET.Chart;
      +using Plotly.NET;
      +using Plotly.NET.Interactive;
      +using Plotly.NET.LayoutObjects;
       
      -for symbol, order_data in order_data_by_symbol.items():
      -    history = all_history.loc[symbol]
      +var equityChart = PlotlyChart.Combine(new[]
      +{
      +    Chart2D.Chart.Line<DateTime, decimal, string>(
      +        liveValues.Select(v => v.Time),
      +        liveValues.Select(v => v.Close.Value),
      +        Name: "Live"),
      +    Chart2D.Chart.Line<DateTime, decimal, string>(
      +        backtestValues.Select(v => v.Time),
      +        backtestValues.Select(v => v.Close.Value),
      +        Name: "OOS Backtest")
      +}).WithTitle("Live vs OOS Backtest Equity");
      +
      +display(equityChart);
      +
      import matplotlib.pyplot as plt
      +
      +fig, ax = plt.subplots(figsize=(12, 6))
      +ax.plot(df.index, df['Live'], label='Live')
      +ax.plot(df.index, df['OOS Backtest'], label='OOS Backtest')
      +ax.set_title('Live vs OOS Backtest Equity')
      +ax.set_xlabel('Time')
      +ax.set_ylabel('Portfolio Value ($)')
      +ax.legend()
      +plt.show()
      +
      + Live vs OOS backtest equity curves +
    9. + Score the reconciliation with the annualized returns DTW distance and the Pearson correlation of daily returns. Use + + tslearn + + 's + + dtw + + with a Sakoe-Chiba band so the algorithm runs in linear time. The + + tslearn + + library is Python-only; run this step in a Python research notebook. +
    10. +
      +
      from tslearn.metrics import dtw as DynamicTimeWarping
       
      -    # Plot security price candlesticks
      -    candlestick = go.Candlestick(x=history.index,
      -                                open=history['open'],
      -                                high=history['high'],
      -                                low=history['low'],
      -                                close=history['close'],
      -                                name='Price')
      -    layout = go.Layout(title=go.layout.Title(text=f'{symbol.value} Trades'),
      -                    xaxis_title='Date',
      -                    yaxis_title='Price',
      -                    xaxis_rangeslider_visible=False,
      -                    height=600)
      -    fig = go.Figure(data=[candlestick], layout=layout)
      +returns = df.pct_change().dropna()
       
      -    # Plot buys
      -    fig.add_trace(go.Scatter(
      -        x=order_data.buy_fill_times,
      -        y=order_data.buy_fill_prices,
      -        marker=go.scatter.Marker(color='aqua', symbol='triangle-up', size=10),
      -        mode='markers',
      -        name='Buys',
      -    ))
      +# Pearson correlation between live and OOS backtest daily returns (closer to 1 is better).
      +returns_correlation = returns.corr().iloc[0, 1]
       
      -    # Plot sells
      -    fig.add_trace(go.Scatter(
      -        x=order_data.sell_fill_times,
      -        y=order_data.sell_fill_prices,
      -        marker=go.scatter.Marker(color='indigo', symbol='triangle-down', size=10),
      -        mode='markers',
      -        name='Sells',
      -    ))
      +# Raw DTW distance on the returns curves.
      +raw_dtw = DynamicTimeWarping(
      +    returns['Live'], returns['OOS Backtest'],
      +    global_constraint='sakoe_chiba', sakoe_chiba_radius=3
      +)
      +# Annualize so the distance is on the scale of yearly percent returns (closer to 0 is better).
      +annualized_dtw = abs(((1 + (raw_dtw / returns.shape[0])) ** 252) - 1)
       
      -fig.show()
      -
      - - Plot of AAPL price with buy/sell markers - Plot of SPY price with buy/sell markers -

      - Note: The preceding plots only show the last fill of each trade. If your trade has partial fills, the plots only display the last fill. -

      +print(f"Returns correlation: {returns_correlation:.3f}") +print(f"Annualized returns DTW: {annualized_dtw:.3f}") +
    -

    Plot Charts

    +

    Plot Order Fills

    - Follow these steps to plot the equity curve, benchmark, and drawdown of a live algorithm: + Follow these steps to overlay live and OOS backtest order fills on a single marker-only chart per symbol. The chart deliberately omits candlesticks and any price history so the comparison between live and backtest executions is not drowned out by other series.

    1. - Define the project Id and read the "Strategy Equity", "Drawdown", and "Benchmark" charts. + Read the live and backtest orders. Each call to + + ReadLiveOrders + + + read_live_orders + + and + + ReadBacktestOrders + + + read_backtest_orders + + returns at most 100 orders, so paginate in 100-Id windows until the endpoint returns an empty window. The first window can take a few seconds to load, so retry while it is empty.
    2. -
      from time import sleep, time
      +    
      List<ApiOrderResponse> ReadAllOrders(Func<int, int, List<ApiOrderResponse>> fetchWindow)
      +{
      +    var all = new List<ApiOrderResponse>();
      +    // Retry the first window while the response is empty (may be loading).
      +    List<ApiOrderResponse> first = null;
      +    for (var attempt = 0; attempt < 10; attempt++)
      +    {
      +        first = fetchWindow(0, 100);
      +        if (first.Any()) break;
      +        Console.WriteLine($"Orders loading... (attempt {attempt + 1}/10)");
      +        Thread.Sleep(10000);
      +    }
      +    if (first == null || !first.Any()) return all;
      +    all.AddRange(first);
      +    // Paginate in 100-Id windows until the endpoint returns an empty window.
      +    var start = 100;
      +    while (true)
      +    {
      +        var window = fetchWindow(start, start + 100);
      +        if (!window.Any()) break;
      +        all.AddRange(window);
      +        start += 100;
      +    }
      +    return all;
      +}
       
      -project_id = 23034953
      +var liveOrders = ReadAllOrders((s, e) => api.ReadLiveOrders(projectId, s, e));
      +var backtestOrders = ReadAllOrders((s, e) => api.ReadBacktestOrders(projectId, backtestId, s, e));
      +Console.WriteLine($"Live orders: {liveOrders.Count}, OOS orders: {backtestOrders.Count}");
      +
      from time import sleep
       
      -def read_chart(project_id, chart_name, start=0, end=int(time()), count=500):
      -    # Retry up to 10 times if the chart data is still loading
      +def read_all_orders(fetch_window):
      +    orders = []
      +    # Retry the first window while the response is empty (may be loading).
      +    first = []
           for attempt in range(10):
      -        result = api.read_live_chart(project_id, chart_name, start, end, count)
      -        if result.status == 'loading':
      -            print(f"Chart data is loading... (attempt {attempt + 1}/10)")
      -            sleep(10)
      -            continue
      -        break
      -    return result.chart
      +        first = fetch_window(0, 100)
      +        if first:
      +            break
      +        print(f"Orders loading... (attempt {attempt + 1}/10)")
      +        sleep(10)
      +    if not first:
      +        return orders
      +    orders.extend(first)
      +    # Paginate in 100-Id windows until the endpoint returns an empty window.
      +    start = 100
      +    while True:
      +        window = fetch_window(start, start + 100)
      +        if not window:
      +            break
      +        orders.extend(window)
      +        start += 100
      +    return orders
       
      -strategy_equity = read_chart(project_id, 'Strategy Equity')
      -drawdown_chart = read_chart(project_id, 'Drawdown')
      -benchmark_chart = read_chart(project_id, 'Benchmark')
      +live_orders = read_all_orders(lambda s, e: api.read_live_orders(project_id, s, e)) +backtest_orders = read_all_orders(lambda s, e: api.read_backtest_orders(project_id, backtest_id, s, e)) +print(f"Live orders: {len(live_orders)}, OOS orders: {len(backtest_orders)}")

      - The process to get your project Id depends on if you use the - - Cloud Platform - - , - - Local Platform - - , or - - CLI + For more on the order objects returned, see + + Plot Order Fills - . + in the Live Analysis documentation.

    3. - Extract the series and create a - - pandas.DataFrame - - . + Organize the trade times and prices for each security into a dictionary for both the live and backtest fills.
    4. -
      def to_series(chart, series_name, selector=lambda x: x.y):
      -    return pd.Series({v.time: selector(v) for v in chart.series[series_name].values})
      +    
      var liveBySymbol = liveOrders.Select(x => x.Order).GroupBy(o => o.Symbol);
      +var backtestBySymbol = backtestOrders.Select(x => x.Order)
      +    .GroupBy(o => o.Symbol)
      +    .ToDictionary(g => g.Key, g => g.ToList());
      +
      import pandas as pd
       
      -df = pd.DataFrame({
      -    "Equity": to_series(strategy_equity, 'Equity', selector=lambda x: x.close), 
      -    "Return": to_series(strategy_equity, 'Return'), 
      -    "Drawdown": to_series(drawdown_chart, 'Equity Drawdown'), 
      -    "Benchmark": to_series(benchmark_chart, 'Benchmark')
      -}).ffill()
      -df.index = df.index.tz_localize('UTC').tz_convert('US/Eastern').tz_localize(None)
      +def to_naive(t): + # Strip tzinfo so plotly can serialize the fill times. + ts = pd.Timestamp(t) + return ts.tz_convert('UTC').tz_localize(None) if ts.tzinfo else ts + +class OrderData: + def __init__(self): + self.buy_fill_times = [] + self.buy_fill_prices = [] + self.sell_fill_times = [] + self.sell_fill_prices = [] + +def group_by_symbol(orders): + data_by_symbol = {} + for order in [x.order for x in orders]: + if order.symbol not in data_by_symbol: + data_by_symbol[order.symbol] = OrderData() + data = data_by_symbol[order.symbol] + is_buy = order.quantity > 0 + (data.buy_fill_times if is_buy else data.sell_fill_times).append(to_naive(order.last_fill_time)) + (data.buy_fill_prices if is_buy else data.sell_fill_prices).append(order.price) + return data_by_symbol + +live_by_symbol = group_by_symbol(live_orders) +backtest_by_symbol = group_by_symbol(backtest_orders)
    5. - Plot the performance chart. + Plot one figure per symbol with four marker traces: live buys, live sells, backtest buys, backtest sells. Distinct markers keep live versus backtest executions visually separable.
    6. -
      # Create subplots to plot series on same/different plots
      -fig, ax = plt.subplots(3, 1, figsize=(12, 16), sharex=True, gridspec_kw={'height_ratios': [2, 1, 1]})
      +    
      foreach (var liveGroup in liveBySymbol)
      +{
      +    var symbol = liveGroup.Key;
      +    var live = liveGroup.ToList();
      +    var bt = backtestBySymbol.TryGetValue(symbol, out var btList) ? btList : new List<Order>();
       
      -# Plot the equity curve
      -ax[0].plot(df.index, df["Equity"])
      -ax[0].set_title("Strategy Equity Curve")
      -ax[0].set_ylabel("Portfolio Value ($)")
      +    var traces = new[]
      +    {
      +        Chart2D.Chart.Point<DateTime, decimal, string>(
      +            live.Where(o => o.Quantity > 0).Select(o => o.LastFillTime ?? o.Time),
      +            live.Where(o => o.Quantity > 0).Select(o => o.Price),
      +            Name: "Live Buys"),
      +        Chart2D.Chart.Point<DateTime, decimal, string>(
      +            live.Where(o => o.Quantity < 0).Select(o => o.LastFillTime ?? o.Time),
      +            live.Where(o => o.Quantity < 0).Select(o => o.Price),
      +            Name: "Live Sells"),
      +        Chart2D.Chart.Point<DateTime, decimal, string>(
      +            bt.Where(o => o.Quantity > 0).Select(o => o.LastFillTime ?? o.Time),
      +            bt.Where(o => o.Quantity > 0).Select(o => o.Price),
      +            Name: "OOS Backtest Buys"),
      +        Chart2D.Chart.Point<DateTime, decimal, string>(
      +            bt.Where(o => o.Quantity < 0).Select(o => o.LastFillTime ?? o.Time),
      +            bt.Where(o => o.Quantity < 0).Select(o => o.Price),
      +            Name: "OOS Backtest Sells")
      +    };
      +    var fillsChart = PlotlyChart.Combine(traces).WithTitle($"{symbol} Live vs OOS Backtest Fills");
      +    display(fillsChart);
      +}
      +
      import plotly.graph_objects as go
       
      -# Plot the benchmark on the same plot, scale by using another y-axis
      -ax2 = ax[0].twinx()
      -ax2.plot(df.index, df["Benchmark"], color="grey")
      -ax2.set_ylabel("Benchmark Price ($)", color="grey")
      +symbols = set(live_by_symbol.keys()) | set(backtest_by_symbol.keys())
       
      -# Plot the daily returns
      -ax[1].plot(df.index, df["Return"], color="blue")
      -ax[1].set_title("Daily Return")
      -ax[1].set_ylabel("%")
      +for symbol in symbols:
      +    live = live_by_symbol.get(symbol, OrderData())
      +    bt = backtest_by_symbol.get(symbol, OrderData())
       
      -# Plot the drawdown on another plot
      -ax[2].plot(df.index, df["Drawdown"], color="red")
      -ax[2].set_title("Drawdown")
      -ax[2].set_xlabel("Time")
      -ax[2].set_ylabel("%");
      + fig = go.Figure(layout=go.Layout( + title=go.layout.Title(text=f'{symbol.value} Live vs OOS Backtest Fills'), + xaxis_title='Fill Time', + yaxis_title='Fill Price', + height=600 + )) + + fig.add_trace(go.Scatter( + x=live.buy_fill_times, y=live.buy_fill_prices, mode='markers', name='Live Buys', + marker=go.scatter.Marker(color='aqua', symbol='triangle-up', size=12) + )) + fig.add_trace(go.Scatter( + x=live.sell_fill_times, y=live.sell_fill_prices, mode='markers', name='Live Sells', + marker=go.scatter.Marker(color='indigo', symbol='triangle-down', size=12) + )) + fig.add_trace(go.Scatter( + x=bt.buy_fill_times, y=bt.buy_fill_prices, mode='markers', name='OOS Backtest Buys', + marker=go.scatter.Marker(color='aqua', symbol='triangle-up-open', size=12, line=dict(width=2)) + )) + fig.add_trace(go.Scatter( + x=bt.sell_fill_times, y=bt.sell_fill_prices, mode='markers', name='OOS Backtest Sells', + marker=go.scatter.Marker(color='indigo', symbol='triangle-down-open', size=12, line=dict(width=2)) + )) + + fig.show()
      - api-equity-curve

    - The following table shows all the chart series you can plot: + Note: the preceding plots only show the last fill of each trade. If your trade has partial fills, the plots only display the last fill.

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - Chart - - Series - - Description -
    - Strategy Equity - - Equity - - Time series of the equity curve. This series may not update daily. -
    - Return - - Time series of daily returns. Use this series for daily updates. -
    - Daily Performance - - Time series of daily percentage change. This series has as many data points as "Equity". -
    - Capacity - - Strategy Capacity - - Time series of - - strategy capacity - - snapshots -
    - Drawdown - - Equity Drawdown - - Time series of equity peak-to-trough value -
    - Benchmark - - Benchmark - - Time series of the - - benchmark - - closing price (SPY, by default) -
    - Exposure - - - SecurityType - - - Long Ratio - - Time series of the overall ratio of - - SecurityType - - long positions of the whole portfolio if any - - SecurityType - - is ever in the universe -
    - - SecurityType - - - Short Ratio - - Time series of the overall ratio of - - SecurityType - - short position of the whole portfolio if any - - SecurityType - - is ever in the universe -
    - Assets Sales Volume - - Each - - ticker - - is one series - - A chart showing the proportion of total volume for each traded security. -
    - Portfolio Turnover - - Portfolio Turnover - - A time series of the portfolio turnover rate. -
    - Portfolio Margin - - Each - - ticker - - is one series - - A stacked area chart of the portfolio margin usage. For more information about this chart, see - - Portfolio Margin Plots - - . -
    - Asset Plot - - Price and Annotations - - A time series of an asset's price with order event annotations. For more information about these charts, see - - Asset Plots - - . -
    - Custom Chart - - Custom Series - - Time series of a - - Series - - in a - - custom chart - -
    -

    Example

    +

    Examples

    - Example 1: Read Live Algorithm Statistics + Example 1: Generate an OOS Reconciliation Curve

    - The following example reads the current running live algorithm's statistics in a jupyter notebook. + The following example reads the live deployment's "Strategy Equity" chart, runs an OOS backtest that matches its start datetime and starting cash, and plots both the equity curves and the order fills side by side. Before running it, make sure your project's main algorithm file sets + + StartDate + + + start_date + + and + + Cash + + + cash + + to match the live deployment (or reads them as parameters).

    // Load the necessary assemblies.
     #load "../Initialize.csx"
     #load "../QuantConnect.csx"
    +#r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
     
     using QuantConnect;
     using QuantConnect.Api;
     using QuantConnect.Research;
    +using System;
    +using System.Linq;
    +using System.Threading;
    +using PlotlyChart = Plotly.NET.Chart;
    +using Plotly.NET;
    +using Plotly.NET.Interactive;
    +using Plotly.NET.LayoutObjects;
     
     // Instantiate QuantBook instance for researching.
     var qb = new QuantBook();
    +var projectId = qb.ProjectId;   // Replace if the live algorithm is not in this project.
    +var nowSec = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds();
    +
    +// Read the live Strategy Equity chart. The first and last valid points give
    +// you the start datetime, starting cash, and end datetime.
    +var liveEquity = api.ReadLiveChart(projectId, "Strategy Equity", 0, nowSec, 500).Chart;
    +var equityValues = liveEquity.Series["Equity"].Values
    +    .OfType<Candlestick>()
    +    .Where(v => v.Close.HasValue)
    +    .ToList();
    +var startDate = equityValues.First().Time;
    +var startingCash = equityValues.First().Close.Value;
    +// Uncomment to reconcile up to "now" instead:
    +// var endDate = DateTime.UtcNow;
    +var endDate = equityValues.Last().Time;
    +
    +// Compile and create the OOS backtest (start date and cash must be set in the algorithm itself).
    +var compilation = api.CreateCompile(projectId);
    +var compileId = compilation.CompileId;
    +while (api.ReadCompile(projectId, compileId).State != CompileState.BuildSuccess)
    +{
    +    Thread.Sleep(5000);
    +}
    +var backtest = api.CreateBacktest(projectId, compileId, "OOS Reconciliation");
    +var backtestId = backtest.BacktestId;
     
    -// Get current running live algorithm list in the current project.
    -var liveAlgorithms = api.ListLiveAlgorithms(AlgorithmStatus.Running)
    -var deployId = liveAlgorithms.Algorithms
    -    .Single(x => x.ProjectId == qb.ProjectId)
    -    .DeployId;
    -var liveAlgorithm = api.ReadLiveAlgorithm(qb.ProjectId, deployId);
    +var completed = false;
    +while (!completed)
    +{
    +    var result = api.ReadBacktest(projectId, backtestId);
    +    completed = result.Completed;
    +    Console.WriteLine($"Backtest running... {result.Progress:P2}");
    +    Thread.Sleep(10000);
    +}
    +Console.WriteLine("Backtest completed.");
     
    -// Obtain the live algorithm statistics.
    -Console.WriteLine(backtest.RuntimeStatistics.ToString());
    +// Read the backtest Strategy Equity chart. +var backtestEquity = api.ReadBacktestChart(projectId, "Strategy Equity", 0, nowSec, 500, backtestId).Chart; + +// Read live and backtest orders for the fill overlay. Each call returns at +// most 100 orders, so paginate in 100-Id windows until we get an empty window. +// The first window can take a few seconds to load, so retry while empty. +List<ApiOrderResponse> ReadAllOrders(Func<int, int, List<ApiOrderResponse>> fetchWindow) +{ + var all = new List<ApiOrderResponse>(); + List<ApiOrderResponse> first = null; + for (var attempt = 0; attempt < 10; attempt++) + { + first = fetchWindow(0, 100); + if (first.Any()) break; + Thread.Sleep(10000); + } + if (first == null || !first.Any()) return all; + all.AddRange(first); + var start = 100; + while (true) + { + var window = fetchWindow(start, start + 100); + if (!window.Any()) break; + all.AddRange(window); + start += 100; + } + return all; +} + +var liveOrders = ReadAllOrders((s, e) => api.ReadLiveOrders(projectId, s, e)); +var backtestOrders = ReadAllOrders((s, e) => api.ReadBacktestOrders(projectId, backtestId, s, e)); + +Console.WriteLine($"Start: {startDate}, Starting cash: {startingCash}, End: {endDate}"); +Console.WriteLine($"Live orders: {liveOrders.Count}, OOS orders: {backtestOrders.Count}"); + +// Overlay the two equity curves with Plotly.NET. +var backtestValues = backtestEquity.Series["Equity"].Values + .OfType<Candlestick>() + .Where(v => v.Close.HasValue) + .ToList(); + +var equityChart = PlotlyChart.Combine(new[] +{ + Chart2D.Chart.Line<DateTime, decimal, string>( + equityValues.Select(v => v.Time), + equityValues.Select(v => v.Close.Value), + Name: "Live"), + Chart2D.Chart.Line<DateTime, decimal, string>( + backtestValues.Select(v => v.Time), + backtestValues.Select(v => v.Close.Value), + Name: "OOS Backtest") +}).WithTitle("Live vs OOS Backtest Equity"); +display(equityChart); + +// Overlay the live and backtest fills per symbol. +var liveBySymbol = liveOrders.Select(x => x.Order).GroupBy(o => o.Symbol); +var backtestBySymbol = backtestOrders.Select(x => x.Order).GroupBy(o => o.Symbol).ToDictionary(g => g.Key, g => g.ToList()); + +foreach (var liveGroup in liveBySymbol) +{ + var symbol = liveGroup.Key; + var live = liveGroup.ToList(); + var bt = backtestBySymbol.TryGetValue(symbol, out var btList) ? btList : new List<Order>(); + + var traces = new[] + { + Chart2D.Chart.Point<DateTime, decimal, string>( + live.Where(o => o.Quantity > 0).Select(o => o.LastFillTime ?? o.Time), + live.Where(o => o.Quantity > 0).Select(o => o.Price), + Name: "Live Buys"), + Chart2D.Chart.Point<DateTime, decimal, string>( + live.Where(o => o.Quantity < 0).Select(o => o.LastFillTime ?? o.Time), + live.Where(o => o.Quantity < 0).Select(o => o.Price), + Name: "Live Sells"), + Chart2D.Chart.Point<DateTime, decimal, string>( + bt.Where(o => o.Quantity > 0).Select(o => o.LastFillTime ?? o.Time), + bt.Where(o => o.Quantity > 0).Select(o => o.Price), + Name: "OOS Backtest Buys"), + Chart2D.Chart.Point<DateTime, decimal, string>( + bt.Where(o => o.Quantity < 0).Select(o => o.LastFillTime ?? o.Time), + bt.Where(o => o.Quantity < 0).Select(o => o.Price), + Name: "OOS Backtest Sells") + }; + var fillsChart = PlotlyChart.Combine(traces).WithTitle($"{symbol} Live vs OOS Backtest Fills"); + display(fillsChart); +}
    # Instantiate QuantBook instance for researching.
    +from datetime import datetime
    +from time import sleep, time
    +import pandas as pd
    +import matplotlib.pyplot as plt
    +import plotly.graph_objects as go
    +from tslearn.metrics import dtw as DynamicTimeWarping
    +
     qb = QuantBook()
    +project_id = qb.project_id   # Replace if the live algorithm is not in this project.
     
    -# Get current running live algorithm list in the current project.
    -live_algorithms = api.list_live_algorithms(AlgorithmStatus.RUNNING)
    -deploy_id = next(
    -    [x for x in live_algorithms.algorithms if x.project_id == qb.project_id]
    -).deploy_id
    -live_algorithm = api.read_live_algorithm(project_id, deploy_id)
    +# Read the live Strategy Equity chart. The first and last points give you the
    +# start datetime, starting cash, and end datetime.
    +def read_chart(project_id, chart_name, start=0, end=int(time()), count=500):
    +    for attempt in range(10):
    +        result = api.read_live_chart(project_id, chart_name, start, end, count)
    +        if result.success:
    +            return result.chart
    +        sleep(10)
    +    raise RuntimeError(f"Failed to read {chart_name} chart after 10 attempts")
    +
    +live_equity_chart = read_chart(project_id, 'Strategy Equity')
    +# Skip leading points with a None close (common at the start of a deployment).
    +valid_values = [v for v in live_equity_chart.series['Equity'].values if v.close is not None]
    +start_datetime = valid_values[0].time
    +starting_cash = valid_values[0].close
    +# Uncomment to reconcile up to "now" instead:
    +# end_datetime = datetime.utcnow()
    +end_datetime = valid_values[-1].time
    +
    +# Compile and create the OOS backtest (start date and cash must be set in the algorithm itself).
    +compilation = api.create_compile(project_id)
    +compile_id = compilation.compile_id
    +while api.read_compile(project_id, compile_id).state != 'BuildSuccess':
    +    sleep(5)
    +backtest = api.create_backtest(project_id, compile_id, 'OOS Reconciliation')
    +backtest_id = backtest.backtest_id
    +completed = False
    +while not completed:
    +    result = api.read_backtest(project_id, backtest_id)
    +    completed = result.completed
    +    print(f'Backtest running... {result.progress:.2%}')
    +    sleep(10)
    +
    +# Read the backtest Strategy Equity chart.
    +def read_backtest_chart(project_id, backtest_id, chart_name, start=0, end=int(time()), count=500):
    +    for attempt in range(10):
    +        result = api.read_backtest_chart(project_id, chart_name, start, end, count, backtest_id)
    +        if result.success:
    +            return result.chart
    +        sleep(10)
    +    raise RuntimeError(f"Failed to read backtest {chart_name} chart after 10 attempts")
    +
    +backtest_equity_chart = read_backtest_chart(project_id, backtest_id, 'Strategy Equity')
    +
    +# Overlay the two equity curves.
    +def to_naive(t):
    +    ts = pd.Timestamp(t)
    +    return ts.tz_convert('UTC').tz_localize(None) if ts.tzinfo else ts
    +
    +def to_series(chart, series_name='Equity'):
    +    values = [v for v in chart.series[series_name].values if v.close is not None]
    +    return pd.Series(
    +        [v.close for v in values],
    +        index=pd.DatetimeIndex([to_naive(v.time) for v in values])
    +    )
     
    -# Obtain the live algorithm statistics.
    -print(live_algorithm.runtime_statistics)
    +df = pd.concat([ + to_series(live_equity_chart).rename('Live'), + to_series(backtest_equity_chart).rename('OOS Backtest') +], axis=1).sort_index().ffill() + +fig, ax = plt.subplots(figsize=(12, 6)) +ax.plot(df.index, df['Live'], label='Live') +ax.plot(df.index, df['OOS Backtest'], label='OOS Backtest') +ax.set_title('Live vs OOS Backtest Equity') +ax.set_xlabel('Time'); ax.set_ylabel('Portfolio Value ($)') +ax.legend(); plt.show() + +# Score the reconciliation: returns correlation and annualized returns DTW distance. +returns = df.pct_change().dropna() +returns_correlation = returns.corr().iloc[0, 1] +raw_dtw = DynamicTimeWarping( + returns['Live'], returns['OOS Backtest'], + global_constraint='sakoe_chiba', sakoe_chiba_radius=3 +) +annualized_dtw = abs(((1 + (raw_dtw / returns.shape[0])) ** 252) - 1) +print(f"Returns correlation: {returns_correlation:.3f}") +print(f"Annualized returns DTW: {annualized_dtw:.3f}") + +# Overlay the live and backtest fills per symbol. +class OrderData: + def __init__(self): + self.buy_fill_times, self.buy_fill_prices = [], [] + self.sell_fill_times, self.sell_fill_prices = [], [] + +def group_by_symbol(orders): + out = {} + for order in [x.order for x in orders]: + out.setdefault(order.symbol, OrderData()) + d = out[order.symbol] + is_buy = order.quantity > 0 + (d.buy_fill_times if is_buy else d.sell_fill_times).append(to_naive(order.last_fill_time)) + (d.buy_fill_prices if is_buy else d.sell_fill_prices).append(order.price) + return out + +def read_all_orders(fetch_window): + orders = [] + first = [] + for attempt in range(10): + first = fetch_window(0, 100) + if first: + break + sleep(10) + if not first: + return orders + orders.extend(first) + start = 100 + while True: + window = fetch_window(start, start + 100) + if not window: + break + orders.extend(window) + start += 100 + return orders + +live_by_symbol = group_by_symbol(read_all_orders(lambda s, e: api.read_live_orders(project_id, s, e))) +backtest_by_symbol = group_by_symbol(read_all_orders(lambda s, e: api.read_backtest_orders(project_id, backtest_id, s, e))) + +for symbol in set(live_by_symbol) | set(backtest_by_symbol): + live = live_by_symbol.get(symbol, OrderData()) + bt = backtest_by_symbol.get(symbol, OrderData()) + fig = go.Figure(layout=go.Layout( + title=go.layout.Title(text=f'{symbol.value} Live vs OOS Backtest Fills'), + xaxis_title='Fill Time', yaxis_title='Fill Price', height=600)) + fig.add_trace(go.Scatter(x=live.buy_fill_times, y=live.buy_fill_prices, mode='markers', + name='Live Buys', marker=go.scatter.Marker(color='aqua', symbol='triangle-up', size=12))) + fig.add_trace(go.Scatter(x=live.sell_fill_times, y=live.sell_fill_prices, mode='markers', + name='Live Sells', marker=go.scatter.Marker(color='indigo', symbol='triangle-down', size=12))) + fig.add_trace(go.Scatter(x=bt.buy_fill_times, y=bt.buy_fill_prices, mode='markers', + name='OOS Backtest Buys', marker=go.scatter.Marker(color='aqua', symbol='triangle-up-open', size=12, line=dict(width=2)))) + fig.add_trace(go.Scatter(x=bt.sell_fill_times, y=bt.sell_fill_prices, mode='markers', + name='OOS Backtest Sells', marker=go.scatter.Marker(color='indigo', symbol='triangle-down-open', size=12, line=dict(width=2)))) + fig.show()
    @@ -52256,7 +54042,7 @@

     

    -
    +

    Meta Analysis

    Live Deployment Automation

    @@ -52309,13 +54095,17 @@

    Get Project Ids

    #load "../Initialize.csx"
  • - Import the data types. + Load the necessary assembly files.
  • #load "../QuantConnect.csx"
    -#r "../Microsoft.Data.Analysis.dll"
    -
    -using QuantConnect;
    +#r "../Microsoft.Data.Analysis.dll"
    +
    +
  • + Import the data types. +
  • +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Algorithm;
     using QuantConnect.Research;
    @@ -52545,12 +54335,16 @@ 

    in a jupyter notebook. It can help with a streamline deployment.

    -
    +
    // Load the necessary assemblies.
    -#load "../Initialize.csx"
    -#load "../QuantConnect.csx"
    -
    -using QuantConnect;
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
    +#load "../QuantConnect.csx"
    +
    +
    +
    using QuantConnect;
     using QuantConnect.Api;
     using QuantConnect.Research;
     
    @@ -52601,7 +54395,7 @@ 

    "ib-password": "password", "ib-weekly-restart-utc-time": "00:00:00" } -result = api.CreateLiveAlgorithm(qb.project_id, compile_id, live_node_id, brokerage_settings)

    +result = api.create_live_algorithm(qb.project_id, compile_id, live_node_id, brokerage_settings)

    @@ -52874,11 +54668,16 @@

    Import Libraries

    libraries by the following:

    +
    +
    // Load the required assembly files and data types in a separate cell.
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
    +#load "../QuantConnect.csx"
    +
    -
    #load "../Initialize.csx"
    -#load "../QuantConnect.csx"
    -
    -using QuantConnect;
    +   
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Data.Market;
     using QuantConnect.Algorithm;
    @@ -53425,12 +55224,16 @@ 

    Examples

    The below code snippets concludes the above jupyter research notebook content.

    -
    +
    // Load the required assembly files and data types.
    -#load "../Initialize.csx"
    -#load "../QuantConnect.csx"
    -
    -using QuantConnect;
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
    +#load "../QuantConnect.csx"
    +
    +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Data.Market;
     using QuantConnect.Algorithm;
    @@ -53588,7 +55391,7 @@ 

    Examples

    symbols[assets[i]] = qb.add_equity(assets[i], Resolution.MINUTE).symbol # Call the History method with qb.Securities.Keys for all tickers, time argument(s), and resolution to request historical data for the symbol. -history = qb.history(qb.securities.Keys, datetime(2021, 1, 1), datetime(2021, 12, 31), Resolution.DAILY) +history = qb.history(qb.securities.keys(), datetime(2021, 1, 1), datetime(2021, 12, 31), Resolution.DAILY) # Select the close column and then call the unstack method. df = history['close'].unstack(level=0) @@ -54129,7 +55932,7 @@

    Set Up Algorithm

    def every_day_before_market_close(self) -> None:
         # Retrain the regressor every month
         if self._time < self.time:
    -        self.BuildModel()
    +        self.build_model()
             self._time = Expiry.end_of_month(self.time)
         
         qb = self
    @@ -54185,7 +55988,7 @@ 

    Examples

    qb.add_equity(assets[i],Resolution.MINUTE).symbol # Call the History method with qb.securities.keys for all tickers, time argument(s), and resolution to request historical data for the symbol. -history = qb.history(qb.securities.Keys, datetime(2019, 1, 1), datetime(2021, 12, 31), Resolution.DAILY) +history = qb.history(qb.securities.keys(), datetime(2019, 1, 1), datetime(2021, 12, 31), Resolution.DAILY) # Select the close column and then call the unstack method. df = history['close'].unstack(level=0) @@ -54265,7 +56068,7 @@

    Examples

    self.regressor = RandomForestRegressor(n_estimators=100, min_samples_split=5, random_state = 1990) # Get historical data - history = self.history(self.securities.Keys, 360, Resolution.DAILY) + history = self.history(self.securities.keys(), 360, Resolution.DAILY) # Select the close column and then call the unstack method. df = history['close'].unstack(level=0) @@ -54289,7 +56092,7 @@

    Examples

    qb = self # Fetch history on our universe - df = qb.history(qb.securities.Keys, 2, Resolution.DAILY) + df = qb.history(qb.securities.keys(), 2, Resolution.DAILY) if df.empty: return # Make all of them into a single time index. @@ -54361,11 +56164,16 @@

    Import Libraries

    libraries by the following:

    +
    +
    // Load the required assembly files and data types in a separate cell.
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
    +#load "../QuantConnect.csx"
    +
    -
    #load "../Initialize.csx"
    -#load "../QuantConnect.csx"
    -
    -using QuantConnect;
    +   
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Data.Market;
     using QuantConnect.Algorithm;
    @@ -54915,12 +56723,16 @@ 

    Examples

    The below code snippets concludes the above jupyter research notebook content.

    -
    +
    // Load the required assembly files and data types.
    -#load "../Initialize.csx"
    -#load "../QuantConnect.csx"
    -
    -using QuantConnect;
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
    +#load "../QuantConnect.csx"
    +
    +
    +
    using QuantConnect;
     using QuantConnect.Data;
     using QuantConnect.Data.Market;
     using QuantConnect.Algorithm;
    @@ -55252,7 +57064,7 @@ 

    Examples

    def every_day_before_market_close(self) -> None: qb = self # Fetch history on our universe - history = qb.history(qb.securities.Keys, 252*2, Resolution.DAILY) + history = qb.history(qb.securities.keys(), 252*2, Resolution.DAILY) if history.empty: return # Select the close column and then call the unstack method, then call pct_change to compute the daily return. @@ -56354,7 +58166,7 @@

    Examples

    # Get the real-time log close price for all assets and store in a Series series = pd.Series() - for symbol in qb.securities.Keys: + for symbol in qb.securities.keys(): series[symbol] = np.log(qb.securities[symbol].close) # Get the spread @@ -56869,7 +58681,7 @@

    Examples

    qb.add_equity(assets[i],Resolution.MINUTE).symbol # Call the History method with qb.Securities.Keys for all tickers, time argument(s), and resolution to request historical data for the symbol. -history = qb.history(qb.Securities.Keys, datetime(2021, 1, 1), datetime(2021, 12, 31), Resolution.DAILY) +history = qb.history(qb.securities.keys(), datetime(2021, 1, 1), datetime(2021, 12, 31), Resolution.DAILY) # Select the close column and then call the unstack method. close_price = history['close'].unstack(level=0) @@ -57047,7 +58859,7 @@

    Examples

    # Get the real-time log close price for all assets and store in a Series series = pd.Series() - for symbol in qb.securities.Keys: + for symbol in qb.securities.keys(): series[symbol] = np.log(qb.securities[symbol].close) # Get the spread @@ -58105,7 +59917,7 @@

    Set Up Algorithm

    # ============================== - if prediction > qb.Securities[self.asset].Price: + if prediction > qb.securities[self.asset].price: self.set_holdings(self.asset, 1.) else: self.set_holdings(self.asset, -1.)
    @@ -58321,7 +60133,7 @@

    Examples

    # ============================== - if prediction > qb.Securities[self.asset].Price: + if prediction > qb.securities[self.asset].price: self.set_holdings(self.asset, 1.) else: self.set_holdings(self.asset, -1.)
    @@ -58911,7 +60723,7 @@

    Examples

    history = qb.history(list(symbols.keys()), datetime(2019, 1, 1), datetime(2021, 12, 31), Resolution.DAILY) # Call SPY history as reference. -spy = qb.history(qb.add_equity("SPY").Symbol, datetime(2019, 1, 1), datetime(2021, 12, 31), Resolution.DAILY) +spy = qb.history(qb.add_equity("SPY").symbol, datetime(2019, 1, 1), datetime(2021, 12, 31), Resolution.DAILY) # Call the History method with list of buyback tickers, time argument(s), and resolution to request buyback data for the symbol. history_buybacks = qb.history(list(symbols.values()), datetime(2019, 1, 1), datetime(2021, 12, 31), Resolution.DAILY) @@ -59708,8 +61520,8 @@

    Examples

    # Subscribe to the universe price data for symbol in etf_symbols: - security = qb.AddSecurity(symbol, Resolution.Daily) - security_timezone = security.Exchange.TimeZone + security = qb.add_security(symbol, Resolution.DAILY) + security_timezone = security.exchange.time_zone security_symbols.append(symbol) return security_symbols, security_timezone @@ -59737,7 +61549,7 @@

    Examples

    print(f'Error: The ETF universe file does not exist') return security_ids = df[df.columns[1]].values - symbols = [qb.Symbol(security_id) for security_id in security_ids] + symbols = [qb.symbol(security_id) for security_id in security_ids] return symbols # Instantiate a QuantBook. diff --git a/single-page/Quantconnect-Writing-Algorithms.html b/single-page/Quantconnect-Writing-Algorithms.html index e52c6213e0..c07372a24a 100644 --- a/single-page/Quantconnect-Writing-Algorithms.html +++ b/single-page/Quantconnect-Writing-Algorithms.html @@ -154,21 +154,29 @@

    Table of Content

  • 6.1 Overview
  • 6.2 QuantConnect
  • 6.2.1 Binance Crypto Future Margin Rate Data
  • -
  • 6.2.2 Bybit Crypto Future Margin Rate Data
  • -
  • 6.2.3 CFD Data
  • -
  • 6.2.4 Cash Indices
  • -
  • 6.2.5 FOREX Data
  • -
  • 6.2.6 Fear and Greed
  • -
  • 6.2.7 International Future Universe
  • -
  • 6.2.8 US ETF Constituents
  • -
  • 6.2.9 US Equities Short Availability
  • -
  • 6.2.10 US Equity Coarse Universe
  • -
  • 6.2.11 US Equity Option Universe
  • -
  • 6.2.12 US Equity Security Master
  • -
  • 6.2.13 US Future Option Universe
  • -
  • 6.2.14 US Future Universe
  • -
  • 6.2.15 US Futures Security Master
  • -
  • 6.2.16 US Index Option Universe
  • +
  • 6.2.2 Binance Crypto Future Price Data
  • +
  • 6.2.3 Binance Crypto Price Data
  • +
  • 6.2.4 Binance US Crypto Price Data
  • +
  • 6.2.5 Bitfinex Crypto Price Data
  • +
  • 6.2.6 Bybit Crypto Future Margin Rate Data
  • +
  • 6.2.7 Bybit Crypto Future Price Data
  • +
  • 6.2.8 Bybit Crypto Price Data
  • +
  • 6.2.9 CFD Data
  • +
  • 6.2.10 Cash Indices
  • +
  • 6.2.11 Coinbase Crypto Price Data
  • +
  • 6.2.12 FOREX Data
  • +
  • 6.2.13 Fear and Greed
  • +
  • 6.2.14 International Future Universe
  • +
  • 6.2.15 Kraken Crypto Price Data
  • +
  • 6.2.16 US ETF Constituents
  • +
  • 6.2.17 US Equities Short Availability
  • +
  • 6.2.18 US Equity Coarse Universe
  • +
  • 6.2.19 US Equity Option Universe
  • +
  • 6.2.20 US Equity Security Master
  • +
  • 6.2.21 US Future Option Universe
  • +
  • 6.2.22 US Future Universe
  • +
  • 6.2.23 US Futures Security Master
  • +
  • 6.2.24 US Index Option Universe
  • 6.3 AlgoSeek
  • 6.3.1 US Equities
  • 6.3.2 US Equity Options
  • @@ -179,24 +187,17 @@

    Table of Content

  • 6.4.1 US Fundamental Data
  • 6.5 TickData
  • 6.5.1 International Futures
  • -
  • 6.6 CoinAPI
  • -
  • 6.6.1 Binance Crypto Future Price Data
  • -
  • 6.6.2 Binance Crypto Price Data
  • -
  • 6.6.3 Binance US Crypto Price Data
  • -
  • 6.6.4 Bitfinex Crypto Price Data
  • -
  • 6.6.5 Bybit Crypto Future Price Data
  • -
  • 6.6.6 Bybit Crypto Price Data
  • -
  • 6.6.7 Coinbase Crypto Price Data
  • -
  • 6.6.8 Kraken Crypto Price Data
  • -
  • 6.7 Benzinga
  • -
  • 6.7.1 Benzinga News Feed
  • -
  • 6.8 Blockchain
  • -
  • 6.8.1 Bitcoin Metadata
  • -
  • 6.9 Brain
  • -
  • 6.9.1 Brain Language Metrics on Company Filings
  • -
  • 6.9.2 Brain ML Stock Ranking
  • -
  • 6.9.3 Brain Sentiment Indicator
  • -
  • 6.9.4 Brain Wikipedia Page Views
  • +
  • 6.6 Benzinga
  • +
  • 6.6.1 Benzinga News Feed
  • +
  • 6.7 Blockchain
  • +
  • 6.7.1 Bitcoin Metadata
  • +
  • 6.8 Brain
  • +
  • 6.8.1 Brain Language Metrics on Company Filings
  • +
  • 6.8.2 Brain ML Stock Ranking
  • +
  • 6.8.3 Brain Sentiment Indicator
  • +
  • 6.8.4 Brain Wikipedia Page Views
  • +
  • 6.9 Bureau of Labor Statistics
  • +
  • 6.9.1 US Bureau of Labor Statistics (BLS)
  • 6.10 CoinGecko
  • 6.10.1 Crypto Market Cap
  • 6.11 EOD Historical Data
  • @@ -227,7 +228,6 @@

    Table of Content

  • 6.18.3 Insider Trading
  • 6.18.4 US Congress Trading
  • 6.18.5 US Government Contracts
  • -
  • 6.18.6 WallStreetBets
  • 6.19 RegAlytics
  • 6.19.1 US Regulatory Alerts - Financial Sector
  • 6.20 Securities and Exchange Commission
  • @@ -270,7 +270,8 @@

    Table of Content

  • 8.2.6 Range Consolidators
  • 8.2.6.1 Range Consolidators
  • 8.2.6.2 Classic Range Consolidators
  • -
  • 8.2.7 Combining Consolidators
  • +
  • 8.2.7 Market Hour Aware Consolidators
  • +
  • 8.2.8 Combining Consolidators
  • 8.3 Consolidator History
  • 8.4 Updating Indicators
  • 9 Historical Data
  • @@ -391,18 +392,19 @@

    Table of Content

  • 11.5.2.4 Tastytrade
  • 11.5.2.5 Alpaca
  • 11.5.2.6 Charles Schwab
  • -
  • 11.5.2.7 Binance
  • -
  • 11.5.2.8 Bybit
  • -
  • 11.5.2.9 Tradier
  • -
  • 11.5.2.10 Kraken
  • -
  • 11.5.2.11 Coinbase
  • -
  • 11.5.2.12 Bitfinex
  • -
  • 11.5.2.13 dYdX
  • -
  • 11.5.2.14 Terminal Link
  • -
  • 11.5.2.15 SSC Eze
  • -
  • 11.5.2.16 Trading Technologies
  • -
  • 11.5.2.17 Wolverine
  • -
  • 11.5.2.18 Oanda
  • +
  • 11.5.2.7 Webull
  • +
  • 11.5.2.8 Binance
  • +
  • 11.5.2.9 Bybit
  • +
  • 11.5.2.10 Tradier
  • +
  • 11.5.2.11 Kraken
  • +
  • 11.5.2.12 Coinbase
  • +
  • 11.5.2.13 Bitfinex
  • +
  • 11.5.2.14 dYdX
  • +
  • 11.5.2.15 Terminal Link
  • +
  • 11.5.2.16 SSC Eze
  • +
  • 11.5.2.17 Trading Technologies
  • +
  • 11.5.2.18 Wolverine
  • +
  • 11.5.2.19 Oanda
  • 11.6 Brokerage Message Handler
  • 11.7 Buying Power
  • 11.8 Settlement
  • @@ -567,10 +569,10 @@

    Getting Started

    border: 1px solid #D9E1EB; border-radius: 4px; margin-bottom: 1rem; + padding: 1rem; } .docs-tutorial .tutorial-step p { - padding: 0.5rem 1rem; - margin-bottom: 0; + margin-bottom: 0.5rem; } .docs-tutorial .tutorial-step .circle-icon { margin: 0 0.2rem; @@ -601,7 +603,7 @@


    - Follow these steps to create, backtest, and paper trade a new algorithm: + Follow these steps to write your first trading algorithm:

    @@ -625,44 +627,238 @@

    +
    +

    + 3. In the + + Initialize + + + initialize + + method, enable the following + + settings .

    +
    +
    Settings.AutomaticIndicatorWarmUp = true;
    +Settings.SeedInitialPrices = true;
    +
    self.settings.automatic_indicator_warm_up = True
    +self.settings.seed_initial_prices = True
    +

    - 3. Deploy the algorithm to live paper trading. + 4. In the + + Initialize + + + initialize + + method, add a + + Scheduled Event + + to rebalance the portfolio each week.

    +
    +
    Schedule.On(DateRules.WeekStart("SPY"), TimeRules.At(8, 0), Rebalance);
    +
    self.schedule.on(self.date_rules.week_start('SPY'), self.time_rules.at(8, 0), self._rebalance)
    +
    +
    + +
    +

    + 6. Add your trading rules and + + position sizing + + to the + + Rebalance + + + _rebalance + + method that runs each week. +

    +
    +
    private void Rebalance()
    +{
    +    // Get the stocks that are currently in the universe.
    +    var securities = _universe.Selected.Select(symbol => Securities[symbol]);
    +    // Select the 10 stocks with the greatest trailing returns.
    +    var selectedAssets = securities.Where(s => ((dynamic)s).Roc.IsReady).OrderBy(s => ((dynamic)s).Roc).TakeLast(10);
    +    // Form an equal-weighted portfolio.
    +    var targets = selectedAssets.Select(security => new PortfolioTarget(security.Symbol, 1m / selectedAssets.Count())).ToList();
    +    SetHoldings(targets, true);
    +}
    +
    def _rebalance(self):
    +    # Get the stocks that are currently in the universe.
    +    securities = [self.securities[symbol] for symbol in self._universe.selected]
    +    # Select the 10 stocks with the greatest trailing returns.
    +    selected_assets = sorted([s for s in securities if s.roc.is_ready], key=lambda s: s.roc)[-10:]
    +    # Form an equal-weighted portfolio.
    +    targets = [PortfolioTarget(security.symbol, 1/len(selected_assets)) for security in selected_assets]
    +    self.set_holdings(targets, True)
    +
    +
    +
    +

    + 7. Delete the + + + OnData + + + on_data + + + method that comes with the template algorithm.

    +

    + Congratulations! You just wrote your first trading algorithm. +

    +
    +
    public class BasicTemplateAlgorithm : QCAlgorithm
    +{
    +    private Universe _universe;
    +    public override void Initialize()
    +    {
    +        SetStartDate(2020, 1, 1);
    +        SetEndDate(2021, 1, 1);
    +        SetCash(100000);
    +        _universe = Universe.ETF("SPY");
    +        AddUniverse(_universe);
    +        Settings.AutomaticIndicatorWarmUp = true;
    +        Settings.SeedInitialPrices = true;
    +        Schedule.On(DateRules.WeekStart("SPY"), TimeRules.At(8, 0), Rebalance);
    +    }
    +
    +    public override void OnSecuritiesChanged(SecurityChanges changes)
    +    {
    +        foreach (dynamic security in changes.AddedSecurities)
    +        {
    +            security.Roc = ROC(security, 21, Resolution.Daily);
    +        }
    +    }
    +
    +    private void Rebalance()
    +    {
    +        // Get the stocks that are currently in the universe.
    +        var securities = _universe.Selected.Select(symbol => Securities[symbol]);
    +        // Select the 10 stocks with the greatest trailing returns.
    +        var selectedAssets = securities.Where(s => ((dynamic)s).Roc.IsReady).OrderBy(s => ((dynamic)s).Roc).TakeLast(10);
    +        // Form an equal-weighted portfolio.
    +        var targets = selectedAssets.Select(security => new PortfolioTarget(security.Symbol, 1m / selectedAssets.Count())).ToList();
    +        SetHoldings(targets, true);
    +    }
    +}
    +
    from AlgorithmImports import *
    +
    +
    +class BasicTemplateAlgorithm(QCAlgorithm):
    +
    +    def initialize(self):
    +        self.set_start_date(2020, 1, 1)
    +        self.set_end_date(2021, 1, 1)
    +        self.set_cash(100000)
    +        self._universe = self.universe.etf('SPY')
    +        self.add_universe(self._universe)
    +        self.settings.automatic_indicator_warm_up = True
    +        self.settings.seed_initial_prices = True
    +        self.schedule.on(self.date_rules.week_start('SPY'), self.time_rules.at(8, 0), self._rebalance)
    +
    +    def on_securities_changed(self, changes):
    +        for security in changes.added_securities:
    +            security.roc = self.roc(security, 21, Resolution.DAILY)
    +
    +    def _rebalance(self):
    +        # Get the stocks that are currently in the universe.
    +        securities = [self.securities[symbol] for symbol in self._universe.selected]
    +        # Select the 10 stocks with the greatest trailing returns.
    +        selected_assets = sorted([s for s in securities if s.roc.is_ready], key=lambda s: s.roc)[-10:]
    +        # Form an equal-weighted portfolio.
    +        targets = [PortfolioTarget(security.symbol, 1/len(selected_assets)) for security in selected_assets]
    +        self.set_holdings(targets, True)
    +
    +
    +
    @@ -2228,7 +2424,7 @@

    self.set_end_date(2024, 12, 31) # Request AAPL data to trade it. We need a resolution denser than 6-hour for the consolidator. - self.aapl = self.add_equity("AAPL", Resolution.Hour).symbol + self.aapl = self.add_equity("AAPL", Resolution.HOUR).symbol # Create a 6-hour consolidator for smoothing the noise. self.consolidator = TradeBarConsolidator(timedelta(hours=6)) @@ -2507,7 +2703,7 @@

    self.set_end_date(2024, 12, 31) # Request ES future data for trading. - self._future = self.add_future("ES", Resolution.Minute, + self._future = self.add_future("ES", Resolution.MINUTE, extended_market_hours=True, data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO, data_mapping_mode=DataMappingMode.OPEN_INTEREST, @@ -2529,7 +2725,7 @@

    # Cancel unfilled orders if they are not filled within 2 minutes to avoid stale fills. # Note that the Time property in the order ticket is in UTC timezone. - if self.ticket.status != OrderStatus.Filled and self.ticket.time + timedelta(minutes=2) < self.utc_time: + if self.ticket.status != OrderStatus.FILLED and self.ticket.time + timedelta(minutes=2) < self.utc_time: # You can change to a different time zone and compare the log differences. self.ticket.cancel(f"{self.time} :: utc: {self.utc_time} :: Order canceled due to placed over 2 minutes") @@ -3223,6 +3419,8 @@

    Markets

    + Market.CBOE +
    Market.EUREX
    Market.HKFE @@ -3232,6 +3430,8 @@

    Markets

    Market.USA
    + Market.CBOE +
    Market.EUREX
    Market.HKFE @@ -5495,7 +5695,7 @@

    self.set_holdings(self.spy, -1) # Schedule a switch 25 days later. self.last_scheduled_event = self.schedule.on( - self.date_rules.On(self.time + timedelta(25)), + self.date_rules.on(self.time + timedelta(25)), self.time_rules.at(9, 30), self.switch ) @@ -5510,7 +5710,7 @@

    # Schedule the next switch 25 days later. self.schedule.remove(self.last_scheduled_event) self.last_scheduled_event = self.schedule.on( - self.date_rules.On(self.time + timedelta(25)), + self.date_rules.on(self.time + timedelta(25)), self.time_rules.at(9, 30), self.switch )

    @@ -7337,6 +7537,224 @@

    Performance Chart

    +

    Memory Metrics

    + + +

    + The static + + OS + + class in the + + QuantConnect + + namespace exposes memory and CPU metrics that you can read from your algorithm to monitor resource usage during backtests and live trading. +

    +

    + The following table lists the static members of the + + OS + + class that report memory and CPU usage: +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Member + + Data Type + + Description +
    + + ApplicationMemoryUsed + + + APPLICATION_MEMORY_USED + + + + long + + + int + + + Private memory allocated to the current process, including managed and unmanaged memory, in megabytes. +
    + + TotalPhysicalMemoryUsed + + + TOTAL_PHYSICAL_MEMORY_USED + + + + long + + + int + + + Managed-runtime RAM usage (sampled with + + GC.GetTotalMemory + + + gc.get_total_memory + + ), in megabytes. LEAN's memory monitor samples this value. +
    + + CpuUsage + + + CPU_USAGE + + + + decimal + + + float + + + Total CPU usage as a percentage, sampled on a background thread. +
    + + GetServerStatistics() + + + get_server_statistics() + + + + Dictionary<string, string> + + + Dictionary[str, str] + + + Snapshot dictionary with + + CPU Usage + + , + + Used RAM + + , + + Total RAM + + , + + Hostname + + , and + + LEAN Version + + entries. +
    +

    + The following examples show how to read these metrics from an algorithm: +

    +
    +
    // Log the current memory usage of the algorithm process.
    +Log($"Total physical memory used: {OS.TotalPhysicalMemoryUsed} MB");
    +Log($"Application memory used: {OS.ApplicationMemoryUsed} MB");
    +
    +// Log the current CPU usage as a percentage.
    +Log($"CPU usage: {OS.CpuUsage}%");
    +
    +// Log a full snapshot of the server statistics.
    +foreach (var kvp in OS.GetServerStatistics()) Log($"{kvp.Key}: {kvp.Value}");
    +
    # Log the current memory usage of the algorithm process.
    +self.log(f"Total physical memory used: {OS.TOTAL_PHYSICAL_MEMORY_USED} MB")
    +self.log(f"Application memory used: {OS.APPLICATION_MEMORY_USED} MB")
    +
    +# Log the current CPU usage as a percentage.
    +self.log(f"CPU usage: {OS.CPU_USAGE}%")
    +
    +# Log a full snapshot of the server statistics.
    +for kvp in OS.get_server_statistics():
    +    self.log(f"{kvp.key}: {kvp.value}")
    +
    +

    + LEAN monitors the process's RAM usage during backtests and live trading so it can stop the algorithm gracefully when it approaches the node's memory limit, instead of letting the runtime crash. + To avoid killing an algorithm over a short-lived spike, LEAN does not act on the raw reading — it smooths the sampled value first and checks the smoothed value against the limit. +

    +

    + LEAN samples + + OS.TotalPhysicalMemoryUsed + + + OS.TOTAL_PHYSICAL_MEMORY_USED + + once per minute and feeds each sample into an exponential moving average. + The engine sets + + emaPeriod = 60 + + , which gives you roughly a 1-hour rolling window. +

    +
    +
    // Update the smoothed memory reading each minute.
    +sample = OS.TotalPhysicalMemoryUsed;
    +memoryUsed = Convert.ToInt64((emaPeriod-1)/emaPeriod * memoryUsed + (1/emaPeriod)*sample);
    +
    # Update the smoothed memory reading each minute.
    +sample = OS.TOTAL_PHYSICAL_MEMORY_USED
    +memory_used = int((ema_period - 1) / ema_period * memory_used + (1 / ema_period) * sample)
    +
    +

    + If the smoothed value exceeds the node's memory limit, LEAN terminates the algorithm with the runtime message + + Execution Security Error: Memory Usage Maxed Out - {memoryCap}MB max, with last sample of {lastSample}MB. + + , where + + {memoryCap} + + is the node's configured limit and + + {lastSample} + + is the most recent reading. +

    + + +

    JIT Compilation

    @@ -8457,32 +8875,32 @@

    Set Account Currency

    set_account_currency - method. By default, the account currency is USD and your starting cash is $100,000. If you call the + method. By default, the account currency is USD and your starting cash is $100,000. You must call the SetAccountCurrency set_account_currency - method, you must call it before you call the + method before you + + add data + + , and we recommend calling it before the SetCash set_cash - method or - - add data - - . If you call the + method. If you call the SetAccountCurrency set_account_currency - method more than once, only the first call takes effect. + method more than once with different currencies, only the first call takes effect.

    // Set the account currency and your starting cash.
    @@ -8495,6 +8913,99 @@ 

    Set Account Currency

    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.
    +

    + If you call + + SetAccountCurrency + + + set_account_currency + + after + + SetCash + + + set_cash + + , one of the following applies: +

    +
      +
    • + If you previously called + + SetCash(amount) + + + set_cash(amount) + + and the new account currency differs, the amount carries over to the new account currency. +
    • +
    • + If you previously called + + SetCash(symbol, amount) + + + set_cash(symbol, amount) + + and the new account currency differs, the previous balance stays in its own + + CashBook + + entry and the new account currency starts at zero (or at + + startingCash + + + starting_cash + + if you provide it). +
    • +
    • + If the new account currency matches the previous one and you provide + + startingCash + + + starting_cash + + , the new amount overrides the previous one. +
    • +
    +
    +
    // SetCash(amount) then a different account currency: the amount carries over to BTC.
    +SetCash(1);
    +SetAccountCurrency("BTC"); // CashBook: BTC = 1.
    +
    +// SetCash(symbol, amount) then a different account currency: USD balance is preserved, BTC starts at 0.
    +SetCash("USD", 100000);
    +SetAccountCurrency("BTC"); // CashBook: USD = 100000, BTC = 0.
    +
    +// SetCash(symbol, amount) then a different account currency with startingCash: USD is preserved, EUR starts at 50000.
    +SetCash("USD", 100000);
    +SetAccountCurrency("EUR", 50000); // CashBook: USD = 100000, EUR = 50000.
    +
    +// Same account currency with startingCash: the new amount overrides the previous one.
    +SetCash(100000);
    +SetAccountCurrency("USD", 200000); // CashBook: USD = 200000.
    +
    +
    # set_cash(amount) then a different account currency: the amount carries over to BTC.
    +self.set_cash(1)
    +self.set_account_currency("BTC") # CashBook: BTC = 1.
    +
    +# set_cash(symbol, amount) then a different account currency: USD balance is preserved, BTC starts at 0.
    +self.set_cash("USD", 100000)
    +self.set_account_currency("BTC") # CashBook: USD = 100000, BTC = 0.
    +
    +# set_cash(symbol, amount) then a different account currency with starting_cash: USD is preserved, EUR starts at 50000.
    +self.set_cash("USD", 100000)
    +self.set_account_currency("EUR", 50000) # CashBook: USD = 100000, EUR = 50000.
    +
    +# Same account currency with starting_cash: the new amount overrides the previous one.
    +self.set_cash(100000)
    +self.set_account_currency("USD", 200000) # CashBook: USD = 200000.
    +
    @@ -8896,9 +9407,12 @@

    Default Value: - + DataMappingMode.OpenInterest + + DataMappingMode.OPEN_INTEREST +

    @@ -8911,7 +9425,7 @@

    DataNormalizationMode - + data_normalization_mode @@ -13780,14 +14294,26 @@

    Resolutions

    + + - - green check + + + ✓ - - green check + + + ✓ @@ -13799,15 +14325,27 @@

    Resolutions

    SECOND
    - - green check + + + ✓ - - green check + + + ✓ + + @@ -13819,15 +14357,27 @@

    Resolutions

    MINUTE
    - - green check + + + ✓ - - green check + + + ✓ + + @@ -13839,14 +14389,26 @@

    Resolutions

    HOUR
    - - green check + + + ✓ + + + @@ -13858,28 +14420,48 @@

    Resolutions

    DAILY - - green check + + + ✓ + + +

    @@ -15901,9 +16483,12 @@

    Delistings

    objects when a delisting is in the near future and when it occurs. To know if the delisting occurs in the near future or now, check the - + Type + + type + property.

    @@ -16019,7 +16604,7 @@

    Delistings

    } }
    def on_data(self, slice: Slice) -> None:
    -    for symbol, delisting in slice.Delistings.items():
    +    for symbol, delisting in slice.delistings.items():
             pass
     
     def on_delistings(self, delistings: Delistings) -> None:
    @@ -17784,17 +18369,17 @@ 

    # Create a list of opening auction tick data. trades = [ tick for tick in self._asset.cache.get_all[Tick]() - if tick.TickType == TickType.TRADE and - tick.Price > 0 and - tick.SaleCondition and + if tick.tick_type == TickType.TRADE and + tick.price > 0 and + tick.sale_condition and ( - tick.ParsedSaleCondition == self.officialOpen or - tick.ParsedSaleCondition == self.openingPrints + tick.parsed_sale_condition == self.officialOpen or + tick.parsed_sale_condition == self.openingPrints ) ] # Log the opening auction tick price for tick in trades: - self.log(f"{self.Time},{tick.Price},{tick.Quantity},{tick.ParsedSaleCondition},{tick.ExchangeCode},{tick.Exchange}")

    + self.log(f"{self.time},{tick.price},{tick.quantity},{tick.parsed_sale_condition},{tick.exchange_code},{tick.exchange}")
    @@ -22903,6 +23488,422 @@

    # The arbitary delta criterion might be set due to hedging need or risk adjustment. selected = sorted(chain, key=lambda x: abs(x.greeks.delta - 0.4))[0] +

    + Example 4: Delta-Hedged Short Straddle +

    +

    + This example demonstrates a delta-hedged + + short straddle + + strategy that sells an at-the-money (ATM) straddle with 7-30 days to expiration and continuously neutralizes the portfolio delta using the underlying shares. + The + + StraddleSelector + + helper class picks the farthest expiry within the DTE range and the strike closest to the spot price. + On each + + slice + + , the combined Option delta is accumulated from the + + OptionChain + + and the net portfolio delta is computed by adding the underlying position. + The + + DeltaHedger + + helper enforces a minimum share threshold and a rehedge band to avoid over-trading, and applies a time delay between successive hedges. + A + + Scheduled Event + + closes the straddle on expiration day and assignment events are handled to keep the underlying position clean. +

    +
    +
    public class DeltaHedgedStraddleAlgorithm : QCAlgorithm
    +{
    +    private Equity _spy;
    +    private Symbol _canonicalOption;
    +    private OptionStrategy _shortStraddle;
    +    private StraddleSelector _straddleSelector;
    +    private DeltaHedger _hedger;
    +    private decimal _straddleWeight = 0.5m, _delta = 0m;
    +    private DateTime? _exitDay;
    +
    +    public override void Initialize()
    +    {
    +        SetStartDate(2024, 9, 1);
    +        SetEndDate(2024, 12, 31);
    +        SetCash(1_000_000);
    +        Settings.SeedInitialPrices = true;
    +        _spy = AddEquity("SPY", dataNormalizationMode: DataNormalizationMode.Raw);
    +        // Initialize the straddle selector with 7-30 day DTE range.
    +        _straddleSelector = new StraddleSelector(minDte: 7, maxDte: 30);
    +        // Initialize the delta hedger with 30 share minimum, 20 share rehedge band, and 4 hour delay.
    +        _hedger = new DeltaHedger(30, 20, TimeSpan.FromHours(4));
    +        _canonicalOption = QuantConnect.Symbol.CreateCanonicalOption(_spy.Symbol);
    +        // Schedule the rebalance method to run 30 minutes after market open.
    +        Schedule.On(
    +            DateRules.EveryDay(_spy.Symbol),
    +            TimeRules.AfterMarketOpen(_spy.Symbol, 30),
    +            Rebalance
    +        );
    +        // Schedule the close expiring method to run 60 minutes before market close.
    +        Schedule.On(
    +            DateRules.EveryDay(_spy.Symbol),
    +            TimeRules.BeforeMarketClose(_spy.Symbol, 60),
    +            CloseExpiring
    +        );
    +        SetWarmUp(TimeSpan.FromDays(45));
    +    }
    +
    +    public override void OnData(Slice slice)
    +    {
    +        if (IsWarmingUp || !slice.Bars.Any()) return;
    +        if (!slice.OptionChains.TryGetValue(_canonicalOption, out var chain) || _shortStraddle == null) return;
    +
    +        // Accumulate the delta from all option legs in the straddle.
    +        foreach (var leg in _shortStraddle.OptionLegs)
    +        {
    +            if (chain.Contracts.TryGetValue(leg.Symbol, out var contract))
    +            {
    +                _delta += contract.Greeks.Delta;
    +            }
    +        }
    +        // Calculate the net portfolio delta including underlying and options.
    +        var netDelta = _hedger.ComputeNetDelta(this, _shortStraddle.OptionLegs, _spy.Holdings.Quantity, _delta);
    +        // Reset the delta accumulator for the next iteration.
    +        _delta = 0m;
    +        // Exit if it's not time to hedge or net delta is zero.
    +        if (!(_hedger.ShouldHedge(Time) && netDelta != 0)) return;
    +        // Calculate the hedge quantity needed to neutralize delta.
    +        var hedgeQty = _hedger.GetQuantity(netDelta);
    +        if (hedgeQty.HasValue)
    +        {
    +            MarketOrder(_spy.Symbol, hedgeQty.Value, tag: $"Daily delta hedge ({netDelta:F2} share delta)");
    +            // Record the hedge operation with timestamp and delta.
    +            _hedger.RecordHedge(Time, netDelta);
    +            // Update the net delta after the hedge.
    +            netDelta += hedgeQty.Value;
    +        }
    +        Plot("Portfolio Delta", "Delta", netDelta);
    +    }
    +
    +    private void Rebalance()
    +    {
    +        if (IsWarmingUp || Portfolio.Invested) return;
    +        var chain = OptionChain(_spy.Symbol);
    +        if (!chain.Any()) return;
    +        // Use the straddle selector to find the best ATM straddle.
    +        var result = _straddleSelector.Select(chain, _spy.Price, Time.Date);
    +        if (result == null) return;
    +        _delta = result.Value.Delta;
    +        _shortStraddle = OptionStrategies.ShortStraddle(_canonicalOption, result.Value.Strike, result.Value.Expiry);
    +        // Calculate the number of contracts based on portfolio value and weight.
    +        var qty = (int)(Portfolio.TotalPortfolioValue * _straddleWeight / (_spy.Price * 100m));
    +        if (qty > 0)
    +            Buy(_shortStraddle, qty, tag: $"Sell ATM straddle exp {result.Value.Expiry:d}");
    +        _hedger.Reset();
    +        _exitDay = result.Value.Expiry.Date;
    +    }
    +
    +    private void CloseExpiring()
    +    {
    +        if (_shortStraddle == null || !_exitDay.HasValue || Time.Date < _exitDay.Value) return;
    +        // Liquidate all positions before expiration.
    +        Liquidate(tag: "Expiry hedge liquidation");
    +    }
    +
    +    public override void OnOrderEvent(OrderEvent orderEvent)
    +    {
    +        // Handle option assignment events.
    +        if (orderEvent.Status == OrderStatus.Filled && orderEvent.IsAssignment)
    +        {
    +            // Liquidate all option legs of the straddle.
    +            foreach (var leg in _shortStraddle.OptionLegs)
    +            {
    +                Liquidate(leg.Symbol, tag: "Assignment liquidation");
    +            }
    +            // Determine the direction based on whether it's a call or put assignment.
    +            var direction = Securities[orderEvent.Symbol].Symbol.ID.OptionRight == OptionRight.Call ? 1 : -1;
    +            MarketOrder(_spy.Symbol, -_spy.Holdings.Quantity + orderEvent.FillQuantity * direction * 100);
    +            _shortStraddle = null;
    +        }
    +    }
    +}
    +
    +public class StraddleSelector
    +{
    +    private readonly int _minDte, _maxDte;
    +
    +    public StraddleSelector(int minDte, int maxDte)
    +    {
    +        // Store the minimum days to expiration for contract selection.
    +        _minDte = minDte;
    +        // Store the maximum days to expiration for contract selection.
    +        _maxDte = maxDte;
    +    }
    +
    +    public (decimal Delta, DateTime Expiry, decimal Strike)? Select(IEnumerable<OptionContract> chain, decimal spotPrice, DateTime currentDate)
    +    {
    +        // Filter contracts to those within the specified DTE range.
    +        var contracts = chain.Where(c =>
    +        {
    +            var dte = (c.Expiry.Date - currentDate).Days;
    +            return dte >= _minDte && dte <= _maxDte;
    +        }).ToList();
    +        if (!contracts.Any()) return null;
    +        // Select the farthest expiration date from the filtered contracts.
    +        var expiry = contracts.Max(c => c.Expiry);
    +        // Filter to only contracts with the selected expiration.
    +        contracts = contracts.Where(c => c.Expiry == expiry).ToList();
    +        // Find the strike price closest to the current spot price.
    +        var strike = contracts.OrderBy(c => Math.Abs(c.Strike - spotPrice)).First().Strike;
    +        // Filter to only contracts with the selected strike.
    +        contracts = contracts.Where(c => c.Strike == strike).ToList();
    +        // Ensure we have both call and put for a straddle.
    +        if (contracts.Count < 2) return null;
    +        // Return the combined delta, expiry, and strike for the straddle.
    +        return (contracts.Sum(c => c.Greeks.Delta), expiry, strike);
    +    }
    +}
    +
    +public class DeltaHedger
    +{
    +    private readonly int _minShares, _rehedgeBand;
    +    private readonly TimeSpan _hedgeDelay;
    +    private DateTime _nextHedgeTime;
    +    private decimal? _lastHedgedDelta;
    +
    +    public DeltaHedger(int minShares, int rehedgeBand, TimeSpan hedgeDelay)
    +    {
    +        // Store the minimum number of shares required to execute a hedge.
    +        _minShares = minShares;
    +        // Store the rehedge band threshold to prevent excessive hedging.
    +        _rehedgeBand = rehedgeBand;
    +        // Store the time delay between hedging operations.
    +        _hedgeDelay = hedgeDelay;
    +        Reset();
    +    }
    +
    +    public void Reset()
    +    {
    +        _nextHedgeTime = DateTime.MinValue;
    +        _lastHedgedDelta = null;
    +    }
    +
    +    public bool ShouldHedge(DateTime currentTime)
    +    {
    +        // Check if enough time has passed since the last hedge.
    +        return currentTime >= _nextHedgeTime;
    +    }
    +
    +    public decimal ComputeNetDelta(QCAlgorithm algorithm, IEnumerable<Leg> legs, decimal underlyingQuantity, decimal contractDelta)
    +    {
    +        // Calculate the net portfolio delta by combining underlying and option positions.
    +        return underlyingQuantity + legs.Sum(leg =>
    +            algorithm.Securities[leg.Symbol].Holdings.Quantity * contractDelta * 100);
    +    }
    +
    +    public int? GetQuantity(decimal netDelta)
    +    {
    +        if (_lastHedgedDelta.HasValue
    +            && Math.Abs(netDelta - _lastHedgedDelta.Value) < _rehedgeBand
    +            && Math.Abs(netDelta) < _minShares + _rehedgeBand)
    +        {
    +            return null;
    +        }
    +        // Calculate the required hedge quantity to neutralize the net delta.
    +        var hedgeQty = (int)Math.Round(-netDelta);
    +        if (Math.Abs(hedgeQty) < _minShares) return null;
    +        // Return the calculated hedge quantity.
    +        return hedgeQty;
    +    }
    +
    +    public void RecordHedge(DateTime currentTime, decimal netDelta)
    +    {
    +        // Record the delta value after this hedge operation.
    +        _lastHedgedDelta = netDelta;
    +        // Set the next allowed hedge time based on the configured delay.
    +        _nextHedgeTime = currentTime + _hedgeDelay;
    +    }
    +}
    +
    class DeltaHedgedStraddleAlgo(QCAlgorithm):
    +
    +    def initialize(self):
    +        self.set_start_date(2024, 9, 1)
    +        self.set_end_date(2024, 12, 31)
    +        self.set_cash(1_000_000)
    +        self.settings.seed_initial_prices = True
    +        self._spy = self.add_equity("SPY", data_normalization_mode=DataNormalizationMode.RAW)
    +        # Initialize the straddle selector with 7-30 day DTE range.
    +        self._straddle_selector = StraddleSelector(min_dte=7, max_dte=30)
    +        # Initialize the delta hedger with 30 share minimum, 20 share rehedge band, and 4 hour delay.
    +        self._hedger = DeltaHedger(30, 20, timedelta(hours=4))
    +        self._canonical_option = Symbol.create_canonical_option(self._spy)
    +        # Set the straddle position size to 50% of portfolio value.
    +        self._straddle_weight = 0.5
    +        self._short_straddle = None
    +        self._exit_day = None
    +        # Schedule the rebalance method to run 30 minutes after market open.
    +        self.schedule.on(
    +            self.date_rules.every_day(self._spy),
    +            self.time_rules.after_market_open(self._spy, 30),
    +            self._rebalance
    +        )
    +        # Schedule the close expiring method to run 60 minutes before market close.
    +        self.schedule.on(
    +            self.date_rules.every_day(self._spy),
    +            self.time_rules.before_market_close(self._spy, 60),
    +            self._close_expiring
    +        )
    +        self.set_warm_up(timedelta(45))
    +
    +    def on_data(self, data):
    +        if self.is_warming_up or not data.bars:
    +            return
    +        chain = data.option_chains.get(self._canonical_option)
    +        if not chain or not self._short_straddle:
    +            return
    +        # Accumulate the delta from all option legs in the straddle.
    +        for leg in self._short_straddle.option_legs:
    +            contract = chain.contracts.get(leg.symbol)
    +            if contract:
    +                self._delta += contract.greeks.delta
    +        # Calculate the net portfolio delta including underlying and options.
    +        net_delta = self._hedger.compute_net_delta(self, self._short_straddle.option_legs, self._spy.holdings.quantity, self._delta)
    +        # Reset the delta accumulator for the next iteration.
    +        self._delta = 0
    +        # Exit if it's not time to hedge or net delta is zero.
    +        if not (self._hedger.should_hedge(self.time) and net_delta):
    +            return
    +        # Calculate the hedge quantity needed to neutralize delta.
    +        hedge_qty = self._hedger.get_quantity(net_delta)
    +        if hedge_qty:
    +            self.market_order(self._spy, hedge_qty, tag=f"Daily delta hedge ({net_delta:.2f} share delta)")
    +            # Record the hedge operation with timestamp and delta.
    +            self._hedger.record_hedge(self.time, net_delta)
    +            # Update the net delta after the hedge.
    +            net_delta += hedge_qty
    +        self.plot("Portfolio Delta", "Delta", net_delta)
    +
    +    def _rebalance(self):
    +        if self.is_warming_up or self.portfolio.invested:
    +            return
    +        chain = self.option_chain(self._spy)
    +        if not chain:
    +            return
    +        # Use the straddle selector to find the best ATM straddle.
    +        result = self._straddle_selector.select(chain, self._spy.price, self.time.date())
    +        if not result:
    +            return
    +        self._delta, expiry, strike = result
    +        self._short_straddle = OptionStrategies.short_straddle(self._canonical_option, strike, expiry)
    +        # Calculate the number of contracts based on portfolio value and weight.
    +        qty = int(self.portfolio.total_portfolio_value * self._straddle_weight / (self._spy.price * 100.0))
    +        if qty:
    +            self.buy(self._short_straddle, qty, tag=f"Sell ATM straddle exp {expiry.date()}")
    +        self._hedger.reset()
    +        self._exit_day = expiry.date()
    +
    +    def _close_expiring(self):
    +        if not self._short_straddle or not self._exit_day or self.time.date() < self._exit_day:
    +            return
    +        # Liquidate all positions before expiration.
    +        self.liquidate(tag="Expiry hedge liquidation")
    +
    +    def on_order_event(self, order_event):
    +        # Handle option assignment events.
    +        if order_event.status == OrderStatus.FILLED and order_event.is_assignment:
    +            # Liquidate all option legs of the straddle.
    +            for leg in self._short_straddle.option_legs:
    +                self.liquidate(leg.symbol, tag="Assignment liquidation")
    +            # Determine the direction based on whether it's a call or put assignment.
    +            direction = 1 if self.securities[order_event.symbol].right == OptionRight.CALL else -1
    +            self.market_order(self._spy, -self._spy.holdings.quantity + order_event.quantity * direction * 100)
    +            self._short_straddle = None
    +
    +
    +class StraddleSelector:
    +
    +    def __init__(self, min_dte, max_dte):
    +        # Store the minimum days to expiration for contract selection.
    +        self._min_dte = min_dte
    +        # Store the maximum days to expiration for contract selection.
    +        self._max_dte = max_dte
    +
    +    def select(self, chain, spot_price, current_date):
    +        # Filter contracts to those within the specified DTE range.
    +        contracts = [
    +            contract for contract in chain
    +            if self._min_dte <= (contract.expiry.date() - current_date).days <= self._max_dte
    +        ]
    +        if not contracts:
    +            return
    +        # Select the farthest expiration date from the filtered contracts.
    +        expiry = max(contract.expiry for contract in contracts)
    +        # Filter to only contracts with the selected expiration.
    +        contracts = [contract for contract in contracts if contract.expiry == expiry]
    +        # Find the strike price closest to the current spot price.
    +        strike = min(contracts, key=lambda c: abs(c.strike - spot_price)).strike
    +        # Filter to only contracts with the selected strike.
    +        contracts = [contract for contract in contracts if contract.strike == strike]
    +        # Ensure we have both call and put for a straddle.
    +        if len(contracts) < 2:
    +            return
    +        # Return the combined delta, expiry, and strike for the straddle.
    +        return sum(contract.greeks.delta for contract in contracts), expiry, strike
    +
    +
    +class DeltaHedger:
    +
    +    def __init__(self, min_shares, rehedge_band, hedge_delay):
    +        # Store the minimum number of shares required to execute a hedge.
    +        self._min_shares = min_shares
    +        # Store the rehedge band threshold to prevent excessive hedging.
    +        self._rehedge_band = rehedge_band
    +        # Store the time delay between hedging operations.
    +        self._hedge_delay = hedge_delay
    +        self.reset()
    +
    +    def reset(self):
    +        self._next_hedge_time = datetime.min
    +        self._last_hedged_delta = None
    +
    +    def should_hedge(self, current_time):
    +        # Check if enough time has passed since the last hedge.
    +        return current_time >= self._next_hedge_time
    +
    +    def compute_net_delta(self, algorithm, legs, underlying_quantity, contract_delta):
    +        # Calculate the net portfolio delta by combining underlying and option positions.
    +        return underlying_quantity + sum(
    +            algorithm.securities[leg.symbol].holdings.quantity * contract_delta * 100
    +            for leg in legs
    +        )
    +
    +    def get_quantity(self, net_delta):
    +        if (
    +            self._last_hedged_delta is not None
    +            and abs(net_delta - self._last_hedged_delta) < self._rehedge_band
    +            and abs(net_delta) < self._min_shares + self._rehedge_band
    +        ):
    +            return
    +        # Calculate the required hedge quantity to neutralize the net delta.
    +        hedge_qty = int(round(-net_delta))
    +        if abs(hedge_qty) < self._min_shares:
    +            return
    +        # Return the calculated hedge quantity.
    +        return hedge_qty
    +
    +    def record_hedge(self, current_time, net_delta):
    +        # Record the delta value after this hedge operation.
    +        self._last_hedged_delta = net_delta
    +        # Set the next allowed hedge time based on the configured delay.
    +        self._next_hedge_time = current_time + self._hedge_delay
    +
    @@ -23928,7 +24929,7 @@

    indicator = self.securities[option].ImpliedVolatility mirror = Symbol.create_option( underlying.value, option.id.market, option.id.option_style, - OptionRight.Call if option.id.option_right == OptionRight.PUT else OptionRight.PUT, + OptionRight.CALL if option.id.option_right == OptionRight.PUT else OptionRight.PUT, option.id.strike_price, option.id.date ) # Check if price data is available for both contracts and the underlying asset. @@ -24607,7 +25608,7 @@

    indicator = self.securities[option].Delta mirror = Symbol.create_option( underlying.value, option.id.market, option.id.option_style, - OptionRight.Call if option.id.option_right == OptionRight.PUT else OptionRight.PUT, + OptionRight.CALL if option.id.option_right == OptionRight.PUT else OptionRight.PUT, option.id.strike_price, option.id.date ) # Check if price data is available for both contracts and the underlying asset. @@ -25185,7 +26186,7 @@

    indicator = self.securities[option].Gamma mirror = Symbol.create_option( underlying.value, option.id.market, option.id.option_style, - OptionRight.Call if option.id.option_right == OptionRight.PUT else OptionRight.PUT, + OptionRight.CALL if option.id.option_right == OptionRight.PUT else OptionRight.PUT, option.id.strike_price, option.id.date ) # Check if price data is available for both contracts and the underlying asset. @@ -25762,7 +26763,7 @@

    indicator = self.securities[option].Vega mirror = Symbol.create_option( underlying.value, option.id.market, option.id.option_style, - OptionRight.Call if option.id.option_right == OptionRight.PUT else OptionRight.PUT, + OptionRight.CALL if option.id.option_right == OptionRight.PUT else OptionRight.PUT, option.id.strike_price, option.id.date ) # Check if price data is available for both contracts and the underlying asset. @@ -26340,7 +27341,7 @@

    indicator = self.securities[option].Theta mirror = Symbol.create_option( underlying.value, option.id.market, option.id.option_style, - OptionRight.Call if option.id.option_right == OptionRight.PUT else OptionRight.PUT, + OptionRight.CALL if option.id.option_right == OptionRight.PUT else OptionRight.PUT, option.id.strike_price, option.id.date ) # Check if price data is available for both contracts and the underlying asset. @@ -26923,7 +27924,7 @@

    indicator = self.securities[option].Rho mirror = Symbol.create_option( underlying.value, option.id.market, option.id.option_style, - OptionRight.Call if option.id.option_right == OptionRight.PUT else OptionRight.PUT, + OptionRight.CALL if option.id.option_right == OptionRight.PUT else OptionRight.PUT, option.id.strike_price, option.id.date ) # Check if price data is available for both contracts and the underlying asset. @@ -27905,8 +28906,8 @@

    Introduction

    on_data
    method. For more information about the specific datasets we use for backtests, see the - - CoinAPI datasets + + QuantConnect datasets . To trade Crypto live, you can use the @@ -27997,8 +28998,8 @@

    Create Subscriptions

    To view the supported assets, see the - - CoinAPI datasets + + QuantConnect datasets .

    @@ -29689,7 +30690,7 @@

    Create Subscriptions

    To view the supported assets in the Crypto Futures dataset, see - + Supported Assets . @@ -31331,7 +32332,7 @@

    Create Subscriptions

    To view the supported Forex pairs, see - + Supported Assets . @@ -32423,7 +33424,7 @@

    # Create a consolidator that produces 5-minute quote bars to reduce reduce noise. consolidator = QuoteBarConsolidator(timedelta(minutes=5)) # Define the consolidator handler so it updates the EMA with the consoildated bars. - consolidator.DataConsolidated += lambda _, bar: self._forex.ema.update(bar.end_time, bar.close) + consolidator.data_consolidated += lambda _, bar: self._forex.ema.update(bar.end_time, bar.close) # Warm up the indicator with historical data. for bar in self.history[QuoteBar](self._forex.symbol, 1000): consolidator.update(bar) @@ -86465,6 +87466,122 @@

    Time Zone

    ], "holidays": [] }, + "Future-cfe-VXM": { + "exchangeTimeZone": "America/Chicago", + "sunday": [ + { + "start": "17:00:00", + "end": "1.00:00:00", + "state": "premarket" + } + ], + "monday": [ + { + "start": "00:00:00", + "end": "08:30:00", + "state": "premarket" + }, + { + "start": "08:30:00", + "end": "15:00:00", + "state": "market" + }, + { + "start": "15:00:00", + "end": "16:00:00", + "state": "postmarket" + }, + { + "start": "17:00:00", + "end": "1.00:00:00", + "state": "postmarket" + } + ], + "tuesday": [ + { + "start": "00:00:00", + "end": "08:30:00", + "state": "premarket" + }, + { + "start": "08:30:00", + "end": "15:00:00", + "state": "market" + }, + { + "start": "15:00:00", + "end": "16:00:00", + "state": "postmarket" + }, + { + "start": "17:00:00", + "end": "1.00:00:00", + "state": "postmarket" + } + ], + "wednesday": [ + { + "start": "00:00:00", + "end": "08:30:00", + "state": "premarket" + }, + { + "start": "08:30:00", + "end": "15:00:00", + "state": "market" + }, + { + "start": "15:00:00", + "end": "16:00:00", + "state": "postmarket" + }, + { + "start": "17:00:00", + "end": "1.00:00:00", + "state": "postmarket" + } + ], + "thursday": [ + { + "start": "00:00:00", + "end": "08:30:00", + "state": "premarket" + }, + { + "start": "08:30:00", + "end": "15:00:00", + "state": "market" + }, + { + "start": "15:00:00", + "end": "16:00:00", + "state": "postmarket" + }, + { + "start": "17:00:00", + "end": "1.00:00:00", + "state": "postmarket" + } + ], + "friday": [ + { + "start": "00:00:00", + "end": "08:30:00", + "state": "premarket" + }, + { + "start": "08:30:00", + "end": "15:00:00", + "state": "market" + }, + { + "start": "15:00:00", + "end": "16:00:00", + "state": "postmarket" + } + ], + "holidays": [] + }, "Future-cme-E3G": { "exchangeTimeZone": "America/New_York", "sunday": [ @@ -92374,6 +93491,395 @@

    Time Zone

    ], "earlyCloses": {} }, + "Index-cboe-[*]": { + "exchangeTimeZone": "America/Chicago", + "monday": [ + { + "start": "08:30:00", + "end": "15:15:00", + "state": "market" + } + ], + "tuesday": [ + { + "start": "08:30:00", + "end": "15:15:00", + "state": "market" + } + ], + "wednesday": [ + { + "start": "08:30:00", + "end": "15:15:00", + "state": "market" + } + ], + "thursday": [ + { + "start": "08:30:00", + "end": "15:15:00", + "state": "market" + } + ], + "friday": [ + { + "start": "08:30:00", + "end": "15:15:00", + "state": "market" + } + ], + "holidays": [ + "1/1/1998", + "1/1/1999", + "1/1/2001", + "1/1/2002", + "1/1/2003", + "1/1/2004", + "1/2/2006", + "1/1/2007", + "1/1/2008", + "1/1/2009", + "1/1/2010", + "1/1/2011", + "1/2/2012", + "1/1/2013", + "1/1/2014", + "1/1/2015", + "1/1/2016", + "1/2/2017", + "1/1/2018", + "1/1/2019", + "1/1/2020", + "1/1/2021", + "1/1/2022", + "1/1/2024", + "1/1/2025", + "1/1/2026", + "1/1/2027", + "1/2/2023", + "1/2/2007", + "1/9/2025", + "1/19/1998", + "1/18/1999", + "1/17/2000", + "1/15/2001", + "1/21/2002", + "1/20/2003", + "1/19/2004", + "1/17/2005", + "1/16/2006", + "1/15/2007", + "1/21/2008", + "1/19/2009", + "1/18/2010", + "1/17/2011", + "1/16/2012", + "1/21/2013", + "1/20/2014", + "1/19/2015", + "1/18/2016", + "1/16/2017", + "1/15/2018", + "1/21/2019", + "1/20/2020", + "1/18/2021", + "1/17/2022", + "1/16/2023", + "1/15/2024", + "1/20/2025", + "1/19/2026", + "1/18/2027", + "2/16/1998", + "2/15/1999", + "2/21/2000", + "2/19/2001", + "2/18/2002", + "2/17/2003", + "2/16/2004", + "2/21/2005", + "2/20/2006", + "2/19/2007", + "2/18/2008", + "2/16/2009", + "2/15/2010", + "2/21/2011", + "2/20/2012", + "2/18/2013", + "2/17/2014", + "2/16/2015", + "2/15/2016", + "2/20/2017", + "2/19/2018", + "2/18/2019", + "2/17/2020", + "2/15/2021", + "2/21/2022", + "2/20/2023", + "2/19/2024", + "2/17/2025", + "2/16/2026", + "2/15/2027", + "4/10/1998", + "4/2/1999", + "4/21/2000", + "4/13/2001", + "3/29/2002", + "4/18/2003", + "4/9/2004", + "3/25/2005", + "4/14/2006", + "4/6/2007", + "3/21/2008", + "4/10/2009", + "4/2/2010", + "4/22/2011", + "4/6/2012", + "3/29/2013", + "4/18/2014", + "4/3/2015", + "3/25/2016", + "4/14/2017", + "3/30/2018", + "4/19/2019", + "4/10/2020", + "4/2/2021", + "4/15/2022", + "4/7/2023", + "3/29/2024", + "4/18/2025", + "4/3/2026", + "3/26/2027", + "5/25/1998", + "5/31/1999", + "5/29/2000", + "5/28/2001", + "5/27/2002", + "5/26/2003", + "5/31/2004", + "5/30/2005", + "5/29/2006", + "5/28/2007", + "5/26/2008", + "5/25/2009", + "5/31/2010", + "5/30/2011", + "5/28/2012", + "5/27/2013", + "5/26/2014", + "5/25/2015", + "5/30/2016", + "5/29/2017", + "5/28/2018", + "5/27/2019", + "5/25/2020", + "5/31/2021", + "5/30/2022", + "5/29/2023", + "5/27/2024", + "5/26/2025", + "5/25/2026", + "5/31/2027", + "6/11/2004", + "6/20/2022", + "6/19/2023", + "6/19/2024", + "6/19/2025", + "6/19/2026", + "6/18/2027", + "7/3/1998", + "7/5/1999", + "7/4/2000", + "7/4/2001", + "7/4/2002", + "7/4/2003", + "7/5/2004", + "7/4/2005", + "7/4/2006", + "7/4/2007", + "7/4/2008", + "7/3/2009", + "7/5/2010", + "7/4/2011", + "7/4/2012", + "7/4/2013", + "7/4/2014", + "7/3/2015", + "7/4/2016", + "7/4/2017", + "7/4/2018", + "7/4/2019", + "7/3/2020", + "7/5/2021", + "7/4/2022", + "7/4/2023", + "7/4/2024", + "7/4/2025", + "7/3/2026", + "7/5/2027", + "9/7/1998", + "9/6/1999", + "9/4/2000", + "9/3/2001", + "9/2/2002", + "9/1/2003", + "9/6/2004", + "9/5/2005", + "9/4/2006", + "9/3/2007", + "9/1/2008", + "9/7/2009", + "9/6/2010", + "9/5/2011", + "9/3/2012", + "9/2/2013", + "9/1/2014", + "9/7/2015", + "9/5/2016", + "9/4/2017", + "9/3/2018", + "9/2/2019", + "9/7/2020", + "9/6/2021", + "9/5/2022", + "9/4/2023", + "9/2/2024", + "9/1/2025", + "9/7/2026", + "9/6/2027", + "9/11/2001", + "9/12/2001", + "9/13/2001", + "9/14/2001", + "10/29/2012", + "10/30/2012", + "11/26/1998", + "11/25/1999", + "11/23/2000", + "11/22/2001", + "11/28/2002", + "11/27/2003", + "11/25/2004", + "11/24/2005", + "11/23/2006", + "11/22/2007", + "11/27/2008", + "11/26/2009", + "11/25/2010", + "11/24/2011", + "11/22/2012", + "11/28/2013", + "11/27/2014", + "11/26/2015", + "11/24/2016", + "11/23/2017", + "11/22/2018", + "11/28/2019", + "11/26/2020", + "11/25/2021", + "11/24/2022", + "11/23/2023", + "11/28/2024", + "11/27/2025", + "11/26/2026", + "11/25/2027", + "12/05/2018", + "12/25/1998", + "12/24/1999", + "12/25/2000", + "12/25/2001", + "12/25/2002", + "12/25/2003", + "12/24/2004", + "12/26/2005", + "12/25/2006", + "12/25/2007", + "12/25/2008", + "12/25/2009", + "12/24/2010", + "12/26/2011", + "12/25/2012", + "12/25/2013", + "12/25/2014", + "12/25/2015", + "12/26/2016", + "12/25/2017", + "12/25/2018", + "12/25/2019", + "12/25/2020", + "12/24/2021", + "12/26/2022", + "12/25/2023", + "12/25/2024", + "12/25/2025", + "12/25/2026", + "12/24/2027" + ], + "earlyCloses": { + "7/3/2000": "12:00:00", + "7/3/2001": "12:00:00", + "7/5/2002": "12:00:00", + "7/3/2003": "12:00:00", + "7/3/2006": "12:00:00", + "7/3/2007": "12:00:00", + "7/3/2008": "12:00:00", + "7/3/2012": "12:00:00", + "7/3/2013": "12:00:00", + "7/3/2014": "12:00:00", + "7/3/2017": "12:00:00", + "7/3/2018": "12:00:00", + "7/3/2019": "12:00:00", + "7/3/2023": "12:00:00", + "7/3/2024": "12:00:00", + "7/3/2025": "12:00:00", + "11/26/1999": "12:00:00", + "11/24/2000": "12:00:00", + "11/23/2001": "12:00:00", + "11/29/2002": "12:00:00", + "11/28/2003": "12:00:00", + "11/26/2004": "12:00:00", + "11/25/2005": "12:00:00", + "11/24/2006": "12:00:00", + "11/23/2007": "12:00:00", + "11/28/2008": "12:00:00", + "11/27/2009": "12:00:00", + "11/26/2010": "12:00:00", + "11/25/2011": "12:00:00", + "11/23/2012": "12:00:00", + "11/29/2013": "12:00:00", + "11/28/2014": "12:00:00", + "11/27/2015": "12:00:00", + "11/25/2016": "12:00:00", + "11/24/2017": "12:00:00", + "11/23/2018": "12:00:00", + "11/29/2019": "12:00:00", + "11/27/2020": "12:00:00", + "11/26/2021": "12:00:00", + "11/25/2022": "12:00:00", + "11/24/2023": "12:00:00", + "11/29/2024": "12:00:00", + "11/28/2025": "12:00:00", + "11/27/2026": "12:00:00", + "11/26/2027": "12:00:00", + "12/24/2001": "12:00:00", + "12/24/2002": "12:00:00", + "12/24/2003": "12:00:00", + "12/26/2003": "12:00:00", + "12/24/2007": "12:00:00", + "12/24/2008": "12:00:00", + "12/24/2009": "12:00:00", + "12/24/2012": "12:00:00", + "12/24/2013": "12:00:00", + "12/24/2014": "12:00:00", + "12/24/2015": "12:00:00", + "12/24/2017": "12:00:00", + "12/24/2018": "12:00:00", + "12/24/2019": "12:00:00", + "12/24/2020": "12:00:00", + "12/24/2024": "12:00:00", + "12/24/2025": "12:00:00", + "12/24/2026": "12:00:00" + } + }, "Future-cme-MES": { "exchangeTimeZone": "America/New_York", "sunday": [ @@ -106015,6 +107521,7 @@

    Time Zone

    "RTY": "E-mini Russell 2000 Index Futures", "YM": "E-mini Dow ($5) Futures", "VX": "VIX futures", + "VXM": "VIX Mini Futures", "E3G": "E-mini FTSE 100 GBP Futures", "ENY": "E-mini Nikkei YEN Futures", "NIY": "Nikkei/YEN Futures", @@ -110288,7 +111795,7 @@

    indicator = self.securities[option].ImpliedVolatility mirror = Symbol.create_option( underlying.value, option.id.market, option.id.option_style, - OptionRight.Call if option.id.option_right == OptionRight.PUT else OptionRight.PUT, + OptionRight.CALL if option.id.option_right == OptionRight.PUT else OptionRight.PUT, option.id.strike_price, option.id.date ).value # Check if price data is available for both contracts and the underlying asset. @@ -110987,7 +112494,7 @@

    indicator = self.securities[option].Delta mirror = Symbol.create_option( underlying.value, option.id.market, option.id.option_style, - OptionRight.Call if option.id.option_right == OptionRight.PUT else OptionRight.PUT, + OptionRight.CALL if option.id.option_right == OptionRight.PUT else OptionRight.PUT, option.id.strike_price, option.id.date ).value # Check if price data is available for both contracts and the underlying asset. @@ -111585,7 +113092,7 @@

    indicator = self.securities[option].Gamma mirror = Symbol.create_option( underlying.value, option.id.market, option.id.option_style, - OptionRight.Call if option.id.option_right == OptionRight.PUT else OptionRight.PUT, + OptionRight.CALL if option.id.option_right == OptionRight.PUT else OptionRight.PUT, option.id.strike_price, option.id.date ).value # Check if price data is available for both contracts and the underlying asset. @@ -112182,7 +113689,7 @@

    indicator = self.securities[option].Vega mirror = Symbol.create_option( underlying.value, option.id.market, option.id.option_style, - OptionRight.Call if option.id.option_right == OptionRight.PUT else OptionRight.PUT, + OptionRight.CALL if option.id.option_right == OptionRight.PUT else OptionRight.PUT, option.id.strike_price, option.id.date ).value # Check if price data is available for both contracts and the underlying asset. @@ -112780,7 +114287,7 @@

    indicator = self.securities[option].Theta mirror = Symbol.create_option( underlying.value, option.id.market, option.id.option_style, - OptionRight.Call if option.id.option_right == OptionRight.PUT else OptionRight.PUT, + OptionRight.CALL if option.id.option_right == OptionRight.PUT else OptionRight.PUT, option.id.strike_price, option.id.date ).value # Check if price data is available for both contracts and the underlying asset. @@ -113383,7 +114890,7 @@

    indicator = self.securities[option].Rho mirror = Symbol.create_option( underlying.value, option.id.market, option.id.option_style, - OptionRight.Call if option.id.option_right == OptionRight.PUT else OptionRight.PUT, + OptionRight.CALL if option.id.option_right == OptionRight.PUT else OptionRight.PUT, option.id.strike_price, option.id.date ).value # Check if price data is available for both contracts and the underlying asset. @@ -113685,7 +115192,7 @@

    Create Subscriptions

    To view the supported assets in the US Cash Indices dataset, see the - + Supported Indices . @@ -121904,7 +123411,7 @@

    if not calls: return # Buy 1 0DTE call option contract for the SPX index. - self.Buy(calls[0].symbol, 1) + self.buy(calls[0].symbol, 1) @@ -122172,7 +123679,7 @@

    var option = AddIndexOptionContract(_contractSymbol);
     option.PriceModel = OptionPriceModels.BlackScholes();
    option = self.add_index_option_contract(self._contract_symbol)
    -option.PriceModel = OptionPriceModels.black_scholes()
    +option.price_model = OptionPriceModels.black_scholes()

    The @@ -125070,7 +126577,7 @@

    indicator = self.securities[option].ImpliedVolatility mirror = Symbol.create_option( underlying.value, option.id.market, option.id.option_style, - OptionRight.Call if option.id.option_right == OptionRight.PUT else OptionRight.PUT, + OptionRight.CALL if option.id.option_right == OptionRight.PUT else OptionRight.PUT, option.id.strike_price, option.id.date ) # Check if price data is available for both contracts and the underlying asset. @@ -125749,7 +127256,7 @@

    indicator = self.securities[option].Delta mirror = Symbol.create_option( underlying.value, option.id.market, option.id.option_style, - OptionRight.Call if option.id.option_right == OptionRight.PUT else OptionRight.PUT, + OptionRight.CALL if option.id.option_right == OptionRight.PUT else OptionRight.PUT, option.id.strike_price, option.id.date ) # Check if price data is available for both contracts and the underlying asset. @@ -126327,7 +127834,7 @@

    indicator = self.securities[option].Gamma mirror = Symbol.create_option( underlying.value, option.id.market, option.id.option_style, - OptionRight.Call if option.id.option_right == OptionRight.PUT else OptionRight.PUT, + OptionRight.CALL if option.id.option_right == OptionRight.PUT else OptionRight.PUT, option.id.strike_price, option.id.date ) # Check if price data is available for both contracts and the underlying asset. @@ -126904,7 +128411,7 @@

    indicator = self.securities[option].Vega mirror = Symbol.create_option( underlying.value, option.id.market, option.id.option_style, - OptionRight.Call if option.id.option_right == OptionRight.PUT else OptionRight.PUT, + OptionRight.CALL if option.id.option_right == OptionRight.PUT else OptionRight.PUT, option.id.strike_price, option.id.date ) # Check if price data is available for both contracts and the underlying asset. @@ -127482,7 +128989,7 @@

    indicator = self.securities[option].Theta mirror = Symbol.create_option( underlying.value, option.id.market, option.id.option_style, - OptionRight.Call if option.id.option_right == OptionRight.PUT else OptionRight.PUT, + OptionRight.CALL if option.id.option_right == OptionRight.PUT else OptionRight.PUT, option.id.strike_price, option.id.date ) # Check if price data is available for both contracts and the underlying asset. @@ -128065,7 +129572,7 @@

    indicator = self.securities[option].Rho mirror = Symbol.create_option( underlying.value, option.id.market, option.id.option_style, - OptionRight.Call if option.id.option_right == OptionRight.PUT else OptionRight.PUT, + OptionRight.CALL if option.id.option_right == OptionRight.PUT else OptionRight.PUT, option.id.strike_price, option.id.date ) # Check if price data is available for both contracts and the underlying asset. @@ -129078,7 +130585,7 @@

    OANDA Subscriptions

    To view the supported CFD contracts, see - + Supported Assets . For more information about the specific dataset we use for backtests, see the @@ -147130,7 +148637,7 @@

    Custom Security Properties

    for security in changes.added_securities: # Create an SMA indicator with 10 periods for the asset. # Use duck typing to store it on the security object. - security.indicator = self.sma(security.Symbol, 10) + security.indicator = self.sma(security.symbol, 10) # Warm up the indicator with historical data. self.warm_up_indicator(security.symbol, security.indicator) @@ -148814,6 +150321,58 @@

    Schedule

    Trigger an event on the last tradable date of each week for a specific symbol minus an offset. + + + + self.date_rules.quarter_start(days_offset: int = 0) + + + DateRules.QuarterStart(int daysOffset = 0) + + + + Trigger an event on the first day of each quarter plus an offset. + + + + + + self.date_rules.quarter_start(symbol: Symbol, days_offset: int = 0) + + + DateRules.QuarterStart(Symbol symbol, int daysOffset = 0) + + + + Trigger an event on the first tradable date of each quarter for a specific symbol plus an offset. + + + + + + self.date_rules.quarter_end(days_offset: int = 0) + + + DateRules.QuarterEnd(int daysOffset = 0) + + + + Trigger an event on the last day of each quarter minus an offset. + + + + + + self.date_rules.quarter_end(symbol: Symbol, days_offset: int = 0) + + + DateRules.QuarterEnd(Symbol symbol, int daysOffset = 0) + + + + Trigger an event on the last tradable date of each quarter for a specific symbol minus an offset. + + @@ -149168,9 +150727,9 @@

    // We want to trade the EMA with raw price but not altered by splits. UniverseSettings.DataNormalizationMode = DataNormalizationMode.SplitAdjusted; - // Only trade on the top 10 most traded stocks since they have the most popularity to drive trends. + // Only trade on the top 10 most traded stocks since they have the most popularity to drive trends. AddUniverse(Universe.Top(10)); - } + } public override void OnData(Slice slice) { @@ -149226,7 +150785,7 @@

    # We want to trade the EMA with raw price but not altered by splits. self.universe_settings.data_normalization_mode = DataNormalizationMode.SPLIT_ADJUSTED - # Only trade on the top 10 most traded stocks since they have the most popularity to drive trends. + # Only trade on the top 10 most traded stocks since they have the most popularity to drive trends. self.add_universe(self.universe.top(10)) def on_data(self, slice: Slice) -> None: @@ -150008,6 +151567,168 @@

    Live Trading Considerations

    +

    Common Questions

    + + + + + +

    + How often does a fundamental universe run? +

    +

    + Fundamental universes run once per day by default. To change the cadence, set + + UniverseSettings.Schedule + + + universe_settings.schedule + + before adding the universe. See + + Schedule + + . +

    +

    + Why do some Fundamental properties return NaN? +

    +

    + Not every company reports every Morningstar field, so unreported values come back as NaN. Filter the + + Fundamental + + objects to exclude NaN values for the property you use before you sort or rank by it. +

    +

    + Does the fundamental universe include delisted companies? +

    +

    + Yes. The + + Morningstar US Fundamentals + + dataset includes delisted tickers so your backtests are free of survivorship bias. The universe excludes ETFs, ADRs, and OTC securities. +

    +

    + Can I call AddEquity inside the universe filter function? +

    +

    + No. Return a list of + + Symbol + + objects from the filter function and LEAN subscribes to them automatically. Calling + + AddEquity + + + add_equity + + inside the filter creates duplicate subscriptions and unexpected behavior. +

    +

    + Can I combine technical indicators with fundamental selection? +

    +

    + Yes. Keep a per-symbol + + SelectionData + + helper that updates a + + manual indicator + + from the daily price and volume on the + + Fundamental + + object, then filter or rank by the indicator value. You cannot attach custom attributes to + + Fundamental + + objects, so use a separate dictionary keyed by + + Symbol + + . For more information, see + + Indicator Universes + + . +

    +

    + Why is my algorithm running out of memory with a fundamental universe? +

    +

    + Each asset in the universe consumes about 5 MB of RAM, so selecting thousands of stocks quickly exhausts the node memory. Tighten the filter so it returns only the assets you trade, and check the RAM capacity of your + + backtesting + + and + + live trading nodes + + . +

    + + +

    Examples

    @@ -150074,102 +151795,194 @@

    Fundamental - object has adjusted price and volume information, so you can do any price-related analysis. + object has daily price and volume information, so you can do any price-related analysis. The following algorithm defines a separate class to contain the indicator of each asset.

    -
    using System.Collections.Concurrent;
    -	
    -public class UpTrendLiquidUniverseAlgorithm : QCAlgorithm
    +   
    public class UpTrendLiquidUniverseAlgorithm  : QCAlgorithm
     {    
    -    // Create a concurrent dictionary to store the EMA data for universe selection.
    -    private ConcurrentDictionary<Symbol, SelectionData> _selectionDataBySymbol = new();
    +    private Dictionary<Symbol, SelectionData> _selectionDataBySymbol = new();
    +    private Universe _universe;
     
         public override void Initialize()
         {
             SetStartDate(2024, 9, 1);
             SetEndDate(2024, 12, 31);
    -
    +        Settings.SeedInitialPrices = true;
             // Add the custom universe.
    -        AddUniverse(SelectAssets);
    +        UniverseSettings.DataNormalizationMode = DataNormalizationMode.Raw;
    +        _universe = AddUniverse(SelectAssets);
    +        // Add a warm-up period to warm up the indicators.
    +        SetWarmUp(TimeSpan.FromDays(300));
         }
    -    
    -    private IEnumerable<Symbol> SelectAssets(IEnumerable<Fundamental> fundamental)
    +
    +    private IEnumerable<Symbol> SelectAssets(IEnumerable<Fundamental> fundamentals)
         {
    -        return (from f in fundamental
    -            // Create/Update the EMA indicators of each stock.
    -            let avg = _selectionDataBySymbol.GetOrAdd(f.Symbol, sym => new SelectionData(200))
    -            where avg.Update(f.EndTime, f.AdjustedPrice)
    -            // Select the Equities that are above their EMA and have a daily volume of $1B.
    -            // These assets are in an uptrend and are very liquid.
    -            where avg.Ema.IsReady && f.Price > avg.Ema.Current.Value && f.DollarVolume > 1000000000
    -            // Select the 10 most liquid Equities to avoid extra slippage.   
    -            orderby f.DollarVolume descending
    -            select f.Symbol).Take(10);
    +        // Update the indicator of all stocks in the universe dataset and
    +        // get the subset of stocks that have their indicator ready.
    +        var readyStocks = new List<Fundamental>();
    +        foreach (var f in fundamentals)
    +        {
    +            if (!_selectionDataBySymbol.TryGetValue(f.Symbol, out var sd))
    +            {
    +                sd = new SelectionData(this, f, 200);
    +                _selectionDataBySymbol[f.Symbol] = sd;
    +            }
    +            if (sd.Update(f))
    +            {
    +                readyStocks.Add(f);
    +            }
    +        }
    +        // As assests leave the Fundamental dataset, delete their SelectionData object.
    +        var activeStocks = fundamentals.Select(f => f.Symbol).ToHashSet();
    +        foreach (var symbol in _selectionDataBySymbol.Keys.Where(s => !activeStocks.Contains(s)).ToList())
    +        {
    +            _selectionDataBySymbol.Remove(symbol);
    +        }
    +        // During warm-up, keep the universe empty.
    +        if (IsWarmingUp)
    +        {
    +            return Enumerable.Empty<Symbol>();
    +        }
    +        // Select the Equities that are above their EMA and have a daily volume of $1B.
    +        // These assets are in an uptrend and are very liquid.
    +        return readyStocks
    +            .Select(f => _selectionDataBySymbol[f.Symbol])
    +            .Where(x => x.IsAboveEma && x.Volume > 1000000000)
    +            // Select the 10 most liquid Equities to avoid extra slippage.    
    +            .OrderBy(x => x.Volume)
    +            .TakeLast(10)
    +            .Select(x => x.Symbol);
         }
     }
     
     // Create a separate class to contain the EMA information of each asset.
    -class SelectionData
    +public class SelectionData
     {
    -    public readonly ExponentialMovingAverage Ema;
    +    private QCAlgorithm _algorithm;
    +    private decimal _priceScaleFactor;
    +    private ExponentialMovingAverage _ema { get; }
    +    public Symbol Symbol;
    +    public bool IsAboveEma = false;
    +    public decimal Volume = 0;
     
    -    // Create an EMA indicator for trend estimation and filtering.
    -    public SelectionData(int period)
    +    public SelectionData(QCAlgorithm algorithm, Fundamental f, int period)
         {
    -        Ema = new ExponentialMovingAverage(period);
    +        _algorithm = algorithm;
    +        _priceScaleFactor = f.PriceScaleFactor;
    +        // Create an EMA indicator for trend estimation and filtering.
    +        Symbol = f.Symbol;
    +        _ema = new ExponentialMovingAverage(period);
         }
     
         // Update your variables and indicators with the latest data.
    -    // You may also want to use the History API here to warm-up the indicator.
    -    public bool Update(DateTime time, decimal value)
    +    public bool Update(Fundamental f)
         {
    -        return Ema.Update(time, value);
    +        // If there hasn't been a split or dividend since the last trading
    +        // day, just update the indicator like normal.
    +        if (f.PriceScaleFactor == _priceScaleFactor)
    +        {
    +            return _update(f.EndTime, f.Volume, f.Price);
    +        }
    +        // Otherwise, reset the indicator and warm it up with the new
    +        // adjusted history.
    +        _priceScaleFactor = f.PriceScaleFactor;
    +        _ema.Reset();
    +        var history = _algorithm.History<TradeBar>(
    +            Symbol,
    +            _ema.WarmUpPeriod,
    +            Resolution.Daily,
    +            dataNormalizationMode: DataNormalizationMode.ScaledRaw
    +        );
    +        foreach (var bar in history)
    +        {
    +            _update(bar.EndTime, bar.Volume, bar.Close);
    +        }
    +        return _ema.IsReady;
    +    }
    +
    +    private bool _update(DateTime endTime, decimal volume, decimal price)
    +    {
    +        Volume = volume * price;
    +        if (_ema.Update(endTime, price))
    +        {
    +            IsAboveEma = price > _ema.Current.Value;
    +        }
    +        return _ema.IsReady;
         }
     }
    class UpTrendLiquidUniverseAlgorithm(QCAlgorithm):
     	
    -    # Create a dictionary to store the EMA data for universe selection.
         _selection_data_by_symbol = {}
     
         def initialize(self) -> None:
             self.set_start_date(2024, 9, 1)
             self.set_end_date(2024, 12, 31)
    -        
    +        self.settings.seed_initial_prices = True
             # Add the custom universe.
    -        self.add_universe(self._select_assets)
    +        self.universe_settings.data_normalization_mode = DataNormalizationMode.RAW
    +        self._universe = self.add_universe(self._select_assets)
    +        # Add a warm-up period to warm up the indicators.
    +        self.set_warm_up(timedelta(300))
         
    -    def _select_assets(self, fundamental: List[Fundamental]) -> List[Symbol]:
    -        for f in fundamental:
    -            # Create/Update the EMA indicators of each stock.
    -            if f.symbol not in self._selection_data_by_symbol:
    -                self._selection_data_by_symbol[f.symbol] = SelectionData(f.symbol, 200)
    -            self._selection_data_by_symbol[f.symbol].update(f.end_time, f.adjusted_price, f.dollar_volume)
    -        
    +    def _select_assets(self, fundamentals: List[Fundamental]) -> List[Symbol]:
    +        # Update the indicator of all stocks in the universe dataset and
    +        # get the subset of stocks that have their indicator ready.
    +        ready_stocks = [
    +            f for f in fundamentals
    +            if self._selection_data_by_symbol.setdefault(f.symbol, SelectionData(self, f, 200)).update(f)
    +        ]
    +        # As assests leave the Fundamental dataset, delete their SelectionData object.
    +        for symbol in self._selection_data_by_symbol.keys() - {f.symbol for f in fundamentals}:
    +            del self._selection_data_by_symbol[symbol]
    +        # During warm-up, keep the universe empty.
    +        if self.is_warming_up:
    +            return []
             # Select the Equities that are above their EMA and have a daily volume of $1B.
             # These assets are in an uptrend and are very liquid.
    -        selected = [x for x in self._selection_data_by_symbol.values() if x.is_above_ema and x.volume > 1_000_000_000]
    -            
    +        selected = [self._selection_data_by_symbol[f.symbol] for f in ready_stocks]
    +        selected = [x for x in selected if x.is_above_ema and x.volume > 1_000_000_000]          
             # Select the 10 most liquid Equities to avoid extra slippage.    
    -        return [ x.symbol for x in sorted(selected, key=lambda x: x.volume)[-10:] ]
    +        return [x.symbol for x in sorted(selected, key=lambda x: x.volume)[-10:]]
     
     
     # Create a separate class to contain the EMA information of each asset.
    -class SelectionData(object):
    +class SelectionData:
     
    -    def __init__(self, symbol, period):
    +    def __init__(self, algorithm, f, period):
    +        self._algorithm = algorithm
    +        self._price_scale_factor = f.price_scale_factor
             # Create an EMA indicator for trend estimation and filtering.
    -        self.symbol = symbol
    +        self.symbol = f.symbol
             self._ema = ExponentialMovingAverage(period)
             self.is_above_ema = False
             self.volume = 0
     
         # Update your variables and indicators with the latest data.
    -    # You may also want to use the History API here to warm-up the indicator.
    -    def update(self, time, price, volume):
    -        self.volume = volume
    -        if self._ema.update(time, price):
    -            self.is_above_ema = price > self._ema.current.value
    + def update(self, f): + # If there hasn't been a split or dividend since the last trading + # day, just update the indicator like normal. + if f.price_scale_factor == self._price_scale_factor: + return self._update(f.end_time, f.volume, f.price) + # Otherwise, reset the indicator and warm it up with the new + # adjusted history. + self._price_scale_factor = f.price_scale_factor + self._ema.reset() + history = self._algorithm.history[TradeBar]( + self.symbol, + self._ema.warm_up_period, + Resolution.DAILY, + data_normalization_mode=DataNormalizationMode.SCALED_RAW + ) + for bar in history: + self._update(bar.end_time, bar.volume, bar.close) + return self._ema.is_ready + + def _update(self, end_time, volume, price): + self.volume = volume * price + if self._ema.update(end_time, price): + self.is_above_ema = price > self._ema.current.value + return self._ema.is_ready

    In this example, the @@ -150244,99 +152057,184 @@

    You can use this ratio to select assets that are above their 10-day SMA and sort the results by the Equities that have had the biggest jump since yesterday.

    -
    using System.Collections.Concurrent;
    -
    -public class HighRelativeVolumeUniverseAlgorithm : QCAlgorithm
    +   
    public class HighRelativeVolumeUniverseAlgorithm  : QCAlgorithm
     {    
    -    // Create a dictionary to store the EMA data for universe selection.
    -    private ConcurrentDictionary<Symbol, SelectionData> _selectionDataBySymbol = new();
    +    private Dictionary<Symbol, SelectionData> _selectionDataBySymbol = new();
    +    private Universe _universe;
     
         public override void Initialize()
         {
             SetStartDate(2024, 9, 1);
             SetEndDate(2024, 12, 31);
    -
    -        // Add a universe with custom selection rules for filtering.
    -        AddUniverse(SelectAssets);
    +        Settings.SeedInitialPrices = true;
    +        // Add the custom universe.
    +        UniverseSettings.DataNormalizationMode = DataNormalizationMode.Raw;
    +        _universe = AddUniverse(SelectAssets);
    +        // Add a warm-up period to warm up the indicators.
    +        SetWarmUp(TimeSpan.FromDays(30));
         }
    -    
    -    private IEnumerable<Symbol> SelectAssets(IEnumerable<Fundamental> fundamental)
    +
    +    private IEnumerable<Symbol> SelectAssets(IEnumerable<Fundamental> fundamentals)
         {
    -        return (from f in fundamental
    -            // Create/Update the volume SMA indicator of each stock.
    -            let avg = _selectionDataBySymbol.GetOrAdd(f.Symbol, sym => new SelectionData(f.Symbol, 10))
    -            where avg.Update(f.EndTime, f.Volume)
    -            // Select the Equities with higher trading volume than their SMA, indicating higher capital flow.
    -            where avg.VolumeRatio > 1
    +        // Update the indicator of all stocks in the universe dataset and
    +        // get the subset of stocks that have their indicator ready.
    +        var readyStocks = new List<Fundamental>();
    +        foreach (var f in fundamentals)
    +        {
    +            if (!_selectionDataBySymbol.TryGetValue(f.Symbol, out var sd))
    +            {
    +                sd = new SelectionData(this, f, 10);
    +                _selectionDataBySymbol[f.Symbol] = sd;
    +            }
    +            if (sd.Update(f))
    +            {
    +                readyStocks.Add(f);
    +            }
    +        }
    +        // As assests leave the Fundamental dataset, delete their SelectionData object.
    +        var activeStocks = fundamentals.Select(f => f.Symbol).ToHashSet();
    +        foreach (var symbol in _selectionDataBySymbol.Keys.Where(s => !activeStocks.Contains(s)).ToList())
    +        {
    +            _selectionDataBySymbol.Remove(symbol);
    +        }
    +        // During warm-up, keep the universe empty.
    +        if (IsWarmingUp)
    +        {
    +            return Enumerable.Empty<Symbol>();
    +        }
    +        // Select the Equities with higher trading volume than their SMA, indicating higher capital flow.
    +        return readyStocks
    +            .Select(f => _selectionDataBySymbol[f.Symbol])
    +            .Where(x => x.VolumeRatio > 1)
                 // Select the 10 Equities with the highest relative volume, since they have the highest capactity
    -            // for scalp-trading or intra-day movement.
    -            orderby avg.VolumeRatio descending
    -            select f.Symbol).Take(10);
    +            // for scalp-trading or intra-day movement. 
    +            .OrderBy(x => x.VolumeRatio)
    +            .TakeLast(10)
    +            .Select(x => x.Symbol);
         }
     }
     
    -// Define a separate class to contain and calculate the SMA of each Equity.
    -class SelectionData
    +// Create a separate class to contain the SMA information of each asset.
    +public class SelectionData
     {
    -    public readonly Symbol Symbol;
    -    public readonly SimpleMovingAverage VolumeSma;
    -    public decimal VolumeRatio;
    +    private QCAlgorithm _algorithm;
    +    private decimal _priceScaleFactor;
    +    private SimpleMovingAverage _sma { get; }
    +    public Symbol Symbol;
    +    public decimal VolumeRatio = 0;
     
    -    public SelectionData(Symbol symbol, int period)
    +    public SelectionData(QCAlgorithm algorithm, Fundamental f, int period)
         {
    -        // Create an SMA of volume to track the popularity of the stock.
    -        Symbol = symbol;
    -        VolumeSma = new SimpleMovingAverage(period);
    +        _algorithm = algorithm;
    +        _priceScaleFactor = f.PriceScaleFactor;
    +        // Create an EMA indicator for trend estimation and filtering.
    +        Symbol = f.Symbol;
    +        _sma = new SimpleMovingAverage(period);
         }
    -    
    -    public bool Update(DateTime time, decimal value)
    +
    +    // Update your variables and indicators with the latest data.
    +    public bool Update(Fundamental f)
    +    {
    +        // If there hasn't been a split or dividend since the last trading
    +        // day, just update the indicator like normal.
    +        if (f.PriceScaleFactor == _priceScaleFactor)
    +        {
    +            return _update(f.EndTime, f.Volume);
    +        }
    +        // Otherwise, reset the indicator and warm it up with the new
    +        // adjusted history.
    +        _priceScaleFactor = f.PriceScaleFactor;
    +        _sma.Reset();
    +        var history = _algorithm.History<TradeBar>(
    +            Symbol,
    +            _sma.WarmUpPeriod,
    +            Resolution.Daily,
    +            dataNormalizationMode: DataNormalizationMode.ScaledRaw
    +        );
    +        foreach (var bar in history)
    +        {
    +            _update(bar.EndTime, bar.Volume);
    +        }
    +        return _sma.IsReady;
    +    }
    +
    +    private bool _update(DateTime endTime, decimal volume)
         {
             // Update the SMA with today's data and calculate the relative volume position for filtering.
    -        var ready = VolumeSma.Update(time, value);
    -        VolumeRatio = VolumeSma.Current.Value != 0m ? value / VolumeSma.Current.Value : -1m;
    +        var ready = _sma.Update(endTime, volume);
    +        VolumeRatio = _sma.Current.Value != 0m ? volume / _sma.Current.Value : -1m;
             return ready;
         }
     }
    class HighRelativeVolumeUniverseAlgorithm(QCAlgorithm):
    -    
    -    # Create a dictionary to store the EMA data for universe selection.
    -    _selection_data_by_symbol = {}
     
         def initialize(self) -> None:
             self.set_start_date(2024, 9, 1)
             self.set_end_date(2024, 12, 31)
    -        
    -        # Add a universe with custom selection rules for filtering.
    -        self.add_universe(self._select_assets)
    +        self.settings.seed_initial_prices = True
    +        # Add the custom universe.
    +        self._selection_data_by_symbol = {}
    +        self.universe_settings.data_normalization_mode = DataNormalizationMode.RAW
    +        self._universe = self.add_universe(self._select_assets)
    +        # Add a warm-up period to warm up the indicators.
    +        self.set_warm_up(timedelta(30))
         
    -    def _select_assets(self, fundamental: List[Fundamental]) -> List[Symbol]:
    -        # Create/Update the volume SMA indicator of each stock.
    -        for f in fundamental:
    -            if f.symbol not in self._selection_data_by_symbol:
    -                self._selection_data_by_symbol[f.symbol] = SelectionData(f.symbol, 10)
    -            self._selection_data_by_symbol[f.symbol].update(f.end_time, f.adjusted_price, f.dollar_volume)
    -
    +    def _select_assets(self, fundamentals: List[Fundamental]) -> List[Symbol]:
    +        # Update the indicator of all stocks in the universe dataset and
    +        # get the subset of stocks that have their indicator ready.
    +        ready_stocks = [
    +            f for f in fundamentals
    +            if self._selection_data_by_symbol.setdefault(f.symbol, SelectionData(self, f, 10)).update(f)
    +        ]
    +        # As assests leave the Fundamental dataset, delete their SelectionData object.
    +        for symbol in self._selection_data_by_symbol.keys() - {f.symbol for f in fundamentals}:
    +            del self._selection_data_by_symbol[symbol]
    +        # During warm-up, keep the universe empty.
    +        if self.is_warming_up:
    +            return []
             # Select the Equities with higher trading volume than their SMA, indicating higher capital flow.
    -        selected = [sd for sd in self._selection_data_by_symbol.values() if sd.volume_ratio > 1]
    -            
    +        selected = [self._selection_data_by_symbol[f.symbol] for f in ready_stocks]
    +        selected = [sd for sd in selected if sd.volume_ratio > 1]        
             # Select the 10 Equities with the highest relative volume, since they have the highest capactity
             # for scalp-trading or intra-day movement.
    -        return [ x.symbol for x in sorted(selected, key=lambda x: x.volume_ratio)[-10:] ]
    +        return [x.symbol for x in sorted(selected, key=lambda x: x.volume_ratio)[-10:]]
     
     
    -# Define a separate class to contain and calculate the SMA of each Equity.
    -class SelectionData(object):
    -    
    -    def __init__(self, symbol, period):
    -        self.symbol = symbol
    -        self.volume_ratio = 0
    +# Create a separate class to contain the SMA information of each asset.
    +class SelectionData:
    +
    +    def __init__(self, algorithm, f, period):
    +        self._algorithm = algorithm
    +        self._price_scale_factor = f.price_scale_factor
             # Create an SMA of volume to track the popularity of the stock.
    +        self.symbol = f.symbol
             self._sma = SimpleMovingAverage(period)
     
    -    def update(self, time, price, volume):
    +    # Update your variables and indicators with the latest data.
    +    def update(self, f):
    +        # If there hasn't been a split or dividend since the last trading
    +        # day, just update the indicator like normal.
    +        if f.price_scale_factor == self._price_scale_factor:
    +            return self._update(f.end_time, f.volume)
    +        # Otherwise, reset the indicator and warm it up with the new 
    +        # adjusted history.
    +        self._price_scale_factor = f.price_scale_factor
    +        self._sma.reset()
    +        history = self._algorithm.history[TradeBar](
    +            self.symbol, 
    +            self._sma.warm_up_period, 
    +            Resolution.DAILY, 
    +            data_normalization_mode=DataNormalizationMode.SCALED_RAW
    +        )
    +        for bar in history:
    +            self._update(bar.end_time, bar.volume)
    +        return self._sma.is_ready
    +    
    +    def _update(self, end_time, volume):
             # Update the SMA with today's data and calculate the relative volume position for filtering.
    -        if self._sma.update(time, volume):
    -            self.volume_ratio = volume / self._sma.current.value if self._sma.current.value != 0 else -1
    + if self._sma.update(end_time, volume): + self.volume_ratio = volume / self._sma.current.value if self._sma.current.value != 0 else -1 + return self._sma.is_ready

    Example 4: 10 "Fastest Moving" Stocks With a 50-Day EMA > 200 Day EMA @@ -150370,6 +152268,190 @@

    Research post.

    +

    + Example 6: Stocks Far Above Their SMA +

    +

    + The following example shows how to select a universe of US Equities based on their price and SMA indicator. + The + + SelectionData + + class keeps track of the SMA indicator for each stock in the universe dataset. + When a split or dividend occurs for a stock, the data in its indicator becomes invalid because it doesn't account for the price adjustments that the split or dividend causes. + The + + SelectionData + + class resets and warms up the indicator with the + + ScaledRaw + + + SCALED_RAW + + + data normalization mode + + , which gives you accurate indicator values to use in your universe selection after each corporate action. +

    +
    +
    class EquityIndicatorUniverseSelectionAlgorithm(QCAlgorithm):
    +
    +    def initialize(self) -> None:
    +        self.set_start_date(2024, 9, 1)
    +        self.set_end_date(2024, 12, 31)
    +        self.settings.seed_initial_prices = True
    +        # Add a universe of US Equities based on an indicator.
    +        self._selection_data_by_symbol = {}
    +        self._universe = self.add_universe(self._select_assets)
    +        # Add a warm-up period to warm up the indicators in the universe selection.
    +        self.set_warm_up(timedelta(60))
    +
    +    def _select_assets(self, fundamentals):
    +        # Update the indicator of all stocks in the universe dataset and
    +        # get the subset of stocks that have their indicator ready.
    +        ready_stocks = [
    +            f for f in fundamentals
    +            if self._selection_data_by_symbol.setdefault(f.symbol, SelectionData(self, f)).update(f)
    +        ]
    +        # As assests leave the Fundamental dataset, delete their SelectionData object.
    +        for symbol in self._selection_data_by_symbol.keys() - {f.symbol for f in fundamentals}:
    +            del self._selection_data_by_symbol[symbol]
    +        # During warm-up, keep the universe empty.
    +        if self.is_warming_up:
    +            return []
    +        # Select a subset of the stocks based on the indicator.
    +        # Example: 10 stocks furthest above their SMA.
    +        factor_by_symbol = {
    +            f.symbol: f.price / self._selection_data_by_symbol[f.symbol].indicator.current.value 
    +            for f in ready_stocks
    +        }
    +        return sorted(
    +            {k: v for k, v in factor_by_symbol.items() if v > 0}, 
    +            key=lambda symbol: factor_by_symbol[symbol]
    +        )[-100:]
    +
    +
    +class SelectionData:
    +
    +    def __init__(self, algorithm, f):
    +        self._algorithm = algorithm
    +        self._price_scale_factor = f.price_scale_factor
    +        self.indicator = SimpleMovingAverage(21)
    +
    +    def update(self, f):
    +        # If there hasn't been a split or dividend since the last trading
    +        # day, just update the indicator like normal.
    +        if f.price_scale_factor == self._price_scale_factor:
    +            return self.indicator.update(f.end_time, f.price)
    +        # Otherwise, reset the indicator and warm it up with the new 
    +        # adjusted history.
    +        self._price_scale_factor = f.price_scale_factor
    +        self.indicator.reset()
    +        history = self._algorithm.history[TradeBar](
    +            f.symbol, 
    +            self.indicator.warm_up_period, 
    +            Resolution.DAILY, 
    +            data_normalization_mode=DataNormalizationMode.SCALED_RAW
    +        )
    +        for bar in history:
    +            self.indicator.update(bar)
    +        return self.indicator.is_ready
    +
    public class EquityIndicatorUniverseSelectionAlgorithm : QCAlgorithm
    +{
    +    private Dictionary<Symbol, SelectionData> _selectionDataBySymbol = new();
    +    private Universe _universe;
    +
    +    public override void Initialize()
    +    {
    +        SetStartDate(2024, 9, 1);
    +        SetEndDate(2024, 12, 31);
    +        Settings.SeedInitialPrices = true;
    +        // Add a universe of US Equities based on an indicator.
    +        _universe = AddUniverse(SelectAssets);
    +        // Add a warm-up period to warm up the indicators in the universe selection.
    +        SetWarmUp(TimeSpan.FromDays(60));
    +    }
    +
    +    private IEnumerable<Symbol> SelectAssets(IEnumerable<Fundamental> fundamentals)
    +    {
    +        // Update the indicator of all stocks in the universe dataset and
    +        // get the subset of stocks that have their indicator ready.
    +        var readyStocks = new List<Fundamental>();
    +        foreach (var f in fundamentals)
    +        {
    +            if (!_selectionDataBySymbol.TryGetValue(f.Symbol, out var sd))
    +            {
    +                sd = new SelectionData(this, f);
    +                _selectionDataBySymbol[f.Symbol] = sd;
    +            }
    +            if (sd.Update(f))
    +            {
    +                readyStocks.Add(f);
    +            }
    +        }
    +        // As assests leave the Fundamental dataset, delete their SelectionData object.
    +        var activeStocks = fundamentals.Select(f => f.Symbol).ToHashSet();
    +        foreach (var symbol in _selectionDataBySymbol.Keys.Where(s => !activeStocks.Contains(s)).ToList())
    +        {
    +            _selectionDataBySymbol.Remove(symbol);
    +        }
    +        // During warm-up, keep the universe empty.
    +        if (IsWarmingUp)
    +        {
    +            return Enumerable.Empty<Symbol>();
    +        }
    +        // Select a subset of the stocks based on the indicator.
    +        // Example: 10 stocks furthest above their SMA.
    +        return readyStocks
    +            .Select(f => (f.Symbol, Factor: f.Price / _selectionDataBySymbol[f.Symbol].Indicator.Current.Value))
    +            .Where(t => t.Factor > 0)
    +            .OrderBy(t => t.Factor)
    +            .TakeLast(100)
    +            .Select(t => t.Symbol);
    +    }
    +}
    +
    +public class SelectionData
    +{
    +    private QCAlgorithm _algorithm;
    +    private decimal _priceScaleFactor;
    +    public SimpleMovingAverage Indicator { get; }
    +
    +    public SelectionData(QCAlgorithm algorithm, Fundamental f)
    +    {
    +        _algorithm = algorithm;
    +        _priceScaleFactor = f.PriceScaleFactor;
    +        Indicator = new SimpleMovingAverage(21);
    +    }
    +
    +    public bool Update(Fundamental f)
    +    {
    +        // If there hasn't been a split or dividend since the last trading
    +        // day, just update the indicator like normal.
    +        if (f.PriceScaleFactor == _priceScaleFactor)
    +        {
    +            return Indicator.Update(f.EndTime, f.Price);
    +        }
    +        // Otherwise, reset the indicator and warm it up with the new
    +        // adjusted history.
    +        _priceScaleFactor = f.PriceScaleFactor;
    +        Indicator.Reset();
    +        var history = _algorithm.History<TradeBar>(
    +            f.Symbol,
    +            Indicator.WarmUpPeriod,
    +            Resolution.Daily,
    +            dataNormalizationMode: DataNormalizationMode.ScaledRaw
    +        );
    +        foreach (var bar in history)
    +        {
    +            Indicator.Update(bar);
    +        }
    +        return Indicator.IsReady;
    +    }
    +}
    +

    Other Examples

    @@ -150471,7 +152553,7 @@

    The ETF ticker. To view the supported ETFs in the US ETF Constituents dataset, see - + Supported ETFs . @@ -150704,7 +152786,7 @@

    Historical Data

    For more information about ETF Constituents data, see - + US ETF Constituents . @@ -151198,91 +153280,97 @@

    Chain Fundamental and Alternative Data

    QuiverCNBCsUniverse alternative universe - . It first selects the 100 most liquid US Equities and then filters them down to those mentioned by CNBC commentator/trader Jim Cramer. The output of the alternative universe selection method is the output of the chained universe. + . It stores every US Equity fundamental, intersects them with the names CNBC commentator Jim Cramer mentions, and trades the 100 most liquid intersection members each morning.

    public class ChainedUniverseAlgorithm : QCAlgorithm
     {
    -    private List<Symbol> _fundamental = new();
    +    private List<Fundamental> _fundamental = [];
    +    private Universe _universe;
     
         public override void Initialize()
         {
             SetStartDate(2024, 9, 1);
             SetEndDate(2024, 12, 31);
             SetCash(100000);
    -
    -        // Filter the top 100 liquid equities of the last trading day, and save the symbols for the next filtering.
    +        Settings.SeedInitialPrices = true;
    +        UniverseSettings.Resolution = Resolution.Minute;
    +        // First universe: store all US Equity fundamentals; emits Universe.Unchanged.
             AddUniverse(fundamental =>
             {
    -            _fundamental = (from c in fundamental
    -                orderby c.DollarVolume descending
    -                select c.Symbol).Take(100).ToList();
    +            _fundamental = [..fundamental];
                 return Universe.Unchanged;
             });
    -        // Filter the equities being commented on by CNBC analyst Cramer, then select the ones that intersect with the fundamental universe.
    -        AddUniverse<QuiverCNBCsUniverse>(altCoarse =>
    -        {
    -            var followers = from d in altCoarse.OfType<QuiverCNBCsUniverse>()
    -                where d.Traders.ToLower().Contains("cramer")
    -                select d.Symbol;
    -            return _fundamental.Intersect(followers);
    +        // Second universe: equities mentioned by Cramer, ranked by dollar volume.
    +        _universe = AddUniverse<QuiverCNBCsUniverse>(altCoarse =>
    +        {
    +            var alt = altCoarse.OfType<QuiverCNBCsUniverse>()
    +                .Where(d => d.Traders.ToLower().Contains("cramer"))
    +                .Select(d => d.Symbol)
    +                .ToHashSet();
    +            Plot("Universe", "Raw", alt.Count);
    +            return _fundamental
    +                .Where(c => alt.Contains(c.Symbol))
    +                .OrderByDescending(c => c.DollarVolume)
    +                .Select(c => c.Symbol)
    +                .Take(100);
             });
    +        // Rebalance before market open to trade today's intersection.
    +        Schedule.On(DateRules.EveryDay("SPY"), TimeRules.At(9, 0, 0), Rebalance);
         }
     
    -    public override void OnSecuritiesChanged(SecurityChanges changes)
    -    {
    -        // Request CNBC data for the selected stocks.
    -        foreach (var added in changes.AddedSecurities)
    -        {
    -            AddData<QuiverCNBCs>(added.Symbol);
    -        }
    -    }
    -
    -    public override void OnData(Slice data)
    +    private void Rebalance()
         {
    -        foreach (var dataPoint in data.Get<QuiverCNBCs>().SelectMany(x=> x.Value.OfType<QuiverCNBC>()))
    +        if (_universe.Selected.Count == 0)
             {
    -            Debug($"{dataPoint.Symbol} traders at {data.Time}: {dataPoint.Traders}");
    +            return;
             }
    +        var weight = _universe.Selected.Count >= 10 ? 1m / _universe.Selected.Count : 0.1m;
    +        var targets = _universe.Selected
    +            .Select(symbol => new PortfolioTarget(symbol, weight))
    +            .ToList();
    +        SetHoldings(targets, true);
         }
     }
    from AlgorithmImports import *
     
    +
     class ChainedUniverseAlgorithm(QCAlgorithm):
     
    -    _fundamental = []
    +    _fundamental: List[Fundamental] = []
     
         def initialize(self) -> None:
             self.set_start_date(2024, 9, 1)
             self.set_end_date(2024, 12, 31)
             self.set_cash(100000)
    -        self.add_universe(self._fundamental_filter_function)
    -        self.add_universe(QuiverCNBCsUniverse, self._mad_money_selection)
    -    
    -    def _fundamental_filter_function(self, fundamental: List[Fundamental]) -> List[Symbol]:
    -        # Filter the top 100 liquid equities of the last trading day, and save the symbols for the next filtering.
    -        sorted_by_dollar_volume = sorted(fundamental, key=lambda x: x.dollar_volume, reverse=True) 
    -        self.fundamental = [c.symbol for c in sorted_by_dollar_volume[:100]]
    +        self.settings.seed_initial_prices = True
    +        self.universe_settings.resolution = Resolution.MINUTE
    +        # First universe: store all US Equity fundamentals; emits Universe.UNCHANGED.
    +        self.add_universe(self._fundamental_filter)
    +        # Second universe: equities mentioned by Cramer, ranked by dollar volume.
    +        self._universe = self.add_universe(QuiverCNBCsUniverse, self._select_assets)
    +        # Rebalance before market open to trade today's intersection.
    +        self.schedule.on(self.date_rules.every_day("SPY"), self.time_rules.at(9, 0, 0), self._rebalance)
    +
    +    def _fundamental_filter(self, fundamental: List[Fundamental]) -> Universe.UnchangedUniverse:
    +        self._fundamental = fundamental
             return Universe.UNCHANGED
    -    
    -    def _mad_money_selection(self, alt_coarse: List[QuiverCNBCsUniverse]) -> List[Symbol]:
    -        # Filter the equities being commented on by CNBC analyst Cramer, then select the ones that intersect with the fundamental universe.
    -        madmoney = [d.symbol for d in alt_coarse if 'Cramer' in d.traders]
    -        return list(set(self._fundamental) & set(madmoney))
    -    
    -    def on_securities_changed(self, changes: SecurityChanges) -> None:
    -        # Request CNBC data for the selected stocks.
    -        for added in changes.added_securities:
    -            self.add_data(QuiverCNBCs, added.symbol)
    -    
    -    def on_data(self, data: Slice) -> None:
    -        # Prices in the slice from the universe selection
    -        # Alternative data in a slice from OnSecuritiesChanged Addition
    -        # for ticker,bar in data.bars.items():
    -        #     pass
    -        for dataset_symbol, data_points in data.get(QuiverCNBCs).items():
    -            for data_point in data_points:
    -                self.debug(f"{dataset_symbol} traders at {data.time}: {data_point.traders}")
    +
    +    def _select_assets(self, alt_coarse: List[QuiverCNBCsUniverse]) -> List[Symbol]:
    +        # Keep symbols mentioned by Cramer.
    +        alt = {d.symbol for d in alt_coarse if 'cramer' in d.traders.lower()}
    +        self.plot('Universe', 'Raw', len(alt))
    +        return [c.symbol for c in sorted(
    +            [c for c in self._fundamental if c.symbol in alt],
    +            key=lambda c: c.dollar_volume, reverse=True
    +        )[:100]]
    +
    +    def _rebalance(self) -> None:
    +        if not self._universe.selected:
    +            return
    +        weight = min(1 / len(self._universe.selected), 0.1)
    +        targets = [PortfolioTarget(symbol, weight) for symbol in self._universe.selected]
    +        self.set_holdings(targets, True)
     
    @@ -151494,92 +153582,100 @@

    Chain ETF and Alternative Data

    QuiverCNBCsUniverse alternative universe - . It first selects all constituents of SPY and then filters them down to those mentioned by CNBC commentator/trader Jim Cramer. The output of the alternative universe selection method is the output of the chained universe. + . It stores every SPY constituent, intersects them with the names CNBC commentator Jim Cramer mentions, and trades the 100 heaviest-weighted intersection members each morning. Names CNBC mentions but that aren't in SPY are dropped.

    public class ChainedUniverseAlgorithm : QCAlgorithm
     {
    -    private List<Symbol> _etf = new();
    +    private List<ETFConstituentUniverse> _etf = [];
    +    private Universe _universe;
     
         public override void Initialize()
         {
             SetStartDate(2024, 9, 1);
             SetEndDate(2024, 12, 31);
             SetCash(100000);
    -        UniverseSettings.Asynchronous = true;
    -
    -        // Save all SPY constituents for the next filtering.
    +        Settings.SeedInitialPrices = true;
    +        UniverseSettings.Resolution = Resolution.Minute;
    +        // First universe: store all SPY constituents; emits Universe.Unchanged.
             AddUniverse(Universe.ETF("SPY", Market.USA, UniverseSettings, constituents =>
             {
    -            _etf = constituents.Select(c => c.Symbol).ToList();
    +            _etf = [..constituents];
                 return Universe.Unchanged;
             }));
    -        // Filter the equities being commented on by CNBC analyst Cramer, then select the ones in SPY constituents.
    -        AddUniverse<QuiverCNBCsUniverse>(altCoarse =>
    -        {
    -            var followers = from d in altCoarse.OfType<QuiverCNBCsUniverse>()
    -                where d.Traders.ToLower().Contains("cramer")
    -                select d.Symbol;
    -            return _etf.Intersect(followers);
    +        // Second universe: equities mentioned by Cramer that are in SPY, ranked by ETF weight.
    +        _universe = AddUniverse<QuiverCNBCsUniverse>(altCoarse =>
    +        {
    +            var alt = altCoarse.OfType<QuiverCNBCsUniverse>()
    +                .Where(d => d.Traders.ToLower().Contains("cramer"))
    +                .Select(d => d.Symbol)
    +                .ToHashSet();
    +            Plot("Universe", "Raw", alt.Count);
    +            // Names not in SPY cannot be traded, so intersect with the ETF list.
    +            return _etf
    +                .Where(c => alt.Contains(c.Symbol))
    +                .OrderByDescending(c => c.Weight)
    +                .Select(c => c.Symbol)
    +                .Take(100);
             });
    +        // Rebalance before market open to trade today's intersection.
    +        Schedule.On(DateRules.EveryDay("SPY"), TimeRules.At(9, 0, 0), Rebalance);
         }
     
    -    public override void OnSecuritiesChanged(SecurityChanges changes)
    -    {
    -        // Request CNBC data for the selected stocks.
    -        foreach (var added in changes.AddedSecurities)
    -        {
    -            AddData<QuiverCNBCs>(added.Symbol);
    -        }
    -    }
    -
    -    public override void OnData(Slice data)
    +    private void Rebalance()
         {
    -        foreach (var dataPoint in data.Get<QuiverCNBCs>().SelectMany(x=> x.Value.OfType<QuiverCNBC>()))
    +        if (_universe.Selected.Count == 0)
             {
    -            Debug($"{dataPoint.Symbol} traders at {data.Time}: {dataPoint.Traders}");
    +            return;
             }
    +        var weight = _universe.Selected.Count >= 10 ? 1m / _universe.Selected.Count : 0.1m;
    +        var targets = _universe.Selected
    +            .Select(symbol => new PortfolioTarget(symbol, weight))
    +            .ToList();
    +        SetHoldings(targets, true);
         }
     }
    from AlgorithmImports import *
     
    +
     class ChainedUniverseAlgorithm(QCAlgorithm):
     
    -    _etf = []
    +    _etf: List[ETFConstituentUniverse] = []
     
    -    def initialize(self):
    +    def initialize(self) -> None:
             self.set_start_date(2024, 9, 1)
             self.set_end_date(2024, 12, 31)
             self.set_cash(100000)
    -        self.universe_settings.asynchronous = True
    -        # Save all SPY constituents for the next filtering.
    -        self.add_universe(self.universe.etf("SPY", Market.USA, self.universe_settings, self._etf_constituents_filter))
    -        # Next filtering based on CNBC data.
    -        self.add_universe(QuiverCNBCsUniverse, self._mad_money_selection)
    -
    -    def _etf_constituents_filter(self, fundamental: List[Fundamental]) -> List[Symbol]:
    -        # Save all SPY constituents for the next filtering.
    -        self._etf = [c.symbol for c in constituents]
    +        self.settings.seed_initial_prices = True
    +        self.universe_settings.resolution = Resolution.MINUTE
    +        # First universe: store all SPY constituents; emits Universe.UNCHANGED.
    +        self.add_universe(self.universe.etf("SPY", Market.USA, self.universe_settings, self._etf_filter))
    +        # Second universe: equities mentioned by Cramer that are in SPY, ranked by ETF weight.
    +        self._universe = self.add_universe(QuiverCNBCsUniverse, self._select_assets)
    +        # Rebalance before market open to trade today's intersection.
    +        self.schedule.on(self.date_rules.every_day("SPY"), self.time_rules.at(9, 0, 0), self._rebalance)
    +
    +    def _etf_filter(self, constituents: List[ETFConstituentUniverse]) -> Universe.UnchangedUniverse:
    +        self._etf = constituents
             return Universe.UNCHANGED
     
    -    def _mad_money_selection(self, alt_coarse: List[QuiverCNBCsUniverse]) -> List[Symbol]:
    -        # Filter the equities being commented on by CNBC analyst Cramer, then select the ones in SPY constituents.
    -        madmoney = [d.symbol for d in alt_coarse if 'Cramer' in d.traders]
    -        return list(set(self._etf) & set(madmoney))
    -
    -    def on_securities_changed(self, changes):
    -        # Request CNBC data for the selected stocks.
    -        for added in changes.added_securities:
    -            self.add_data(QuiverCNBCs, added.symbol)
    +    def _select_assets(self, alt_coarse: List[QuiverCNBCsUniverse]) -> List[Symbol]:
    +        # Keep symbols mentioned by Cramer.
    +        alt = {d.symbol for d in alt_coarse if 'cramer' in d.traders.lower()}
    +        self.plot('Universe', 'Raw', len(alt))
    +        # Names not in SPY cannot be traded, so intersect with the ETF list.
    +        return [c.symbol for c in sorted(
    +            [c for c in self._etf if c.symbol in alt],
    +            key=lambda c: c.weight, reverse=True
    +        )[:100]]
     
    -    def on_data(self, data):
    -        # Prices in the slice from the universe selection
    -        # Alternative data in a slice from OnSecuritiesChanged Addition
    -        # for ticker,bar in data.bars.items():
    -        #     pass
    -        for dataset_symbol, data_points in data.get(QuiverCNBCs).items():
    -            for data_point in data_points:
    -                self.debug(f"{dataset_symbol} traders at {data.time}: {data_point.traders}")
    + def _rebalance(self) -> None: + if not self._universe.selected: + return + weight = min(1 / len(self._universe.selected), 0.1) + targets = [PortfolioTarget(symbol, weight) for symbol in self._universe.selected] + self.set_holdings(targets, True) +
    @@ -152127,17 +154223,17 @@

    Supported Datasets

    @@ -246215,25 +336331,25 @@

    class EODHDMacroIndicatorsAlgorithm(QCAlgorithm): def initialize(self): - self.set_start_date(2024, 9, 1) + self.set_start_date(2024, 1, 1) self.set_end_date(2024, 12, 31) - self.equity_symbol = self.add_equity("SPY", Resolution.Daily).symbol - + self.equity_symbol = self.add_equity("SPY", Resolution.DAILY).symbol + ticker = EODHD.MacroIndicators.UnitedStates.GDP_GROWTH_ANNUAL - self.dataset_symbol = self.add_data(EODHDMacroIndicators, ticker).Symbol + self.dataset_symbol = self.add_data(EODHDMacroIndicators, ticker).symbol def on_data(self, slice): indicators = slice.get(EODHDMacroIndicators).get(self.dataset_symbol) if indicators: gdp = indicators.data[0].value - self.SetHoldings(self.equity_symbol, 1 if gdp > 0 else -1) + self.set_holdings(self.equity_symbol, 1 if gdp > 0 else -1)
    public class EODHDMacroIndicatorsAlgorithm : QCAlgorithm
     {
         private Symbol _equitySymbol, _datasetSymbol;
     
         public override void Initialize()
         {
    -        SetStartDate(2024, 9, 1);
    +        SetStartDate(2024, 1, 1);
             SetEndDate(2024, 12, 31);
             _equitySymbol = AddEquity("SPY", Resolution.Daily).Symbol;
             var ticker = EODHD.MacroIndicators.UnitedStates.GdpGrowthAnnual;
    @@ -246263,7 +336379,7 @@ 

    class EODHDMacroIndicatorsAlgorithm(QCAlgorithm): def initialize(self) -> None: - self.set_start_date(2024, 9, 1) + self.set_start_date(2024, 1, 1) self.set_end_date(2024, 12, 31) # Use market ETF as a vehicle to trade. symbol = Symbol.create("SPY", SecurityType.EQUITY, Market.USA) @@ -246300,7 +336416,7 @@

    { public override void Initialize() { - SetStartDate(2024, 9, 1); + SetStartDate(2024, 1, 1); SetEndDate(2024, 12, 31); // Use market ETF as a vehicle to trade. var symbol = QuantConnect.Symbol.Create("SPY", SecurityType.Equity, Market.USA); @@ -246512,51 +336628,6 @@

    Data Summary

    -

    Universe Selection

    - - -

    - To select a dynamic universe of US Equities based on the Upcoming Dividends dataset, call the - - AddUniverse - - - add_universe - - method with a - - EODHDUpcomingDividends - - cast. -

    -
    -
    def initialize(self) -> None:
    -    self._universe = self.add_universe(EODHDUpcomingDividends, self.universe_selection_filter)
    -
    -def universe_selection_filter(self, dividends: List[EODHDUpcomingDividends]) -> List[Symbol]:
    -    return [d.symbol for d in dividends if d.dividend_date <= self.time + timedelta(1) and d.dividend > 0.05]
    -
    public override void Initialize()
    -{
    -    _universe = AddUniverse<EODHDUpcomingDividends>(UniverseSelectionFilter);
    -}
    -
    -private IEnumerable<Symol> UniverseSelectionFilter(IEnumerable<BaseData> dividends)
    -{
    -    return from EODHDUpcomingDividends d in dividends
    -           where d.DividendsDate <= Time.AddDays(1) && d.Dividends > 0.05m
    -           select d.Symbol;
    -}
    -
    -

    - For more information about universe settings, see - - Settings - - . -

    - - -

    Requesting Data

    @@ -246702,6 +336773,124 @@

    Historical Data

    +

    Universe Selection

    + + +

    + To select a dynamic universe of US Equities based on the Upcoming Dividends dataset, call the + + AddUniverse + + + add_universe + + method with a + + EODHDUpcomingDividends + + cast. +

    +
    +
    def initialize(self) -> None:
    +    self._universe = self.add_universe(EODHDUpcomingDividends, self.universe_selection_filter)
    +
    +def universe_selection_filter(self, dividends: List[EODHDUpcomingDividends]) -> List[Symbol]:
    +    return [d.symbol for d in dividends if d.dividend_date <= self.time + timedelta(1) and d.dividend > 0.05]
    +
    public override void Initialize()
    +{
    +    _universe = AddUniverse<EODHDUpcomingDividends>(UniverseSelectionFilter);
    +}
    +
    +private IEnumerable<Symol> UniverseSelectionFilter(IEnumerable<BaseData> dividends)
    +{
    +    return from EODHDUpcomingDividends d in dividends
    +           where d.DividendsDate <= Time.AddDays(1) && d.Dividends > 0.05m
    +           select d.Symbol;
    +}
    +
    +

    + For more information about universe settings, see + + Settings + + . +

    + + + +

    Universe History

    + + +

    + You can get historical universe data in an algorithm and in the Research Environment. +

    +

    + Historical Universe Data in Algorithms +

    +

    + To get historical universe data in an algorithm, call the + + History + + + history + + method with the + + Universe + + object and the lookback period. If there is no data in the period you request, the history result is empty. +

    +
    +
    var universeHistory = History(_universe, 30, Resolution.Daily);
    +foreach (var dividends in universeHistory)
    +{
    +    foreach (EODHDUpcomingDividends dividend in dividends)
    +    {
    +        Log($"{dividend.Symbol} dividend on {dividend.DividendDate}: {dividend.dividend}");
    +    }
    +}
    +
    # DataFrame example where the columns are the EODHDUpcomingDividends attributes: 
    +history_df = self.history(self._universe, 30, Resolution.DAILY, flatten=True)
    +
    +# Series example where the values are lists of EODHDUpcomingDividends objects: 
    +universe_history = self.history(self._universe, 30, Resolution.DAILY)
    +for (_, time), dividends in universe_history.items():
    +    for dividend in dividends:
    +        self.log(f"{dividend.symbol} dividend on {dividend.dividend_date}: {dividend.dividend}")
    +
    +

    + Historical Universe Data in Research +

    +

    + To get historical universe data in research, call the + + History + + + history + + method with the + + Universe + + object, a start date, and an end date. This method returns the filtered universe. If there is no data in the period you request, the history result is empty. +

    +
    +
    var universeHistory = qb.History(universe, qb.Time.AddDays(-30), qb.Time);
    +foreach (var dividends in universeHistory)
    +{
    +    foreach (EODHDUpcomingDividends dividend in dividends)
    +    {
    +        Console.WriteLine($"{dividend.Symbol} dividend on {dividend.DividendDate}: {dividend.dividend}");
    +    }
    +}
    +
    # DataFrame example where the columns are the EODHDUpcomingDividends attributes: 
    +history = qb.history(universe, qb.time-timedelta(30), qb.time, flatten=True)
    +
    + + +

    Remove Subscriptions

    @@ -246756,8 +336945,7 @@

    self.settings.seed_initial_prices = True # Universe consists of equities with upcoming dividend events. self._universe = self.add_universe(EODHDUpcomingDividends, self.selection) - # Add a Scheduled Event to rebalance the portfolio every morning - # based on upcoming dividend signals. + # Add a Scheduled Event to rebalance the portfolio every morning based on upcoming dividend signals. spy = Symbol.create('SPY', SecurityType.EQUITY, Market.USA) self.schedule.on( self.date_rules.every_day(spy), @@ -246770,11 +336958,11 @@

    return [x.symbol for x in dividends if x.dividend_date < self.time + timedelta(1) and x.dividend > 0.5] def _rebalance(self) -> None: - # Equally invest in each member of the universe to evenly dissipate the capital risk. + # Equally invest in each member of the universe. symbols = [s for s in self._universe.selected if self.securities[s].price] total_count = len(symbols) - targets = [PortfolioTarget(symbol, -1. / total_count) for symbol in symbols] - self.set_holdings(targets, liquidate_existing_holdings=True)

    + targets = [PortfolioTarget(symbol, -1 / total_count) for symbol in symbols] + self.set_holdings(targets, True)
    public class UpcomingDividendsExampleAlgorithm : QCAlgorithm
     {
         private Universe _universe;
    @@ -246787,27 +336975,27 @@ 

    // Seed the price of each asset with its last known price to avoid trading errors. Settings.SeedInitialPrices = true; // Universe consists of equities with upcoming dividend events. - _universe = AddUniverse<EODHDUpcomingDividends>((dividends) => { - // Select the stocks with upcoming dividend record date, with a sufficient dividend size. - return from EODHDUpcomingDividends d in dividends - where d.DividendDate < Time.AddDays(1) && d.Dividend > 0.5m - select d.Symbol; - }); - // Add a Scheduled Event to rebalance the portfolio every morning - // based on upcoming dividend signals. + // Select the stocks with upcoming dividend record date, with a sufficient dividend size. + _universe = AddUniverse<EODHDUpcomingDividends>( + dividends => dividends + .OfType<EODHDUpcomingDividends>() + .Where(dividend => dividend.DividendDate < Time.AddDays(1) && dividend.Dividend > 0.5m) + .Select(dividend => dividend.Symbol) + ); + // Add a Scheduled Event to rebalance the portfolio every morning based on upcoming dividend signals. var spy = QuantConnect.Symbol.Create("SPY", SecurityType.Equity, Market.USA); Schedule.On(DateRules.EveryDay(spy), TimeRules.AfterMarketOpen(spy, 1), Rebalance); } public void Rebalance() { - // Equally invest in each member of the universe to evenly dissipate the capital risk. + // Equally invest in each member of the universe. var symbols = _universe.Selected.Where(s => Securities[s].Price != 0); var totalCount = symbols.Count(); var targets = symbols .Select(symbol => new PortfolioTarget(symbol, -1m / totalCount)) .ToList(); - SetHoldings(targets, liquidateExistingHoldings: true); + SetHoldings(targets, true); } }

    @@ -247004,51 +337192,6 @@

    Data Summary

    -

    Universe Selection

    - - -

    - To select a dynamic universe of US Equities based on the Upcoming Earnings dataset, call the - - AddUniverse - - - add_universe - - method with a - - EODHDUpcomingEarnings - - cast. -

    -
    -
    def initialize(self) -> None:
    -    self._universe = self.add_universe(EODHDUpcomingEarnings, self.universe_selection_filter)
    -
    -def universe_selection_filter(self, earnings: List[EODHDUpcomingEarnings]) -> List[Symbol]:
    -    return [d.symbol for d in earnings if d.report_date <= self.time + timedelta(3) and d.estimate > 0]
    -
    public override void Initialize()
    -{
    -    _universe = AddUniverse<EODHDUpcomingEarnings>(UniverseSelectionFilter);
    -}
    -
    -private IEnumerable<Symbol> UniverseSelectionFilter(IEnumerable<EODHDUpcomingEarnings> earnings)
    -{
    -    return from d in earnings
    -           where d.ReportDate <= Time.AddDays(3) && d.Estimate > 0m
    -           select d.Symbol;
    -}
    -
    -

    - For more information about universe settings, see - - Settings - - . -

    - - -

    Requesting Data

    @@ -247126,8 +337269,8 @@

    Accessing Data

    def on_data(self, slice: Slice) -> None:
         upcomings_earnings_for_symbol = slice.get(EODHDUpcomingEarnings).get(self._symbol)
    -        if upcomings_earnings_for_symbol:
    -            self.log(f"{self._symbol} will report earnings at {upcomings_earnings_for_symbol.report_date} {upcomings_earnings_for_symbol.report_time} with estimated EPS {upcomings_earnings_for_symbol.estimate}")
    + if upcomings_earnings_for_symbol: + self.log(f"{self._symbol} will report earnings at {upcomings_earnings_for_symbol.report_date} {upcomings_earnings_for_symbol.report_time} with estimated EPS {upcomings_earnings_for_symbol.estimate}")
    public override void OnData(Slice slice)
     {
         var upcomingEarnings = slice.Get<EODHDUpcomingEarnings>();
    @@ -247175,7 +337318,7 @@ 

    Historical Data

    method with the type - EODHDUpcomingEarning + EODHDUpcomingEarnings .

    @@ -247217,6 +337360,124 @@

    Historical Data

    +

    Universe Selection

    + + +

    + To select a dynamic universe of US Equities based on the Upcoming Earnings dataset, call the + + AddUniverse + + + add_universe + + method with a + + EODHDUpcomingEarnings + + cast. +

    +
    +
    def initialize(self) -> None:
    +    self._universe = self.add_universe(EODHDUpcomingEarnings, self.universe_selection_filter)
    +
    +def universe_selection_filter(self, earnings: List[EODHDUpcomingEarnings]) -> List[Symbol]:
    +    return [d.symbol for d in earnings if d.report_date <= self.time + timedelta(3) and d.estimate > 0]
    +
    public override void Initialize()
    +{
    +    _universe = AddUniverse<EODHDUpcomingEarnings>(UniverseSelectionFilter);
    +}
    +
    +private IEnumerable<Symbol> UniverseSelectionFilter(IEnumerable<EODHDUpcomingEarnings> earnings)
    +{
    +    return from d in earnings
    +           where d.ReportDate <= Time.AddDays(3) && d.Estimate > 0m
    +           select d.Symbol;
    +}
    +
    +

    + For more information about universe settings, see + + Settings + + . +

    + + + +

    Universe History

    + + +

    + You can get historical universe data in an algorithm and in the Research Environment. +

    +

    + Historical Universe Data in Algorithms +

    +

    + To get historical universe data in an algorithm, call the + + History + + + history + + method with the + + Universe + + object and the lookback period. If there is no data in the period you request, the history result is empty. +

    +
    +
    var universeHistory = History(_universe, 30, Resolution.Daily);
    +foreach (var earnings in universeHistory)
    +{
    +    foreach (EODHDUpcomingEarnings earning in earnings)
    +    {
    +        Log($"{earning.Symbol} estimate at {earning.EndTime}: {earning.Estimate}");
    +    }
    +}
    +
    # DataFrame example where the columns are the EODHDUpcomingEarnings attributes: 
    +history_df = self.history(self._universe, 30, Resolution.DAILY, flatten=True)
    +
    +# Series example where the values are lists of EODHDUpcomingEarnings objects: 
    +universe_history = self.history(self._universe, 30, Resolution.DAILY)
    +for (_, time), earnings in universe_history.items():
    +    for earning in earnings:
    +        self.log(f"{earning.symbol} estimate at {earning.end_time}: {earning.estimate}")
    +
    +

    + Historical Universe Data in Research +

    +

    + To get historical universe data in research, call the + + History + + + history + + method with the + + Universe + + object, a start date, and an end date. This method returns the filtered universe. If there is no data in the period you request, the history result is empty. +

    +
    +
    var universeHistory = qb.History(universe, qb.Time.AddDays(-30), qb.Time);
    +foreach (var earnings in universeHistory)
    +{
    +    foreach (EODHDUpcomingEarnings earning in earnings)
    +    {
    +        Console.WriteLine($"{earning.Symbol} estimate at {earning.EndTime}: {earning.Rank2Days}");
    +    }
    +}
    +
    # DataFrame example where the columns are the EODHDUpcomingEarnings attributes: 
    +history = qb.history(universe, qb.time-timedelta(30), qb.time, flatten=True)
    +
    + + +

    Remove Subscriptions

    @@ -247838,6 +338099,115 @@

    Data Summary

    +

    Requesting Data

    + + +

    + To add Upcoming IPOs data to your algorithm, call the + + AddData<EODHDUpcomingIPOs> + + + add_data + + method. +

    +
    +
    class UpcomingIPOsDataAlgorithm(QCAlgorithm):
    +    def initialize(self) -> None:
    +        self.set_start_date(2019, 1, 1)
    +        self.set_end_date(2020, 6, 1)
    +        self.set_cash(100000)
    +
    +        self.dataset_symbol = self.add_data(EODHDUpcomingIPOs, "ipos").symbol
    +
    public class UpcomingIPOsDataAlgorithm : QCAlgorithm
    +{
    +    private Symbol _datasetSymbol;
    +
    +    public override void Initialize()
    +    {
    +        SetStartDate(2019, 1, 1);
    +        SetEndDate(2020, 6, 1);
    +        SetCash(100000);
    +
    +        _datasetSymbol = AddData<EODHDUpcomingIPOs>("ipos").Symbol;
    +    }
    +}
    +
    + + + +

    Accessing Data

    + + +

    + To get the current Upcoming IPOs data, call the + + Get<EODHDUpcomingIPOs> + + + get(EODHDUpcomingIPOs) + + method from the current + + + Slice + + + . Then, iterate through all of the dataset objects in the current + + Slice + +

    +
    +
    def on_data(self, slice: Slice) -> None:
    +    for equity_symbol, upcomings_ipos_data_point in slice.get(EODHDUpcomingIPOs).items():
    +        self.log(f"{equity_symbol} will start IPO at {upcomings_ipos_data_point.ipo_date} with price {upcomings_ipos_data_point.offer_price} and {upcomings_ipos_data_point.shares} shares")
    +
    +
    public override void OnData(Slice slice)
    +{
    +    foreach (var kvp in slice.Get<EODHDUpcomingIPOs>())
    +    {
    +        var equitySymbol = kvp.Key;
    +        var upcomingIPOsDataPoint = kvp.Value;
    +        Log($"{equitySymbol} will start IPO at {upcomingIPOsDataPoint.IPODate} with price {upcomingIPOsDataPoint.OfferPrice} and {upcomingIPOsDataPoint.Shares} shares");
    +    }
    +}
    +
    + + + +

    Historical Data

    + + +

    + To get historical Upcoming IPOs data, call the + + History + + + history + + method with the type + + EODHDUpcomingIPOs + + cast and the period of request. If there is no data in the period you request, the history result is empty. +

    +
    +
    history_bars = self.history[EODHDUpcomingIPOs](timedelta(100), Resolution.DAILY)
    +
    var history = History<EODHDUpcomingSplits>(TimeSpan.FromDays(100), Resolution.Daily);
    +
    +

    + For more information about historical data, see + + History Requests + + . +

    + + +

    Universe Selection

    @@ -247891,112 +338261,76 @@

    Universe Selection

    -

    Requesting Data

    +

    Universe History

    - To add Upcoming IPOs data to your algorithm, call the - - AddData<EODHDUpcomingIPOs> - - - add_data - - method. + You can get historical universe data in an algorithm and in the Research Environment.

    -
    -
    class UpcomingIPOsDataAlgorithm(QCAlgorithm):
    -    def initialize(self) -> None:
    -        self.set_start_date(2019, 1, 1)
    -        self.set_end_date(2020, 6, 1)
    -        self.set_cash(100000)
    -
    -        self.dataset_symbol = self.add_data(EODHDUpcomingIPOs, "ipos").symbol
    -
    public class UpcomingIPOsDataAlgorithm : QCAlgorithm
    -{
    -    private Symbol _datasetSymbol;
    -
    -    public override void Initialize()
    -    {
    -        SetStartDate(2019, 1, 1);
    -        SetEndDate(2020, 6, 1);
    -        SetCash(100000);
    -
    -        _datasetSymbol = AddData<EODHDUpcomingIPOs>("ipos").Symbol;
    -    }
    -}
    -
    - - - -

    Accessing Data

    - - +

    + Historical Universe Data in Algorithms +

    - To get the current Upcoming IPOs data, call the + To get historical universe data in an algorithm, call the - Get<EODHDUpcomingIPOs> + History - get(EODHDUpcomingIPOs) + history - method from the current - - - Slice - - - . Then, iterate through all of the dataset objects in the current + method with the - Slice + Universe + object and the lookback period. If there is no data in the period you request, the history result is empty.

    -
    def on_data(self, slice: Slice) -> None:
    -    for equity_symbol, upcomings_ipos_data_point in slice.get(EODHDUpcomingIPOs).items():
    -        self.log(f"{equity_symbol} will start IPO at {upcomings_ipos_data_point.ipo_date} with price {upcomings_ipos_data_point.offer_price} and {upcomings_ipos_data_point.shares} shares")
    -
    -
    public override void OnData(Slice slice)
    +   
    var universeHistory = History(_universe, 30, Resolution.Daily);
    +foreach (var ipos in universeHistory)
     {
    -    foreach (var kvp in slice.Get<EODHDUpcomingIPOs>())
    +    foreach (EODHDUpcomingIPOs ipo in ipos)
         {
    -        var equitySymbol = kvp.Key;
    -        var upcomingIPOsDataPoint = kvp.Value;
    -        Log($"{equitySymbol} will start IPO at {upcomingIPOsDataPoint.IPODate} with price {upcomingIPOsDataPoint.OfferPrice} and {upcomingIPOsDataPoint.Shares} shares");
    +        Log($"{ipo.Symbol} offer price at {ipo.IpoDate}: {ipo.OfferPrice}");
         }
     }
    -
    - - +
    # DataFrame example where the columns are the EODHDUpcomingIPOs attributes: 
    +history_df = self.history(self._universe, 30, Resolution.DAILY, flatten=True)
     
    -

    Historical Data

    - - +# Series example where the values are lists of EODHDUpcomingIPOs objects: +universe_history = self.history(self._universe, 30, Resolution.DAILY) +for (_, time), ipos in universe_history.items(): + for ipo in ipos: + self.log(f"{ipo.symbol} offer price at {ipo.ipo_date}: {ipo.offer_price}")
    +
    +

    + Historical Universe Data in Research +

    - To get historical Upcoming IPOs data, call the + To get historical universe data in research, call the History history - method with the type + method with the - EODHDUpcomingIPOs + Universe - cast and the period of request. If there is no data in the period you request, the history result is empty. + object, a start date, and an end date. This method returns the filtered universe. If there is no data in the period you request, the history result is empty.

    -
    history_bars = self.history[EODHDUpcomingIPOs](timedelta(100), Resolution.DAILY)
    -
    var history = History<EODHDUpcomingSplits>(TimeSpan.FromDays(100), Resolution.Daily);
    +
    var universeHistory = qb.History(universe, qb.Time.AddDays(-30), qb.Time);
    +foreach (var ipos in universeHistory)
    +{
    +    foreach (EODHDUpcomingIPOs ipo in ipos)
    +    {
    +        Console.WriteLine($"{ipo.Symbol} offer price at {ipo.IpoDate}: {ipo.OfferPrice}");
    +    }
    +}
    +
    # DataFrame example where the columns are the EODHDUpcomingIPOs attributes: 
    +history = qb.history(universe, qb.time-timedelta(30), qb.time, flatten=True)
    -

    - For more information about historical data, see - - History Requests - - . -

    @@ -248056,30 +338390,30 @@

    self.set_start_date(2024, 9, 1) self.set_end_date(2024, 12, 31) self.set_cash(100000) - # Filter for new stocks to trade their hype using EODHDUpcomingIPOs. + # Filter for new stocks with EODHDUpcomingIPOs to trade first-day hype. self._universe = self.add_universe(EODHDUpcomingIPOs, self.selection) def selection(self, ipos: List[EODHDUpcomingIPOs]) -> List[Symbol]: - # Select the stocks that IPO starts today and traded in Nasdaq + # Select stocks with IPO dates by tomorrow on Nasdaq. return [ - x.symbol for x in ipos - if (x.ipo_date and - x.ipo_date <= self.time + timedelta(1) and + x.symbol for x in ipos + if (x.ipo_date and + x.ipo_date <= self.time + timedelta(1) and x.exchange == Exchange.NASDAQ) ] - def on_data(self, slice: Slice) -> None: - # Invest in new stocks and trade on their first day for their hype. - # Equally invest in each new stocks to evenly dissipate the capital risk. + def on_data(self, data: Slice) -> None: + # Invest in new stocks to trade first-day hype. + # Equally invest in each new stock to distribute capital risk. for symbol in self._universe.selected: security = self.securities[symbol] if not security.holdings.invested and security.price: - self.set_holdings(symbol, 0.1/len(self._universe.selected)) + self.set_holdings(symbol, 0.1 / len(self._universe.selected)) def on_securities_changed(self, changes: SecurityChanges) -> None: - # Liquidate the new stocks traded today/yesterday to capitalize the first-day hype. - for removed in changes.removed_securities: - self.liquidate(removed.symbol) + # Liquidate new stocks traded today or yesterday to capitalize on first-day hype. + for security in changes.removed_securities: + self.liquidate(security)
    public class UpcomingIPOsExampleAlgorithm : QCAlgorithm
     {
         private Universe _universe;
    @@ -248089,35 +338423,34 @@ 

    SetStartDate(2024, 9, 1); SetEndDate(2024, 12, 31); SetCash(100000); - - // Filter for new stocks to trade their hype using EODHDUpcomingIPOs. - _universe = AddUniverse<EODHDUpcomingIPOs>(ipos => - ipos.Select(x => x as EODHDUpcomingIPOs) + // Filter for new stocks with EODHDUpcomingIPOs to trade first-day hype. + _universe = AddUniverse<EODHDUpcomingIPOs>(ipos => + ipos.OfType<EODHDUpcomingIPOs>() .Where(x => x.IpoDate <= Time.AddDays(1) && x.Exchange == Exchange.NASDAQ) .Select(x => x.Symbol) ); } - public override void OnData(Slice slice) + public override void OnData(Slice data) { - // Invest in new stocks and trade on their first day for their hype. - // Equally invest in each new stocks to evenly dissipate the capital risk. + // Invest in new stocks to trade first-day hype. + // Equally invest in each new stock to distribute capital risk. foreach (var symbol in _universe.Selected) { var security = Securities[symbol]; if (!security.Holdings.Invested && security.Price != 0) { - SetHoldings(symbol, 0.1m/_universe.Selected.Count); + SetHoldings(symbol, 0.1m / _universe.Selected.Count); } } } public override void OnSecuritiesChanged(SecurityChanges changes) { - // Liquidate the new stocks traded today/yesterday to capitalize the first-day hype. - foreach (var removed in changes.RemovedSecurities) + // Liquidate new stocks traded today or yesterday to capitalize on first-day hype. + foreach (var security in changes.RemovedSecurities) { - Liquidate(removed.Symbol); + Liquidate(security.Symbol); } } }

    @@ -248402,51 +338735,6 @@

    Data Summary

    -

    Universe Selection

    - - -

    - To select a dynamic universe of US Equities based on the Upcoming Splits dataset, call the - - AddUniverse - - - add_universe - - method with a - - EODHDUpcomingSplits - - cast. -

    -
    -
    def initialize(self) -> None:
    -    self._universe = self.add_universe(EODHDUpcomingSplits, self.universe_selection_filter)
    -
    -def universe_selection_filter(self, splits: List[EODHDUpcomingSplits]) -> List[Symbol]:
    -    return [d.symbol for d in splits if d.split_date <= self.time + timedelta(3) and d.split_factor > 1]
    -
    public override void Initialize()
    -{
    -    _universe = AddUniverse<EODHDUpcomingSplits>(UniverseSelectionFilter);
    -}
    -
    -private IEnumerable<Symol> UniverseSelectionFilter(IEnumerable<EODHDUpcomingSplits> splits)
    -{
    -    return from d in splits
    -           where d.SplitDate <= Time.AddDays(3) && d.SplitFactor > 1m
    -           select d.Symbol;
    -}
    -
    -

    - For more information about universe settings, see - - Settings - - . -

    - - -

    Requesting Data

    @@ -248592,6 +338880,124 @@

    Historical Data

    +

    Universe Selection

    + + +

    + To select a dynamic universe of US Equities based on the Upcoming Splits dataset, call the + + AddUniverse + + + add_universe + + method with a + + EODHDUpcomingSplits + + cast. +

    +
    +
    def initialize(self) -> None:
    +    self._universe = self.add_universe(EODHDUpcomingSplits, self.universe_selection_filter)
    +
    +def universe_selection_filter(self, splits: List[EODHDUpcomingSplits]) -> List[Symbol]:
    +    return [d.symbol for d in splits if d.split_date <= self.time + timedelta(3) and d.split_factor > 1]
    +
    public override void Initialize()
    +{
    +    _universe = AddUniverse<EODHDUpcomingSplits>(UniverseSelectionFilter);
    +}
    +
    +private IEnumerable<Symol> UniverseSelectionFilter(IEnumerable<EODHDUpcomingSplits> splits)
    +{
    +    return from d in splits
    +           where d.SplitDate <= Time.AddDays(3) && d.SplitFactor > 1m
    +           select d.Symbol;
    +}
    +
    +

    + For more information about universe settings, see + + Settings + + . +

    + + + +

    Universe History

    + + +

    + You can get historical universe data in an algorithm and in the Research Environment. +

    +

    + Historical Universe Data in Algorithms +

    +

    + To get historical universe data in an algorithm, call the + + History + + + history + + method with the + + Universe + + object and the lookback period. If there is no data in the period you request, the history result is empty. +

    +
    +
    var universeHistory = History(_universe, 30, Resolution.Daily);
    +foreach (var splits in universeHistory)
    +{
    +    foreach (EODHDUpcomingSplits split in splits)
    +    {
    +        Log($"{split.Symbol} split factor on {split.SplitDate}: {split.SplitFactor}");
    +    }
    +}
    +
    # DataFrame example where the columns are the EODHDUpcomingSplits attributes: 
    +history_df = self.history(self._universe, 30, Resolution.DAILY, flatten=True)
    +
    +# Series example where the values are lists of EODHDUpcomingSplits objects: 
    +universe_history = self.history(self._universe, 30, Resolution.DAILY)
    +for (_, time), splits in universe_history.items():
    +    for split in splits:
    +        self.log(f"{split.symbol} split factor on {split.split_factor}: {split.split_factor}")
    +
    +

    + Historical Universe Data in Research +

    +

    + To get historical universe data in research, call the + + History + + + history + + method with the + + Universe + + object, a start date, and an end date. This method returns the filtered universe. If there is no data in the period you request, the history result is empty. +

    +
    +
    var universeHistory = qb.History(universe, qb.Time.AddDays(-30), qb.Time);
    +foreach (var splits in universeHistory)
    +{
    +    foreach (EODHDUpcomingSplits split in splits)
    +    {
    +        Console.WriteLine($"{split.Symbol} split factor on {split.SplitDate}: {split.SplitFactor}");
    +    }
    +}
    +
    # DataFrame example where the columns are the EODHDUpcomingSplits attributes: 
    +history = qb.history(universe, qb.time-timedelta(30), qb.time, flatten=True)
    +
    + + +

    Remove Subscriptions

    @@ -248640,38 +339046,33 @@

    class UpcomingSplitsExampleAlgorithm(QCAlgorithm): - + def initialize(self) -> None: self.set_start_date(2024, 9, 1) self.set_end_date(2024, 12, 31) self.set_cash(100000) # Seed the price of each asset with its last known price to avoid trading errors. self.settings.seed_initial_prices = True - # Universe consists of equities with upcoming splits events. + # Select equities with upcoming split events. self._universe = self.add_universe(EODHDUpcomingSplits, self.selection) - # Add a Scheduled Event to rebalance the portfolio every morning - # based on upcoming splits signals. + # Add a Scheduled Event to rebalance the portfolio every morning based on upcoming split signals. spy = Symbol.create('SPY', SecurityType.EQUITY, Market.USA) self.schedule.on( - self.date_rules.every_day(spy), - self.time_rules.after_market_open(spy, 1), + self.date_rules.every_day(spy), + self.time_rules.after_market_open(spy, 1), self._rebalance ) - + def selection(self, splits: List[EODHDUpcomingSplits]) -> List[Symbol]: - # Split (more shares with lower price) will make the stock more affordable and drive up the demand. - # Hence, include all stocks that will have a split within the next 7 days. - # Note that spliting up the stock means the split factor > 1. + # Select splits that increase share count and make each share more affordable. return [x.symbol for x in splits if x.split_factor > 1] - + def _rebalance(self) -> None: # Equally invest in each member of the universe to evenly dissipate the capital risk. symbols = [s for s in self._universe.selected if self.securities[s].price] total_count = len(symbols) targets = [PortfolioTarget(symbol, 1. / total_count) for symbol in symbols] - self.set_holdings(targets, liquidate_existing_holdings=True) - - + self.set_holdings(targets, True)
    public class UpcomingSplitsExampleAlgorithm : QCAlgorithm
     {
         private Universe _universe;
    @@ -248683,17 +339084,14 @@ 

    SetCash(100000); // Seed the price of each asset with its last known price to avoid trading errors. Settings.SeedInitialPrices = true; - // Universe consists of equities with upcoming earnings events. - _universe = AddUniverse<EODHDUpcomingSplits>((splits) => { - return splits - // Split (more shares with lower price) will make the stock more affordable and drive up the demand. - // Hence, include all stocks that will have a split within the next 7 days. - // Note that spliting up the stock means the split factor > 1. - .Where(datum => (datum as EODHDUpcomingSplits).SplitFactor > 1) - .Select(datum => datum.Symbol); - }); - // Add a Scheduled Event to rebalance the portfolio every morning - // based on upcoming splits signals. + // Select splits that increase share count and make each share more affordable. + _universe = AddUniverse<EODHDUpcomingSplits>( + splits => splits + .OfType<EODHDUpcomingSplits>() + .Where(split => split.SplitFactor > 1) + .Select(split => split.Symbol) + ); + // Add a Scheduled Event to rebalance the portfolio every morning based on upcoming split signals. var spy = QuantConnect.Symbol.Create("SPY", SecurityType.Equity, Market.USA); Schedule.On(DateRules.EveryDay(spy), TimeRules.AfterMarketOpen(spy, 1), Rebalance); } @@ -248706,7 +339104,7 @@

    var targets = symbols .Select(symbol => new PortfolioTarget(symbol, 1m / totalCount)) .ToList(); - SetHoldings(targets, liquidateExistingHoldings: true); + SetHoldings(targets, true); } }

    @@ -261802,7 +352200,7 @@

    def on_data(self, slice: Slice) -> None: # Trade with updated FED peak-to-trough indicator if slice.contains_key(self.fred_peak_to_trough) and slice.contains_key(self.spy): - peak_to_trough = slice.Get(Fred, self.fred_peak_to_trough).value + peak_to_trough = slice.get(Fred, self.fred_peak_to_trough).value # Buy SPY if peak to trough value is 0, which is the expansionary period if peak_to_trough == 0 and not self.portfolio.invested: @@ -261902,7 +352300,7 @@

    # Trade with updated FED peak-to-trough indicator if slice.contains_key(self.fred_peak_to_trough): - self.peak_to_trough_value = slice.Get(Fred, self.fred_peak_to_trough).value + self.peak_to_trough_value = slice.get(Fred, self.fred_peak_to_trough).value # Ensure we have a FRED peak to trough value if self.peak_to_trough_value is None: @@ -263878,17 +354276,6 @@

    - @@ -264389,65 +354776,64 @@

    def initialize(self) -> None: self.set_start_date(2024, 9, 1) self.set_end_date(2024, 12, 31) - self.aapl = self.add_equity("AAPL", Resolution.DAILY).symbol - # Subscribe to CNBC data for AAPL to generate trade signal - self.dataset_symbol = self.add_data(QuiverCNBCs, self.aapl).symbol - # Define a mapping from OrderDirection to int. + self._equity = self.add_equity("AAPL", Resolution.DAILY) + # Subscribe to CNBC data for AAPL to generate the trade signal. + self._dataset_symbol = self.add_data(QuiverCNBCs, self._equity).symbol + # Normalize order directions into numeric signals. self._int_by_direction = { OrderDirection.BUY: 1, OrderDirection.HOLD: 0, OrderDirection.SELL: -1 } + # Historical data + history = self.history(self._dataset_symbol, 10, Resolution.DAILY) + self.debug(f"We got {len(history)} items from historical data request of {self._dataset_symbol}.") - # history request - history = self.history(self.dataset_symbol, 10, Resolution.DAILY) - self.debug(f"We got {len(history)} items from historical data request of {self.dataset_symbol}.") - - def on_data(self, slice: Slice) -> None: - for cnbcs in slice.Get(QuiverCNBCs).values(): - # Using mean prediction from CNBC analysts to be the trade signal - # If the average CNBC insight is upward movement, invest AAPL + def on_data(self, data: Slice) -> None: + for cnbcs in data.get(QuiverCNBCs).values(): + # Use the mean CNBC analyst prediction as the trade signal. + # Invest in AAPL when the average CNBC insight is positive. if np.mean([self._int_by_direction[cnbc.direction] for cnbc in cnbcs]) > 0: - self.set_holdings(self.aapl, 1) + self.set_holdings(self._equity, 1) else: - self.set_holdings(self.aapl, 0) + self.set_holdings(self._equity, 0)
    public class QuiverCNBCsAlgorithm : QCAlgorithm
     {
    -    private Symbol _symbol, _datasetSymbol;
    +    private Equity _equity;
    +    private Symbol _datasetSymbol;
         private Dictionary<OrderDirection, int> _intByOrderDirection;
     
         public override void Initialize()
         {
             SetStartDate(2024, 9, 1);
             SetEndDate(2024, 12, 31);
    -        _symbol = AddEquity("AAPL").Symbol;
    -        // Subscribe to CNBC data for AAPL to generate trade signal
    -        _datasetSymbol = AddData<QuiverCNBCs>(_symbol).Symbol;
    -        // Define a mapping from OrderDirection to int.
    +        _equity = AddEquity("AAPL", Resolution.Daily);
    +        // Subscribe to CNBC data for AAPL to generate the trade signal.
    +        _datasetSymbol = AddData<QuiverCNBCs>(_equity.Symbol).Symbol;
    +        // Normalize order directions into numeric signals.
             _intByOrderDirection = new () {
                 {OrderDirection.Buy, 1},
                 {OrderDirection.Hold, 0},
                 {OrderDirection.Sell, -1}
             };
    -
    -        // history request
    -        var history = History<QuiverCNBCs>(new[] {_datasetSymbol}, 10, Resolution.Daily);
    +        // Historical data
    +        var history = History<QuiverCNBCs>([_datasetSymbol], 10, Resolution.Daily);
             Debug($"We got {history.Count()} items from historical data request of {_datasetSymbol}.");
         }
     
    -    public override void OnData(Slice slice)
    +    public override void OnData(Slice data)
         {
    -        foreach (var kvp in slice.Get<QuiverCNBCs>())
    +        foreach (var cnbcs in data.Get<QuiverCNBCs>().Values)
             {
    -            // Using mean prediction from CNBC analysts to be the trade signal
    -            // If the average CNBC insight is upward movement, invest AAPL
    -            if (kvp.Value.Average(x => _intByOrderDirection[(x as QuiverCNBC).Direction]) > 0)
    +            // Use the mean CNBC analyst prediction as the trade signal.
    +            // Invest in AAPL when the average CNBC insight is positive.
    +            if (cnbcs.Average(cnbc => _intByOrderDirection[(cnbc as QuiverCNBC).Direction]) > 0)
                 {
    -                SetHoldings(_symbol, 1);
    +                SetHoldings(_equity.Symbol, 1);
                 }
                 else
                 {
    -                SetHoldings(_symbol, 0);
    +                SetHoldings(_equity.Symbol, 0);
                 }
             }
         }
    @@ -264749,7 +355135,7 @@ 

    Getting Started

    The following snippet demonstrates how to request data from the Corporate Lobbying dataset:

    -
    self.aapl = self.add_equity("AAPL", Resolution.DAILY).symbol
    +   
    self.symbol = self.add_equity("AAPL", Resolution.DAILY).symbol
     self.dataset_symbol = self.add_data(QuiverLobbyings, self.symbol).symbol
     
     self._universe = self.add_universe(QuiverLobbyingUniverse, self.universe_selection_filter)
    @@ -265191,55 +355577,55 @@

    def initialize(self) -> None: self.set_start_date(2024, 9, 1) self.set_end_date(2024, 12, 31) - self._symbol = self.add_equity("AAPL", Resolution.DAILY).symbol - # Subscribe to lobbying data for AAPL to generate trade signal - self._dataset_symbol = self.add_data(QuiverLobbyings, self._symbol).symbol - - # history request + self._equity = self.add_equity("AAPL", Resolution.DAILY) + self.settings.seed_initial_prices = True + # Subscribe to lobbying data for AAPL to generate the trade signal. + self._dataset_symbol = self.add_data(QuiverLobbyings, self._equity).symbol + # Request recent history to confirm the custom data subscription. history = self.history(self._dataset_symbol, 10, Resolution.DAILY) self.debug(f"We got {len(history)} items from historical data request of {self._dataset_symbol}.") - def on_data(self, slice: Slice) -> None: - # Trade only base on lobbying data - for lobbyings in slice.get(QuiverLobbyings).values(): - # Buy if over 50000 lobbying amount, suggesting a favored political prospect and sentiment + def on_data(self, data: Slice) -> None: + # Trade only based on lobbying data. + for lobbyings in data.get(QuiverLobbyings).values(): + # Buy when lobbying amount is over 50000, suggesting favorable political sentiment. if any([lobbying.amount > 50000 for lobbying in lobbyings]): - self.set_holdings(self._symbol, 1) - # Sell if below 10000 lobbying amount, suggesting a less favorable political prospect and sentiment + self.set_holdings(self._equity, 1) + # Sell when lobbying amount is below 10000, suggesting less favorable political sentiment. elif any([lobbying.amount < 10000 for lobbying in lobbyings]): - self.set_holdings(self._symbol, -1)

    + self.set_holdings(self._equity, -1)
    public class QuiverLobbyingAlgorithm : QCAlgorithm
     {
    -    private Symbol _symbol, _datasetSymbol;
    +    private Equity _equity;
    +    private Symbol _datasetSymbol;
     
         public override void Initialize()
         {
             SetStartDate(2024, 9, 1);
             SetEndDate(2024, 12, 31);
    -        _symbol = AddEquity("AAPL").Symbol;
    -        // Subscribe to lobbying data for AAPL to generate trade signal
    -        _datasetSymbol = AddData<QuiverLobbyings>(_symbol).Symbol;
    -
    -        // history request
    -        var history = History<QuiverLobbyings>(new[] {_datasetSymbol}, 10, Resolution.Daily);
    +        _equity = AddEquity("AAPL", Resolution.Daily);
    +        // Subscribe to lobbying data for AAPL to generate the trade signal.
    +        _datasetSymbol = AddData<QuiverLobbyings>(_equity.Symbol).Symbol;
    +        // Request recent history to confirm the custom data subscription.
    +        var history = History<QuiverLobbyings>([_datasetSymbol], 10, Resolution.Daily);
             Debug($"We got {history.Count()} items from historical data request of {_datasetSymbol}.");
         }
     
    -    public override void OnData(Slice slice)
    +    public override void OnData(Slice data)
         {
    -        // Trade only base on lobbying data
    -        foreach (var kvp in slice.Get<QuiverLobbyings>())
    +        // Trade only based on lobbying data.
    +        foreach (var kvp in data.Get<QuiverLobbyings>())
             {
                 var lobbyings = kvp.Value;
    -            // Buy if over 50000 lobbying amount, suggesting a favored political prospect and sentiment
    -            if (lobbyings.Any(lobbying => ((QuiverLobbying) lobbying).Amount >= 50000m))
    +            // Buy when lobbying amount is over 50000, suggesting favorable political sentiment.
    +            if (lobbyings.Any(lobbying => ((QuiverLobbying) lobbying).Amount > 50000m))
                 {
    -                SetHoldings(_symbol, 1);
    +                SetHoldings(_equity.Symbol, 1);
                 }
    -            // Sell if below 10000 lobbying amount, suggesting a less favorable political prospect and sentiment
    -            else if (lobbyings.Any(lobbying => ((QuiverLobbying) lobbying).Amount <= 10000m))
    +            // Sell when lobbying amount is below 10000, suggesting less favorable political sentiment.
    +            else if (lobbyings.Any(lobbying => ((QuiverLobbying) lobbying).Amount < 10000m))
                 {
    -                SetHoldings(_symbol, -1);
    +                SetHoldings(_equity.Symbol, -1);
                 }
             }
         }
    @@ -265575,7 +355961,7 @@ 

    Data Summary

    Start Date - 25 April 2014 + April 25 2014* @@ -265612,6 +355998,16 @@

    Data Summary

    +

    + Before May 14 2021, this dataset included legacy data without all the information. Only the following are valid: + + price_per_share, shares, and shares_owned_following + + + PricePerShare, Shares, and SharesOwnedFollowing + + . +

    @@ -266011,61 +356407,60 @@

    class QuiverInsiderTradingAlgorithm(QCAlgorithm): - - def initialize(self): + + def initialize(self) -> None: self.set_start_date(2024, 9, 1) self.set_end_date(2024, 12, 31) - self._symbol = self.add_equity("AAPL", Resolution.DAILY).symbol - # Subscribe to insider trade data for AAPL to generate trade signal - self.dataset_symbol = self.add_data(QuiverInsiderTrading, self._symbol).symbol - - # history request - history = self.history(self.dataset_symbol, 10, Resolution.DAILY) - self.debug(f"We got {len(history)} items from historical data request of {self.dataset_symbol}.") + self._equity = self.add_equity("AAPL", Resolution.DAILY) + # Subscribe to insider trade data for AAPL to generate trade signals. + self._dataset_symbol = self.add_data(QuiverInsiderTrading, self._equity).symbol + # Warm up the custom data subscription with recent history. + history = self.history(self._dataset_symbol, 10, Resolution.DAILY) + self.debug(f"We got {len(history)} items from historical data request of {self._dataset_symbol}.") - def on_data(self, slice: Slice) -> None: - # Trade only base on insider trade data - for insider_trades in slice.Get(QuiverInsiderTrading).values(): + def on_data(self, data: Slice) -> None: + # Trade only from insider trade data. + for insider_trades in data.get(QuiverInsiderTrading).values(): for insider_trade in insider_trades: - # Any buy insider trade will result in buying, assuming insider have confidence in stock price with more informed information and projection + # Buy when insiders purchase shares because it may signal confidence in future prices. if insider_trade.shares > 0: - self.set_holdings(self._symbol, 1) - # Any sell insider trade will result in liquidation, assuming insiders believe the stock price has reached maximum or poor future confidence + self.set_holdings(self._equity, 1) + # Liquidate when insiders sell shares because it may signal weaker future confidence. else: - self.liquidate(self._symbol)

    + self.liquidate(self._equity)
    public class QuiverInsiderTradingAlgorithm : QCAlgorithm
     {
    -    private Symbol _symbol, _datasetSymbol;
    +    private Equity _equity;
    +    private Symbol _datasetSymbol;
     
         public override void Initialize()
         {
             SetStartDate(2024, 9, 1);
             SetEndDate(2024, 12, 31);
    -        _symbol = AddEquity("AAPL").Symbol;
    -        // Subscribe to insider trade data for AAPL to generate trade signal
    -        _datasetSymbol = AddData<QuiverInsiderTrading>(_symbol).Symbol;
    -
    -        // history request
    -        var history = History<QuiverInsiderTrading>(new[] {_datasetSymbol}, 10, Resolution.Daily);
    +        _equity = AddEquity("AAPL", Resolution.Daily);
    +        // Subscribe to insider trade data for AAPL to generate trade signals.
    +        _datasetSymbol = AddData<QuiverInsiderTrading>(_equity.Symbol).Symbol;
    +        // Warm up the custom data subscription with recent history.
    +        var history = History<QuiverInsiderTrading>([_datasetSymbol], 10, Resolution.Daily);
             Debug($"We got {history.Count()} items from historical data request of {_datasetSymbol}.");
         }
     
    -    public override void OnData(Slice slice)
    +    public override void OnData(Slice data)
         {
    -        // Trade only base on insider trade data
    -        foreach (var kvp in slice.Get<QuiverInsiderTrading>())
    +        // Trade only from insider trade data.
    +        foreach (var kvp in data.Get<QuiverInsiderTrading>())
             {
                 foreach (QuiverInsiderTrading insiderTrade in kvp.Value)
                 {
    -                // Any buy insider trade will result in buying, assuming insider have confidence in stock price with more informed information and projection
    +                // Buy when insiders purchase shares because it may signal confidence in future prices.
                     if (insiderTrade.Shares > 0)
                     {
    -                    SetHoldings(_symbol, 1);
    +                    SetHoldings(_equity.Symbol, 1);
                     }
    -                // Any sell insider trade will result in liquidation, assuming insiders believe the stock price has reached maximum or poor future confidence
    +                // Liquidate when insiders sell shares because it may signal weaker future confidence.
                     else
                     {
    -                    Liquidate(_symbol);
    +                    Liquidate(_equity.Symbol);
                     }
                 }
             }
    @@ -266766,57 +357161,50 @@ 

    self.set_start_date(2024, 9, 1) self.set_end_date(2024, 12, 31) self.set_cash(100000) - - # Requesting data per underlying data to subscribe to updated congress members' trade information - aapl = self.add_equity("AAPL", Resolution.DAILY).symbol - quiver_congress_symbol = self.add_data(QuiverCongress, aapl).symbol - - # Historical data + # Subscribe to Congress trading updates for the underlying equity. + equity = self.add_equity("AAPL", Resolution.DAILY) + quiver_congress_symbol = self.add_data(QuiverCongress, equity).symbol + # Warm up the custom dataset with recent Congress trading history. history = self.history(QuiverCongress, quiver_congress_symbol, 60, Resolution.DAILY) - self.debug(f"We got {len(history)} items from our history request"); - - def on_data(self, slice: Slice) -> None: - congress_by_symbol = slice.Get(QuiverCongress) + self.debug(f"We got {len(history)} items from our history request") - # Determine net direction of Congress trades for each security to estimate the sentiment direction due to political factor + def on_data(self, data: Slice) -> None: + congress_by_symbol = data.get(QuiverCongress) + # Estimate sentiment for each security from the net direction of Congress trades. net_quantity_by_symbol = {} for symbol, points in congress_by_symbol.items(): symbol = symbol.underlying if symbol not in net_quantity_by_symbol: net_quantity_by_symbol[symbol] = 0 - # Voting weight is by order size + # Weight each vote by order size. for point in points: net_quantity_by_symbol[symbol] += (1 if point.transaction == OrderDirection.BUY else -1) * point.amount - for symbol, net_quantity in net_quantity_by_symbol.items(): if net_quantity == 0: self.liquidate(symbol) continue - # Buy when Congress members have bought, short otherwise + # Follow net Congress buying direction and short net selling direction. self.set_holdings(symbol, 1 if net_quantity > 0 else -1)

    public class QuiverCongressDataAlgorithm : QCAlgorithm
     {
    +
         public override void Initialize()
         {
             SetStartDate(2024, 9, 1);
             SetEndDate(2024, 12, 31);
             SetCash(100000);
    -        
    -        // Requesting data per underlying data to subscribe to updated congress members' trade information
    -        var aapl = AddEquity("AAPL", Resolution.Daily).Symbol;
    -        var quiverCongressSymbol = AddData<QuiverCongress>(aapl).Symbol;
    -
    -        // Historical data
    +        // Subscribe to Congress trading updates for the underlying equity.
    +        var equity = AddEquity("AAPL", Resolution.Daily);
    +        var quiverCongressSymbol = AddData<QuiverCongress>(equity).Symbol;
    +        // Warm up the custom dataset with recent Congress trading history.
             var history = History<QuiverCongress>(quiverCongressSymbol, 60, Resolution.Daily);
             Debug($"We got {history.Count()} items from our history request");
         }
     
    -    
    -    public override void OnData(Slice slice)
    +    public override void OnData(Slice data)
         {
    -        var congressBySymbol = slice.Get<QuiverCongress>();
    -        
    -        // Determine net direction of Congress trades for each security to estimate the sentiment direction due to political factor
    +        var congressBySymbol = data.Get<QuiverCongress>();
    +        // Estimate sentiment for each security from the net direction of Congress trades.
             var netQuantityBySymbol = new Dictionary<Symbol, decimal>();
             foreach (var (s, points) in congressBySymbol)
             {
    @@ -266825,13 +357213,12 @@ 

    { netQuantityBySymbol[symbol] = 0m; } - // Voting weight is by order size - foreach(QuiverCongressDataPoint point in points) + // Weight each vote by order size. + foreach (QuiverCongressDataPoint point in points) { netQuantityBySymbol[symbol] += (point.Transaction == OrderDirection.Buy ? 1 : -1) * (point.Amount ?? 0m); } } - foreach (var (symbol, netQuantity) in netQuantityBySymbol) { if (netQuantity == 0) @@ -266839,7 +357226,7 @@

    Liquidate(symbol); continue; } - // Buy when Congress members have bought, short otherwise + // Follow net Congress buying direction and short net selling direction. SetHoldings(symbol, netQuantity > 0 ? 1 : -1); } } @@ -266861,7 +357248,9 @@

    self.set_start_date(2024, 9, 1) self.set_end_date(2024, 12, 31) self.set_cash(100000) - # Filter according to QuiverCongress data + self.settings.seed_initial_prices = True + self.universe_settings.minimum_time_in_universe = timedelta(days=7) + # Add the Quiver Congress universe. self.add_universe(QuiverQuantCongressUniverse, "QuiverQuantCongresssUniverse", Resolution.DAILY, self.universe_selection) self.add_alpha(CongressAlphaModel()) self.set_portfolio_construction(InsightWeightingPortfolioConstructionModel(lambda time: None)) @@ -266869,71 +357258,56 @@

    self.set_execution(ImmediateExecutionModel()) def universe_selection(self, alt_coarse: List[QuiverQuantCongressUniverse]) -> List[Symbol]: - # Only include the ones with large size buy, since they are estimated to be materially confident to go up - return [d.symbol for d in alt_coarse if d.amount > 10_000 and d.transaction == OrderDirection.BUY] - + # Return all symbols from the Quiver Congress universe. + return [d.symbol for d in alt_coarse] + class CongressAlphaModel(AlphaModel): - - symbol_data_by_symbol = {} - insight_by_symbol = {} + _securities = [] + _insight_by_asset = {} def update(self, algorithm: QCAlgorithm, slice: Slice) -> List[Insight]: - congress_by_symbol = slice.get(QuiverCongress) - if congress_by_symbol: - self.insight_by_symbol.clear() - # Determine net direction of Congress trades for each security to estimate the sentiment direction due to political factor - net_quantity_by_symbol = {} - for symbol, points in congress_by_symbol.items(): - symbol = symbol.underlying - if symbol not in net_quantity_by_symbol: - net_quantity_by_symbol[symbol] = 0 - # Voting weight is by order size - for point in points: - net_quantity_by_symbol[symbol] += (1 if point.transaction == OrderDirection.BUY else -1) * point.amount - - for symbol, net_quantity in net_quantity_by_symbol.items(): - # Buy when Congress members have bought, as they may have advance information to be confident to the return direction - if net_quantity > 0 and not algorithm.portfolio[symbol].is_long: + congress_data_by_asset = slice.get(QuiverCongress) + if congress_data_by_asset: + self._insight_by_asset.clear() + net_quantity_by_asset = {} + for congress_data_symbol, congress_data in congress_data_by_asset.items(): + security = algorithm.securities[congress_data_symbol.underlying] + if security not in net_quantity_by_asset: + net_quantity_by_asset[security] = 0 + for point in congress_data: + net_quantity_by_asset[security] += (1 if point.transaction == OrderDirection.BUY else -1) * point.amount + for security, net_quantity in net_quantity_by_asset.items(): + # Emit an up insight when Congress members are net buyers. + if net_quantity > 0 and not security.holdings.is_long: direction = InsightDirection.UP - # Short sell when Congress members have sold - elif net_quantity < 0 and not algorithm.portfolio[symbol].is_short: + # Emit a down insight when Congress members are net sellers. + elif net_quantity < 0 and not security.holdings.is_short: direction = InsightDirection.DOWN else: continue - self.insight_by_symbol[symbol] = Insight.price(symbol, timedelta(7), direction, weight=0.5) + self._insight_by_asset[security] = Insight.price(security, timedelta(7), direction, weight=0.5) # To avoid error messages, emit the insights when the algorithm has a price for the asset. return [ - self.insight_by_symbol.pop(symbol) - for symbol, i in list(self.insight_by_symbol.items()) - if algorithm.securities[symbol].price + self._insight_by_asset.pop(security) + for security in list(self._insight_by_asset.keys()) + if security.price ] def on_securities_changed(self, algorithm: QCAlgorithm, changes: SecurityChanges) -> None: for security in changes.added_securities: - symbol = security.symbol - self.symbol_data_by_symbol[symbol] = SymbolData(algorithm, symbol) - + self._securities.append(security) + # Add the Quiver Congress subscription for the security. + security.congress_data = algorithm.add_data(QuiverCongress, security, Resolution.DAILY) + history = algorithm.history(QuiverCongress, security.congress_data, 14, Resolution.DAILY) + algorithm.debug(f"We got {len(history)} items from our history request for {security}") for security in changes.removed_securities: - symbol_data = self.symbol_data_by_symbol.pop(security.symbol, None) - if symbol_data: - symbol_data.dispose() - - -class SymbolData: - - def __init__(self, algorithm: QCAlgorithm, symbol: Symbol): - self.algorithm = algorithm - - # Requesting data per underlying data to subscribe to updated congress members' trade information - self.quiver_congress_symbol = algorithm.add_data(QuiverCongress, symbol).symbol - - # Historical data - history = algorithm.history(self.quiver_congress_symbol, 14, Resolution.DAILY) - - def dispose(self) -> None: - # Unsubscribe from Quiver Congress feed for this security to release computational resources - self.algorithm.remove_security(self.quiver_congress_symbol)

    + if security in self._securities: + if security in self._insight_by_asset: + self._insight_by_asset.pop(security) + # Remove the Quiver Congress subscription for the security. + algorithm.remove_security(security.congress_data) + self._securities.remove(security)
    public class QuiverCongressDataAlgorithm : QCAlgorithm
     {
         public override void Initialize()
    @@ -266941,14 +357315,10 @@ 

    SetStartDate(2024, 9, 1); SetEndDate(2024, 12, 31); SetCash(100000); - // Filter according to QuiverCongress data - AddUniverse<QuiverQuantCongressUniverse>("QuiverQuantCongresssUniverse", Resolution.Daily, altCoarse => - { - // Only include the ones with large size buy, since they are estimated to be materially confident to go up - return from d in altCoarse.OfType<QuiverCongressDataPoint>() - where d.Amount > 10000 && d.Transaction == OrderDirection.Buy - select d.Symbol; - }); + Settings.SeedInitialPrices = true; + UniverseSettings.MinimumTimeInUniverse = TimeSpan.FromDays(7); + // Add the Quiver Congress universe. + AddUniverse<QuiverQuantCongressUniverse>(altCoarse => altCoarse.Select(d => d.Symbol)); AddAlpha(new CongressAlphaModel()); SetPortfolioConstruction(new InsightWeightingPortfolioConstructionModel(time => null)); AddRiskManagement(new NullRiskManagementModel()); @@ -266958,41 +357328,41 @@

    public class CongressAlphaModel : AlphaModel { - private Dictionary<Symbol, SymbolData> _symbolDataBySymbol = new (); - private Dictionary<Symbol, Insight> _insightBySymbol = new (); + private readonly List<Security> _securities = []; + private readonly Dictionary<Security, Insight> _insightByAsset = []; public override IEnumerable<Insight> Update(QCAlgorithm algorithm, Slice slice) { - var congressBySymbol = slice.Get<QuiverCongress>(); - if (congressBySymbol.Count > 0) + var congressDataByAsset = slice.Get<QuiverCongress>(); + if (congressDataByAsset.Count > 0) { - _insightBySymbol.Clear(); - // Determine net direction of Congress trades for each security to estimate the sentiment direction due to political factor - var netQuantityBySymbol = new Dictionary<Symbol, decimal>(); - foreach (var (s, points) in congressBySymbol) + _insightByAsset.Clear(); + // Aggregate the net Congress trade quantity for each security. + var netQuantityByAsset = new Dictionary<Security, decimal>(); + foreach (var (congressDataSymbol, congressData) in congressDataByAsset) { - var symbol = s.Underlying; - if (!netQuantityBySymbol.ContainsKey(symbol)) + var security = algorithm.Securities[congressDataSymbol.Underlying]; + if (!netQuantityByAsset.ContainsKey(security)) { - netQuantityBySymbol[symbol] = 0m; + netQuantityByAsset[security] = 0m; } - // Voting weight is by order size - foreach(QuiverCongressDataPoint point in points) + // Weight each transaction by its reported amount. + foreach (QuiverCongressDataPoint point in congressData) { - netQuantityBySymbol[symbol] += (point.Transaction == OrderDirection.Buy ? 1 : -1) * (point.Amount ?? 0m); + netQuantityByAsset[security] += (point.Transaction == OrderDirection.Buy ? 1 : -1) * (point.Amount ?? 0m); } } - foreach (var (symbol, netQuantity) in netQuantityBySymbol) + foreach (var (security, netQuantity) in netQuantityByAsset) { - // Buy when Congress members have bought, as they may have advance information to be confident to the return direction + // Emit an up insight when Congress members are net buyers. InsightDirection direction; - if (netQuantity > 0 && !algorithm.Portfolio[symbol].IsLong) + if (netQuantity > 0 && !security.Holdings.IsLong) { direction = InsightDirection.Up; } - // Short sell when Congress members have sold - else if (netQuantity < 0 && !algorithm.Portfolio[symbol].IsShort) + // Emit a down insight when Congress members are net sellers. + else if (netQuantity < 0 && !security.Holdings.IsShort) { direction = InsightDirection.Down; } @@ -267000,16 +357370,17 @@

    { continue; } - _insightBySymbol[symbol] = Insight.Price(symbol, TimeSpan.FromDays(7), direction, weight: 0.5); + _insightByAsset[security] = Insight.Price(security.Symbol, TimeSpan.FromDays(7), direction, weight: 0.5); } } - return _insightBySymbol.Keys - .ToList() // snapshot keys - .Where(symbol => algorithm.Securities[symbol].Price != 0m) - .Select(symbol => { - var val = _insightBySymbol[symbol]; // capture value - _insightBySymbol.Remove(symbol); // remove - return val; + // To avoid error messages, emit the insights when the algorithm has a price for the asset. + return _insightByAsset.Keys + .ToList() // snapshot keys + .Where(security => security.Price != 0m) + .Select(security => { + var insight = _insightByAsset[security]; // capture value + _insightByAsset.Remove(security); // remove + return insight; }) .ToList(); } @@ -267018,44 +357389,26 @@

    { foreach (var security in changes.AddedSecurities) { - var symbol = security.Symbol; - _symbolDataBySymbol.Add(symbol, new SymbolData(algorithm, symbol)); + _securities.Add(security); + // Add the Quiver Congress subscription for the security. + var congressData = algorithm.AddData<QuiverCongress>(security.Symbol, Resolution.Daily); + security.Set("CongressData", congressData); + var history = algorithm.History<QuiverCongress>(congressData.Symbol, 14, Resolution.Daily); + algorithm.Debug($"We got {history.Count()} items from our history request for {security}"); } foreach (var security in changes.RemovedSecurities) { - var symbol = security.Symbol; - if (_symbolDataBySymbol.ContainsKey(symbol)) + if (_securities.Contains(security)) { - _symbolDataBySymbol[symbol].dispose(); - _symbolDataBySymbol.Remove(symbol); + _insightByAsset.Remove(security); + // Remove the Quiver Congress subscription for the security. + var congressData = security.Get<Security>("CongressData"); + algorithm.RemoveSecurity(congressData.Symbol); + _securities.Remove(security); } } } -} - -public class SymbolData -{ - private Symbol _quiverCongressSymbol; - private QCAlgorithm _algorithm; - - public SymbolData(QCAlgorithm algorithm, Symbol symbol) - { - _algorithm = algorithm; - - // Requesting data per underlying data to subscribe to updated congress members' trade information - _quiverCongressSymbol = algorithm.AddData<QuiverCongress>(symbol).Symbol; - - // Historical data - var history = algorithm.History<QuiverCongress>(_quiverCongressSymbol, 60, Resolution.Daily); - algorithm.Debug($"We got {history.Count()} items from our history request for {symbol} Quiver Congress data"); - } - - public void dispose() - { - // Unsubscribe from Quiver Congress feed for this security to release computational resources - _algorithm.RemoveSecurity(_quiverCongressSymbol); - } }

    @@ -267635,847 +357988,7 @@

    Remove Subscriptions

    RemoveSecurity(_datasetSymbol);

    - If you subscribe to US Government Contracts data for assets in a dynamic universe, remove the dataset subscription when the asset leaves your universe. To view a common design pattern, see - - Track Security Changes - - . -

    - - - -

    Example Applications

    - - -

    - The Quiver Quantitative US Government Contracts dataset enables you to create strategies using the latest information on government contracts activity. Examples include the following strategies: -

    -
      -
    • - Buying securities that have received the most new government contract awards over the last month -
    • -
    • - Trading securities that have had the biggest change in government contracts awards over the last year -
    • -
    -

    - Classic Algorithm Example -

    -

    - The following example algorithm buys Apple stock when they receive a new government contract worth over $50K. If they receive a new contract worth under $10K, the algorithm short sells Apple. -

    -
    -
    from AlgorithmImports import *
    -
    -
    -class QuiverGovernmentContractAlgorithm(QCAlgorithm):
    -
    -    def initialize(self) -> None:
    -        self.set_start_date(2024, 9, 1)
    -        self.set_end_date(2024, 12, 31)
    -        self.aapl = self.add_equity("AAPL", Resolution.DAILY).symbol
    -        # Subscribe to government contract data for AAPL to generate trade signal
    -        self.dataset_symbol = self.add_data(QuiverGovernmentContract, self.aapl).symbol
    -
    -        # history request
    -        history = self.history(self.dataset_symbol, 10, Resolution.DAILY)
    -        self.debug(f"We got {len(history)} items from historical data request of {self.dataset_symbol}.")
    -
    -    def on_data(self, slice: Slice) -> None:
    -        # Trade only base on government contract data
    -        for gov_contracts in slice.Get(QuiverGovernmentContract).values():
    -            # Buy if over 50000 government contract amount, suggesting a large income
    -            if any([gov_contract.amount > 50000 for gov_contract in gov_contracts]):
    -                self.set_holdings(self.aapl, 1)
    -            # Sell if below 10000 government contract amount, suggesting a smaller than usual income
    -            elif any([gov_contract.amount < 10000 for gov_contract in gov_contracts]):
    -                self.set_holdings(self.aapl, -1)
    -
    public class QuiverGovernmentContractAlgorithm : QCAlgorithm
    -{
    -    private Symbol _symbol, _datasetSymbol;
    -
    -    public override void Initialize()
    -    {
    -        SetStartDate(2024, 9, 1);
    -        SetEndDate(2024, 12, 31);
    -        _symbol = AddEquity("AAPL").Symbol;
    -        // Subscribe to government contract data for AAPL to generate trade signal
    -        _datasetSymbol = AddData<QuiverGovernmentContract>(_symbol).Symbol;
    -
    -        // history request
    -        var history = History<QuiverGovernmentContract>(new[] {_datasetSymbol}, 10, Resolution.Daily);
    -        Debug($"We got {history.Count()} items from historical data request of {_datasetSymbol}.");
    -    }
    -
    -    public override void OnData(Slice slice)
    -    {
    -        // Trade only base on government contract data
    -        foreach (var kvp in slice.Get<QuiverGovernmentContract>())
    -        {
    -            // Buy if over 50000 government contract amount, suggesting a large income
    -            if (kvp.Value.Any(x => (int) (x as QuiverGovernmentContract).Amount > 50000m))
    -            {
    -                SetHoldings(_symbol, 1);
    -            }
    -            // Sell if below 10000 government contract amount, suggesting a smaller than usual income
    -            else if (kvp.Value.Any(x => (int) (x as QuiverGovernmentContract).Amount < 10000m))
    -            {
    -                SetHoldings(_symbol, -1);
    -            }
    -        }
    -    }
    -}
    -
    -

    - Framework Algorithm Example -

    -

    - The following example algorithm creates a dynamic universe of US Equities that have just received a government contract worth at least $5K. Each day, it then forms an equal-weighted dollar-neutral portfolio with the 10 companies that received the largest contracts and the 10 companies that received the smallest contracts. -

    -
    -
    from AlgorithmImports import *
    -
    -
    -class QuiverGovernmentContractDataAlgorithm(QCAlgorithm):
    -
    -    def initialize(self) -> None:
    -        self.set_start_date(2024, 9, 1)
    -        self.set_end_date(2024, 12, 31)
    -        self.set_cash(100000)
    -        # Seed the price of each asset with its last known price to avoid trading errors.
    -        self.settings.seed_initial_prices = True
    -        # Filter universe using government contract data
    -        self.add_universe(QuiverGovernmentContractUniverse, self.universe_selection)
    -
    -        # Custom alpha model that emit insights based on updated government contract data
    -        self.add_alpha(QuiverGovernmentContractAlphaModel())
    -        
    -        # Invest equally to evenly dissipate the capital concentration risk
    -        self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
    -        
    -        self.set_execution(ImmediateExecutionModel())
    -        
    -    def universe_selection(self, data: List[QuiverGovernmentContractUniverse]) -> List[Symbol]:
    -        gov_contract_data_by_symbol = {}
    -
    -        for datum in data:
    -            symbol = datum.symbol
    -            
    -            if symbol not in gov_contract_data_by_symbol:
    -                gov_contract_data_by_symbol[symbol] = []
    -            gov_contract_data_by_symbol[symbol].append(datum)
    -        
    -        # Only select the stocks with over 5000 government contracts, which is considered material information
    -        return [symbol for symbol, d in gov_contract_data_by_symbol.items()
    -                if sum([x.amount for x in d]) > 5000]
    -        
    -class QuiverGovernmentContractAlphaModel(AlphaModel):
    -    
    -    def __init__(self) -> None:
    -        # A variable to control the rebalancing time
    -        self.last_time = datetime.min
    -        # To hold the government contract dataset symbol for managing subscription
    -        self.dataset_symbol_by_symbol = {}
    -        
    -    def update(self, algorithm: QCAlgorithm, slice: Slice) -> List[Insight]:
    -        if self.last_time > algorithm.time: return []
    -        
    -        # Trade signal only based on government contract data
    -        data_points = slice.Get(QuiverGovernmentContract)
    -        
    -        if not data_points: return []
    -        
    -        gov_contracts = {}
    -        # To aggregate all data per symbol for analysis
    -        for data_point in data_points:
    -            if not algorithm.securities[data_point.key.underlying].price:
    -                continue
    -            if data_point.Key not in gov_contracts:
    -                gov_contracts[data_point.Key] = 0
    -
    -            for gov_contract in data_point.Value:
    -                gov_contracts[data_point.Key] += gov_contract.amount
    -        
    -        # Long the top 10 highest government contract amount, predicting a higher expected income and return
    -        # Short the lowest 10 government contract amount, predicting a lower expected income and return
    -        sorted_by_gov_contracts = sorted(gov_contracts.items(), key=lambda x: x[1])
    -        long_symbols = [x[0].underlying for x in sorted_by_gov_contracts[-10:]]
    -        short_symbols = [x[0].underlying for x in sorted_by_gov_contracts[:10]]
    -        
    -        insights = []
    -        for symbol in long_symbols:
    -            insights.append(Insight.price(symbol, Expiry.END_OF_DAY, InsightDirection.UP))        
    -        for symbol in short_symbols:
    -            insights.append(Insight.price(symbol, Expiry.END_OF_DAY, InsightDirection.DOWN))
    -        
    -        self.last_time = Expiry.END_OF_DAY(algorithm.Time)
    -        
    -        return insights
    -        
    -    def on_securities_changed(self, algorithm: QCAlgorithm, changes: SecurityChanges) -> None:
    -        for security in changes.added_securities:
    -            # Requesting government contract data for trade signal generation
    -            symbol = security.symbol
    -            dataset_symbol = algorithm.add_data(QuiverGovernmentContract, symbol).symbol
    -            self.dataset_symbol_by_symbol[symbol] = dataset_symbol
    -            # Historical Data
    -            history = algorithm.history(dataset_symbol, 10, Resolution.DAILY)
    -
    -        for security in changes.removed_securities:
    -            dataset_symbol = self.dataset_symbol_by_symbol.pop(security.symbol, None)
    -            if dataset_symbol:
    -                # Remove government contract data subscription to release computation resources
    -                algorithm.remove_security(dataset_symbol)
    -
    public class QuiverGovernmentContractFrameworkAlgorithm : QCAlgorithm
    -{
    -    public override void Initialize()
    -    {
    -        SetStartDate(2024, 9, 1);
    -        SetEndDate(2024, 12, 31);
    -        SetCash(100000);
    -        // Seed the price of each asset with its last known price to avoid trading errors.
    -        Settings.SeedInitialPrices = true;
    -        // Filter universe using government contract data
    -        AddUniverse<QuiverGovernmentContractUniverse>(data =>
    -        {
    -            var govContractDataBySymbol = new Dictionary<Symbol, List<QuiverGovernmentContractUniverse>>();
    -
    -            foreach (var datum in data.OfType<QuiverGovernmentContractUniverse>())
    -            {
    -                var symbol = datum.Symbol;
    -
    -                if (!govContractDataBySymbol.ContainsKey(symbol))
    -                {
    -                    govContractDataBySymbol.Add(symbol, new List<QuiverGovernmentContractUniverse>());
    -                }
    -                govContractDataBySymbol[symbol].Add(datum);
    -            }
    -
    -            // Only select the stocks with over 5000 government contracts, which is considered material information
    -            return from kvp in govContractDataBySymbol
    -                where kvp.Value.Sum(x => x.Amount) > 5000m
    -                select kvp.Key;
    -        });
    -
    -        // Custom alpha model that emit insights based on updated government contract data
    -        AddAlpha(new QuiverGovernmentContractAlphaModel());
    -
    -        // Invest equally to evenly dissipate the capital concentration risk
    -        SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel());
    -        
    -        SetExecution(new ImmediateExecutionModel());
    -    }
    -}
    -
    -public class QuiverGovernmentContractAlphaModel: AlphaModel
    -{
    -    // A variable to control the rebalancing time
    -    private DateTime _time;
    -    // To hold the government contract dataset symbol for managing subscription
    -    private Dictionary<Symbol, Symbol> _datasetSymbolBySymbol = new();
    -    
    -    public QuiverGovernmentContractAlphaModel()
    -    {
    -        _time = DateTime.MinValue;
    -    }
    -    
    -    public override IEnumerable<Insight> Update(QCAlgorithm algorithm, Slice slice)
    -    {
    -        if (_time > algorithm.Time) return new List<Insight>();
    -        
    -        // Trade signal only based on government contract data
    -        var dataPoints = slice.Get<QuiverGovernmentContract>();
    -        
    -        if (dataPoints.IsNullOrEmpty()) return new List<Insight>();
    -
    -        // To aggregate all data per symbol for analysis
    -        var govContracts = dataPoints
    -            .Where(kvp => algorithm.Securities[kvp.Key.Underlying].Price != 0)
    -            .ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Sum(x => ((QuiverGovernmentContract)x).Amount));
    -        
    -        // Long the top 10 highest government contract amount, predicting a higher expected income and return
    -        // Short the lowest 10 government contract amount, predicting a lower expected income and return
    -        var sortedByGovContract = from kvp in govContracts
    -                        orderby kvp.Value descending
    -                        select kvp.Key.Underlying;
    -        var longSymbols = sortedByGovContract.Take(10).ToList();
    -        var shortSymbols = sortedByGovContract.TakeLast(10).ToList();
    -        
    -        var insights = new List<Insight>();
    -        insights.AddRange(longSymbols.Select(symbol => 
    -            new Insight(symbol, Expiry.EndOfDay, InsightType.Price, InsightDirection.Up)));
    -        insights.AddRange(shortSymbols.Select(symbol => 
    -            new Insight(symbol, Expiry.EndOfDay, InsightType.Price, InsightDirection.Down)));
    -        
    -        _time = Expiry.EndOfDay(algorithm.Time);
    -        
    -        return insights;
    -    }
    -    
    -    public override void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes)
    -    {
    -        foreach (var security in changes.AddedSecurities)
    -        {
    -            // Requesting government contract data for trade signal generation
    -            var symbol = security.Symbol;
    -            var datasetSymbol = algorithm.AddData<QuiverGovernmentContract>(symbol).Symbol;
    -            _datasetSymbolBySymbol.Add(symbol, datasetSymbol);
    -            // History request
    -            var history = algorithm.History<QuiverGovernmentContract>(datasetSymbol, 10, Resolution.Daily);
    -        }
    -        
    -        foreach (var security in changes.RemovedSecurities)
    -        {
    -            var symbol = security.Symbol;
    -            if (_datasetSymbolBySymbol.ContainsKey(symbol))
    -            {
    -                // Remove government contract data subscription to release computation resources
    -                _datasetSymbolBySymbol.Remove(symbol, out var datasetSymbol);
    -                algorithm.RemoveSecurity(datasetSymbol);
    -            }
    -        }
    -    }
    -}
    -
    -

    - Research Example -

    -

    - The following example lists all US Equities with Government contracts in the past year. -

    -
    -
    #r "../QuantConnect.DataSource.QuiverGovernmentContracts.dll"
    -using QuantConnect.DataSource;
    -
    -// Requesting data
    -var aapl = qb.AddEquity("AAPL", Resolution.Daily).Symbol;
    -var symbol = qb.AddData<QuiverGovernmentContract>(aapl).Symbol;
    -
    -// Historical data
    -var history = qb.History<QuiverGovernmentContract>(symbol, 360, Resolution.Daily);
    -foreach (var contracts in history)
    -{
    -    foreach (QuiverGovernmentContract contract in contracts)
    -    {
    -        Console.WriteLine($"{contract.Symbol} amount at {contract.EndTime}: {contract.Amount}");
    -    }
    -}
    -
    -// Add Universe Selection
    -IEnumerable<Symbol> UniverseSelection(IEnumerable<BaseData> altCoarse)
    -{
    -    return from d in altCoarse.OfType<QuiverGovernmentContractUniverse>()
    -        select d.Symbol;
    -}
    -var universe = qb.AddUniverse<QuiverGovernmentContractUniverse<(UniverseSelection);
    -
    -// Historical Universe data
    -var universeHistory = qb.UniverseHistory(universe, qb.Time.AddDays(-360), qb.Time);
    -foreach (var contracts in universeHistory)
    -{
    -    foreach (QuiverGovernmentContractUniverse contract in contracts)
    -    {
    -        Console.WriteLine($"{contract.Symbol} amount at {contract.EndTime}: {contract.Amount}");
    -    }
    -}
    -
    qb = QuantBook()
    -
    -# Requesting Data
    -aapl = qb.add_equity("AAPL", Resolution.DAILY).symbol
    -symbol = qb.add_data(QuiverGovernmentContract, aapl).symbol
    -
    -# Historical data
    -history = qb.history(QuiverGovernmentContract, symbol, 360, Resolution.DAILY)
    -for (symbol, time), contracts in history.items():
    -    for contract in contracts:
    -        print(f"{contract.symbol} amount at {contract.end_time}: {contract.amount}")
    -
    -# Add Universe Selection
    -def universe_selection(alt_coarse: List[QuiverGovernmentContractUniverse]) -> List[Symbol]:
    -    return [d.symbol for d in alt_coarse]
    -
    -universe = qb.add_universe(QuiverGovernmentContractUniverse, universe_selection)
    -        
    -# Historical Universe data
    -universe_history = qb.universe_history(universe, qb.time-timedelta(360), qb.time)
    -for (_, time), contracts in universe_history.items():
    -    for contract in contracts:
    -        print(f"{contract.symbol} amount at {contract.end_time}: {contract.amount}")
    -
    - - - -

    Data Point Attributes

    - - -

    - The US Government Contracts dataset provides - - QuiverGovernmentContract - - and - - QuiverGovernmentContractUniverse - - objects. -

    -

    - QuiverGovernmentContract -

    -

    - - QuiverGovernmentContract - - objects have the following attributes: -

    -
    -
    -

    - QuiverGovernmentContractUniverse -

    -

    - - QuiverGovernmentContractUniverse - - objects have the following attributes: -

    -
    -
    - - - -

     

    - -
    -
    -

    Quiver Quantitative

    -

    WallStreetBets

    -
    -
    -

    Introduction

    - - -

    - The WallStreetBets dataset by Quiver Quantitative tracks daily mentions of different equities on Reddit’s popular WallStreetBets forum. The data covers 6,000 Equities, starts in August 2018, and is delivered on a daily frequency. The dataset is created by scraping the daily discussion threads on r/WallStreetBets and parsing the comments for ticker mentions. -

    -

    - This dataset depends on the - - US Equity Security Master - - dataset because the US Equity Security Master dataset contains information on splits, dividends, and symbol changes. -

    -

    - For more information about the WallStreetBets dataset, including CLI commands and pricing, see the - - dataset listing - - . -

    -

    -

    - - - -

    About the Provider

    - - -

    - - Quiver Quantitative - - was founded by two college students in February 2020 with the goal of bridging the information gap between Wall Street and non-professional investors. Quiver allows retail investors to tap into the power of big data and have access to actionable, easy to interpret data that hasn’t already been dissected by Wall Street. -

    - - - -

    Getting Started

    - - -

    - The following snippet demonstrates how to request data from the WallStreetBets dataset: -

    -
    -
    self.aapl = self.add_equity("AAPL", Resolution.DAILY).symbol
    -self.dataset_symbol = self.add_data(QuiverWallStreetBets, self.aapl).symbol
    -
    -self._universe = self.add_universe(QuiverWallStreetBetsUniverse, self.universe_selection)
    -
    _symbol = AddEquity("AAPL", Resolution.Daily).Symbol;
    -_datasetSymbol = AddData<QuiverWallStreetBets>(_symbol).Symbol;
    -
    -_universe = AddUniverse<QuiverWallStreetBetsUniverse>(UniverseSelection);
    -
    - - - -

    Data Summary

    - - -

    - The following table describes the dataset properties: -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - Property - - Value -
    - Start Date - - August 2018 -
    - Asset Coverage - - 6,000 US Equities -
    - Data Density - - Sparse -
    - Resolution - - Daily -
    - Timezone - - UTC -
    - - - -

    Requesting Data

    - - -

    - To add WallStreetBets data to your algorithm, call the - - AddData - - - add_data - - method. Save a reference to the dataset - - Symbol - - so you can access the data later in your algorithm. -

    -
    -
    class QuiverWallStreetBetsDataAlgorithm(QCAlgorithm):
    -    def initialize(self) -> None:
    -        self.set_start_date(2019, 1, 1)
    -        self.set_end_date(2020, 6, 1)
    -        self.set_cash(100000)
    -
    -        self.aapl = self.add_equity("AAPL", Resolution.DAILY).symbol
    -        self.dataset_symbol = self.add_data(QuiverWallStreetBets, self.aapl).symbol
    -
    public class QuiverWallStreetBetsDataAlgorithm : QCAlgorithm
    -{
    -    private Symbol _symbol, _datasetSymbol;
    -
    -    public override void Initialize()
    -    {
    -        SetStartDate(2019, 1, 1);
    -        SetEndDate(2020, 6, 1);
    -        SetCash(100000);
    -        _symbol = AddEquity("AAPL", Resolution.Daily).Symbol;
    -        _datasetSymbol = AddData<QuiverWallStreetBets>(_symbol).Symbol;
    -    }
    -}
    -
    - - - -

    Accessing Data

    - - -

    - To get the current WallStreetBets 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. -

    -
    -
    def on_data(self, slice: Slice) -> None:
    -    if slice.contains_key(self.dataset_symbol):
    -        data_points = slice[self.dataset_symbol]
    -        for data_point in data_points:
    -            self.log(f"{self.dataset_symbol} mentions at {slice.time}: {data_point.mentions}")
    -
    public override void OnData(Slice slice)
    -{
    -    if (slice.ContainsKey(_datasetSymbol))
    -    {
    -        var dataPoints = slice[_datasetSymbol];
    -        foreach (var dataPoint in dataPoints)
    -        {
    -            Log($"{_datasetSymbol} mentions at {slice.Time}: {dataPoint.Mentions}");
    -        }
    -    }
    -}
    -
    -

    - To iterate through all of the dataset objects in the current - - Slice - - , call the - - Get - - - get - - method. -

    -
    -
    def on_data(self, slice: Slice) -> None:
    -    for dataset_symbol, data_points in slice.get(QuiverWallStreetBets).items():
    -        for data_point in data_points:
    -            self.log(f"{dataset_symbol} mentions at {slice.time}: {data_point.mentions}")
    -
    public override void OnData(Slice slice)
    -{
    -    foreach (var kvp in slice.Get<QuiverWallStreetBets>())
    -    {
    -        var datasetSymbol = kvp.Key;
    -        var dataPoints = kvp.Value;
    -        foreach (var dataPoint in dataPoints)
    -        {
    -            Log($"{datasetSymbol} mentions at {slice.Time}: {dataPoint.Mentions}");
    -        }
    -    }
    -}
    -
    - - - -

    Historical Data

    - - -

    - To get historical WallStreetBets data, call the - - History - - - history - - method with the dataset - - Symbol - - . If there is no data in the period you request, the history result is empty. -

    -
    -
    # DataFrame
    -history_df = self.history(self.dataset_symbol, 100, Resolution.DAILY)
    -
    -# Dataset objects
    -history_bars = self.history[QuiverWallStreetBets](self.dataset_symbol, 100, Resolution.DAILY)
    -
    var history = History<QuiverWallStreetBets>(_datasetSymbol, 100, Resolution.Daily);
    -
    -

    - For more information about historical data, see - - History Requests - - . -

    - - - -

    Universe Selection

    - - -

    - To select a dynamic universe of US Equities based on WallStreetBets data, call the - - AddUniverse - - - add_universe - - method with the - - QuiverWallStreetBetsUniverse - - class and a selection function. -

    -
    -
    def initialize(self) -> None:
    -    self.universe = self.add_universe(QuiverWallStreetBetsUniverse, self.universe_selection)
    -        
    -def universe_selection(self, alt_coarse: List[QuiverWallStreetBetsUniverse]) -> List[Symbol]:
    -    return [d.symbol for d in alt_coarse if d.mentions > 100  and d.rank < 100]
    -
    private Universe _universe;
    -public override void Initialize()
    -{
    -    _universe = AddUniverse<QuiverWallStreetBetsUniverse>(altCoarse =>
    -    {
    -        return from d in altCoarse.OfType<QuiverWallStreetBetsUniverse>()
    -            where d.Mentions > 10 && d.Rank > 10 select d.Symbol;
    -    });
    -}
    -
    -

    - For more information about dynamic universes, see - - Universes - - . -

    - - - -

    Universe History

    - - -

    - You can get historical universe data in an algorithm and in the Research Environment. -

    -

    - Historical Universe Data in Algorithms -

    -

    - To get historical universe data in an algorithm, call the - - History - - - history - - method with the - - Universe - - object and the lookback period. If there is no data in the period you request, the history result is empty. -

    -
    -
    var universeHistory = History(_universe, 30, Resolution.Daily);
    -foreach (var bets in universeHistory)
    -{
    -    foreach (QuiverWallStreetBetsUniverse bet in bets)
    -    {
    -        Log($"{bet.Symbol} mentions at {bet.EndTime}: {bet.Mentions}");
    -    }
    -}
    -
    # DataFrame example where the columns are the QuiverWallStreetBetsUniverse attributes: 
    -history_df = self.history(self._universe, 30, Resolution.DAILY, flatten=True)
    -
    -# Series example where the values are lists of QuiverWallStreetBetsUniverse objects: 
    -universe_history = self.history(self._universe, 30, Resolution.DAILY)
    -for (univere_symbol, time), bets in universe_history.items():
    -    for bet in bets:
    -        self.log(f"{bet.symbol} mentions at {bet.end_time}: {bet.mentions}")
    -
    -

    - Historical Universe Data in Research -

    -

    - To get historical universe data in research, call the - - UniverseHistory - - - universe_history - - method with the - - Universe - - object, a start date, and an end date. This method returns the filtered universe. If there is no data in the period you request, the history result is empty. -

    -
    -
    var universeHistory = qb.UniverseHistory(universe, qb.Time.AddDays(-30), qb.Time);
    -{
    -    foreach (QuiverWallStreetBetsUniverse bet in bets)
    -    {
    -        Log($"{bet.Symbol} rank at {bet.EndTime}: {bet.Rank}");
    -    }
    -}
    -
    # DataFrame example where the columns are the QuiverWallStreetBetsUniverse attributes: 
    -history_df = qb.universe_history(universe, qb.time-timedelta(30), qb.time, flatten=True)
    -
    -# Series example where the values are lists of QuiverWallStreetBetsUniverse objects: 
    -universe_history = qb.universe_history(universe, qb.time-timedelta(30), qb.time)
    -for (univere_symbol, time), bets in universe_history.items():
    -    for bet in bets:
    -        print(f"{bet.symbol} rank at {bet.end_time}: {bet.rank}")
    -
    -

    - You can call the - - History - - - history - - method in Research. -

    - - - -

    Remove Subscriptions

    - - -

    - To remove a subscription, call the - - RemoveSecurity - - - remove_security - - method. -

    -
    -
    self.remove_security(self.dataset_symbol)
    -
    RemoveSecurity(_datasetSymbol);
    -
    -

    - If you subscribe to WallStreetBets data for assets in a dynamic universe, remove the dataset subscription when the asset leaves your universe. To view a common design pattern, see + If you subscribe to US Government Contracts data for assets in a dynamic universe, remove the dataset subscription when the asset leaves your universe. To view a common design pattern, see Track Security Changes @@ -268488,393 +358001,359 @@

    Example Applications

    - The WallStreetBets dataset enables you to create strategies using the latest activity on the WallStreetBets daily discussion thread. Examples include the following strategies: + The Quiver Quantitative US Government Contracts dataset enables you to create strategies using the latest information on government contracts activity. Examples include the following strategies:

    • - Trading any security that is being mentioned -
    • -
    • - Trading securities that are receiving more/less mentions than they were previously + Buying securities that have received the most new government contract awards over the last month
    • - Trading the security that is being mentioned the most/least for the day + Trading securities that have had the biggest change in government contracts awards over the last year

    Classic Algorithm Example

    - The following example algorithm creates a dynamic universe of US Equities based on daily WallStreetBets data. When a security is mentioned on r/WallStreetBets more than five times in a day, the algorithm buys the security. When a security is mentioned five time in a day or less, the algorithm short sells the security. + The following example algorithm buys Apple stock when they receive a new government contract worth over $50K. If they receive a new contract worth under $10K, the algorithm short sells Apple.

    from AlgorithmImports import *
     
     
    -class QuiverWallStreetBetsDataAlgorithm(QCAlgorithm):
    +class QuiverGovernmentContractAlgorithm(QCAlgorithm):
    +
         def initialize(self) -> None:
             self.set_start_date(2024, 9, 1)
             self.set_end_date(2024, 12, 31)
    -        self.set_cash(100000)
    -        # Seed the price of each asset with its last known price to avoid trading errors.
    -        self.settings.seed_initial_prices = True
    -        self.universe_settings.resolution = Resolution.DAILY
    -        # Filter using wall street bet insights
    -        self._universe = self.add_universe(QuiverWallStreetBetsUniverse, self.universe_selection)
    -
    -    def on_data(self, slice: Slice) -> None:
    -        points = slice.Get(QuiverWallStreetBets)
    -        for point in points.Values:
    -            symbol = point.symbol.underlying
    -            security = self.securities[symbol]
    -            if not security.is_tradable:
    -                continue
    -
    -            # Buy if the stock was mentioned more than 5 times in the WallStreetBets daily discussion, which translate into high popularity of rise
    -            if point.mentions > 5 and not security.holdings.is_long:
    -                self.market_order(symbol, 1)
    -                
    -            # Otherwise, short sell
    -            elif point.mentions <= 5 and not security.holdings.is_short:
    -                self.market_order(symbol, -1)
    -
    -    def on_securities_changed(self, changes: SecurityChanges) -> None:
    -        for added in changes.added_securities:
    -            # Requesting wall street bet data to obtain the trader's insights
    -            quiver_wsb_symbol = self.add_data(QuiverWallStreetBets, added.symbol).symbol
    +        self.aapl = self.add_equity("AAPL", Resolution.DAILY).symbol
    +        # Subscribe to government contract data for AAPL to generate trade signal
    +        self.dataset_symbol = self.add_data(QuiverGovernmentContract, self.aapl).symbol
     
    -            # Historical data
    -            history = self.history(QuiverWallStreetBets, quiver_wsb_symbol, 60, Resolution.DAILY)
    +        # history request
    +        history = self.history(self.dataset_symbol, 10, Resolution.DAILY)
    +        self.debug(f"We got {len(history)} items from historical data request of {self.dataset_symbol}.")
     
    -    def universe_selection(self, alt_coarse: List[QuiverWallStreetBetsUniverse]) -> List[Symbol]:       
    -        # Select the ones with popularity (mentions) of better-than-others performance (rank)
    -        return [d.symbol for d in alt_coarse \
    -                    if d.mentions > 10 \
    -                    and d.rank < 100]
    -
    public class QuiverWallStreetBetsDataAlgorithm : QCAlgorithm
    +    def on_data(self, slice: Slice) -> None:
    +        # Trade only base on government contract data
    +        for gov_contracts in slice.get(QuiverGovernmentContract).values():
    +            # Buy if over 50000 government contract amount, suggesting a large income
    +            if any([gov_contract.amount > 50000 for gov_contract in gov_contracts]):
    +                self.set_holdings(self.aapl, 1)
    +            # Sell if below 10000 government contract amount, suggesting a smaller than usual income
    +            elif any([gov_contract.amount < 10000 for gov_contract in gov_contracts]):
    +                self.set_holdings(self.aapl, -1)
    +
    public class QuiverGovernmentContractAlgorithm : QCAlgorithm
     {
    -    private Universe _universe;
    +    private Symbol _symbol, _datasetSymbol;
    +
         public override void Initialize()
         {
             SetStartDate(2024, 9, 1);
             SetEndDate(2024, 12, 31);
    -        SetCash(100000);
    -        // Seed the price of each asset with its last known price to avoid trading errors.
    -        Settings.SeedInitialPrices = true;
    -        UniverseSettings.Resolution = Resolution.Daily;
    -        // Filter using wall street bet insights
    -        _universe = AddUniverse<QuiverWallStreetBetsUniverse>(altCoarse =>
    -        {
    -            // Select the ones with popularity (mentions) of better-than-others performance (rank)
    -            return from d in altCoarse.OfType<QuiverWallStreetBetsUniverse>()
    -                    where d.Mentions > 10 && d.Rank < 100
    -                    select d.Symbol;
    -        });
    +        _symbol = AddEquity("AAPL").Symbol;
    +        // Subscribe to government contract data for AAPL to generate trade signal
    +        _datasetSymbol = AddData<QuiverGovernmentContract>(_symbol).Symbol;
    +
    +        // history request
    +        var history = History<QuiverGovernmentContract>(new[] {_datasetSymbol}, 10, Resolution.Daily);
    +        Debug($"We got {history.Count()} items from historical data request of {_datasetSymbol}.");
         }
     
         public override void OnData(Slice slice)
         {
    -        var points = slice.Get<QuiverWallStreetBets>();
    -        foreach (var point in points.Values)
    +        // Trade only base on government contract data
    +        foreach (var kvp in slice.Get<QuiverGovernmentContract>())
             {
    -            var symbol = point.Symbol.Underlying;
    -            var security = Securities[symbol];
    -            if (!security.IsTradable)
    -            {
    -                continue;
    -            }
    -            
    -            // Buy if the stock was mentioned more than 5 times in the WallStreetBets daily discussion, which translate into high popularity of rise
    -            if (point.Mentions > 5 && !security.Holdings.IsLong)
    +            // Buy if over 50000 government contract amount, suggesting a large income
    +            if (kvp.Value.Any(x => (int) (x as QuiverGovernmentContract).Amount > 50000m))
                 {
    -                MarketOrder(symbol, 1);
    +                SetHoldings(_symbol, 1);
                 }
    -            // Otherwise, short sell
    -            else if (point.Mentions <= 5 && !security.Holdings.IsShort)
    +            // Sell if below 10000 government contract amount, suggesting a smaller than usual income
    +            else if (kvp.Value.Any(x => (int) (x as QuiverGovernmentContract).Amount < 10000m))
                 {
    -                MarketOrder(symbol, -1);
    +                SetHoldings(_symbol, -1);
                 }
             }
         }
    -
    -    public override void OnSecuritiesChanged(SecurityChanges changes)
    -    {
    -        foreach(var added in changes.AddedSecurities)
    -        {
    -            // Requesting wall street bet data to obtain the trader's insights
    -            var quiverWSBSymbol = AddData<QuiverWallStreetBets>(added.Symbol).Symbol;
    -            // Historical data
    -            var history = History<QuiverWallStreetBets>(quiverWSBSymbol, 60, Resolution.Daily);
    -        }
    -    }
     }

    Framework Algorithm Example

    - The following example algorithm creates a dynamic universe of US Equities based on daily WallStreetBets data. When a security is mentioned on r/WallStreetBets more than five times in a day, the algorithm buys the security. When a security is mentioned five time in a day or less, the algorithm short sells the security. + The following example algorithm creates a dynamic universe of US Equities that have just received a government contract worth at least $5K. Each day, it then forms an equal-weighted dollar-neutral portfolio with the 10 companies that received the largest contracts and the 10 companies that received the smallest contracts.

    from AlgorithmImports import *
     
     
    -class QuiverWallStreetBetsDataAlgorithm(QCAlgorithm):
    +class QuiverGovernmentContractDataAlgorithm(QCAlgorithm):
    +
         def initialize(self) -> None:
             self.set_start_date(2024, 9, 1)
             self.set_end_date(2024, 12, 31)
             self.set_cash(100000)
    +        # Seed the price of each asset with its last known price to avoid trading errors.
    +        self.settings.seed_initial_prices = True
    +        # Filter universe using government contract data
    +        self.add_universe(QuiverGovernmentContractUniverse, self.universe_selection)
     
    -        self.universe_settings.resolution = Resolution.DAILY
    -        # Filter using wall street bet insights
    -        self._universe = self.add_universe(QuiverWallStreetBetsUniverse, self.universe_selection)
    -        
    -        self.add_alpha(WallStreamBetsAlphaModel())
    +        # Custom alpha model that emit insights based on updated government contract data
    +        self.add_alpha(QuiverGovernmentContractAlphaModel())
             
    +        # Invest equally to evenly dissipate the capital concentration risk
             self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
             
    -        self.add_risk_management(NullRiskManagementModel())
    -        
             self.set_execution(ImmediateExecutionModel())
    +        
    +    def universe_selection(self, data: List[QuiverGovernmentContractUniverse]) -> List[Symbol]:
    +        gov_contract_data_by_symbol = {}
     
    -    def universe_selection(self, alt_coarse: List[QuiverWallStreetBetsUniverse]) -> List[Symbol]:        
    -        # Select the ones with popularity (mentions) of better-than-others performance (rank)
    -        return [d.symbol for d in alt_coarse
    -                    if d.mentions > 10 and d.rank < 100]
    -
    -class WallStreamBetsAlphaModel(AlphaModel):
    -    
    -    symbol_data_by_symbol = {}
    -    
    -    def __init__(self, mentions_threshold: int = 5) -> None:
    -        self.mentions_threshold = mentions_threshold
    +        for datum in data:
    +            symbol = datum.symbol
    +            
    +            if symbol not in gov_contract_data_by_symbol:
    +                gov_contract_data_by_symbol[symbol] = []
    +            gov_contract_data_by_symbol[symbol].append(datum)
    +        
    +        # Only select the stocks with over 5000 government contracts, which is considered material information
    +        return [symbol for symbol, d in gov_contract_data_by_symbol.items()
    +                if sum([x.amount for x in d]) > 5000]
    +        
    +class QuiverGovernmentContractAlphaModel(AlphaModel):
         
    +    def __init__(self) -> None:
    +        # A variable to control the rebalancing time
    +        self.last_time = datetime.min
    +        # To hold the government contract dataset symbol for managing subscription
    +        self.dataset_symbol_by_symbol = {}
    +        
         def update(self, algorithm: QCAlgorithm, slice: Slice) -> List[Insight]:
    -        insights = []
    +        if self.last_time > algorithm.time: return []
             
    -        points = slice.Get(QuiverWallStreetBets)
    -        for point in points.Values:
    -            # Buy if the stock was mentioned more than 5 times in the WallStreetBets daily discussion, which translate into high popularity of rise
    -            # Otherwise short sell
    -            target_direction = InsightDirection.UP if point.mentions > self.mentions_threshold else InsightDirection.DOWN
    -            self.symbol_data_by_symbol[point.symbol.underlying].target_direction = target_direction
    -            
    -        for symbol, symbol_data in self.symbol_data_by_symbol.items():
    -            # Ensure we have security data for the current Slice to avoid stale fill
    -            if not (slice.contains_key(symbol) and slice[symbol] is not None):
    +        # Trade signal only based on government contract data
    +        data_points = slice.get(QuiverGovernmentContract)
    +        
    +        if not data_points: return []
    +        
    +        gov_contracts = {}
    +        # To aggregate all data per symbol for analysis
    +        for data_point in data_points:
    +            if not algorithm.securities[data_point.key.underlying].price:
                     continue
    -            
    -            if symbol_data.target_direction is not None:
    -                insights += [Insight.price(symbol, timedelta(1), symbol_data.target_direction)]
    -                symbol_data.target_direction = None
    +            if data_point.key not in gov_contracts:
    +                gov_contracts[data_point.key] = 0
     
    -        return insights
    +            for gov_contract in data_point.value:
    +                gov_contracts[data_point.key] += gov_contract.amount
    +        
    +        # Long the top 10 highest government contract amount, predicting a higher expected income and return
    +        # Short the lowest 10 government contract amount, predicting a lower expected income and return
    +        sorted_by_gov_contracts = sorted(gov_contracts.items(), key=lambda x: x[1])
    +        long_symbols = [x[0].underlying for x in sorted_by_gov_contracts[-10:]]
    +        short_symbols = [x[0].underlying for x in sorted_by_gov_contracts[:10]]
    +        
    +        insights = []
    +        for symbol in long_symbols:
    +            insights.append(Insight.price(symbol, Expiry.END_OF_DAY, InsightDirection.UP))        
    +        for symbol in short_symbols:
    +            insights.append(Insight.price(symbol, Expiry.END_OF_DAY, InsightDirection.DOWN))
    +        
    +        self.last_time = Expiry.END_OF_DAY(algorithm.time)
             
    +        return insights
             
         def on_securities_changed(self, algorithm: QCAlgorithm, changes: SecurityChanges) -> None:
             for security in changes.added_securities:
    +            # Requesting government contract data for trade signal generation
                 symbol = security.symbol
    -            self.symbol_data_by_symbol[symbol] = SymbolData(algorithm, symbol)
    -        
    +            dataset_symbol = algorithm.add_data(QuiverGovernmentContract, symbol).symbol
    +            self.dataset_symbol_by_symbol[symbol] = dataset_symbol
    +            # Historical Data
    +            history = algorithm.history(dataset_symbol, 10, Resolution.DAILY)
    +
             for security in changes.removed_securities:
    -            symbol_data = self.symbol_data_by_symbol.pop(security.symbol, None)
    -            if symbol_data:
    -                symbol_data.dispose()
    -                
    -                
    -class SymbolData:
    -    target_direction = None
    -    
    -    def __init__(self, algorithm: QCAlgorithm, symbol: Symbol) -> None:
    -        self.algorithm = algorithm
    -        
    -        # Requesting wall street bet data to obtain the trader's insights
    -        self.quiver_wsb_symbol = algorithm.add_data(QuiverWallStreetBets, symbol).symbol
    -        
    -        # Historical data
    -        history = algorithm.history(self.quiver_wsb_symbol, 60, Resolution.DAILY)
    -        
    -    def dispose(self) -> None:
    -        # Unsubscribe from the Quiver WallStreetBets feed for this security to release computationa resources
    -        self.algorithm.remove_security(self.quiver_wsb_symbol)
    -
    public class QuiverWallStreetBetsDataAlgorithm : QCAlgorithm
    +            dataset_symbol = self.dataset_symbol_by_symbol.pop(security.symbol, None)
    +            if dataset_symbol:
    +                # Remove government contract data subscription to release computation resources
    +                algorithm.remove_security(dataset_symbol)
    +
    public class QuiverGovernmentContractFrameworkAlgorithm : QCAlgorithm
     {
    -    private Universe _universe;
         public override void Initialize()
         {
             SetStartDate(2024, 9, 1);
             SetEndDate(2024, 12, 31);
             SetCash(100000);
    -
    -        UniverseSettings.Resolution = Resolution.Daily;
    -        // Filter using wall street bet insights
    -        _universe = AddUniverse<QuiverWallStreetBetsUniverse>(altCoarse =>
    +        // Seed the price of each asset with its last known price to avoid trading errors.
    +        Settings.SeedInitialPrices = true;
    +        // Filter universe using government contract data
    +        AddUniverse<QuiverGovernmentContractUniverse>(data =>
             {
    -            // Select the ones with popularity (mentions) of better-than-others performance (rank)
    -            return from d in altCoarse.OfType<QuiverWallStreetBetsUniverse>()
    -                    where d.Mentions > 10 && d.Rank < 100
    -                    select d.Symbol;
    +            var govContractDataBySymbol = new Dictionary<Symbol, List<QuiverGovernmentContractUniverse>>();
    +
    +            foreach (var datum in data.OfType<QuiverGovernmentContractUniverse>())
    +            {
    +                var symbol = datum.Symbol;
    +
    +                if (!govContractDataBySymbol.ContainsKey(symbol))
    +                {
    +                    govContractDataBySymbol.Add(symbol, new List<QuiverGovernmentContractUniverse>());
    +                }
    +                govContractDataBySymbol[symbol].Add(datum);
    +            }
    +
    +            // Only select the stocks with over 5000 government contracts, which is considered material information
    +            return from kvp in govContractDataBySymbol
    +                where kvp.Value.Sum(x => x.Amount) > 5000m
    +                select kvp.Key;
             });
     
    -        AddAlpha(new WallStreamBetsAlphaModel());
    -        
    +        // Custom alpha model that emit insights based on updated government contract data
    +        AddAlpha(new QuiverGovernmentContractAlphaModel());
    +
    +        // Invest equally to evenly dissipate the capital concentration risk
             SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel());
             
    -        AddRiskManagement(new NullRiskManagementModel());
    -        
             SetExecution(new ImmediateExecutionModel());
         }
     }
     
    -public class WallStreamBetsAlphaModel : AlphaModel
    +public class QuiverGovernmentContractAlphaModel: AlphaModel
     {
    -    private Dictionary<Symbol, SymbolData> _symbolDataBySymbol = new Dictionary<Symbol, SymbolData>();
    -    private int _mentionsThreshold;
    +    // A variable to control the rebalancing time
    +    private DateTime _time;
    +    // To hold the government contract dataset symbol for managing subscription
    +    private Dictionary<Symbol, Symbol> _datasetSymbolBySymbol = new();
         
    -    public WallStreamBetsAlphaModel(int mentionsThreshold=5)
    +    public QuiverGovernmentContractAlphaModel()
         {
    -        _mentionsThreshold = mentionsThreshold;
    +        _time = DateTime.MinValue;
         }
    -
    +    
         public override IEnumerable<Insight> Update(QCAlgorithm algorithm, Slice slice)
         {
    +        if (_time > algorithm.Time) return new List<Insight>();
    +        
    +        // Trade signal only based on government contract data
    +        var dataPoints = slice.Get<QuiverGovernmentContract>();
    +        
    +        if (dataPoints.IsNullOrEmpty()) return new List<Insight>();
    +
    +        // To aggregate all data per symbol for analysis
    +        var govContracts = dataPoints
    +            .Where(kvp => algorithm.Securities[kvp.Key.Underlying].Price != 0)
    +            .ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Sum(x => ((QuiverGovernmentContract)x).Amount));
    +        
    +        // Long the top 10 highest government contract amount, predicting a higher expected income and return
    +        // Short the lowest 10 government contract amount, predicting a lower expected income and return
    +        var sortedByGovContract = from kvp in govContracts
    +                        orderby kvp.Value descending
    +                        select kvp.Key.Underlying;
    +        var longSymbols = sortedByGovContract.Take(10).ToList();
    +        var shortSymbols = sortedByGovContract.TakeLast(10).ToList();
    +        
             var insights = new List<Insight>();
    +        insights.AddRange(longSymbols.Select(symbol => 
    +            new Insight(symbol, Expiry.EndOfDay, InsightType.Price, InsightDirection.Up)));
    +        insights.AddRange(shortSymbols.Select(symbol => 
    +            new Insight(symbol, Expiry.EndOfDay, InsightType.Price, InsightDirection.Down)));
             
    -        var points = slice.Get<QuiverWallStreetBets>();
    -        foreach (var point in points.Values)
    -        {
    -            // Buy if the stock was mentioned more than 5 times in the WallStreetBets daily discussion, which translate into high popularity of rise
    -            // Otherwise short sell
    -            var targetDirection = point.Mentions > _mentionsThreshold ? InsightDirection.Up : InsightDirection.Down;
    -            _symbolDataBySymbol[point.Symbol.Underlying].targetDirection = targetDirection;
    -        }
    +        _time = Expiry.EndOfDay(algorithm.Time);
             
    -        foreach (var kvp in _symbolDataBySymbol)
    -        {
    -            var symbol = kvp.Key;
    -            var symbolData = kvp.Value;
    -            
    -            // Ensure we have security data for the current Slice to avoid stale fill
    -            if (!(slice.ContainsKey(symbol) && slice[symbol] != null))
    -            {
    -                continue;
    -            }
    -            
    -            if (symbolData.targetDirection != null)
    -            {
    -                insights.Add(Insight.Price(symbol, TimeSpan.FromDays(1), (InsightDirection)symbolData.targetDirection));
    -                symbolData.targetDirection = null;
    -            }
    -        }
             return insights;
         }
    -
    +    
         public override void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes)
         {
             foreach (var security in changes.AddedSecurities)
             {
    +            // Requesting government contract data for trade signal generation
                 var symbol = security.Symbol;
    -            _symbolDataBySymbol.Add(symbol, new SymbolData(algorithm, symbol));
    +            var datasetSymbol = algorithm.AddData<QuiverGovernmentContract>(symbol).Symbol;
    +            _datasetSymbolBySymbol.Add(symbol, datasetSymbol);
    +            // History request
    +            var history = algorithm.History<QuiverGovernmentContract>(datasetSymbol, 10, Resolution.Daily);
             }
    -
    +        
             foreach (var security in changes.RemovedSecurities)
             {
                 var symbol = security.Symbol;
    -            if (_symbolDataBySymbol.ContainsKey(symbol))
    +            if (_datasetSymbolBySymbol.ContainsKey(symbol))
                 {
    -                _symbolDataBySymbol[symbol].dispose();
    -                _symbolDataBySymbol.Remove(symbol);
    +                // Remove government contract data subscription to release computation resources
    +                _datasetSymbolBySymbol.Remove(symbol, out var datasetSymbol);
    +                algorithm.RemoveSecurity(datasetSymbol);
                 }
             }
         }
    -}
    -
    -public class SymbolData
    -{
    -    private Symbol _quiverWSBSymbol;
    -    private QCAlgorithm _algorithm;
    -    public InsightDirection? targetDirection = null;
    -    
    -    public SymbolData(QCAlgorithm algorithm, Symbol symbol)
    -    {
    -        _algorithm = algorithm;
    -        
    -        // Requesting wall street bet data to obtain the trader's insights
    -        _quiverWSBSymbol = algorithm.AddData<QuiverWallStreetBets>(symbol).Symbol;
    -        
    -        // Historical data
    -        var history = algorithm.History<QuiverWallStreetBets>(_quiverWSBSymbol, 60, Resolution.Daily);
    -    }
    -    
    -    public void dispose()
    -    {
    -        // Unsubscribe from the Quiver WallStreetBets feed for this security to release computationa resources
    -        _algorithm.RemoveSecurity(_quiverWSBSymbol);
    -    }
     }

    Research Example

    - The following example lists low-ranking US Equities that are mentioned more than ten times on r/WallStreetBets. + The following example lists all US Equities with Government contracts in the past year.

    -
    #r "../QuantConnect.DataSource.QuiverWallStreetBets.dll"
    +   
    #r "../QuantConnect.DataSource.QuiverGovernmentContracts.dll"
     using QuantConnect.DataSource;
     
    -var qb = new QuantBook();
    -
     // Requesting data
     var aapl = qb.AddEquity("AAPL", Resolution.Daily).Symbol;
    -var symbol = qb.AddData<QuiverWallStreetBets>(aapl).Symbol;
    +var symbol = qb.AddData<QuiverGovernmentContract>(aapl).Symbol;
     
     // Historical data
    -var history = qb.History<QuiverWallStreetBets>(symbol, 60, Resolution.Daily);
    -foreach (var bet in history.OfType<QuiverWallStreetBets>())
    +var history = qb.History<QuiverGovernmentContract>(symbol, 360, Resolution.Daily);
    +foreach (var contracts in history)
     {
    -    Console.WriteLine($"{bet.Symbol} rank at {bet.EndTime}: {bet.Rank}");
    +    foreach (QuiverGovernmentContract contract in contracts)
    +    {
    +        Console.WriteLine($"{contract.Symbol} amount at {contract.EndTime}: {contract.Amount}");
    +    }
     }
     
     // Add Universe Selection
     IEnumerable<Symbol> UniverseSelection(IEnumerable<BaseData> altCoarse)
     {
    -    return from d in altCoarse.OfType<QuiverWallStreetBetsUniverse>()
    -        where d.Mentions > 10 && d.Rank < 100 select d.Symbol;
    +    return from d in altCoarse.OfType<QuiverGovernmentContractUniverse>()
    +        select d.Symbol;
     }
    -var universe = qb.AddUniverse<QuiverWallStreetBetsUniverse>(UniverseSelection);
    +var universe = qb.AddUniverse<QuiverGovernmentContractUniverse<(UniverseSelection);
     
     // Historical Universe data
    -var universeHistory = qb.UniverseHistory(universe, qb.Time.AddDays(-60), qb.Time);
    -foreach (var bets in universeHistory)
    +var universeHistory = qb.UniverseHistory(universe, qb.Time.AddDays(-360), qb.Time);
    +foreach (var contracts in universeHistory)
     {
    -    foreach (QuiverWallStreetBetsUniverse bet in bets)
    +    foreach (QuiverGovernmentContractUniverse contract in contracts)
         {
    -        Console.WriteLine($"{bet.Symbol} rank at {bet.EndTime}: {bet.Rank}");
    +        Console.WriteLine($"{contract.Symbol} amount at {contract.EndTime}: {contract.Amount}");
         }
     }
    qb = QuantBook()
     
    -# Requesting data
    +# Requesting Data
     aapl = qb.add_equity("AAPL", Resolution.DAILY).symbol
    -symbol = qb.add_data(QuiverWallStreetBets, aapl).symbol
    +symbol = qb.add_data(QuiverGovernmentContract, aapl).symbol
     
     # Historical data
    -history = qb.history(QuiverWallStreetBets, symbol, 60, Resolution.DAILY)
    -for (symbol, time), bet in history.iterrows():
    -    print(f"{symbol} rank at {time}: {bet['rank']}")
    +history = qb.history(QuiverGovernmentContract, symbol, 360, Resolution.DAILY)
    +for (symbol, time), contracts in history.items():
    +    for contract in contracts:
    +        print(f"{contract.symbol} amount at {contract.end_time}: {contract.amount}")
     
     # Add Universe Selection
    -def universe_selection(alt_coarse: List[QuiverWallStreetBetsUniverse]) -> List[Symbol]:
    -    return [d.symbol for d in alt_coarse if d.mentions > 10 and d.rank < 100]
    +def universe_selection(alt_coarse: List[QuiverGovernmentContractUniverse]) -> List[Symbol]:
    +    return [d.symbol for d in alt_coarse]
     
    -universe = qb.add_universe(QuiverWallStreetBetsUniverse, universe_selection)
    +universe = qb.add_universe(QuiverGovernmentContractUniverse, universe_selection)
             
     # Historical Universe data
    -universe_history = qb.universe_history(universe, qb.time-timedelta(60), qb.time)
    -for (univere_symbol, time), bets in universe_history.items():
    -    for bet in bets:
    -        print(f"{bet.symbol} rank at {bet.end_time}: {bet.rank}")
    +universe_history = qb.universe_history(universe, qb.time-timedelta(360), qb.time) +for (_, time), contracts in universe_history.items(): + for contract in contracts: + print(f"{contract.symbol} amount at {contract.end_time}: {contract.amount}")
    @@ -268883,37 +358362,37 @@

    Data Point Attributes

    - The WallStreetBets dataset provides + The US Government Contracts dataset provides - QuiverWallStreetBets + QuiverGovernmentContract and - QuiverWallStreetBetsUniverse + QuiverGovernmentContractUniverse objects.

    - QuiverWallStreetBets Attributes + QuiverGovernmentContract

    - QuiverWallStreetBets + QuiverGovernmentContract objects have the following attributes:

    -
    +

    - QuiverWallStreetBetsUniverse Attributes + QuiverGovernmentContractUniverse

    - QuiverWallStreetBetsUniverse + QuiverGovernmentContractUniverse objects have the following attributes:

    -
    +
    @@ -269840,9 +359319,9 @@

    Accessing Data

    data_point = slice[self.report_10k_symbol] self.log(f"{self.report_10k_symbol} report count at {slice.time}: {len(data_point.report.documents)}") - if slice.ContainsKey(self.report_10q_symbol): + if slice.contains_key(self.report_10q_symbol): data_point = slice[self.report_10q_symbol] - self.log(f"{self.report_10q_symbol} report count at {slice.Time}: {len(data_point.report.documents)}") + self.log(f"{self.report_10q_symbol} report count at {slice.time}: {len(data_point.report.documents)}")
    public override void OnData(Slice slice)
     {
         if (slice.ContainsKey(_report8KSymbol))
    @@ -270048,7 +359527,7 @@ 

    def on_data(self, slice: Slice) -> None: # Trade from SEC data - for report in slice.Get(SECReport8K).Values: + for report in slice.get(SECReport8K).values(): underlying_symbol = report.symbol.underlying # Skip the Symbol if it's no longer in the universe (insuffucient popularity to reach market efficiency of fundamental factor) if underlying_symbol not in self.dataset_symbol_by_symbol: @@ -271022,70 +360501,58 @@

    self.set_start_date(2024, 9, 1) self.set_end_date(2024, 12, 31) self.set_cash(100000) - - self.aapl = self.add_equity("AAPL", Resolution.MINUTE).symbol - - # Requesting insider trade intention news and actual trades to estimate the return, since insiders may have better information of the future confidence - self.smart_insider_intention = self.add_data(SmartInsiderIntention, self.aapl).symbol - self.smart_insider_transaction = self.add_data(SmartInsiderTransaction, self.aapl).symbol - - # Historical data - history = self.history(self.smart_insider_intention, 365, Resolution.DAILY) + self._equity = self.add_equity("AAPL", Resolution.DAILY) + # Request insider trade intention news and actual trades because insiders may have better information about future prospects. + self._smart_insider_intention_data = self.add_data(SmartInsiderIntention, self._equity).symbol + self._smart_insider_transaction_data = self.add_data(SmartInsiderTransaction, self._equity).symbol + # Warm up custom data subscriptions with historical data. + history = self.history(self._smart_insider_intention_data, 252, Resolution.DAILY) self.debug(f"We got {len(history)} items from our history request for intentions") - - history = self.history(self.smart_insider_transaction, 365, Resolution.DAILY) + history = self.history(self._smart_insider_transaction_data, 252, Resolution.DAILY) self.debug(f"We got {len(history)} items from our history request for transactions") - def on_data(self, slice: Slice) -> None: - # Buy Apple whenever we receive a buyback intention or transaction notification, given the insiders may have confidence in the future to buy more - # This news may stimulate market popularity - if slice.contains_key(self.smart_insider_intention) or slice.contains_key(self.smart_insider_transaction): - self.set_holdings(self.aapl, 1) - self.entry_time = self.time - - # Liquidate holdings 3 days after the latest entry - # The market popularity and possible overbrought is cooled - if self.portfolio.invested and self.time >= self.entry_time + timedelta(days=3): + def on_data(self, data: Slice) -> None: + # Buy Apple when insider buyback events suggest stronger sentiment and potential market attention. + if self._smart_insider_intention_data in data or self._smart_insider_transaction_data in data: + self.set_holdings(self._equity, 1) + self._entry_time = self.time + # Liquidate after three days so the position exits once the event reaction has cooled. + if self.portfolio.invested and self.time >= self._entry_time + timedelta(days=3): self.liquidate()

    public class CorporateBuybacksDataAlgorithm : QCAlgorithm
     {
    -    private Symbol _aapl;
    -    private Symbol _smartInsiderIntention;
    -    private Symbol _smartInsiderTransaction;
    +    private Equity _equity;
    +    private Symbol _smartInsiderIntentionData;
    +    private Symbol _smartInsiderTransactionData;
         private DateTime _entryTime;
    -    
    +
         public override void Initialize()
         {
             SetStartDate(2024, 9, 1);
             SetEndDate(2024, 12, 31);
             SetCash(100000);
    -        
    -        _aapl = AddEquity("AAPL", Resolution.Minute).Symbol;
    -        
    -        // Requesting insider trade intention news and actual trades to estimate the return, since insiders may have better information of the future confidence
    -        _smartInsiderIntention = AddData<SmartInsiderIntention>(_aapl).Symbol;
    -        _smartInsiderTransaction = AddData<SmartInsiderTransaction>(_aapl).Symbol;
    -        
    -        // Historical data
    -        var intentionHistory = History<SmartInsiderIntention>(_smartInsiderIntention, 365, Resolution.Daily);
    -        Debug($"We got {intentionHistory.Count()} items from our history request for intentions");
    -        
    -        var transactionHistory = History<SmartInsiderTransaction>(_smartInsiderTransaction, 365, Resolution.Daily);
    -        Debug($"We got {transactionHistory.Count()} items from our history request for transactions");
    +        _equity = AddEquity("AAPL", Resolution.Daily);
    +        // Request insider trade intention news and actual trades because insiders may have better information about future prospects.
    +        _smartInsiderIntentionData = AddData<SmartInsiderIntention>(_equity.Symbol).Symbol;
    +        _smartInsiderTransactionData = AddData<SmartInsiderTransaction>(_equity.Symbol).Symbol;
    +        // Warm up custom data subscriptions with historical data.
    +        var intentionHistory = History<SmartInsiderIntention>(_smartInsiderIntentionData, TimeSpan.FromDays(252), Resolution.Daily);
    +        var intentionCount = intentionHistory.Count();
    +        Debug($"We got {intentionCount} items from our history request for intentions");
    +        var transactionHistory = History<SmartInsiderTransaction>(_smartInsiderTransactionData, TimeSpan.FromDays(252), Resolution.Daily);
    +        var transactionCount = transactionHistory.Count();
    +        Debug($"We got {transactionCount} items from our history request for transactions");
         }
     
    -    public override void OnData(Slice slice)
    +    public override void OnData(Slice data)
         {
    -        // Buy Apple whenever we receive a buyback intention or transaction notification, given the insiders may have confidence in the future to buy more
    -        // This news may stimulate market popularity
    -        if (slice.ContainsKey(_smartInsiderIntention) || slice.ContainsKey(_smartInsiderTransaction))
    +        // Buy Apple when insider buyback events suggest stronger sentiment and potential market attention.
    +        if (data.ContainsKey(_smartInsiderIntentionData) || data.ContainsKey(_smartInsiderTransactionData))
             {
    -            SetHoldings(_aapl, 1);
    +            SetHoldings(_equity.Symbol, 1);
                 _entryTime = Time;
             }
    -
    -        // Liquidate holdings 3 days after the latest entry
    -        // The market popularity and possible overbrought is cooled
    +        // Liquidate after three days so the position exits once the event reaction has cooled.
             if (Portfolio.Invested && Time >= _entryTime + TimeSpan.FromDays(3))
             {
                 Liquidate();
    @@ -271135,10 +360602,10 @@ 

    self.smart_insider_transaction = algorithm.add_data(SmartInsiderTransaction, self.aapl).symbol # Historical data - history = algorithm.history(self.smart_insider_intention, 365, Resolution.DAILY) + history = algorithm.history(self.smart_insider_intention, 252, Resolution.DAILY) algorithm.debug(f"We got {len(history)} items from our history request for intentions") - history = algorithm.history(self.smart_insider_transaction, 365, Resolution.DAILY) + history = algorithm.history(self.smart_insider_transaction, 252, Resolution.DAILY) algorithm.debug(f"We got {len(history)} items from our history request for transactions")

    public class CorporateBuybacksDataAlgorithm : QCAlgorithm
     {
    @@ -271186,10 +360653,10 @@ 

    _smartInsiderTransaction = algorithm.AddData<SmartInsiderTransaction>(_aapl).Symbol; // Historical data - var intentionHistory = algorithm.History<SmartInsiderIntention>(_smartInsiderIntention, 365, Resolution.Daily); + var intentionHistory = algorithm.History<SmartInsiderIntention>(_smartInsiderIntention, 252, Resolution.Daily); algorithm.Debug($"We got {intentionHistory.Count()} items from our history request for intentions"); - var transactionHistory = algorithm.History<SmartInsiderTransaction>(_smartInsiderTransaction, 365, Resolution.Daily); + var transactionHistory = algorithm.History<SmartInsiderTransaction>(_smartInsiderTransaction, 252, Resolution.Daily); algorithm.Debug($"We got {transactionHistory.Count()} items from our history request for transactions"); } } @@ -271268,46 +360735,46 @@

    qb = QuantBook() # Requesting Data -symbol = qb.AddEquity("AAPL").Symbol -intention_symbol = qb.AddData(SmartInsiderIntention, symbol).Symbol -transaction_symbol = qb.AddData(SmartInsiderTransaction, symbol).Symbol +symbol = qb.add_equity("AAPL").symbol +intention_symbol = qb.add_data(SmartInsiderIntention, symbol).symbol +transaction_symbol = qb.add_data(SmartInsiderTransaction, symbol).symbol # Historical data -intention_history = qb.History(SmartInsiderIntention, intention_symbol, 300, Resolution.Daily) +intention_history = qb.history(SmartInsiderIntention, intention_symbol, 300, Resolution.DAILY) for (symbol, time), row in intention_history.iterrows(): if isnan(row['amountvalue']): continue print(f"{symbol} amount value at {time}: {row['amountvalue']}") -transaction_history = qb.History(SmartInsiderTransaction, transaction_symbol, 300, Resolution.Daily) +transaction_history = qb.history(SmartInsiderTransaction, transaction_symbol, 300, Resolution.DAILY) for (symbol, time), row in transaction_history.iterrows(): if isnan(row['amount']): continue print(f"{symbol} amount at {time}: {row['amount']}") # Add Universe Selection for SmartInsiderIntention -def IntentionSelection(alt_coarse: List[SmartInsiderIntentionUniverse]) -> List[Symbol]: - return [d.Symbol for d in sorted([x for x in alt_coarse if x.AmountValue], - key=lambda x: x.AmountValue, reverse=True)[:10]] +def intention_selection(alt_coarse: List[SmartInsiderIntentionUniverse]) -> List[Symbol]: + return [d.symbol for d in sorted([x for x in alt_coarse if x.amount_value], + key=lambda x: x.amount_value, reverse=True)[:10]] -intention_universe = qb.AddUniverse(SmartInsiderIntentionUniverse, IntentionSelection) +intention_universe = qb.add_universe(SmartInsiderIntentionUniverse, intention_selection) # Historical Universe data -intention_universe_history = qb.UniverseHistory(intention_universe, qb.Time-timedelta(10), qb.Time) +intention_universe_history = qb.universe_history(intention_universe, qb.time-timedelta(10), qb.time) for (_, time), intentions in intention_universe_history.items(): for intention in intentions: - print(f"{intention.Symbol.Value} amount value at {intention.EndTime}: {intention.AmountValue}") + print(f"{intention.symbol.value} amount value at {intention.end_time}: {intention.amount_value}") # Add Universe Selection for SmartInsiderTransaction -def IntentionSelection(alt_coarse: List[SmartInsiderTransactionUniverse]) -> List[Symbol]: - return [d.Symbol for d in sorted([x for x in alt_coarse if x.Amount], - key=lambda x: x.Amount, reverse=True)[:10]] +def intention_selection(alt_coarse: List[SmartInsiderTransactionUniverse]) -> List[Symbol]: + return [d.symbol for d in sorted([x for x in alt_coarse if x.amount], + key=lambda x: x.amount, reverse=True)[:10]] -transaction_universe = qb.AddUniverse(SmartInsiderTransactionUniverse, IntentionSelection) +transaction_universe = qb.add_universe(SmartInsiderTransactionUniverse, intention_selection) # Historical Universe data -transaction_universe_history = qb.UniverseHistory(transaction_universe, qb.Time-timedelta(10), qb.Time) +transaction_universe_history = qb.universe_history(transaction_universe, qb.time-timedelta(10), qb.time) for (_, time), transactions in transaction_universe_history.items(): for transaction in transactions: - print(f"{transaction.Symbol.Value} amount at {transaction.EndTime}: {transaction.Amount}")

    + print(f"{transaction.symbol.value} amount at {transaction.end_time}: {transaction.amount}")
    @@ -275469,7 +364936,7 @@

    Debugging

    class MyCustomDataTypeAlgorithm(QCAlgorithm):
         def initialize(self):
             MyCustomDataType.ALGORITHM = self
    -        self._symbol = self.add_data(MyCustomDataType, "<name>", Resolution.DAILY).Symbol
    +        self._symbol = self.add_data(MyCustomDataType, "<name>", Resolution.DAILY).symbol
     
     class MyCustomDataType(PythonData):
         def reader(self, config: SubscriptionDataConfig, line: str, date: datetime, is_live_mode: bool) -> BaseData:
    @@ -275478,7 +364945,7 @@ 

    Debugging

    custom.symbol = config.symbol if not line[0].isdigit(): # Display the line with the header - MyCustomDataType.ALGORITHM.Debug(f"HEADER: {line}") + MyCustomDataType.ALGORITHM.debug(f"HEADER: {line}") return custom custom.end_time = datetime.strptime(data[0], '%Y%m%d') + timedelta(1) custom.value = float(data[1]) @@ -277927,7 +367394,69 @@

    Transport Binary Data

    - Follow these steps to transport binary files: + Follow these steps to transport binary files with + + joblib + + : +

    +
      +
    1. + Add the following imports to your local program: +
    2. +
      +
      import joblib
      +from io import BytesIO
      +from base64 import b64encode, b64decode
      +
      +
    3. + Serialize your object and save it to a file. This Base64 serialization is necessary because the default + + joblib.dump + + serialization produces a binary file that the + + download + + method can't read without data corruption. +
    4. +
      +
      buffer = BytesIO()
      +joblib.dump(my_object, buffer)
      +base64_str = b64encode(buffer.getvalue()).decode('ascii')
      +with open("my_model.b64", "w") as f:
      +    f.write(base64_str)
      +
      +
    5. + Save the file to one of the + + supported sources + + . For example, save the file to Dropbox. +
    6. +
    7. + + Download the remote file + + into your project. +
    8. +
      +
      base64_str = self.download("<fileURL>")
      +
      +
    9. + Restore the object. +
    10. +
      +
      model_bytes = b64decode(base64_str.encode('ascii'))
      +restored_model = joblib.load(BytesIO(model_bytes))
      +
      +
    +

    + Follow these steps to transport binary files with + + pickle + + :

    1. @@ -277935,21 +367464,23 @@

      Transport Binary Data

    2. import pickle
      -import base64
      +from base64 import b64encode, b64decode
  • - Serialize your object. + Serialize your object and save it to a file.
  • pickle_bytes = pickle.dumps(my_object)
    -base64_str = base64.b64encode(pickle_bytes).decode('ascii')
    +base64_str = b64encode(pickle_bytes).decode('ascii') +with open("my_model.b64", "w") as f: + f.write(base64_str)
  • - Save the string representation of your object to one of the + Save the file to one of the supported sources - . + . For example, save the file to Dropbox.
  • @@ -277964,9 +367495,8 @@

    Transport Binary Data

    Restore the object.
  • -
    base64_bytes = base64_str.encode('ascii')
    -model = base64.b64decode(base64_bytes)
    -restored_model = pickle.loads(model)
    +
    model_bytes = b64decode(base64_str.encode('ascii'))
    +restored_model = pickle.loads(model_bytes)
    @@ -277997,9 +367527,11 @@

    method.

    -
    from sklearn.svm import SVR
    +   
    import joblib
    +from io import BytesIO
    +from base64 import b64decode, b64encode
    +from sklearn.svm import SVR
     from sklearn.model_selection import GridSearchCV
    -import joblib
     
     class BulkDownloadExampleAlgorithm(QCAlgorithm):
         def initialize(self) -> None:
    @@ -278007,23 +367539,25 @@ 

    self.set_end_date(2024, 12, 31) self.set_cash(100000) # Request SPY data for model training, prediction, and trading. - self.symbol = self.add_equity("SPY", Resolution.DAILY).symbol + self._symbol = self.add_equity("SPY", Resolution.DAILY).symbol # 2-year data to train the model. training_length = 252*2 self.training_data = RollingWindow(training_length) # Warm up the training dataset to train the model immediately. - history = self.history[TradeBar](self.symbol, training_length, Resolution.DAILY) + history = self.history[TradeBar](self._symbol, training_length, Resolution.DAILY) for trade_bar in history: self.training_data.add(trade_bar) # Retrieve the already trained model from the object store for immediate use. if self.object_store.contains_key("sklearn_model"): - file = self.object_store.get_file_path("sklearn_model") - # Otherwise, bulk-download the model from an external source (Dropbox in this example). + self.model = joblib.load(self.object_store.get_file_path("sklearn_model")) + # Otherwise, bulk-download the base64-encoded model from an external source (Dropbox in this example). else: - file = self.download("https://www.dropbox.com/scl/fi/nhz2zxq3pr2bweia4av0o/sklearn_model?rlkey=loy09wbh69k9j6umlru9icsaj&st=6vdazyp4&dl=1") - self.model = joblib.load(file) + url = "https://www.dropbox.com/scl/fi/tj7wmpv1u2ysvejb0skpm/sklearn_model_b64?rlkey=pvcnuvqrp78oz1t73rytgnvdx&dl=1" + base64_str = self.download(url) + model_bytes = b64decode(base64_str.encode('ascii')) + self.model = joblib.load(BytesIO(model_bytes)) # Train the model to use the prediction right away. self.train(self.my_training_method) @@ -278056,8 +367590,9 @@

    self.model = self.model.fit(features, labels) def on_data(self, slice: Slice) -> None: - if self.symbol in slice.bars: - self.training_data.add(slice.bars[self.symbol]) + bar = slice.bars.get(self._symbol) + if bar: + self.training_data.add(bar) # Get predictions by the updated features. features, _ = self.get_features_and_labels() @@ -278066,16 +367601,28 @@

    # If the predicted direction is going upward, buy SPY. if prediction > 0: - self.set_holdings(self.symbol, 1) + self.set_holdings(self._symbol, 1) # If the predicted direction is going downward, sell SPY. elif prediction < 0: - self.set_holdings(self.symbol, -1) + self.set_holdings(self._symbol, -1) def on_end_of_algorithm(self) -> None: # Store the model in the object store to retrieve it in other instances if the algorithm stops. model_key = "sklearn_model" - file_name = self.object_store.get_file_path(model_key) - joblib.dump(self.model, file_name)

    + joblib.dump(self.model, self.object_store.get_file_path(model_key)) + self._save_base64_encoded_version(model_key) + + def _save_base64_encoded_version(self, model_key): + # Save a base64-encoded version of the model to upload to an external source (e.g., Dropbox). + buffer = BytesIO() + joblib.dump(self.model, buffer) + content = b64encode(buffer.getvalue()).decode('ascii') + path = self.object_store.get_file_path(f"{model_key}_b64") + self.object_store.save(path, content) + + # Locally, you may want to use the open method + #with open(path, "w") as f: + # f.write(content)

    Other Examples @@ -278463,8 +368010,218 @@

    - - TradeBarConsolidator + + TradeBarConsolidator + + + + + + + QuoteBar + + + + + QuoteBar + + + + + QuoteBarConsolidator + + + + + + + Tick + + + + + TradeBar + + + + + TickConsolidator + + + + + + + Tick + + + + + QuoteBar + + + + + TickQuoteBarConsolidator + + + + + +

    + Mixed-Mode Consolidators +

    +

    + Mixed-mode consolidators are a combination of count consolidators and time period consolidators. This type of consolidator aggregates + + n + + samples together or aggregates samples over a specific time period, whichever happens first. +

    +

    + The following table shows which consolidator type to use based on the data format of the input and output: +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Input + + Output + + Class Type +
    + + TradeBar + + + + TradeBar + + + + TradeBarConsolidator + +
    + + QuoteBar + + + + QuoteBar + + + + QuoteBarConsolidator + +
    + + Tick + + + + TradeBar + + + + TickConsolidator + +
    + + Tick + + + + QuoteBar + + + + TickQuoteBarConsolidator + +
    +

    + Renko Consolidators +

    +

    + Most Renko consolidators aggregate bars based on a fixed price movement. The + + RenkoConsolidator + + produces Renko bars by their traditional definition. + In the case of a $1 bar size, the + + RenkoConsolidator + + produces bars that have a body spanning $1. + The opening price of the first bar is set to the closest $1 multiple of the first trade. + When the price moves by at least $1, the first bar closes. + If a bar is a rising bar, the following bar closes when the price moves $1 above the closing price of the previous bar or $1 below the opening price of the previous bar. + If a bar is a falling bar, the following bar closes when the price moves $1 below the closing price of the previous bar or $1 above the opening price of the previous bar. + If the price jumps multiple dollars in a single tick, the + + RenkoConsolidator + + produces multiple $1 bars in a single time step. +

    +

    + The following table shows which consolidator type to use based on the data format of the input and output: +

    + + + + + + + + + + + + + @@ -278480,8 +368237,8 @@

    @@ -278497,8 +368254,8 @@

    @@ -278514,22 +368271,34 @@

    + Input + + Output + + Class Type +
    + + TradeBar + + + + TradeBar + + + + RenkoConsolidator
    - - QuoteBarConsolidator + + RenkoConsolidator
    - - TickConsolidator + + RenkoConsolidator
    - - TickQuoteBarConsolidator + + RenkoConsolidator

    - Mixed-Mode Consolidators + Classic Renko Consolidators

    - Mixed-mode consolidators are a combination of count consolidators and time period consolidators. This type of consolidator aggregates - - n - - samples together or aggregates samples over a specific time period, whichever happens first. + Most Renko consolidators aggregate bars based on a fixed price movement. The + + ClassicRenkoConsolidator + + produces a different type of Renko bars than the + + RenkoConsolidator + + . A + + ClassicRenkoConsolidator + + with a bar size of $1 produces a new bar that spans $1 every time an asset closes $1 away from the close of the previous bar. If the price jumps multiple dollars in a single tick, the + + ClassicRenkoConsolidator + + only produces one bar per time step where the open of each bar matches the close of the previous bar.

    The following table shows which consolidator type to use based on the data format of the input and output: @@ -278561,8 +368330,8 @@

    - - TradeBarConsolidator + + ClassicRenkoConsolidator @@ -278578,8 +368347,8 @@

    - - QuoteBarConsolidator + + ClassicRenkoConsolidator @@ -278595,8 +368364,8 @@

    - - TickConsolidator + + ClassicRenkoConsolidator @@ -278612,36 +368381,30 @@

    - - TickQuoteBarConsolidator + + ClassicRenkoConsolidator

    - Renko Consolidators + Volume Renko Consolidators

    - Most Renko consolidators aggregate bars based on a fixed price movement. The - - RenkoConsolidator - - produces Renko bars by their traditional definition. - In the case of a $1 bar size, the + Volume Renko consolidators aggregate bars based on a fixed trading volume. These types of bars are commonly known as volume bars. A - RenkoConsolidator + VolumeRenkoConsolidator - produces bars that have a body spanning $1. - The opening price of the first bar is set to the closest $1 multiple of the first trade. - When the price moves by at least $1, the first bar closes. - If a bar is a rising bar, the following bar closes when the price moves $1 above the closing price of the previous bar or $1 below the opening price of the previous bar. - If a bar is a falling bar, the following bar closes when the price moves $1 below the closing price of the previous bar or $1 above the opening price of the previous bar. - If the price jumps multiple dollars in a single tick, the + with a bar size of 10,000 produces a new bar every time 10,000 units of the security trades in the market. If the trading volume of a single + + time step + + exceeds two step sizes (i.e. >20,000), the - RenkoConsolidator + VolumeRenkoConsolidator - produces multiple $1 bars in a single time step. + produces multiple bars in a single time step.

    The following table shows which consolidator type to use based on the data format of the input and output: @@ -278673,25 +368436,8 @@

    - - RenkoConsolidator - - - - - - - QuoteBar - - - - - QuoteBar - - - - - RenkoConsolidator + + VolumeRenkoConsolidator @@ -278707,51 +368453,20 @@

    - - RenkoConsolidator - - - - - - - Tick - - - - - QuoteBar - - - - - RenkoConsolidator + + VolumeRenkoConsolidator

    - Classic Renko Consolidators + Range Consolidators

    - Most Renko consolidators aggregate bars based on a fixed price movement. The - - ClassicRenkoConsolidator - - produces a different type of Renko bars than the - - RenkoConsolidator - - . A - - ClassicRenkoConsolidator - - with a bar size of $1 produces a new bar that spans $1 every time an asset closes $1 away from the close of the previous bar. If the price jumps multiple dollars in a single tick, the - - ClassicRenkoConsolidator - - only produces one bar per time step where the open of each bar matches the close of the previous bar. + Most Range consolidators aggregate bars based on a fixed price movement. It is very much like a Renko Bar, but instead of consolidating a fix movement from the opening price, + a Range Bar would fix the range of the whole bar, i.e. High - Low. A new bar is formed when the range of the whole bar reached a preset value, thus it completely neglects the time + effect and focuses on the variance/information carried by the price movement.

    The following table shows which consolidator type to use based on the data format of the input and output: @@ -278783,8 +368498,8 @@

    - - ClassicRenkoConsolidator + + RangeConsolidator @@ -278800,8 +368515,8 @@

    - - ClassicRenkoConsolidator + + RangeConsolidator @@ -278817,8 +368532,8 @@

    - - ClassicRenkoConsolidator + + RangeConsolidator @@ -278834,92 +368549,24 @@

    - - ClassicRenkoConsolidator + + RangeConsolidator

    - Volume Renko Consolidators + Market Hour Aware Consolidators

    - Volume Renko consolidators aggregate bars based on a fixed trading volume. These types of bars are commonly known as volume bars. A - - VolumeRenkoConsolidator - - with a bar size of 10,000 produces a new bar every time 10,000 units of the security trades in the market. If the trading volume of a single - - time step + Market hour aware consolidators aggregate data based on the asset's + + trading hours - exceeds two step sizes (i.e. >20,000), the - - VolumeRenkoConsolidator - - produces multiple bars in a single time step. -

    -

    - The following table shows which consolidator type to use based on the data format of the input and output: -

    - - - - - - - - - - - - - - - - - - - - -
    - Input - - Output - - Class Type -
    - - TradeBar - - - - TradeBar - - - - VolumeRenkoConsolidator - -
    - - Tick - - - - TradeBar - - - - VolumeRenkoConsolidator - -
    -

    - Range Consolidators -

    -

    - Most Range consolidators aggregate bars based on a fixed price movement. It is very much like a Renko Bar, but instead of consolidating a fix movement from the opening price, - a Range Bar would fix the range of the whole bar, i.e. High - Low. A new bar is formed when the range of the whole bar reached a preset value, thus it completely neglects the time - effect and focuses on the variance/information carried by the price movement. + . + For example, if you create a 7-minute consolidator, the first consolidated bar of each day spans the first 7 minutes of the regular trading session. + If you enable extended market hours for the asset and the consolidator, the first consolidated bar of each day spans the first 7 minutes of the pre-market trading session and continue in 7-minute increments from there.

    The following table shows which consolidator type to use based on the data format of the input and output: @@ -278951,8 +368598,8 @@

    - - RangeConsolidator + + MarketHourAwareConsolidator @@ -278968,8 +368615,8 @@

    - - RangeConsolidator + + MarketHourAwareConsolidator @@ -278985,8 +368632,8 @@

    - - RangeConsolidator + + MarketHourAwareConsolidator @@ -279002,8 +368649,8 @@

    - - RangeConsolidator + + MarketHourAwareConsolidator @@ -279017,7 +368664,7 @@

    For more information about sequential consolidators, see - + Combining Consolidators . @@ -279740,9 +369387,12 @@

    Consolidate Trade Bars

    You can also use the - + Consolidate + + consolidate + helper method to create period consolidators and register them for automatic updates. With just one line of code, you can create data in any time period based on a timedelta @@ -280117,9 +369767,12 @@

    Consolidate Quote Bars

    You can also use the - + Consolidate + + consolidate + helper method to create period consolidators and register them for automatic updates. With just one line of code, you can create data in any time period based on a timedelta @@ -280476,9 +370129,12 @@

    Consolidate Trade Ticks

    You can also use the - + Consolidate + + consolidate + helper method to create period consolidators and register them for automatic updates. With just one line of code, you can create data in any time period based on a timedelta @@ -280835,9 +370491,12 @@

    Consolidate Quote Ticks

    You can also use the - + Consolidate + + consolidate + helper method to create period consolidators and register them for automatic updates. With just one line of code, you can create data in any time period based on a timedelta @@ -281182,9 +370841,12 @@

    Consolidate Other Data

    You can also use the - + Consolidate + + consolidate + helper method to create period consolidators and register them for automatic updates. With just one line of code, you can create data in any time period based on a timedelta @@ -291403,9 +381065,1611 @@

    Examples

     

    - +
    +

    Consolidator Types

    +

    Market Hour Aware Consolidators

    +
    +
    +

    Introduction

    + + +

    + Market hour aware consolidators aggregate data based on the asset's + + trading hours + + . + For example, if you create a 7-minute consolidator, the first consolidated bar of each day spans the first 7 minutes of the regular trading session. + If you enable extended market hours for the asset and the consolidator, the first consolidated bar of each day spans the first 7 minutes of the pre-market trading session and continue in 7-minute increments from there. +

    + + + +

    Consolidate Trade Bars

    + + +

    + + TradeBar + + consolidators aggregate + + TradeBar + + objects into + + TradeBar + + objects of the same size or larger. Follow these steps to create and manage a + + TradeBar + + consolidator based on regular or extended market hours: +

    +
      +
    1. + Create the consolidator. +
    2. +

      + To create a market hour aware consolidator, pass the following arguments to the + + MarketHourAwareConsolidator + + constructor: +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      + Argument + + Data Type + + Description +
      + + dailyStrictEndTimeEnabled + + + daily_strict_end_time_enabled + + + + bool + + + Whether daily bars should have an + + + EndTime + + + end_time + + + that matches the market close time ( + + True + + + true + + ) instead of the following midnight ( + + False + + + false + + ). This argument usually matches your + + + DailyPreciseEndTime + + + daily_precise_end_time + + + + setting + + . +
      + + period + + + + TimeSpan + + + timedelta + + + The duration of the consolidation period. The first consolidation period of each day always starts at the beginning of the regular or extended market hours. +
      + + dataType + + + data_type + + + + Type + + + type + + + The type of data the consolidator produces. In this case, use + + TradeBar + + . +
      + + tickType + + + tick_type + + + + TickType + + + The type of data to consolidate. In this case, use + + TickType.Trade + + + TickType.TRADE + + . +
      + + extendedMarketHours + + + extended_market_hours + + + + bool + + + Whether or not to consolidate data from extended market hours. If + + False + + + false + + , the consolidator ignores data that occurs while the market is closed and anchors bars to the regular market open. If + + True + + + true + + , it anchors bars to the start of the extended market hours. +
      +
      +
      _consolidator = new MarketHourAwareConsolidator(true, TimeSpan.FromMinutes(7), typeof(TradeBar), TickType.Trade, false);
      +
      self._consolidator = MarketHourAwareConsolidator(True, timedelta(minutes=7), TradeBar, TickType.TRADE, False)
      +
      +
    3. + Add an event handler to the consolidator. +
    4. +
      +
      _consolidator.DataConsolidated += ConsolidationHandler;
      +
      self._consolidator.data_consolidated += self._consolidation_handler
      +
      +

      + LEAN passes consolidated bars to the consolidator event handler in your algorithm. The most common error when creating consolidators is to put parenthesis + + () + + at the end of your method name when setting the event handler of the consolidator. If you use parenthesis, the method executes and the result is passed as the event handler instead of the method itself. Remember to pass the name of your method to the event system. Specifically, it should be + + ConsolidationHandler + + + self._consolidation_handler + + , not + + ConsolidationHandler() + + + self._consolidation_handler() + + . +

      +
    5. + Define the consolidation handler. +
    6. +
      +
      void ConsolidationHandler(object sender, TradeBar consolidatedBar)
      +{
      +
      +}
      +
      def _consolidation_handler(self, sender: object, consolidated_bar: TradeBar) -> None:
      +    pass
      +
      +

      + When the consolidation period ends, LEAN passes the consolidated bar to the consolidation handler. +

      +
    7. + Update the consolidator. +
    8. +

      + You can automatically or manually update the consolidator. +

      +
        +
      • + Automatic Updates +
      • +

        + To automatically update a consolidator with data from the security subscription, call the + + AddConsolidator + + + add_consolidator + + method of the Subscription Manager. +

        +
        +
        self.subscription_manager.add_consolidator(self._symbol, self._consolidator)
        +
        SubscriptionManager.AddConsolidator(_symbol, _consolidator);
        +
        +
      • + Manual Updates +
      • +

        + Manual updates let you control when the consolidator updates and what data you use to update it. If you need to warm up a consolidator with data outside of the + + warm-up period + + , you can manually update the consolidator. To manually update a consolidator, call its + + Update + + + update + + method with a + + TradeBar + + object. You can update the consolidator with data from the + + Slice + + object in the + + OnData + + + on_data + + method or with data from a + + history request + + . +

        +
        +
        # Example 1: Update the consolidator with data from the Slice object
        +def on_data(self, slice: Slice) -> None:
        +    trade_bar = slice.bars[self._symbol]
        +    self._consolidator.update(trade_bar)
        +
        +# Example 2: Update the consolidator with data from a history request
        +history = self.history[TradeBar](self._symbol, 30, Resolution.MINUTE)
        +for trade_bar in history:
        +    self._consolidator.update(trade_bar)
        +
        // Example 1: Update the consolidator with data from the Slice object
        +public override void OnData(Slice slice)
        +{
        +    var tradeBar = slice.Bars[_symbol];
        +    _consolidator.Update(tradeBar);
        +}
        +
        +// Example 2: Update the consolidator with data from a history request
        +var history = History<TradeBar>(_symbol, 30, Resolution.Minute);
        +foreach (var tradeBar in history)
        +{
        +    _consolidator.Update(tradeBar);
        +}
        +
        +
      +
    9. + If you create consolidators for securities in a dynamic universe and register them for automatic updates, remove the consolidator when the security leaves the universe. +
    10. +
      +
      SubscriptionManager.RemoveConsolidator(_symbol, _consolidator);
      +
      self.subscription_manager.remove_consolidator(self._symbol, self._consolidator)
      +
      +

      + If you have a dynamic universe and don't remove consolidators, they compound internally, causing your algorithm to slow down and eventually die once it runs out of RAM. For an example of removing consolidators from universe subscriptions, see the + + GasAndCrudeOilEnergyCorrelationAlpha + + + GasAndCrudeOilEnergyCorrelationAlpha + + in the LEAN GitHub repository. +

      +
    + + + +

    Consolidate Quote Bars

    + + +

    + + QuoteBar + + consolidators aggregate + + QuoteBar + + objects into + + QuoteBar + + objects of the same size or larger. Follow these steps to create and manage a + + QuoteBar + + consolidator based on regular or extended market hours: +

    +
      +
    1. + Create the consolidator. +
    2. +

      + To create a market hour aware consolidator, pass the following arguments to the + + MarketHourAwareConsolidator + + constructor: +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      + Argument + + Data Type + + Description +
      + + dailyStrictEndTimeEnabled + + + daily_strict_end_time_enabled + + + + bool + + + Whether daily bars should have an + + + EndTime + + + end_time + + + that matches the market close time ( + + True + + + true + + ) instead of the following midnight ( + + False + + + false + + ). This argument usually matches your + + + DailyPreciseEndTime + + + daily_precise_end_time + + + + setting + + . +
      + + period + + + + TimeSpan + + + timedelta + + + The duration of the consolidation period. The first consolidation period of each day always starts at the beginning of the regular or extended market hours. +
      + + dataType + + + data_type + + + + Type + + + type + + + The type of data the consolidator produces. In this case, use + + QuoteBar + + . +
      + + tickType + + + tick_type + + + + TickType + + + The type of data to consolidate. In this case, use + + TickType.Quote + + + TickType.QUOTE + + . +
      + + extendedMarketHours + + + extended_market_hours + + + + bool + + + Whether or not to consolidate data from extended market hours. If + + False + + + false + + , the consolidator ignores data that occurs while the market is closed and anchors bars to the regular market open. If + + True + + + true + + , it anchors bars to the start of the extended market hours. +
      +
      +
      _consolidator = new MarketHourAwareConsolidator(true, TimeSpan.FromMinutes(7), typeof(QuoteBar), TickType.Quote, false);
      +
      self._consolidator = MarketHourAwareConsolidator(True, timedelta(minutes=7), QuoteBar, TickType.QUOTE, False)
      +
      +
    3. + Add an event handler to the consolidator. +
    4. +
      +
      _consolidator.DataConsolidated += ConsolidationHandler;
      +
      self._consolidator.data_consolidated += self._consolidation_handler
      +
      +

      + LEAN passes consolidated bars to the consolidator event handler in your algorithm. The most common error when creating consolidators is to put parenthesis + + () + + at the end of your method name when setting the event handler of the consolidator. If you use parenthesis, the method executes and the result is passed as the event handler instead of the method itself. Remember to pass the name of your method to the event system. Specifically, it should be + + ConsolidationHandler + + + self._consolidation_handler + + , not + + ConsolidationHandler() + + + self._consolidation_handler() + + . +

      +
    5. + Define the consolidation handler. +
    6. +
      +
      void ConsolidationHandler(object sender, QuoteBar consolidatedBar)
      +{
      +
      +}
      +
      def _consolidation_handler(self, sender: object, consolidated_bar: QuoteBar) -> None:
      +    pass
      +
      +

      + When the consolidation period ends, LEAN passes the consolidated bar to the consolidation handler. +

      +
    7. + Update the consolidator. +
    8. +

      + You can automatically or manually update the consolidator. +

      +
        +
      • + Automatic Updates +
      • +

        + To automatically update a consolidator with data from the security subscription, call the + + AddConsolidator + + + add_consolidator + + method of the Subscription Manager. +

        +
        +
        self.subscription_manager.add_consolidator(self._symbol, self._consolidator)
        +
        SubscriptionManager.AddConsolidator(_symbol, _consolidator);
        +
        +
      • + Manual Updates +
      • +

        + Manual updates let you control when the consolidator updates and what data you use to update it. If you need to warm up a consolidator with data outside of the + + warm-up period + + , you can manually update the consolidator. To manually update a consolidator, call its + + Update + + + update + + method with a + + QuoteBar + + object. You can update the consolidator with data from the + + Slice + + object in the + + OnData + + + on_data + + method or with data from a + + history request + + . +

        +
        +
        # Example 1: Update the consolidator with data from the Slice object
        +def on_data(self, slice: Slice) -> None:
        +    quote_bar = slice.quote_bars[self._symbol]
        +    self._consolidator.update(quote_bar)
        +
        +# Example 2: Update the consolidator with data from a history request
        +history = self.history[QuoteBar](self._symbol, 30, Resolution.MINUTE)
        +for quote_bar in history:
        +    self._consolidator.update(quote_bar)
        +
        // Example 1: Update the consolidator with data from the Slice object
        +public override void OnData(Slice slice)
        +{
        +    var quoteBar = slice.QuoteBars[_symbol];
        +    _consolidator.Update(quoteBar);
        +}
        +
        +// Example 2: Update the consolidator with data from a history request
        +var history = History<QuoteBar>(_symbol, 30, Resolution.Minute);
        +foreach (var quoteBar in history)
        +{
        +    _consolidator.Update(quoteBar);
        +}
        +
        +
      +
    9. + If you create consolidators for securities in a dynamic universe and register them for automatic updates, remove the consolidator when the security leaves the universe. +
    10. +
      +
      SubscriptionManager.RemoveConsolidator(_symbol, _consolidator);
      +
      self.subscription_manager.remove_consolidator(self._symbol, self._consolidator)
      +
      +

      + If you have a dynamic universe and don't remove consolidators, they compound internally, causing your algorithm to slow down and eventually die once it runs out of RAM. For an example of removing consolidators from universe subscriptions, see the + + GasAndCrudeOilEnergyCorrelationAlpha + + + GasAndCrudeOilEnergyCorrelationAlpha + + in the LEAN GitHub repository. +

      +
    + + + +

    Consolidate Trade Ticks

    + + +

    + + Tick + + consolidators aggregate + + Tick + + objects into + + TradeBar + + objects. Follow these steps to create and manage a + + Tick + + consolidator based on regular or extended market hours: +

    +
      +
    1. + Create the consolidator. +
    2. +

      + To create a market hour aware consolidator, pass the following arguments to the + + MarketHourAwareConsolidator + + constructor: +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      + Argument + + Data Type + + Description +
      + + dailyStrictEndTimeEnabled + + + daily_strict_end_time_enabled + + + + bool + + + Whether daily bars should have an + + + EndTime + + + end_time + + + that matches the market close time ( + + True + + + true + + ) instead of the following midnight ( + + False + + + false + + ). This argument usually matches your + + + DailyPreciseEndTime + + + daily_precise_end_time + + + + setting + + . +
      + + period + + + + TimeSpan + + + timedelta + + + The duration of the consolidation period. The first consolidation period of each day always starts at the beginning of the regular or extended market hours. +
      + + dataType + + + data_type + + + + Type + + + type + + + The type of data the consolidator produces. In this case, use + + Tick + + . +
      + + tickType + + + tick_type + + + + TickType + + + The type of data to consolidate. In this case, use + + TickType.Trade + + + TickType.TRADE + + . +
      + + extendedMarketHours + + + extended_market_hours + + + + bool + + + Whether or not to consolidate data from extended market hours. If + + False + + + false + + , the consolidator ignores data that occurs while the market is closed and anchors bars to the regular market open. If + + True + + + true + + , it anchors bars to the start of the extended market hours. +
      +
      +
      _consolidator = new MarketHourAwareConsolidator(true, TimeSpan.FromMinutes(7), typeof(Tick), TickType.Trade, false);
      +
      self._consolidator = MarketHourAwareConsolidator(True, timedelta(minutes=7), Tick, TickType.TRADE, False)
      +
      +
    3. + Add an event handler to the consolidator. +
    4. +
      +
      _consolidator.DataConsolidated += ConsolidationHandler;
      +
      self._consolidator.data_consolidated += self._consolidation_handler
      +
      +

      + LEAN passes consolidated bars to the consolidator event handler in your algorithm. The most common error when creating consolidators is to put parenthesis + + () + + at the end of your method name when setting the event handler of the consolidator. If you use parenthesis, the method executes and the result is passed as the event handler instead of the method itself. Remember to pass the name of your method to the event system. Specifically, it should be + + ConsolidationHandler + + + self._consolidation_handler + + , not + + ConsolidationHandler() + + + self._consolidation_handler() + + . +

      +
    5. + Define the consolidation handler. +
    6. +
      +
      void ConsolidationHandler(object sender, TradeBar consolidatedBar)
      +{
      +
      +}
      +
      def _consolidation_handler(self, sender: object, consolidated_bar: TradeBar) -> None:
      +    pass
      +
      +

      + When the consolidation period ends, LEAN passes the consolidated bar to the consolidation handler. +

      +
    7. + Update the consolidator. +
    8. +

      + You can automatically or manually update the consolidator. +

      +
        +
      • + Automatic Updates +
      • +

        + To automatically update a consolidator with data from the security subscription, call the + + AddConsolidator + + + add_consolidator + + method of the Subscription Manager. +

        +
        +
        self.subscription_manager.add_consolidator(self._symbol, self._consolidator)
        +
        SubscriptionManager.AddConsolidator(_symbol, _consolidator);
        +
        +
      • + Manual Updates +
      • +

        + Manual updates let you control when the consolidator updates and what data you use to update it. If you need to warm up a consolidator with data outside of the + + warm-up period + + , you can manually update the consolidator. To manually update a consolidator, call its + + Update + + + update + + method with a + + Tick + + object. You can update the consolidator with data from the + + Slice + + object in the + + OnData + + + on_data + + method or with data from a + + history request + + . +

        +
        +
        # Example 1: Update the consolidator with data from the Slice object
        +def on_data(self, slice: Slice) -> None:
        +    ticks = slice.ticks[self._symbol]
        +    for tick in ticks:
        +        self._consolidator.update(tick)
        +
        +# Example 2: Update the consolidator with data from a history request
        +ticks = self.history[Tick](self._symbol, timedelta(minutes=3), Resolution.TICK)
        +for tick in ticks:
        +    self._consolidator.update(tick)
        +
        // Example 1: Update the consolidator with data from the Slice object
        +public override void OnData(Slice slice)
        +{
        +    var ticks = slice.Ticks[_symbol];
        +    foreach (var tick in ticks)
        +    {
        +    	_consolidator.Update(tick);
        +    }
        +}
        +
        +// Example 2: Update the consolidator with data from a history request
        +var ticks = History<Tick>(_symbol, TimeSpan.FromMinutes(3), Resolution.Tick);
        +foreach (var tick in ticks)
        +{
        +    _consolidator.Update(tick);
        +}
        +
        +
      +
    9. + If you create consolidators for securities in a dynamic universe and register them for automatic updates, remove the consolidator when the security leaves the universe. +
    10. +
      +
      SubscriptionManager.RemoveConsolidator(_symbol, _consolidator);
      +
      self.subscription_manager.remove_consolidator(self._symbol, self._consolidator)
      +
      +

      + If you have a dynamic universe and don't remove consolidators, they compound internally, causing your algorithm to slow down and eventually die once it runs out of RAM. For an example of removing consolidators from universe subscriptions, see the + + GasAndCrudeOilEnergyCorrelationAlpha + + + GasAndCrudeOilEnergyCorrelationAlpha + + in the LEAN GitHub repository. +

      +
    + + + +

    Consolidate Quote Ticks

    + + +

    + + Tick + + quote bar consolidators aggregate + + Tick + + objects that represent quotes into + + QuoteBar + + objects. Follow these steps to create and manage a + + Tick + + quote bar consolidator based on regular or extended market hours: +

    +
      +
    1. + Create the consolidator. +
    2. +

      + To create a market hour aware consolidator, pass the following arguments to the + + MarketHourAwareConsolidator + + constructor: +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      + Argument + + Data Type + + Description +
      + + dailyStrictEndTimeEnabled + + + daily_strict_end_time_enabled + + + + bool + + + Whether daily bars should have an + + + EndTime + + + end_time + + + that matches the market close time ( + + True + + + true + + ) instead of the following midnight ( + + False + + + false + + ). This argument usually matches your + + + DailyPreciseEndTime + + + daily_precise_end_time + + + + setting + + . +
      + + period + + + + TimeSpan + + + timedelta + + + The duration of the consolidation period. The first consolidation period of each day always starts at the beginning of the regular or extended market hours. +
      + + dataType + + + data_type + + + + Type + + + type + + + The type of data the consolidator produces. In this case, use + + Tick + + . +
      + + tickType + + + tick_type + + + + TickType + + + The type of data to consolidate. In this case, use + + TickType.Quote + + + TickType.QUOTE + + . +
      + + extendedMarketHours + + + extended_market_hours + + + + bool + + + Whether or not to consolidate data from extended market hours. If + + False + + + false + + , the consolidator ignores data that occurs while the market is closed and anchors bars to the regular market open. If + + True + + + true + + , it anchors bars to the start of the extended market hours. +
      +
      +
      _consolidator = new MarketHourAwareConsolidator(true, TimeSpan.FromMinutes(7), typeof(Tick), TickType.Quote, false);
      +
      self._consolidator = MarketHourAwareConsolidator(True, timedelta(minutes=7), Tick, TickType.QUOTE, False)
      +
      +
    3. + Add an event handler to the consolidator. +
    4. +
      +
      _consolidator.DataConsolidated += ConsolidationHandler;
      +
      self._consolidator.data_consolidated += self._consolidation_handler
      +
      +

      + LEAN passes consolidated bars to the consolidator event handler in your algorithm. The most common error when creating consolidators is to put parenthesis + + () + + at the end of your method name when setting the event handler of the consolidator. If you use parenthesis, the method executes and the result is passed as the event handler instead of the method itself. Remember to pass the name of your method to the event system. Specifically, it should be + + ConsolidationHandler + + + self._consolidation_handler + + , not + + ConsolidationHandler() + + + self._consolidation_handler() + + . +

      +
    5. + Define the consolidation handler. +
    6. +
      +
      void ConsolidationHandler(object sender, QuoteBar consolidatedBar)
      +{
      +
      +}
      +
      def _consolidation_handler(self, sender: object, consolidated_bar: QuoteBar) -> None:
      +    pass
      +
      +

      + When the consolidation period ends, LEAN passes the consolidated bar to the consolidation handler. +

      +
    7. + Update the consolidator. +
    8. +

      + You can automatically or manually update the consolidator. +

      +
        +
      • + Automatic Updates +
      • +

        + To automatically update a consolidator with data from the security subscription, call the + + AddConsolidator + + + add_consolidator + + method of the Subscription Manager. +

        +
        +
        self.subscription_manager.add_consolidator(self._symbol, self._consolidator)
        +
        SubscriptionManager.AddConsolidator(_symbol, _consolidator);
        +
        +
      • + Manual Updates +
      • +

        + Manual updates let you control when the consolidator updates and what data you use to update it. If you need to warm up a consolidator with data outside of the + + warm-up period + + , you can manually update the consolidator. To manually update a consolidator, call its + + Update + + + update + + method with a + + Tick + + object. You can update the consolidator with data from the + + Slice + + object in the + + OnData + + + on_data + + method or with data from a + + history request + + . +

        +
        +
        # Example 1: Update the consolidator with data from the Slice object
        +def on_data(self, slice: Slice) -> None:
        +    ticks = slice.ticks[self._symbol]
        +    for tick in ticks:
        +        self._consolidator.update(tick)
        +
        +# Example 2: Update the consolidator with data from a history request
        +ticks = self.history[Tick](self._symbol, timedelta(minutes=3), Resolution.TICK)
        +for tick in ticks:
        +    self._consolidator.update(tick)
        +
        // Example 1: Update the consolidator with data from the Slice object
        +public override void OnData(Slice slice)
        +{
        +    var ticks = slice.Ticks[_symbol];
        +    foreach (var tick in ticks)
        +    {
        +    	_consolidator.Update(tick);
        +    }
        +}
        +
        +// Example 2: Update the consolidator with data from a history request
        +var ticks = History<Tick>(_symbol, TimeSpan.FromMinutes(3), Resolution.Tick);
        +foreach (var tick in ticks)
        +{
        +    _consolidator.Update(tick);
        +}
        +
        +
      +
    9. + If you create consolidators for securities in a dynamic universe and register them for automatic updates, remove the consolidator when the security leaves the universe. +
    10. +
      +
      SubscriptionManager.RemoveConsolidator(_symbol, _consolidator);
      +
      self.subscription_manager.remove_consolidator(self._symbol, self._consolidator)
      +
      +

      + If you have a dynamic universe and don't remove consolidators, they compound internally, causing your algorithm to slow down and eventually die once it runs out of RAM. For an example of removing consolidators from universe subscriptions, see the + + GasAndCrudeOilEnergyCorrelationAlpha + + + GasAndCrudeOilEnergyCorrelationAlpha + + in the LEAN GitHub repository. +

      +
    + + + +

    Reset Consolidators

    + + +

    + To reset a consolidator, call its + + Reset + + + reset + + method. +

    +
    +
    self._consolidator.reset() 
    +
    +
    _consolidator.Reset();
    +
    +

    + If you are live trading Equities or backtesting Equities without the adjusted + + data normalization mode + + , + reset your consolidators when + + splits + + and + + dividends + + occur. + When a split or dividend occurs while the consolidator is in the process of building a bar, the open, high, and low may reflect prices from before the split or dividend. + To avoid issues, call the consolidator's + + Reset + + + reset + + method and then warm it up with + + ScaledRaw + + + SCALED_RAW + + data from a + + history request + + . +

    +
    +
    def on_data(self, data: Slice):
    +    # When a split or dividend occurs...
    +    if (data.splits.contains_key(self._symbol) and data.splits[self._symbol].type == SplitType.SPLIT_OCCURRED or 
    +        data.dividends.contains_key(self._symbol)):
    +        # If the consolidator is working on a bar...
    +        if self._consolidator.working_data:
    +            # Get adjusted prices for the time period of the working bar.
    +            history = self.history[TradeBar](self._symbol, self._consolidator.working_data.time, self.time, data_normalization_mode=DataNormalizationMode.SCALED_RAW)
    +            # Reset the consolidator.
    +            self._consolidator.reset()
    +            # Warm-up the consolidator with the adjusted price data.
    +            for bar in history:
    +                self._consolidator.update(bar)
    +
    public override void OnData(Slice data)
    +{
    +    // When a split or dividend occurs...
    +    if ((data.Splits.ContainsKey(_symbol) && data.Splits[_symbol].Type == SplitType.SplitOccurred) ||
    +        data.Dividends.ContainsKey(_symbol))
    +    {
    +        // If the consolidator is working on a bar...
    +        if (_consolidator.WorkingData != null)
    +        {
    +            // Get adjusted prices for the time period of the working bar.
    +            var history = History<TradeBar>(_symbol, _consolidator.WorkingData.Time, Time, dataNormalizationMode: DataNormalizationMode.ScaledRaw);
    +            // Reset the consolidator.
    +            _consolidator.Reset();
    +            // Warm-up the consolidator with the adjusted price data.
    +            foreach (var bar in history)
    +            {
    +                _consolidator.Update(bar);
    +            }
    +        }
    +    }
    +}
    +
    + + + +

    Examples

    + + +

    + The following examples demonstrate some common practices for market hour aware consolidators. +

    +

    + Example 1: Intraday Bars Anchored to the Market Open +

    +

    + The following algorithm creates a + + MarketHourAwareConsolidator + + with a 7-minute period and registers it for automatic updates. + The consolidator anchors intraday bars to the market open, so the first 7-minute bar of each day arrives 7 minutes after the market opens. +

    +
    +
    public class MarketHourAwareConsolidatorExampleAlgorithm : QCAlgorithm
    +{
    +    private MarketHourAwareConsolidator _consolidator;
    +
    +    public override void Initialize()
    +    {
    +        SetStartDate(2024, 9, 1);
    +        SetEndDate(2024, 12, 31);
    +        var symbol = AddEquity("SPY", Resolution.Minute).Symbol;
    +        // Create a market hour aware consolidator that builds 7-minute TradeBar objects anchored to the market open.
    +        _consolidator = new MarketHourAwareConsolidator(true, TimeSpan.FromMinutes(7), typeof(TradeBar), TickType.Trade, false);
    +        // Attach a consolidation handler. The DataConsolidated event provides the bar as an
    +        // IBaseData object, so cast it to a TradeBar.
    +        _consolidator.DataConsolidated += (sender, consolidated) => OnBar(sender, (TradeBar)consolidated);
    +        // Register the consolidator for automatic updates.
    +        SubscriptionManager.AddConsolidator(symbol, _consolidator);
    +    }
    +
    +    private void OnBar(object sender, TradeBar bar)
    +    {
    +        // Plot the close of each 7-minute bar built from regular trading hours only.
    +        Plot("SPY", "7-Minute Close", bar.Close);
    +    }
    +}
    +
    class MarketHourAwareConsolidatorExampleAlgorithm(QCAlgorithm):
    +
    +    def initialize(self) -> None:
    +        self.set_start_date(2024, 9, 1)
    +        self.set_end_date(2024, 12, 31)
    +        self._symbol = self.add_equity("SPY", Resolution.MINUTE).symbol
    +        # Create a market hour aware consolidator that builds 7-minute TradeBar objects anchored to the market open.
    +        self._consolidator = MarketHourAwareConsolidator(True, timedelta(minutes=7), TradeBar, TickType.TRADE, False)
    +        # Attach a consolidation handler.
    +        self._consolidator.data_consolidated += self._on_bar
    +        # Register the consolidator for automatic updates.
    +        self.subscription_manager.add_consolidator(self._symbol, self._consolidator)
    +
    +    def _on_bar(self, sender: object, bar: TradeBar) -> None:
    +        # Plot the close of each 7-minute bar built from regular trading hours only.
    +        self.plot("SPY", "7-Minute Close", bar.close)
    +
    + + + +

     

    + +
    +

    Consolidator Types

    Combining Consolidators

    @@ -296314,7 +387578,7 @@

    Ticks

    history = self.history[Tick](symbol, timedelta(2), Resolution.TICK) # Iterate through each quote tick and calculate the quote size. for tick in history: - if tick.tick_type == TickType.Quote: + if tick.tick_type == TickType.QUOTE: t = tick.end_time size = max(tick.bid_size, tick.ask_size) @@ -301359,7 +392623,7 @@

    if option_chain: for option in option_chain: # Request historical quote data for signal generation. - history = self.history(QuoteBar, option.symbol, 15, Resolution.Minute) + history = self.history(QuoteBar, option.symbol, 15, Resolution.MINUTE) if not history.empty: # Calculate total bid and ask dollar volume to determine the capital directional force. total_bid_volume = (history['bidclose'] * history['bidsize']).sum() @@ -302442,7 +393706,7 @@

    Ticks

    history = self.history[Tick](symbol, timedelta(2), Resolution.TICK) # Iterate through each quote tick and calculate the quote size. for tick in history: - if tick.tick_type == TickType.Quote: + if tick.tick_type == TickType.QUOTE: t = tick.end_time size = max(tick.bid_size, tick.ask_size) @@ -305003,7 +396267,7 @@

    Ticks

    history = self.history[Tick](symbol, timedelta(2), Resolution.TICK) # Iterate through each quote tick and calculate the quote size. for tick in history: - if tick.tick_type == TickType.Quote: + if tick.tick_type == TickType.QUOTE: t = tick.end_time size = max(tick.bid_size, tick.ask_size) @@ -307985,7 +399249,7 @@

    Ticks

    history = self.history[Tick](symbol, timedelta(2), Resolution.TICK) # Iterate through each quote tick and calculate the quote size. for tick in history: - if tick.tick_type == TickType.Quote: + if tick.tick_type == TickType.QUOTE: t = tick.end_time size = max(tick.bid_size, tick.ask_size) @@ -310903,7 +402167,7 @@

    Indicators

    # Get the Symbol of the mirror contract. mirror = Symbol.create_option( option.underlying, option.id.market, option.id.option_style, - OptionRight.Call if option.id.option_right == OptionRight.PUT else OptionRight.PUT, + OptionRight.CALL if option.id.option_right == OptionRight.PUT else OptionRight.PUT, option.id.strike_price, option.id.date ) # Create the indicator. @@ -311031,7 +402295,7 @@

    for option_chain in self.current_slice.option_chains.values(): for option in option_chain: # Request historical quote data for signal generation. - history = self.history(QuoteBar, option.symbol, 15, Resolution.Minute) + history = self.history(QuoteBar, option.symbol, 15, Resolution.MINUTE) if not history.empty: # Calculate the total bid and ask for dollar volume to determine the capital directional force. total_bid_volume = 0 @@ -324948,7 +416212,7 @@

    def on_data(self, slice: Slice) -> None: bar = slice.bars.get(self._symbol) # Place order if not invested. Make sure only trade once a day to avoid chasing the loss. - if not self.portfolio.Invested and self._day != slice.time.day and bar: + if not self.portfolio.invested and self._day != slice.time.day and bar: self.market_order(self._symbol, 10) # Update the day variable to avoid repeat ordering. self._day = slice.time.day @@ -329912,20 +421176,29 @@

    Requirements

    MOO orders don't support the - + GoodTilDate + + good_til_date + time in force . If you submit a MOO order with the - + GoodTilDate + + good_til_date + time in force, LEAN automatically adjusts the time in force to be - + GoodTilCanceled + + GOOD_TIL_CANCELED + .

    @@ -330473,20 +421746,29 @@

    Requirements

    MOC orders don't support the - + GoodTilDate + + good_til_date + time in force . If you submit a MOC order with the - + GoodTilDate + + good_til_date + time in force, LEAN automatically adjusts the time in force to be - + GoodTilCanceled + + GOOD_TIL_CANCELED + .

    @@ -331168,11 +422450,11 @@

    Introduction

    limit orders - for muliple securities. Combo limit orders are different from + for multiple securities. Combo limit orders are different from combo leg limit orders - because you must set the limit price of all the leg orders to be the same with combo limit orders. With combo leg limit orders, you can create the order without forcing each leg to have the same limit price. Combo limit orders currently only work for trading Option contracts and their underlying Equities. + because you set a single limit price for the combined (net) price of all the legs, rather than a separate limit price per leg. Combo limit orders currently only work for trading Option contracts and their underlying Equities.

    @@ -331859,11 +423141,11 @@

    Introduction

    limit orders - for muliple securities. Combo leg limit orders are different from + for multiple securities. Combo leg limit orders are different from combo limit orders - because you can create combo leg limit orders without forcing each leg to have the same limit price. Combo leg limit orders currently only work for trading Option contracts. + because you set a separate limit price for each leg, rather than a single limit price for the combined (net) price of all the legs. Combo leg limit orders currently only work for trading Option contracts.

    @@ -343961,7 +435243,7 @@

    Implementation

    near_call = call_contracts[0] far_call = call_contracts[1] near_put = put_contracts[1] - far_put = [x for x in put_contracts if x.Strike == near_put.strike - far_call.strike + near_call.strike][0] + far_put = [x for x in put_contracts if x.strike == near_put.strike - far_call.strike + near_call.strike][0]
  • In the @@ -344380,7 +435662,7 @@

    Example

    near_call = call_contracts[0] far_call = call_contracts[1] near_put = put_contracts[1] - far_put = [x for x in put_contracts if x.Strike == near_put.strike - far_call.strike + near_call.strike][0] + far_put = [x for x in put_contracts if x.strike == near_put.strike - far_call.strike + near_call.strike][0] iron_condor = OptionStrategies.iron_condor( self._symbol, @@ -345033,7 +436315,7 @@

    Example

    near_call = call_contracts[0] far_call = call_contracts[1] near_put = put_contracts[1] - far_put = [x for x in put_contracts if x.Strike == near_put.strike - far_call.strike + near_call.strike][0] + far_put = [x for x in put_contracts if x.strike == near_put.strike - far_call.strike + near_call.strike][0] short_iron_condor = OptionStrategies.short_iron_condor( self._symbol, @@ -346761,7 +438043,7 @@

    Example

    # Find ATM put with the farthest expiry expiry = max([x.expiry for x in chain]) put_contracts = sorted([x for x in chain - if x.right == OptionRight.Put and x.expiry == expiry], + if x.right == OptionRight.PUT and x.expiry == expiry], key=lambda x: abs(chain.underlying.price - x.strike)) if not put_contracts: @@ -348319,7 +439601,7 @@

    Implementation

    call_contracts = sorted([contract for contract in contracts if contract.right == OptionRight.CALL and contract.strike > chain.underlying.price], - key=lambda x: x.Strike) + key=lambda x: x.strike) if not call_contracts: return @@ -348327,7 +439609,7 @@

    Implementation

    put_contracts = sorted([contract for contract in contracts if contract.right == OptionRight.PUT and contract.strike < chain.underlying.price], - key=lambda x: x.Strike, reverse=True) + key=lambda x: x.strike, reverse=True) if not put_contracts: return @@ -349642,7 +440924,7 @@

    Example

    expiry = sorted(chain, key = lambda x: x.expiry)[-1].expiry # select ATM strike price - strike = sorted(chain, key = lambda x: abs(x.Strike - chain.underlying.price))[0].strike + strike = sorted(chain, key = lambda x: abs(x.strike - chain.underlying.price))[0].strike # Order Strategy conversion = OptionStrategies.conversion(self.symbol, strike, expiry) @@ -350117,7 +441399,7 @@

    Example

    expiry = sorted(chain, key = lambda x: x.expiry)[-1].expiry # select ATM strike price - strike = sorted(chain, key = lambda x: abs(x.Strike - chain.underlying.price))[0].strike + strike = sorted(chain, key = lambda x: abs(x.strike - chain.underlying.price))[0].strike # Order Strategy reverse_conversion = OptionStrategies.reverse_conversion(self.symbol, strike, expiry) @@ -350632,7 +441914,7 @@

    Example

    self.universe_settings.asynchronous = True option = self.add_option("GOOG", Resolution.MINUTE) - self._symbol = option.Symbol + self._symbol = option.symbol option.set_filter(lambda universe: universe.box_spread(30, 5)) def on_data(self, slice: Slice) -> None: @@ -350657,7 +441939,7 @@

    Example

    def on_end_of_day(self, symbol: Symbol) -> None: if symbol == self._symbol.underlying: - self.Log(f"{self.time}::{symbol}::{self.securities[symbol].price}") + self.log(f"{self.time}::{symbol}::{self.securities[symbol].price}") @@ -351168,7 +442450,7 @@

    Example

    self.universe_settings.asynchronous = True self.equity_symbol = self.add_equity("GOOG").symbol - option = self.add_option("GOOG", Resolution.Minute) + option = self.add_option("GOOG", Resolution.MINUTE) self.option_symbol = option.symbol option.set_filter(lambda universe: universe.box_spread(30, 5)) @@ -356494,9 +447776,12 @@

    Time In Force

    good_til_date
    time in force, LEAN automatically adjusts the time in force to be - + GoodTilCanceled + + GOOD_TIL_CANCELED + .

    @@ -356613,7 +447898,11 @@

    Tags

    Order - class you can set. To set an order tag, pass it as an argument when you create the order or use the order update methods. + class you can set. To set an order tag, pass it as an argument when you create the order or use the order update methods. This is useful for + + debugging + + your algorithm's trading logic because you can include the indicator values or conditions that triggered the order.

  • @@ -361361,9 +452656,12 @@

    Limit if Touched Orders

    Tick
    data and the most recent batch of ticks contains a tick of type - + TickType.Trade + + TickType.TRADE + , use the last trade price.
  • @@ -362126,219 +453424,225 @@

    Market on Open Orders

    Tick
    data and the most recent batch of ticks contains a tick of type - + + TickType.Trade + + + TickType.TRADE + + , use the last trade price. +
  • +
  • + If the subscription provides + + TradeBar + + data, use the closing bid price of the most recent + + QuoteBar + + . +
  • + + + + +

    Market on Close Orders

    + + +

    + The following table describes the fill price of market on close orders for each data format and order direction: +
    +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Data Format + + Order Direction + + Fill Price +
    +
    + + Tick + + + Buy + + If the model receives the + + official closing auction price + + within one minute after the close, the order fills at official close price + slippage. After one minute, the order fills at the most recent trade price + slippage. If the security doesn't trade within the first two minutes, the order fills at the best effort ask price + slippage. +
    + + Tick + + + Sell + + If the model receives the + + official closing auction price + + within one minute after the close, the order fills at the official close price - slippage. After one minute, the order fills at the most recent trade price - slippage. If the security doesn't trade within the first two minutes after the close, the order fills at the best effort bid price - slippage. +
    + + TradeBar + + + Buy + + Open price + slippage +
    +
    + + TradeBar + + + Sell + + Open price - slippage +
    +
    + + QuoteBar + + + Buy + + Best effort ask price + slippage +
    +
    + + QuoteBar + + + Sell + + Best effort bid price - slippage +
    +
    +

    + The model checks the data format in the following order: +

    +
      +
    1. + + Tick + +
    2. +
    3. + + TradeBar + +
    4. +
    5. + + QuoteBar + +
    6. +
    +

    + To get the best effort bid price, the model uses the following procedure: +

    +
      +
    1. + If the subscription provides + + Tick + + data and the most recent batch of ticks contains a buy quote, use the bid price of the most recent quote tick. +
    2. +
    3. + If the subscription provides + + QuoteBar + + data, use the closing bid price of the most recent + + QuoteBar + + . +
    4. +
    +

    + To get the best effort ask price, the model uses the following procedure: +

    +
      +
    1. + If the subscription provides + + Tick + + data and the most recent batch of ticks contains a sell quote, use the ask price of the most recent quote tick. +
    2. +
    3. + If the subscription provides + + QuoteBar + + data, use the closing ask price of the most recent + + QuoteBar + + . +
    4. +
    +

    + If neither of the preceding procedures yield a result, the model uses the following procedure to get the best effort bid or ask price: +

    +
      +
    1. + If the subscription provides + + Tick + + data and the most recent batch of ticks contains a tick of type + TickType.Trade - , use the last trade price. -
    2. -
    3. - If the subscription provides - - TradeBar - - data, use the closing bid price of the most recent - - QuoteBar - - . -
    4. -
    - - - -

    Market on Close Orders

    - - -

    - The following table describes the fill price of market on close orders for each data format and order direction: -
    -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - Data Format - - Order Direction - - Fill Price -
    -
    - - Tick - - - Buy - - If the model receives the - - official closing auction price - - within one minute after the close, the order fills at official close price + slippage. After one minute, the order fills at the most recent trade price + slippage. If the security doesn't trade within the first two minutes, the order fills at the best effort ask price + slippage. -
    - - Tick - - - Sell - - If the model receives the - - official closing auction price - - within one minute after the close, the order fills at the official close price - slippage. After one minute, the order fills at the most recent trade price - slippage. If the security doesn't trade within the first two minutes after the close, the order fills at the best effort bid price - slippage. -
    - - TradeBar - - - Buy - - Open price + slippage -
    -
    - - TradeBar - - - Sell - - Open price - slippage -
    -
    - - QuoteBar - - - Buy - - Best effort ask price + slippage -
    -
    - - QuoteBar - - - Sell - - Best effort bid price - slippage -
    -
    -

    - The model checks the data format in the following order: -

    -
      -
    1. - - Tick - -
    2. -
    3. - - TradeBar - -
    4. -
    5. - - QuoteBar - -
    6. -
    -

    - To get the best effort bid price, the model uses the following procedure: -

    -
      -
    1. - If the subscription provides - - Tick - - data and the most recent batch of ticks contains a buy quote, use the bid price of the most recent quote tick. -
    2. -
    3. - If the subscription provides - - QuoteBar - - data, use the closing bid price of the most recent - - QuoteBar - - . -
    4. -
    -

    - To get the best effort ask price, the model uses the following procedure: -

    -
      -
    1. - If the subscription provides - - Tick - - data and the most recent batch of ticks contains a sell quote, use the ask price of the most recent quote tick. -
    2. -
    3. - If the subscription provides - - QuoteBar - - data, use the closing ask price of the most recent - - QuoteBar - - . -
    4. -
    -

    - If neither of the preceding procedures yield a result, the model uses the following procedure to get the best effort bid or ask price: -

    -
      -
    1. - If the subscription provides - - Tick - - data and the most recent batch of ticks contains a tick of type - - TickType.Trade + + TickType.TRADE , use the last trade price.
    2. @@ -363898,9 +455202,12 @@

      Limit if Touched Orders

      Tick
      data and the most recent batch of ticks contains a tick of type - + TickType.Trade + + TickType.TRADE + , use the last trade price.
    3. @@ -366805,9 +458112,12 @@

      Limit if Touched Orders

      Tick
      data and the most recent batch of ticks contains a tick of type - + TickType.Trade + + TickType.TRADE + , use the last trade price.
    4. @@ -369712,9 +461022,12 @@

      Limit if Touched Orders

      Tick
      data and the most recent batch of ticks contains a tick of type - + TickType.Trade + + TickType.TRADE + , use the last trade price.
    5. @@ -372639,9 +463952,12 @@

      Limit if Touched Orders

      Tick
      data and the most recent batch of ticks contains a tick of type - + TickType.Trade + + TickType.TRADE + , use the last trade price.
    6. @@ -379743,9 +471059,9 @@

      TradeStation Model

      TradeStationFeeModel - models -
      - the fees of TradeStation + models the bespoke, improved fees that a QuantConnect user is given on + + TradeStation .

      @@ -379788,19 +471104,69 @@

      Equity Options

      - Equity Option trades cost $0.60 USD per contract. For non-US residents, they cost an extra $5 USD per order. + Equity Option trades are free, (but incur $0.80c exchange fees per contract). For non-US residents, they cost an extra $5 USD per order.

      Index Options

      - Index Option trades cost $1.00 USD per contract. For non-US residents, they cost an extra $5 USD per order. + Index Option trades are free, (but incur $0.80c exchange fees per contract). For non-US residents, they cost an extra $5 USD per order.

      Futures

      - Futures trades cost $1.50 USD per contract. + Futures trades cost $1.75 USD per contract, or $0.50 for mini-contracts. +

      + + + +

      Webull Model

      + + +

      + The + + WebullFeeModel + + models + + the fees of Webull + + . +

      +
      +
      security.SetFeeModel(new WebullFeeModel());
      +
      security.set_fee_model(WebullFeeModel())
      +
      +

      + The + + WebullFeeModel + + charges $0 USD for Equity and Equity Option trades. For Index Options, it charges a $0.50 USD Webull fee per contract plus a per-contract exchange fee that depends on the underlying Index (for example, SPX, SPXW, VIX, VIXW, XSP, DJX, NDX, and NDXP). +

      +

      + For more information about this model, see the + + class reference + + and + + implementation + + . +

      +

      + For more information about this model, see the + + class reference + + and + + implementation + + .

      @@ -380107,7 +471473,7 @@

      Set Models

      def initialize(self) -> None:
       	# Set the brokerage model to backtest with the most realistic scenario to handle the order validity, margin rate and transaction fee
       	self.set_brokerage_model(BrokerageName.OANDA_BROKERAGE) # Defaults to margin account
      -	self.set_brokerage_model(BrokerageName.BITFINEX, AccountType.MARGIN) # Overrides the default account type, which is AccountType.Cash
      +	self.set_brokerage_model(BrokerageName.BITFINEX, AccountType.MARGIN) # Overrides the default account type, which is AccountType.CASH
       

      @@ -380675,8 +472041,19 @@

      +

    7. -
    8. - - GoodTilDate - - - good_til_date - -
    9. +
    10. + + GoodTilDate + + + good_til_date + +
    11. + + + + + TimeInForce.GoodTilCanceled + + + TimeInForce.GOOD_TIL_CANCELED + + + + + + + ExtendedRegularTradingHours + + + extended_regular_trading_hours + + + + + bool + + + + If set to true, allows orders to also trigger or fill outside of regular trading hours. + + + + false + + + False + + + + + +
      +
      public override void Initialize()
      +{
      +    // Set the default order properties
      +    DefaultOrderProperties.TimeInForce = TimeInForce.GoodTilCanceled;
      +}
      +
      +public override void OnData(Slice slice)
      +{
      +    // Use default order order properties
      +    LimitOrder(_symbol, quantity, limitPrice);
      +    
      +    // Override the default order properties
      +    LimitOrder(_symbol, quantity, limitPrice, 
      +               orderProperties: new CharlesSchwabOrderProperties
      +               { 
      +                   TimeInForce = TimeInForce.Day,
      +                   ExtendedRegularTradingHours = false
      +               });
      +    LimitOrder(_symbol, quantity, limitPrice, 
      +               orderProperties: new CharlesSchwabOrderProperties
      +               { 
      +                   TimeInForce = TimeInForce.GoodTilDate(new DateTime(year, month, day))
      +               });
      +}
      +
      def initialize(self) -> None:
      +    # Set the default order properties
      +    self.default_order_properties.time_in_force = TimeInForce.GOOD_TIL_CANCELED
      +
      +def on_data(self, slice: Slice) -> None:
      +    # Use default order order properties
      +    self.limit_order(self._symbol, quantity, limit_price)
      +    
      +    # Override the default order properties
      +    order_properties = CharlesSchwabOrderProperties()
      +    order_properties.time_in_force = TimeInForce.DAY
      +    order_properties.extended_regular_trading_hours = True
      +    self.limit_order(self._symbol, quantity, limit_price, order_properties=order_properties)
      +
      +    order_properties.time_in_force = TimeInForce.good_til_date(datetime(year, month, day))
      +    self.limit_order(self._symbol, quantity, limit_price, order_properties=order_properties)
      +
      +

      + Updates +

      +

      + The + + CharlesSchwabBrokerageModel + + supports + + order updates + + . +

      +

      + Handling Splits +

      +

      + If you're using raw + + data normalization + + and you have active orders with a limit, stop, or trigger price in the market for a US Equity when a + + stock split + + occurs, the following properties of your orders automatically adjust to reflect the stock split: +

      +
        +
      • + Quantity +
      • +
      • + Limit price +
      • +
      • + Stop price +
      • +
      • + Trigger price +
      • +
      + + + +

      Fills

      + + +

      + The + + CharlesSchwabBrokerageModel + + uses the + + EquityFillModel + + for Equity trades and the + + ImmediateFillModel + + for Equity and Index Options trades. +

      + + + +

      Slippage

      + + +

      + The + + CharlesSchwabBrokerageModel + + uses the + + NullSlippageModel + + . +

      + + + +

      Fees

      + + +

      + The + + CharlesSchwabBrokerageModel + + uses the + + CharlesSchwabFeeModel + + . +

      + + + +

      Buying Power

      + + +

      + The + + CharlesSchwabBrokerageModel + + uses the + + SecurityMarginModel + + . If you have a margin account, the + + CharlesSchwabBrokerageModel + + allows up to 2x leverage. +

      + + + +

      Settlement

      + + +

      + The + + CharlesSchwabBrokerageModel + + uses the + + ImmediateSettlementModel + + for margin accounts and the + + DelayedSettlementModel + + with the + + default settlement rules + + for cash accounts. +

      +
      +
      // For cash accounts:
      +security.SetSettlementModel(new DelayedSettlementModel(Equity.DefaultSettlementDays, Equity.DefaultSettlementTime));
      +
      +// For margin accounts:
      +security.SetSettlementModel(new ImmediateSettlementModel());
      +
      # For cash accounts:
      +security.set_settlement_model(DelayedSettlementModel(Equity.DEFAULT_SETTLEMENT_DAYS, Equity.DEFAULT_SETTLEMENT_TIME))
      +
      +# For margin accounts:
      +security.set_settlement_model(ImmediateSettlementModel())
      +
      + + + +

      Margin Interest Rate

      + + +

      + The + + CharlesSchwabBrokerageModel + + uses the + + NullMarginInterestRateModel + + . +

      + + + +

      Default Markets

      + + +

      + The default market of the + + CharlesSchwabBrokerageModel + + is + + Market.USA + + . +

      + + + +

      Account Currency

      + + +

      + The + + CharlesSchwabBrokerageModel + + doesn't set a default currency. + To change the algorithm's currency from USD to a different currency, see + + Set Account Currency + + . +

      + + + +

       

      + +
      +
      +

      Supported Models

      +

      Webull

      +
      +
      +

      Introduction

      + + +

      + This page explains the + + WebullBrokerageModel + + , including the asset classes it supports, its default + + security-level models + + , and its default markets. +

      +
      +
      SetBrokerageModel(BrokerageName.Webull, AccountType.Cash);
      +SetBrokerageModel(BrokerageName.Webull, AccountType.Margin);
      +
      self.set_brokerage_model(BrokerageName.WEBULL, AccountType.CASH)
      +self.set_brokerage_model(BrokerageName.WEBULL, AccountType.MARGIN)
      +
      +

      + For more information about this model, see the + + class reference + + and + + implementation + + . +

      +

      + For more information about this model, see the + + class reference + + and + + implementation + + . +

      + + + +

      Asset Classes

      + + +

      + The + + WebullBrokerageModel + + supports the following asset classes: +

      + + + + +

      Orders

      + + +

      + The + + WebullBrokerageModel + + supports several order types, order properties, and order updates. +

      +

      + Order Types +

      +

      + The following table describes the available order types for each asset class that the + + WebullBrokerageModel + + supports: +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      + Order Type + + Equity + + Equity Options + + Index Options +
      + + Market + + + green check + + green check + + green check +
      + + Limit + + + green check + + green check + + green check +
      + + Stop market + + + green check + + green check + + green check +
      + + Stop limit + + + green check + + green check + + green check +
      + + Trailing stop + + + green check + + +
      + +

      + Order Properties +

      +

      + The + + WebullBrokerageModel + + supports custom order properties. The following table describes the members of the + + WebullOrderProperties + + object that you can set to customize order execution. +

      + + + + + + + + + + + + + + - @@ -391169,9 +483147,20 @@

      + + + + + - - -
      + Property + + Data Type + + Description + + Default Value +
      + + TimeInForce + + + time_in_force + + + + TimeInForce + + + A + + TimeInForce + + instruction to apply to the order. The following instructions are supported: +
        +
      • + + Day + + + DAY + +
      • +
      • + + GoodTilCanceled + + + GOOD_TIL_CANCELED + +
      + Market orders support only + + Day + + + DAY + + , which the brokerage sets automatically. Option and Index Option sell orders also support only + + Day + + + DAY + + .
      @@ -386349,10 +478313,10 @@

      - ExtendedRegularTradingHours + OutsideRegularTradingHours - extended_regular_trading_hours + outside_regular_trading_hours @@ -386361,7 +478325,7 @@

      - If set to true, allows orders to also trigger or fill outside of regular trading hours. + If set to true, allows orders to also trigger or fill outside of regular trading hours. This property applies to Equity orders only and isn't supported for market orders. @@ -386385,18 +478349,13 @@

      { // Use default order order properties LimitOrder(_symbol, quantity, limitPrice); - + // Override the default order properties - LimitOrder(_symbol, quantity, limitPrice, - orderProperties: new CharlesSchwabOrderProperties - { + LimitOrder(_symbol, quantity, limitPrice, + orderProperties: new WebullOrderProperties + { TimeInForce = TimeInForce.Day, - ExtendedRegularTradingHours = false - }); - LimitOrder(_symbol, quantity, limitPrice, - orderProperties: new CharlesSchwabOrderProperties - { - TimeInForce = TimeInForce.GoodTilDate(new DateTime(year, month, day)) + OutsideRegularTradingHours = true }); }
      def initialize(self) -> None:
      @@ -386406,14 +478365,11 @@ 

      def on_data(self, slice: Slice) -> None: # Use default order order properties self.limit_order(self._symbol, quantity, limit_price) - + # Override the default order properties - order_properties = CharlesSchwabOrderProperties() + order_properties = WebullOrderProperties() order_properties.time_in_force = TimeInForce.DAY - order_properties.extended_regular_trading_hours = True - self.limit_order(self._symbol, quantity, limit_price, order_properties=order_properties) - - order_properties.time_in_force = TimeInForce.good_til_date(datetime(year, month, day)) + order_properties.outside_regular_trading_hours = True self.limit_order(self._symbol, quantity, limit_price, order_properties=order_properties)

      @@ -386422,7 +478378,7 @@

      The - CharlesSchwabBrokerageModel + WebullBrokerageModel supports @@ -386467,7 +478423,7 @@

      Fills

      The - CharlesSchwabBrokerageModel + WebullBrokerageModel uses the @@ -386477,7 +478433,7 @@

      Fills

      ImmediateFillModel - for Equity and Index Options trades. + for Equity Options and Index Options trades.

      @@ -386488,7 +478444,7 @@

      Slippage

      The - CharlesSchwabBrokerageModel + WebullBrokerageModel uses the @@ -386505,11 +478461,11 @@

      Fees

      The - CharlesSchwabBrokerageModel + WebullBrokerageModel uses the - - CharlesSchwabFeeModel + + WebullFeeModel .

      @@ -386522,7 +478478,7 @@

      Buying Power

      The - CharlesSchwabBrokerageModel + WebullBrokerageModel uses the @@ -386530,7 +478486,7 @@

      Buying Power

      . If you have a margin account, the - CharlesSchwabBrokerageModel + WebullBrokerageModel allows up to 2x leverage.

      @@ -386543,7 +478499,7 @@

      Settlement

      The - CharlesSchwabBrokerageModel + WebullBrokerageModel uses the @@ -386580,7 +478536,7 @@

      Margin Interest Rate

      The - CharlesSchwabBrokerageModel + WebullBrokerageModel uses the @@ -386597,7 +478553,7 @@

      Default Markets

      The default market of the - CharlesSchwabBrokerageModel + WebullBrokerageModel is @@ -386614,9 +478570,9 @@

      Account Currency

      The - CharlesSchwabBrokerageModel + WebullBrokerageModel - doesn't set a default currency. + doesn't set a default currency. To change the algorithm's currency from USD to a different currency, see Set Account Currency @@ -386629,7 +478585,7 @@

      Account Currency

       

      -
      +

      Supported Models

      Binance

      @@ -387427,7 +479383,7 @@

      Account Currency

      set the account currency
      to USDT or another - + Cryptocurrency that Binance supports . @@ -387438,7 +479394,7 @@

      Account Currency

       

      -
      +

      Supported Models

      Bybit

      @@ -388052,7 +480008,7 @@

      Account Currency

       

      -
      +

      Supported Models

      Tradier

      @@ -388678,7 +480634,7 @@

      Account Currency

       

      -
      +

      Supported Models

      Kraken

      @@ -389491,7 +481447,7 @@

      Account Currency

       

      -
      +

      Supported Models

      Coinbase

      @@ -389960,7 +481916,7 @@

      Account Currency

       

      -
      +

      Supported Models

      Bitfinex

      @@ -390464,7 +482420,7 @@

      Account Currency

       

      -
      +

      Supported Models

      dYdX

      @@ -391072,7 +483028,7 @@

      Account Currency

       

      -
      +

      Supported Models

      Terminal Link

      @@ -391081,12 +483037,42 @@

      Introduction

      - This page explains the Terminal Link brokerage, including the asset classes it supports, its default + This page explains the + + TerminalLinkBrokerageModel + + , including the asset classes it supports, its default security-level models , and its default markets.

      +
      +
      SetBrokerageModel(BrokerageName.TerminalLink, AccountType.Margin);
      +
      self.set_brokerage_model(BrokerageName.TERMINAL_LINK, AccountType.MARGIN)
      +
      +

      + For more information about this model, see the + + class reference + + and + + implementation + + . +

      +

      + For more information about this model, see the + + class reference + + and + + implementation + + . +

      @@ -391112,11 +483098,6 @@

      Asset Classes

      Futures -
    12. - - Index Options - -
    13. @@ -391148,9 +483129,6 @@

      Futures - Index Options -
      green check
      + + Market on open + + green check + +
      @@ -391188,9 +483177,6 @@

      green check - green check -
      @@ -391207,9 +483193,6 @@

      green check - green check -
      @@ -391226,9 +483209,6 @@

      green check - green check -
      @@ -391533,6 +483513,77 @@

      + + + + LocateBroker + + + locate_broker + + + + + string + + + str + + + + The EMSX locate broker code that identifies the counterparty the shares are borrowed from for a short equity sale (for example, + + "BMTB" + + ). + Maps to the + + LocBrkr + + field on the EMSX trading ticket. + Setting this property (or + + LocateId + + + locate_id + + ) on a short equity sale causes the brokerage to emit + + EMSX_LOCATE_REQ = "Y" + + alongside. + + + + + + + + LocateId + + + locate_id + + + + + string + + + str + + + + The EMSX locate confirmation/ticket Id that the lending broker returns. Maps to the + + LocId + + field on the EMSX trading ticket. + + + + @@ -391924,7 +483975,7 @@

      Account Currency

       

      -
      +

      Supported Models

      SSC Eze

      @@ -392896,7 +484947,7 @@

      Default Markets

       

      -
      +

      Supported Models

      Trading Technologies

      @@ -393303,7 +485354,7 @@

      Account Currency

       

      -
      +

      Supported Models

      Wolverine

      @@ -393758,7 +485809,7 @@

      Fees

      WolverineBrokerageModel
      uses the - + WolverineFeeModel . @@ -393879,7 +485930,7 @@

      Account Currency

       

      -
      +

      Supported Models

      Oanda

      @@ -395966,7 +488017,7 @@

      return new Position(symbol, leg.Quantity, 1); });
          def get_symbol(leg):
      -        return Symbol.CreateOption(
      +        return Symbol.create_option(
                   self._symbol.underlying, self._symbol.id.market, self._symbol.id.option_style, 
                   leg.right, leg.strike, leg.expiration
               )
      @@ -396170,7 +488221,7 @@ 

      } }

      class BuyingPowerModelAlgorithm(QCAlgorithm):
      -    def Initialize(self) -> None:
      +    def initialize(self) -> None:
               self.set_start_date(2024, 9, 1)
               self.set_end_date(2024, 12, 31)
               self.settings.seed_initial_prices = True
      @@ -396217,7 +488268,7 @@ 

      def _custom_security_initializer(self, security: Security): # Do not allow buying power for options; only a hedging strategy with 0 margin is allowed. - security.SetBuyingPowerModel(NullBuyingPowerModel())

      + security.set_buying_power_model(NullBuyingPowerModel())

      Example 2: Default Model Wrapper @@ -397007,8 +489058,8 @@

      self._traded = False def _plot_cash(self): - self.Plot("Settled Cash", "USD", self.Portfolio.CashBook["USD"].Amount) - self.Plot("Unsettled Cash", "USD", self.Portfolio.UnsettledCashBook["USD"].Amount) + self.plot("Settled Cash", "USD", self.portfolio.cash_book["USD"].amount) + self.plot("Unsettled Cash", "USD", self.portfolio.unsettled_cash_book["USD"].amount) def on_data(self, data: Slice) -> None: if self._traded: @@ -400582,7 +492633,7 @@

      def _initialize_security(self, security: Security) -> None: # Overwrite the assignment model. - if security.Type == SecurityType.OPTION: # Option type + if security.type == SecurityType.OPTION: # Option type security.set_option_assignment_model(MyOptionAssignmentModel()) def on_data(self, data) -> None: @@ -400604,8 +492655,8 @@

      def get_assignment(self, parameters: OptionAssignmentParameters) -> OptionAssignmentResult: option = parameters.option # Check if the contract is ITM. - if (option.Right == OptionRight.CALL and option.Underlying.Price > option.StrikePrice or - option.Right == OptionRight.PUT and option.Underlying.Price < option.StrikePrice): + if (option.right == OptionRight.CALL and option.underlying.price > option.strike_price or + option.right == OptionRight.PUT and option.underlying.price < option.strike_price): return OptionAssignmentResult(option.holdings.absolute_quantity, "MyTag") return OptionAssignmentResult.NULL @@ -403535,7 +495586,7 @@

      class CustomShortableProviderAlgorithm(QCAlgorithm):
       
      -    def Initialize(self):
      +    def initialize(self):
               self.set_start_date(2024, 9, 1)
               self.set_end_date(2024, 12, 31)
               self.set_cash(100000)
      @@ -403546,7 +495597,7 @@ 

      def on_data(self, data: Slice) -> None: if self.portfolio.invested: return - shortable_quantity = self._security.shortable_provider.shortable_quantity(self._security.symbol, self.Time) + shortable_quantity = self._security.shortable_provider.shortable_quantity(self._security.symbol, self.time) if not shortable_quantity: return fee_rate = self._security.shortable_provider.fee_rate(self._security.symbol, self.time) @@ -404459,6 +496510,58 @@

      Date Rules

      Trigger an event on the last tradable date of each week for a specific symbol minus an offset. + + + + self.date_rules.quarter_start(days_offset: int = 0) + + + DateRules.QuarterStart(int daysOffset = 0) + + + + Trigger an event on the first day of each quarter plus an offset. + + + + + + self.date_rules.quarter_start(symbol: Symbol, days_offset: int = 0) + + + DateRules.QuarterStart(Symbol symbol, int daysOffset = 0) + + + + Trigger an event on the first tradable date of each quarter for a specific symbol plus an offset. + + + + + + self.date_rules.quarter_end(days_offset: int = 0) + + + DateRules.QuarterEnd(int daysOffset = 0) + + + + Trigger an event on the last day of each quarter minus an offset. + + + + + + self.date_rules.quarter_end(symbol: Symbol, days_offset: int = 0) + + + DateRules.QuarterEnd(Symbol symbol, int daysOffset = 0) + + + + Trigger an event on the last tradable date of each quarter for a specific symbol minus an offset. + + @@ -405194,12 +497297,26 @@

      Examples

      TimeRules.AfterMarketOpen("SPY", 5), RebalancingCode); - // Schedule an event to fire at the end of the week, the symbol is optional. + // Schedule an event to fire at the end of the week, the symbol is optional. // If specified, it will fire the last trading day for that symbol of the week. // Otherwise, it will fire on the first day of the week. Schedule.On(DateRules.WeekEnd("SPY"), TimeRules.BeforeMarketClose("SPY", 5), RebalancingCode); + + // Schedule an event to fire at the beginning of the quarter, the symbol is optional. + // If specified, it will fire the first trading day for that symbol of the quarter. + // Otherwise, it will fire on the first day of the quarter. + Schedule.On(DateRules.QuarterStart("SPY"), + TimeRules.AfterMarketOpen("SPY"), + RebalancingCode); + + // Schedule an event to fire at the end of the quarter, the symbol is optional. + // If specified, it will fire the last trading day for that symbol of the quarter. + // Otherwise, it will fire on the last day of the quarter. + Schedule.On(DateRules.QuarterEnd("SPY"), + TimeRules.BeforeMarketClose("SPY"), + RebalancingCode); } // The following methods are not defined in Initialize: @@ -405301,6 +497418,20 @@

      Examples

      self.time_rules.before_market_close("SPY", 5), self._rebalancing_code) + # Schedule an event to fire at the beginning of the quarter, the symbol is optional. + # If specified, it will fire the first trading day for that symbol of the quarter. + # Otherwise, it will fire on the first day of the quarter. + self.schedule.on(self.date_rules.quarter_start("SPY"), + self.time_rules.after_market_open("SPY"), + self._rebalancing_code) + + # Schedule an event to fire at the end of the quarter, the symbol is optional. + # If specified, it will fire the last trading day for that symbol of the quarter. + # Otherwise, it will fire on the last day of the quarter. + self.schedule.on(self.date_rules.quarter_end("SPY"), + self.time_rules.before_market_close("SPY"), + self._rebalancing_code) + # The following methods from examples above should be defined in the algorithm body. def _liquidate_unrealized_losses(self) -> None: ''' if we have over 1000 dollars in unrealized losses, liquidate''' @@ -406764,6 +498895,17 @@

      +

      For more information about this indicator, see its @@ -451429,6 +543604,13 @@

      Indicator History

      var indicatorHistory = IndicatorHistory(_swiss, _symbol, 100, Resolution.Minute); var timeSpanIndicatorHistory = IndicatorHistory(_swiss, _symbol, TimeSpan.FromDays(10), Resolution.Minute); var timePeriodIndicatorHistory = IndicatorHistory(_swiss, _symbol, new DateTime(2024, 7, 1), new DateTime(2024, 7, 5), Resolution.Minute); + + // Access all attributes of indicatorHistory + var gauss = indicatorHistory.Select(x => ((dynamic)x).Gauss).ToList(); + var butter = indicatorHistory.Select(x => ((dynamic)x).Butter).ToList(); + var highPass = indicatorHistory.Select(x => ((dynamic)x).HighPass).ToList(); + var twoPoleHighPass = indicatorHistory.Select(x => ((dynamic)x).TwoPoleHighPass).ToList(); + var bandPass = indicatorHistory.Select(x => ((dynamic)x).BandPass).ToList(); } }
      class SwissArmyKnifeAlgorithm(QCAlgorithm):
      @@ -451439,7 +543621,14 @@ 

      Indicator History

      indicator_history = self.indicator_history(self._swiss, self._symbol, 100, Resolution.MINUTE) timedelta_indicator_history = self.indicator_history(self._swiss, self._symbol, timedelta(days=10), Resolution.MINUTE) time_period_indicator_history = self.indicator_history(self._swiss, self._symbol, datetime(2024, 7, 1), datetime(2024, 7, 5), Resolution.MINUTE) -
      + + # Access all attributes of indicator_history + indicator_history_df = indicator_history.data_frame + gauss = indicator_history_df["gauss"] + butter = indicator_history_df["butter"] + high_pass = indicator_history_df["highpass"] + two_pole_high_pass = indicator_history_df["twopolehighpass"] + band_pass = indicator_history_df["bandpass"]
      @@ -456402,40 +548591,315 @@

      Indicator History

      argument, it defaults to match the resolution of the security subscription.

      -
      public class VolumeWeightedMovingAverageAlgorithm : QCAlgorithm
      +   
      public class VolumeWeightedMovingAverageAlgorithm : QCAlgorithm
      +{
      +    private Symbol _symbol;
      +    private VolumeWeightedMovingAverage _vwma;
      +
      +    public override void Initialize()
      +    {
      +        _symbol = AddEquity("SPY", Resolution.Daily).Symbol;
      +        _vwma = VWMA(_symbol, 20);
      +
      +        var indicatorHistory = IndicatorHistory(_vwma, _symbol, 100, Resolution.Minute);
      +        var timeSpanIndicatorHistory = IndicatorHistory(_vwma, _symbol, TimeSpan.FromDays(10), Resolution.Minute);
      +        var timePeriodIndicatorHistory = IndicatorHistory(_vwma, _symbol, new DateTime(2024, 7, 1), new DateTime(2024, 7, 5), Resolution.Minute);
      +    }
      +}
      +
      class VolumeWeightedMovingAverageAlgorithm(QCAlgorithm):
      +    def initialize(self) -> None:
      +        self._symbol = self.add_equity("SPY", Resolution.DAILY).symbol
      +        self._vwma = self.vwma(self._symbol, 20)
      +
      +        indicator_history = self.indicator_history(self._vwma, self._symbol, 100, Resolution.MINUTE)
      +        timedelta_indicator_history = self.indicator_history(self._vwma, self._symbol, timedelta(days=10), Resolution.MINUTE)
      +        time_period_indicator_history = self.indicator_history(self._vwma, self._symbol, datetime(2024, 7, 1), datetime(2024, 7, 5), Resolution.MINUTE)
      +    
      +
      + + + +

       

      + +
      +
      +

      Supported Indicators

      +

      Vortex

      +
      +
      +

      Introduction

      + + + +

      + This indicator represents the Vortex Indicator, which identifies the start and continuation of market trends. It includes components that capture positive (upward) and negative (downward) trend movements. This indicator compares the ranges within the current period to previous periods to calculate upward and downward movement trends. +

      +

      + To view the implementation of this indicator, see the + + LEAN GitHub repository + + . +

      + + + +

      Using VTX Indicator

      + + +

      + To create an automatic indicator for + + Vortex + + , call the + + VTX + + + vtx + + helper method from the + + QCAlgorithm + + class. The + + VTX + + + vtx + + method creates a + + Vortex + + object, hooks it up for automatic updates, and returns it so you can used it in your algorithm. In most cases, you should call the helper method in the + + Initialize + + + initialize + + method. +

      +

      +

      +
      +
      public class VortexAlgorithm : QCAlgorithm
      +{
      +    private Symbol _symbol;
      +    private Vortex _vtx;
      +
      +    public override void Initialize()
      +    {
      +        _symbol = AddEquity("SPY", Resolution.Daily).Symbol;
      +        _vtx = VTX(_symbol, 14);
      +    }
      +
      +    public override void OnData(Slice data)
      +    {
      +
      +        if (_vtx.IsReady)
      +        {
      +            // The current value of _vtx is represented by itself (_vtx)
      +            // or _vtx.Current.Value
      +            Plot("Vortex", "vtx", _vtx);
      +            // Plot all properties of abands
      +            Plot("Vortex", "plusvortex", _vtx.PlusVortex);
      +            Plot("Vortex", "minusvortex", _vtx.MinusVortex);
      +        }
      +    }
      +}
      +
      class VortexAlgorithm(QCAlgorithm):
      +    def initialize(self) -> None:
      +        self._symbol = self.add_equity("SPY", Resolution.DAILY).symbol
      +        self._vtx = self.vtx(self._symbol, 14)
      +
      +    def on_data(self, slice: Slice) -> None:
      +
      +        if self._vtx.is_ready:
      +            # The current value of self._vtx is represented by self._vtx.current.value
      +            self.plot("Vortex", "vtx", self._vtx.current.value)
      +            # Plot all attributes of self._vtx
      +            self.plot("Vortex", "plus_vortex", self._vtx.plus_vortex.current.value)
      +            self.plot("Vortex", "minus_vortex", self._vtx.minus_vortex.current.value)
      +
      +

      + For more information about this method, see the + + QCAlgorithm class + + + QCAlgorithm class + + . +

      +

      + You can manually create a + + Vortex + + indicator, so it doesn't automatically update. Manual indicators let you update their values with any data you choose. +

      +

      + Updating your indicator manually enables you to control when the indicator is updated and what data you use to update it. To manually update the indicator, call the + + Update + + + update + + method. The indicator will only be ready after you prime it with enough data. +

      +
      +
      public class VortexAlgorithm : QCAlgorithm
      +{
      +    private Symbol _symbol;
      +    private Vortex _vortex;
      +
      +    public override void Initialize()
      +    {
      +        _symbol = AddEquity("SPY", Resolution.Daily).Symbol;
      +        _vortex = new Vortex(14);
      +    }
      +
      +    public override void OnData(Slice data)
      +    {
      +        if (data.Bars.TryGetValue(_symbol, out var bar))
      +            _vortex.Update(bar);
      +
      +        if (_vortex.IsReady)
      +        {
      +            // The current value of _vortex is represented by itself (_vortex)
      +            // or _vortex.Current.Value
      +            Plot("Vortex", "vortex", _vortex);
      +            // Plot all properties of abands
      +            Plot("Vortex", "plusvortex", _vortex.PlusVortex);
      +            Plot("Vortex", "minusvortex", _vortex.MinusVortex);
      +        }
      +    }
      +}
      +
      class VortexAlgorithm(QCAlgorithm):
      +    def initialize(self) -> None:
      +        self._symbol = self.add_equity("SPY", Resolution.DAILY).symbol
      +        self._vortex = Vortex(14)
      +
      +    def on_data(self, slice: Slice) -> None:
      +        bar = slice.bars.get(self._symbol)
      +        if bar:
      +            self._vortex.update(bar)
      +
      +        if self._vortex.is_ready:
      +            # The current value of self._vortex is represented by self._vortex.current.value
      +            self.plot("Vortex", "vortex", self._vortex.current.value)
      +            # Plot all attributes of self._vortex
      +            self.plot("Vortex", "plus_vortex", self._vortex.plus_vortex.current.value)
      +            self.plot("Vortex", "minus_vortex", self._vortex.minus_vortex.current.value)
      +
      +

      + For more information about this indicator, see its + + reference + + + reference + + . +

      + + + +

      Visualization

      + + + +

      + The following plot shows values for some of the + + Vortex + + indicator properties: +

      + Vortex line plot. + + + +

      Indicator History

      + + +

      + To get the historical data of the + + Vortex + + indicator, call the + + IndicatorHistory + + + self.indicator_history + + method. This method resets your indicator, makes a + + history request + + , and updates the indicator with the historical data. Just like with regular history requests, the + + IndicatorHistory + + + indicator_history + + method supports time periods based on a trailing number of bars, a trailing period of time, or a defined period of time. If you don't provide a + + resolution + + argument, it defaults to match the resolution of the security subscription. +

      +
      +
      public class VortexAlgorithm : QCAlgorithm
       {
           private Symbol _symbol;
      -    private VolumeWeightedMovingAverage _vwma;
      +    private Vortex _vtx;
       
           public override void Initialize()
           {
               _symbol = AddEquity("SPY", Resolution.Daily).Symbol;
      -        _vwma = VWMA(_symbol, 20);
      +        _vtx = VTX(_symbol, 14);
       
      -        var indicatorHistory = IndicatorHistory(_vwma, _symbol, 100, Resolution.Minute);
      -        var timeSpanIndicatorHistory = IndicatorHistory(_vwma, _symbol, TimeSpan.FromDays(10), Resolution.Minute);
      -        var timePeriodIndicatorHistory = IndicatorHistory(_vwma, _symbol, new DateTime(2024, 7, 1), new DateTime(2024, 7, 5), Resolution.Minute);
      +        var indicatorHistory = IndicatorHistory(_vtx, _symbol, 100, Resolution.Minute);
      +        var timeSpanIndicatorHistory = IndicatorHistory(_vtx, _symbol, TimeSpan.FromDays(10), Resolution.Minute);
      +        var timePeriodIndicatorHistory = IndicatorHistory(_vtx, _symbol, new DateTime(2024, 7, 1), new DateTime(2024, 7, 5), Resolution.Minute);
      +
      +        // Access all attributes of indicatorHistory
      +        var plusVortex = indicatorHistory.Select(x => ((dynamic)x).PlusVortex).ToList();
      +        var minusVortex = indicatorHistory.Select(x => ((dynamic)x).MinusVortex).ToList();
           }
       }
      -
      class VolumeWeightedMovingAverageAlgorithm(QCAlgorithm):
      +   
      class VortexAlgorithm(QCAlgorithm):
           def initialize(self) -> None:
               self._symbol = self.add_equity("SPY", Resolution.DAILY).symbol
      -        self._vwma = self.vwma(self._symbol, 20)
      +        self._vtx = self.vtx(self._symbol, 14)
       
      -        indicator_history = self.indicator_history(self._vwma, self._symbol, 100, Resolution.MINUTE)
      -        timedelta_indicator_history = self.indicator_history(self._vwma, self._symbol, timedelta(days=10), Resolution.MINUTE)
      -        time_period_indicator_history = self.indicator_history(self._vwma, self._symbol, datetime(2024, 7, 1), datetime(2024, 7, 5), Resolution.MINUTE)
      -    
      + indicator_history = self.indicator_history(self._vtx, self._symbol, 100, Resolution.MINUTE) + timedelta_indicator_history = self.indicator_history(self._vtx, self._symbol, timedelta(days=10), Resolution.MINUTE) + time_period_indicator_history = self.indicator_history(self._vtx, self._symbol, datetime(2024, 7, 1), datetime(2024, 7, 5), Resolution.MINUTE) + + # Access all attributes of indicator_history + indicator_history_df = indicator_history.data_frame + plus_vortex = indicator_history_df["plusvortex"] + minus_vortex = indicator_history_df["minusvortex"]

       

      - +
      -
      +

      Supported Indicators

      -

      Vortex

      +

      Wave Trend Oscillator

      Introduction

      @@ -456443,11 +548907,14 @@

      Introduction

      - This indicator represents the Vortex Indicator, which identifies the start and continuation of market trends. It includes components that capture positive (upward) and negative (downward) trend movements. This indicator compares the ranges within the current period to previous periods to calculate upward and downward movement trends. + The WaveTrend Oscillator (WTO) is a momentum indicator that highlights overbought and oversold conditions by measuring how far the typical price has deviated from a smoothed moving average, normalized by an exponentially smoothed mean absolute deviation. The oscillator's main line (WT1) is an EMA of this normalized channel index, and the signal line (WT2) is an SMA of WT1; crossovers between the two lines are commonly used as entry and exit signals. Formula: HLC3 = (High + Low + Close) / 3 ESA = EMA(HLC3, channelPeriod) D = EMA(|HLC3 - ESA|, channelPeriod) CI = (HLC3 - ESA) / (0.015 * D) WT1 = EMA(CI, averagePeriod) (the indicator's Current.Value) WT2 = SMA(WT1, signalPeriod) (exposed via + + + )

      To view the implementation of this indicator, see the - + LEAN GitHub repository . @@ -456455,20 +548922,20 @@

      Introduction

      -

      Using VTX Indicator

      +

      Using WTO Indicator

      To create an automatic indicator for - Vortex + WaveTrendOscillator , call the - VTX + WTO - vtx + wto helper method from the @@ -456476,14 +548943,14 @@

      Using VTX Indicator

      class. The - VTX + WTO - vtx + wto method creates a - Vortex + WaveTrendOscillator object, hooks it up for automatic updates, and returns it so you can used it in your algorithm. In most cases, you should call the helper method in the @@ -456497,51 +548964,55 @@

      Using VTX Indicator

      -
      public class VortexAlgorithm : QCAlgorithm
      +   
      public class WaveTrendOscillatorAlgorithm : QCAlgorithm
       {
           private Symbol _symbol;
      -    private Vortex _vtx;
      +    private WaveTrendOscillator _wto;
       
           public override void Initialize()
           {
               _symbol = AddEquity("SPY", Resolution.Daily).Symbol;
      -        _vtx = VTX(_symbol, 14);
      +        _wto = WTO(_symbol, 10, 21, 4);
           }
       
           public override void OnData(Slice data)
           {
       
      -        if (_vtx.IsReady)
      +        if (_wto.IsReady)
               {
      -            // The current value of _vtx is represented by itself (_vtx)
      -            // or _vtx.Current.Value
      -            Plot("Vortex", "vtx", _vtx);
      +            // The current value of _wto is represented by itself (_wto)
      +            // or _wto.Current.Value
      +            Plot("WaveTrendOscillator", "wto", _wto);
                   // Plot all properties of abands
      -            Plot("Vortex", "plusvortex", _vtx.PlusVortex);
      -            Plot("Vortex", "minusvortex", _vtx.MinusVortex);
      +            Plot("WaveTrendOscillator", "channelaverage", _wto.ChannelAverage);
      +            Plot("WaveTrendOscillator", "channeldeviation", _wto.ChannelDeviation);
      +            Plot("WaveTrendOscillator", "channelindexaverage", _wto.ChannelIndexAverage);
      +            Plot("WaveTrendOscillator", "signal", _wto.Signal);
               }
           }
       }
      -
      class VortexAlgorithm(QCAlgorithm):
      +   
      class WaveTrendOscillatorAlgorithm(QCAlgorithm):
           def initialize(self) -> None:
               self._symbol = self.add_equity("SPY", Resolution.DAILY).symbol
      -        self._vtx = self.vtx(self._symbol, 14)
      +        self._wto = self.wto(self._symbol, 10, 21, 4)
       
           def on_data(self, slice: Slice) -> None:
       
      -        if self._vtx.is_ready:
      -            # The current value of self._vtx is represented by self._vtx.current.value
      -            self.plot("Vortex", "vtx", self._vtx.current.value)
      -            # Plot all attributes of self._vtx
      -            self.plot("Vortex", "plus_vortex", self._vtx.plus_vortex.current.value)
      -            self.plot("Vortex", "minus_vortex", self._vtx.minus_vortex.current.value)
      + if self._wto.is_ready: + # The current value of self._wto is represented by self._wto.current.value + self.plot("WaveTrendOscillator", "wto", self._wto.current.value) + # Plot all attributes of self._wto + self.plot("WaveTrendOscillator", "channel_average", self._wto.channel_average.current.value) + self.plot("WaveTrendOscillator", "channel_deviation", self._wto.channel_deviation.current.value) + self.plot("WaveTrendOscillator", "channel_index_average", self._wto.channel_index_average.current.value) + self.plot("WaveTrendOscillator", "signal", self._wto.signal.current.value)

      For more information about this method, see the QCAlgorithm class - + QCAlgorithm class . @@ -456549,7 +549020,7 @@

      Using VTX Indicator

      You can manually create a - Vortex + WaveTrendOscillator indicator, so it doesn't automatically update. Manual indicators let you update their values with any data you choose.

      @@ -456564,56 +549035,60 @@

      Using VTX Indicator

      method. The indicator will only be ready after you prime it with enough data.

      -
      public class VortexAlgorithm : QCAlgorithm
      +   
      public class WaveTrendOscillatorAlgorithm : QCAlgorithm
       {
           private Symbol _symbol;
      -    private Vortex _vortex;
      +    private WaveTrendOscillator _wavetrendoscillator;
       
           public override void Initialize()
           {
               _symbol = AddEquity("SPY", Resolution.Daily).Symbol;
      -        _vortex = new Vortex(14);
      +        _wavetrendoscillator = new WaveTrendOscillator(10, 21, 4);
           }
       
           public override void OnData(Slice data)
           {
               if (data.Bars.TryGetValue(_symbol, out var bar))
      -            _vortex.Update(bar);
      +            _wavetrendoscillator.Update(bar);
       
      -        if (_vortex.IsReady)
      +        if (_wavetrendoscillator.IsReady)
               {
      -            // The current value of _vortex is represented by itself (_vortex)
      -            // or _vortex.Current.Value
      -            Plot("Vortex", "vortex", _vortex);
      +            // The current value of _wavetrendoscillator is represented by itself (_wavetrendoscillator)
      +            // or _wavetrendoscillator.Current.Value
      +            Plot("WaveTrendOscillator", "wavetrendoscillator", _wavetrendoscillator);
                   // Plot all properties of abands
      -            Plot("Vortex", "plusvortex", _vortex.PlusVortex);
      -            Plot("Vortex", "minusvortex", _vortex.MinusVortex);
      +            Plot("WaveTrendOscillator", "channelaverage", _wavetrendoscillator.ChannelAverage);
      +            Plot("WaveTrendOscillator", "channeldeviation", _wavetrendoscillator.ChannelDeviation);
      +            Plot("WaveTrendOscillator", "channelindexaverage", _wavetrendoscillator.ChannelIndexAverage);
      +            Plot("WaveTrendOscillator", "signal", _wavetrendoscillator.Signal);
               }
           }
       }
      -
      class VortexAlgorithm(QCAlgorithm):
      +   
      class WaveTrendOscillatorAlgorithm(QCAlgorithm):
           def initialize(self) -> None:
               self._symbol = self.add_equity("SPY", Resolution.DAILY).symbol
      -        self._vortex = Vortex(14)
      +        self._wavetrendoscillator = WaveTrendOscillator(10, 21, 4)
       
           def on_data(self, slice: Slice) -> None:
               bar = slice.bars.get(self._symbol)
               if bar:
      -            self._vortex.update(bar)
      +            self._wavetrendoscillator.update(bar)
       
      -        if self._vortex.is_ready:
      -            # The current value of self._vortex is represented by self._vortex.current.value
      -            self.plot("Vortex", "vortex", self._vortex.current.value)
      -            # Plot all attributes of self._vortex
      -            self.plot("Vortex", "plus_vortex", self._vortex.plus_vortex.current.value)
      -            self.plot("Vortex", "minus_vortex", self._vortex.minus_vortex.current.value)
      + if self._wavetrendoscillator.is_ready: + # The current value of self._wavetrendoscillator is represented by self._wavetrendoscillator.current.value + self.plot("WaveTrendOscillator", "wavetrendoscillator", self._wavetrendoscillator.current.value) + # Plot all attributes of self._wavetrendoscillator + self.plot("WaveTrendOscillator", "channel_average", self._wavetrendoscillator.channel_average.current.value) + self.plot("WaveTrendOscillator", "channel_deviation", self._wavetrendoscillator.channel_deviation.current.value) + self.plot("WaveTrendOscillator", "channel_index_average", self._wavetrendoscillator.channel_index_average.current.value) + self.plot("WaveTrendOscillator", "signal", self._wavetrendoscillator.signal.current.value)

      For more information about this indicator, see its - + reference - + reference . @@ -456628,11 +549103,11 @@

      Visualization

      The following plot shows values for some of the - Vortex + WaveTrendOscillator indicator properties:

      - Vortex line plot. + WaveTrendOscillator line plot. @@ -456642,7 +549117,7 @@

      Indicator History

      To get the historical data of the - Vortex + WaveTrendOscillator indicator, call the @@ -456669,38 +549144,42 @@

      Indicator History

      argument, it defaults to match the resolution of the security subscription.

      -
      public class VortexAlgorithm : QCAlgorithm
      +   
      public class WaveTrendOscillatorAlgorithm : QCAlgorithm
       {
           private Symbol _symbol;
      -    private Vortex _vtx;
      +    private WaveTrendOscillator _wto;
       
           public override void Initialize()
           {
               _symbol = AddEquity("SPY", Resolution.Daily).Symbol;
      -        _vtx = VTX(_symbol, 14);
      +        _wto = WTO(_symbol, 10, 21, 4);
       
      -        var indicatorHistory = IndicatorHistory(_vtx, _symbol, 100, Resolution.Minute);
      -        var timeSpanIndicatorHistory = IndicatorHistory(_vtx, _symbol, TimeSpan.FromDays(10), Resolution.Minute);
      -        var timePeriodIndicatorHistory = IndicatorHistory(_vtx, _symbol, new DateTime(2024, 7, 1), new DateTime(2024, 7, 5), Resolution.Minute);
      +        var indicatorHistory = IndicatorHistory(_wto, _symbol, 100, Resolution.Minute);
      +        var timeSpanIndicatorHistory = IndicatorHistory(_wto, _symbol, TimeSpan.FromDays(10), Resolution.Minute);
      +        var timePeriodIndicatorHistory = IndicatorHistory(_wto, _symbol, new DateTime(2024, 7, 1), new DateTime(2024, 7, 5), Resolution.Minute);
       
               // Access all attributes of indicatorHistory
      -        var plusVortex = indicatorHistory.Select(x => ((dynamic)x).PlusVortex).ToList();
      -        var minusVortex = indicatorHistory.Select(x => ((dynamic)x).MinusVortex).ToList();
      +        var channelAverage = indicatorHistory.Select(x => ((dynamic)x).ChannelAverage).ToList();
      +        var channelDeviation = indicatorHistory.Select(x => ((dynamic)x).ChannelDeviation).ToList();
      +        var channelIndexAverage = indicatorHistory.Select(x => ((dynamic)x).ChannelIndexAverage).ToList();
      +        var signal = indicatorHistory.Select(x => ((dynamic)x).Signal).ToList();
           }
       }
      -
      class VortexAlgorithm(QCAlgorithm):
      +   
      class WaveTrendOscillatorAlgorithm(QCAlgorithm):
           def initialize(self) -> None:
               self._symbol = self.add_equity("SPY", Resolution.DAILY).symbol
      -        self._vtx = self.vtx(self._symbol, 14)
      +        self._wto = self.wto(self._symbol, 10, 21, 4)
       
      -        indicator_history = self.indicator_history(self._vtx, self._symbol, 100, Resolution.MINUTE)
      -        timedelta_indicator_history = self.indicator_history(self._vtx, self._symbol, timedelta(days=10), Resolution.MINUTE)
      -        time_period_indicator_history = self.indicator_history(self._vtx, self._symbol, datetime(2024, 7, 1), datetime(2024, 7, 5), Resolution.MINUTE)
      +        indicator_history = self.indicator_history(self._wto, self._symbol, 100, Resolution.MINUTE)
      +        timedelta_indicator_history = self.indicator_history(self._wto, self._symbol, timedelta(days=10), Resolution.MINUTE)
      +        time_period_indicator_history = self.indicator_history(self._wto, self._symbol, datetime(2024, 7, 1), datetime(2024, 7, 5), Resolution.MINUTE)
           
               # Access all attributes of indicator_history
               indicator_history_df = indicator_history.data_frame
      -        plus_vortex = indicator_history_df["plusvortex"]
      -        minus_vortex = indicator_history_df["minusvortex"]
      + channel_average = indicator_history_df["channelaverage"] + channel_deviation = indicator_history_df["channeldeviation"] + channel_index_average = indicator_history_df["channelindexaverage"] + signal = indicator_history_df["signal"]
      @@ -456708,7 +549187,7 @@

      Indicator History

       

      -
      +

      Supported Indicators

      Wilder Accumulative Swing Index

      @@ -456993,7 +549472,7 @@

      Indicator History

       

      -
      +

      Supported Indicators

      Wilder Moving Average

      @@ -457248,7 +549727,7 @@

      Indicator History

       

      -
      +

      Supported Indicators

      Wilder Swing Index

      @@ -457591,7 +550070,7 @@

      Indicator History

       

      -
      +

      Supported Indicators

      Williams Percent R

      @@ -457866,7 +550345,7 @@

      Indicator History

       

      -
      +

      Supported Indicators

      Zero Lag Exponential Moving Average

      @@ -458121,7 +550600,7 @@

      Indicator History

       

      -
      +

      Supported Indicators

      Zig Zag

      @@ -458609,7 +551088,7 @@

      Create Indicators

      # Create a manual indicator with the indicator constructor self.manual_bb = BollingerBands(20, 2) # Create an automatic indicator with the helper method - self._symbol = self.add_crypto("BTCUSD").Symbol + self._symbol = self.add_crypto("BTCUSD").symbol self.auto_bb = self.bb(self._symbol, 20, 2, Resolution.DAILY)
      @@ -458624,9 +551103,12 @@

      Set Historical Values Window Size

      RollingWindow that stores their historical values. The default window size is 2. To change the window size, set the - + Window.Size + + window.size + member.

      @@ -458738,9 +551220,12 @@

      Check Readiness

      RollingWindow
      objects that store the historical values may not be full when the indicator is ready to use. To check if the - + Window + + window + is full, use its IsReady @@ -459249,7 +551734,7 @@

      def on_securities_changed(self, changes: SecurityChanges) -> None:
           for security in changes.added_securities:
               # Create an indicator
      -        security.indicator = self.sma(security.Symbol, 10)
      +        security.indicator = self.sma(security.symbol, 10)
       
               # Warm up the indicator
               self.warm_up_indicator(security.symbol, security.indicator)
      @@ -459682,21 +552167,33 @@ 

      Automatic Updates

      QuoteBar
      data, that price is the mid-price of the bid closing price and the ask closing price. To create an indicator with the other fields like the - + Open + + open + , - + High + + high + , - + Low + + low + , or - + Close + + close + , provide a selector @@ -460732,7 +553229,7 @@

      WarmUpIndicator(new[] {aapl, spy}, beta, Resolution.Daily);

      self._spy = self.add_equity("SPY").symbol
       self._aapl = self.add_equity('AAPL').symbol
      -self._beta = self.B(self._aapl, self._spy, 21, Resolution.DAILY)
      +self._beta = self.b(self._aapl, self._spy, 21, Resolution.DAILY)
       self.warm_up_indicator([self._aapl, self._spy], self._beta, Resolution.DAILY)

      @@ -463058,7 +555555,7 @@

      # Update the rolling windows for cointegration analysis. window = self._windows.get(input.symbol) if not window: - raise Exception(f"{input.Symbol} is not part of the Cointegration relation.") + raise Exception(f"{input.symbol} is not part of the Cointegration relation.") window.add(IndicatorDataPoint(input.symbol, input.end_time, input.value)) if not all(x == 0 for x in self._coefficients): @@ -463363,6 +555860,276 @@

      def is_ready(self) -> bool: return len(self.positive_money_flow) == self.positive_money_flow.maxlen

      +

      + Example 3: Custom Annualized Volatility Indicator +

      +

      + The following algorithm implements a custom annualized log-return volatility indicator using a rolling window of close prices. + It screens a + + dynamic Equity universe + + for overbought stocks whose annualized volatility exceeds 100% and enters short positions via + + limit orders + + using an equal-weight allocation, exiting when the + + Connors Relative Strength Index + + reverts below the exit threshold. +

      +
      +
      public class ConnorsCrashAlgorithm : QCAlgorithm
      +{
      +    private readonly int _rsiPeriod = 3, _streakPeriod = 2, _pctRankPeriod = 100, _volaPeriod = 100, _maxShorts = 40;
      +    private readonly decimal _crsiEntry = 90m, _crsiExit = 30m;
      +    private Universe _universe;
      +    private readonly Dictionary<Symbol, (ConnorsRelativeStrengthIndex Crsi, CustomVolatility Vola)> _indicators = new();
      +
      +    public override void Initialize()
      +    {
      +        SetStartDate(2024, 9, 1);
      +        SetEndDate(2024, 12, 31);
      +        SetCash(100_000);
      +
      +        Settings.AutomaticIndicatorWarmUp = true;
      +        Settings.SeedInitialPrices = true;
      +        UniverseSettings.Resolution = Resolution.Daily;
      +
      +        // Select liquid, tradeable-priced equities for the universe.
      +        _universe = AddUniverse(fundamentals =>
      +            fundamentals.Where(f => f.Price > 5 && f.DollarVolume > 1e6).Select(f => f.Symbol));
      +
      +        Schedule.On(
      +            DateRules.EveryDay("SPY"),
      +            TimeRules.At(8, 0),
      +            Rebalance
      +        );
      +    }
      +
      +    public override void OnSecuritiesChanged(SecurityChanges changes)
      +    {
      +        foreach (var security in changes.AddedSecurities)
      +        {
      +            // Attach ConnorsRSI indicator to each security.
      +            var crsi = CRSI(security.Symbol, _rsiPeriod, _streakPeriod, _pctRankPeriod);
      +            // Initialize and warm up the custom volatility indicator.
      +            var vola = new CustomVolatility(_volaPeriod);
      +            foreach (var bar in History<TradeBar>(security.Symbol, _volaPeriod + 1))
      +            {
      +                vola.Update(bar);
      +            }
      +            RegisterIndicator(security.Symbol, vola, Resolution.Daily);
      +            _indicators[security.Symbol] = (crsi, vola);
      +        }
      +        foreach (var security in changes.RemovedSecurities)
      +        {
      +            if (_indicators.TryGetValue(security.Symbol, out var pair))
      +            {
      +                DeregisterIndicator(pair.Crsi);
      +                DeregisterIndicator(pair.Vola);
      +                _indicators.Remove(security.Symbol);
      +            }
      +            Liquidate(security.Symbol);
      +        }
      +    }
      +
      +    private void Rebalance()
      +    {
      +        if (!_universe.Selected.Any()) return;
      +
      +        // Filter to securities with ready indicators.
      +        var securities = _universe.Selected
      +            .Where(s => Securities.ContainsKey(s) && _indicators.ContainsKey(s)
      +                        && _indicators[s].Crsi.IsReady && _indicators[s].Vola.IsReady)
      +            .Select(s => Securities[s])
      +            .ToList();
      +
      +        // Filter to securities with above-threshold annualized volatility.
      +        var filterVola = securities.Where(s => _indicators[s.Symbol].Vola.Current.Value > 100).ToList();
      +
      +        // Find currently invested short positions.
      +        var shortPositions = securities.Where(s => s.Holdings.IsShort).ToList();
      +
      +        // Liquidate short positions when ConnorsRSI falls below exit threshold.
      +        foreach (var security in shortPositions)
      +        {
      +            if (_indicators[security.Symbol].Crsi.Current.Value < _crsiExit)
      +            {
      +                Liquidate(security.Symbol);
      +            }
      +        }
      +
      +        // Exclude symbols with pending open orders.
      +        var pendingSymbols = Transactions.GetOpenOrderTickets()
      +            .Select(t => t.Symbol)
      +            .ToHashSet();
      +
      +        // Find short entry candidates that are not currently invested (high volatility and high CRSI).
      +        var shortCandidates = filterVola
      +            .Where(s => _indicators[s.Symbol].Crsi.Current.Value > _crsiEntry
      +                        && !s.Holdings.Invested
      +                        && !pendingSymbols.Contains(s.Symbol))
      +            .OrderBy(s => _indicators[s.Symbol].Crsi.Current.Value)
      +            .ThenBy(s => _indicators[s.Symbol].Vola.Current.Value)
      +            .ToList();
      +
      +        // Set union: liquidated positions are only counted once.
      +        var occupied = shortPositions.Select(s => s.Symbol).ToHashSet();
      +        occupied.UnionWith(pendingSymbols);
      +        var availableSlots = _maxShorts - occupied.Count;
      +
      +        if (availableSlots <= 0 || !shortCandidates.Any()) return;
      +
      +        var nOrders = Math.Min(shortCandidates.Count, availableSlots);
      +        var targetWeight = -1m / _maxShorts;
      +
      +        foreach (var security in shortCandidates.TakeLast(nOrders))
      +        {
      +            var quantity = (int)CalculateOrderQuantity(security.Symbol, targetWeight);
      +            if (quantity != 0)
      +            {
      +                LimitOrder(security.Symbol, quantity, Math.Round(1.03m * security.Price, 2));
      +            }
      +        }
      +    }
      +}
      +
      +public class CustomVolatility : TradeBarIndicator, IIndicatorWarmUpPeriodProvider
      +{
      +    private readonly RollingWindow<double> _window;
      +
      +    public override bool IsReady => _window.IsReady;
      +    public int WarmUpPeriod => _window.Size;
      +
      +    public CustomVolatility(int period) : base("CustomVolatility")
      +    {
      +        _window = new RollingWindow<double>(period);
      +    }
      +
      +    protected override decimal ComputeNextValue(TradeBar input)
      +    {
      +        // Annualized log-return volatility.
      +        var price = (double)input.Value;
      +        if (price <= 0) return Current.Value;
      +
      +        _window.Add(price);
      +        if (!_window.IsReady) return 0m;
      +
      +        // Collect prices from oldest to newest to compute chronological log returns.
      +        var prices = _window.ToArray().Reverse().ToArray();
      +        var logDiffs = new double[prices.Length - 1];
      +        for (var i = 0; i < logDiffs.Length; i++)
      +        {
      +            logDiffs[i] = Math.Log(prices[i + 1] / prices[i]);
      +        }
      +
      +        // Annualized standard deviation of log returns.
      +        var mean = logDiffs.Average();
      +        var variance = logDiffs.Select(d => (d - mean) * (d - mean)).Average();
      +        return (decimal)(Math.Sqrt(variance) * Math.Sqrt(252) * 100.0);
      +    }
      +}
      +
      class ConnorsCrash(QCAlgorithm):
      +
      +    def initialize(self):
      +        self.set_start_date(2024, 9, 1)
      +        self.set_end_date(2024, 12, 31)
      +        self.set_cash(100_000)
      +        self._rsi_period = 3
      +        self._streak_period = 2
      +        self._pct_rank_period = 100
      +        self._vola_period = 100
      +        self._crsi_entry = 90
      +        self._crsi_exit = 30
      +        self._max_shorts = 40
      +        self._stop_trading_days_before_end = 1
      +        self.settings.automatic_indicator_warm_up = True
      +        self.settings.seed_initial_prices = True
      +        self.universe_settings.resolution = Resolution.DAILY
      +        # Select liquid, tradeable-priced equities for the universe.
      +        self._universe = self.add_universe(
      +            lambda fundamentals: [f.symbol for f in fundamentals if f.price > 5 and f.dollar_volume > 1e6]
      +        )
      +        self.schedule.on(
      +            self.date_rules.every_day('SPY'),
      +            self.time_rules.at(8, 0),
      +            self._rebalance
      +        )
      +
      +    def on_securities_changed(self, changes):
      +        for security in changes.added_securities:
      +            # Attach ConnorsRSI indicator to each security.
      +            security.connors = self.crsi(security, self._rsi_period, self._streak_period, self._pct_rank_period)
      +            # Initialize and warm up the custom volatility indicator.
      +            security.volatility = CustomVolatility(self._vola_period)
      +            for bar in self.history[TradeBar](security, self._vola_period + 1):
      +                security.volatility.update(bar)
      +            self.register_indicator(security, security.volatility)
      +        for security in changes.removed_securities:
      +            self.deregister_indicator(security.connors)
      +            self.deregister_indicator(security.volatility)
      +            self.liquidate(security)
      +
      +    def _rebalance(self):
      +        if not self._universe.selected:
      +            return
      +        securities = [self.securities[symbol] for symbol in self._universe.selected]
      +        securities = [s for s in securities if s.connors.is_ready and s.volatility.is_ready]
      +        filter_vola = [s for s in securities if s.volatility.value > 100]
      +        # Find currently invested short positions.
      +        short_positions = [s for s in securities if s.holdings.is_short]
      +        # Liquidate short positions when ConnorsRSI falls below exit threshold.
      +        for security in short_positions:
      +            if security.connors.current.value < self._crsi_exit:
      +                self.liquidate(security)
      +        # Exclude symbols with pending open orders.
      +        pending_symbols = {t.symbol for t in self.transactions.get_open_order_tickets()}
      +        # Find short entry candidates that are not currently invested (high volatility and high CRSI).
      +        short_candidates = sorted([
      +            s for s in filter_vola
      +            if (s.connors.current.value > self._crsi_entry and
      +            not s.holdings.invested and
      +            s.symbol not in pending_symbols)
      +        ], key=lambda s: (s.connors.current.value, s.volatility.value))
      +        # Set union: liquidated positions are only counted once.
      +        short_position_symbols = {s.symbol for s in short_positions}
      +        occupied = short_position_symbols | pending_symbols
      +        available_slots = self._max_shorts - len(occupied)
      +        if available_slots <= 0 or not short_candidates:
      +            return
      +        n_orders = min(len(short_candidates), available_slots)
      +        target_weight = -1 / self._max_shorts
      +        for security in short_candidates[-n_orders:]:
      +            quantity = int(self.calculate_order_quantity(security, target_weight))
      +            if quantity:
      +                self.limit_order(security, quantity, round(1.03 * security.price, 2))
      +
      +
      +class CustomVolatility(PythonIndicator):
      +
      +    def __init__(self, period):
      +        super().__init__()
      +        self.value = 0
      +        self._window = RollingWindow[float](period)
      +
      +    def update(self, input_: BaseData):
      +        # Annualized log-return volatility.
      +        price = input_.value
      +        if price <= 0:
      +            return
      +        self._window.add(price)
      +        if self._window.is_ready:
      +            prices = np.array(list(self._window)[::-1])
      +            log_diffs = np.diff(np.log(prices))
      +            self.value = np.std(log_diffs) * math.sqrt(252) * 100.0
      +        return self.is_ready
      +
      +    @property
      +    def is_ready(self) -> bool:
      +        return self._window.is_ready
      +

      Other Examples

      @@ -463828,6 +556595,190 @@

      self.deregister_indicator(removed.ema) self.deregister_indicator(removed.sma)

      +

      + Example 2: Reset Indicators on Corporate Actions +

      +

      + The following example shows how to select a universe of US Equities based on their price and SMA indicator. + The + + SelectionData + + class keeps track of the SMA indicator for each stock in the universe dataset. + When a split or dividend occurs for a stock, the data in its indicator becomes invalid because it doesn't account for the price adjustments that the split or dividend causes. + The + + SelectionData + + class resets and warms up the indicator with the + + ScaledRaw + + + SCALED_RAW + + + data normalization mode + + , which gives you accurate indicator values to use in your universe selection after each corporate action. +

      +
      +
      class EquityIndicatorUniverseSelectionAlgorithm(QCAlgorithm):
      +
      +    def initialize(self) -> None:
      +        self.set_start_date(2024, 9, 1)
      +        self.set_end_date(2024, 12, 31)
      +        self.settings.seed_initial_prices = True
      +        # Add a universe of US Equities based on an indicator.
      +        self._selection_data_by_symbol = {}
      +        self._universe = self.add_universe(self._select_assets)
      +        # Add a warm-up period to warm up the indicators in the universe selection.
      +        self.set_warm_up(timedelta(60))
      +
      +    def _select_assets(self, fundamentals):
      +        # Update the indicator of all stocks in the universe dataset and
      +        # get the subset of stocks that have their indicator ready.
      +        ready_stocks = [
      +            f for f in fundamentals
      +            if self._selection_data_by_symbol.setdefault(f.symbol, SelectionData(self, f)).update(f)
      +        ]
      +        # As assests leave the Fundamental dataset, delete their SelectionData object.
      +        for symbol in self._selection_data_by_symbol.keys() - {f.symbol for f in fundamentals}:
      +            del self._selection_data_by_symbol[symbol]
      +        # During warm-up, keep the universe empty.
      +        if self.is_warming_up:
      +            return []
      +        # Select a subset of the stocks based on the indicator.
      +        # Example: 10 stocks furthest above their SMA.
      +        factor_by_symbol = {
      +            f.symbol: f.price / self._selection_data_by_symbol[f.symbol].indicator.current.value 
      +            for f in ready_stocks
      +        }
      +        return sorted(
      +            {k: v for k, v in factor_by_symbol.items() if v > 0}, 
      +            key=lambda symbol: factor_by_symbol[symbol]
      +        )[-100:]
      +
      +
      +class SelectionData:
      +
      +    def __init__(self, algorithm, f):
      +        self._algorithm = algorithm
      +        self._price_scale_factor = f.price_scale_factor
      +        self.indicator = SimpleMovingAverage(21)
      +
      +    def update(self, f):
      +        # If there hasn't been a split or dividend since the last trading
      +        # day, just update the indicator like normal.
      +        if f.price_scale_factor == self._price_scale_factor:
      +            return self.indicator.update(f.end_time, f.price)
      +        # Otherwise, reset the indicator and warm it up with the new 
      +        # adjusted history.
      +        self._price_scale_factor = f.price_scale_factor
      +        self.indicator.reset()
      +        history = self._algorithm.history[TradeBar](
      +            f.symbol, 
      +            self.indicator.warm_up_period, 
      +            Resolution.DAILY, 
      +            data_normalization_mode=DataNormalizationMode.SCALED_RAW
      +        )
      +        for bar in history:
      +            self.indicator.update(bar)
      +        return self.indicator.is_ready
      +
      public class EquityIndicatorUniverseSelectionAlgorithm : QCAlgorithm
      +{
      +    private Dictionary<Symbol, SelectionData> _selectionDataBySymbol = new();
      +    private Universe _universe;
      +
      +    public override void Initialize()
      +    {
      +        SetStartDate(2024, 9, 1);
      +        SetEndDate(2024, 12, 31);
      +        Settings.SeedInitialPrices = true;
      +        // Add a universe of US Equities based on an indicator.
      +        _universe = AddUniverse(SelectAssets);
      +        // Add a warm-up period to warm up the indicators in the universe selection.
      +        SetWarmUp(TimeSpan.FromDays(60));
      +    }
      +
      +    private IEnumerable<Symbol> SelectAssets(IEnumerable<Fundamental> fundamentals)
      +    {
      +        // Update the indicator of all stocks in the universe dataset and
      +        // get the subset of stocks that have their indicator ready.
      +        var readyStocks = new List<Fundamental>();
      +        foreach (var f in fundamentals)
      +        {
      +            if (!_selectionDataBySymbol.TryGetValue(f.Symbol, out var sd))
      +            {
      +                sd = new SelectionData(this, f);
      +                _selectionDataBySymbol[f.Symbol] = sd;
      +            }
      +            if (sd.Update(f))
      +            {
      +                readyStocks.Add(f);
      +            }
      +        }
      +        // As assests leave the Fundamental dataset, delete their SelectionData object.
      +        var activeStocks = fundamentals.Select(f => f.Symbol).ToHashSet();
      +        foreach (var symbol in _selectionDataBySymbol.Keys.Where(s => !activeStocks.Contains(s)).ToList())
      +        {
      +            _selectionDataBySymbol.Remove(symbol);
      +        }
      +        // During warm-up, keep the universe empty.
      +        if (IsWarmingUp)
      +        {
      +            return Enumerable.Empty<Symbol>();
      +        }
      +        // Select a subset of the stocks based on the indicator.
      +        // Example: 10 stocks furthest above their SMA.
      +        return readyStocks
      +            .Select(f => (f.Symbol, Factor: f.Price / _selectionDataBySymbol[f.Symbol].Indicator.Current.Value))
      +            .Where(t => t.Factor > 0)
      +            .OrderBy(t => t.Factor)
      +            .TakeLast(100)
      +            .Select(t => t.Symbol);
      +    }
      +}
      +
      +public class SelectionData
      +{
      +    private QCAlgorithm _algorithm;
      +    private decimal _priceScaleFactor;
      +    public SimpleMovingAverage Indicator { get; }
      +
      +    public SelectionData(QCAlgorithm algorithm, Fundamental f)
      +    {
      +        _algorithm = algorithm;
      +        _priceScaleFactor = f.PriceScaleFactor;
      +        Indicator = new SimpleMovingAverage(21);
      +    }
      +
      +    public bool Update(Fundamental f)
      +    {
      +        // If there hasn't been a split or dividend since the last trading
      +        // day, just update the indicator like normal.
      +        if (f.PriceScaleFactor == _priceScaleFactor)
      +        {
      +            return Indicator.Update(f.EndTime, f.Price);
      +        }
      +        // Otherwise, reset the indicator and warm it up with the new
      +        // adjusted history.
      +        _priceScaleFactor = f.PriceScaleFactor;
      +        Indicator.Reset();
      +        var history = _algorithm.History<TradeBar>(
      +            f.Symbol,
      +            Indicator.WarmUpPeriod,
      +            Resolution.Daily,
      +            dataNormalizationMode: DataNormalizationMode.ScaledRaw
      +        );
      +        foreach (var bar in history)
      +        {
      +            Indicator.Update(bar);
      +        }
      +        return Indicator.IsReady;
      +    }
      +}
      +

      Other Examples

      @@ -465459,9 +558410,12 @@

      Cache Data

    To clear the cache, call the - + Clear + + clear + method.

    @@ -465754,13 +558708,16 @@

    Example for Plotting

    . -
    +
    // Execute the following command in first
    -#load "../Initialize.csx"
    -
    -// Create a QuantBook object
    -#load "../QuantConnect.csx"
    -using QuantConnect;
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
    +#load "../QuantConnect.csx"
    +
    +
    +
    using QuantConnect;
     using QuantConnect.Research;
     
     var qb = new QuantBook();
    @@ -465801,8 +558758,12 @@

    Example for Plotting

    packages.
    -
    #r "../Plotly.NET.dll"
    -using Plotly.NET;
    +    
    #r "nuget: Plotly.NET"
    +#r "nuget: Plotly.NET.Interactive"
    +
    +
    +
    using Plotly.NET;
    +using Plotly.NET.Interactive;
     using Plotly.NET.LayoutObjects;
  • @@ -465872,15 +558833,11 @@

    Example for Plotting

    Line - object and create the - - HTML - - object. + object and display the chart.
  • chart.WithLayout(layout);
    -var result = HTML(GenericChart.toChartHTML(chart));
    +display(chart);
    @@ -465985,9 +558942,12 @@

    Example for Logging

  • In the - + OnData + + on_data + method, place market orders when the EMAs cross.
  • @@ -465997,11 +558957,11 @@

    Example for Logging

    if (_emaShort > _emaLong && !Portfolio[_symbol].IsLong) { - MarketOrder(_symbol, 100, tag: $"BUY: ema-short: {_emaShort:F4} > ema-long: {_emaLong:F4}"); + MarketOrder(_symbol, 100, tag: $"BUY: ema-short: {_emaShort:F4} > ema-long: {_emaLong:F4}"); } else if (_emaShort < _emaLong && !Portfolio[_symbol].IsShort) { - MarketOrder(_symbol, -100, tag: $"SELL: ema-short: {_emaShort:F4} < ema-long: {_emaLong:F4}"); + MarketOrder(_symbol, -100, tag: $"SELL: ema-short: {_emaShort:F4} < ema-long: {_emaLong:F4}"); } }
    def on_data(self, data: Slice):
    @@ -466011,15 +558971,18 @@ 

    Example for Logging

    ema_short = self._ema_short.current.value ema_long = self._ema_long.current.value if ema_short > ema_long and not self.portfolio[self._symbol].is_long: - self.market_order(self._symbol, 100, tag=f'BUY: ema-short: {ema_short:.4f} > ema-long: {ema_long:.4f}') + self.market_order(self._symbol, 100, tag=f'BUY: ema-short: {ema_short:.4f} > ema-long: {ema_long:.4f}') elif ema_short < ema_long and not self.portfolio[self._symbol].is_short: - self.market_order(self._symbol, -100, tag=f'SELL: ema-short: {ema_short:.4f} < ema-long: {ema_long:.4f}')
    + self.market_order(self._symbol, -100, tag=f'SELL: ema-short: {ema_short:.4f} < ema-long: {ema_long:.4f}')
  • In the - + OnOrderEvent + + on_order_event + method, log each fill to the content string.
  • @@ -466071,13 +559034,16 @@

    Example for Logging

    . -
    +
    // Execute the following command in first
    -#load "../Initialize.csx"
    -
    -// Create a QuantBook object
    -#load "../QuantConnect.csx"
    -using QuantConnect;
    +#load "../Initialize.csx"
    +
    +
    +
    // Load the necessary assembly files.
    +#load "../QuantConnect.csx"
    +
    +
    +
    using QuantConnect;
     using QuantConnect.Research;
     
     var qb = new QuantBook();
    @@ -466201,11 +559167,11 @@

    Example for Logging

    // Place a market order when the EMAs cross. if (_emaShort > _emaLong && !Portfolio[_symbol].IsLong) { - MarketOrder(_symbol, 100, tag: $"BUY: ema-short: {_emaShort:F4} > ema-long: {_emaLong:F4}"); + MarketOrder(_symbol, 100, tag: $"BUY: ema-short: {_emaShort:F4} > ema-long: {_emaLong:F4}"); } else if (_emaShort < _emaLong && !Portfolio[_symbol].IsShort) { - MarketOrder(_symbol, -100, tag: $"SELL: ema-short: {_emaShort:F4} < ema-long: {_emaLong:F4}"); + MarketOrder(_symbol, -100, tag: $"SELL: ema-short: {_emaShort:F4} < ema-long: {_emaLong:F4}"); } } @@ -466247,9 +559213,9 @@

    Example for Logging

    ema_short = self._ema_short.current.value ema_long = self._ema_long.current.value if ema_short > ema_long and not self.portfolio[self._symbol].is_long: - self.market_order(self._symbol, 100, tag=f'BUY: ema-short: {ema_short:.4f} > ema-long: {ema_long:.4f}') + self.market_order(self._symbol, 100, tag=f'BUY: ema-short: {ema_short:.4f} > ema-long: {ema_long:.4f}') elif ema_short < ema_long and not self.portfolio[self._symbol].is_short: - self.market_order(self._symbol, -100, tag=f'SELL: ema-short: {ema_short:.4f} < ema-long: {ema_long:.4f}') + self.market_order(self._symbol, -100, tag=f'SELL: ema-short: {ema_short:.4f} < ema-long: {ema_long:.4f}') def on_order_event(self, order_event: OrderEvent) -> None: if order_event.status != OrderStatus.FILLED: @@ -466373,7 +559339,12 @@

    Example of Custom Data

    Implement the - Reader + + Reader + + + reader + method for the custom data class. @@ -466847,6 +559818,103 @@

    Live Trading Considerations

    +

    Special Folders

    + + +

    + Your organization's Object Store includes a reserved + + .assistant + + folder that the + + QuantConnect AI assistants + + read. + It holds the custom + + skills + + , + + memories + + , and + + templates + + you define for your organization, which let the assistants apply your own expertise, remember your preferences, and start new projects from your own scaffolds. +

    +

    + The + + .assistant + + folder contains the following subfolders: +

    + + + + + + + + + + + + + + + + + + + + + +
    + Subfolder + + Purpose +
    + + skills + + + Reusable instructions and conventions the assistants apply. +
    + + memories + + + Facts the assistants remember across conversations. +
    + + templates + + + Your own project scaffolds for the assistants to initialize for new projects. +
    +

    + These files are shared across your organization. + For information on uploading files to the Object Store, see the documentation for the + + Algorithm Lab + + , + + CLI + + , or + + API + + . +

    + + +

     

    @@ -468884,7 +561952,7 @@

    # Prepare feature and label data for training.
     def get_features_and_labels(self, n_steps=5):
    -    training_df = self.PandasConverter.GetDataFrame[TradeBar](list(self.training_data)[::-1])['close']
    +    training_df = self.pandas_converter.get_data_frame[TradeBar](list(self.training_data)[::-1])['close']
     
         features = []
         for i in range(1, n_steps + 1):
    @@ -472429,7 +565497,6 @@ 

    Build Models

    # Define the model structure. def __init__(self): super(NeuralNetwork, self).__init__() - self.flatten = nn.Flatten() self.linear_relu_stack = nn.Sequential( nn.Linear(5, 5), # input size, output size of the layer. nn.ReLU(), # Relu non-linear transformation. @@ -472606,7 +565673,7 @@

    # Add the latest bar to training data to ensure the model is trained with the most recent market data.
     def on_data(self, slice: Slice) -> None:
    -    if self._symbol in slice.Bars:
    +    if self._symbol in slice.bars:
             self.training_data.add(slice.bars[self._symbol].close)
    @@ -472939,7 +566006,6 @@

    # Model Structure def __init__(self): super(NeuralNetwork, self).__init__() - self.flatten = nn.Flatten() self.linear_relu_stack = nn.Sequential( nn.Linear(5, 5), # input size, output size of the layer nn.ReLU(), # Relu non-linear transformation @@ -473258,9 +566324,9 @@

    Predict Labels

    # Place orders based on the forecasted market direction.
     if prediction > 0:
    -    self.SetHoldings(self._symbol, 1)
    +    self.set_holdings(self._symbol, 1)
     elif prediction < 0:            
    -    self.SetHoldings(self._symbol, -1)
    + self.set_holdings(self._symbol, -1)
    @@ -473845,9 +566911,9 @@

    Predict Labels

    # Place orders based on the forecasted market direction.
     if prediction > 0:
    -    self.SetHoldings(self._symbol, 1)
    +    self.set_holdings(self._symbol, 1)
     elif prediction < 0:
    -    self.SetHoldings(self._symbol, -1)
    + self.set_holdings(self._symbol, -1)
    @@ -476991,7 +570057,7 @@

    Use Cases

    Tiingo and - + Benzinga while reducing noise. @@ -478878,9 +571944,12 @@

    Add Models

    initialize
    method, call the - + AddUniverseSelection + + add_universe_selection + method.

    @@ -480094,6 +573163,58 @@

    Schedule

    Trigger an event on the last tradable date of each week for a specific symbol minus an offset. + + + + self.date_rules.quarter_start(days_offset: int = 0) + + + DateRules.QuarterStart(int daysOffset = 0) + + + + Trigger an event on the first day of each quarter plus an offset. + + + + + + self.date_rules.quarter_start(symbol: Symbol, days_offset: int = 0) + + + DateRules.QuarterStart(Symbol symbol, int daysOffset = 0) + + + + Trigger an event on the first tradable date of each quarter for a specific symbol plus an offset. + + + + + + self.date_rules.quarter_end(days_offset: int = 0) + + + DateRules.QuarterEnd(int daysOffset = 0) + + + + Trigger an event on the last day of each quarter minus an offset. + + + + + + self.date_rules.quarter_end(symbol: Symbol, days_offset: int = 0) + + + DateRules.QuarterEnd(Symbol symbol, int daysOffset = 0) + + + + Trigger an event on the last tradable date of each quarter for a specific symbol minus an offset. + + @@ -480448,9 +573569,9 @@

    // We want to trade the EMA with raw price but not altered by splits. UniverseSettings.DataNormalizationMode = DataNormalizationMode.SplitAdjusted; - // Only trade on the top 10 most traded stocks since they have the most popularity to drive trends. - AddUniverse(Universe.Top(10)); - } + // Select and trade the top liquid universe. + AddUniverseSelection(new QC500UniverseSelectionModel()); + } public override void OnData(Slice slice) { @@ -480506,8 +573627,8 @@

    # We want to trade the EMA with raw price but not altered by splits. self.universe_settings.data_normalization_mode = DataNormalizationMode.SPLIT_ADJUSTED - # Only trade on the top 10 most traded stocks since they have the most popularity to drive trends. - self.add_universe(self.universe.top(10)) + # Select and trade the top liquid universe. + self.add_universe_selection(QC500UniverseSelectionModel()) def on_data(self, slice: Slice) -> None: for symbol, bar in slice.bars.items(): @@ -480594,9 +573715,12 @@

    Add Manual Universe Selection

    initialize
    method, call the - + AddUniverseSelection + + add_universe_selection + method. The ManualUniverseSelectionModel @@ -481073,7 +574197,7 @@

    Fundamental Selection

    most_liquid = sorted(filtered, key=lambda x: x.dollar_volume, reverse=True)[:100] # Select the 10 assets with the lowest PE ratio. lowest_pe_ratio = sorted(most_liquid, key=lambda x: x.valuation_ratios.pe_ratio, reverse=True)[:10] - return [x.Symbol for x in lowest_pe_ratio] + return [x.symbol for x in lowest_pe_ratio]

    To return the current universe constituents from the selection function, return @@ -481469,7 +574593,7 @@

    self.volume = 0 # Warm up the EMA indicator. - algorithm.warm_up_indicator(symbol, self._ema, Resolution.Daily); + algorithm.warm_up_indicator(symbol, self._ema, Resolution.DAILY); # Update your variables and indicators with the latest data. # You may also want to use the History API here to warm up the indicator. @@ -482007,7 +575131,7 @@

    Add ETF Constituents Universe Selection

    Ticker of the ETF to get constituents for. To view the available ETFs, see - + Supported ETFs . @@ -482462,7 +575586,7 @@

    symbol = c.symbol if symbol in self.ema_by_symbol and c.weight: # Update EMA with the latest weight. - self.ema_by_symbol[symbol].update(c.EndTime, c.weight) + self.ema_by_symbol[symbol].update(c.end_time, c.weight) else: # Create an EMA to filter by trend. self.ema_by_symbol[symbol] = ExponentialMovingAverage(60) @@ -482986,6 +576110,58 @@

    Date Rules

    Trigger an event on the last tradable date of each week for a specific symbol minus an offset. + + + + self.date_rules.quarter_start(days_offset: int = 0) + + + DateRules.QuarterStart(int daysOffset = 0) + + + + Trigger an event on the first day of each quarter plus an offset. + + + + + + self.date_rules.quarter_start(symbol: Symbol, days_offset: int = 0) + + + DateRules.QuarterStart(Symbol symbol, int daysOffset = 0) + + + + Trigger an event on the first tradable date of each quarter for a specific symbol plus an offset. + + + + + + self.date_rules.quarter_end(days_offset: int = 0) + + + DateRules.QuarterEnd(int daysOffset = 0) + + + + Trigger an event on the last day of each quarter minus an offset. + + + + + + self.date_rules.quarter_end(symbol: Symbol, days_offset: int = 0) + + + DateRules.QuarterEnd(Symbol symbol, int daysOffset = 0) + + + + Trigger an event on the last tradable date of each quarter for a specific symbol minus an offset. + + @@ -485233,7 +578409,7 @@

    Options Universe Selection

    # In the initialize method, define the universe settings and add data.
     self.universe_settings.asynchronous = True
    -self.add_universe_settings(EarliestExpiringAtTheMoneyCallOptionUniverseSelectionModel(self))
    +self.add_universe_selection(EarliestExpiringAtTheMoneyCallOptionUniverseSelectionModel(self))
     
     # Outside of the algorithm class, define the universe selection model.
     class EarliestExpiringAtTheMoneyCallOptionUniverseSelectionModel(OptionUniverseSelectionModel):
    @@ -485255,7 +578431,7 @@ 

    Options Universe Selection

    return [Symbol.create_canonical_option(contract.symbol) for contract in self.algo.futures_chain(future_symbol)] # Create a filter to select contracts that have the strike price within 1 strike level and expire within 7 days. - def Filter(self, option_filter_universe: OptionFilterUniverse) -> OptionFilterUniverse: + def filter(self, option_filter_universe: OptionFilterUniverse) -> OptionFilterUniverse: return option_filter_universe.strikes(-1, -1).expiration(0, 7).calls_only()

    @@ -486346,9 +579522,12 @@

    Coarse Fundamental Selection

    To return the current universe constituents from the coarse selection function, return - + Universe.Unchanged + + Universe.UNCHANGED + .

    @@ -486639,9 +579818,12 @@

    Add Models

    initialize
    method, call the - + AddAlpha + + add_alpha + method.

    @@ -487099,9 +580281,12 @@

    To mark insights as a group, call the - + Insight.Group + + Insight.group + method.

    @@ -487136,9 +580321,12 @@

    cancel / - + Expire + + expire + method with the algorithm's Coordinated Universal Time (UTC).

    @@ -490743,7 +583931,13 @@

    Accumulative Insight Model

    , it decreases the position size by a fixed percent. For each active Insight of direction - InsightDirection.Flat + InsightDirection. + + Flat + + + FLAT + , it moves the position size towards 0 by a fixed percent.

    @@ -491176,310 +584370,316 @@

    Mean Variance Optimization Model

    MinimumVariancePortfolioOptimizer with upper and lower weights that respect the - + + portfolioBias + + + portfolio_bias + + . +

    +

    + This model removes expired insights from the + + Insight Manager + + during each rebalance. It also removes all insights for a security when the security is removed from the + + universe + + . +

    +

    + For more information about this model, see the + + class reference + + and + + implementation + + . +

    +

    + For more information about this model, see the + + class reference + + and + + implementation + + . +

    + + + +

    Black Litterman Optimization Model

    + + +

    + The + + BlackLittermanOptimizationPortfolioConstructionModel + + receives + + Insight + + objects from multiple Alphas and combines them into a single portfolio. These multiple sources of Alpha models can be seen as the "investor views" required in the classical model. +
    +

    +
    +
    // Use BlackLittermanOptimizationPortfolioConstructionModel to incorporate market equilibrium portfolio and the investor views to create an improved portfolio aligning with both market expectations and investor insights.
    +SetPortfolioConstruction(new BlackLittermanOptimizationPortfolioConstructionModel());
    +
    # Use BlackLittermanOptimizationPortfolioConstructionModel to incorporate market equilibrium portfolio and the investor views to create an improved portfolio aligning with both market expectations and investor insights.
    +self.set_portfolio_construction(BlackLittermanOptimizationPortfolioConstructionModel())
    +
    +

    + The following table describes the arguments the model accepts: +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Argument + + Data Type + + Description + + Default Value +
    + + rebalance + + + rebalanceResolution + + + + Resolution + + + Rebalancing frequency + + + Resolution.Daily + + + Resolution.DAILY + +
    + + portfolioBias + + + portfolio_bias + + + + PortfolioBias + + + The bias of the portfolio + + + PortfolioBias. + + LongShort + + + LONG_SHORT + + +
    + + lookback + + + + int + + + Historical return lookback period + + 1 +
    + + period + + + + int + + + The time interval of history price to calculate the weight + + 63 +
    + + resolution + + + + Resolution + + + The resolution of the history price + + + Resolution.Daily + + + Resolution.DAILY + +
    + + risk_free_rate + + + riskFreeRate + + + + double + + + float + + + The risk free rate + + 0.0 +
    + + delta + + + + double + + + float + + + The risk aversion coefficient of the market portfolio + + 2.5 +
    + + tau + + + + double + + + float + + + The model parameter indicating the uncertainty of the CAPM prior + + 0.05 +
    + + optimizer + + + + IPortfolioOptimizer + + + The portfolio optimization algorithm + + + null + + + None + +
    +

    + This model supports other data types for the rebalancing frequency argument. For more information about the supported types, see + + Rebalance Frequency + + . If you don't provide an + + optimizer + + argument, the default one is the + + MinimumVariancePortfolioOptimizer + + with upper and lower weights that respect the + portfolioBias - . -

    -

    - This model removes expired insights from the - - Insight Manager - - during each rebalance. It also removes all insights for a security when the security is removed from the - - universe - - . -

    -

    - For more information about this model, see the - - class reference - - and - - implementation - - . -

    -

    - For more information about this model, see the - - class reference - - and - - implementation - - . -

    - - - -

    Black Litterman Optimization Model

    - - -

    - The - - BlackLittermanOptimizationPortfolioConstructionModel - - receives - - Insight - - objects from multiple Alphas and combines them into a single portfolio. These multiple sources of Alpha models can be seen as the "investor views" required in the classical model. -
    -

    -
    -
    // Use BlackLittermanOptimizationPortfolioConstructionModel to incorporate market equilibrium portfolio and the investor views to create an improved portfolio aligning with both market expectations and investor insights.
    -SetPortfolioConstruction(new BlackLittermanOptimizationPortfolioConstructionModel());
    -
    # Use BlackLittermanOptimizationPortfolioConstructionModel to incorporate market equilibrium portfolio and the investor views to create an improved portfolio aligning with both market expectations and investor insights.
    -self.set_portfolio_construction(BlackLittermanOptimizationPortfolioConstructionModel())
    -
    -

    - The following table describes the arguments the model accepts: -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - Argument - - Data Type - - Description - - Default Value -
    - - rebalance - - - rebalanceResolution - - - - Resolution - - - Rebalancing frequency - - - Resolution.Daily - - - Resolution.DAILY - -
    - - portfolioBias - - - portfolio_bias - - - - PortfolioBias - - - The bias of the portfolio - - - PortfolioBias. - - LongShort - - - LONG_SHORT - - -
    - - lookback - - - - int - - - Historical return lookback period - - 1 -
    - - period - - - - int - - - The time interval of history price to calculate the weight - - 63 -
    - - resolution - - - - Resolution - - - The resolution of the history price - - - Resolution.Daily - - - Resolution.DAILY - -
    - - risk_free_rate - - - riskFreeRate - - - - double - - - float - - - The risk free rate - - 0.0 -
    - - delta - - - - double - - - float - - - The risk aversion coefficient of the market portfolio - - 2.5 -
    - - tau - - - - double - - - float - - - The model parameter indicating the uncertainty of the CAPM prior - - 0.05 -
    - - optimizer - - - - IPortfolioOptimizer - - - The portfolio optimization algorithm - - - null - - - None - -
    -

    - This model supports other data types for the rebalancing frequency argument. For more information about the supported types, see - - Rebalance Frequency - - . If you don't provide an - - optimizer - - argument, the default one is the - - MinimumVariancePortfolioOptimizer - - with upper and lower weights that respect the - - portfolioBias + + portfolio_bias .

    @@ -492543,9 +585743,12 @@

    Multi-Model Algorithms

    initialize
    method, call the - + AddRiskManagement + + add_risk_management + method multiple times.

    @@ -493138,241 +586341,391 @@

    $$

    -
    using Accord.Statistics;
    -using MathNet.Numerics.Distributions;
    +   
    using MathNet.Numerics.Distributions;
    +
     
     public class FrameworkRiskManagementAlgorithm : QCAlgorithm
     {
    +
         public override void Initialize()
         {
             SetStartDate(2024, 9, 1);
             SetEndDate(2024, 9, 15);
             SetCash(1000000);
    -        
             // Add a universe of the most liquid stocks since their trend is more capital-supported.
    -        AddUniverseSelection(new QC500UniverseSelectionModel());
    -        // Emit insights all for selected stocks, rebalancing every 2 weeks.
    +        AddUniverseSelection(new ETFConstituentsUniverseSelectionModel("SPY"));
    +        // Emit insights for all selected stocks, rebalancing every two weeks.
             AddAlpha(new ConstantAlphaModel(InsightType.Price, InsightDirection.Up, TimeSpan.FromDays(14)));
             // Equal weighting on each insight is needed to dissipate capital risk evenly.
             SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel());
    -
    -        // Liquidate on extreme catastrophic event.
    +        // Liquidate on extreme catastrophic events.
             AddRiskManagement(new TailValueAtRiskRiskManagementModel(0.05d, 14d));
         }
    +}
     
    -    private class TailValueAtRiskRiskManagementModel : RiskManagementModel
    -    {
    -        // The alpha level on TVaR calculation.
    -        private readonly double _alpha;
    -        // The number of days that the insight signal lasts for.
    -        private readonly double _days;
    -        private Dictionary<Symbol, SymbolData> _logRetBySymbol = new();
    -
    -        public TailValueAtRiskRiskManagementModel(double alpha = 0.05d, double numDays = 14d)
    -        {
    -            _alpha = alpha;
    -            _days = numDays;
    -        }
    -
    -        // Adjust the portfolio targets and return them. If there are no changes, emit nothing.
    -        public override IEnumerable<PortfolioTarget> ManageRisk(QCAlgorithm algorithm, IPortfolioTarget[] _)
    -        {
    -            var targets = new List<PortfolioTarget>();
    -
    -            foreach (var (symbol, security) in algorithm.Securities)
    -            {
    -                if (!security.Invested)
    -                {
    -                    continue;
    -                }
    -
    -                var pnl = security.Holdings.UnrealizedProfitPercent;
    -                // If the %PnL is worse than the preset level TVaR, we liquidate it.
    -                if (pnl < GetTVaR(symbol))
    -                {
    -                    // Cancel insights to avoid reordering afterward.
    -                    algorithm.Insights.Cancel(new[] { symbol });
    -                    // Liquidate.
    -                    targets.Add(new PortfolioTarget(symbol, 0, tag: "Liquidate due to TVaR"));
    -                }
    -            }
     
    -            return targets;
    -        }
    -
    -        private decimal GetTVaR(Symbol symbol)
    -        {
    -            if (!_logRetBySymbol.TryGetValue(symbol, out var symbolData))
    -            {
    -                return 0m;
    -            }
    +public class TailValueAtRiskRiskManagementModel : RiskManagementModel
    +{
    +    private const string LogReturnKey = "LogReturn";
    +    private const string LogReturnMeanKey = "LogReturnMean";
    +    private const string LogReturnSdKey = "LogReturnSd";
    +    private readonly double _alpha;
    +    private readonly double _days;
    +    private readonly List<Security> _securities = [];
     
    -            // TVaR = \mu + \sigma * \phi[\Phi^{-1}(p)] / (1 - p)
    -            // Scale up to the days of the signal. By stochastic calculus, we multiply by sqrt(days).
    -            var dailyTVaR = symbolData.MeanLogRet + symbolData.SdLogRet * Math.Sqrt(_days) * Normal.PDF(0d, 1d, Normal.InvCDF(0d, 1d, _alpha)) / (1d - _alpha);
    -            // We want the left side of the symmetric distribution.
    -            return -Convert.ToDecimal(dailyTVaR);
    -        }
    +    public TailValueAtRiskRiskManagementModel(double alpha = 0.05d, double numDays = 7d)
    +    {
    +        // Set the alpha level for the TVaR calculation.
    +        _alpha = alpha;
    +        // Set the number of days the insight signal lasts.
    +        _days = numDays;
    +    }
     
    -        public override void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes)
    +    public override IEnumerable<PortfolioTarget> ManageRisk(QCAlgorithm algorithm, IPortfolioTarget[] _)
    +    {
    +        var targets = new List<PortfolioTarget>();
    +        foreach (var security in _securities)
             {
    -            foreach (var added in changes.AddedSecurities)
    +            if (!security.Invested)
                 {
    -                // Add SymbolData class to handle log returns.
    -                _logRetBySymbol[added.Symbol] = new SymbolData(algorithm, added.Symbol);
    +                continue;
                 }
    -
    -            foreach (var removed in changes.RemovedSecurities)
    +            var pnl = security.Holdings.UnrealizedProfitPercent;
    +            // Liquidate when the unrealized loss is worse than the preset TVaR level.
    +            if (pnl < GetTVaR(security))
                 {
    -                // Stop subscription on the data to release computational resources.
    -                if (_logRetBySymbol.Remove(removed.Symbol, out var symbolData))
    -                {
    -                    symbolData.Dispose();
    -                }
    +                // Cancel insights to avoid reordering afterward.
    +                var symbols = new List<Symbol> { security.Symbol };
    +                algorithm.Insights.Cancel(symbols);
    +                targets.Add(new PortfolioTarget(security.Symbol, 0, tag: "Liquidate due to TVaR"));
                 }
             }
    +        return targets;
         }
     
    -    private class SymbolData
    +    public override void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes)
         {
    -        private readonly QCAlgorithm _algorithm;
    -        private readonly Symbol _symbol;
    -        // Since the return is assumed log-normal, we use the log return indicator to calculate TVaR later.
    -        private LogReturn _logRet = new(1);
    -        // Set up a rolling window to save the log return for calculating the mean and SD for TVaR calculation.
    -        private RollingWindow<double> _window = new(252);
    -
    -        public bool IsReady => _window.IsReady;
    -
    -        public double MeanLogRet => _window.Average();
    -
    -        public double SdLogRet => Measures.StandardDeviation(_window.ToArray());
    -
    -        public SymbolData(QCAlgorithm algorithm, Symbol symbol)
    +        foreach (var security in changes.AddedSecurities)
             {
    -            _algorithm = algorithm;
    -            // Register the indicator for automatic updating for daily log returns.
    -            algorithm.RegisterIndicator(symbol, _logRet, Resolution.Daily);
    -            // Add a handler to save the log return to the rolling window.
    -            _logRet.Updated += (_algorithm, point) => _window.Add((double)point.Value);
    -            // Warm up the rolling window.
    -            var history = algorithm.History<TradeBar>(symbol, 253, Resolution.Daily);
    +            // Store the log return indicator and rolling window directly on the security.
    +            var logReturn = algorithm.LOGR(security.Symbol, 1, Resolution.Daily);
    +            var logReturnMean = IndicatorExtensions.SMA(logReturn, 252);
    +            var logReturnSd = IndicatorExtensions.Of(new StandardDeviation(252), logReturn);
    +            security.Set(LogReturnKey, logReturn);
    +            security.Set(LogReturnMeanKey, logReturnMean);
    +            security.Set(LogReturnSdKey, logReturnSd);
    +            var history = algorithm.History<TradeBar>(security.Symbol, 253, Resolution.Daily);
                 foreach (var bar in history)
                 {
    -                _logRet.Update(bar.EndTime, bar.Close);
    +                logReturn.Update(bar.EndTime, bar.Close);
                 }
    +            _securities.Add(security);
             }
    -
    -        public void Dispose()
    +        foreach (var security in changes.RemovedSecurities)
             {
    -            // Stop subscription on the data to release computational resources.
    -            _algorithm.DeregisterIndicator(_logRet);
    +            if (!_securities.Contains(security))
    +            {
    +                continue;
    +            }
    +            // Stop updating the log return indicator to release resources.
    +            algorithm.DeregisterIndicator(security.Get<LogReturn>(LogReturnKey));
    +            _securities.Remove(security);
             }
         }
    +
    +    private decimal GetTVaR(Security security)
    +    {
    +        var logReturnMean = security.Get<SimpleMovingAverage>(LogReturnMeanKey);
    +        var logReturnSd = security.Get<StandardDeviation>(LogReturnSdKey);
    +        // Calculate TVaR from the log return mean and standard deviation.
    +        var dailyTVaR = (double)logReturnMean.Current.Value + (double)logReturnSd.Current.Value * Math.Sqrt(_days) * Normal.PDF(0d, 1d, Normal.InvCDF(0d, 1d, _alpha)) / (1d - _alpha);
    +        // Use the left side of the symmetric distribution.
    +        return -Convert.ToDecimal(dailyTVaR);
    +    }
     }
    from scipy.stats import norm
     
    +
     class FrameworkRiskManagementAlgorithm(QCAlgorithm):
    +
         def initialize(self) -> None:
             self.set_start_date(2024, 9, 1)
             self.set_end_date(2024, 9, 15)
             self.set_cash(1000000)
    -
             # Add a universe of the most liquid stocks since their trend is more capital-supported.
    -        self.add_universe_selection(QC500UniverseSelectionModel())
    -        # Emit insights all for selected stocks, rebalancing every 2 weeks.
    +        self.add_universe_selection(ETFConstituentsUniverseSelectionModel('SPY'))
    +        # Emit insights for all selected stocks, rebalancing every two weeks.
             self.add_alpha(ConstantAlphaModel(InsightType.PRICE, InsightDirection.UP, timedelta(14)))
    -        # Equal weighting on each insight to dissipate capital risk evenly.
    +        # Equal weighting on each insight is needed to dissipate capital risk evenly.
             self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
    -
             # Liquidate on extreme catastrophic events.
             self.add_risk_management(TailValueAtRiskRiskManagementModel(0.05, 14))
     
    +
     class TailValueAtRiskRiskManagementModel(RiskManagementModel):
    -    _log_ret_by_symbol = {}
     
         def __init__(self, alpha: float = 0.05, num_days: float = 7) -> None:
    -        # The alpha level on TVaR calculation.
    -        self.alpha = alpha
    -        # The number of days that the insight signal lasts for.
    -        self.days = num_days
    +        # Set the alpha level for the TVaR calculation.
    +        self._alpha = alpha
    +        # Set the number of days the insight signal lasts.
    +        self._days = num_days
    +        self._securities = []
     
    -    # Adjust the portfolio targets and return them. If there are no changes, emit nothing.
    -    def manage_risk(self, algorithm: QCAlgorithm, targets: List[PortfolioTarget]) -> List[PortfolioTarget]:
    +    def manage_risk(self, algorithm: QCAlgorithm, targets: List[IPortfolioTarget]) -> List[IPortfolioTarget]:
             targets = []
    -        for kvp in algorithm.securities:
    -            security = kvp.value
    +        for security in self._securities:
                 if not security.invested:
                     continue
    -
                 pnl = security.holdings.unrealized_profit_percent
    -            symbol = security.symbol
    -            # If the %PnL is worse than the preset level TVaR, we liquidate it.
    -            if pnl < self.get_tvar(symbol):
    +            # Liquidate when the unrealized loss is worse than the preset TVaR level.
    +            if pnl < self._get_tvar(security):
                     # Cancel insights to avoid reordering afterward.
    -                algorithm.insights.cancel([symbol])
    -                # Liquidate.
    -                targets.append(PortfolioTarget(symbol, 0, tag="Liquidate due to TVaR"))
    -
    +                algorithm.insights.cancel([security])
    +                targets.append(PortfolioTarget(security, 0, tag="Liquidate due to TVaR"))
             return targets
     
    -    def get_tvar(self, symbol: Symbol) -> float:
    -        symbol_data = self._log_ret_by_symbol.get(symbol)
    -        if not symbol_data:
    -            return 0
    -        
    -        # TVaR = \mu + \sigma * \phi[\Phi^{-1}(p)] / (1 - p)
    -        # Scale up to the days of the signal. By stochastic calculus, we multiply by sqrt(days).
    -        daily_tvar = symbol_data.mean_log_ret + symbol_data.sd_log_ret * np.sqrt(self.days) * norm.pdf(norm.ppf(self.alpha)) / (1 - self.alpha)
    -        # We want the left side of the symmetric distribution.
    -        return -daily_tvar
    +    def _get_tvar(self, security: Security) -> float:
    +        # Calculate TVaR from the log return mean and standard deviation.
    +        return -(
    +            security.log_return_mean.current.value 
    +            + security.log_return_sd.current.value 
    +            * np.sqrt(self._days) 
    +            * norm.pdf(norm.ppf(self._alpha)) 
    +            / (1 - self._alpha)
    +        )
     
         def on_securities_changed(self, algorithm: QCAlgorithm, changes: SecurityChanges) -> None:
    -        for added in changes.added_securities:
    -            # Add SymbolData class to handle log returns.
    -            self._log_ret_by_symbol[added.symbol] = SymbolData(algorithm, added.symbol)
    +        for security in changes.added_securities:
    +            # Store the log return indicator and rolling window directly on the security.
    +            security.log_ret = algorithm.logr(security, 1, Resolution.DAILY)
    +            security.log_return_mean = IndicatorExtensions.sma(security.log_ret, 252)
    +            security.log_return_sd = IndicatorExtensions.of(StandardDeviation(252), security.log_ret)
    +            history = algorithm.history[TradeBar](security, 253, Resolution.DAILY)
    +            for bar in history:
    +                security.log_ret.update(bar.end_time, bar.close)
    +            self._securities.append(security)
    +        for security in changes.removed_securities:
    +            if security not in self._securities:
    +                continue
    +            # Stop updating the log return indicator to release resources.
    +            algorithm.deregister_indicator(security.log_ret)
    +            self._securities.remove(security)
    +
    +
    +

    + Example 3: Bracket Risk Model +

    +

    + The following algorithm implements a custom bracket risk management model that combines a trailing stop-loss with a take-profit target. + Positions are exited when unrealized profit exceeds a threshold or when the price falls by more than a specified percentage from the trailing high. + It is used alongside an XGBoost Alpha model that trades Equities near their earnings dates. +

    +
    +
    from sklearn.model_selection import RandomizedSearchCV, train_test_split
    +from sklearn.preprocessing import MinMaxScaler
    +import xgboost as xgb
     
    -        for removed in changes.removed_securities:
    -            # Stop subscription on the data to release computational resources.
    -            symbol_data = self._log_ret_by_symbol.pop(removed.symbol, None)
    -            if symbol_data:
    -                symbol_data.dispose()
    +class ModulatedNadionReplicator(QCAlgorithm):
     
    -class SymbolData:
    -    def __init__(self, algorithm: QCAlgorithm, symbol: Symbol) -> None:
    -        self.algorithm = algorithm
    -        self.symbol = symbol
    +    def initialize(self):
    +        self.set_start_date(2024, 9, 1)
    +        self.set_end_date(2024, 12, 31)
    +        self.set_cash(100_000)
    +        self.settings.free_portfolio_value_percentage = 0.05
    +        self.settings.seed_initial_prices = True
    +        self._spy = self.add_equity("SPY", Resolution.HOUR)
     
    -        # Since the return is assumed log-normal, we use the log return indicator to calculate TVaR later.
    -        self.log_ret = LogReturn(1)
    -        # Register the indicator for automatic updating for daily log returns.
    -        algorithm.register_indicator(symbol, self.log_ret, Resolution.DAILY)
    -        # Set up a rolling window to save the log return for calculating the mean and SD for TVaR calculation.
    -        self.window = RollingWindow(252)
    -        # Add a handler to save the log return to the rolling window.
    -        self.log_ret.updated += lambda _, point: self.window.add(point.value)
    -        # Warm up the rolling window.
    -        history = algorithm.history[TradeBar](symbol, 253, Resolution.DAILY)
    -        for bar in history:
    -            self.log_ret.update(bar.end_time, bar.close)
    +        # Create universe parameters.
    +        self.universe_settings.resolution = Resolution.HOUR
    +        self.universe_settings.asynchronous = True
    +        date_rule = self.date_rules.month_start(self._spy)
    +        self.universe_settings.schedule.on(date_rule)
    +        self.add_universe_selection(EarningsVolumeUniverseSelectionModel(10))
     
    -    @property
    -    def is_ready(self) -> bool:
    -        return self.window.is_ready
    +        # Add the other framework models.
    +        self.add_alpha(XGBoostAlphaModel(self, date_rule, self._spy))
    +        self.set_portfolio_construction(InsightWeightingPortfolioConstructionModel())
    +        self.set_risk_management(BracketRiskModel(0.05, 0.15))
     
    -    @property
    -    def mean_log_ret(self) -> float:
    -        # Mean log return for TVaR calculation.
    -        return np.mean(list(self.window))
    +        # Add a warm up so the algorithm has insights on deployment day.
    +        self.set_warm_up(timedelta(45))
     
    -    @property
    -    def sd_log_ret(self) -> float:
    -        # SD of log return for TVaR calculation.
    -        return np.std(list(self.window), ddof=1)
     
    -    def dispose(self) -> None:
    -        # Stop subscription on the data to release computational resources.
    -        self.algorithm.deregister_indicator(self.log_ret)
    +class EarningsVolumeUniverseSelectionModel(FundamentalUniverseSelectionModel): + # Selects symbols by liquidity and nearest earnings report date. + def __init__(self, universe_size=10): + self._universe_size = universe_size + super().__init__(self._select) + + def _select(self, fundamental): + # Sort the top 30 by price and dollar volume. + liquid = sorted( + [f for f in fundamental if f.has_fundamental_data and 20 <= f.price <= 200 and f.dollar_volume > 5_000_000], + key=lambda f: f.dollar_volume + )[-30:] + + # Select symbols with nearest earnings report dates. + selected = sorted( + [f for f in liquid if f.earning_reports.file_date], + key=lambda f: str(f.earning_reports.file_date) + )[:self._universe_size] + + return [f.symbol for f in selected] + + +class XGBoostAlphaModel(AlphaModel): + + def __init__(self, algorithm, date_rule, spy): + self._algorithm = algorithm + self._universe = [] + self._insights = [] + self._feature_window = 24 + self._history_bars = 48 + self._rsi_period = 12 + self._scaler = MinMaxScaler(feature_range=(-1, 1)) + self._spy = spy + + # Create rate-of-change indicator for SPY with specified window. + self._spy.rocp = algorithm.rocp(spy, self._feature_window) + self._spy.rocp.window.size = self._feature_window + algorithm.indicator_history(self._spy.rocp, spy, self._spy.rocp.period + self._spy.rocp.window.size) + + algorithm.train(date_rule, algorithm.time_rules.at(8, 0), self._train_models) + algorithm.schedule.on(date_rule, algorithm.time_rules.after_market_open(self._spy, 30), self._create_insights) + + def _create_insights(self): + self._insights.clear() + + # Extract predictions from all trained models. + for security in self._universe: + # Skip prediction if model not yet trained. + if not (security.model and security.rocp.window.is_ready and self._spy.rocp.window.is_ready): + continue + features, _ = self._build_features(security) + # Get prediction from the trained model. + magnitude = security.model.predict(features)[-1] + # Determine signal direction based on prediction magnitude. + direction = InsightDirection.FLAT + if magnitude > 0.05: + direction = InsightDirection.UP + elif magnitude < -0.05: + direction = InsightDirection.DOWN + # Generate price insights with computed weights and directions. + self._insights.append(Insight.price(security, timedelta(1), direction, weight=0.3 * abs(magnitude))) + + def update(self, algorithm, data): + if algorithm.is_warming_up: + return [] + + insights = self._insights.copy() + self._insights.clear() + + return insights + + def on_securities_changed(self, algorithm, changes): + for security in changes.added_securities: + if security == self._spy or security in self._universe: + continue + # Add security and register indicators to universe. + self._universe.append(security) + security.model = None + security.rocp = algorithm.rocp(security, self._feature_window) + security.rsi = algorithm.rsi(security, self._rsi_period, MovingAverageType.SIMPLE) + security.atr = algorithm.atr(security, self._feature_window, MovingAverageType.SIMPLE) + # Configure window sizes and populate history for indicator and window. + bars = algorithm.history[TradeBar](security, self._feature_window * 2) + for indicator in [security.rocp, security.rsi, security.atr]: + indicator.window.size = self._feature_window + for bar in bars: + indicator.update(bar) + + for security in changes.removed_securities: + if security not in self._universe: + continue + # Remove security and deregister indicators from universe. + self._universe.remove(security) + for indicator in [security.rocp, security.rsi, security.atr]: + algorithm.deregister_indicator(indicator) + + def _build_features(self, security): + # Compute momentum and volatility indicators and normalize all features to consistent range. + scaled = self._scaler.fit_transform(np.hstack( + [self._get_indicator_history(self._spy.rocp)] + + [self._get_indicator_history(indicator) for indicator in [security.rocp, security.rsi, security.atr]] + )) + + return scaled[:, :3], scaled[:, [3]] + + def _get_indicator_history(self, indicator): + return np.array([x.value for x in indicator.window])[::-1].reshape(-1, 1) + + def _train_models(self): + # Iterate through all security in the universe for model training. + for security in self._universe: + if not (security.rocp.window.is_ready and self._spy.rocp.window.is_ready): + continue + + features, scaled_rocp = self._build_features(security) + target = scaled_rocp.ravel() + + # Prepare training and validation splits from the feature matrix. + x_train, x_valid, y_train, y_valid = train_test_split( + features, target, test_size=0.35, random_state=42, + ) + + # Define hyperparameter grid for randomized search optimization. + parameters = { + 'n_estimators': [100, 200, 300, 400], + 'learning_rate': [0.001, 0.005, 0.01, 0.05], + 'max_depth': [8, 10, 12, 15], + 'gamma': [0.001, 0.005, 0.01, 0.02], + 'random_state': [42] + } + eval_set = [(x_train, y_train), (x_valid, y_valid)] + base_model = xgb.XGBRegressor(objective="reg:squarederror", verbosity=0) + model = RandomizedSearchCV( + estimator=base_model, param_distributions=parameters, n_iter=5, scoring="neg_mean_squared_error", cv=4, verbose=0, + ) + + # Execute randomized search for optimal model hyperparameters. + model.fit(x_train, y_train, eval_set=eval_set, verbose=False) + security.model = model + + +class BracketRiskModel(RiskManagementModel): + + def __init__(self, drawdown_pct, profit_pct): + # Store drawdown and profit thresholds as percentage values. + self._drawdown_pct = -abs(drawdown_pct) + self._profit_pct = abs(profit_pct) + + def manage_risk(self, algorithm, targets): + # Build list of risk-adjusted targets. + adjusted_targets = [] + + # Iterate through all securities to evaluate positions. + for symbol, security in algorithm.securities.items(): + # Reset trailing high for non-invested securities. + if not security.invested: + security.trailing_high = None + # Take profit when unrealized gains exceed the threshold. + elif security.holdings.unrealized_profit_percent > self._profit_pct: + adjusted_targets.append(PortfolioTarget(symbol, 0)) + algorithm.insights.cancel([symbol]) + # Initialize trailing high from the entry price. + elif security.trailing_high is None: + security.trailing_high = security.holdings.average_price + # Update trailing high if a new high is reached. + elif security.trailing_high < security.high: + security.trailing_high = security.high + # Exit position when drawdown from trailing high exceeds limit. + elif (security.low / security.trailing_high) - 1 < self._drawdown_pct: + adjusted_targets.append(PortfolioTarget(symbol, 0)) + algorithm.insights.cancel([symbol]) + + return adjusted_targets + +