Meta Analysis
Live Analysis
Read Live Results
To get the results of a live algorithm, call the ReadLiveAlgorithm
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.ReadLiveAlgorithm(project_id, deploy_id)
To get the project Id, open the project in the Algorithm Lab and check the URL. For example, the project Id of https://www.quantconnect.com/project/13946911 is 13946911.
To get the deployment Id, open a live result in the Algorithm Lab and check its log file. An example deployment Id is Lac54ffadf4ca52efabcd1ac29e4735cf. If you have deployed the project multiple times, the log file has multiple deployment Ids. In this case, use the most recent Id.
The ReadLiveAlgorithm
method returns a LiveAlgorithmResults
object, which have the following attributes:
Reconciliation
Reconciliation is a way to quantify the difference between an algorithm's live performance and its outofsample (OOS) performance (a backtest run over the live deployment period).
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.
Reconciliation is scored using two metrics: returns correlation and dynamic time warping (DTW) distance.
What is DTW Distance?
Dynamic Time Warp (DTW) Distance quantifies the difference between two timeseries. It is an algorithm that measures the shortest path between the points of two timeseries. It uses Euclidean distance as a measurement of pointtopoint distance and returns an overall measurement of the distance on the scale of the initial timeseries values. We apply DTW to the returns curve of the live and OOS performance, so the DTW distance measurement is on the scale of percent returns.
$$\begin{equation} DTW(X,Y) = min\bigg\{\sum_{l=1}^{L}\left(x_{m_l}  y_{n_l}\right)^{2}\in P^{N\times M}\bigg\} \end{equation}$$For the reasons outlined in our research notebook on the topic (linked below), QuantConnect annualizes the daily DTW. An annualized distance provides a user with a measurement of the annual difference in the magnitude of returns between the two curves. A perfect score is 0, meaning the returns for each day were precisely the same. A DTW score of 0 is nearly impossible to achieve, and we consider anything below 0.2 to be a decent score. A distance of 0.2 means the returns between an algorithm's live and OOS performance deviated by 20% over a year.
What is Returns Correlation?
Returns correlation is the simple Pearson correlation between the live and OOS returns. Correlation gives us a rudimentary understanding of how the returns move together. Do they trend up and down at the same time? Do they deviate in direction or timing?
$$\begin{equation} \rho_{XY} = \frac{cov(X, Y)}{\sigma_X\sigma_Y} \end{equation}$$An algorithm's returns correlation should be as close to 1 as possible. We consider a good score to be 0.8 or above, meaning that there is a strong positive correlation. This indicates that the returns move together most of the time and that for any given return you see from one of the curves, the other curve usually has a similar direction return (positive or negative).
Why Do We Need Both DTW and Returns Correlation?
Each measurement provides insight into distinct elements of timeseries similarity, but neither measurement alone gives us the whole picture. Returns correlation tells us whether or not the live and OOS returns move together, but it doesn't account for the possible differences in the magnitude of the returns. DTW distance measures the difference in magnitude of returns but provides no insight into whether or not the returns move in the same direction. It is possible for there to be two cases of equity curve similarity where both pairs have the same DTW distance, but one has perfectly negatively correlated returns, and the other has a perfectly positive correlation. Similarly, it is possible for two pairs of equity curves to each have perfect correlation but substantially different DTW distance. Having both measurements provides us with a more comprehensive understanding of the actual similarity between live and OOS performance. We outline several interesting cases and go into more depth on the topic of reconciliation in research we have published.
Plot Order Fills
Follow these steps to plot the daily order fills of a live algorithm:
 Get the live trading orders.

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 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.LastFillTime.date()) (order_data.buy_fill_prices if is_buy else order_data.sell_fill_prices).append(order.Price)

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(): start_date = min(start_date, min(order_data.buy_fill_times), min(order_data.sell_fill_times)) end_date = max(end_date, max(order_data.buy_fill_times), max(order_data.sell_fill_times)) start_date = timedelta(days=1) all_history = qb.History(list(order_data_by_symbol.keys()), start_date, end_date, Resolution.Daily)

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='triangleup', 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='triangledown', size=10), mode='markers', name='Sells', )) fig.show()
orders = api.ReadLiveOrders(project_id)
To get the project Id, open the project in the Algorithm Lab and check the URL. For example, the project Id of https://www.quantconnect.com/project/13946911 is 13946911.
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
method. Note that end
 start
must be less than 100.
orders = api.ReadLiveOrders(project_id, 100, 150)
The ReadLiveOrders
method returns a list of Order
objects, which have the following properties:
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.