From d5df9d10140b0bc1a3078596ff3fadf3ff41bb80 Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Wed, 11 Mar 2026 21:33:19 -0700 Subject: [PATCH] Create a simulation to test 1 month of data in 30 mins --- WebServer/AIPython/aipredictor.py | 19 ++++- WebServer/AIPython/features.py | 5 +- WebServer/Components/Pages/Home.razor | 15 ++++ WebServer/Controllers/Automation.cs | 11 ++- WebServer/Controllers/ProjectTest.cs | 111 +++++++++++++++++++++++++ WebServer/Controllers/PythonInterop.cs | 4 +- 6 files changed, 156 insertions(+), 9 deletions(-) create mode 100644 WebServer/Controllers/ProjectTest.cs diff --git a/WebServer/AIPython/aipredictor.py b/WebServer/AIPython/aipredictor.py index 70af3a45..566ced2a 100644 --- a/WebServer/AIPython/aipredictor.py +++ b/WebServer/AIPython/aipredictor.py @@ -1,3 +1,4 @@ +from datetime import datetime, timedelta import os import sys import joblib @@ -13,13 +14,29 @@ def Predict(): # Get the Symbol from ARGV Symbol = sys.argv[1] + # get the number of days ago to run the simulation for + DaysBack = int(sys.argv[2]) + + print(f"Days back: {DaysBack}") + + # calculate the time offsets + end_date = datetime.now() - timedelta(days=DaysBack) + start_date = end_date - timedelta(days=70) + + # convert to string formats + start_str = start_date.strftime('%Y-%m-%d') + end_str = end_date.strftime('%Y-%m-%d') + + print(f"Start Date: {start_str}") + print(f"End Date: {end_str}") + # Define paths (consistent with your previous script) SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) DATA_DIR = os.path.join(SCRIPT_DIR, "data") MODEL_PATH = os.path.join(DATA_DIR, "model.keras") # Pull 1 month of current data to make prediction against | for volatility 20 - df = yf.download(Symbol, period="2mo", auto_adjust=True) + df = yf.download(Symbol, start=start_str, end=end_str, auto_adjust=True, progress=False) if not df.empty: # Remove the horizontal ticker column df.columns = df.columns.get_level_values(0) diff --git a/WebServer/AIPython/features.py b/WebServer/AIPython/features.py index 981ecee7..c08d8169 100644 --- a/WebServer/AIPython/features.py +++ b/WebServer/AIPython/features.py @@ -78,10 +78,11 @@ def MakeFeatures(df): df.drop(col, axis=1, inplace=True) # Drop rows with null values - df.dropna(inplace=True) + feature_columns = [col for col in df.columns if col != 'Target_Close'] # Dont drop any Target_Close as we need this data for predictions + df.dropna(subset=feature_columns, inplace=True) # Replace Infinity with 0 -> This fixes the AI mental breakdown - df = df.replace([np.inf, -np.inf], 0) + df.replace([np.inf, -np.inf], 0, inplace=True) # Replace Infinity with 0 -> This fixes the AI mental breakdown df['Volume_Chg'] = df['Volume_Chg'].replace([np.inf, -np.inf], 0) diff --git a/WebServer/Components/Pages/Home.razor b/WebServer/Components/Pages/Home.razor index 5b8e0b6f..fb58464b 100644 --- a/WebServer/Components/Pages/Home.razor +++ b/WebServer/Components/Pages/Home.razor @@ -1,5 +1,6 @@ @page "/" @using Controllers.Payment +@using Controllers.ProjectTest @using Microsoft.Extensions.FileSystemGlobbing.Internal.PathSegments @rendermode InteractiveServer @@ -46,6 +47,11 @@ +
+
+ + @TestResults +
@resultError @@ -195,4 +201,13 @@ string dbPrefix = $"[{userName.ToLower()}]:"; dbDriver.Set(dbPrefix + "watched", JsonConvert.SerializeObject(Session.TrackedStocks) ); } + + + string TestResults = ""; + async Task RunTest(){ + D683_Project_Test tests = new D683_Project_Test(aiModule); + float score = await tests.Simulate(); + TestResults = score.ToString(); + } + } \ No newline at end of file diff --git a/WebServer/Controllers/Automation.cs b/WebServer/Controllers/Automation.cs index 3c8700aa..97676e34 100644 --- a/WebServer/Controllers/Automation.cs +++ b/WebServer/Controllers/Automation.cs @@ -17,14 +17,17 @@ namespace Controllers.Automation { _paymentProcessor = PaymentProcessor; } - public void GlobalPredictAI() { + public void GlobalPredictAI(int DaysBefore = 0, bool testmode = false) { // Start this process on a background thread so its non-blocking Task thread = new Task(() => { // Load the userlist - List? UserList = JsonConvert.DeserializeObject>(_dbDriver.Get("Users")); - List VerifiedUserList = UserList != null ? UserList : new List(); + List VerifiedUserList = new List(){ "TESTMODE" }; + if (!testmode) { + List? UserList = JsonConvert.DeserializeObject>(_dbDriver.Get("Users")); + VerifiedUserList = UserList != null ? UserList : new List(); + } // Process each request at the same time for speed improvement Parallel.ForEach(VerifiedUserList, async (username) => { @@ -40,7 +43,7 @@ namespace Controllers.Automation { // Predict the trend on a new thread Task thread = new Task(() => { - (string, float)Result = _aiModule.PredictAI(cur.Symbol); + (string, float)Result = _aiModule.PredictAI(cur.Symbol, DaysBefore); // If error log it if (!string.IsNullOrEmpty(Result.Item1)){ diff --git a/WebServer/Controllers/ProjectTest.cs b/WebServer/Controllers/ProjectTest.cs new file mode 100644 index 00000000..2846f002 --- /dev/null +++ b/WebServer/Controllers/ProjectTest.cs @@ -0,0 +1,111 @@ +using Controllers.PythonInterop; +using Entities; +using Microsoft.AspNetCore.Razor.TagHelpers; + +namespace Controllers.ProjectTest { + + public class D683_Project_Test { + + // TESTING Starting Money + float Money = 1000; + + // TESTING WATCH STOCK LIST + List TrackedStocks = new List() { + new Stock(){ Symbol="NVDA" }, + new Stock(){ Symbol="INTL" }, + new Stock(){ Symbol="AAPL" }, + new Stock(){ Symbol="SHOP" }, + new Stock(){ Symbol="PANW" }, + new Stock(){ Symbol="BBBY" }, + new Stock(){ Symbol="REAL" }, + new Stock(){ Symbol="W" }, + new Stock(){ Symbol="ROKU" }, + new Stock(){ Symbol="FUN" } + }; + + // TESTING STOCK HISTORY + List StockHistory = new List(); + + AIModule _aiModule; + public D683_Project_Test( AIModule aiModule ) { + _aiModule = aiModule; + } + + public async Task Simulate() { + float StartingMoney = Money; + // Run once for each day 30 days straight + for (int i=30; i>=0; i--) { + + // Go through each watched stock and find the highest prediction + List threadpool = new List(); + foreach(Stock cur in TrackedStocks) { + + // Predict the trend on a new thread + Task thread = new Task(async () => { + (string, float)Result = _aiModule.PredictAI(cur.Symbol, i); + + // 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 processes to finish + await Task.WhenAll( threadpool ); + + // Get the highest ranked + Stock HighestRanking = new Stock(){ Symbol="NVDA", Score = -400 }; // Just a placeholder incase an empty list comes through there is a fallback + foreach(Stock cur in TrackedStocks) { + if (HighestRanking.Score < cur.Score) { + HighestRanking = cur; + } + } + + // Sell all stocks + float totalSale = 0; + foreach(PurchasedStock cur in StockHistory) { + if (cur.Sold == false) { + // Get sell price + float sellPrice = cur.Quantity * _aiModule.GetCurrentPrice( cur.Symbol ); + // Add up the total sale + totalSale += sellPrice; + // Mark as sold + cur.Sold = true; + } + } + Money += totalSale; + + // Dont buy anything on the last run + if (i != 0) { + + // Buy predicted stock + float stockPrice = _aiModule.GetCurrentPrice( HighestRanking.Symbol ); + + // Get max stocks user can purchase [ int cast truncates the decimal ] + int MaxQty = (int)( Money / stockPrice ); + + // Add the stock + StockHistory.Add( new PurchasedStock(){ + Symbol = HighestRanking.Symbol.ToUpper(), + PurchasePrice = stockPrice, + Quantity = MaxQty, + } ); + Money = Money - ( stockPrice * MaxQty ); + } + } + + // Return a score [Bigger than 0 is money earned] or [Less than 0 is money lost] + return Money - StartingMoney; + } + + + + + } +} \ No newline at end of file diff --git a/WebServer/Controllers/PythonInterop.cs b/WebServer/Controllers/PythonInterop.cs index fed83587..d2290daf 100644 --- a/WebServer/Controllers/PythonInterop.cs +++ b/WebServer/Controllers/PythonInterop.cs @@ -27,8 +27,8 @@ namespace Controllers.PythonInterop { } // Return ( Error, Signal ) - public (string, float) PredictAI(string StockSymbol) { - (bool, string) Success = PyProcess.RunPythonProcess(_PyPath, _ExecPath + "/aipredictor.py", returns: true, PyArgs: StockSymbol); + public (string, float) PredictAI(string StockSymbol, int DataEndDaysAgo = 0) { + (bool, string) Success = PyProcess.RunPythonProcess(_PyPath, _ExecPath + "/aipredictor.py", returns: true, PyArgs: $"{StockSymbol} {DataEndDaysAgo}"); if (!Success.Item1) { return (Success.Item2, 0); } else {