---
title: "Live Analysis"
description: "Learn how to analyze live trading results in the Research Environment."
canonical: https://www.quantconnect.com/docs/v2/research-environment/meta-analysis/live-analysis
markdown: https://www.quantconnect.com/docs/v2/research-environment/meta-analysis/live-analysis.md
language: python
source: https://github.com/QuantConnect/Documentation/tree/master/04%20Research%20Environment/10%20Meta%20Analysis/04%20Live%20Analysis
modified: 2026-10-30
index: https://www.quantconnect.com/llms.txt
---

# Live Analysis

[Documentation](https://www.quantconnect.com/docs/v2.md) > [Research Environment](https://www.quantconnect.com/docs/v2/research-environment.md) > Meta Analysis > Live Analysis

> Code examples are shown in Python. Append `?lang=csharp` to this URL for the C# version of this page.

## 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 `read_live_algorithm` method with the project Id and deployment ID.

```python
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](https://www.quantconnect.com/docs/v2/cloud-platform/projects/getting-started.md#13-Get-Project-Id) | [Get Deployment Id](https://www.quantconnect.com/docs/v2/cloud-platform/live-trading/getting-started.md#11-Get-Deployment-Id) |
| Local Platform | [Get Project Id](https://www.quantconnect.com/docs/v2/local-platform/projects/getting-started.md#14-Get-Project-Id) | [Get Deployment Id](https://www.quantconnect.com/docs/v2/local-platform/live-trading/getting-started.md#10-Get-Deployment-Id) |
| CLI | [Get Project Id](https://www.quantconnect.com/docs/v2/lean-cli/projects/project-management.md#07-Get-Project-Id) |  |

The `read_live_algorithm` method returns a `LiveAlgorithmResults` object, which have the following attributes:

*Members of `QuantConnect.Api.LiveAlgorithmResults` (JSON): https://www.quantconnect.com/services/inspector?type=T:QuantConnect.Api.LiveAlgorithmResults&language=python*

## Plot Order Fills

Follow these steps to plot the daily order fills of a live algorithm:

1. Get the live trading orders.
   ```python
   orders = api.read_live_orders(project_id).orders
   ```
   The following table provides links to documentation that explains how to get the project Id, depending on the platform you use:
   | Platform | Project Id |
   | --- | --- |
   | Cloud Platform | [Get Project Id](https://www.quantconnect.com/docs/v2/cloud-platform/projects/getting-started.md#13-Get-Project-Id) |
   | Local Platform | [Get Project Id](https://www.quantconnect.com/docs/v2/local-platform/projects/getting-started.md#14-Get-Project-Id) |
   | CLI | [Get Project Id](https://www.quantconnect.com/docs/v2/lean-cli/projects/project-management.md#07-Get-Project-Id) |
   The `read_live_orders` method returns an `OrdersResponseWrapper` object. Its `length` property is the total number of orders. Each call returns at most 100 orders. By default, you get the first 100 orders. To get the next ones, pass `start` and `end` arguments. Note that `end` - `start` must be 100 or less.
   ```python
   orders = api.read_live_orders(project_id, start=100, end=200).orders
   ```
   The `orders` property of the `OrdersResponseWrapper` is a list of `ApiOrderResponse` objects, which have the following properties:
   *Members of `QuantConnect.Orders.ApiOrderResponse` (JSON): https://www.quantconnect.com/services/inspector?type=T:QuantConnect.Orders.ApiOrderResponse&language=python*
2. Organize the trade times and prices for each security into a dictionary.

   ```python
   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)
   ```
3. Get the price history of each security you traded.

   ```python
   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)
   ```
4. Create a candlestick plot for each security and annotate each plot with buy and sell markers.

   ```python
   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()
   ```
   ![Plot of AAPL price with buy/sell markers](https://cdn.quantconnect.com/i/tu/plot-backtest-trades-in-research-env-aapl.png)
   ![Plot of SPY price with buy/sell markers](https://cdn.quantconnect.com/i/tu/plot-backtest-trades-in-research-env-spy.png)
   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 Charts

Follow these steps to plot the equity curve, benchmark, and drawdown of a live algorithm:

1. Define the project Id and read the "Strategy Equity", "Drawdown", and "Benchmark" charts.
   ```python
   from time import sleep, time

   project_id = 23034953

   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, 'Strategy Equity')
   drawdown_chart = read_chart(project_id, 'Drawdown')
   benchmark_chart = read_chart(project_id, 'Benchmark')
   ```
   The process to get your project Id depends on if you use the [Cloud Platform](https://www.quantconnect.com/docs/v2/cloud-platform/projects/getting-started.md#13-Get-Project-Id), [Local Platform](https://www.quantconnect.com/docs/v2/local-platform/projects/getting-started.md#14-Get-Project-Id), or [CLI](https://www.quantconnect.com/docs/v2/lean-cli/projects/project-management.md#07-Get-Project-Id).
2. Extract the series and create a `pandas.DataFrame`.
   ```python
   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)
   ```
3. Plot the performance chart.
   ```python
   # 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](https://cdn.quantconnect.com/i/tu/api-result-plot.png)

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. |
| Strategy Equity | Return | Time series of daily returns. Use this series for daily updates. |
| Strategy Equity | 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](https://www.quantconnect.com/docs/v2/writing-algorithms/key-concepts/glossary.md#08-capacity) snapshots |
| Drawdown | Equity Drawdown | Time series of equity peak-to-trough value |
| Benchmark | Benchmark | Time series of the [benchmark](https://www.quantconnect.com/docs/v2/writing-algorithms/initialization.md#11-Set-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 |
| Exposure | 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](https://www.quantconnect.com/docs/v2/cloud-platform/backtesting/results/portfolio-margin-plots.md). |
| 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](https://www.quantconnect.com/docs/v2/cloud-platform/live-trading/results.md#05-Asset-Plots). |
| Custom Chart | Custom Series | Time series of a `Series` in a [custom chart](https://www.quantconnect.com/docs/v2/writing-algorithms/charting.md) |

## Reconstruct Trades

When you stop a live algorithm, it loses its in-memory state. After you redeploy the project, the [trade builder](https://www.quantconnect.com/docs/v2/writing-algorithms/trading-and-orders/trade-statistics.md) starts empty, so its `closed_trades` property only contains the trades of the current deployment. Follow these steps to reconstruct the complete trade list across all the live deployments of a project in the Research Environment:

1. Define the project Id and get the deployments of the project.
   ```python
   project_id = 12345678

   # Request each status separately. Requests without a status filter can time out on accounts with many deployments.
   statuses = [
       AlgorithmStatus.RUNNING, AlgorithmStatus.STOPPED, AlgorithmStatus.LIQUIDATED, AlgorithmStatus.RUNTIME_ERROR
   ]
   deployments = sorted(
       [x for status in statuses for x in api.list_live_algorithms(status).algorithms if x.project_id == project_id],
       key=lambda x: x.launched
   )
   for deployment in deployments:
       print(f'{deployment.deploy_id}: {deployment.launched} - {deployment.stopped}')
   ```
   The following table provides links to documentation that explains how to get the project Id, depending on the platform you use:
   | Platform | Project Id |
   | --- | --- |
   | Cloud Platform | [Get Project Id](https://www.quantconnect.com/docs/v2/cloud-platform/projects/getting-started.md#13-Get-Project-Id) |
   | Local Platform | [Get Project Id](https://www.quantconnect.com/docs/v2/local-platform/projects/getting-started.md#14-Get-Project-Id) |
   | CLI | [Get Project Id](https://www.quantconnect.com/docs/v2/lean-cli/projects/project-management.md#07-Get-Project-Id) |
   The `list_live_algorithms` method returns a list of deployment summaries with the `deploy_id`, `launched`, and `stopped` times of each deployment. The reconstruction in the following steps doesn't need them, but each fill event carries its deployment Id, so you can use these summaries to relate the reconstructed trades to a specific deployment.
2. Get all the orders of the project.
   ```python
   from time import sleep

   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):
           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-index 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

   orders = read_all_orders(lambda s, e: api.read_live_orders(project_id, start=s, end=e).orders)
   print(f'Orders: {len(orders)}')
   ```
   The `read_live_orders` method returns the orders of every deployment of the project, concatenated in the order the deployments launched, so a single project-level loop retrieves the complete order history. Each call returns at most 100 orders, so paginate in 100-index windows until the endpoint returns an empty window, like the [Live Reconciliation](https://www.quantconnect.com/docs/v2/research-environment/meta-analysis/live-reconciliation.md#05-Plot-Order-Fills) tutorial does. Each element of the result is an `ApiOrderResponse` object that contains the order and its fill events.
3. Replay the fill events through a new trade builder.
   ```python
   # Use the same grouping and matching methods as the trade builder in your algorithm.
   trade_builder = TradeBuilder(FillGroupingMethod.FILL_TO_FILL, FillMatchingMethod.FIFO)

   # The orders are already in deployment-launch order, so replay them in list order.
   for response in orders:
       for event in [x for x in response.events if x.fill_quantity != 0]:
           fill = OrderEvent.from_serialized(event)
           trade_builder.process_fill(fill, 1, event.order_fee_amount if event.order_fee_amount else 0)
   ```
   Each fill event carries the Id of the deployment that produced it in its `algorithm_id` property, so you can group the fills by deployment.
4. Get the closed trades of the trade builder.
   ```python
   trades = trade_builder.closed_trades
   data = pd.DataFrame([{
       'symbol': trade.symbols[0].value,
       'direction': str(trade.direction),
       'quantity': trade.quantity,
       'entry_time': trade.entry_time,
       'entry_price': trade.entry_price,
       'exit_time': trade.exit_time,
       'exit_price': trade.exit_price,
       'profit_loss': trade.profit_loss,
       'total_fees': trade.total_fees
   } for trade in trades])
   data
   ```
   The `closed_trades` property returns a list of `Trade` objects, which have the following attributes:
   *Members of `QuantConnect.Statistics.Trade` (JSON): https://www.quantconnect.com/services/inspector?type=T:QuantConnect.Statistics.Trade&language=python*

The preceding replay passes a conversion rate of 1 and the raw fee amount of each event, so it assumes the security and its fees are denominated in your account currency and the contract multiplier is 1. Adjust these arguments if these assumptions don't hold for the products you trade. Order Ids restart at 1 for each deployment and the trade builder assigns an order fee only to the first fill of each order Id, so the total fees exclude the fees of reused order Ids in later deployments. Positions that stay open across a redeployment form complete trades only because the replay is continuous across all the deployments.

## Example

### Example 1: Read Live Algorithm Statistics

The following example reads the current running live algorithm's statistics in a jupyter notebook.

```python
# Instantiate QuantBook instance for researching.
qb = QuantBook()

# 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 live algorithm statistics.
print(live_algorithm.runtime_statistics)
```

### Example 2: Reconstruct Trades Across Deployments

The following example reconstructs the complete trade list across all the live deployments of the current project in a jupyter notebook.

```python
from time import sleep

# Instantiate a QuantBook instance to get the project Id.
qb = QuantBook()
project_id = qb.project_id

# Get the deployments of the project, sorted by launch time.
# Request each status separately, since unfiltered requests can time out on large accounts.
statuses = [
    AlgorithmStatus.RUNNING, AlgorithmStatus.STOPPED, AlgorithmStatus.LIQUIDATED, AlgorithmStatus.RUNTIME_ERROR
]
deployments = sorted(
    [x for status in statuses for x in api.list_live_algorithms(status).algorithms if x.project_id == project_id],
    key=lambda x: x.launched
)
print(f'Deployments: {len(deployments)}')

# Get the first window of orders. Retry while it is empty, since the orders may still be loading.
orders = []
for attempt in range(10):
    orders = api.read_live_orders(project_id, start=0, end=100).orders
    if orders:
        break
    sleep(10)

# Get the remaining orders. A single project-level loop spans every deployment.
start = 100
while True:
    window = api.read_live_orders(project_id, start=start, end=start + 100).orders
    if not window:
        break
    orders.extend(window)
    start += 100

# Replay the fill events through a trade builder to reconstruct the trades.
trade_builder = TradeBuilder(FillGroupingMethod.FILL_TO_FILL, FillMatchingMethod.FIFO)
for response in orders:
    for event in [x for x in response.events if x.fill_quantity != 0]:
        fee = event.order_fee_amount if event.order_fee_amount else 0
        trade_builder.process_fill(OrderEvent.from_serialized(event), 1, fee)

# Organize the closed trades of every deployment into a DataFrame.
trades = trade_builder.closed_trades
pd.DataFrame([{
    'symbol': trade.symbols[0].value,
    'direction': str(trade.direction),
    'quantity': trade.quantity,
    'entry_time': trade.entry_time,
    'entry_price': trade.entry_price,
    'exit_time': trade.exit_time,
    'exit_price': trade.exit_price,
    'profit_loss': trade.profit_loss,
    'total_fees': trade.total_fees
} for trade in trades])
```
