private async Task <IFund> GetFundAsync(IFundSettings settings, IInvictusFund fund, CurrencyCode currencyCode)
        {
            var now = DateTime.UtcNow;

            var priceData = settings.Tradable
                ? await ethplorerClient.GetTokenInfoAsync(new EthereumAddress(settings.DataKey))
                : null;

            var navData = await ListPerformanceAsync(settings.Symbol, PriceMode.Close, now.AddDays(-29), now, currencyCode)
                          .ToListAsync(CancellationToken);

            return(await MapFundAsync(
                       fund,
                       navData
                       .Select(x => x.NetAssetValuePerToken)
                       .ToList(),
                       priceData?.Price,
                       settings.Symbol,
                       currencyCode));
        }
        private async Task <IFund> MapFundAsync(IInvictusFund fund, IReadOnlyList <decimal> navs, EthplorerPriceSummary priceData, Symbol symbol, CurrencyCode currencyCode)
        {
            var dailyNavs  = navs.TakeLast(2);
            var weeklyNavs = navs.TakeLast(8);
            var netVal     = fund.Assets.Any()
                ? CurrencyConverter.Convert(fund.Assets.Select(x => x.Total.FromPythonString()).Where(x => x > 0).Sum(), currencyCode)
                : CurrencyConverter.Convert(fund.NetValue.FromPythonString(), currencyCode);
            var circulatingSupply = fund.CirculatingSupply.FromPythonString();
            var fundInfo          = GetFundInfo(symbol);

            var businessFund = new BusinessFund()
            {
                Name         = fund.Name,
                Category     = fundInfo.Category,
                Description  = fundInfo.Description,
                FactSheetUri = fundInfo.Links.Fact,
                LitepaperUri = fundInfo.Links.Lite,
                InvictusUri  = fundInfo.Links.External,
                Token        = new BusinessToken()
                {
                    Symbol          = fundInfo.Symbol,
                    ContractAddress = new EthereumAddress(fundInfo.ContractAddress),
                    Decimals        = fundInfo.Decimals
                },
                CirculatingSupply = circulatingSupply,
                Nav = new BusinessNav()
                {
                    Value         = CurrencyConverter.Convert(fund.NetValue.FromPythonString(), currencyCode),
                    ValuePerToken = CurrencyConverter.Convert(fund.NetAssetValuePerToken.FromPythonString(), currencyCode),
                    DiffDaily     = dailyNavs.First().PercentageDiff(dailyNavs.Last()),
                    DiffWeekly    = weeklyNavs.First().PercentageDiff(weeklyNavs.Last()),
                    DiffMonthly   = navs.First().PercentageDiff(navs.Last()),
                },
                Market = new BusinessMarket()
                {
                    IsTradable        = priceData != null,
                    Cap               = priceData?.MarketCap ?? decimal.Zero,
                    Total             = CurrencyConverter.Convert((priceData?.MarketValuePerToken ?? decimal.Zero) * circulatingSupply, currencyCode),
                    PricePerToken     = CurrencyConverter.Convert(priceData?.MarketValuePerToken ?? decimal.Zero, currencyCode),
                    DiffDaily         = priceData?.DiffDaily ?? decimal.Zero,
                    DiffWeekly        = priceData?.DiffWeekly ?? decimal.Zero,
                    DiffMonthly       = priceData?.DiffMonthly ?? decimal.Zero,
                    Volume            = CurrencyConverter.Convert(priceData?.Volume ?? decimal.Zero, currencyCode),
                    VolumeDiffDaily   = priceData?.VolumeDiffDaily ?? decimal.Zero,
                    VolumeDiffWeekly  = priceData?.VolumeDiffWeekly ?? decimal.Zero,
                    VolumeDiffMonthly = priceData?.VolumeDiffMonthly ?? decimal.Zero
                }
            };

            var assets = fund.Assets
                         .Select(FromInvictusAsset)
                         .Where(x => x.Total > 0)
                         .ToList();

            foreach (var asset in fundInfo.Assets)
            {
                assets.Add(await MapFundAssetAsync(asset, netVal, currencyCode));
            }

            businessFund.Assets = assets
                                  .OrderByDescending(x => x.Total)
                                  .ToList();

            return(businessFund);

            BusinessFundAsset FromInvictusAsset(InvictusAsset asset)
            {
                var total         = CurrencyConverter.Convert(asset.Total.FromPythonString(), currencyCode);
                var holding       = GetHoldingInfo(asset.Symbol);
                var isFiat        = Enum.IsDefined(typeof(CurrencyCode), asset.Symbol);
                var sanitisedId   = asset.Name.Replace(" ", "-").Replace(".", "-").ToLower().Trim();
                var sanitisedName = string.Join(" ", asset.Name.Trim()
                                                .Replace("-", " ")
                                                .Split(' ')
                                                .Select(x => x.Length > 1 ? x.Substring(0, 1).ToUpper() + x.Substring(1) : x.ToUpper()));
                var coinloreId      = GetAssetInfo(asset.Symbol)?.CoinLore ?? sanitisedId;
                var coinMarketCapId = GetAssetInfo(asset.Symbol)?.CoinMarketCap ?? sanitisedId;
                var fund            = Enum.TryParse(asset.Symbol, out Symbol symbol)
                    ? GetFundInfo(symbol)
                    : null;

                return(new BusinessFundAsset()
                {
                    Holding = new BusinessHolding()
                    {
                        Name = sanitisedName,
                        Symbol = asset.Symbol,
                        HexColour = holding?.Colour ?? GetAssetInfo(asset.Symbol)?.Colour,
                        IsCoin = holding == null,
                        Link = holding?.Link
                               ?? fund?.Links?.External
                               ?? (isFiat
                                ? new Uri(string.Format(FiatTemplate, asset.Symbol.ToUpper()), UriKind.Absolute)
                                : new Uri(string.Format(LinkTemplate, coinMarketCapId), UriKind.Absolute)),
                        ImageLink = holding?.ImageLink ?? (fund != null || isFiat
                            ? new Uri($"https://{HostUrl.Host}/resources/{asset.Symbol}.png", UriKind.Absolute)
                            : new Uri(string.Format(ImageTemplate, coinloreId), UriKind.Absolute)),
                        MarketLink = (fund == null || fund.Tradable) && !isFiat && holding == null
                            ? new Uri(string.Format(MarketTemplate, coinloreId, currencyCode), UriKind.Absolute)
                            : null
                    },
                    Quantity = CurrencyConverter.Convert(asset.Quantity.FromPythonString(), currencyCode),
                    PricePerToken = CurrencyConverter.Convert(asset.PricePerToken.FromPythonString(), currencyCode),
                    Total = total,
                    Share = total / netVal * 100
                });
            }
        }