From d34548c89c7100d9466cd82cdc5fbffabeeb3937 Mon Sep 17 00:00:00 2001
From: JKorf <jankorf91@gmail.com>
Date: Mon, 2 Dec 2024 21:45:48 +0100
Subject: [PATCH 1/2] Client order id update

---
 .../UnifiedApi/OKXRestClientUnifiedApi.cs     |  5 +--
 .../OKXRestClientUnifiedApiTrading.cs         | 40 ++++++++++++++-----
 .../UnifiedApi/OKXSocketClientUnifiedApi.cs   |  5 +--
 .../OKXSocketClientUnifiedApiTrading.cs       | 14 +++++--
 .../ServiceCollectionExtensions.cs            |  4 ++
 OKX.Net/OKX.Net.csproj                        |  6 ++-
 OKX.Net/OKX.Net.xml                           | 30 +++++++++++---
 OKX.Net/OKXExchange.cs                        |  3 ++
 OKX.Net/Objects/Account/OKXAccountBill.cs     |  1 +
 OKX.Net/Objects/Options/OKXOptions.cs         |  9 +++++
 OKX.Net/Objects/Options/OKXRestOptions.cs     | 11 +++--
 OKX.Net/Objects/Options/OKXSocketOptions.cs   | 11 +++--
 OKX.Net/Objects/Trade/OKXAlgoOrder.cs         |  2 +
 .../Trade/OKXAlgoOrderAmendResponse.cs        |  1 +
 OKX.Net/Objects/Trade/OKXAlgoOrderResponse.cs |  2 +
 .../Objects/Trade/OKXClosePositionResponse.cs |  1 +
 OKX.Net/Objects/Trade/OKXOrder.cs             |  5 ++-
 .../Objects/Trade/OKXOrderAmendResponse.cs    |  1 +
 .../Objects/Trade/OKXOrderCancelResponse.cs   |  1 +
 .../Objects/Trade/OKXOrderPlaceResponse.cs    |  1 +
 OKX.Net/Objects/Trade/OKXTransaction.cs       |  1 +
 21 files changed, 120 insertions(+), 34 deletions(-)

