public void TestWithSnapshot()
        {
            var snapshot = new SnapshotEntity()
            {
                Balance      = 1,
                AvgOpenPrice = 40000
            };
            var ops = new List <AvgPriceUpdate>();

            // ops.Add(new OperationUpdate()
            // {
            //     Amount = 1,
            //     Balance = 1,
            //     AssetPriceInUsd = 40000
            // });
            ops.Add(new AvgPriceUpdate()
            {
                Amount          = 1,
                Balance         = 2,
                AssetPriceInUsd = 50000
            });
            ops.Add(new AvgPriceUpdate()
            {
                Amount          = (decimal) - 1.5,
                Balance         = (decimal)0.5,
                AssetPriceInUsd = 45000
            });
            ops.Add(new AvgPriceUpdate()
            {
                Amount          = 1,
                Balance         = (decimal)1.5,
                AssetPriceInUsd = 53000
            });
            ops.Add(new AvgPriceUpdate()
            {
                Amount          = (decimal) - 0.5,
                Balance         = 1,
                AssetPriceInUsd = 55000
            });
            ops.Add(new AvgPriceUpdate()
            {
                Amount          = 1,
                Balance         = 2,
                AssetPriceInUsd = 50000
            });
            var t = AvgPriceCalculator.CalculateAvgPrice(ops, snapshot);


            Assert.AreEqual(45000, t[0].AvgOpenPrice);
            Assert.AreEqual(45000, t[1].AvgOpenPrice);
            Assert.AreEqual((decimal)50333.33, t[2].AvgOpenPrice);
            Assert.AreEqual((decimal)50333.33, t[3].AvgOpenPrice);
            Assert.AreEqual((decimal)50166.66, t[4].AvgOpenPrice);
        }
        public void TestWithoutSnapshot()
        {
            var ops = new List <AvgPriceUpdate>();

            ops.Add(new AvgPriceUpdate()
            {
                Amount          = 1,
                Balance         = 1,
                AssetPriceInUsd = 40000
            });
            ops.Add(new AvgPriceUpdate()
            {
                Amount          = 1,
                Balance         = 2,
                AssetPriceInUsd = 50000
            });
            ops.Add(new AvgPriceUpdate()
            {
                Amount          = (decimal) - 1.5,
                Balance         = (decimal)0.5,
                AssetPriceInUsd = 45000
            });
            ops.Add(new AvgPriceUpdate()
            {
                Amount          = 1,
                Balance         = (decimal)1.5,
                AssetPriceInUsd = 53000
            });
            ops.Add(new AvgPriceUpdate()
            {
                Amount          = (decimal) - 0.5,
                Balance         = 1,
                AssetPriceInUsd = 55000
            });
            ops.Add(new AvgPriceUpdate()
            {
                Amount          = 1,
                Balance         = 2,
                AssetPriceInUsd = 50000
            });
            var t = AvgPriceCalculator.CalculateAvgPrice(ops, null);


            Assert.AreEqual(40000, t[0].AvgOpenPrice);
            Assert.AreEqual(45000, t[1].AvgOpenPrice);
            Assert.AreEqual(45000, t[2].AvgOpenPrice);
            Assert.AreEqual((decimal)50333.33, t[3].AvgOpenPrice);
            Assert.AreEqual((decimal)50333.33, t[4].AvgOpenPrice);
            Assert.AreEqual((decimal)50166.66, t[5].AvgOpenPrice);
        }
Beispiel #3
0
        private async ValueTask HandleEvents(IReadOnlyList <ExBalanceUpdateMessage> messages)
        {
            using var activity = MyTelemetry.StartActivity("Handle ExBalanceUpdateMessage's")
                                 ?.AddTag("count-events", messages.Count);
            try
            {
                var sw = new Stopwatch();
                sw.Start();

                var list           = new List <AvgPriceUpdate>();
                var calculatedList = new List <AvgPriceUpdate>();

                if (!messages.Any())
                {
                    return;
                }

                list.AddRange(GetOperationUpdates(messages));
                if (!list.Any())
                {
                    return;
                }

                await using var ctx = DatabaseContext.Create(_dbContextOptionsBuilder);
                var groups = list.GroupBy(t => new { t.AssetId, t.WalletId });
                foreach (var group in groups)
                {
                    var semaphore = SemaphoreManager.GetOrCreate(group.Key.WalletId, group.Key.AssetId);
                    await semaphore.WaitAsync();

                    try
                    {
                        var groupedList = group.OrderBy(t => t.TimeStamp).ToList();

                        var snapshot = await _snapshotManager.GetSnapshot(group.Key.WalletId, group.Key.AssetId);

                        if (snapshot != null)
                        {
                            groupedList = groupedList.Where(t => t.TimeStamp > snapshot.LastTimestamp).ToList();
                        }

                        if (!groupedList.Any())
                        {
                            continue;
                        }

                        groupedList = AvgPriceCalculator.CalculateAvgPrice(groupedList, snapshot);
                        calculatedList.AddRange(groupedList);

                        var openTimestamp = groupedList.LastOrDefault(t => t.Balance == 0)?.TimeStamp ??
                                            snapshot?.OpenTimestamp ?? default;

                        var lastUpdate  = groupedList.Last();
                        var newSnapshot = new SnapshotEntity
                        {
                            WalletId      = lastUpdate.WalletId,
                            AssetId       = lastUpdate.AssetId,
                            Balance       = lastUpdate.Balance,
                            AvgOpenPrice  = lastUpdate.AvgOpenPrice,
                            OpenTimestamp = openTimestamp,
                            LastTimestamp = lastUpdate.TimeStamp
                        };
                        await _snapshotManager.UpsertSnapshots(new() { newSnapshot });
                    }
                    finally
                    {
                        semaphore.Release();
                    }
                }

                await ctx.UpsertAvgPriceAsync(calculatedList.Where(t => t.EventType != "Refund Blockchain Withdrawal").Select(update => new OperationHistoryEntity(update)));

                await ctx.SaveChangesAsync();

                sw.Stop();

                _logger.LogDebug(
                    "Write {countUpdates} avg price updates in database from {countEvents} ME events. Time: {elapsedText} [{elapsedMilliseconds} ms]",
                    list.Count, messages.Count, sw.Elapsed.ToString(), sw.ElapsedMilliseconds);
            }
            catch (Exception ex)
            {
                ex.FailActivity();
                messages.AddToActivityAsJsonTag("me events");
                throw;
            }
        }