Monitor trending momentum of sector ETFs

 Monitor trending momentum of sector ETFs 



Sector​

ETF Ticker​

Information Technology​

XLK​

Financials​

XLF​

Health Care​

XLV​

Communication Services​

XLC​

Consumer Discretionary​

XLY​

Industrials​

XLI​

Consumer Staples​

XLP​

Energy​

XLE​

Utilities​

XLU​

Materials​

XLB​

Real Estate​

XLRE​


Looking to build a gemini ai agent based process to monitor trending momentum of sector ETFs to see which ETFs perform better than others. Sector​ ETF Ticker - ​Information Technology​ XLK, ​Financials ​XLF,​ Health Care ​XLV, ​Communication Services ​XLC, ​Consumer Discretionary​ XLY, ​Industrials​ XLI, ​Consumer Staples ​XLP​, Energy​ XLE, ​Utilities​ XLU, ​Materials​ XLB, ​Real Estate ​XLRE​  

Building a Gemini AI agent-driven pipeline to track and rank sector ETF momentum is a powerful way to automate quantitative market analysis. By combining Python (for fetching price data and calculating metrics) with a Gemini Agent framework (using the google-genai SDK or Agent Development Kit), you can create an autonomous system that ingests market data, computes momentum scores, interprets trend shifts, and delivers structured advisory updates.

Below is a complete blueprint and production-ready architecture for your Gemini-powered sector ETF momentum monitor.

Step 1: Core Python Data & Momentum Engine

This script pulls historical adjusted close prices for your 11 sector ETFs, computes the standard 12-minus-1 month momentum factor (skipping the most recent month to avoid short-term market noise), ranks them, and identifies the leaders.

import pandas as pd

import yfinance as yf


# Define the 11 sector ETF universe

SECTOR_ETFS = {

    'Information Technology': 'XLK',

    'Financials': 'XLF',

    'Health Care': 'XLV',

    'Communication Services': 'XLC',

    'Consumer Discretionary': 'XLY',

    'Industrials': 'XLI',

    'Consumer Staples': 'XLP',

    'Energy': 'XLE',

    'Utilities': 'XLU',

    'Materials': 'XLB',

    'Real Estate': 'XLRE',

}



def fetch_and_calculate_momentum():

  tickers = list(SECTOR_ETFS.values())


  # Download historical adjusted close prices (approx. 1.5 years for 12m lookback)

  data = yf.download(tickers, period='2y', progress=False)['Close']


  # Resample to monthly end prices to compute institutional momentum

  monthly_data = data.resample('ME').last()


  # 12-1 Month Momentum: Return from 12 months ago to 1 month ago

  # (skips the most recent month to avoid short-term reversals)

  momentum_12_1 = monthly_data.shift(1) / monthly_data.shift(12) - 1


  # Get the latest available momentum scores

  latest_scores = momentum_12_1.iloc[-1].dropna()


  # Rank sectors from highest momentum to lowest

  ranked_sectors = latest_scores.sort_values(ascending=False)


  # Map tickers back to readable names

  ticker_to_name = {v: k for k, v in SECTOR_ETFS.items()}


  results = []

  for rank, (ticker, score) in enumerate(ranked_sectors.items(), 1):

    results.append({

        'Rank': rank,

        'Sector': ticker_to_name.get(ticker, ticker),

        'Ticker': ticker,

        '12M-1M Momentum (%)': round(score * 100, 2),

    })


  return pd.DataFrame(results)



if __name__ == '__main__':

  df_ranked = fetch_and_calculate_momentum()

  print(df_ranked.to_string(index=False))


Step 2: Integrating the Gemini Agent Layer

To turn this into a Gemini AI agent process, you wrap the data engine inside a tool or execution loop using the google-genai SDK. The Gemini model acts as the reasoning engine, deciding when to call the momentum tool, analyzing structural market shifts, and generating a synthesized executive briefing.

from google import genai

from google.genai import types


# Initialize the Gemini client

client = genai.Client()



# Define a tool function that the Gemini agent can invoke

def get_sector_momentum_rankings() -> str:

  """Calculates and returns the current momentum rankings for all 11 S&P 500 sector ETFs."""

  df = fetch_and_calculate_momentum()

  return df.to_json(orient='records')



# Configure the agent with tool access

config = types.GenerateContentConfig(

    tools=[get_sector_momentum_rankings],

    system_instruction=(

        "You are an expert quantitative portfolio manager and AI trading assistant. "

        "Your job is to analyze sector ETF momentum data, identify market leadership shifts "

        "(e.g., rotation from defensive to cyclical sectors), and provide clear, "

        "actionable market commentary based strictly on the computed metrics."

    ),

    temperature=0.2,

)


# Prompt the agent to run the analysis

