Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<ol>
<li>Define the project Id.</li>
<div class="section-example-container">
<pre class="csharp">var projectId = 23034953;</pre>
<pre class="python">project_id = 23034953</pre>
</div>

Expand Down Expand Up @@ -33,6 +34,40 @@

<li>Read the live "Strategy Equity" chart with the <code class="csharp">ReadLiveChart</code><code class="python">read_live_chart</code> method. The first and last <code>Equity</code> points give you the start datetime, starting equity, and end datetime.</li>
<div class="section-example-container">
<pre class="csharp">var nowSec = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds();

Chart ReadLiveChartWithRetry(int projectId, string chartName)
{
for (var attempt = 0; attempt &lt; 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&lt;Candlestick&gt;()
.Where(v =&gt; 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}");</pre>
<pre class="python">from datetime import datetime
from time import sleep, time

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@

<li>Compile the project by calling the <code class="csharp">CreateCompile</code><code class="python">create_compile</code> method, then poll <code class="csharp">ReadCompile</code><code class="python">read_compile</code> until the compile state is <code>BuildSuccess</code>.</li>
<div class="section-example-container">
<pre class="csharp">var compilation = api.CreateCompile(projectId);
var compileId = compilation.CompileId;

// Poll until the build succeeds.
for (var attempt = 0; attempt &lt; 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);
}</pre>
<pre class="python">from time import sleep

compilation = api.create_compile(project_id)
Expand All @@ -23,13 +38,25 @@

<li>Create the OOS backtest with the <code class="csharp">CreateBacktest</code><code class="python">create_backtest</code> method.</li>
<div class="section-example-container">
<pre class="csharp">var backtest = api.CreateBacktest(projectId, compileId, "OOS Reconciliation");
var backtestId = backtest.BacktestId;
Console.WriteLine($"Backtest Id: {backtestId}");</pre>
<pre class="python">backtest = api.create_backtest(project_id, compile_id, 'OOS Reconciliation')
backtest_id = backtest.backtest_id
print(f"Backtest Id: {backtest_id}")</pre>
</div>

<li>Poll the <code class="csharp">ReadBacktest</code><code class="python">read_backtest</code> method until the <code>completed</code> flag is <code>True</code>. Log the <code>progress</code> attribute on each poll so you can watch the backtest advance.</li>
<div class="section-example-container">
<pre class="csharp">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.");</pre>
<pre class="python">completed = False
while not completed:
result = api.read_backtest(project_id, backtest_id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,25 @@
<ol>
<li>Read the live "Strategy Equity" chart using the retry helper from the previous step.</li>
<div class="section-example-container">
<pre class="csharp">var liveEquityChart = ReadLiveChartWithRetry(projectId, "Strategy Equity");</pre>
<pre class="python">live_equity_chart = read_chart(project_id, 'Strategy Equity')</pre>
</div>

<li>Read the backtest "Strategy Equity" chart by calling the <code class="csharp">ReadBacktestChart</code><code class="python">read_backtest_chart</code> method with the same retry pattern.</li>
<div class="section-example-container">
<pre class="csharp">Chart ReadBacktestChartWithRetry(int projectId, string backtestId, string chartName)
{
for (var attempt = 0; attempt &lt; 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");</pre>
<pre class="python">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)
Expand All @@ -20,8 +34,16 @@
backtest_equity_chart = read_backtest_chart(project_id, backtest_id, 'Strategy Equity')</pre>
</div>

<li>Extract the <code>Equity</code> series from each chart into a <code>pandas.Series</code> indexed by timestamp. Preserve every timestamp from both the live and backtest series and strip any timezone info so <code>pandas</code> and <code>plotly</code> can align and render the curves cleanly.</li>
<li>Extract the <code>Equity</code> series from each chart, filtering out points with a null close. Python uses a <code>pandas.Series</code> indexed by timestamp so the two curves can be aligned on the union of their timestamps; C# keeps two lists of <code>Candlestick</code> points and lets Plotly.NET align them on the same x-axis.</li>
<div class="section-example-container">
<pre class="csharp">var liveValues = liveEquityChart.Series["Equity"].Values
.OfType&lt;Candlestick&gt;()
.Where(v =&gt; v.Close.HasValue)
.ToList();
var backtestValues = backtestEquityChart.Series["Equity"].Values
.OfType&lt;Candlestick&gt;()
.Where(v =&gt; v.Close.HasValue)
.ToList();</pre>
<pre class="python">import pandas as pd

def to_naive(t):
Expand All @@ -42,8 +64,28 @@
df = pd.concat([live_series.rename('Live'), backtest_series.rename('OOS Backtest')], axis=1).sort_index().ffill()</pre>
</div>

<li>Plot both curves on a single <code>matplotlib</code> axis.</li>
<li>Plot both curves on the same axis. Python uses <code>matplotlib</code>; C# uses <a href='/docs/v2/research-environment/charting/plotly-net'>Plotly.NET</a> — load <code>Plotly.NET</code> and <code>Plotly.NET.Interactive</code> from NuGet and alias <code>Plotly.NET.Chart</code> to avoid ambiguity with <code>QuantConnect.Chart</code>.</li>
<div class="section-example-container">
<pre class="csharp">#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;

var equityChart = PlotlyChart.Combine(new[]
{
Chart2D.Chart.Line&lt;DateTime, decimal, string&gt;(
liveValues.Select(v =&gt; v.Time),
liveValues.Select(v =&gt; v.Close.Value),
Name: "Live"),
Chart2D.Chart.Line&lt;DateTime, decimal, string&gt;(
backtestValues.Select(v =&gt; v.Time),
backtestValues.Select(v =&gt; v.Close.Value),
Name: "OOS Backtest")
}).WithTitle("Live vs OOS Backtest Equity");

display(equityChart);</pre>
<pre class="python">import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(12, 6))
Expand All @@ -57,7 +99,7 @@
</div>
<img class='docs-image' src="https://cdn.quantconnect.com/i/tu/reconciliation-4.png" alt="Live vs OOS backtest equity curves">

<li>Score the reconciliation with the annualized returns DTW distance and the Pearson correlation of daily returns. Use <code>tslearn</code>'s <code>dtw</code> with a Sakoe-Chiba band so the algorithm runs in linear time.</li>
<li>Score the reconciliation with the annualized returns DTW distance and the Pearson correlation of daily returns. Use <code>tslearn</code>'s <code>dtw</code> with a Sakoe-Chiba band so the algorithm runs in linear time. The <code>tslearn</code> library is Python-only; run this step in a Python research notebook.</li>
<div class="section-example-container">
<pre class="python">from tslearn.metrics import dtw as DynamicTimeWarping

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
<p>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.</p>

<ol>
<li>Read the live and backtest orders. Both endpoints can take a few seconds to load the first time, so retry until the response reports success.</li>
<li>Read the live and backtest orders. Both endpoints can take a few seconds to load the first time, so retry until the response is non-empty.</li>
<div class="section-example-container">
<pre class="csharp">List&lt;ApiOrderResponse&gt; ReadOrders(Func&lt;List&lt;ApiOrderResponse&gt;&gt; fetch)
{
for (var attempt = 0; attempt &lt; 10; attempt++)
{
var result = fetch();
if (result.Any()) return result;
Console.WriteLine($"Orders loading... (attempt {attempt + 1}/10)");
Thread.Sleep(10000);
}
throw new Exception("Failed to read orders after 10 attempts");
}

var liveOrders = ReadOrders(() =&gt; api.ReadLiveOrders(projectId, 0, 100));
var backtestOrders = ReadOrders(() =&gt; api.ReadBacktestOrders(projectId, backtestId, 0, 100));</pre>
<pre class="python">from time import sleep

def read_orders(fetch):
Expand All @@ -21,6 +35,10 @@

<li>Organize the trade times and prices for each security into a dictionary for both the live and backtest fills.</li>
<div class="section-example-container">
<pre class="csharp">var liveBySymbol = liveOrders.Select(x =&gt; x.Order).GroupBy(o =&gt; o.Symbol);
var backtestBySymbol = backtestOrders.Select(x =&gt; x.Order)
.GroupBy(o =&gt; o.Symbol)
.ToDictionary(g =&gt; g.Key, g =&gt; g.ToList());</pre>
<pre class="python">import pandas as pd

def to_naive(t):
Expand Down Expand Up @@ -52,6 +70,34 @@ class OrderData:

<li>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.</li>
<div class="section-example-container">
<pre class="csharp">foreach (var liveGroup in liveBySymbol)
{
var symbol = liveGroup.Key;
var live = liveGroup.ToList();
var bt = backtestBySymbol.TryGetValue(symbol, out var btList) ? btList : new List&lt;Order&gt;();

var traces = new[]
{
Chart2D.Chart.Point&lt;DateTime, decimal, string&gt;(
live.Where(o =&gt; o.Quantity &gt; 0).Select(o =&gt; o.LastFillTime ?? o.Time),
live.Where(o =&gt; o.Quantity &gt; 0).Select(o =&gt; o.Price),
Name: "Live Buys"),
Chart2D.Chart.Point&lt;DateTime, decimal, string&gt;(
live.Where(o =&gt; o.Quantity &lt; 0).Select(o =&gt; o.LastFillTime ?? o.Time),
live.Where(o =&gt; o.Quantity &lt; 0).Select(o =&gt; o.Price),
Name: "Live Sells"),
Chart2D.Chart.Point&lt;DateTime, decimal, string&gt;(
bt.Where(o =&gt; o.Quantity &gt; 0).Select(o =&gt; o.LastFillTime ?? o.Time),
bt.Where(o =&gt; o.Quantity &gt; 0).Select(o =&gt; o.Price),
Name: "OOS Backtest Buys"),
Chart2D.Chart.Point&lt;DateTime, decimal, string&gt;(
bt.Where(o =&gt; o.Quantity &lt; 0).Select(o =&gt; o.LastFillTime ?? o.Time),
bt.Where(o =&gt; o.Quantity &lt; 0).Select(o =&gt; o.Price),
Name: "OOS Backtest Sells")
};
var fillsChart = PlotlyChart.Combine(traces).WithTitle($"{symbol} Live vs OOS Backtest Fills");
display(fillsChart);
}</pre>
<pre class="python">import plotly.graph_objects as go

symbols = set(live_by_symbol.keys()) | set(backtest_by_symbol.keys())
Expand Down
4 changes: 3 additions & 1 deletion Resources/live-trading/reconciliation/introduction.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@

<img class="img-responsive" src="https://cdn.quantconnect.com/i/tu/live-strategy-equity.png" alt="The live and OSS backtest equity curves of an Alpha">

<p>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.</p>
<p>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.</p>

<p>To generate the OOS reconciliation curve for a live deployment and overlay live vs. backtest equity and order fills from the Research Environment, see <a href="/docs/v2/research-environment/meta-analysis/live-reconciliation">Live Reconciliation</a>.</p>