IReadOnlyDictionary <CurrencyCode, SumState> CalculateAssetSums( AssetISIN isin, IReadOnlyCollection <VirtualAsset> allAssets, DateTimeOffset date) { var assets = allAssets .Where(a => a.Isin == isin) .ToArray(); if (assets.Length == 0) { return(new Dictionary <CurrencyCode, SumState>()); } var baseCurrency = assets[0].Currency; var baseRealSum = assets.Sum(a => a.RealSum); var baseVirtualSum = assets.Sum(a => a.VirtualSum); return(_currencyConfig.GetAll() .ToDictionary( c => c, c => { if (c == baseCurrency) { return new SumState(baseRealSum, baseVirtualSum); } var realSum = _exchangeManager.Exchange(baseCurrency, c, baseRealSum, date); var virtualSum = _exchangeManager.Exchange(baseCurrency, c, baseRealSum, date); return new SumState(realSum, virtualSum); } )); }
public ReadOnlyAsset(Asset asset) { Id = asset.Id; Isin = asset.Isin; Currency = asset.Currency; RawName = asset.RawName; Count = asset.Count; }
public Asset(AssetId id, AssetISIN isin, CurrencyCode currency, string rawName, int count) { Id = id; Isin = isin; Currency = currency; Count = count; RawName = rawName; }
AssetMetadata?GetMetadata(AssetISIN isin) { var models = _repository.GetAll(); var model = models.FirstOrDefault(m => m.Isin == isin); return((model != null) ? new AssetMetadata(new(model.Isin), new(model.Figi), model.Name, new(model.Type)) : null); }
AssetMetadata GetFallbackMetadata(AssetISIN isin, UserId user) { var rawName = _stateManager.ReadState(user).Brokers .SelectMany(b => b.Inventory) .Where(a => a.Isin == isin) .Select(a => a.RawName) .FirstOrDefault(); return(new AssetMetadata(isin, null, rawName ?? string.Empty, null)); }
DateTime GetStartDate(AssetISIN isin) { var price = _priceManager.TryGet(isin); if (price == null) { _logger.LogTrace($"Asset price for ISIN '{isin}' is unknown, lookup first add date"); return(GetFirstAddDate(isin)); } var lastRecordedDate = price.Candles[^ 1].Date;
public decimal?GetVirtualPricePerOne(AssetISIN isin, DateTimeOffset date) { var model = TryGetModel(isin); if (model == null) { _logger.LogWarning($"No model for ISIN '{isin}'"); return(null); } var lastCandleBeforeDate = model.Candles .LastOrDefault(c => c.Date < date); _logger.LogTrace($"Last candle value for ISIN '{isin}' before {date} is {lastCandleBeforeDate}"); return(lastCandleBeforeDate?.Close); }
public async Task RemoveTag(UserId user, AssetISIN asset, AssetTag tag) { var model = await _repository.Get(user); if (model == null) { return; } if (!model.Tags.TryGetValue(asset, out var tags)) { return; } tags.Remove(tag); await _repository.Update(model); }
public async Task AddTag(UserId user, AssetISIN asset, AssetTag tag) { var model = await _repository.Get(user); Func <UserAssetTagsModel, Task> apply = (model != null) ? _repository.Update : _repository.Add; if (model == null) { model = new UserAssetTagsModel(user, new Dictionary <string, List <string> >()); } if (!model.Tags.TryGetValue(asset, out var tags)) { tags = new List <string>(); model.Tags[asset] = tags; } tags.Add(tag); await apply(model); }
public async Task Handle(UserId user, AssetISIN asset, AssetTag tag) { _logger.LogTrace($"Remove tag '{tag}' from asset '{asset}' for user '{user}'"); await _assetTagManager.RemoveTag(user, asset, tag); }
public IEnumerable <AddAssetCommand> GetAddAssetCommands(AssetISIN isin) => _stateManager.ReadCommands <AddAssetCommand>() .Where(c => c.Isin == isin);
AssetPriceModel?TryGetModel(AssetISIN isin) => _repository.GetAll() .FirstOrDefault(m => m.Isin == isin);
public async Task <AssetId> Handle( DateTimeOffset date, UserId user, BrokerId brokerId, AccountId payAccountId, AccountId feeAccountId, AssetISIN isin, decimal price, decimal fee, string name, int count) { if (string.IsNullOrWhiteSpace(isin.Value)) { throw new InvalidAssetException(); } if (count <= 0) { throw new InvalidCountException(); } var state = _stateManager.ReadState(date, user); var broker = state.Brokers.FirstOrDefault(b => b.Id == brokerId); if (broker == null) { throw new InvalidBrokerException(); } var payAccount = broker.Accounts.FirstOrDefault(a => a.Id == payAccountId); if (payAccount == null) { throw new InvalidAccountException(); } var feeAccount = broker.Accounts.FirstOrDefault(a => a.Id == feeAccountId); if (feeAccount == null) { throw new InvalidAccountException(); } var asset = broker.Inventory.FirstOrDefault(a => a.Isin == isin); AssetId id; if (asset != null) { id = asset.Id; await _stateManager.AddCommand(new IncreaseAssetCommand(date, user, brokerId, asset.Id, count)); } else { id = new AssetId(_idGenerator.GenerateNewId()); var currency = payAccount.Currency; await _stateManager.AddCommand(new AddAssetCommand(date, user, brokerId, id, isin, currency, name, count)); } switch (price) { case < 0: throw new InvalidPriceException(); case 0: break; default: await _addExpense.Handle( date, user, brokerId, payAccountId, price, ExpenseCategory.BuyAsset, id); break; } switch (fee) { case < 0: throw new InvalidPriceException(); case 0: break; default: await _addExpense.Handle( date, user, brokerId, feeAccountId, fee, ExpenseCategory.BuyAssetFee, id); break; } return(id); }
public AssetMetadata GetMetadataWithFallback(AssetISIN isin, UserId user) { var metadata = GetMetadata(isin); return((metadata != null) ? metadata : GetFallbackMetadata(isin, user)); }
public IReadOnlyCollection <(DateTime, DateTime)> TryCalculateRequiredIntervals(AssetISIN isin) { var startDate = GetStartDate(isin); var endDate = GetEndDate(); _logger.LogTrace($"Interval for ISIN '{isin}' is {startDate}-{endDate}"); var result = new List <(DateTime, DateTime)>(); var diff = (endDate - startDate); if (diff < TimeSpan.FromDays(1)) { _logger.LogTrace("No processing required"); return(result); } var date = startDate; while (date < endDate) { var interval = (endDate - date); var nextDate = (interval.TotalDays < 365) ? endDate : date.AddDays(365); result.Add((date, nextDate)); date = nextDate.AddDays(1); } _logger.LogTrace( $"Interval splits into {result.Count} parts: {string.Join("; ", result.Select(r => r.ToString()))}"); return(result); }