Start pulling AI out of the UI and into the background service

This commit is contained in:
2026-03-10 20:20:42 -07:00
parent c74eb3b3fa
commit d9fd9b2581
5 changed files with 252 additions and 239 deletions
+95 -235
View File
@@ -1,5 +1,6 @@
@page "/" @page "/"
@using Controllers.Payment @using Controllers.Payment
@using Microsoft.Extensions.FileSystemGlobbing.Internal.PathSegments
@rendermode InteractiveServer @rendermode InteractiveServer
<PageTitle>Home</PageTitle> <PageTitle>Home</PageTitle>
@@ -36,28 +37,14 @@
<span>Money: $@Session.Money</span> <span>Money: $@Session.Money</span>
</div> </div>
</div> </div>
<!-- Tool Frame --> <!-- Tool Frame -->
<div class="gridFrame"> <div class="gridFrame">
<div> <div>
<div><h2>Actions</h2></div> <div><h2>Actions</h2></div>
@if (Debounce){
<button @onclick="train">@trainButtonText</button>
<button @onclick="predict">@predictButtonText</button>
}else{
<button disabled>@trainButtonText</button>
<button disabled>@predictButtonText</button>
}
<br/>
<div> <div>
@if (Debounce){ <input placeholder="Stock Symbol [NVDA]" @bind="addStockSymbol" />
<input placeholder="Stock Symbol [NVDA]" @bind="buyStockSymbol" /> <button @onclick="addStock">Add To Tracked Stocks</button>
<input placeholder="Stock Quantity [1.0]" @bind="buyStockQuantity" />
<button @onclick="buyStock">Buy Stock</button>
} else {
<input disabled placeholder="Stock Symbol [NVDA]" @bind="buyStockSymbol" />
<input disabled placeholder="Stock Quantity [1.0]" @bind="buyStockQuantity" />
<button disabled>Buy Stock</button>
}
</div> </div>
<span>@resultError</span> <span>@resultError</span>
</div> </div>
@@ -66,36 +53,34 @@
<!-- AI Frame --> <!-- AI Frame -->
@if (Session != null){ @if (Session != null){
<div class="gridFrame"> <div>
<div> <!-- Tracked Stocks -->
<h2>Current Signal</h2> <div class="gridFrame">
</div> <div>
@foreach (PurchasedStock cur in Session.TrackedStocks){ <h2>Current Signal</h2>
<div class="signalBlock">
<div>
<p style="text-align: center; font-weight: bold;">@cur.Symbol</p>
<p>Purchased Price: @cur.PurchasePrice</p>
<p>Purchased Quantity: @cur.Quantity</p>
<p>Purchased Date: @cur.PurchaseDate.ToString("M-dd-yyyy")</p>
@if (Debounce){
<button @onclick="async () => {await sellStock(cur);}">Sell All</button>
}else{
<button disabled>Sell All</button>
}
</div>
<div>
<span>-></span>
</div>
@if (cur.PredictedMovement == -1){
<div style="background-color: red;"><p style=>Sell</p></div>
} else if (cur.PredictedMovement == 1){
<div style="background-color: green;"><p>Buy</p></div>
} else{
<div style="background-color: #444;"><p>Hold</p></div>
}
</div> </div>
} @foreach (Stock cur in Session.TrackedStocks){
<div class="signalBlock">
<p style="text-align: center; font-weight: bold;">@cur.Symbol</p>
<p>AI Predicted Score: @cur.Score</p>
<button @onclick="async () => {await removeStock(cur);}">Remove</button>
</div>
}
</div>
<!-- Trade History -->
<div class="gridFrame">
<div>
<h2>Trade History</h2>
</div>
@foreach (PurchasedStock cur in Session.TradeHistory){
<div class="signalBlock">
<p style="text-align: center; font-weight: bold;">@cur.Symbol</p>
<p>Purchased Quantity: @cur.Quantity</p>
<p>Purchased Price: @cur.PurchasePrice</p>
<p>Sell Price: @cur.PurchasePrice</p>
</div>
}
</div>
</div> </div>
} }
@@ -108,16 +93,8 @@
///////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////
loginSession? Session = null; loginSession? Session = null;
string resultError = "";
// On Page Load
string PaymentKey = "";
protected override async Task OnInitializedAsync(){
}
@@ -126,213 +103,96 @@
string passWord = ""; string passWord = "";
string loginError = ""; string loginError = "";
async Task LoginSession(){ async Task LoginSession(){
string dbPrefix = $"[{userName.ToLower()}]:"; // Set the DB prefix for the get and set string dbPrefix = $"[{userName.ToLower()}]:";
string passwordhash = dbDriver.Get( dbPrefix + "password" ); // Pull the password hash
// Check if user already exists
string passwordhash = dbDriver.Get( dbPrefix + "password" );
if (string.IsNullOrEmpty(passwordhash)){ if (string.IsNullOrEmpty(passwordhash)){
loginError = "no account found with that username"; loginError = "no account found with that username";
return; return;
} }
if (BCrypt.Verify( passWord, passwordhash )){ // If the password is valid
List<PurchasedStock>? stocks = JsonConvert.DeserializeObject<List<PurchasedStock>>( dbDriver.Get( dbPrefix + "Stocks" ) ); // Check the password is valid
bool moneyLoaded = float.TryParse(dbDriver.Get( dbPrefix + "money" ), out float moneyResult); if (!BCrypt.Verify( passWord, passwordhash )){
Session = new loginSession(){
UserName = userName.ToLower(),
TrackedStocks = stocks != null ? stocks : new List<PurchasedStock>(),
Money = moneyLoaded ? moneyResult : 1000
};
(bool, string) result = PaymentProcessor.CreatePayment(Session.UserName);
if (!result.Item1){
resultError = result.Item2;
}
PaymentKey = result.Item2;
}else{
loginError = "wrong password"; loginError = "wrong password";
return;
} }
// Load the users account
List<PurchasedStock>? history = JsonConvert.DeserializeObject<List<PurchasedStock>>( dbDriver.Get( dbPrefix + "history" ) );
List<Stock>? stocks = JsonConvert.DeserializeObject<List<Stock>>( dbDriver.Get( dbPrefix + "watched" ) );
bool moneyLoaded = float.TryParse(dbDriver.Get( dbPrefix + "money" ), out float moneyResult);
Session = new loginSession(){
UserName = userName.ToLower(),
TradeHistory = history != null ? history : new List<PurchasedStock>(),
TrackedStocks = stocks != null ? stocks : new List<Stock>(),
Money = moneyLoaded ? moneyResult : 1000
};
} }
async Task registerSession(){ async Task registerSession(){
string dbPrefix = $"[{userName.ToLower()}]:"; string dbPrefix = $"[{userName.ToLower()}]:";
// Check if user already exists
string passwordhash = dbDriver.Get( dbPrefix + "password" ); string passwordhash = dbDriver.Get( dbPrefix + "password" );
if (string.IsNullOrEmpty(passwordhash)){ if (!string.IsNullOrEmpty(passwordhash)){
dbDriver.Set( dbPrefix + "password", BCrypt.HashPassword( passWord, BCrypt.GenerateSalt() ) );
dbDriver.Set( dbPrefix + "money", "1000" );
Session = new loginSession(){
UserName = userName.ToLower(),
TrackedStocks = new List<PurchasedStock>(),
Money = 1000
};
(bool, string) result = PaymentProcessor.CreatePayment(Session.UserName);
if (!result.Item1){
resultError = result.Item2;
}
PaymentKey = result.Item2;
}else{
loginError = "account is taken"; loginError = "account is taken";
return;
} }
// Create User Object on the database
dbDriver.Set( dbPrefix + "password", BCrypt.HashPassword( passWord, BCrypt.GenerateSalt() ) );
dbDriver.Set( dbPrefix + "money", "1000" );
Session = new loginSession(){
UserName = userName.ToLower(),
TradeHistory = new List<PurchasedStock>(),
TrackedStocks = new List<Stock>(),
Money = 1000
};
// Add the users to a global table for automation
List<string>? Users = JsonConvert.DeserializeObject<List<string>>( dbDriver.Get( "Users" ) );
List<string> verifiedUsers = Users != null ? Users : new List<string>();
verifiedUsers.Add(userName.ToLower());
dbDriver.Set( "Users", JsonConvert.SerializeObject(verifiedUsers) );
} }
string addStockSymbol = "";
async Task addStock(){
// Make sure a session exists
if (Session == null){
// AI Stuff return;
string trainButtonText = "Force Retrain AI";
string predictButtonText = "Predict AI";
string resultError = "";
bool Debounce = true;
async Task train(){
resultError = "";
if (Debounce){
Debounce = false;
trainButtonText = "Do not refresh the page. The AI is training.";
await Task.Delay(1);
Task thread = new Task(async () => {
aiModule.TrainAI();
trainButtonText = "AI Trained";
await Task.Delay(2000);
trainButtonText = "Train AI";
await Task.Delay(1);
Debounce = true;
});
thread.Start();
} }
}
async Task predict(){ // Add a tracked stock to the users account
resultError = ""; Session.TrackedStocks.Add(new Stock(){
if (Debounce && Session != null){ Symbol = addStockSymbol.ToUpper()
Debounce = false; });
predictButtonText = "Do not refresh the page. The AI is predicting";
await Task.Delay(1); // Save the users account
List<Task> threadpool = new List<Task>(); string dbPrefix = $"[{userName.ToLower()}]:";
foreach(PurchasedStock cur in Session.TrackedStocks){ dbDriver.Set(dbPrefix + "watched", JsonConvert.SerializeObject(Session.TrackedStocks) );
Task thread = new Task(() => {
(string, int)Result = aiModule.PredictAI(cur.Symbol);
if (string.IsNullOrEmpty(Result.Item1)){
cur.PredictedMovement = Result.Item2;
}else{
resultError = Result.Item1;
}
Console.WriteLine("Received Signal [" + cur.Symbol + "] : " + cur.PredictedMovement);
});
thread.Start();
threadpool.Add(thread);
}
await Task.WhenAll(threadpool);
predictButtonText = "Predictions loaded";
await Task.Delay(2000);
predictButtonText = "Predict AI";
Debounce = true;
await Task.Delay(1);
}
} }
async Task removeStock(Stock stock){
// Make sure a session exists
if (Session == null){
// Stock Manipulation return;
string buyStockSymbol = "";
string buyStockQuantity = "";
async Task buyStock(){
if (Debounce && Session != null){
Debounce = false;
await Task.Delay(1);
string dbPrefix = $"[{userName.ToLower()}]:";
// Try Parse the quantitiy input
bool success = float.TryParse(buyStockQuantity, out float QuantityResult);
if (!success){
resultError = "Quantity field is not a number";
Debounce = true;
await Task.Delay(1);
return;
}
// Get Stock Price
float stockPrice = aiModule.GetCurrentPrice( buyStockSymbol );
// Try Pay for the stock
(bool, string) result = PaymentProcessor.TryPayment(PaymentKey, QuantityResult * stockPrice);
if (!result.Item1){
resultError = result.Item2;
Debounce = true;
await Task.Delay(1);
return;
}
// Add the stock
Session.TrackedStocks.Add( new PurchasedStock(){
Symbol = buyStockSymbol.ToUpper(),
PurchasePrice = stockPrice,
Quantity = QuantityResult,
PurchaseDate = DateTime.Now
} );
dbDriver.Set( dbPrefix + "Stocks", Newtonsoft.Json.JsonConvert.SerializeObject(Session.TrackedStocks) );
// Reload the users money
bool moneyLoaded = float.TryParse(dbDriver.Get( dbPrefix + "money" ), out float moneyResult);
Session.Money = moneyLoaded ? moneyResult : 1000;
// Reset the Impodent Key
result = PaymentProcessor.CreatePayment(Session.UserName);
if (!result.Item1){
resultError = "[New payment session failed] : " + result.Item2;
Debounce = true;
await Task.Delay(1);
return;
}
PaymentKey = result.Item2;
Debounce = true;
await Task.Delay(1);
} }
}
// Remove the stock
Session.TrackedStocks.Remove(stock);
async Task sellStock(PurchasedStock stock){ // Save the users account
string dbPrefix = $"[{userName.ToLower()}]:"; string dbPrefix = $"[{userName.ToLower()}]:";
if (Debounce && Session != null){ dbDriver.Set(dbPrefix + "watched", JsonConvert.SerializeObject(Session.TrackedStocks) );
Debounce = false;
await Task.Delay(1);
// Get sell price
float sellPrice = stock.Quantity * aiModule.GetCurrentPrice( stock.Symbol );
// Try to sell the stock
(bool, string) result = PaymentProcessor.TrySell(PaymentKey, sellPrice);
if (!result.Item1){
resultError = result.Item2;
Debounce = true;
await Task.Delay(1);
return;
}
// Remove Stock
Session.TrackedStocks.Remove(stock);
dbDriver.Set( dbPrefix + "Stocks", Newtonsoft.Json.JsonConvert.SerializeObject(Session.TrackedStocks) );
// Reload the users money
bool moneyLoaded = float.TryParse(dbDriver.Get( dbPrefix + "money" ), out float moneyResult);
Session.Money = moneyLoaded ? moneyResult : 1000;
// Reset the Impodent Key
result = PaymentProcessor.CreatePayment(Session.UserName);
if (!result.Item1){
resultError = "[New payment session failed] : " + result.Item2;
Debounce = true;
await Task.Delay(1);
return;
}
PaymentKey = result.Item2;
Debounce = true;
await Task.Delay(1);
}
} }
} }
+145
View File
@@ -0,0 +1,145 @@
using Controllers.DataBase;
using Entities;
using Newtonsoft.Json;
namespace Controllers.PythonInterop {
public class Automation {
AIModule _aiModule;
DbDriver _dbDriver;
public Automation(AIModule aiModule, DbDriver dbDriver) {
_aiModule = aiModule;
_dbDriver = dbDriver;
}
public void GlobalPredictAI() {
// Start this process on a background thread so its non-blocking
Task thread = new Task(async () => {
// Load the userlist
List<string>? UserList = JsonConvert.DeserializeObject<List<string>>(_dbDriver.Get("Users"));
List<string> VerifiedUserList = UserList != null ? UserList : new List<string>();
// Process each request at the same time for speed improvement
Parallel.ForEach(VerifiedUserList, async (username) => {
string dbPrefix = $"[{username.ToLower()}]:";
// Load the Tracked stocks for each user
List<Stock>? TrackedStocks = JsonConvert.DeserializeObject<List<Stock>>( _dbDriver.Get( dbPrefix + "watched" ) );
List<Stock> VerifiedTrackedStocks = TrackedStocks != null ? TrackedStocks : new List<Stock>();
// Go through each stock
List<Task> threadpool = new List<Task>();
foreach(Stock cur in VerifiedTrackedStocks) {
// Predict the trend on a new thread
Task thread = new Task(() => {
(string, float)Result = _aiModule.PredictAI(cur.Symbol);
// If error log it
if (!string.IsNullOrEmpty(Result.Item1)){
Console.WriteLine(Result.Item1);
}
// Write the score to the users tracked stocks
cur.Score = Result.Item2;
});
thread.Start();
threadpool.Add(thread);
}
// Wait for all the threads to finish
await Task.WhenAll(threadpool);
// Process Stocks -> Buy Sell Hold / Based on the global data from earlier
// Save the new scores to the database for reference from the UI
_dbDriver.Set( dbPrefix + "watched", JsonConvert.SerializeObject( VerifiedTrackedStocks ) );
});
});
thread.Start();
}
public void GlobalTrainAI(){
Task thread = new Task(async () => {
_aiModule.TrainAI();
});
thread.Start();
}
void sellStock(PurchasedStock stock){
string dbPrefix = $"[{userName.ToLower()}]:";
if (Session != null){
// Get sell price
float sellPrice = stock.Quantity * aiModule.GetCurrentPrice( stock.Symbol );
// Try to sell the stock
(bool, string) result = PaymentProcessor.TrySell(PaymentKey, sellPrice);
if (!result.Item1){
resultError = result.Item2;
return;
}
// Remove Stock
Session.TradeHistory.Remove(stock);
dbDriver.Set( dbPrefix + "Stocks", Newtonsoft.Json.JsonConvert.SerializeObject(Session.TradeHistory) );
// Reload the users money
bool moneyLoaded = float.TryParse(dbDriver.Get( dbPrefix + "money" ), out float moneyResult);
Session.Money = moneyLoaded ? moneyResult : 1000;
// Reset the Impodent Key
result = PaymentProcessor.CreatePayment(Session.UserName);
if (!result.Item1){
resultError = "[New payment session failed] : " + result.Item2;
return;
}
PaymentKey = result.Item2;
}
}
void buyStock(string StockSymbol){
if (Session != null){
string dbPrefix = $"[{userName.ToLower()}]:";
// Try Parse the quantitiy input
// Get the max quantity that I can Afford
float QuantityResult = 0;
// Get Stock Price
float stockPrice = aiModule.GetCurrentPrice( StockSymbol );
// Try Pay for the stock
(bool, string) result = PaymentProcessor.TryPayment(PaymentKey, QuantityResult * stockPrice);
if (!result.Item1){
resultError = result.Item2;
return;
}
// Add the stock
Session.TradeHistory.Add( new PurchasedStock(){
Symbol = StockSymbol.ToUpper(),
PurchasePrice = stockPrice,
Quantity = QuantityResult,
} );
dbDriver.Set( dbPrefix + "Stocks", Newtonsoft.Json.JsonConvert.SerializeObject(Session.TradeHistory) );
// Reload the users money
bool moneyLoaded = float.TryParse(dbDriver.Get( dbPrefix + "money" ), out float moneyResult);
Session.Money = moneyLoaded ? moneyResult : 1000;
// Reset the Impodent Key
result = PaymentProcessor.CreatePayment(Session.UserName);
if (!result.Item1){
resultError = "[New payment session failed] : " + result.Item2;
return;
}
PaymentKey = result.Item2;
}
}
}
}
+2 -1
View File
@@ -3,7 +3,8 @@ namespace Entities {
class loginSession { class loginSession {
public string UserName { get; set; } = ""; public string UserName { get; set; } = "";
public float Money { get; set; } = 0; public float Money { get; set; } = 0;
public List<PurchasedStock> TrackedStocks { get; set; } = new List<PurchasedStock>(); public List<Stock> TrackedStocks { get; set; } = new List<Stock>();
public List<PurchasedStock> TradeHistory { get; set; } = new List<PurchasedStock>();
} }
} }
+2 -3
View File
@@ -2,10 +2,9 @@ namespace Entities {
class PurchasedStock { class PurchasedStock {
public string Symbol { get; set; } = ""; public string Symbol { get; set; } = "";
public float PurchasePrice { get; set; } = 0;
public float Quantity { get; set; } = 0; public float Quantity { get; set; } = 0;
public DateTime PurchaseDate { get; set; } = DateTime.Now; public float PurchasePrice { get; set; } = 0;
public int PredictedMovement { get; set; } = 0; public float SellPrice { get; set; } = 0;
} }
} }
+8
View File
@@ -0,0 +1,8 @@
namespace Entities {
class Stock {
public string Symbol { get; set; } = "";
public float Score { get; set; } = 0;
}
}