Introduction to Neuro-Symbolic AI
Neuro-Symbolic AI is an advanced paradigm that combines neural networks (deep learning) with symbolic reasoning to enhance decision-making, interpretability, and generalization. While neural models excel at pattern recognition from unstructured data, symbolic AI uses logic-based rules to perform reasoning and inference. The integration of these two approaches results in more robust, explainable, and data-efficient AI systems.
Use of Neuro-Symbolic AI in Algorithmic Trading
In algorithmic trading, Neuro-Symbolic AI plays a crucial role in improving market predictions and trade execution by:
- Combining deep learning (e.g., Transformer-based forecasting) with symbolic reasoning (e.g., sentiment analysis, volatility rules).
- Adjusting price forecasts dynamically based on real-time sentiment analysis of news articles.
- Using logic-driven volatility factors to refine predictions and mitigate market noise.
- Enhancing risk management by integrating rule-based adjustments to model-driven outputs.
Summary of the Code
The provided code implements an algorithmic trading model enhanced with Neuro-Symbolic AI techniques:
- Sentiment Analysis Using Llama3.1: Extracts sentiment scores for two stocks based on local news data.
- Stock Data Generation: Simulates stock prices using a Multivariate Gaussian distribution.
- i-Transformer Model for Forecasting: A deep learning model based on a Transformer encoder predicts future stock prices using a sliding window of past prices.
- Neuro-Symbolic Adjustment: Adjusts the model’s predictions using sentiment scores and a volatility factor.
- Simulated Trading Decisions: Iterates through test data, predicting stock prices and applying symbolic adjustments to improve trading decisions.
This hybrid approach enhances algorithmic trading by incorporating both data-driven (neural) and logic-driven (symbolic) methodologies, leading to more accurate and interpretable market predictions.
Code: Algorithmic Trading using Neuro-Symbolic AI (a toy example):
import numpy as np
import pandas as pd
import tensorflow as tf
import re
# from tensorflow import keras
from keras.models import Model
from keras.layers import Input, Dense, Dropout, MultiHeadAttention, LayerNormalization, \
GlobalAveragePooling1D, Conv1D, Add
import json
import requests
from groq import Groq
client = Groq(
api_key='PLEASE USE YOUR OWN API KEY',
)
stock1 = "Stock_A"
stock2 = "Stock_B"
# Step 1: Fetch News from Local Storage and Use Llama 3.1 for Sentiment Analysis
def fetch_news_from_local(stock_name):
with open(stock_name, 'r') as file:
news_data = json.load(file)
return news_data # Assume the JSON file contains text news data
context1 = str(fetch_news_from_local("news_data_Stock_A.json")) + ":\n"
question1 = "calculate the sentiment score in the range [-1.0 to +1.0] for " + stock1 +"return only number nothing else"
context2 = str(fetch_news_from_local("news_data_Stock_B.json")) + ":\n"
question2 = "calculate the sentiment score in the range [-1.0 to +1.0] for " + stock2 +"return only number nothing else"
# Combine context and question
input_prompt1 = f"{context1}\nQuestion: {question1}\nAnswer:"
input_prompt2 = f"{context2}\nQuestion: {question2}\nAnswer:"
# Step 2: Calculate the sentiment
def extract_sentiment_score(input_prompt):
chat_completion = client.chat.completions.create(
messages=[
{
"role": "user",
"content": input_prompt,
}
],
model="llama-3.1-8b-instant",
# model="llama-3.1-70b-versatile",
# model = "llama3-70b-8192"
)
response = chat_completion.choices[0].message.content
# Use regex to extract the first numerical value from the response
match = re.search(r'[-+]?\d*\.\d+|\d+', response) # Extracts numbers including negatives & decimals
if match:
return float(match.group()) # Convert extracted number to float
else:
raise ValueError(f"Could not extract a valid sentiment score from response: {response}")
llm_output1 = extract_sentiment_score(input_prompt1)
# llm_output1 = float(llm_output1)
print(llm_output1)
llm_output2 = extract_sentiment_score(input_prompt2)
# llm_output2 = float(llm_output2)
print(llm_output2)
# Step 3: Generate Synthetic Stock Data (Multivariate Gaussian)
def generate_stock_data(days=180, mean=[100, 200], cov=[[1, 0.5], [0.5, 1]]):
np.random.seed(42)
stock_data = np.random.multivariate_normal(mean, cov, size=days)
df = pd.DataFrame(stock_data, columns=['Stock_A', 'Stock_B'])
df['Date'] = pd.date_range(start='2024-01-01', periods=days, freq='D')
return df
stock_df = generate_stock_data()
# Step 4: Prepare Data for i-Transformer
def prepare_data(df, window=10):
data = df[['Stock_A', 'Stock_B']].values
X, y = [], []
for i in range(len(data) - window):
X.append(data[i:i + window])
y.append(data[i + window])
return np.array(X), np.array(y)
X, y = prepare_data(stock_df)
train_size = int(0.8 * len(X))
X_train, X_test, y_train, y_test = X[:train_size], X[train_size:], y[:train_size], y[train_size:]
print("X_train => ",X_train)
print("X_test => ",X_test)
def i_transformer_encoder(inputs, head_size=64, num_heads=4, ff_dim=128, dropout=0.1):
# Apply Conv1D layer to adjust the shape
x = Conv1D(filters=head_size, kernel_size=1, activation='relu')(inputs)
# Apply MultiHeadAttention
attn_output = MultiHeadAttention(key_dim=head_size, num_heads=num_heads)(x, x)
attn_output = Dropout(dropout)(attn_output)
attn_output = LayerNormalization(epsilon=1e-6)(attn_output)
# Ensure that attention output has the same shape as 'x' before addition
attn_output = Dense(head_size)(attn_output)
# Residual connection
res = Add()([x, attn_output])
# Feed-forward layers
x = Dense(ff_dim, activation="relu")(res)
x = Dropout(dropout)(x)
x = Dense(head_size)(x)
x = LayerNormalization(epsilon=1e-6)(x)
return Add()([x, res])
input_shape = (X.shape[1], X.shape[2])
inputs = Input(shape=input_shape)
x = i_transformer_encoder(inputs)
x = GlobalAveragePooling1D()(x)
x = Dense(2)(x)
model = Model(inputs, x)
model.compile(optimizer='adam', loss='mse')
model.fit(X_train, y_train, epochs=20, batch_size=16, validation_data=(X_test, y_test))
# Step 6: Advanced Neuro-Symbolic AI Adjustment (Updated)
def neuro_symbolic_adjustment(forecast, input_prompt):
# Analyze sentiment for both stocks
sentiment_1 = extract_sentiment_score(input_prompt)
# Compute volatility factors for both stocks
volatility_factor_1 = np.std([np.random.uniform(-0.05, 0.05) for _ in range(10)]) # Simulated volatility for Stock 1
symbolic_adjustment = sentiment_1 * 0.05 + volatility_factor_1 # Adjust forecast with both factors
# Adjust forecast values
adjusted_forecast = forecast * (1 + symbolic_adjustment)
return max(0, adjusted_forecast) # Ensure price does not go negative
# Step 7: Simulate Trading Decisions
import csv
# Step 7: Simulate Trading Decisions & Save to CSV
results = []
for i in range(len(y_test)):
forecast = model.predict(X_test[i:i + 1])[0]
# Raw Predictions
raw_forecast_A = forecast[0]
raw_forecast_B = forecast[1]
# Adjusted Predictions using Neuro-Symbolic AI
adjusted_forecast_A = neuro_symbolic_adjustment(raw_forecast_A, input_prompt1)
adjusted_forecast_B = neuro_symbolic_adjustment(raw_forecast_B, input_prompt2)
# Calculate the percentage change due to neuro-symbolic AI
change_A = ((adjusted_forecast_A - raw_forecast_A) / raw_forecast_A) * 100
change_B = ((adjusted_forecast_B - raw_forecast_B) / raw_forecast_B) * 100
# Print results
print(f"Day {i + 1}: Stock_A Raw: {raw_forecast_A:.2f}, Adjusted: {adjusted_forecast_A:.2f} (Δ{change_A:.2f}%)")
print(f" Stock_B Raw: {raw_forecast_B:.2f}, Adjusted: {adjusted_forecast_B:.2f} (Δ{change_B:.2f}%)")
# Append results to list for saving
results.append([i + 1, raw_forecast_A, adjusted_forecast_A, change_A, raw_forecast_B, adjusted_forecast_B, change_B])
# Save results to CSV
csv_filename = "trading_predictions.csv"
with open(csv_filename, mode="w", newline="") as file:
writer = csv.writer(file)
writer.writerow(["Day", "Stock_A_Raw", "Stock_A_Adjusted", "Stock_A_Change(%)",
"Stock_B_Raw", "Stock_B_Adjusted", "Stock_B_Change(%)"])
writer.writerows(results)
print(f"\nPredictions saved to {csv_filename}")
Code: Dummy News Data Generator.
import json
def generate_dummy_news(stock_name):
"""
Generates dummy news data for a given stock and saves it as a JSON file.
:param stock_name: Name of the stock for which dummy news data is generated.
"""
news_samples = {
"Stock_A": [
"Stock_A sees a surge in trading volume due to positive earnings report.",
"Analysts predict continued growth for Stock_A in the coming quarter.",
"Stock_A's new product launch receives strong market approval."
],
"Stock_B": [
"Stock_B faces regulatory scrutiny leading to a drop in stock value.",
"Market experts suggest caution as Stock_B struggles with supply chain issues.",
"Stock_B's latest acquisition expected to boost long-term profits."
]
}
with open(f'news_data_{stock_name}.json', 'w') as file:
json.dump(news_samples.get(stock_name, []), file, indent=4)
print(f"Dummy news data for {stock_name} saved.")
# Generate dummy news for Stock_A and Stock_B
generate_dummy_news("Stock_A")
generate_dummy_news("Stock_B")
Reference:
- Besold, T. R., et al. (2017). Neural-Symbolic Learning and Reasoning: A Survey and Interpretation.
- Garcez, A. S., & Lamb, L. C. (2020). Neurosymbolic AI: The 3rd Wave.
- Bengio, Y., et al. (2021). The Neuro-Symbolic Concept Learner.
- Mitchell, M. (2019). Artificial Intelligence: A Guide to Intelligent Systems.