/// <summary> /// Calculate overnight swaps for account/instrument/direction order package. /// </summary> /// <param name="instrument"></param> /// <param name="account"></param> /// <param name="accountAssetPair"></param> /// <param name="direction"></param> /// <param name="orders"></param> /// <returns></returns> private async Task ProcessOrders(IReadOnlyList <Order> orders, string instrument, IMarginTradingAccount account, IAccountAssetPair accountAssetPair, OrderDirection direction) { //check if swaps had already been taken if (_overnightSwapCache.TryGet(OvernightSwapCalculation.GetKey(account.Id, instrument, direction), out var lastCalc) && lastCalc.Time > CalcLastInvocationTime) { throw new Exception($"Overnight swaps had already been taken: {JsonConvert.SerializeObject(lastCalc)}"); } //calc swaps var swapRate = direction == OrderDirection.Buy ? accountAssetPair.OvernightSwapLong : accountAssetPair.OvernightSwapShort; if (swapRate == 0) { return; } var total = orders.Sum(order => _commissionService.GetOvernightSwap(order, swapRate)); if (total == 0) { return; } //create calculation obj & add to cache var calculation = OvernightSwapCalculation.Create(account.Id, instrument, orders.Select(order => order.Id).ToList(), _currentStartTimestamp, true, null, total, swapRate, direction); _overnightSwapCache.AddOrReplace(calculation); //charge comission await _accountManager.UpdateBalanceAsync( account : account, amount : -total, historyType : AccountHistoryType.Swap, comment : $"{accountAssetPair.Instrument} {(direction == OrderDirection.Buy ? "long" : "short")} swaps. Positions count: {orders.Count}. Rate: {swapRate}. Time: {_currentStartTimestamp:u}.", auditLog : calculation.ToJson()); //write state and log await _overnightSwapStateRepository.AddOrReplaceAsync(calculation); await _overnightSwapHistoryRepository.AddAsync(calculation); }
/// <summary> /// Calculate overnight swaps for account order. /// </summary> /// <param name="account"></param> /// <param name="accountAssetPair"></param> /// <param name="order"></param> /// <returns></returns> private async Task ProcessOrder(Order order, IMarginTradingAccount account, IAccountAssetPair accountAssetPair) { //check if swaps had already been taken var lastCalcExists = _overnightSwapCache.TryGet(OvernightSwapCalculation.GetKey(order.Id), out var lastCalc) && lastCalc.Time >= CalcLastInvocationTime(); if (lastCalcExists) { await _log.WriteErrorAsync(nameof(OvernightSwapService), nameof(ProcessOrder), new Exception($"Overnight swap had already been taken, filtering: {JsonConvert.SerializeObject(lastCalc)}"), DateTime.UtcNow); } //calc swaps var swapRate = order.GetOrderType() == OrderDirection.Buy ? accountAssetPair.OvernightSwapLong : accountAssetPair.OvernightSwapShort; if (swapRate == 0) { await _log.WriteInfoAsync(nameof(OvernightSwapService), nameof(ProcessOrder), $"Overnight swap rate on order {order.Id } is 0, what will not be calculated", DateTime.UtcNow); return; } //special rule for Wednesday if (_currentStartTimestamp.DayOfWeek == DayOfWeek.Wednesday) { swapRate *= 3; } var total = _commissionService.GetOvernightSwap(order, swapRate); if (total == 0) { await _log.WriteInfoAsync(nameof(OvernightSwapService), nameof(ProcessOrder), $"Overnight swap rate on order {order.Id } is 0, what will not be calculated", DateTime.UtcNow); return; } //create calculation obj & add to cache var volume = order.Volume; var calculation = OvernightSwapCalculation.Create(account.ClientId, account.Id, order.Instrument, order.Id, _currentStartTimestamp, true, null, volume, total, swapRate, order.GetOrderType()); await _accountManager.UpdateBalanceAsync( account : account, amount : -total, historyType : AccountHistoryType.Swap, comment : $"Position {order.Id} swaps. Time: {_currentStartTimestamp:u}.", auditLog : calculation.ToJson(), eventSourceId : order.Id); //update calculation state if previous existed var newCalcState = lastCalcExists ? OvernightSwapCalculation.Update(calculation, lastCalc) : OvernightSwapCalculation.Create(calculation); //add to cache _overnightSwapCache.AddOrReplace(newCalcState); //write state and log await _overnightSwapStateRepository.AddOrReplaceAsync(newCalcState); await _overnightSwapHistoryRepository.AddAsync(calculation); }
/// <summary> /// Calculate overnight swaps for account/instrument/direction order package. /// </summary> /// <param name="instrument"></param> /// <param name="account"></param> /// <param name="accountAssetPair"></param> /// <param name="direction"></param> /// <param name="orders"></param> /// <returns></returns> private async Task ProcessOrders(IReadOnlyList <Order> orders, string instrument, IMarginTradingAccount account, IAccountAssetPair accountAssetPair, OrderDirection direction) { IReadOnlyList <Order> filteredOrders = orders.ToList(); //check if swaps had already been taken var lastCalcExists = _overnightSwapCache.TryGet(OvernightSwapCalculation.GetKey(account.Id, instrument, direction), out var lastCalc) && lastCalc.Time >= CalcLastInvocationTime(); if (lastCalcExists) { await _log.WriteErrorAsync(nameof(OvernightSwapService), nameof(ProcessOrders), new Exception($"Overnight swaps had already been taken, filtering: {JsonConvert.SerializeObject(lastCalc)}"), DateTime.UtcNow); filteredOrders = orders.Where(x => !lastCalc.OpenOrderIds.Contains(x.Id)).ToList(); } //calc swaps var swapRate = direction == OrderDirection.Buy ? accountAssetPair.OvernightSwapLong : accountAssetPair.OvernightSwapShort; if (swapRate == 0) { return; } var total = filteredOrders.Sum(order => _commissionService.GetOvernightSwap(order, swapRate)); if (total == 0) { return; } //create calculation obj & add to cache var volume = filteredOrders.Select(x => Math.Abs(x.Volume)).Sum(); var calculation = OvernightSwapCalculation.Create(account.ClientId, account.Id, instrument, filteredOrders.Select(order => order.Id).ToList(), _currentStartTimestamp, true, null, volume, total, swapRate, direction); //charge comission var instrumentName = _assetPairsCache.GetAssetPairByIdOrDefault(accountAssetPair.Instrument)?.Name ?? accountAssetPair.Instrument; await _accountManager.UpdateBalanceAsync( account : account, amount : -total, historyType : AccountHistoryType.Swap, comment : $"{instrumentName} {(direction == OrderDirection.Buy ? "long" : "short")} swaps. Volume: {volume}. Positions count: {filteredOrders.Count}. Rate: {swapRate}. Time: {_currentStartTimestamp:u}.", auditLog : calculation.ToJson()); //update calculation state if previous existed var newCalcState = lastCalcExists ? OvernightSwapCalculation.Update(calculation, lastCalc) : OvernightSwapCalculation.Create(calculation); //add to cache _overnightSwapCache.AddOrReplace(newCalcState); //write state and log await _overnightSwapStateRepository.AddOrReplaceAsync(newCalcState); await _overnightSwapHistoryRepository.AddAsync(calculation); }