@@ -52,7 +52,8 @@ public class OandaRestApiV20 : OandaRestApiBase
5252
5353 private TransactionStreamSession _eventsSession ;
5454 private PricingStreamSession _ratesSession ;
55- private readonly Dictionary < string , ( Symbol Symbol , DateTimeZone ExchangeTimeZone ) > _symbolsByInstrument = new Dictionary < string , ( Symbol , DateTimeZone ) > ( ) ;
55+ private readonly Dictionary < string , ( Symbol Symbol , DateTimeZone ExchangeTimeZone ) > _symbolsByInstrument = new ( ) ;
56+ private BrokerageConcurrentMessageHandler < string > _messageHandler ;
5657
5758 /// <summary>
5859 /// Initializes a new instance of the <see cref="OandaRestApiV20"/> class.
@@ -65,7 +66,8 @@ public class OandaRestApiV20 : OandaRestApiBase
6566 /// <param name="accessToken">The Oanda access token (can be the user's personal access token or the access token obtained with OAuth by QC on behalf of the user)</param>
6667 /// <param name="accountId">The account identifier.</param>
6768 /// <param name="agent">The Oanda agent string</param>
68- public OandaRestApiV20 ( OandaSymbolMapper symbolMapper , IOrderProvider orderProvider , ISecurityProvider securityProvider , IDataAggregator aggregator , Environment environment , string accessToken , string accountId , string agent )
69+ /// <param name="concurrencyEnabled">Enables or disables concurrent processing of messages</param>
70+ public OandaRestApiV20 ( OandaSymbolMapper symbolMapper , IOrderProvider orderProvider , ISecurityProvider securityProvider , IDataAggregator aggregator , Environment environment , string accessToken , string accountId , string agent , bool concurrencyEnabled )
6971 : base ( symbolMapper , orderProvider , securityProvider , aggregator , environment , accessToken , accountId , agent )
7072 {
7173 var basePathRest = environment == Environment . Trade ?
@@ -76,6 +78,7 @@ public OandaRestApiV20(OandaSymbolMapper symbolMapper, IOrderProvider orderProvi
7678 "https://stream-fxtrade.oanda.com/v3" :
7779 "https://stream-fxpractice.oanda.com/v3" ;
7880
81+ _messageHandler = new BrokerageConcurrentMessageHandler < string > ( OnTransactionDataReceived , concurrencyEnabled ) ;
7982 _apiRest = new DefaultApi ( basePathRest ) ;
8083 _apiRest . Configuration . AddDefaultHeader ( OandaAgentKey , Agent ) ;
8184
@@ -135,11 +138,11 @@ public override List<CashAmount> GetCashBalance()
135138 {
136139 var response = _apiRest . GetAccountSummary ( Authorization , AccountId ) ;
137140
138- return new List < CashAmount >
139- {
141+ return
142+ [
140143 new CashAmount ( response . Account . Balance . ToDecimal ( ) ,
141144 response . Account . Currency )
142- } ;
145+ ] ;
143146 }
144147
145148 /// <summary>
@@ -172,8 +175,8 @@ public override bool PlaceOrder(Order order)
172175 var marketOrderStatus = OrderStatus . Filled ;
173176 var request = GenerateOrderRequest ( order ) ;
174177
175- lock ( Locker )
176- {
178+ var submitted = true ;
179+ _messageHandler . WithLockedStream ( ( ) => {
177180 var response = _apiRest . CreateOrder ( Authorization , AccountId , request ) ;
178181 order . BrokerId . Add ( response . Data . OrderCreateTransaction . Id ) ;
179182
@@ -184,7 +187,8 @@ public override bool PlaceOrder(Order order)
184187 if ( response . Data . OrderCancelTransaction != null && response . Data . OrderCancelTransaction . Type == OrderCancelTransaction . TypeEnum . ORDERCANCEL )
185188 {
186189 OnOrderEvent ( new OrderEvent ( order , DateTime . UtcNow , orderFee , response . Data . OrderCancelTransaction . Reason . ToString ( ) ) { Status = OrderStatus . Invalid } ) ;
187- return false ;
190+ submitted = false ;
191+ return ;
188192 }
189193
190194 var fill = response . Data . OrderFillTransaction ;
@@ -213,7 +217,7 @@ public override bool PlaceOrder(Order order)
213217 PendingFilledMarketOrders [ order . Id ] = marketOrderStatus ;
214218 }
215219 }
216- }
220+ } ) ;
217221 OnOrderEvent ( new OrderEvent ( order , DateTime . UtcNow , orderFee ) { Status = OrderStatus . Submitted } ) ;
218222
219223 // If 'marketOrderRemainingQuantity < order.AbsoluteQuantity' is false it means the order was not even PartiallyFilled, wait for callback
@@ -227,7 +231,7 @@ public override bool PlaceOrder(Order order)
227231 } ) ;
228232 }
229233
230- return true ;
234+ return submitted ;
231235 }
232236
233237 /// <summary>
@@ -247,25 +251,27 @@ public override bool UpdateOrder(Order order)
247251 }
248252
249253 var request = GenerateOrderRequest ( order ) ;
254+ _messageHandler . WithLockedStream ( ( ) => {
250255
251- var orderId = order . BrokerId . First ( ) ;
252- var response = _apiRest . ReplaceOrder ( Authorization , AccountId , orderId , request ) ;
256+ var orderId = order . BrokerId . First ( ) ;
257+ var response = _apiRest . ReplaceOrder ( Authorization , AccountId , orderId , request ) ;
253258
254- // replace the brokerage order id
255- order . BrokerId [ 0 ] = response . Data . OrderCreateTransaction . Id ;
259+ // replace the brokerage order id
260+ order . BrokerId [ 0 ] = response . Data . OrderCreateTransaction . Id ;
256261
257- OnOrderEvent ( new OrderEvent ( order , DateTime . UtcNow , OrderFee . Zero ) { Status = OrderStatus . UpdateSubmitted } ) ;
262+ OnOrderEvent ( new OrderEvent ( order , DateTime . UtcNow , OrderFee . Zero ) { Status = OrderStatus . UpdateSubmitted } ) ;
258263
259- // check if the updated (marketable) order was filled
260- if ( response . Data . OrderFillTransaction != null )
261- {
262- OnOrderEvent ( new OrderEvent ( order , DateTime . UtcNow , OrderFee . Zero , "Oanda Fill Event" )
264+ // check if the updated (marketable) order was filled
265+ if ( response . Data . OrderFillTransaction != null )
263266 {
264- Status = OrderStatus . Filled ,
265- FillPrice = response . Data . OrderFillTransaction . Price . ToDecimal ( ) ,
266- FillQuantity = response . Data . OrderFillTransaction . Units . ConvertInvariant < decimal > ( )
267- } ) ;
268- }
267+ OnOrderEvent ( new OrderEvent ( order , DateTime . UtcNow , OrderFee . Zero , "Oanda Fill Event" )
268+ {
269+ Status = OrderStatus . Filled ,
270+ FillPrice = response . Data . OrderFillTransaction . Price . ToDecimal ( ) ,
271+ FillQuantity = response . Data . OrderFillTransaction . Units . ConvertInvariant < decimal > ( )
272+ } ) ;
273+ }
274+ } ) ;
269275
270276 return true ;
271277 }
@@ -285,14 +291,16 @@ public override bool CancelOrder(Order order)
285291 return false ;
286292 }
287293
288- foreach ( var orderId in order . BrokerId )
289- {
290- _apiRest . CancelOrder ( Authorization , AccountId , orderId ) ;
291- OnOrderEvent ( new OrderEvent ( order ,
292- DateTime . UtcNow ,
293- OrderFee . Zero ,
294- "Oanda Cancel Order Event" ) { Status = OrderStatus . Canceled } ) ;
295- }
294+ _messageHandler . WithLockedStream ( ( ) => {
295+ foreach ( var orderId in order . BrokerId )
296+ {
297+ _apiRest . CancelOrder ( Authorization , AccountId , orderId ) ;
298+ OnOrderEvent ( new OrderEvent ( order ,
299+ DateTime . UtcNow ,
300+ OrderFee . Zero ,
301+ "Oanda Cancel Order Event" ) { Status = OrderStatus . Canceled } ) ;
302+ }
303+ } ) ;
296304
297305 return true ;
298306 }
@@ -303,7 +311,7 @@ public override bool CancelOrder(Order order)
303311 public override void StartTransactionStream ( )
304312 {
305313 _eventsSession = new TransactionStreamSession ( this ) ;
306- _eventsSession . DataReceived += OnTransactionDataReceived ;
314+ _eventsSession . DataReceived += OnDataReceived ;
307315 _eventsSession . StartSession ( ) ;
308316 }
309317
@@ -314,7 +322,7 @@ public override void StopTransactionStream()
314322 {
315323 if ( _eventsSession != null )
316324 {
317- _eventsSession . DataReceived -= OnTransactionDataReceived ;
325+ _eventsSession . DataReceived -= OnDataReceived ;
318326 _eventsSession . StopSession ( ) ;
319327 }
320328 }
@@ -351,6 +359,15 @@ private static DateTime GetTickDateTimeFromString(string time)
351359 return OandaBrokerage . GetDateTimeFromString ( time . Remove ( 25 , 3 ) ) ;
352360 }
353361
362+ /// <summary>
363+ /// Handles the data received from the transaction stream session.
364+ /// </summary>
365+ /// <param name="data">The raw data string received from the transaction stream.</param>
366+ private void OnDataReceived ( string data )
367+ {
368+ _messageHandler . HandleNewMessage ( data ) ;
369+ }
370+
354371 /// <summary>
355372 /// Event handler for streaming events
356373 /// </summary>
@@ -369,11 +386,7 @@ private void OnTransactionDataReceived(string json)
369386 case "ORDER_FILL" :
370387 var transaction = obj . ToObject < OrderFillTransaction > ( ) ;
371388
372- Order order ;
373- lock ( Locker )
374- {
375- order = OrderProvider . GetOrdersByBrokerageId ( transaction . OrderID ) ? . SingleOrDefault ( ) ;
376- }
389+ Order order = OrderProvider . GetOrdersByBrokerageId ( transaction . OrderID ) ? . SingleOrDefault ( ) ;
377390 if ( order != null )
378391 {
379392 OrderStatus status ;
@@ -800,4 +813,4 @@ private string GenerateOrderRequest(Order order)
800813 return request ;
801814 }
802815 }
803- }
816+ }
0 commit comments