response = client.models.generate_content(

    model='gemini-2.5-flash',

    contents=(

        'Run the sector momentum scan, identify the top 3 leading sector ETFs,'

        ' evaluate whether leadership is currently concentrated in growth or'

        ' defensive sectors, and format a concise weekly briefing.'

    ),

    config=config,

)


print(response.text)

Step 3: Actionable Implementation Roadmap

To scale this into a fully automated production pipeline (e.g., running weekly on Google Cloud Run or Cloud Functions), follow this structured plan:

Database Persistence: Set up a Cloud SQL MySQL instance to store historical daily prices, monthly momentum ranks, and agent run logs using composite primary keys (date, ticker).

Automated Event Triggers: Use Cloud Scheduler and Eventarc to trigger your Python script every Monday pre-market.

Agentic RAG & Synthesis: Have the Gemini agent compare the current week's top 3 sector leaders against the previous month's rankings to automatically detect sector rotation anomalies (e.g., capital flowing out of Utilities XLU into Technology XLK).

Alerting Output: Route the Gemini-generated markdown briefing directly to an internal dashboard or notification channel.

Detail steps to create on gemini cloud platform 


Building and deploying your sector ETF momentum agent on Google Cloud Platform (GCP) involves leveraging serverless infrastructure, automated scheduling, and the Gemini API via the google-genai SDK.

Here is the detailed, step-by-step guide to setting up, containerizing, and deploying your agentic momentum monitor on GCP.

Step 1: Set Up Your GCP Project & Enable APIs

First, ensure your Google Cloud environment is properly configured with the necessary services and permissions.

Create or Select a Project:

Open the Google Cloud Console.

Create a new project (e.g., etf-momentum-agent-prod) or select an existing one.

Enable Required APIs: Run the following command in your terminal (or enable them via the Cloud Console API Library):


gcloud services enable \

  run.googleapis.com \

  artifactregistry.googleapis.com \

  sqladmin.googleapis.com \

  cloudscheduler.googleapis.com \

  secretmanager.googleapis.com \

  aiplatform.googleapis.com


Set Up Authentication: Ensure you have the Google Cloud CLI installed and authenticated: 


Step 2: Provision Cloud SQL MySQL Database

To store your historical daily prices, momentum ranks, and agent run logs, deploy a managed MySQL database.

NOTE: Would use Cloud SQL instance already set up


Create the Cloud SQL Instance:

Bash

gcloud sql instances create etf-db-instance \

  --database-version=MYSQL_8_0 \

  --tier=db-f1-micro \

  --region=us-central1 \

  --root-password=YOUR_SECURE_PASSWORD



Create the Database and User:

Bash

gcloud sql databases create momentum_db --instance=etf-db-instance



Secure Credentials in Secret Manager: Store your database connection string and Gemini API key securely:

Bash

echo -n "mysql+pymysql://root:YOUR_SECURE_PASSWORD@<INSTANCE_IP>/momentum_db" | \

  gcloud secrets create db-connection-uri --data-file=-


echo -n "YOUR_GEMINI_API_KEY" | \

  gcloud secrets create gemini-api-key --data-file=-


Step 3: Containerize the Agent Application

Package your Python momentum script and Gemini agent into a container image using Docker.

Create a requirements.txt:

pandas

yfinance

pymysql

sqlalchemy

google-genai

requests


Create a Dockerfile:

Dockerfile

FROM python:3.12-slim


WORKDIR /app


COPY requirements.txt .

RUN pip install --no-cache-dir -r requirements.txt


COPY . .


CMD ["python", "agent_runner.py"]



Build and Push to Artifact Registry: Create an Artifact Registry repository and push your container image:

Bash

gcloud artifacts repositories create agent-repo \

  --repository-format=docker \

  --location=us-central1


gcloud auth configure-docker us-central1-docker.pkg.dev


docker build -t us-central1-docker.pkg.dev/YOUR_PROJECT_ID/agent-repo/etf-momentum-agent:v1 .

docker push us-central1-docker.pkg.dev/YOUR_PROJECT_ID/agent-repo/etf-momentum-agent:v1

Step 4: Deploy as a Serverless Job on Cloud Run

Cloud Run Jobs allow you to run script-based batch workloads that execute to completion (ideal for weekly or daily market scans).

Deploy the Cloud Run Job:

Bash

gcloud run jobs create etf-momentum-job \

  --image=us-central1-docker.pkg.dev/YOUR_PROJECT_ID/agent-repo/etf-momentum-agent:v1 \

  --region=us-central1 \

  --set-secrets=DATABASE_URL=db-connection-uri:latest,GEMINI_API_KEY=gemini-api-key:latest \

  --max-retries=3 \

  --task-timeout=5m



Step 5: Automate with Cloud Scheduler

