Пример #1
0
        public void BuyOffer(String stock, int shares, int collCode)
        {
            bool stockBought = false;

            foreach (StockOffer offer in stockSaleOffers)
            {
                if ((offer.GetStockSymbol == stock) && (offer.GetstockShares == shares))
                {
                    Console.WriteLine(shares + " shares of " + stock +
                                      " bought by colleague code " + offer.GetCollCode);

                    stockSaleOffers.Remove(offer);

                    stockBought = true;
                }

                if (stockBought)
                {
                    break;
                }
            }

            if (!stockBought)
            {
                Console.WriteLine(shares + " shares of " + stock +
                                  " added to inventory");

                StockOffer newOffering = new StockOffer(shares, stock, collCode);

                stockBuyOffers.Add(newOffering);
            }
        }
Пример #2
0
        public static async Task <StockOffer> GetHighestBuyOffer(string ticker, VooperContext context)
        {
            StockOffer highest = await context.StockOffers
                                 .AsQueryable()
                                 .Where(s => s.Ticker == ticker && s.Order_Type == "BUY")
                                 .OrderByDescending(s => s.Target)
                                 .FirstOrDefaultAsync();

            return(highest);
        }
Пример #3
0
        public static async Task <StockOffer> GetLowestSellOffer(string ticker, VooperContext context)
        {
            StockOffer lowest = await context.StockOffers
                                .AsQueryable()
                                .Where(s => s.Ticker == ticker && s.Order_Type == "SELL")
                                .OrderBy(s => s.Target)
                                .FirstOrDefaultAsync();

            return(lowest);
        }
Пример #4
0
        public async Task <ActionResult <decimal> > GetStockBuyPrice(string ticker)
        {
            decimal    lowest   = 0;
            StockOffer lowOffer = await ExchangeManager.GetLowestSellOffer(ticker, _context);

            if (lowOffer != null)
            {
                lowest = lowOffer.Target;
            }

            if (lowest == 0)
            {
                return(NotFound($"No stocks with ticker {ticker} are for sale."));
            }

            return(lowest);
        }
Пример #5
0
        public async Task <ActionResult <TaskResult> > CancelOrder(string orderid, string accountid, string auth)
        {
            // Account logic first
            Entity account = await Entity.FindAsync(accountid);

            if (account == null)
            {
                return(new TaskResult(false, "Failed to find account " + accountid));
            }

            User authUser = await _context.Users.AsQueryable().FirstOrDefaultAsync(u => u.Api_Key == auth);

            if (authUser == null)
            {
                return(new TaskResult(false, "Failed to find auth account."));
            }

            StockOffer stockOffer = await _context.StockOffers.FindAsync(orderid);

            if (stockOffer == null)
            {
                return(new TaskResult(false, "Failed to find offer " + orderid));
            }

            // Authority check
            if (!await account.HasPermissionAsync(authUser, "eco"))
            {
                return(new TaskResult(false, "You do not have permission to trade for this entity!"));
            }

            if (stockOffer.Order_Type == "BUY")
            {
                _context.StockOffers.Remove(stockOffer);

                await _context.SaveChangesAsync();

                // Refund user and delete offer
                decimal refund = stockOffer.Amount * stockOffer.Target;

                TransactionRequest transaction = new TransactionRequest(EconomyManager.VooperiaID, account.Id, refund, $"Stock buy cancellation: {stockOffer.Amount} {stockOffer.Ticker}@{stockOffer.Target}", ApplicableTax.None, true);
                TaskResult         transResult = await transaction.Execute();

                if (!transResult.Succeeded)
                {
                    return(transResult);
                }
            }
            else if (stockOffer.Order_Type == "SELL")
            {
                _context.StockOffers.Remove(stockOffer);

                await _context.SaveChangesAsync();

                // Refund user stock and delete offer
                await ExchangeManager.AddStock(stockOffer.Ticker, stockOffer.Amount, accountid, _context);
            }

            string json = JsonConvert.SerializeObject(stockOffer);

            await ExchangeHub.Current.Clients.All.SendAsync("StockOfferCancel", json);

            return(new TaskResult(true, "Successfully removed order."));
        }
