public async IAsyncEnumerable <ApiPerformance> ListPerformance(
     [Required, FromRoute] Symbol symbol, [FromQuery] ApiPerformanceQueryFilter queryFilter)
 {
     await foreach (var perf in fundService
                    .ListPerformanceAsync(symbol, queryFilter.Mode, queryFilter.FromDate, queryFilter.ToDate, queryFilter.CurrencyCode)
                    .WithCancellation(scopedCancellationToken.Token))
     {
         yield return(new ApiPerformance()
         {
             Date = perf.Date,
             NetAssetValuePerToken = perf.NetAssetValuePerToken,
             MarketCap = perf.MarketCap,
             MarketValuePerToken = perf.MarketAssetValuePerToken,
             Volume = perf.Volume
         });
     }
 }
Пример #2
0
        private async Task SyncStakingPowerAsync(
            IFundService fundService,
            IStakeService stakeService,
            IStakingPowerRepository stakingRepository,
            IStakeSettings stake,
            DateTimeOffset startDate,
            DateTimeOffset endDate,
            CancellationToken cancellationToken)
        {
            var currentDate      = GetHourlyDate(startDate.UtcDateTime).UtcDateTime;
            var lastStakingPower = await stakingRepository.GetStakingPowerAsync(stake.ContractAddress, currentDate.AddHours(-1));

            var stakingEvents = await stakeService.ListStakeEventsAsync(stake.Symbol, startDate.UtcDateTime, endDate.UtcDateTime)
                                .ToListAsync(cancellationToken);

            var hourlyGroups = stakingEvents
                               .OrderBy(x => x.ConfirmedAt)
                               .GroupBy(x => GetHourlyDate(x.ConfirmedAt))
                               .ToList();

            do
            {
                try
                {
                    var fundPowers   = new List <DataStakingPowerFund>();
                    var hourlyEvents = hourlyGroups.SingleOrDefault(g => g.Key == currentDate)
                                       ?? Enumerable.Empty <IStakeEvent>();

                    foreach (var symbol in stake.FundMultipliers.Keys)
                    {
                        var fund = AppSettings.Funds
                                   .Cast <IFundSettings>()
                                   .Single(x => x.Symbol == symbol);

                        var events = lastStakingPower?.Breakdown.SingleOrDefault(x =>
                                                                                 x.ContractAddress.Equals(fund.ContractAddress.Address, StringComparison.OrdinalIgnoreCase))
                                     ?.Events.ToList()
                                     ?? new List <DataStakingEvent>();

                        events.AddRange(hourlyEvents
                                        .Where(e =>
                                               e.ContractAddress == fund.ContractAddress &&
                                               e.Type == StakeEventType.Lockup)
                                        .Select(e => new DataStakingEvent()
                        {
                            UserAddress  = e.UserAddress,
                            StakedAt     = e.ConfirmedAt,
                            Quantity     = e.Change,
                            ExpiresAt    = e.Lock.ExpiresAt,
                            TimeModifier = stake.TimeMultipliers
                                           .SingleOrDefault(tm =>
                                                            tm.RangeMin <= e.Lock.Duration.Days &&
                                                            tm.RangeMax >= e.Lock.Duration.Days)
                                           ?.Multiplier ?? 1
                        }));

                        foreach (var releaseEvent in hourlyEvents
                                 .Where(e =>
                                        e.ContractAddress == fund.ContractAddress &&
                                        e.Type != StakeEventType.Lockup))
                        {
                            var approximateQuantity = releaseEvent.Release.Quantity + (releaseEvent.Release.FeeQuantity ?? decimal.Zero);
                            var userStakes          = events
                                                      .Where(e => e.UserAddress.Equals(releaseEvent.UserAddress.Address, StringComparison.OrdinalIgnoreCase))
                                                      .ToList();

                            var lockUp = userStakes.Count > 0
                                ? userStakes.Count == 1
                                    ? userStakes.Single()
                                    : userStakes
                                         .OrderBy(x => x.StakedAt)
                                         .FirstOrDefault(e => Math.Abs(e.Quantity - approximateQuantity) <= Precision)
                                : null;

                            if (lockUp != null)
                            {
                                events.Remove(lockUp);
                            }
                        }

                        if (events.Any())
                        {
                            var prices = await fundService.ListPerformanceAsync(
                                symbol,
                                PriceMode.Raw,
                                currentDate.Date.AddDays(-1),
                                currentDate.Date.AddDays(2),
                                CurrencyCode.USD)
                                         .ToListAsync(cancellationToken);

                            var closestPrice = prices
                                               .OrderBy(i => Math.Abs(
                                                            new DateTimeOffset(i.Date, TimeSpan.Zero).ToUnixTimeSeconds() -
                                                            new DateTimeOffset(currentDate, TimeSpan.Zero).ToUnixTimeSeconds()))
                                               .FirstOrDefault()
                                               ?? throw new PermanentException($"No Price data could be found for date {currentDate}");

                            fundPowers.Add(new DataStakingPowerFund()
                            {
                                ContractAddress = fund.ContractAddress,
                                FundModifier    = stake.FundMultipliers[symbol],
                                PricePerToken   = closestPrice.NetAssetValuePerToken,
                                Events          = events
                            });
                        }
                    }

                    lastStakingPower = new DataStakingPower()
                    {
                        Address   = stake.ContractAddress,
                        Date      = currentDate.AddHours(1),
                        Power     = fundPowers.Sum(fp => fp.PricePerToken * fp.Events.Sum(fpe => fpe.Quantity * fpe.TimeModifier * fp.FundModifier)),
                        Breakdown = fundPowers,
                        Summary   = fundPowers
                                    .Select(fp => new DataStakingPowerSummary()
                        {
                            ContractAddress = fp.ContractAddress,
                            Power           = fp.PricePerToken * fp.Events.Sum(fpe => fpe.Quantity * fpe.TimeModifier * fp.FundModifier)
                        })
                                    .ToList()
                    };

                    await stakingRepository.UploadItemsAsync(lastStakingPower);
                }
                finally
                {
                    currentDate = currentDate.AddHours(1);
                }
            }while (!cancellationToken.IsCancellationRequested && currentDate < endDate.Round());
        }