Set up a cron trigger so the agent automatically scans the market and stores results before the market opens every Monday.

Create a Scheduler Trigger:

Bash

gcloud scheduler jobs create http weekly-momentum-trigger \

  --location=us-central1 \

  --schedule="0 8 * * 1" \

  --uri="https://us-central1-run.googleapis.com/apis/run.googleapis.com/v1/namespaces/YOUR_PROJECT_ID/jobs/etf-momentum-job:run" \

  --oauth-service-account-email=YOUR_PROJECT_NUMBER-compute@developer.gserviceaccount.com


(Note: This schedules the job to run every Monday at 8:00 AM).

What would be the directory structure 

To support a clean, modular, and production-ready Python application that you can easily containerize and deploy to Google Cloud Run, here is the recommended directory structure for your Sector ETF Momentum Agent.

This structure cleanly separates your data ingestion logic, database models, Gemini agent configuration, configuration files, and deployment assets.

Recommended Directory Structure


Key File Descriptions

main.py: The application entrypoint. When triggered by Cloud Run or Cloud Scheduler, it executes the pipeline: fetches the latest momentum rankings via momentum.py, logs the metrics into Cloud SQL via database/, and invokes the Gemini agent in engine/agent.py to produce the executive briefing.

engine/momentum.py: Contains the yfinance download logic, monthly resampling, and 12-minus-1 month return calculations for your 11 sector ETFs (XLK, XLF, XLV, etc.).

engine/agent.py: Configures the google-genai client, wraps your momentum function as a Gemini tool, and manages system instructions for the model.

database/models.py: Defines your SQLAlchemy ORM models with composite primary keys (date, ticker) to track historical momentum scores safely over time.

.dockerignore: Ensures local environment files, caches (__pycache__), and git folders are ignored when building your Docker image.

List out all the python code for each .py file


Here is the complete, production-ready Python code for each file in your directory structure, integrating data ingestion, SQLAlchemy database persistence, and the Gemini AI agent loop.

1. config/settings.py

Handles environment configuration and secret loading.

Python

import os

from dotenv import load_dotenv


load_dotenv()


DATABASE_URL = os.getenv("DATABASE_URL")

GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")

GEMINI_MODEL = os.getenv("GEMINI_MODEL", "gemini-2.5-flash")


2. database/connection.py

Sets up the SQLAlchemy database engine, session factory, and base model class.

Python

from sqlalchemy import create_engine

from sqlalchemy.orm import declarative_base, sessionmaker

from config.settings import DATABASE_URL


if not DATABASE_URL:

    raise ValueError("DATABASE_URL environment variable is not set.")


engine = create_engine(DATABASE_URL, echo=False, pool_pre_ping=True)

SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()



def get_db():

  db = SessionLocal()

  try:

    yield db

  finally:

    db.close()


3. database/models.py

Defines MySQL database tables using SQLAlchemy ORM models.

Python

from datetime import datetime

from sqlalchemy import Column, Date, DateTime, Float, Integer, String, Text

from database.connection import Base



class ETFPriceHistory(Base):

  __tablename__ = "etf_price_history"


  date = Column(Date, primary_key=True)

  ticker = Column(String(10), primary_key=True)

  adjusted_close = Column(Float, nullable=False)

  created_at = Column(DateTime, default=datetime.utcnow)



class MomentumRanking(Base):

  __tablename__ = "momentum_rankings"


  date = Column(Date, primary_key=True)

  ticker = Column(String(10), primary_key=True)

  sector_name = Column(String(50), nullable=False)

  momentum_score = Column(Float, nullable=False)

  rank = Column(Integer, nullable=False)

  created_at = Column(DateTime, default=datetime.utcnow)



class AgentExecutionLog(Base):

  __tablename__ = "agent_execution_logs"


  id = Column(Integer, primary_key=True, autoincrement=True)

  execution_date = Column(DateTime, default=datetime.utcnow)

  status = Column(String(20), nullable=False)

  summary_output = Column(Text, nullable=True)

  error_message = Column(Text, nullable=True)


4. engine/momentum.py

Downloads historical ETF prices via yfinance and computes institutional 12-minus-1 month momentum scores.

Python

import pandas as pd

import yfinance as yf


SECTOR_ETFS = {

    'Information Technology': 'XLK',

    'Financials': 'XLF',

    'Health Care': 'XLV',

    'Communication Services': 'XLC',

    'Consumer Discretionary': 'XLY',

    'Industrials': 'XLI',

    'Consumer Staples': 'XLP',

    'Energy': 'XLE',

    'Utilities': 'XLU',

    'Materials': 'XLB',

    'Real Estate': 'XLRE',

}



