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); } }
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); }
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); }
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); }
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.")); }
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}")); }
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}")); }
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 } } }
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")); }