Пример #3
0
        public async Task <IStake> GetStakeAsync(Symbol stakeSymbol, CurrencyCode currencyCode)
        {
            var stakeInfo = GetStakeInfo(stakeSymbol);

            var stakePower = await stakingPowerRepository.GetLatestAsync(stakeInfo.ContractAddress);

            var tokenInfo = await ethplorerClient.GetTokenInfoAsync(stakeInfo.ContractAddress);

            var tokenPair = await graphClient.GetUniswapPairAsync(stakeInfo.PoolAddress);

            var circulatingSupply = tokenInfo.TotalSupply.FromBigInteger(int.Parse(tokenInfo.Decimals));
            var token             = tokenPair.Tokens
                                    .Single(x => x.Symbol.Equals(stakeInfo.Symbol.ToString(), StringComparison.OrdinalIgnoreCase));

            var now = DateTime.UtcNow;

            var priceHistory = await fundService.ListPerformanceAsync(stakeSymbol, PriceMode.Raw, now.AddDays(-29), now, currencyCode)
                               .ToListAsync(CancellationToken);

            var dailyNavs  = priceHistory.TakeLast(2);
            var weeklyNavs = priceHistory.TakeLast(8);

            return(new BusinessStake()
            {
                Name = stakeInfo.Name,
                Category = stakeInfo.Category,
                Description = stakeInfo.Description,
                InvictusUri = stakeInfo.Links.External,
                FactSheetUri = stakeInfo.Links.Fact,
                PoolUri = new Uri(string.Format(PoolTemplate, stakeInfo.PoolAddress), UriKind.Absolute),
                Token = new BusinessToken()
                {
                    Symbol = stakeInfo.Symbol,
                    ContractAddress = stakeInfo.ContractAddress,
                    Decimals = stakeInfo.Decimals
                },
                CirculatingSupply = circulatingSupply,
                Market = new BusinessMarket()
                {
                    IsTradable = true,
                    Cap = CurrencyConverter.Convert(priceHistory.Last().MarketCap.Value, currencyCode),
                    PricePerToken = CurrencyConverter.Convert(token.PricePerToken, currencyCode),
                    Total = CurrencyConverter.Convert(circulatingSupply * token.PricePerToken, currencyCode),
                    DiffDaily = dailyNavs.First().MarketAssetValuePerToken.Value.PercentageDiff(dailyNavs.Last().MarketAssetValuePerToken.Value),
                    DiffWeekly = weeklyNavs.First().MarketAssetValuePerToken.Value.PercentageDiff(weeklyNavs.Last().MarketAssetValuePerToken.Value),
                    DiffMonthly = priceHistory.First().MarketAssetValuePerToken.Value.PercentageDiff(priceHistory.Last().MarketAssetValuePerToken.Value),
                    Volume = CurrencyConverter.Convert(tokenPair.Volume, currencyCode),
                    VolumeDiffDaily = dailyNavs.First().Volume.Value.PercentageDiff(dailyNavs.Last().Volume.Value),
                    VolumeDiffWeekly = weeklyNavs.First().Volume.Value.PercentageDiff(weeklyNavs.Last().Volume.Value),
                    VolumeDiffMonthly = priceHistory.First().Volume.Value.PercentageDiff(priceHistory.Last().Volume.Value),
                },
                StakingAddress = stakeInfo.StakingAddress,
                FundMultipliers = stakeInfo.FundMultipliers,
                TimeMultipliers = stakeInfo.TimeMultipliers
                                  .Select(tm => new BusinessTimeMultiplier()
                {
                    RangeMin = tm.RangeMin,
                    RangeMax = tm.RangeMax,
                    Multiplier = tm.Multiplier
                })
                                  .ToList(),
                Power = MapStakingPower(stakeInfo, stakePower, currencyCode)
            });
        }