Пример #6
0
        public async Task <ActionResult <TaskResult> > SubmitStockSell(string ticker, int count, decimal price, string accountid, string auth)
        {
            // Account logic first
            Entity account = await Entity.FindAsync(accountid);

            if (account == null)
            {
                return(new TaskResult(false, "Failed to find account " + accountid));
            }

            User authUser = await _context.Users.AsQueryable().FirstOrDefaultAsync(u => u.Api_Key == auth);

            if (authUser == null)
            {
                return(new TaskResult(false, "Failed to find auth account."));
            }

            StockDefinition stockDef = await _context.StockDefinitions.FindAsync(ticker.ToUpper());

            if (stockDef == null)
            {
                return(new TaskResult(false, "Failed to find stock with ticker " + ticker));
            }

            // Authority check
            if (!await account.HasPermissionAsync(authUser, "eco"))
            {
                return(new TaskResult(false, "You do not have permission to trade for this entity!"));
            }

            if (count < 1)
            {
                return(new TaskResult(false, "You must sell a positive number of stocks!"));
            }

            // At this point the account is authorized //
            // Find currently owned stock
            var owned = await _context.StockObjects.AsQueryable().FirstOrDefaultAsync(s => s.Owner_Id == accountid && s.Ticker == ticker);

            if (owned == null || count > owned.Amount)
            {
                return(new TaskResult(false, "You don't own that many stocks!"));
            }

            if (price < 0)
            {
                return(new TaskResult(false, "Negatives are not allowed!"));
            }

            if (price == 0)
            {
                StockOffer highOffer = await ExchangeManager.GetHighestBuyOffer(ticker, _context);

                if (highOffer != null)
                {
                    price = highOffer.Target;
                }
                else
                {
                    return(new TaskResult(false, "There is no market rate!"));
                }
            }

            decimal totalPrice = count * price;

            // Perform transaction
            owned.Amount -= count;

            // If amount hit 0, remove the object
            if (owned.Amount == 0)
            {
                _context.StockObjects.Remove(owned);
            }
            else
            {
                _context.StockObjects.Update(owned);
            }

            // Create offer
            StockOffer sellOffer = new StockOffer()
            {
                Id         = Guid.NewGuid().ToString(),
                Amount     = count,
                Order_Type = "SELL",
                Owner_Name = account.Name,
                Owner_Id   = account.Id,
                Target     = price.Round(),
                Ticker     = ticker
            };

            _context.StockOffers.Add(sellOffer);
            await _context.SaveChangesAsync();

            string json = JsonConvert.SerializeObject(sellOffer);

            await ExchangeHub.Current.Clients.All.SendAsync("StockOffer", json);

            return(new TaskResult(true, $"Successfully posted SELL order {sellOffer.Id}"));
        }
Пример #7
0
        public async Task <ActionResult <TaskResult> > SubmitStockBuy(string ticker, int count, decimal price, string accountid, string auth)
        {
            // Account logic first
            Entity account = await Entity.FindAsync(accountid);

            if (account == null)
            {
                return(new TaskResult(false, "Failed to find account " + accountid));
            }

            User authUser = await _context.Users.AsQueryable().FirstOrDefaultAsync(u => u.Api_Key == auth);

            if (authUser == null)
            {
                return(new TaskResult(false, "Failed to find auth account."));
            }

            StockDefinition stockDef = await _context.StockDefinitions.FindAsync(ticker.ToUpper());

            if (stockDef == null)
            {
                return(new TaskResult(false, "Failed to find stock with ticker " + ticker));
            }

            // Authority check
            if (!await account.HasPermissionAsync(authUser, "eco"))
            {
                return(new TaskResult(false, "You do not have permission to trade for this entity!"));
            }

            // At this point the account is authorized //
            if (count < 1)
            {
                return(new TaskResult(false, "You must buy a positive number of stocks!"));
            }

            if (price == 0)
            {
                StockOffer lowOffer = await ExchangeManager.GetLowestSellOffer(ticker, _context);

                if (lowOffer != null)
                {
                    price = lowOffer.Target;
                }
                else
                {
                    return(new TaskResult(false, "There is no market rate!"));
                }
            }

            if (price < 0)
            {
                return(new TaskResult(false, "Negatives are not allowed!"));
            }

            decimal totalPrice = count * price;

            if (totalPrice > account.Credits)
            {
                return(new TaskResult(false, "You cannot afford this!"));
            }

            TransactionRequest transaction = new TransactionRequest(account.Id, EconomyManager.VooperiaID, totalPrice, $"Stock purchase: {count} {ticker}@{price}", ApplicableTax.None, false);
            TaskResult         transResult = await transaction.Execute();

            if (!transResult.Succeeded)
            {
                return(transResult);
            }

            // Create offer
            StockOffer buyOffer = new StockOffer()
            {
                Id         = Guid.NewGuid().ToString(),
                Amount     = count,
                Order_Type = "BUY",
                Owner_Name = account.Name,
                Owner_Id   = account.Id,
                Target     = price.Round(),
                Ticker     = ticker
            };

            _context.StockOffers.Add(buyOffer);
            await _context.SaveChangesAsync();

            string json = JsonConvert.SerializeObject(buyOffer);

            await ExchangeHub.Current.Clients.All.SendAsync("StockOffer", json);

            return(new TaskResult(true, $"Successfully posted BUY order {buyOffer.Id}"));
        }
Пример #8
0
        public static async Task RunTrades()
        {
#if DEBUG
            // Prevent local testing from running the stock exchange
            return;
#endif
            using (VooperContext context = new VooperContext(VooperContext.DBOptions))
            {
                foreach (StockDefinition def in context.StockDefinitions)
                {
                    StockOffer sell = await GetLowestSellOffer(def.Ticker, context);

                    StockOffer buy = await GetHighestBuyOffer(def.Ticker, context);

                    // If buy > sell, trade occurs
                    if (buy != null &&
                        sell != null &&
                        buy.Target >= sell.Target)
                    {
                        GovControls gov = await context.GovControls.AsQueryable().FirstAsync();

                        int remainder = buy.Amount - sell.Amount;

                        decimal beforePrice = def.Current_Value;

                        decimal tradePrice = buy.Target;

                        int tradeAmount = Math.Min(buy.Amount, sell.Amount);

                        string buyer  = buy.Owner_Id;
                        string seller = sell.Owner_Id;

                        string buyId  = buy.Id;
                        string sellId = sell.Id;

                        // If remainder is 0, they cancel out perfectly
                        // If remainder > 0, the buy order is not finished and sell is deleted
                        // If remainder < 0, the sell order is not finished and the buy is deleted
                        if (remainder == 0)
                        {
                            context.StockOffers.Remove(sell);
                            context.StockOffers.Remove(buy);
                        }
                        else if (remainder > 0)
                        {
                            context.StockOffers.Remove(sell);
                            buy.Amount = buy.Amount - sell.Amount;
                            context.StockOffers.Update(buy);
                        }
                        else
                        {
                            context.StockOffers.Remove(buy);
                            sell.Amount = sell.Amount - buy.Amount;
                            context.StockOffers.Update(sell);
                        }

                        // Volume stuff
                        if (VolumesMinute.ContainsKey(def.Ticker))
                        {
                            VolumesMinute[def.Ticker] += tradeAmount;
                            VolumesHour[def.Ticker]   += tradeAmount;
                            VolumesDay[def.Ticker]    += tradeAmount;
                        }
                        else
                        {
                            VolumesMinute.Add(def.Ticker, tradeAmount);
                            VolumesHour.Add(def.Ticker, tradeAmount);
                            VolumesDay.Add(def.Ticker, tradeAmount);
                        }
                        // End volume stuff

                        decimal totalTrade = tradeAmount * tradePrice;
                        decimal tax        = totalTrade * (gov.CapitalGainsTaxRate / 100);

                        await new TransactionRequest(EconomyManager.VooperiaID, sell.Owner_Id, totalTrade - tax, $"Stock sale: {tradeAmount} {def.Ticker}@¢{tradePrice.Round()}", ApplicableTax.None, true).Execute();
                        gov.CapitalGainsTaxRevenue += tax;
                        gov.UBIAccount             += (tax * (gov.UBIBudgetPercent / 100.0m));

                        context.GovControls.Update(gov);

                        await context.SaveChangesAsync();

                        await AddStock(def.Ticker, tradeAmount, buyer, context);

                        Console.WriteLine($"Processed Stock sale: {tradeAmount} {def.Ticker}@¢{tradePrice.Round()}");

                        decimal trueValue = 0.0m;

                        StockOffer nextBuy = await GetHighestBuyOffer(def.Ticker, context);

                        StockOffer nextSell = await GetLowestSellOffer(def.Ticker, context);

                        if (nextBuy == null && nextSell == null)
                        {
                            trueValue = tradePrice;
                        }
                        else if (nextBuy == null)
                        {
                            trueValue = nextSell.Target;
                        }
                        else if (nextSell == null)
                        {
                            trueValue = nextBuy.Target;
                        }
                        else
                        {
                            trueValue = (nextBuy.Target + nextSell.Target) / 2;
                        }

                        StockTradeModel noti = new StockTradeModel()
                        {
                            Ticker     = def.Ticker,
                            Amount     = tradeAmount,
                            Price      = tradePrice,
                            True_Price = trueValue,
                            From       = sell.Owner_Id,
                            To         = buy.Owner_Id,
                            Buy_Id     = buyId,
                            Sell_Id    = sellId
                        };

                        def.Current_Value = noti.True_Price;
                        context.StockDefinitions.Update(def);

                        Entity sellAccount = await Entity.FindAsync(seller);

                        sellAccount.Credits_Invested -= totalTrade;
                        if (sellAccount is User)
                        {
                            context.Users.Update((User)sellAccount);
                        }
                        else if (sellAccount is Group)
                        {
                            context.Groups.Update((Group)sellAccount);
                        }


                        Entity buyAccount = await Entity.FindAsync(buyer);

                        buyAccount.Credits_Invested += totalTrade;
                        if (buyAccount is User)
                        {
                            context.Users.Update((User)buyAccount);
                        }
                        else if (buyAccount is Group)
                        {
                            context.Groups.Update((Group)buyAccount);
                        }

                        await context.SaveChangesAsync();

                        await ExchangeHub.Current.Clients.All.SendAsync("StockTrade", JsonConvert.SerializeObject(noti));

                        if (trueValue < beforePrice)
                        {
                            VoopAI.ecoChannel.SendMessageAsync($":chart_with_downwards_trend: ({def.Ticker}) Trade: {tradeAmount}@{buy.Target}, price drop to ¢{trueValue.Round()}");
                        }
                        else if (trueValue > beforePrice)
                        {
                            VoopAI.ecoChannel.SendMessageAsync($":chart_with_upwards_trend: ({def.Ticker}) Trade: {tradeAmount}@{buy.Target}, price increase to ¢{trueValue.Round()}");
                        }
                    }

                    // Otherwise move on to the next stock
                }
            }
        }
