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

Live Trading

Commands

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.

Basic Usage

To receive and handle generic commands in your algorithm, define the on_commandOnCommand method. This method receives the data you want to inject into your algorithm and returns a boolean that represents if the command was successful.

public class BasicCommandAlgorithm : QCAlgorithm
{

    public override void Initialize() 
    {
        SetBenchmark(x => 1); // So the algorithm needs no asset data.
    }

    public override bool? OnCommand(dynamic data)
    {
        Log($"Got command at {Time} with data: {data}");
        return true;
    }
}
class BasicCommandAlgorithm(QCAlgorithm):

    def initialize(self):
        self.set_benchmark(lambda x: 1) # So the algorithm needs no asset data.

    def on_command(self, data):
        self.log(f'Got command at {self.time} with data: {data}')
        return True

To invoke the on_commandOnCommand event handler, send a payload (for example, {"ticker": "AAPL", "quantity": 1}) through the QuantConnect REST API or LEAN CLI. The keys of the payload become the members of the data object.

Encapsulate Event Handlers

To encapsulate the command handler logic, define isolated classes. Command classes should extend the Command class. Extensions of the Command class must implement a runRun method, which receives the payload you send and returns a boolean that represents if the command was successful. To add the command to your algorithm, call the add_commandAddCommand method.

public class EncapsulatedCommandAlgorithm : QCAlgorithm
{

    public override void Initialize()
    {
        AddCommand<MyCommand>();
        SetBenchmark(x => 1); // So the algorithm doesn’t need asset data.
    }

    public void DoSomething()
    {
        Log("Something was done!");
    }
}

public class MyCommand : Command
{
    public string Ticker { get; set; }
    public int Quantity { get; set; }
    public Dictionary<string, string> Parameters { get; set; }
    public override bool? Run(IAlgorithm algorithm)
    {
        ((QCAlgorithm)algorithm).Log($"ticker: {Ticker}; quantity: {Quantity}; parameters: {Parameters}");
        ((EncapsulatedAndLinkedCommandAlgorithm)algorithm).DoSomething();
        return true;
    }
}
class EncapsulatedCommandAlgorithm(QCAlgorithm):

    def initialize(self):
        MyCommand.ALGORITHM = self
        self.add_command(MyCommand)
        self.set_benchmark(lambda x: 1) # So the algorithm doesn’t need asset data.

    def do_something(self):
        self.log('Something was done!')

class MyCommand(Command):
    ALGORITHM = None
    ticker = None
    quantity = None
    parameters = {}

    def run(self, algorithm):
        algorithm.log(f"ticker: {self.ticker}; quantity: {self.quantity}; parameters: {self.parameters}")
        MyCommand.ALGORITHM.do_something()
        return True

To invoke the runRun method of the class you define, send a payload with a $type key (for example, {"ticker": "AAPL", "quantity": 1, "$type": "MyCommand"}). The value of the $type key must be the Command class you define. The other keys of the payload set the members of the class. If you don’t provide a $type key, LEAN runs the on_commandOnCommand method instead of the runRun method in your custom Command class.

To access your algorithm's attributes, you need to create a global static variable, and assign the reference to the global variable in the algorithm's initialize. Then, in the run merhod, you can acess your algorithm's attributes through the global.

Send Command Link

You can create the command in your algorithm and then send yourself an email notification with a link inside it. When you click the link, it then executes the event handler in your algorithm. To create the link, call the linkLink method. The following algorithm demonstrates how send command requests for the on_command event handler:

public class BasicLinkedCommandAlgorithm : QCAlgorithm
{
    public override void Initialize() 
    {
        SetBenchmark(x => 1); // So the algorithm needs no asset data.
        var link = Link(new {Ticker = "AAPL", Quantity = 1});
        Notify.Email("email@address.com", "Run Command?", $"Click here to run: {link}");
    }

    public override bool? OnCommand(dynamic data)
    {
        Log($"Got command at {Time} with data: {data}");
        return true;
    }
}
class BasicLinkedCommandAlgorithm(QCAlgorithm):

    def initialize(self):
        self.set_benchmark(lambda x: 1) # So the algorithm needs no asset data.
        link = self.link({"ticker": "AAPL", "quantity": 1})
        self.notify.email("email@address.com", "Run Command?", f"Click here to run: {link}")

    def on_command(self, data):
        self.log(f'Got command at {self.time} with data: {data}')
        return True

The following algorithm demonstrates how to send command requests for an event handler you encapsulate in a class:

public class EncapsulatedAndLinkedCommandAlgorithm : QCAlgorithm
{
    public override void Initialize()
    {
        AddCommand<MyCommand>();
        SetBenchmark(x => 1); // So the algorithm doesn’t need asset data.
        var potentialCommand = new MyCommand
        {
            Ticker = "AAPL",
            Quantity = 1,
            Parameters = new() { {"tag", "Signal X"} }
        };
        var link = Link(potentialCommand);
        Notify.Email("email@address.com", "Run Command?", $"Click here to run: {link}");
    }

}

public class MyCommand : Command
{
    public string Ticker { get; set; }
    public int Quantity { get; set; }
    public Dictionary<string, string> Parameters { get; set; }
    public override bool? Run(IAlgorithm algorithm)
    {
        ((QCAlgorithm)algorithm).Log($"ticker: {Ticker}; quantity: {Quantity}; parameters: {Parameters}");
        return true;
    }
}
class EncapsulatedAndLinkedCommandAlgorithm(QCAlgorithm):

    def initialize(self):
        self.add_command(MyCommand)
        self.set_benchmark(lambda x: 1) # So the algorithm doesn’t need asset data.
        potential_command = MyCommand()
        potential_command.ticker = 'AAPL'
        potential_command.quantity = 1
        potential_command.parameters = {"tag": "Signal X"}
        link = self.link(potential_command)
        self.notify.email("email@address.com", "Run Command?", f"Click here to run: {link}")