diff --git a/OKX.Net/Clients/UnifiedApi/OKXRestClientUnifiedApi.cs b/OKX.Net/Clients/UnifiedApi/OKXRestClientUnifiedApi.cs
index e453459..a24a00d 100644
--- a/OKX.Net/Clients/UnifiedApi/OKXRestClientUnifiedApi.cs
+++ b/OKX.Net/Clients/UnifiedApi/OKXRestClientUnifiedApi.cs
@@ -13,8 +13,9 @@ namespace OKX.Net.Clients.UnifiedApi;
 internal partial class OKXRestClientUnifiedApi : RestApiClient, IOKXRestClientUnifiedApi, ISpotClient
 {
     #region Internal Fields
+    public new OKXRestOptions ClientOptions => (OKXRestOptions)base.ClientOptions;
+
     private static TimeSyncState _timeSyncState = new("Unified Api");
-    internal readonly string _ref;
 
     public event Action<OrderId>? OnOrderPlaced;
     public event Action<OrderId>? OnOrderCanceled;
@@ -38,8 +39,6 @@ internal OKXRestClientUnifiedApi(ILogger logger, HttpClient? httpClient, OKXRest
         Trading = new OKXRestClientUnifiedApiTrading(this);
         SubAccounts = new OKXRestClientUnifiedApiSubAccounts(this);
 
-        _ref = !string.IsNullOrEmpty(options.BrokerId) ? options.BrokerId! : "1425d83a94fbBCDE";
-
         if (options.Environment.Name == TradeEnvironmentNames.Testnet)
         {
             StandardRequestHeaders = new Dictionary<string, string>
diff --git a/OKX.Net/Clients/UnifiedApi/OKXRestClientUnifiedApiTrading.cs b/OKX.Net/Clients/UnifiedApi/OKXRestClientUnifiedApiTrading.cs
index 0c67f27..d407173 100644
--- a/OKX.Net/Clients/UnifiedApi/OKXRestClientUnifiedApiTrading.cs
+++ b/OKX.Net/Clients/UnifiedApi/OKXRestClientUnifiedApiTrading.cs
@@ -42,12 +42,12 @@ public virtual async Task<WebCallResult<OKXOrderPlaceResponse>> PlaceOrderAsync(
 
         CancellationToken ct = default)
     {
-        clientOrderId ??= ExchangeHelpers.AppendRandomString(_baseClient._ref, 32);
+        clientOrderId = LibraryHelpers.ApplyBrokerId(clientOrderId, OKXExchange.ClientOrderId, 32, _baseClient.ClientOptions.AllowAppendingClientOrderId);
 
         var parameters = new ParameterCollection {
             {"instId", symbol },
             {"sz", quantity.ToString(CultureInfo.InvariantCulture) },
-            {"tag", _baseClient._ref },
+            {"tag", OKXExchange.ClientOrderId },
             {"clOrdId",  clientOrderId },
         };
         parameters.AddEnum("tdMode", tradeMode ?? Enums.TradeMode.Cash);
@@ -61,6 +61,11 @@ public virtual async Task<WebCallResult<OKXOrderPlaceResponse>> PlaceOrderAsync(
         parameters.AddOptional("stpId", selfTradePreventionId);
         parameters.AddOptionalEnum("stpMode", selfTradePreventionMode);
 
+        if (attachedAlgoOrders != null)
+        {
+            foreach(var attachOrder in attachedAlgoOrders)
+                attachOrder.ClientOrderId = LibraryHelpers.ApplyBrokerId(attachOrder.ClientOrderId, OKXExchange.ClientOrderId, 32, _baseClient.ClientOptions.AllowAppendingClientOrderId);
+        }
         parameters.AddOptional("attachAlgoOrds", attachedAlgoOrders?.ToArray());
 
         parameters.AddOptional("tag", tag);
@@ -118,8 +123,7 @@ public virtual async Task<WebCallResult<OKXCheckOrderResponse>> CheckOrderAsync(
     {
         var parameters = new ParameterCollection {
             {"instId", symbol },
-            {"sz", quantity.ToString(CultureInfo.InvariantCulture) },
-            {"tag", _baseClient._ref },
+            {"sz", quantity.ToString(CultureInfo.InvariantCulture) }
         };
         parameters.AddEnum("tdMode", tradeMode ?? Enums.TradeMode.Cash);
         parameters.AddEnum("side", side);
@@ -148,8 +152,8 @@ public virtual async Task<WebCallResult<IEnumerable<OKXOrderPlaceResponse>>> Pla
     {
         foreach (var order in orders)
         {
-            var clientOrderId = order.ClientOrderId ?? ExchangeHelpers.AppendRandomString(_baseClient._ref, 32);
-            order.Tag = _baseClient._ref;
+            var clientOrderId = LibraryHelpers.ApplyBrokerId(order.ClientOrderId, OKXExchange.ClientOrderId, 32, _baseClient.ClientOptions.AllowAppendingClientOrderId);
+            order.Tag = OKXExchange.ClientOrderId;
             order.ClientOrderId = clientOrderId;
         }
 
@@ -186,6 +190,9 @@ public virtual async Task<WebCallResult<IEnumerable<OKXOrderPlaceResponse>>> Pla
     /// <inheritdoc />
     public virtual async Task<WebCallResult<OKXOrderCancelResponse>> CancelOrderAsync(string symbol, long? orderId = null, string? clientOrderId = null, CancellationToken ct = default)
     {
+        if (clientOrderId != null)
+            clientOrderId = LibraryHelpers.ApplyBrokerId(clientOrderId, OKXExchange.ClientOrderId, 32, _baseClient.ClientOptions.AllowAppendingClientOrderId);
+
         var parameters = new ParameterCollection {
             {"instId", symbol },
         };
@@ -265,6 +272,9 @@ public virtual async Task<WebCallResult<OKXOrderAmendResponse>> AmendOrderAsync(
         TriggerPriceType? newStopLossPriceTriggerType = null,
         CancellationToken ct = default)
     {
+        if (clientOrderId != null)
+            clientOrderId = LibraryHelpers.ApplyBrokerId(clientOrderId, OKXExchange.ClientOrderId, 32, _baseClient.ClientOptions.AllowAppendingClientOrderId);
+
         var parameters = new ParameterCollection
         {
             { "instId", symbol },
@@ -310,11 +320,11 @@ public virtual async Task<WebCallResult<OKXClosePositionResponse>> ClosePosition
         string? clientOrderId = null,
         CancellationToken ct = default)
     {
-        clientOrderId ??= ExchangeHelpers.AppendRandomString(_baseClient._ref, 32);
+        clientOrderId = LibraryHelpers.ApplyBrokerId(clientOrderId, OKXExchange.ClientOrderId, 32, _baseClient.ClientOptions.AllowAppendingClientOrderId);
 
         var parameters = new ParameterCollection {
             {"instId", symbol },
-            {"tag", _baseClient._ref },
+            {"tag", OKXExchange.ClientOrderId },
             {"clOrdId", clientOrderId }
         };
         parameters.AddEnum("mgnMode", marginMode);
@@ -334,6 +344,9 @@ public virtual async Task<WebCallResult<OKXOrder>> GetOrderDetailsAsync(
         string? clientOrderId = null,
         CancellationToken ct = default)
     {
+        if (clientOrderId != null)
+            clientOrderId = LibraryHelpers.ApplyBrokerId(clientOrderId, OKXExchange.ClientOrderId, 32, _baseClient.ClientOptions.AllowAppendingClientOrderId);
+
         var parameters = new ParameterCollection {
             {"instId", symbol },
         };
@@ -568,10 +581,11 @@ public virtual async Task<WebCallResult<OKXAlgoOrderResponse>> PlaceAlgoOrderAsy
 
         CancellationToken ct = default)
     {
-        clientOrderId ??= ExchangeHelpers.AppendRandomString(_baseClient._ref, 32);
+        clientOrderId = LibraryHelpers.ApplyBrokerId(clientOrderId, OKXExchange.ClientOrderId, 32, _baseClient.ClientOptions.AllowAppendingClientOrderId);
+
         var parameters = new ParameterCollection {
             {"instId", symbol },
-            {"tag", _baseClient._ref },
+            {"tag", OKXExchange.ClientOrderId },
             {"clOrdId", clientOrderId }
         };
         parameters.AddEnum("tdMode", tradeMode);
@@ -718,6 +732,9 @@ public virtual async Task<WebCallResult<OKXAlgoOrder>> GetAlgoOrderAsync(string?
         if ((algoId == null) == (clientAlgoId == null))
             throw new ArgumentException("Either algoId or clientAlgoId needs to be provided");
 
+        if (clientAlgoId != null)
+            clientAlgoId = LibraryHelpers.ApplyBrokerId(clientAlgoId, OKXExchange.ClientOrderId, 32, _baseClient.ClientOptions.AllowAppendingClientOrderId);
+
         var parameters = new ParameterCollection();
         parameters.AddOptional("algoId", algoId);
         parameters.AddOptional("algoClOrdId", clientAlgoId);
@@ -746,6 +763,9 @@ public virtual async Task<WebCallResult<OKXAlgoOrderAmendResponse>> AmendAlgoOrd
         if ((algoId == null) == (clientAlgoId == null))
             throw new ArgumentException("Either algoId or clientAlgoId needs to be provided");
 
+        if (clientAlgoId != null)
+            clientAlgoId = LibraryHelpers.ApplyBrokerId(clientAlgoId, OKXExchange.ClientOrderId, 32, _baseClient.ClientOptions.AllowAppendingClientOrderId);
+
         var parameters = new ParameterCollection
         {
             { "instId", symbol },
diff --git a/OKX.Net/Clients/UnifiedApi/OKXSocketClientUnifiedApi.cs b/OKX.Net/Clients/UnifiedApi/OKXSocketClientUnifiedApi.cs
index 12b7f58..c58a798 100644
--- a/OKX.Net/Clients/UnifiedApi/OKXSocketClientUnifiedApi.cs
+++ b/OKX.Net/Clients/UnifiedApi/OKXSocketClientUnifiedApi.cs
@@ -24,6 +24,8 @@ internal partial class OKXSocketClientUnifiedApi : SocketApiClient, IOKXSocketCl
     private static readonly MessagePath _instTypePath = MessagePath.Get().Property("arg").Property("instType");
     private static readonly MessagePath _instFamilyPath = MessagePath.Get().Property("arg").Property("instFamily");
 
+    public new OKXSocketOptions ClientOptions => (OKXSocketOptions)base.ClientOptions;
+
     /// <inheritdoc />
     public IOKXSocketClientUnifiedApiAccount Account { get; }
     /// <inheritdoc />
@@ -31,7 +33,6 @@ internal partial class OKXSocketClientUnifiedApi : SocketApiClient, IOKXSocketCl
     /// <inheritdoc />
     public IOKXSocketClientUnifiedApiTrading Trading { get; }
 
-    internal readonly string _ref;
     private readonly bool _demoTrading;
 
     #region ctor
@@ -43,8 +44,6 @@ internal OKXSocketClientUnifiedApi(ILogger logger, OKXSocketOptions options) :
         ExchangeData = new OKXSocketClientUnifiedApiExchangeData(logger, this);
         Trading = new OKXSocketClientUnifiedApiTrading(logger, this);
 
-        _ref = !string.IsNullOrEmpty(options.BrokerId) ? options.BrokerId! : "078ee129065aBCDE";
-
         _demoTrading = options.Environment.Name == TradeEnvironmentNames.Testnet;
 
         RegisterPeriodicQuery("Ping", TimeSpan.FromSeconds(20), x => new OKXPingQuery(), null);
diff --git a/OKX.Net/Clients/UnifiedApi/OKXSocketClientUnifiedApiTrading.cs b/OKX.Net/Clients/UnifiedApi/OKXSocketClientUnifiedApiTrading.cs
index 7d29053..b91727a 100644
--- a/OKX.Net/Clients/UnifiedApi/OKXSocketClientUnifiedApiTrading.cs
+++ b/OKX.Net/Clients/UnifiedApi/OKXSocketClientUnifiedApiTrading.cs
@@ -51,11 +51,11 @@ public async Task<CallResult<OKXOrderPlaceResponse>> PlaceOrderAsync(string symb
             { "sz", quantity.ToString(CultureInfo.InvariantCulture) },
         };
 
-        clientOrderId = clientOrderId ?? ExchangeHelpers.AppendRandomString(_client._ref, 32);
+        clientOrderId = LibraryHelpers.ApplyBrokerId(clientOrderId, OKXExchange.ClientOrderId, 32, _client.ClientOptions.AllowAppendingClientOrderId);
 
         parameters.AddOptionalParameter("ccy", asset);
         parameters.AddOptionalParameter("clOrdId", clientOrderId);
-        parameters.AddOptionalParameter("tag", _client._ref);
+        parameters.AddOptionalParameter("tag", OKXExchange.ClientOrderId);
         parameters.AddOptionalParameter("posSide", EnumConverter.GetString(positionSide));
         parameters.AddOptionalParameter("px", price?.ToString(CultureInfo.InvariantCulture));
         parameters.AddOptionalParameter("reduceOnly", reduceOnly);
@@ -79,8 +79,8 @@ public async Task<CallResult<IEnumerable<OKXOrderPlaceResponse>>> PlaceMultipleO
     {
         foreach (var order in orders)
         {
-            order.Tag = _client._ref;
-            order.ClientOrderId = order.ClientOrderId ?? ExchangeHelpers.AppendRandomString(_client._ref, 32);
+            order.Tag = OKXExchange.ClientOrderId;
+            order.ClientOrderId = LibraryHelpers.ApplyBrokerId(order.ClientOrderId, OKXExchange.ClientOrderId, 32, _client.ClientOptions.AllowAppendingClientOrderId);
         }
 
         return await _client.QueryInternalAsync<OKXOrderPlaceResponse>(_client.GetUri("/ws/v5/private"), "batch-orders", orders, true, 1, ct).ConfigureAwait(false);
@@ -89,6 +89,9 @@ public async Task<CallResult<IEnumerable<OKXOrderPlaceResponse>>> PlaceMultipleO
     /// <inheritdoc />
     public async Task<CallResult<OKXOrderCancelResponse>> CancelOrderAsync(string symbol, string? orderId = null, string? clientOrderId = null, CancellationToken ct = default)
     {
+        if (clientOrderId != null)
+            clientOrderId = LibraryHelpers.ApplyBrokerId(clientOrderId, OKXExchange.ClientOrderId, 32, _client.ClientOptions.AllowAppendingClientOrderId);
+
         var parameters = new Dictionary<string, object>()
         {
             { "instId", symbol }
@@ -123,6 +126,9 @@ public virtual async Task<CallResult<OKXOrderAmendResponse>> AmendOrderAsync(
         decimal? newPrice = null, 
         CancellationToken ct = default)
     {
+        if (clientOrderId != null)
+            clientOrderId = LibraryHelpers.ApplyBrokerId(clientOrderId, OKXExchange.ClientOrderId, 32, _client.ClientOptions.AllowAppendingClientOrderId);
+
         var parameters = new Dictionary<string, object>
         {
             { "instId", symbol },
diff --git a/OKX.Net/ExtensionMethods/ServiceCollectionExtensions.cs b/OKX.Net/ExtensionMethods/ServiceCollectionExtensions.cs
index 7872f5e..b5c6c77 100644
--- a/OKX.Net/ExtensionMethods/ServiceCollectionExtensions.cs
+++ b/OKX.Net/ExtensionMethods/ServiceCollectionExtensions.cs
@@ -40,8 +40,10 @@ public static IServiceCollection AddOKX(
             var socketEnvName = options.Socket.Environment?.Name ?? options.Environment?.Name ?? OKXEnvironment.Live.Name;
             options.Rest.Environment = OKXEnvironment.GetEnvironmentByName(restEnvName) ?? options.Rest.Environment!;
             options.Rest.ApiCredentials = options.Rest.ApiCredentials ?? options.ApiCredentials;
+            options.Rest.AllowAppendingClientOrderId = options.Rest.AllowAppendingClientOrderId || options.AllowAppendingClientOrderId;
             options.Socket.Environment = OKXEnvironment.GetEnvironmentByName(socketEnvName) ?? options.Socket.Environment!;
             options.Socket.ApiCredentials = options.Socket.ApiCredentials ?? options.ApiCredentials;
+            options.Socket.AllowAppendingClientOrderId = options.Socket.AllowAppendingClientOrderId || options.AllowAppendingClientOrderId;
 
 
             services.AddSingleton(x => Options.Options.Create(options.Rest));
@@ -70,8 +72,10 @@ public static IServiceCollection AddOKX(
 
             options.Rest.Environment = options.Rest.Environment ?? options.Environment ?? OKXEnvironment.Live;
             options.Rest.ApiCredentials = options.Rest.ApiCredentials ?? options.ApiCredentials;
+            options.Rest.AllowAppendingClientOrderId = options.Rest.AllowAppendingClientOrderId || options.AllowAppendingClientOrderId;
             options.Socket.Environment = options.Socket.Environment ?? options.Environment ?? OKXEnvironment.Live;
             options.Socket.ApiCredentials = options.Socket.ApiCredentials ?? options.ApiCredentials;
+            options.Socket.AllowAppendingClientOrderId = options.Socket.AllowAppendingClientOrderId || options.AllowAppendingClientOrderId;
 
             services.AddSingleton(x => Options.Options.Create(options.Rest));
             services.AddSingleton(x => Options.Options.Create(options.Socket));
diff --git a/OKX.Net/OKX.Net.csproj b/OKX.Net/OKX.Net.csproj
index a2cbb26..f961fbd 100644
--- a/OKX.Net/OKX.Net.csproj
+++ b/OKX.Net/OKX.Net.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
     <LangVersion>latest</LangVersion>
@@ -49,10 +49,12 @@
       <PrivateAssets>all</PrivateAssets>
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
     </PackageReference>
-    <PackageReference Include="CryptoExchange.Net" Version="8.4.0" />
     <PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0">
       <PrivateAssets>all</PrivateAssets>
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
     </PackageReference>
   </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\CryptoExchange.Net\CryptoExchange.Net\CryptoExchange.Net.csproj" />
+  </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/OKX.Net/OKX.Net.xml b/OKX.Net/OKX.Net.xml
index b428984..e443bfe 100644
--- a/OKX.Net/OKX.Net.xml
+++ b/OKX.Net/OKX.Net.xml
@@ -6312,6 +6312,16 @@
             OKX options
             </summary>
         </member>
+        <member name="P:OKX.Net.Objects.Options.OKXOptions.AllowAppendingClientOrderId">
+            <summary>
+            Whether to allow the client to adjust the clientOrderId parameter send by the user when placing orders to include a client reference. This reference is used by the exchange to allocate a small percentage of the paid trading fees to developer of this library. Defaults to false.<br />
+            Note that:<br />
+            * It does not impact the amount of fees a user pays in any way<br />
+            * It does not impact functionality. The reference is added just before sending the request and removed again during data deserialization<br />
+            * It does respect client order id field limitations. For example if the user provided client order id parameter is too long to fit the reference it will not be added<br />
+            * Toggling this option might fail operations using a clientOrderId parameter for pre-existing orders which were placed before the toggle. Operations on orders placed after the toggle will work as expected. It's adviced to toggle when there are no open orders
+            </summary>
+        </member>
         <member name="T:OKX.Net.Objects.Options.OKXOrderBookOptions">
             <summary>
             Order book options
@@ -6352,9 +6362,14 @@
             Whether or not to sign public requests
             </summary>
         </member>
-        <member name="P:OKX.Net.Objects.Options.OKXRestOptions.BrokerId">
+        <member name="P:OKX.Net.Objects.Options.OKXRestOptions.AllowAppendingClientOrderId">
             <summary>
-            Broker ID for earning rebates
+            Whether to allow the client to adjust the clientOrderId parameter send by the user when placing orders to include a client reference. This reference is used by the exchange to allocate a small percentage of the paid trading fees to developer of this library. Defaults to false.<br />
+            Note that:<br />
+            * It does not impact the amount of fees a user pays in any way<br />
+            * It does not impact functionality. The reference is added just before sending the request and removed again during data deserialization<br />
+            * It does respect client order id field limitations. For example if the user provided client order id parameter is too long to fit the reference it will not be added<br />
+            * Toggling this option might fail operations using a clientOrderId parameter for pre-existing orders which were placed before the toggle. Operations on orders placed after the toggle will work as expected. It's adviced to toggle when there are no open orders
             </summary>
         </member>
         <member name="P:OKX.Net.Objects.Options.OKXRestOptions.UnifiedOptions">
@@ -6377,9 +6392,14 @@
             ctor
             </summary>
         </member>
-        <member name="P:OKX.Net.Objects.Options.OKXSocketOptions.BrokerId">
+        <member name="P:OKX.Net.Objects.Options.OKXSocketOptions.AllowAppendingClientOrderId">
             <summary>
-            Broker ID for earning rebates
+            Whether to allow the client to adjust the clientOrderId parameter send by the user when placing orders to include a client reference. This reference is used by the exchange to allocate a small percentage of the paid trading fees to developer of this library. Defaults to false.<br />
+            Note that:<br />
+            * It does not impact the amount of fees a user pays in any way<br />
+            * It does not impact functionality. The reference is added just before sending the request and removed again during data deserialization<br />
+            * It does respect client order id field limitations. For example if the user provided client order id parameter is too long to fit the reference it will not be added<br />
+            * Toggling this option might fail operations using a clientOrderId parameter for pre-existing orders which were placed before the toggle. Operations on orders placed after the toggle will work as expected. It's adviced to toggle when there are no open orders
             </summary>
         </member>
         <member name="P:OKX.Net.Objects.Options.OKXSocketOptions.UnifiedOptions">
@@ -8419,7 +8439,7 @@
         </member>
         <member name="P:OKX.Net.Objects.Trade.OKXOrder.AttachAlgoCllientOrderId">
             <summary>
-            Client-supplied Algo ID when plaing order attaching TP/SL.
+            Client-supplied Algo ID when placing order attaching TP/SL.
             </summary>
         </member>
         <member name="P:OKX.Net.Objects.Trade.OKXOrder.TakeProfitTriggerPriceType">
diff --git a/OKX.Net/OKXExchange.cs b/OKX.Net/OKXExchange.cs
index 350c40c..474152e 100644
--- a/OKX.Net/OKXExchange.cs
+++ b/OKX.Net/OKXExchange.cs
@@ -36,6 +36,9 @@ public static class OKXExchange
             "https://www.okx.com/docs-v5/en/"
             };
 
+        internal const string ClientOrderId = "1425d83a94fbBCDE";
+        internal const string ClientOrderIdPrefix = ClientOrderId + LibraryHelpers.ClientOrderIdSeperator;
+
         /// <summary>
         /// Format a base and quote asset to an OKX recognized symbol 
         /// </summary>
diff --git a/OKX.Net/Objects/Account/OKXAccountBill.cs b/OKX.Net/Objects/Account/OKXAccountBill.cs
index 07195c7..d8e1fbc 100644
--- a/OKX.Net/Objects/Account/OKXAccountBill.cs
+++ b/OKX.Net/Objects/Account/OKXAccountBill.cs
@@ -137,6 +137,7 @@ public record OKXAccountBill
     /// Client order id
     /// </summary>
     [JsonPropertyName("clOrdId")]
+    [JsonConverterCtor<ReplaceConverter>($"{OKXExchange.ClientOrderIdPrefix}->")]
     public string? ClientOrderId { get; set; }
 
     /// <summary>
diff --git a/OKX.Net/Objects/Options/OKXOptions.cs b/OKX.Net/Objects/Options/OKXOptions.cs
index 824affc..9ae4004 100644
--- a/OKX.Net/Objects/Options/OKXOptions.cs
+++ b/OKX.Net/Objects/Options/OKXOptions.cs
@@ -10,4 +10,13 @@ namespace OKX.Net.Objects.Options;
 /// </summary>
 public class OKXOptions : LibraryOptions<OKXRestOptions, OKXSocketOptions, OKXApiCredentials, OKXEnvironment>
 {
+    /// <summary>
+    /// Whether to allow the client to adjust the clientOrderId parameter send by the user when placing orders to include a client reference. This reference is used by the exchange to allocate a small percentage of the paid trading fees to developer of this library. Defaults to false.<br />
+    /// Note that:<br />
+    /// * It does not impact the amount of fees a user pays in any way<br />
+    /// * It does not impact functionality. The reference is added just before sending the request and removed again during data deserialization<br />
+    /// * It does respect client order id field limitations. For example if the user provided client order id parameter is too long to fit the reference it will not be added<br />
+    /// * Toggling this option might fail operations using a clientOrderId parameter for pre-existing orders which were placed before the toggle. Operations on orders placed after the toggle will work as expected. It's adviced to toggle when there are no open orders
+    /// </summary>
+    public bool AllowAppendingClientOrderId { get; set; } = false;
 }
diff --git a/OKX.Net/Objects/Options/OKXRestOptions.cs b/OKX.Net/Objects/Options/OKXRestOptions.cs
index 01e56a4..2c762f3 100644
--- a/OKX.Net/Objects/Options/OKXRestOptions.cs
+++ b/OKX.Net/Objects/Options/OKXRestOptions.cs
@@ -29,9 +29,14 @@ public OKXRestOptions()
     public bool SignPublicRequests { get; set; }
 
     /// <summary>
-    /// Broker ID for earning rebates
+    /// Whether to allow the client to adjust the clientOrderId parameter send by the user when placing orders to include a client reference. This reference is used by the exchange to allocate a small percentage of the paid trading fees to developer of this library. Defaults to false.<br />
+    /// Note that:<br />
+    /// * It does not impact the amount of fees a user pays in any way<br />
+    /// * It does not impact functionality. The reference is added just before sending the request and removed again during data deserialization<br />
+    /// * It does respect client order id field limitations. For example if the user provided client order id parameter is too long to fit the reference it will not be added<br />
+    /// * Toggling this option might fail operations using a clientOrderId parameter for pre-existing orders which were placed before the toggle. Operations on orders placed after the toggle will work as expected. It's adviced to toggle when there are no open orders
     /// </summary>
-    public string? BrokerId { get; set; }
+    public bool AllowAppendingClientOrderId { get; set; } = false;
 
     /// <summary>
     /// Options for the  unified API
@@ -42,7 +47,7 @@ internal OKXRestOptions Set(OKXRestOptions targetOptions)
     {
         targetOptions = base.Set<OKXRestOptions>(targetOptions);
         targetOptions.SignPublicRequests = SignPublicRequests;
-        targetOptions.BrokerId = BrokerId;
+        targetOptions.AllowAppendingClientOrderId = AllowAppendingClientOrderId;
         targetOptions.UnifiedOptions = UnifiedOptions.Set(targetOptions.UnifiedOptions);
         return targetOptions;
     }
diff --git a/OKX.Net/Objects/Options/OKXSocketOptions.cs b/OKX.Net/Objects/Options/OKXSocketOptions.cs
index c709c56..b162ba0 100644
--- a/OKX.Net/Objects/Options/OKXSocketOptions.cs
+++ b/OKX.Net/Objects/Options/OKXSocketOptions.cs
@@ -25,9 +25,14 @@ public OKXSocketOptions()
     }
 
     /// <summary>
-    /// Broker ID for earning rebates
+    /// Whether to allow the client to adjust the clientOrderId parameter send by the user when placing orders to include a client reference. This reference is used by the exchange to allocate a small percentage of the paid trading fees to developer of this library. Defaults to false.<br />
+    /// Note that:<br />
+    /// * It does not impact the amount of fees a user pays in any way<br />
+    /// * It does not impact functionality. The reference is added just before sending the request and removed again during data deserialization<br />
+    /// * It does respect client order id field limitations. For example if the user provided client order id parameter is too long to fit the reference it will not be added<br />
+    /// * Toggling this option might fail operations using a clientOrderId parameter for pre-existing orders which were placed before the toggle. Operations on orders placed after the toggle will work as expected. It's adviced to toggle when there are no open orders
     /// </summary>
-    public string? BrokerId { get; set; }
+    public bool AllowAppendingClientOrderId { get; set; } = false;
 
     /// <summary>
     /// Options for the Unified API
@@ -37,7 +42,7 @@ public OKXSocketOptions()
     internal OKXSocketOptions Set(OKXSocketOptions targetOptions)
     {
         targetOptions = base.Set<OKXSocketOptions>(targetOptions);
-        targetOptions.BrokerId = BrokerId;
+        targetOptions.AllowAppendingClientOrderId = AllowAppendingClientOrderId;
         targetOptions.UnifiedOptions = UnifiedOptions.Set(targetOptions.UnifiedOptions);
         return targetOptions;
     }
diff --git a/OKX.Net/Objects/Trade/OKXAlgoOrder.cs b/OKX.Net/Objects/Trade/OKXAlgoOrder.cs
index e077583..9e4e3b2 100644
--- a/OKX.Net/Objects/Trade/OKXAlgoOrder.cs
+++ b/OKX.Net/Objects/Trade/OKXAlgoOrder.cs
@@ -41,6 +41,7 @@ public record OKXAlgoOrder
     /// Client order id
     /// </summary>
     [JsonPropertyName("clOrdId")]
+    [JsonConverterCtor<ReplaceConverter>($"{OKXExchange.ClientOrderIdPrefix}->")]
     public string? ClientOrderId { get; set; }
 
     /// <summary>
@@ -278,6 +279,7 @@ public record OKXAlgoOrder
     /// Client algo order id
     /// </summary>
     [JsonPropertyName("algoClOrdId")]
+    [JsonConverterCtor<ReplaceConverter>($"{OKXExchange.ClientOrderIdPrefix}->")]
     public string? AlgoClientOrderId { get; set; }
 
     /// <summary>
diff --git a/OKX.Net/Objects/Trade/OKXAlgoOrderAmendResponse.cs b/OKX.Net/Objects/Trade/OKXAlgoOrderAmendResponse.cs
index 8b6b468..f1e6547 100644
--- a/OKX.Net/Objects/Trade/OKXAlgoOrderAmendResponse.cs
+++ b/OKX.Net/Objects/Trade/OKXAlgoOrderAmendResponse.cs
@@ -15,6 +15,7 @@ public record OKXAlgoOrderAmendResponse
     /// Client order id
     /// </summary>
     [JsonPropertyName("algoClOrdId")]
+    [JsonConverterCtor<ReplaceConverter>($"{OKXExchange.ClientOrderIdPrefix}->")]
     public string? ClientOrderId { get; set; }
 
     /// <summary>
diff --git a/OKX.Net/Objects/Trade/OKXAlgoOrderResponse.cs b/OKX.Net/Objects/Trade/OKXAlgoOrderResponse.cs
index e3a4c4c..0621ac9 100644
--- a/OKX.Net/Objects/Trade/OKXAlgoOrderResponse.cs
+++ b/OKX.Net/Objects/Trade/OKXAlgoOrderResponse.cs
@@ -15,12 +15,14 @@ public record OKXAlgoOrderResponse
     /// Client order id
     /// </summary>
     [JsonPropertyName("clOrdId")]
+    [JsonConverterCtor<ReplaceConverter>($"{OKXExchange.ClientOrderIdPrefix}->")]
     public string? ClientOrderId { get; set; }
 
     /// <summary>
     /// Algo client order id
     /// </summary>
     [JsonPropertyName("algoClOrdId")]
+    [JsonConverterCtor<ReplaceConverter>($"{OKXExchange.ClientOrderIdPrefix}->")]
     public string? AgloClientOrderId { get; set; }
 
     /// <summary>
diff --git a/OKX.Net/Objects/Trade/OKXClosePositionResponse.cs b/OKX.Net/Objects/Trade/OKXClosePositionResponse.cs
index 4058e89..82ee8b1 100644
--- a/OKX.Net/Objects/Trade/OKXClosePositionResponse.cs
+++ b/OKX.Net/Objects/Trade/OKXClosePositionResponse.cs
@@ -23,6 +23,7 @@ public record OKXClosePositionResponse
     /// Client order id
     /// </summary>
     [JsonPropertyName("clOrdId")]
+    [JsonConverterCtor<ReplaceConverter>($"{OKXExchange.ClientOrderIdPrefix}->")]
     public string? ClientOrderId { get; set; }
 
     /// <summary>
diff --git a/OKX.Net/Objects/Trade/OKXOrder.cs b/OKX.Net/Objects/Trade/OKXOrder.cs
index e84112d..3e69ed3 100644
--- a/OKX.Net/Objects/Trade/OKXOrder.cs
+++ b/OKX.Net/Objects/Trade/OKXOrder.cs
@@ -53,6 +53,7 @@ public record OKXOrder
     /// Client order id
     /// </summary>
     [JsonPropertyName("clOrdId")]
+    [JsonConverterCtor<ReplaceConverter>($"{OKXExchange.ClientOrderIdPrefix}->")]
     public string? ClientOrderId { get; set; }
 
     /// <summary>
@@ -212,9 +213,10 @@ public record OKXOrder
     public decimal? Leverage { get; set; }
 
     /// <summary>
-    /// Client-supplied Algo ID when plaing order attaching TP/SL.
+    /// Client-supplied Algo ID when placing order attaching TP/SL.
     /// </summary>
     [JsonPropertyName("attachAlgoClOrdId")]
+    [JsonConverterCtor<ReplaceConverter>($"{OKXExchange.ClientOrderIdPrefix}->")]
     public string? AttachAlgoCllientOrderId { get; set; }
 
     /// <summary>
@@ -266,6 +268,7 @@ public record OKXOrder
     /// Client algo order id
     /// </summary>
     [JsonPropertyName("algoClOrdId")]
+    [JsonConverterCtor<ReplaceConverter>($"{OKXExchange.ClientOrderIdPrefix}->")]
     public string? AlgoClientOrderId { get; set; }
 
     /// <summary>
diff --git a/OKX.Net/Objects/Trade/OKXOrderAmendResponse.cs b/OKX.Net/Objects/Trade/OKXOrderAmendResponse.cs
index 0083b18..f4a0787 100644
--- a/OKX.Net/Objects/Trade/OKXOrderAmendResponse.cs
+++ b/OKX.Net/Objects/Trade/OKXOrderAmendResponse.cs
@@ -15,6 +15,7 @@ public record OKXOrderAmendResponse
     /// Client order id
     /// </summary>
     [JsonPropertyName("clOrdId")]
+    [JsonConverterCtor<ReplaceConverter>($"{OKXExchange.ClientOrderIdPrefix}->")]
     public string? ClientOrderId { get; set; }
 
     /// <summary>
diff --git a/OKX.Net/Objects/Trade/OKXOrderCancelResponse.cs b/OKX.Net/Objects/Trade/OKXOrderCancelResponse.cs
index 6bdb287..5a0c003 100644
--- a/OKX.Net/Objects/Trade/OKXOrderCancelResponse.cs
+++ b/OKX.Net/Objects/Trade/OKXOrderCancelResponse.cs
@@ -15,6 +15,7 @@ public record OKXOrderCancelResponse
     /// Client order id
     /// </summary>
     [JsonPropertyName("clOrdId")]
+    [JsonConverterCtor<ReplaceConverter>($"{OKXExchange.ClientOrderIdPrefix}->")]
     public string ClientOrderId { get; set; } = string.Empty;
 
     /// <summary>
diff --git a/OKX.Net/Objects/Trade/OKXOrderPlaceResponse.cs b/OKX.Net/Objects/Trade/OKXOrderPlaceResponse.cs
index 77da634..e1de35b 100644
--- a/OKX.Net/Objects/Trade/OKXOrderPlaceResponse.cs
+++ b/OKX.Net/Objects/Trade/OKXOrderPlaceResponse.cs
@@ -15,6 +15,7 @@ public record OKXOrderPlaceResponse
     /// Client order id
     /// </summary>
     [JsonPropertyName("clOrdId")]
+    [JsonConverterCtor<ReplaceConverter>($"{OKXExchange.ClientOrderIdPrefix}->")]
     public string? ClientOrderId { get; set; }
 
     /// <summary>
diff --git a/OKX.Net/Objects/Trade/OKXTransaction.cs b/OKX.Net/Objects/Trade/OKXTransaction.cs
index 99216e6..dc5248a 100644
--- a/OKX.Net/Objects/Trade/OKXTransaction.cs
+++ b/OKX.Net/Objects/Trade/OKXTransaction.cs
@@ -35,6 +35,7 @@ public record OKXTransaction
     /// Client order id
     /// </summary>
     [JsonPropertyName("clOrdId")]
+    [JsonConverterCtor<ReplaceConverter>($"{OKXExchange.ClientOrderIdPrefix}->")]
     public string? ClientOrderId { get; set; }
 
     /// <summary>

From 824311f96d10305f1e73cb269a6ecf785aff3559 Mon Sep 17 00:00:00 2001
From: Jkorf <jankorf91@gmail.com>
Date: Tue, 3 Dec 2024 09:13:32 +0100
Subject: [PATCH 2/2] Update OKX.Net.csproj

---
 OKX.Net/OKX.Net.csproj | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/OKX.Net/OKX.Net.csproj b/OKX.Net/OKX.Net.csproj
index f961fbd..5254b4d 100644
--- a/OKX.Net/OKX.Net.csproj
+++ b/OKX.Net/OKX.Net.csproj
@@ -49,12 +49,10 @@
       <PrivateAssets>all</PrivateAssets>
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
     </PackageReference>
+    <PackageReference Include="CryptoExchange.Net" Version="8.4.2" />
     <PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0">
       <PrivateAssets>all</PrivateAssets>
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
     </PackageReference>
   </ItemGroup>
-  <ItemGroup>
-    <ProjectReference Include="..\..\CryptoExchange.Net\CryptoExchange.Net\CryptoExchange.Net.csproj" />
-  </ItemGroup>
 </Project>
\ No newline at end of file