From 234b9dcc709d40154a4c188365ad3e142aef3eda Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Mon, 9 Mar 2026 22:02:49 -0700 Subject: [PATCH] Try to fix the AI some more --- WebServer/AIPython/aipredictor.py | 17 +++++++++++++---- WebServer/AIPython/aitrainer.py | 14 ++++++++------ WebServer/AIPython/features.py | 31 +++++++++++++------------------ 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/WebServer/AIPython/aipredictor.py b/WebServer/AIPython/aipredictor.py index 1bc36a7b..d8f3b226 100644 --- a/WebServer/AIPython/aipredictor.py +++ b/WebServer/AIPython/aipredictor.py @@ -49,20 +49,29 @@ def Predict(): scaled_predictions = reconstructed_model.predict(scaled_data) # Use the loaded target scaler to get back to % change - actual_prediction = target_scaler.inverse_transform(scaled_predictions) + actual_prediction = target_scaler.inverse_transform(scaled_predictions.reshape(-1, 1)).flatten() # 'predictions' will be a 2D array, flatten it if you want a simple list - flat_predictions = actual_prediction.flatten().tolist() + flat_predictions = actual_prediction + + # Get the overall trend to pull predictions from + predictionTrend = 0 + with open("Target_Close_Average.txt", "r") as f: + predictionTrend = float(f.read().strip()) # Set the movement indicator movement_indicator = 0 - if (np.mean(flat_predictions) > 0.01): + averagePrediction = np.mean(flat_predictions) + if (averagePrediction > 0.005): # as in 3% swing up movement_indicator = 1 - elif (np.mean(flat_predictions) < -0.01): + elif (averagePrediction < -0.005): # as in 3% swing down movement_indicator = -1 else: movement_indicator = 0 + # Debug data + print(f"averagePrediction: {averagePrediction}") + # Return to C# via stdout print(f"---RESULT_START---") print(movement_indicator) diff --git a/WebServer/AIPython/aitrainer.py b/WebServer/AIPython/aitrainer.py index 717a433e..ca3e8639 100644 --- a/WebServer/AIPython/aitrainer.py +++ b/WebServer/AIPython/aitrainer.py @@ -35,12 +35,14 @@ def TrainAI(): # Create the DNN dnn_model = Sequential([ layers.Input(shape=(train_features.shape[1],)), # Load the feature count dynamically - layers.Dense(256, activation='elu'), - layers.Dropout(0.1), # Small dropout to prevent it from memorizing noise - layers.Dense(128, activation='elu'), - layers.Dropout(0.1), # Small dropout to prevent it from memorizing noise - layers.Dense(24, activation='elu'), - layers.Dense(1, activation='linear') + layers.Dense(256, activation='elu'), # DNN layer + layers.BatchNormalization(), # Nomralize + layers.Dropout(0.1), # Small dropout to prevent it from memorizing noise + layers.Dense(128, activation='elu'), # DNN layer + layers.BatchNormalization(), # Nomralize + layers.Dropout(0.1), # Small dropout to prevent it from memorizing noise + layers.Dense(24, activation='elu'), # DNN layer + layers.Dense(1, activation='linear') # DNN layer ]) # Allow negative numbers diff --git a/WebServer/AIPython/features.py b/WebServer/AIPython/features.py index 03e9d48d..4e9f9a04 100644 --- a/WebServer/AIPython/features.py +++ b/WebServer/AIPython/features.py @@ -11,37 +11,25 @@ def MakeFeatures(df): df['Body_Size'] = (df['Close'] - df['Open']).abs() / (df['High'] - df['Low']) df['Upper_Shadow'] = (df['High'] - candle_top) / (df['High'] - df['Low']) -# Remove Unused Symbols to save ram - df.drop('Open', axis=1, inplace=True) - df.drop('High', axis=1, inplace=True) - df.drop('Low', axis=1, inplace=True) - # Is volume 2x higher than the 20-day average? df['Vol_Intensity'] = df['Volume'] / df['Volume'].shift(1).rolling(20).mean() # Volume Change df['Volume_Chg'] = df['Volume'].pct_change() -# Remove Unused Symbols to save ram - df.drop('Volume', axis=1, inplace=True) - # Moving Average Crossover (Golden/Death Cross logic) - df['Moving_Average_5'] = df['Close'].shift(1).rolling(window=20).mean() - df['Moving_Average_20'] = df['Close'].shift(1).rolling(window=20).mean() + df['Moving_Average_5'] = df['Close'].rolling(window=5).mean() + df['Moving_Average_20'] = df['Close'].rolling(window=20).mean() # if short term > long term (bullish), else 0 df['Trend_Signal'] = (df['Moving_Average_5'] > df['Moving_Average_20']).astype(int) # Distance from MA (How overextended are we?) df['Dist_From_MA20'] = (df['Close'] / df['Moving_Average_20']) - 1 # Bollinger Band Position (Where are we relative to volatility?) - std_20 = df['Close'].shift(1).rolling(20).std() + std_20 = df['Close'].rolling(20).std() upper_band = df['Moving_Average_20'] + (std_20 * 2) lower_band = df['Moving_Average_20'] - (std_20 * 2) df['BB_Pos'] = (df['Close'] - lower_band) / (upper_band - lower_band) -# Remove Unused Symbols to save ram - df.drop('Moving_Average_5', axis=1, inplace=True) - df.drop('Moving_Average_20', axis=1, inplace=True) - # Add feature for Returns df['Return'] = df['Close'].pct_change() # Log Returns (Better for AI than pct_change for statistical normality) @@ -65,10 +53,17 @@ def MakeFeatures(df): df[f'Vol_Lag_{lag}'] = df['Volume_Chg'].shift(lag) # This is our training metric of price difference 5 days ahead - df['Target_Close'] = np.log(df['Close'].shift(-5) / df['Close']) / df['Volatility_5'] + df['Target_Close'] = (np.log(df['Close'].shift(-5) / df['Close']) / df['Volatility_5']).clip(-10, 10) -# Remove Unused Symbols to save ram - df.drop('Close', axis=1, inplace=True) + # Save the overall trend to predict based off of later + with open("Target_Close_Average.txt", "w") as file: + file.write(str(df["Target_Close"].mean())) + + # Drop every column that is a raw price or an unscaled average + cols_to_drop = ['Open', 'High', 'Low', 'Volume', 'Close', 'Moving_Average_5', 'Moving_Average_20'] + for col in cols_to_drop: + if col in df.columns: + df.drop(col, axis=1, inplace=True) # Drop rows with null values df.dropna(inplace=True)