In the world of algorithmic trading, it’s easy to get lost in the numbers. We build complex strategies based on moving averages, RSI, and MACD, assuming that all the information we need is baked into the historical price. But as any seasoned trader knows, markets are driven by something far more unpredictable: human emotion, triggered by news.
A pure technical strategy is blind to a sudden regulatory announcement or a groundbreaking product launch. Conversely, a strategy based only on news sentiment is noisy and lacks structure, often chasing false signals.
The concepts in this article are based on several key chapters from the book, "Finance & AI Trading with Python Programming" by Edgar Milvus. We'll be exploring the book's capstone 'News + Math' strategy, a powerful approach that combines the technical indicators from Chapter 11 with the AI-driven sentiment analysis from Chapter 6 to create the hybrid decision engine detailed in Chapter 20.
The most robust trading signals don't come from one source; they emerge when market structure (the Math) and an external catalyst (the News) confirm each other.
This article will show you how to build a simple yet powerful hybrid trading bot in Python. We'll create two independent signal generators and a final "Arbiter" that only executes a trade when both agree.
- The "Math" Engine: A simple technical indicator using Pandas to identify a bullish setup.
- The "News" Engine: A sentiment analyzer powered by Google's Gemini Flash to confirm positive market psychology.
- The Hybrid Arbiter: The final logic that combines both signals for a high-conviction entry.
The Tech Stack
- Python 3.8+
-
pandasandyfinancefor technical analysis. -
langchain-openaifor connecting to the Gemini API. - A free Google AI API Key.
Step 1: Get Your Free Gemini API Key
Before we write any code, you need credentials to access Google's powerful gemini-flash-latest model. The free tier is generous and perfect for this project.
- Go to Google AI Studio: Navigate to aistudio.google.com.
- Sign In: Use your Google account to sign in.
- Get API Key: In the top left menu (or via a button on the main page), click "Get API key".
- Create a New Project: A pop-up will appear. Click "Create API key in new project".
- Copy and Secure Your Key: Your new API key will be displayed. Copy this key immediately. This is a secret credential—treat it like a password.
-
Set as an Environment Variable: The most secure way to use your key is to set it as an environment variable. Open your terminal and run:
export GOOGLE_API_KEY="PASTE_YOUR_API_KEY_HERE"Note: This variable will only last for your current terminal session. To make it permanent, add the line to your shell's startup file (e.g.,
.bashrc,.zshrc, or.profile).
Step 2: The "Math" Engine — Identifying a Bullish Structure
Our "Math" engine will use a classic technical signal: the Moving Average Crossover. The strategy is simple: if a short-term moving average (e.g., 10-day) crosses above a long-term moving average (e.g., 50-day), it suggests that short-term momentum is turning bullish.
This function will fetch real data for a stock and return True if the bullish crossover condition is met.
import pandas as pd
import yfinance as yf
def get_technical_signal(symbol: str, short_window: int = 10, long_window: int = 50) -> bool:
"""
Calculates if a bullish moving average crossover has occurred.
Returns True if the fast MA is above the slow MA.
"""
print("1. Analyzing 'Math' signal...")
try:
# Fetch the last 100 days of data to ensure enough points for the long MA
data = yf.download(symbol, period="100d", progress=False)
if data.empty:
print(f"Warning: Could not fetch data for {symbol}.")
return False
# Calculate the fast and slow simple moving averages (SMAs)
data['SMA_Fast'] = data['Close'].rolling(window=short_window).mean()
data['SMA_Slow'] = data['Close'].rolling(window=long_window).mean()
# Get the most recent values
latest_fast_ma = data['SMA_Fast'].iloc[-1]
latest_slow_ma = data['SMA_Slow'].iloc[-1]
print(f" -> Latest Fast MA ({short_window}-day): {latest_fast_ma:.2f}")
print(f" -> Latest Slow MA ({long_window}-day): {latest_slow_ma:.2f}")
# The signal is bullish if the fast MA is above the slow MA
is_bullish_math = latest_fast_ma > latest_slow_ma
return is_bullish_math
except Exception as e:
print(f"An error occurred during technical analysis: {e}")
return False
Step 3: The "News" Engine — Confirming with Gemini
Now for the AI part. We need a function that takes a real-time news headline and asks Gemini if the sentiment is positive.
We'll use the ChatOpenAI library, but point it to Google's API endpoint. This is a neat trick that lets you use libraries designed for OpenAI-compatible APIs to interact with other model providers.
import os
from langchain_openai import ChatOpenAI
# This must be set before running the script (from Step 1)
if "GOOGLE_API_KEY" not in os.environ:
raise ValueError("GOOGLE_API_KEY environment variable not set. Please follow Step 1.")
def get_sentiment_signal(headline: str, required_threshold: str = "Positive") -> bool:
"""
Uses Gemini Flash to analyze the sentiment of a news headline.
Returns True only if the sentiment matches the required threshold.
"""
print("2. Analyzing 'News' signal with Gemini...")
LLM_MODEL = "gemini-flash-latest"
try:
# We use ChatOpenAI but configure it for Google's Generative Language API
llm = ChatOpenAI(
model=LLM_MODEL,
temperature=0.0,
# This is the key part: pointing to Google's endpoint
base_url="https://generativelanguage.googleapis.com/v1beta/",
api_key=os.getenv("GOOGLE_API_KEY"),
# The API expects a specific format, so we add the model name to the URL
model_provider="google"
)
prompt = f"""
Analyze the sentiment of the following financial news headline.
Your response must be one word only: Positive, Negative, or Neutral.
Headline: "{headline}"
"""
response = llm.invoke(prompt)
sentiment = response.content.strip()
print(f" -> Gemini Sentiment Analysis: '{sentiment}'")
# Check if the analyzed sentiment meets our requirement
is_bullish_news = (sentiment == required_threshold)
return is_bullish_news
except Exception as e:
print(f"An error occurred during sentiment analysis: {e}")
return False
Note: The model_provider="google" part is a way some libraries handle the specific URL formatting Google's API requires. .../models/gemini-flash-latest:generateContent.
Step 4: The Hybrid Arbiter — The Final Decision
This is the simplest but most important part of the code. It’s the "brain" of our bot, and it operates on a single, strict rule: act only on confirmation.
def generate_hybrid_signal(math_signal: bool, news_signal: bool) -> str:
"""
Combines both signals. A trade is only executed if both confirm each other.
"""
print("\n3. Generating Hybrid Signal...")
# The 'and' operator is our confirmation gate
if math_signal and news_signal:
return "HIGH_CONVICTION_BUY: Both technicals and sentiment are bullish."
elif math_signal and not news_signal:
return "HOLD: Technicals are bullish, but sentiment is not confirmed."
elif not math_signal and news_signal:
return "HOLD: Sentiment is bullish, but technical structure is weak."
else:
return "NO_ACTION: Neither signal is bullish."
Putting It All Together
Let's simulate a scenario where the technicals for a stock are bullish, and a positive news story breaks.
if __name__ == "__main__":
# Define our target asset and the breaking news
target_symbol = "NVDA"
breaking_news_headline = "NVIDIA unveils new AI chip, promising 2x performance gains and securing major cloud contracts."
print(f"--- Running Hybrid Bot for {target_symbol} ---")
# Step A: Get the "Math" signal
is_math_bullish = get_technical_signal(target_symbol)
# Step B: Get the "News" signal
is_news_bullish = get_sentiment_signal(breaking_news_headline)
# Step C: The Arbiter makes the final call
final_decision = generate_hybrid_signal(is_math_bullish, is_news_bullish)
print("\n==============================================")
print(f"FINAL DECISION: {final_decision}")
print("==============================================")
Why This Approach Is More Robust
When you run this script, the final_decision will only be a "BUY" if the get_technical_signal function finds that NVDA's 10-day SMA is above its 50-day SMA and Gemini confirms that the headline is Positive.
By forcing these two independent systems to agree, you create a powerful filter. You are no longer just trading chart patterns; you're trading chart patterns that are confirmed by real-world, positive momentum. This simple rule dramatically reduces the risk of entering trades based on random market noise or unsustained sentiment hype.
This is just the beginning. You can expand this framework by adding more technical indicators, analyzing the full text of news articles, or even connecting to a real-time news feed. But the core philosophy remains the same: trust, but verify. Let the math find the structure, and let the AI find the catalyst.
The concepts in this article are based on several key chapters from the book, “Finance & AI Trading with Python Programming” by Edgar Milvus: https://www.amazon.com/dp/B0G655N1S7, you can read it standalone. We have explored the book’s capstone ‘News + Math’ strategy, a powerful approach that combines the technical indicators from Chapter 11 with the AI-driven sentiment analysis from Chapter 6 to create the hybrid decision engine detailed in Chapter 20.
Explore the complete multi-volume “Python Programming Series” for a comprehensive journey from Python fundamentals to advanced AI deployment: https://www.amazon.com/dp/B0FTTQNXKG . Each book can be read as a standalone.
Follow me to stay updated on upcoming articles.
Happy trading!

Top comments (0)