Skip to content

Commit

Permalink
Improve live trading streaming packets
Browse files Browse the repository at this point in the history
  • Loading branch information
Martin-Molinero committed Feb 11, 2025
1 parent 9be02d7 commit 13bcb6b
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 32 deletions.
25 changes: 25 additions & 0 deletions Common/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1309,6 +1309,31 @@ public static decimal SmartRounding(this decimal input)
return input.RoundToSignificantDigits(7).Normalize();
}

/// <summary>
/// Provides global smart rounding to a shorter version
/// </summary>
public static decimal SmartRoundingShort(this decimal input)
{
input = Normalize(input);
if (input <= 1)
{
// 0.99 > input
return input;
}
else if (input <= 10)
{
// 1.01 to 9.99
return Math.Round(input, 2);
}
else if (input <= 100)
{
// 99.9 to 10.1
return Math.Round(input, 1);
}
// 100 to inf
return Math.Truncate(input);
}

/// <summary>
/// Casts the specified input value to a decimal while acknowledging the overflow conditions
/// </summary>
Expand Down
76 changes: 70 additions & 6 deletions Common/Global.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

using System;
using Newtonsoft.Json;
using System.ComponentModel;
using QuantConnect.Securities;
using Newtonsoft.Json.Converters;
using System.Runtime.Serialization;
Expand Down Expand Up @@ -63,6 +64,13 @@ public static class DateFormat
[JsonObject]
public class Holding
{
private decimal? _conversionRate;
private decimal _marketValue;
private decimal _unrealizedPnl;
private decimal _unrealizedPnLPercent;

private decimal _quantity;

/// Symbol of the Holding:
[JsonProperty(PropertyName = "symbol")]
public Symbol Symbol { get; set; } = Symbol.Empty;
Expand All @@ -72,7 +80,8 @@ public class Holding
public SecurityType Type => Symbol.SecurityType;

/// The currency symbol of the holding, such as $
[JsonProperty(PropertyName = "currencySymbol")]
[DefaultValue("$")]
[JsonProperty(PropertyName = "currencySymbol", DefaultValueHandling = DefaultValueHandling.Ignore)]
public string CurrencySymbol { get; set; }

/// Average Price of our Holding in the currency the symbol is traded in
Expand All @@ -81,27 +90,82 @@ public class Holding

/// Quantity of Symbol We Hold.
[JsonProperty(PropertyName = "quantity", DefaultValueHandling = DefaultValueHandling.Ignore)]
public decimal Quantity { get; set; }
public decimal Quantity
{
get
{
return _quantity;
}
set
{
// we remove any trailing zeros
_quantity = value.Normalize();
}
}

/// Current Market Price of the Asset in the currency the symbol is traded in
[JsonProperty(PropertyName = "marketPrice", DefaultValueHandling = DefaultValueHandling.Ignore)]
public decimal MarketPrice { get; set; }

/// Current market conversion rate into the account currency
[JsonProperty(PropertyName = "conversionRate", DefaultValueHandling = DefaultValueHandling.Ignore)]
public decimal? ConversionRate { get; set; }
public decimal? ConversionRate
{
get
{
return _conversionRate;
}
set
{
if (value != 1)
{
_conversionRate = value;
}
}
}


/// Current market value of the holding
[JsonProperty(PropertyName = "marketValue", DefaultValueHandling = DefaultValueHandling.Ignore)]
public decimal MarketValue { get; set; }
public decimal MarketValue
{
get
{
return _marketValue;
}
set
{
_marketValue = value.SmartRoundingShort();
}
}

/// Current unrealized P/L of the holding
[JsonProperty(PropertyName = "unrealizedPnl", DefaultValueHandling = DefaultValueHandling.Ignore)]
public decimal UnrealizedPnL { get; set; }
public decimal UnrealizedPnL
{
get
{
return _unrealizedPnl;
}
set
{
_unrealizedPnl = value.SmartRoundingShort();
}
}

/// Current unrealized P/L % of the holding
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public decimal UnrealizedPnLPercent { get; set; }
public decimal UnrealizedPnLPercent
{
get
{
return _unrealizedPnLPercent;
}
set
{
_unrealizedPnLPercent = value.SmartRoundingShort();
}
}

/// Create a new default holding:
public Holding()
Expand Down
21 changes: 0 additions & 21 deletions Common/Packets/LiveResultPacket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,31 +39,16 @@ public class LiveResultPacket : Packet
/// </summary>
public int ProjectId { get; set; }

/// <summary>
/// User session Id who issued the result packet
/// </summary>
public string SessionId { get; set; } = string.Empty;

/// <summary>
/// Live Algorithm Id (DeployId) for this result packet
/// </summary>
public string DeployId { get; set; } = string.Empty;

/// <summary>
/// Compile Id algorithm which generated this result packet
/// </summary>
public string CompileId { get; set; } = string.Empty;

/// <summary>
/// Result data object for this result packet
/// </summary>
public LiveResult Results { get; set; } = new LiveResult();

/// <summary>
/// Processing time / running time for the live algorithm.
/// </summary>
public double ProcessingTime { get; set; }

/// <summary>
/// Default constructor for JSON Serialization
/// </summary>
Expand All @@ -80,15 +65,12 @@ public LiveResultPacket(string json)
try
{
var packet = JsonConvert.DeserializeObject<LiveResultPacket>(json);
CompileId = packet.CompileId;
Channel = packet.Channel;
SessionId = packet.SessionId;
DeployId = packet.DeployId;
Type = packet.Type;
UserId = packet.UserId;
ProjectId = packet.ProjectId;
Results = packet.Results;
ProcessingTime = packet.ProcessingTime;
}
catch (Exception err)
{
Expand All @@ -106,13 +88,10 @@ public LiveResultPacket(LiveNodePacket job, LiveResult results)
{
try
{
SessionId = job.SessionId;
CompileId = job.CompileId;
DeployId = job.DeployId;
Results = results;
UserId = job.UserId;
ProjectId = job.ProjectId;
SessionId = job.SessionId;
Channel = job.Channel;
}
catch (Exception err) {
Expand Down
7 changes: 2 additions & 5 deletions Engine/Results/LiveTradingResultHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,6 @@ private void Update()
Log.Debug("LiveTradingResultHandler.Update(): End build delta charts");

//Profit loss changes, get the banner statistics, summary information on the performance for the headers.
var deltaStatistics = new Dictionary<string, string>();
var serverStatistics = GetServerStatistics(utcNow);
var holdings = GetHoldings(Algorithm.Securities.Values, Algorithm.SubscriptionManager.SubscriptionDataConfigService);

Expand All @@ -232,7 +231,7 @@ private void Update()

// since we're sending multiple packets, let's do it async and forget about it
// chart data can get big so let's break them up into groups
var splitPackets = SplitPackets(deltaCharts, deltaOrders, holdings, Algorithm.Portfolio.CashBook, deltaStatistics, runtimeStatistics, serverStatistics, deltaOrderEvents);
var splitPackets = SplitPackets(deltaCharts, deltaOrders, holdings, Algorithm.Portfolio.CashBook, runtimeStatistics, serverStatistics, deltaOrderEvents);

foreach (var liveResultPacket in splitPackets)
{
Expand All @@ -256,6 +255,7 @@ private void Update()

var orderEvents = GetOrderEventsToStore();

var deltaStatistics = new Dictionary<string, string>();
var orders = new Dictionary<int, Order>(TransactionHandler.Orders);
var complete = new LiveResultPacket(_job, new LiveResult(new LiveResultParameters(chartComplete, orders, Algorithm.Transactions.TransactionRecord, holdings, Algorithm.Portfolio.CashBook, deltaStatistics, runtimeStatistics, orderEvents, serverStatistics, state: GetAlgorithmState())));
StoreResult(complete);
Expand Down Expand Up @@ -469,7 +469,6 @@ private IEnumerable<LiveResultPacket> SplitPackets(Dictionary<string, Chart> del
Dictionary<int, Order> deltaOrders,
Dictionary<string, Holding> holdings,
CashBook cashbook,
Dictionary<string, string> deltaStatistics,
SortedDictionary<string, string> runtimeStatistics,
Dictionary<string, string> serverStatistics,
List<OrderEvent> deltaOrderEvents)
Expand Down Expand Up @@ -514,7 +513,6 @@ private IEnumerable<LiveResultPacket> SplitPackets(Dictionary<string, Chart> del
new LiveResultPacket(_job, new LiveResult { Holdings = holdings, CashBook = cashbook}),
new LiveResultPacket(_job, new LiveResult
{
Statistics = deltaStatistics,
RuntimeStatistics = runtimeStatistics,
ServerStatistics = serverStatistics
})
Expand Down Expand Up @@ -824,7 +822,6 @@ protected void SendFinalResult()
result = LiveResultPacket.CreateEmpty(_job);
result.Results.State = endState;
}
result.ProcessingTime = (endTime - StartTime).TotalSeconds;

StoreInsights();

Expand Down
18 changes: 18 additions & 0 deletions Tests/Common/HoldingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

using System;
using NUnit.Framework;
using Newtonsoft.Json;
using QuantConnect.Securities;
using QuantConnect.Data.Market;
using QuantConnect.Tests.Engine.DataFeeds;
Expand Down Expand Up @@ -66,5 +67,22 @@ public void PriceRounding(SecurityType securityType)
Assert.AreEqual(10.0000m, holding.AveragePrice);
}
}

[Test]
public void Serialization()
{
var algo = new AlgorithmStub();
var security = algo.AddEquity("SPY");
security.SetMarketPrice(new Tick(new DateTime(2022, 01, 04), security.Symbol, 10.0001m, 10.0001m));
security.Holdings.SetHoldings(10.0000000000m, 10);

var holding = new Holding(security);

var result = JsonConvert.SerializeObject(holding);

Assert.AreEqual("{\"symbol\":{\"value\":\"SPY\",\"id\":\"SPY R735QTJ8XC9X\",\"permtick\":\"SPY\"},\"type\":1," +
"\"averagePrice\":10.00,\"quantity\":10.0,\"marketPrice\":10.00,\"marketValue\":100.0," +
"\"unrealizedPnl\":-1.0,\"UnrealizedPnLPercent\":-1.0}", result);
}
}
}
10 changes: 10 additions & 0 deletions Tests/Common/Util/ExtensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1114,6 +1114,16 @@ public void NormalizeDecimalReturnsNoTrailingZeros(decimal input, string expecte
Assert.AreEqual(expectedOutput, output.ToStringInvariant());
}

[TestCase(0.072842, "0.072842")]
[TestCase(7.5819999, "7.58")]
[TestCase(54.1119999, "54.1")]
[TestCase(1152280.01234568423, "1152280")]
public void SmartRoundingShort(decimal input, string expectedOutput)
{
var output = input.SmartRoundingShort().ToStringInvariant();
Assert.AreEqual(expectedOutput, output);
}

[Test]
[TestCase(0.072842, 3, "0.0728")]
[TestCase(0.0019999, 2, "0.0020")]
Expand Down

0 comments on commit 13bcb6b

Please sign in to comment.