Пример #9
0
        public async Task <IActionResult> ListNewStock(CreateStockModel model)
        {
            // Validate model
            if (!ModelState.IsValid)
            {
                return(View());
            }

            // Additional validations
            Group group = await _context.Groups.FindAsync(model.Group_Id);

            if (group == null)
            {
                StatusMessage = $"Failed: Could not find group {model.Group_Id}";
                return(View());
            }

            // Check if group already has a stock
            if (_context.StockDefinitions.Any(s => s.Group_Id == model.Group_Id))
            {
                StatusMessage = $"Failed: {group.Name} already has a stock!";
                return(View());
            }

            if (model.Amount < 0)
            {
                StatusMessage = $"Failed: Amount must be positive!";
                return(View());
            }

            if (model.Keep < 0)
            {
                StatusMessage = $"Failed: Keep must be positive!";
                return(View());
            }

            // Check if ticker is taken
            if (_context.StockDefinitions.Any(s => s.Ticker == model.Ticker))
            {
                StatusMessage = $"Failed: A ticker {model.Ticker} already exists!";
                return(View());
            }

            if (model.Initial_Value < 1)
            {
                StatusMessage = $"Failed: Initial value must be greater or equal to 1!";
                return(View());
            }

            if (model.Keep > model.Amount)
            {
                StatusMessage = $"Failed: Keep must be less than Amount!";
                return(View());
            }

            // Create stock definition
            StockDefinition stockDef = new StockDefinition()
            {
                Ticker        = model.Ticker,
                Group_Id      = model.Group_Id,
                Current_Value = model.Initial_Value
            };

            // Add stock definition to database
            await _context.StockDefinitions.AddAsync(stockDef);

            // Create stock object for keeping
            StockObject keepStock = new StockObject()
            {
                Id       = Guid.NewGuid().ToString(),
                Amount   = model.Keep,
                Owner_Id = model.Group_Id,
                Ticker   = model.Ticker,
            };

            // Add
            await _context.StockObjects.AddAsync(keepStock);

            // Create stock sale for issued part
            StockOffer sellOffer = new StockOffer()
            {
                Id         = Guid.NewGuid().ToString(),
                Order_Type = "SELL",
                Target     = model.Initial_Value,
                Ticker     = model.Ticker,
                Amount     = model.Amount - model.Keep,
                Owner_Id   = model.Group_Id
            };

            // Add
            await _context.StockOffers.AddAsync(sellOffer);

            // Save changes if successful
            await _context.SaveChangesAsync();

            StatusMessage = $"Successfully issued {model.Amount} ${model.Ticker}";
            await VoopAI.ecoChannel.SendMessageAsync($":new: Welcome {model.Amount} {model.Ticker}, from {group.Name} to the market at ¢{model.Initial_Value}, with an initial {sellOffer.Amount} on the market!");

            return(RedirectToAction("Index"));
        }