def fetch_and_calculate_momentum() -> pd.DataFrame:

  tickers = list(SECTOR_ETFS.values())


  # Download historical adjusted close prices

  data = yf.download(tickers, period='2y', progress=False)['Close']


  # Resample to monthly end prices

  monthly_data = data.resample('ME').last()


  # 12-1 Month Momentum: Return from 12 months ago to 1 month ago

  momentum_12_1 = monthly_data.shift(1) / monthly_data.shift(12) - 1

  latest_scores = momentum_12_1.iloc[-1].dropna()


  ranked_sectors = latest_scores.sort_values(ascending=False)

  ticker_to_name = {v: k for k, v in SECTOR_ETFS.items()}


  results = []

  for rank, (ticker, score) in enumerate(ranked_sectors.items(), 1):

    results.append({

        'Rank': rank,

        'Sector': ticker_to_name.get(ticker, ticker),

        'Ticker': ticker,

        '12M-1M Momentum (%)': round(score * 100, 2),

        'Raw Score': float(score),

    })


  return pd.DataFrame(results)


5. engine/agent.py

Initializes the google-genai client, exposes the momentum function as a tool, and configures the Gemini model.

Python

from google import genai

from google.genai import types

from config.settings import GEMINI_API_KEY, GEMINI_MODEL

from engine.momentum import fetch_and_calculate_momentum


client = genai.Client(api_key=GEMINI_API_KEY) if GEMINI_API_KEY else genai.Client()



def get_sector_momentum_rankings() -> str:

  """Calculates and returns the current momentum rankings for all 11 S&P 500 sector ETFs."""

  df = fetch_and_calculate_momentum()

  return df.to_json(orient='records')



def run_momentum_agent(custom_prompt: str = None) -> str:

  config = types.GenerateContentConfig(

      tools=[get_sector_momentum_rankings],

      system_instruction=(

          "You are an expert quantitative portfolio manager and AI trading"

          " assistant. Your job is to analyze sector ETF momentum data, identify"

          " market leadership shifts (e.g., rotation from defensive to"

          " cyclical sectors), and provide clear, actionable market commentary"

          " based strictly on the computed metrics."

      ),

      temperature=0.2,

  )


  prompt = custom_prompt or (

      'Run the sector momentum scan, identify the top 3 leading sector ETFs,'

      ' evaluate whether leadership is currently concentrated in growth or'

      ' defensive sectors, and format a concise weekly briefing.'

  )


  response = client.models.generate_content(

      model=GEMINI_MODEL,

      contents=prompt,

      config=config,

  )


  return response.text


6. main.py

The orchestration entrypoint that runs the pipeline, writes metrics to Cloud SQL, and triggers the Gemini agent.

Python

from datetime import date

import logging

from database.connection import Base, SessionLocal, engine

from database.models import AgentExecutionLog, MomentumRanking

from engine.agent import run_momentum_agent

from engine.momentum import fetch_and_calculate_momentum


logging.basicConfig(

    level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s'

)

logger = logging.getLogger(__name__)



def init_db():

  logger.info('Initializing database tables...')

  Base.metadata.create_all(bind=engine)



def main():

  logger.info('Starting Sector ETF Momentum Agent pipeline...')

  init_db()


  db = SessionLocal()

  try:

    logger.info('Fetching market data and calculating momentum...')

    df_rankings = fetch_and_calculate_momentum()


    today = date.today()


    logger.info('Persisting momentum rankings to database...')

    for _, row in df_rankings.iterrows():

      ranking_record = MomentumRanking(

          date=today,

          ticker=row['Ticker'],

          sector_name=row['Sector'],

          momentum_score=row['Raw Score'],

          rank=int(row['Rank']),

      )

      db.merge(ranking_record)

    db.commit()


    logger.info('Invoking Gemini AI Agent for market commentary...')

    agent_output = run_momentum_agent()


    logger.info('--- GEMINI AGENT EXECUTIVE BRIEFING ---')

    print(agent_output)

    logger.info('---------------------------------------')


    log_record = AgentExecutionLog(status='SUCCESS', summary_output=agent_output)

    db.add(log_record)

    db.commit()

    logger.info('Pipeline completed successfully.')


  except Exception as e:

    logger.error(f'Pipeline execution failed: {e}', exc_info=True)

    db.rollback()

    try:

      error_log = AgentExecutionLog(status='FAILED', error_message=str(e))

      db.add(error_log)

      db.commit()

    except Exception as db_err:

      logger.error(f'Failed to write error log to DB: {db_err}')

    raise e

  finally:

    db.close()



if __name__ == '__main__':

  main()


No comments:

Post a Comment

Assignment #5 Due 10/7/26

https://googleclouduconn.blogspot.com/p/data-driven-solutions.html  Using a strategy different then trailing PE my example. Use one of the o...