public void ValidateInstrumentPositionVolume(IAccountAssetPair assetPair, Order order) { var existingPositionsVolume = _ordersCache.ActiveOrders.GetOrdersByInstrumentAndAccount(assetPair.Instrument, order.AccountId).Sum(o => o.Volume); if (assetPair.PositionLimit > 0 && Math.Abs(existingPositionsVolume + order.Volume) > assetPair.PositionLimit) { throw new ValidateOrderException(OrderRejectReason.InvalidVolume, $"Margin Trading is in beta testing. The volume of the net open position is temporarily limited to {assetPair.PositionLimit} {assetPair.Instrument}. Thank you for using Lykke Margin Trading, the limit will be cancelled soon!"); } }
public async Task <IAccountAssetPair> AddOrReplaceAccountAssetAsync(IAccountAssetPair model) { await _pairsRepository.AddOrReplaceAsync(model); await UpdateAccountAssetsCache(); await _clientNotifyService.NotifyTradingConditionsChanged(model.TradingConditionId); return(model); }
/// <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); }
public static AccountAssetPairModel ToBackendContract(this IAccountAssetPair src) { return(new AccountAssetPairModel { TradingConditionId = src.TradingConditionId, BaseAssetId = src.BaseAssetId, Instrument = src.Instrument, LeverageInit = src.LeverageInit, LeverageMaintenance = src.LeverageMaintenance, SwapLong = src.SwapLong, SwapShort = src.SwapShort, OvernightSwapLong = src.OvernightSwapLong, OvernightSwapShort = src.OvernightSwapShort, CommissionLong = src.CommissionLong, CommissionShort = src.CommissionShort, CommissionLot = src.CommissionLot, DeltaBid = src.DeltaBid, DeltaAsk = src.DeltaAsk, DealLimit = src.DealLimit, PositionLimit = src.PositionLimit }); }
public static AccountAssetPairEntity Create(IAccountAssetPair src) { return(new AccountAssetPairEntity { PartitionKey = GeneratePartitionKey(src.TradingConditionId, src.BaseAssetId), RowKey = GenerateRowKey(src.Instrument), TradingConditionId = src.TradingConditionId, BaseAssetId = src.BaseAssetId, LeverageInit = src.LeverageInit, LeverageMaintenance = src.LeverageMaintenance, SwapLong = (double)src.SwapLong, SwapShort = (double)src.SwapShort, OvernightSwapLong = (double)src.OvernightSwapLong, OvernightSwapShort = (double)src.OvernightSwapShort, CommissionLong = (double)src.CommissionLong, CommissionShort = (double)src.CommissionShort, CommissionLot = (double)src.CommissionLot, DeltaBid = (double)src.DeltaBid, DeltaAsk = (double)src.DeltaAsk, DealLimit = (double)src.DealLimit, PositionLimit = (double)src.PositionLimit }); }
private AccountAssetPairContract Convert(IAccountAssetPair accountAssetPair) { return(_convertService.Convert <IAccountAssetPair, AccountAssetPairContract>(accountAssetPair)); }
private AccountAssetPairContract Convert(IAccountAssetPair settings) { return(_convertService.Convert <IAccountAssetPair, AccountAssetPairContract>(settings)); }
public async Task AddOrReplaceAsync(IAccountAssetPair accountAssetPair) { await _tableStorage.InsertOrReplaceAsync(AccountAssetPairEntity.Create(accountAssetPair)); }
/// <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); }