private AccountsProjection AssertEnv(string accountId = null, string failMessage = null) { _accountBalanceChangedEventChannelMock = new Mock <IEventChannel <AccountBalanceChangedEventArgs> >(); _accountUpdateServiceMock = new Mock <IAccountUpdateService>(); _accountUpdateServiceMock.Setup(s => s.UnfreezeWithdrawalMargin(It.Is <string>(x => x == accountId), "test")) .Returns(Task.CompletedTask); _operationExecutionInfoRepositoryMock = new Mock <IOperationExecutionInfoRepository>(); _operationExecutionInfoRepositoryMock.Setup(s => s.GetOrAddAsync(It.Is <string>(x => x == "AccountsProjection"), It.IsAny <string>(), It.IsAny <Func <IOperationExecutionInfo <OperationData> > >())) .ReturnsAsync(() => new OperationExecutionInfo <OperationData>( operationName: "AccountsProjection", id: Guid.NewGuid().ToString(), lastModified: DateService.Now(), data: new OperationData { State = OperationState.Initiated } )); _logMock = new Mock <ILog>(); if (failMessage != null) { _logCounter = 0; _logMock.Setup(s => s.WriteInfoAsync(It.IsAny <string>(), It.IsAny <string>(), It.Is <string>(x => x == failMessage), It.IsAny <DateTime?>())) .Callback(() => _logCounter++).Returns(Task.CompletedTask); _logMock.Setup(s => s.WriteWarningAsync(It.IsAny <string>(), It.IsAny <string>(), It.Is <string>(x => x == failMessage), It.IsAny <DateTime?>())) .Callback(() => _logCounter++).Returns(Task.CompletedTask); } _accountsCacheService = new AccountsCacheService(DateService, _logMock.Object); _accountsCacheService.TryAddNew(Convert(Accounts[0])); MtServiceLocator.AccountsCacheService = _accountsCacheService; _ordersCache = new OrdersCache(); _fakePosition = new Mock <Position>(); _fakePosition.SetupProperty(s => s.Id, "test"); _fakePosition.SetupProperty(s => s.AccountId, Accounts[0].Id); _fakePosition.SetupProperty(s => s.AssetPairId, "test"); _fakePosition.SetupProperty(s => s.FxAssetPairId, "test"); _fakePosition.SetupProperty(s => s.ChargePnlOperations, new HashSet <string>()); _fakePosition.Setup(s => s.ChargePnL(It.Is <string>(x => x == "test"), It.IsAny <decimal>())); _ordersCache.Positions.Add(_fakePosition.Object); return(new AccountsProjection(_accountsCacheService, _accountBalanceChangedEventChannelMock.Object, ConvertService, _accountUpdateServiceMock.Object, DateService, _operationExecutionInfoRepositoryMock.Object, Mock.Of <IChaosKitty>(), _ordersCache, _logMock.Object)); }
public async Task Handle(AccountChangedEvent e) { var executionInfo = await _operationExecutionInfoRepository.GetOrAddAsync( operationName : OperationName, operationId : e.OperationId, factory : () => new OperationExecutionInfo <OperationData>( operationName: OperationName, id: e.OperationId, lastModified: _dateService.Now(), data: new OperationData { State = OperationState.Initiated } )); if (executionInfo.Data.SwitchState(OperationState.Initiated, OperationState.Finished)) { var updatedAccount = Convert(e.Account); switch (e.EventType) { case AccountChangedEventTypeContract.Created: _accountsCacheService.TryAddNew(MarginTradingAccount.Create(updatedAccount)); break; case AccountChangedEventTypeContract.Updated: { var account = _accountsCacheService.TryGet(e.Account.Id); if (await ValidateAccount(account, e) && await _accountsCacheService.UpdateAccountChanges(updatedAccount.Id, updatedAccount.TradingConditionId, updatedAccount.WithdrawTransferLimit, updatedAccount.IsDisabled, updatedAccount.IsWithdrawalDisabled, e.ChangeTimestamp)) { _accountUpdateService.RemoveLiquidationStateIfNeeded(e.Account.Id, "Trading conditions changed"); } break; } case AccountChangedEventTypeContract.BalanceUpdated: { if (e.BalanceChange != null) { var account = _accountsCacheService.TryGet(e.Account.Id); if (await ValidateAccount(account, e)) { switch (e.BalanceChange.ReasonType) { case AccountBalanceChangeReasonTypeContract.Withdraw: await _accountUpdateService.UnfreezeWithdrawalMargin(updatedAccount.Id, e.BalanceChange.Id); break; case AccountBalanceChangeReasonTypeContract.UnrealizedDailyPnL: if (_ordersCache.Positions.TryGetPositionById(e.BalanceChange.EventSourceId, out var position)) { position.ChargePnL(e.BalanceChange.Id, e.BalanceChange.ChangeAmount); } else { _log.WriteWarning("AccountChangedEvent Handler", e.ToJson(), $"Position [{e.BalanceChange.EventSourceId} was not found]"); } break; case AccountBalanceChangeReasonTypeContract.RealizedPnL: await _accountUpdateService.UnfreezeUnconfirmedMargin(e.Account.Id, e.BalanceChange.EventSourceId); break; } if (await _accountsCacheService.UpdateAccountBalance(updatedAccount.Id, e.BalanceChange.Balance, e.ChangeTimestamp)) { _accountUpdateService.RemoveLiquidationStateIfNeeded(e.Account.Id, "Balance updated"); _accountBalanceChangedEventChannel.SendEvent(this, new AccountBalanceChangedEventArgs(updatedAccount)); } } } else { _log.WriteWarning("AccountChangedEvent Handler", e.ToJson(), "BalanceChange info is empty"); } break; } case AccountChangedEventTypeContract.Deleted: //account deletion from cache is double-handled by CQRS flow _accountsCacheService.Remove(e.Account.Id); break; default: await _log.WriteErrorAsync(nameof(AccountsProjection), nameof(AccountChangedEvent), e.ToJson(), new Exception("AccountChangedEventTypeContract was in incorrect state")); break; } _chaosKitty.Meow(e.OperationId); await _operationExecutionInfoRepository.Save(executionInfo); } }
public void OneTimeSetUp() { RegisterDependencies(); _accountsCacheService = Container.Resolve <IAccountsCacheService>(); _matchingEngineRoutesManager = Container.Resolve <MatchingEngineRoutesManager>(); if (_matchingEngineRoutesManager == null) { throw new Exception("Unable to resolve MatchingEngineRoutesCacheService"); } // Add user accounts _accountsCacheService.TryAddNew(new MarginTradingAccount { Id = "CLIENT001" }); _accountsCacheService.TryAddNew(new MarginTradingAccount { Id = "CLIENT002" }); _accountsCacheService.TryAddNew(new MarginTradingAccount { Id = "CLIENT003" }); _accountsCacheService.TryAddNew(new MarginTradingAccount { Id = "CLIENT004" }); // Add trading conditions //_tradingConditionsCache.AddOrUpdateTradingCondition(new TradingCondition() { Id = "TCID001", Name= "MarginTradingCondition 1", IsDefault = true }); //_tradingConditionsCache.AddOrUpdateTradingCondition(new TradingCondition() { Id = "TCID003", Name = "MarginTradingCondition 3", IsDefault = false}); //_tradingConditionsCache.AddOrUpdateTradingCondition(new TradingCondition() { Id = "TCID004", Name = "MarginTradingCondition 4", IsDefault = false }); //_tradingConditionsCache.AddOrUpdateTradingCondition(new TradingCondition() { Id = "TCID005", Name = "MarginTradingCondition 5", IsDefault = false }); System.Threading.Tasks.Task.Run(async() => { await _matchingEngineRoutesManager.AddOrReplaceRouteInCacheAsync(new MatchingEngineRoute() { Id = "1", Rank = 10, MatchingEngineId = "LYKKE" }); await _matchingEngineRoutesManager.AddOrReplaceRouteInCacheAsync(new MatchingEngineRoute() { Id = "2", Rank = 5, Instrument = "BTCUSD", MatchingEngineId = "ICM" }); await _matchingEngineRoutesManager.AddOrReplaceRouteInCacheAsync(new MatchingEngineRoute() { Id = "3", Rank = 4, TradingConditionId = "TCID001", Instrument = "EURCHF", Type = OrderDirection.Buy, MatchingEngineId = "LYKKE" }); await _matchingEngineRoutesManager.AddOrReplaceRouteInCacheAsync(new MatchingEngineRoute() { Id = "4", Rank = 3, TradingConditionId = "TCID001", ClientId = "CLIENT001", Instrument = "EURCHF", Type = OrderDirection.Buy, MatchingEngineId = "ICM" }); await _matchingEngineRoutesManager.AddOrReplaceRouteInCacheAsync(new MatchingEngineRoute() { Id = "5", Rank = 4, TradingConditionId = "TCID001", ClientId = "CLIENT002", Instrument = "EURCHF", Type = OrderDirection.Buy, MatchingEngineId = "LYKKE" }); await _matchingEngineRoutesManager.AddOrReplaceRouteInCacheAsync(new MatchingEngineRoute() { Id = "6", Rank = 4, Instrument = "EURCHF", Type = OrderDirection.Buy, MatchingEngineId = "LYKKE" }); await _matchingEngineRoutesManager.AddOrReplaceRouteInCacheAsync(new MatchingEngineRoute() { Id = "7", Rank = 4, Instrument = "EURCHF", MatchingEngineId = "LYKKE" }); await _matchingEngineRoutesManager.AddOrReplaceRouteInCacheAsync(new MatchingEngineRoute() { Id = "8", Rank = 5, ClientId = "CLIENT003", Instrument = "EURCHF", MatchingEngineId = "LYKKE" }); await _matchingEngineRoutesManager.AddOrReplaceRouteInCacheAsync(new MatchingEngineRoute() { Id = "9", Rank = 6, TradingConditionId = "TCID003", ClientId = "CLIENT002", Instrument = "EURCHF", Type = OrderDirection.Sell, MatchingEngineId = "ICM" }); await _matchingEngineRoutesManager.AddOrReplaceRouteInCacheAsync(new MatchingEngineRoute() { Id = "10", Rank = 4, TradingConditionId = "TCID004", ClientId = "CLIENT004", Instrument = "EURJPY", MatchingEngineId = "ICM" }); await _matchingEngineRoutesManager.AddOrReplaceRouteInCacheAsync(new MatchingEngineRoute() { Id = "11", Rank = 4, TradingConditionId = "TCID004", Instrument = "EURJPY", Type = OrderDirection.Buy, MatchingEngineId = "LYKKE" }); await _matchingEngineRoutesManager.AddOrReplaceRouteInCacheAsync(new MatchingEngineRoute() { Id = "12", Rank = 4, TradingConditionId = "TCID005", Instrument = "EURUSD", Type = OrderDirection.Buy, MatchingEngineId = "LYKKE" }); await _matchingEngineRoutesManager.AddOrReplaceRouteInCacheAsync(new MatchingEngineRoute() { Id = "13", Rank = 4, TradingConditionId = "TCID005", Instrument = "EURUSD", Type = OrderDirection.Buy, MatchingEngineId = "ICM" }); await _matchingEngineRoutesManager.AddOrReplaceRouteInCacheAsync(new MatchingEngineRoute() { Id = "14", Rank = 9, Instrument = "BTCEUR", MatchingEngineId = "LYKKE" }); await _matchingEngineRoutesManager.AddOrReplaceRouteInCacheAsync(new MatchingEngineRoute() { Id = "15", Rank = 7, Type = OrderDirection.Buy, MatchingEngineId = "ICM", Asset = "EUR" }); await _matchingEngineRoutesManager.AddOrReplaceRouteInCacheAsync(new MatchingEngineRoute() { Id = "16", Rank = 8, Type = OrderDirection.Sell, MatchingEngineId = "ICM", Asset = "EUR" }); }).Wait(); /* TABLE PROTOTYPE * Id Rank tradingConditionId clientId instrument type matchingEngineId asset * * 1 10 * * * * LYKKE * 2 5 * * BTCUSD * ICM * 3 4 TCID001 * EURCHF Buy LYKKE * 4 3 TCID001 CLIENT001 EURCHF Buy ICM * 5 4 TCID001 CLIENT002 EURCHF Buy LYKKE * 6 4 * * EURCHF Buy LYKKE * 7 4 * * EURCHF * LYKKE * 8 5 * CLIENT003 EURCHF * LYKKE * 9 6 TCID003 CLIENT002 EURCHF Sell ICM * 10 4 TCID004 CLIENT004 EURJPY * ICM * 11 4 TCID004 * EURJPY Buy LYKKE * 12 4 TCID005 * EURUSD Buy LYKKE * 13 4 TCID005 * EURUSD Buy ICM * 14 9 * * BTCEUR * LYKKE * 15 7 * * Buy ICM EUR * 16 8 * * Buy LYKKE EUR * * */ }