StockSharp

Форк
0
/
MarketDepth.cs 
735 строк · 20.4 Кб
1
#region S# License
2
/******************************************************************************************
3
NOTICE!!!  This program and source code is owned and licensed by
4
StockSharp, LLC, www.stocksharp.com
5
Viewing or use of this code requires your acceptance of the license
6
agreement found at https://github.com/StockSharp/StockSharp/blob/master/LICENSE
7
Removal of this comment is a violation of the license agreement.
8

9
Project: StockSharp.BusinessEntities.BusinessEntities
10
File: MarketDepth.cs
11
Created: 2015, 11, 11, 2:32 PM
12

13
Copyright 2010 by StockSharp, LLC
14
*******************************************************************************************/
15
#endregion S# License
16
namespace StockSharp.BusinessEntities
17
{
18
	using System;
19
	using System.ComponentModel;
20
	using System.Collections;
21
	using System.Collections.Generic;
22
	using System.ComponentModel.DataAnnotations;
23
	using System.Linq;
24

25
	using Ecng.Collections;
26
	using Ecng.Common;
27

28
	using StockSharp.Messages;
29
	using StockSharp.Localization;
30

31
	/// <summary>
32
	/// Order book.
33
	/// </summary>
34
	[System.Runtime.Serialization.DataContract]
35
	[Serializable]
36
	[Obsolete("Use IOrderBookMessage.")]
37
	public class MarketDepth : Cloneable<MarketDepth>, IEnumerable<QuoteChange>, IOrderBookMessage
38
	{
39
		/// <summary>
40
		/// Create order book.
41
		/// </summary>
42
		/// <param name="security">Security.</param>
43
		public MarketDepth(Security security)
44
		{
45
			Security = security ?? throw new ArgumentNullException(nameof(security));
46
		}
47

48
		QuoteChangeStates? IOrderBookMessage.State { get => null; set => throw new NotSupportedException(); }
49

50
		private SecurityId? _securityId;
51

52
		SecurityId ISecurityIdMessage.SecurityId
53
		{
54
			get => _securityId ??= Security?.Id.ToSecurityId() ?? default;
55
			set => throw new NotSupportedException();
56
		}
57

58
		/// <summary>
59
		/// Security.
60
		/// </summary>
61
		public Security Security { get; }
62

63
		/// <summary>
64
		/// Whether to use aggregated quotes <see cref="QuoteChange.InnerQuotes"/> at the join of the volumes with the same price.
65
		/// </summary>
66
		/// <remarks>
67
		/// The default is disabled for performance.
68
		/// </remarks>
69
		public bool UseAggregatedQuotes { get; set; }
70

71
		/// <inheritdoc/>
72
		[Display(
73
			ResourceType = typeof(LocalizedStrings),
74
			Name = LocalizedStrings.ServerTimeKey,
75
			Description = LocalizedStrings.ChangeServerTimeKey,
76
			GroupName = LocalizedStrings.CommonKey,
77
			Order = 2)]
78
		public DateTimeOffset ServerTime { get; set; }
79

80
		/// <summary>
81
		/// Last change time.
82
		/// </summary>
83
		[Browsable(false)]
84
		[Obsolete("Use ServerTime property.")]
85
		public DateTimeOffset LastChangeTime
86
		{
87
			get => ServerTime;
88
			set => ServerTime = value;
89
		}
90

91
		/// <inheritdoc/>
92
		[Display(
93
			ResourceType = typeof(LocalizedStrings),
94
			Name = LocalizedStrings.LocalTimeKey,
95
			Description = LocalizedStrings.LocalTimeDescKey,
96
			GroupName = LocalizedStrings.CommonKey,
97
			Order = 3)]
98
		public DateTimeOffset LocalTime { get; set; }
99

100
		/// <inheritdoc/>
101
		public long SeqNum { get; set; }
102

103
		/// <inheritdoc/>
104
		public Messages.DataType BuildFrom { get; set; }
105

106
		/// <summary>
107
		/// Get the array of bids sorted by descending price. The first (best) bid will be the maximum price.
108
		/// </summary>
109
		[Obsolete("Use Bids property.")]
110
		public QuoteChange[] Bids2 => Bids;
111

112
		/// <summary>
113
		/// Get the array of asks sorted by ascending price. The first (best) ask will be the minimum price.
114
		/// </summary>
115
		[Obsolete("Use Asks property.")]
116
		public QuoteChange[] Asks2 => Asks;
117

118
		private QuoteChange[] _bids = Array.Empty<QuoteChange>();
119

120
		/// <inheritdoc/>
121
		[Display(
122
			ResourceType = typeof(LocalizedStrings),
123
			Name = LocalizedStrings.BidsKey,
124
			Description = LocalizedStrings.QuotesBuyKey,
125
			GroupName = LocalizedStrings.CommonKey,
126
			Order = 0)]
127
		public QuoteChange[] Bids
128
		{
129
			get => _bids;
130
			set => _bids = value ?? throw new ArgumentNullException(nameof(value));
131
		}
132

133
		private QuoteChange[] _asks = Array.Empty<QuoteChange>();
134

135
		/// <inheritdoc/>
136
		[Display(
137
			ResourceType = typeof(LocalizedStrings),
138
			Name = LocalizedStrings.AsksKey,
139
			Description = LocalizedStrings.QuotesSellKey,
140
			GroupName = LocalizedStrings.CommonKey,
141
			Order = 1)]
142
		public QuoteChange[] Asks
143
		{
144
			get => _asks;
145
			set => _asks = value ?? throw new ArgumentNullException(nameof(value));
146
		}
147

148
		/// <summary>
149
		/// Trading security currency.
150
		/// </summary>
151
		[Display(ResourceType = typeof(LocalizedStrings), Name = LocalizedStrings.CurrencyKey)]
152
		public CurrencyTypes? Currency { get; set; }
153

154
		/// <summary>
155
		/// The best bid. If the order book does not contain bids, will be returned <see langword="null" />.
156
		/// </summary>
157
		[Display(ResourceType = typeof(LocalizedStrings), Name = LocalizedStrings.BestBidKey)]
158
		public QuoteChange? BestBid2 { get; private set; }
159

160
		/// <summary>
161
		/// The best ask. If the order book does not contain asks, will be returned <see langword="null" />.
162
		/// </summary>
163
		[Display(ResourceType = typeof(LocalizedStrings), Name = LocalizedStrings.BestAskKey)]
164
		public QuoteChange? BestAsk2 { get; private set; }
165

166
		/// <summary>
167
		/// The best pair. If the order book is empty, will be returned <see langword="null" />.
168
		/// </summary>
169
		[Display(ResourceType = typeof(LocalizedStrings), Name = LocalizedStrings.BestPairKey)]
170
		public MarketDepthPair BestPair => GetPair(0);
171

172
		/// <summary>
173
		/// To get the total price size by bids.
174
		/// </summary>
175
		[Display(ResourceType = typeof(LocalizedStrings), Name = LocalizedStrings.TotalBidsPriceKey)]
176
		public decimal TotalBidsPrice => _bids.Length > 0 ? Security.ShrinkPrice(_bids.Sum(b => b.Price)) : 0;
177

178
		/// <summary>
179
		/// To get the total price size by offers.
180
		/// </summary>
181
		[Display(ResourceType = typeof(LocalizedStrings), Name = LocalizedStrings.TotalAsksPriceKey)]
182
		public decimal TotalAsksPrice => _asks.Length > 0 ? Security.ShrinkPrice(_asks.Sum(a => a.Price)) : 0;
183

184
		/// <summary>
185
		/// Get bids total volume.
186
		/// </summary>
187
		[Display(ResourceType = typeof(LocalizedStrings), Name = LocalizedStrings.TotalBidsVolumeKey)]
188
		public decimal TotalBidsVolume => _bids.Sum(b => b.Volume);
189

190
		/// <summary>
191
		/// Get asks total volume.
192
		/// </summary>
193
		[Display(ResourceType = typeof(LocalizedStrings), Name = LocalizedStrings.TotalAsksVolumeKey)]
194
		public decimal TotalAsksVolume => _asks.Sum(a => a.Volume);
195

196
		/// <summary>
197
		/// Get total volume.
198
		/// </summary>
199
		[Display(ResourceType = typeof(LocalizedStrings), Name = LocalizedStrings.TotalVolumeKey)]
200
		public decimal TotalVolume => TotalBidsVolume + TotalAsksVolume;
201

202
		/// <summary>
203
		/// To get the total price size.
204
		/// </summary>
205
		[Display(ResourceType = typeof(LocalizedStrings), Name = LocalizedStrings.TotalPriceKey)]
206
		public decimal TotalPrice => TotalBidsPrice + TotalAsksPrice;
207

208
		/// <summary>
209
		/// Total quotes count (bids + asks).
210
		/// </summary>
211
		[Display(ResourceType = typeof(LocalizedStrings), Name = LocalizedStrings.TotalQuotesCountKey)]
212
		public int Count => _bids.Length + _asks.Length;
213

214
		/// <summary>
215
		/// Depth of book.
216
		/// </summary>
217
		[Display(ResourceType = typeof(LocalizedStrings), Name = LocalizedStrings.DepthOfBookKey)]
218
		public int Depth { get; private set; }
219

220
		/// <summary>
221
		/// To reduce the order book to the required depth.
222
		/// </summary>
223
		/// <param name="newDepth">New order book depth.</param>
224
		public void Decrease(int newDepth)
225
		{
226
			var currentDepth = Depth;
227

228
			if (newDepth < 0)
229
				throw new ArgumentOutOfRangeException(nameof(newDepth), newDepth, LocalizedStrings.InvalidValue);
230
			else if (newDepth > currentDepth)
231
				throw new ArgumentOutOfRangeException(nameof(newDepth), newDepth, LocalizedStrings.NewDepthCannotMoreCurrent.Put(currentDepth));
232

233
			Bids = Decrease(_bids, newDepth);
234
			Asks = Decrease(_asks, newDepth);
235

236
			UpdateDepthAndTime();
237
		}
238

239
		private static QuoteChange[] Decrease(QuoteChange[] quotes, int newDepth)
240
		{
241
			if (quotes is null)
242
				throw new ArgumentNullException(nameof(quotes));
243

244
			if (newDepth <= quotes.Length)
245
				Array.Resize(ref quotes, newDepth);
246

247
			return quotes;
248
		}
249

250
		/// <summary>
251
		/// To get a quote by the direction <see cref="Sides"/> and the depth index.
252
		/// </summary>
253
		/// <param name="orderDirection">Orders side.</param>
254
		/// <param name="depthIndex">Depth index. Zero index means the best quote.</param>
255
		/// <returns>Quote. If a quote does not exist for specified depth, then the <see langword="null" /> will be returned.</returns>
256
		public QuoteChange? GetQuote(Sides orderDirection, int depthIndex)
257
		{
258
			return GetQuotesInternal(orderDirection).ElementAtOr(depthIndex);
259
		}
260

261
		/// <summary>
262
		/// To get a quote by the price.
263
		/// </summary>
264
		/// <param name="price">Quote price.</param>
265
		/// <returns>Found quote. If there is no quote in the order book for the passed price, then the <see langword="null" /> will be returned.</returns>
266
		public QuoteChange? GetQuote(decimal price)
267
		{
268
			var quotes = GetQuotes(price);
269
			var i = GetQuoteIndex(quotes, price);
270
			return i < 0 ? default : quotes[i];
271
		}
272

273
		/// <summary>
274
		/// To get quotes by the direction <see cref="Sides"/>.
275
		/// </summary>
276
		/// <param name="orderDirection">Orders side.</param>
277
		/// <returns>Quotes.</returns>
278
		public QuoteChange[] GetQuotes(Sides orderDirection)
279
		{
280
			return orderDirection == Sides.Buy ? Bids : Asks;
281
		}
282

283
		/// <summary>
284
		/// To get the best quote by the direction <see cref="Sides"/>.
285
		/// </summary>
286
		/// <param name="orderDirection">Order side.</param>
287
		/// <returns>The best quote. If the order book is empty, then the <see langword="null" /> will be returned.</returns>
288
		public QuoteChange? GetBestQuote(Sides orderDirection)
289
		{
290
			return orderDirection == Sides.Buy ? BestBid2 : BestAsk2;
291
		}
292

293
		/// <summary>
294
		/// To get a pair of quotes (bid + offer) by the depth index.
295
		/// </summary>
296
		/// <param name="depthIndex">Depth index. Zero index means the best pair of quotes.</param>
297
		/// <returns>The pair of quotes. If the index is larger than book order depth <see cref="MarketDepth.Depth"/>, then the <see langword="null" /> is returned.</returns>
298
		public MarketDepthPair GetPair(int depthIndex)
299
		{
300
			var (bid, ask) = Extensions.GetPair(this, depthIndex);
301

302
			if (bid is null && ask is null)
303
				return null;
304

305
			return new MarketDepthPair(bid, ask);
306
		}
307

308
		/// <summary>
309
		/// To get a pair of quotes for a given book depth.
310
		/// </summary>
311
		/// <param name="depth">Book depth. The counting is from the best quotes.</param>
312
		/// <returns>Spread.</returns>
313
		public IEnumerable<MarketDepthPair> GetTopPairs(int depth)
314
			=> Extensions.GetTopPairs(this, depth).Select(t => new MarketDepthPair(t.bid, t.ask));
315

316
		/// <summary>
317
		/// To get quotes for a given book depth.
318
		/// </summary>
319
		/// <param name="depth">Book depth. Quotes are in order of price increasing from bids to offers.</param>
320
		/// <returns>Spread.</returns>
321
		public IEnumerable<QuoteChange> GetTopQuotes(int depth)
322
			=> Extensions.GetTopQuotes(this, depth);
323

324
		/// <summary>
325
		/// To update the order book. The version without checks and blockings.
326
		/// </summary>
327
		/// <param name="bids">Sorted bids.</param>
328
		/// <param name="asks">Sorted asks.</param>
329
		/// <param name="lastChangeTime">Change time.</param>
330
		/// <returns>Market depth.</returns>
331
		public MarketDepth Update(QuoteChange[] bids, QuoteChange[] asks, DateTimeOffset lastChangeTime)
332
		{
333
			if (bids is null)
334
				throw new ArgumentNullException(nameof(bids));
335

336
			if (asks is null)
337
				throw new ArgumentNullException(nameof(asks));
338

339
			_bids = bids.ToArray();
340
			_asks = asks.ToArray();
341

342
			UpdateDepthAndTime(lastChangeTime);
343

344
			return this;
345
		}
346

347
		/// <summary>
348
		/// To refresh the quote. If a quote with the same price is already in the order book, it is updated as passed. Otherwise, it automatically rebuilds the order book.
349
		/// </summary>
350
		/// <param name="quote">The new quote.</param>
351
		/// <param name="side">Side.</param>
352
		public void UpdateQuote(QuoteChange quote, Sides side)
353
		{
354
			SetQuote(quote, side, false);
355
		}
356

357
		/// <summary>
358
		/// Add buy quote.
359
		/// </summary>
360
		/// <param name="price">Buy price.</param>
361
		/// <param name="volume">Buy volume.</param>
362
		public void AddBid(decimal price, decimal volume)
363
		{
364
			AddQuote(new QuoteChange
365
			{
366
				Price = price,
367
				Volume = volume,
368
			}, Sides.Buy);
369
		}
370

371
		/// <summary>
372
		/// Add sell quote.
373
		/// </summary>
374
		/// <param name="price">Sell price.</param>
375
		/// <param name="volume">Sell volume.</param>
376
		public void AddAsk(decimal price, decimal volume)
377
		{
378
			AddQuote(new QuoteChange
379
			{
380
				Price = price,
381
				Volume = volume,
382
			}, Sides.Sell);
383
		}
384

385
		/// <summary>
386
		/// To add the quote. If a quote with the same price is already in the order book, they are combined into the <see cref="QuoteChange.InnerQuotes"/>.
387
		/// </summary>
388
		/// <param name="quote">The new quote.</param>
389
		/// <param name="side">Side.</param>
390
		public void AddQuote(QuoteChange quote, Sides side)
391
		{
392
			SetQuote(quote, side, true);
393
		}
394

395
		private void SetQuote(QuoteChange quote, Sides side, bool isAggregate)
396
		{
397
			//CheckQuote(quote);
398

399
			//Quote outOfDepthQuote = null;
400

401
			//lock (_syncRoot)
402
			//{
403
				var quotes = GetQuotes(side);
404

405
				var index = GetQuoteIndex(quotes, quote.Price);
406

407
				if (index != -1)
408
				{
409
					if (isAggregate)
410
					{
411
						var existedQuote = quotes[index];
412

413
						//if (UseAggregatedQuotes)
414
						//{
415
						//	if (existedQuote is not AggregatedQuote aggQuote)
416
						//	{
417
						//		aggQuote = new AggregatedQuote
418
						//		{
419
						//			Price = quote.Price,
420
						//			Security = quote.Security,
421
						//			OrderDirection = quote.OrderDirection
422
						//		};
423

424
						//		aggQuote.InnerQuotes.Add(existedQuote);
425

426
						//		quotes[index] = aggQuote;
427
						//	}
428

429
						//	aggQuote.InnerQuotes.Add(quote);
430
						//}
431
						//else
432
						existedQuote.Volume += quote.Volume;
433
					}
434
					else
435
					{
436
						quotes[index] = quote;
437
					}
438
				}
439
				else
440
				{
441
					for (index = 0; index < quotes.Length; index++)
442
					{
443
						var currentPrice = quotes[index].Price;
444

445
						if (side == Sides.Buy)
446
						{
447
							if (quote.Price > currentPrice)
448
								break;
449
						}
450
						else
451
						{
452
							if (quote.Price < currentPrice)
453
								break;
454
						}
455
					}
456

457
					Array.Resize(ref quotes, quotes.Length + 1);
458

459
					if (index < (quotes.Length - 1))
460
						Array.Copy(quotes, index, quotes, index + 1, quotes.Length - 1 - index);
461

462
					quotes[index] = quote;
463

464
					//if (quotes.Length > MaxDepth)
465
					//{
466
					//	outOfDepthQuote = quotes[quotes.Length - 1];
467
					//	quotes = RemoveAt(quotes, quotes.Length - 1);
468
					//}
469

470
					if (side == Sides.Buy)
471
						Bids = quotes;
472
					else
473
						Asks = quotes;
474
				}
475

476
				UpdateDepthAndTime();
477
			//}
478

479
			//if (outOfDepthQuote != null)
480
			//	QuoteOutOfDepth?.Invoke(outOfDepthQuote);
481
		}
482

483
		#region IEnumerable<QuoteChange>
484

485
		/// <summary>
486
		/// To get the enumerator object.
487
		/// </summary>
488
		/// <returns>The enumerator object.</returns>
489
		public IEnumerator<QuoteChange> GetEnumerator()
490
		{
491
			return Bids.Reverse().Concat(Asks).Cast<QuoteChange>().GetEnumerator();
492
		}
493

494
		/// <summary>
495
		/// To get the enumerator object.
496
		/// </summary>
497
		/// <returns>The enumerator object.</returns>
498
		IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
499

500
		#endregion
501

502
		/// <summary>
503
		/// To get all pairs from the order book.
504
		/// </summary>
505
		/// <returns>Pairs from which the order book is composed.</returns>
506
		public IEnumerable<MarketDepthPair> ToPairs()
507
		{
508
			return GetTopPairs(Depth);
509
		}
510

511
		/// <summary>
512
		/// Remove the volume for the price.
513
		/// </summary>
514
		/// <param name="price">Remove the quote for the price.</param>
515
		/// <param name="volume">The volume to be deleted. If it is not specified, then all the quote is removed.</param>
516
		/// <param name="lastChangeTime">Order book change time.</param>
517
		public void Remove(decimal price, decimal volume = 0, DateTimeOffset lastChangeTime = default)
518
		{
519
			var dir = GetDirection(price) ?? throw new ArgumentOutOfRangeException(nameof(price), price, LocalizedStrings.QuotePriceNotSpecified);
520

521
			Remove(dir, price, volume, lastChangeTime);
522
		}
523

524
		/// <summary>
525
		/// Remove the volume for the price.
526
		/// </summary>
527
		/// <param name="direction">Order side.</param>
528
		/// <param name="price">Remove the quote for the price.</param>
529
		/// <param name="volume">The volume to be deleted. If it is not specified, then all the quote is removed.</param>
530
		/// <param name="lastChangeTime">Order book change time.</param>
531
		public void Remove(Sides direction, decimal price, decimal volume = 0, DateTimeOffset lastChangeTime = default)
532
		{
533
			if (price <= 0)
534
				throw new ArgumentOutOfRangeException(nameof(price), price, LocalizedStrings.InvalidValue);
535

536
			if (volume < 0)
537
				throw new ArgumentOutOfRangeException(nameof(volume), volume, LocalizedStrings.InvalidValue);
538

539
			var quotes = GetQuotesInternal(direction);
540
			var index = GetQuoteIndex(quotes, price);
541

542
			if (index == -1)
543
				throw new ArgumentOutOfRangeException(nameof(price), price, LocalizedStrings.QuotePriceNotSpecified);
544

545
			var quote = quotes[index];
546

547
			decimal leftVolume;
548

549
			if (volume > 0)
550
			{
551
				if (quote.Volume < volume)
552
					throw new ArgumentOutOfRangeException(nameof(volume), volume, LocalizedStrings.VolumeLessThanRequired.Put(quote));
553

554
				leftVolume = quote.Volume - volume;
555

556
				//if (UseAggregatedQuotes)
557
				//{
558
				//	if (quote is AggregatedQuote aggQuote)
559
				//	{
560
				//		while (volume > 0)
561
				//		{
562
				//			var innerQuote = aggQuote.InnerQuotes.First();
563

564
				//			if (innerQuote.Volume > volume)
565
				//			{
566
				//				innerQuote.Volume -= volume;
567
				//				break;
568
				//			}
569
				//			else
570
				//			{
571
				//				aggQuote.InnerQuotes.Remove(innerQuote);
572
				//				volume -= innerQuote.Volume;
573
				//			}
574
				//		}
575
				//	}
576
				//}
577
			}
578
			else
579
				leftVolume = 0;
580

581
			if (leftVolume == 0)
582
			{
583
				quotes = RemoveAt(quotes, index);
584

585
				if (direction == Sides.Buy)
586
					Bids = quotes;
587
				else
588
					Asks = quotes;
589

590
				UpdateDepthAndTime(lastChangeTime);
591
			}
592
			else
593
			{
594
				quote.Volume = leftVolume;
595
				UpdateTime(lastChangeTime);
596
			}
597
		}
598

599
		private static QuoteChange[] RemoveAt(QuoteChange[] quotes, int index)
600
		{
601
			var newQuotes = new QuoteChange[quotes.Length - 1];
602

603
			if (index > 0)
604
				Array.Copy(quotes, 0, newQuotes, 0, index);
605

606
			if (index < (quotes.Length - 1))
607
				Array.Copy(quotes, index + 1, newQuotes, index, quotes.Length - index - 1);
608

609
			return newQuotes;
610
		}
611

612
		private static int GetQuoteIndex(QuoteChange[] quotes, decimal price)
613
		{
614
			var stop = quotes.Length - 1;
615
			if (stop < 0)
616
				return -1;
617

618
			var first = quotes[0];
619

620
			var cmp = decimal.Compare(price, first.Price);
621
			if (cmp == 0)
622
				return 0;
623

624
			var last = quotes[stop];
625
			var desc = first.Price - last.Price > 0m;
626

627
			if (desc)
628
				cmp = -cmp;
629

630
			if (cmp < 0)
631
				return -1;
632

633
			cmp = decimal.Compare(price, last.Price);
634

635
			if (desc)
636
				cmp = -cmp;
637

638
			if (cmp > 0)
639
				return -1;
640

641
			if (cmp == 0)
642
				return stop;
643

644
			var start = 0;
645

646
			while (stop - start >= 0)
647
			{
648
				var mid = (start + stop) >> 1;
649

650
				cmp = decimal.Compare(price, quotes[mid].Price);
651

652
				if (desc)
653
					cmp = -cmp;
654
				if (cmp > 0)
655
					start = mid + 1;
656
				else if (cmp < 0)
657
					stop = mid - 1;
658
				else
659
					return mid;
660
			}
661

662
			return -1;
663
		}
664

665
		private QuoteChange[] GetQuotesInternal(Sides direction)
666
		{
667
			return direction == Sides.Buy ? _bids : _asks;
668
		}
669

670
		private QuoteChange[] GetQuotes(decimal price)
671
		{
672
			var dir = GetDirection(price);
673

674
			if (dir == null)
675
				return Array.Empty<QuoteChange>();
676
			else
677
				return dir == Sides.Buy ? _bids : _asks;
678
		}
679

680
		private Sides? GetDirection(decimal price)
681
		{
682
			if (BestBid2 != null && BestBid2.Value.Price >= price)
683
				return Sides.Buy;
684
			else if (BestAsk2 != null && BestAsk2.Value.Price <= price)
685
				return Sides.Sell;
686
			else
687
				return null;
688
		}
689

690
		private void UpdateDepthAndTime(DateTimeOffset lastChangeTime = default)
691
		{
692
			Depth = _bids.Length > _asks.Length ? _bids.Length : _asks.Length;
693

694
			BestBid2 = _bids.Length > 0 ? _bids[0] : null;
695
			BestAsk2 = _asks.Length > 0 ? _asks[0] : null;
696

697
			UpdateTime(lastChangeTime);
698
		}
699

700
		private void UpdateTime(DateTimeOffset lastChangeTime)
701
		{
702
			if (lastChangeTime != default)
703
			{
704
				ServerTime = lastChangeTime;
705
			}
706
		}
707

708
		/// <summary>
709
		/// Create a copy of <see cref="MarketDepth"/>.
710
		/// </summary>
711
		/// <returns>Copy.</returns>
712
		public override MarketDepth Clone()
713
		{
714
			return new(Security)
715
			{
716
				//MaxDepth = MaxDepth,
717
				//UseAggregatedQuotes = UseAggregatedQuotes,
718
				//AutoVerify = AutoVerify,
719
				Currency = Currency,
720
				LocalTime = LocalTime,
721
				ServerTime = ServerTime,
722
				_bids = _bids.ToArray(),
723
				_asks = _asks.ToArray(),
724
				SeqNum = SeqNum,
725
				BuildFrom = BuildFrom,
726
			};
727
		}
728

729
		/// <inheritdoc />
730
		public override string ToString()
731
		{
732
			return this.Select(q => q.ToString()).JoinNL();
733
		}
734
	}
735
}

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.