class MyCommand(Command):
    ticker = None
    quantity = None
    parameters = {}

    def run(self, algorithm):
        algorithm.log(f"ticker: {self.ticker}; quantity: {self.quantity}; parameters: {self.parameters}")
        return True

Send Commands by API

To send a command to your algorithm, send a payload through the QuantConnect REST API or LEAN CLI. LEAN converts the first character in each key of the payload to uppercase before injecting the data into your algorithm. The following script demonstrates how to send a command with the REST API. In this case, it calls the on_commandOnCommand method.

from base64 import b64encode
from hashlib import sha256
from time import time
from requests import get, post

# Edit these values:
USER_ID = 0
API_TOKEN = '_____'

BASE_URL = 'https://www.quantconnect.com/api/v2/'

def get_headers():
    # Get timestamp
    timestamp = f'{int(time())}'
    time_stamped_token = f'{API_TOKEN}:{timestamp}'.encode('utf-8')

    # Get hased API token
    hashed_token = sha256(time_stamped_token).hexdigest()
    authentication = f'{USER_ID}:{hashed_token}'.encode('utf-8')
    authentication = b64encode(authentication).decode('ascii')

    # Create headers dictionary.
    return {
        'Authorization': f'Basic {authentication}',
        'Timestamp': timestamp
    }

def create_command(project_id, command):
    return post(
        f'{BASE_URL}/live/commands/create', 
        headers=get_headers(), 
        json={"projectId": project_id, "command": command}
    ).json()

# Provide your project Id and command here:
print(create_command(<project_id>, {'ticker': 'AAPL', 'quantity': 1}))

To get your user Id and API token, see Request API Token.

Examples

The following examples demonstrate common practices for implementing live commands.

Example 1: Grey Box

The following algorithm trades a simple EMA cross strategy on SPY. However, instead of direct ordering by the algorithm as a "black box", it sends an email to ask for clicking a confirmation link before ordering, making each order decision an informed manual decision. The algorithm is called a "grey box".

public class LiveCommandAlgorithm : QCAlgorithm
{
    private Symbol _spy;
    private ExponentialMovingAverage _ema;

    public override void Initialize()
    {
        SetStartDate(2024, 8, 12);
        SetEndDate(2024, 9, 1);
        SetCash(1000000);

        // Request SPY data to trade.
        _spy = AddEquity("SPY").Symbol;
        // Create an EMA indicator to generate trade signals.
        _ema = EMA(_spy, 20, Resolution.Daily);
        // Warm-up indicator for immediate readiness.
        WarmUpIndicator(_spy, _ema, Resolution.Daily);
    }

    public override void OnData(Slice slice)
    {
        if (slice.Bars.TryGetValue(_spy, out var bar) && LiveMode)
        {
            // Trend-following strategy using price and EMA.
            // If the price is above EMA, SPY is in an uptrend, and we buy it.
            // We sent a link to our email address and await confirmation.
            string link;
            if (bar.Close > _ema && !Portfolio[_spy].IsLong)
            {
                link = Link(new {Ticker = "SPY", Size = 1});
                Notify.Email("email@address.com", "Trade Confirmation Needed", $"Click here to run: {link}");
            }
            else if (bar.Close < _ema && !Portfolio[_spy].IsShort)
            {
                link = Link(new {Ticker = "SPY", Size = -1});
                Notify.Email("email@address.com", "Trade Confirmation Needed", $"Click here to run: {link}");
            }
        }
    }

    public override bool? OnCommand(dynamic data)
    {
        // If we click the email link to confirm the trade, the algorithm will place the order.
        SetHoldings(data.Ticker, data.Size);
        return true;
    }
}
class LiveCommandAlgorithm(QCAlgorithm):
    def initialize(self) -> None:
        self.set_start_date(2024, 8, 12)
        self.set_end_date(2024, 9, 1)
        self.set_cash(1000000)

        # Request SPY data to trade.
        self.spy = self.add_equity("SPY").symbol
        # Create an EMA indicator to generate trade signals.
        self._ema = self.ema(self.spy, 20, Resolution.DAILY)
        # Warm-up indicator for immediate readiness.
        self.warm_up_indicator(self.spy, self._ema, Resolution.DAILY)

    def on_data(self, slice: Slice) -> None:
        bar = slice.bars.get(self.spy)
        if bar and self.live_mode:
            # Trend-following strategy using price and EMA.
            # If the price is above EMA, SPY is in an uptrend, and we buy it.
            # We sent a link to our email address and await confirmation.
            if bar.close > self._ema.current.value and not self.portfolio[self.spy].is_long:
                link = self.link({"ticker": "SPY", "size": 1})
                self.notify.email("email@address.com", "Trade Confirmation Needed", f"Click here to run: {link}")
            elif bar.close < self._ema.current.value and not self.portfolio[self.spy].is_short:
                link = self.link({"ticker": "SPY", "size": -1})
                self.notify.email("email@address.com", "Trade Confirmation Needed", f"Click here to run: {link}")

    def on_command(self, data: DynamicData) -> Optional[bool]:
        # The algorithm will place the order if we click the email link to confirm the trade.
        self.set_holdings(data["ticker"], data["size"])
        return True

Other Examples

For more examples, see the following algorithms:

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

Did you find this page helpful?

Contribute to the documentation: