Beispiel #1
0
        public async Task <ActionResult <Users> > RegisterUser(LoginInput input)
        {
            try
            {
                if (!_context.Users.Any(x => x.Username == input.username))
                {
                    DataSyncManager dataSyncManager = new DataSyncManager(_syncDbConnectionString);

                    byte[] salt = new byte[128 / 8];
                    using (var rng = RandomNumberGenerator.Create())
                    {
                        rng.GetBytes(salt);
                    }

                    Users newUser = new Users()
                    {
                        Username      = input.username,
                        Password      = PasswordHashHelper.GetHash(input.password, salt),
                        Salt          = salt,
                        EnvironmentId = RDSEnums.EnvironmentId.ProductionPrimary
                    };

                    _context.Users.Add(newUser);
                    await _context.SaveChangesAsync();

                    Users createdUser = await _context.Users.FirstOrDefaultAsync(x => x.Username == input.username);

                    Tokens newToken = new Tokens()
                    {
                        UserID        = createdUser.Id,
                        Token         = TokenHelper.GenerateToken(input.username, TokenHelper.TokenType.Standard),
                        EnvironmentId = RDSEnums.EnvironmentId.ProductionPrimary
                    };

                    Accounts newAccount = new Accounts()
                    {
                        UserID        = createdUser.Id,
                        Balance       = 1000000.99,
                        EnvironmentId = RDSEnums.EnvironmentId.ProductionPrimary
                    };

                    _context.Tokens.Add(newToken);
                    _context.Accounts.Add(newAccount);
                    await _context.SaveChangesAsync();

                    if (_dataSyncEnabled)
                    {
                        Users replicatedUser = new Users()
                        {
                            Username      = newUser.Username,
                            Password      = newUser.Password,
                            Salt          = newUser.Salt,
                            EnvironmentId = RDSEnums.EnvironmentId.ProductionSecondary
                        };

                        await dataSyncManager.SyncNewUser(replicatedUser, newToken.Token);
                    }

                    StatusOutput output = new StatusOutput()
                    {
                        message = StatusMessages.UserCreatedMessage
                    };
                    return(Ok(output));
                }
                else
                {
                    StatusOutput output = new StatusOutput()
                    {
                        message = StatusMessages.UserExistsMessage
                    };
                    return(Ok(output));
                }
            }
            catch (Exception e)
            {
                Log.Fatal(e, $"{nameof(RegisterUser)}");
                return(StatusCode(500));
            }
        }
        public static async Task <TradeExecOutput> ProcessTradingRequestUsingRdsFallback(Users user, Accounts account,
                                                                                         TradeExecInput input, PseudoMarketsDbContext context, MarketDataServiceClient marketDataService,
                                                                                         DateTimeHelper dateTimeHelper)
        {
            try
            {
                var transactionId = Guid.NewGuid().ToString();

                var latestPrice = await marketDataService.GetLatestPrice(input.Symbol);

                double price = latestPrice.price;

                double value = price * input.Quantity;
                bool   hasSufficientBalance = account.Balance >= value;

                TradeExecOutput output = new TradeExecOutput();

                // TODO: Extract trade logic into a separate static class
                if (hasSufficientBalance)
                {
                    if (input.Type.ToUpper() == "BUY" || input.Type.ToUpper() == "SELL" ||
                        input.Type.ToUpper() == "SELLSHORT")
                    {
                        if (price > 0 && input.Quantity > 0)
                        {
                            if (dateTimeHelper.IsMarketOpen())
                            {
                                Orders order = new Orders
                                {
                                    Symbol         = input.Symbol,
                                    Type           = input.Type,
                                    Price          = price,
                                    Quantity       = input.Quantity,
                                    Date           = DateTime.Now,
                                    TransactionID  = transactionId,
                                    EnvironmentId  = RDSEnums.EnvironmentId.RdsFallback,
                                    OriginId       = RDSEnums.OriginId.PseudoMarkets,
                                    SecurityTypeId = RDSEnums.SecurityType.RealWorld
                                };

                                Transactions transaction = new Transactions
                                {
                                    AccountId     = account.Id,
                                    TransactionId = transactionId,
                                    EnvironmentId = RDSEnums.EnvironmentId.RdsFallback,
                                    OriginId      = RDSEnums.OriginId.PseudoMarkets
                                };

                                context.Orders.Add(order);
                                context.Transactions.Add(transaction);
                                await context.SaveChangesAsync();

                                var createdOrder =
                                    await context.Orders.FirstOrDefaultAsync(x => x.TransactionID == transactionId);

                                if (input.Type.ToUpper() == "BUY")
                                {
                                    var doesAccountHaveExistingPosition =
                                        context.Positions.Any(x =>
                                                              x.AccountId == account.Id && x.Symbol == input.Symbol);
                                    if (doesAccountHaveExistingPosition)
                                    {
                                        var existingPosition = await context.Positions
                                                               .Where(x => x.AccountId == account.Id && x.Symbol == input.Symbol)
                                                               .FirstOrDefaultAsync();

                                        // Long position
                                        if (existingPosition.Quantity > 0)
                                        {
                                            existingPosition.Value               += value;
                                            existingPosition.Quantity            += input.Quantity;
                                            context.Entry(existingPosition).State = EntityState.Modified;
                                            account.Balance = account.Balance - value;
                                            context.Entry(account).State = EntityState.Modified;
                                            await context.SaveChangesAsync();
                                        }
                                        else // Short position
                                        {
                                            if (Math.Abs(existingPosition.Quantity) == input.Quantity)
                                            {
                                                double gainOrLoss = existingPosition.Value - value;
                                                account.Balance += gainOrLoss;
                                                context.Entry(account).State          = EntityState.Modified;
                                                context.Entry(existingPosition).State = EntityState.Deleted;
                                                await context.SaveChangesAsync();
                                            }
                                            else
                                            {
                                                existingPosition.Value                = existingPosition.Value - value;
                                                existingPosition.Quantity            += input.Quantity;
                                                account.Balance                      += existingPosition.Value - value;
                                                context.Entry(existingPosition).State = EntityState.Modified;
                                                context.Entry(account).State          = EntityState.Modified;
                                                await context.SaveChangesAsync();
                                            }
                                        }
                                    }
                                    else
                                    {
                                        Positions position = new Positions
                                        {
                                            AccountId      = account.Id,
                                            OrderId        = createdOrder.Id,
                                            Value          = value,
                                            Symbol         = input.Symbol,
                                            Quantity       = input.Quantity,
                                            EnvironmentId  = RDSEnums.EnvironmentId.RdsFallback,
                                            OriginId       = RDSEnums.OriginId.PseudoMarkets,
                                            SecurityTypeId = RDSEnums.SecurityType.RealWorld
                                        };
                                        context.Positions.Add(position);
                                        await context.SaveChangesAsync();

                                        account.Balance = account.Balance - value;
                                        context.Entry(account).State = EntityState.Modified;
                                        await context.SaveChangesAsync();
                                    }
                                }
                                else if (input.Type.ToUpper() == "SELL")
                                {
                                    var doesAccountHaveExistingPosition =
                                        context.Positions.Any(x =>
                                                              x.AccountId == account.Id && x.Symbol == input.Symbol);
                                    if (doesAccountHaveExistingPosition)
                                    {
                                        var existingPosition = await context.Positions
                                                               .Where(x => x.AccountId == account.Id && x.Symbol == input.Symbol)
                                                               .FirstOrDefaultAsync();

                                        if (input.Quantity == existingPosition.Quantity)
                                        {
                                            account.Balance = account.Balance + value;
                                            context.Entry(existingPosition).State = EntityState.Deleted;
                                            context.Entry(account).State          = EntityState.Modified;
                                            await context.SaveChangesAsync();
                                        }
                                        else
                                        {
                                            existingPosition.Value               -= value;
                                            existingPosition.Quantity            -= input.Quantity;
                                            context.Entry(existingPosition).State = EntityState.Modified;
                                            account.Balance = account.Balance + value;
                                            context.Entry(account).State = EntityState.Modified;
                                            await context.SaveChangesAsync();
                                        }
                                    }
                                    else
                                    {
                                        output.StatusCode    = TradeStatusCodes.ExecutionError;
                                        output.StatusMessage =
                                            StatusMessages.InvalidPositionsMessage + input.Symbol;
                                        return(output);
                                    }
                                }
                                else if (input.Type.ToUpper() == "SELLSHORT")
                                {
                                    var doesAccountHaveExistingPosition =
                                        context.Positions.Any(x =>
                                                              x.AccountId == account.Id && x.Symbol == input.Symbol);
                                    if (doesAccountHaveExistingPosition)
                                    {
                                        var existingPosition = await context.Positions
                                                               .Where(x => x.AccountId == account.Id && x.Symbol == input.Symbol)
                                                               .FirstOrDefaultAsync();

                                        if (existingPosition.Quantity > 0)
                                        {
                                            context.Entry(createdOrder).State = EntityState.Deleted;
                                            await context.SaveChangesAsync();

                                            output.StatusCode    = TradeStatusCodes.ExecutionError;
                                            output.StatusMessage =
                                                StatusMessages.InvalidShortPositionMessage;
                                            return(output);
                                        }

                                        existingPosition.Value               += value;
                                        existingPosition.Quantity            += input.Quantity * -1;
                                        context.Entry(existingPosition).State = EntityState.Modified;
                                        await context.SaveChangesAsync();
                                    }
                                    else
                                    {
                                        Positions position = new Positions
                                        {
                                            AccountId      = account.Id,
                                            OrderId        = createdOrder.Id,
                                            Value          = value,
                                            Symbol         = input.Symbol,
                                            Quantity       = input.Quantity * -1,
                                            EnvironmentId  = RDSEnums.EnvironmentId.RdsFallback,
                                            OriginId       = RDSEnums.OriginId.PseudoMarkets,
                                            SecurityTypeId = RDSEnums.SecurityType.RealWorld
                                        };

                                        context.Positions.Add(position);
                                        account.Balance = account.Balance - value;
                                        context.Entry(account).State = EntityState.Modified;
                                        await context.SaveChangesAsync();
                                    }
                                }

                                output.StatusCode    = TradeStatusCodes.ExecutionOk;
                                output.StatusMessage = StatusMessages.SuccessMessage;
                                output.Order         = createdOrder;
                                return(output);
                            }

                            CreateQueuedOrder(input, user.Id, context);
                            output.StatusMessage =
                                "Market is closed, order has been queued to be filled on next market open";
                            output.StatusCode = TradeStatusCodes.ExecutionQueued;
                            return(output);
                        }

                        output.StatusCode    = TradeStatusCodes.ExecutionError;
                        output.StatusMessage = StatusMessages.InvalidSymbolOrQuantityMessage;
                        return(output);
                    }

                    output.StatusCode    = TradeStatusCodes.ExecutionError;
                    output.StatusMessage = StatusMessages.InvalidOrderTypeMessage;
                    return(output);
                }

                output.StatusCode    = TradeStatusCodes.ExecutionError;
                output.StatusMessage = StatusMessages.InsufficientBalanceMessage;
                return(output);
            }
            catch (Exception e)
            {
                TradeExecOutput status = new TradeExecOutput
                {
                    Order         = null,
                    StatusCode    = TradeStatusCodes.ExecutionError,
                    StatusMessage = StatusMessages.FailureMessage
                };
                Log.Fatal(e, $"{nameof(ProcessTradingRequestUsingRdsFallback)}");
                return(status);
            }
        }