JustinGuese
python_tradingbot_framework
Jupyter Notebook

Python algorithmic trading bot framework for Kubernetes: backtesting, hyperparameter optimization, 150+ technical analysis indicators (RSI, MACD, Bollinger Bands, ADX), portfolio management, PostgreSQL integration, Helm deployment, CronJob scheduling. Minimal overhead, production-ready, Yahoo Finance data.

Last updated Jul 6, 2026
34
Stars
13
Forks
0
Issues
+1
Stars/day
Attention Score
73
Language breakdown
Jupyter Notebook 86.7%
Python 13.3%
Dockerfile 0.0%
Shell 0.0%
โ–ธ Files click to expand
README

๐Ÿค– Trading Bot Framework

A Production-Ready, Kubernetes-Native Algorithmic Trading System

kubectl create secret generic tradingbot-secrets --from-env-file=.env --namespace=tradingbots-2025 --dry-run=client -o yaml | kubectl apply -f -

Trading Bot Framework

This framework allows developers to build, backtest, and deploy automated trading strategies as Kubernetes CronJobs. It handles the "boring stuff"โ€”data ingestion, technical analysis, database persistence, and portfolio trackingโ€”so you can focus on the alpha.

๐Ÿš€ Why this Framework?

  • Batteries Included: 150+ Technical Indicators (RSI, MACD, etc.) ready out of the box.
  • Infrastructure as Code: Native Helm charts for easy scaling on K8s.
  • Data Consistency: Built-in caching and PostgreSQL persistence for trade history and market data.
  • Backtesting to Production: One class handles local testing, hyperparameter optimization, and live execution.

๐Ÿ›  System Architecture

The system is designed to be lightweight and stateless. Each "Bot" is a containerized instance triggered by a schedule.

  • Ingestion: Fetches data from Yahoo Finance (with DB caching).
  • Analysis: Enriches data with the ta library (Technical Analysis).
  • Execution: BotClass manages the state of your portfolio in PostgreSQL.
  • Monitoring: Real-time performance tracking via the included Dashboard.

โšก Quick Start

1. Requirements

  • Python 3.12+ (We recommend uv for speed)
  • Docker (for local DB)

2. Launch Local Environment

# Start PostgreSQL
docker run -d --name pg-trading -e POSTGRESPASSWORD=pass -e POSTGRESDB=tradingbot -p 5432:5432 postgres:17-alpine

Install project

uv sync export POSTGRES_URI="postgresql://postgres:pass@localhost:5432/tradingbot"

3. Your First Strategy

Create a simple RSI Mean Reversion bot in seconds:

from tradingbot.utils.botclass import Bot

class RSIBot(Bot): def init(self): super().init("RSIBot", "AAPL", interval="1m", period="1d")

def decisionFunction(self, row): if row["momentum_rsi"] < 30: return 1 # Buy if row["momentum_rsi"] > 70: return -1 # Sell return 0

if name == "main": bot = RSIBot() bot.run() # Single iteration

4. Local Development & Testing

Backtest your strategy before going live:

bot = RSIBot()
results = bot.localbacktest(initialcapital=10000.0)
print(f"Sharpe Ratio: {results['sharpe_ratio']:.2f}")
print(f"Yearly Return: {results['yearly_return']:.2%}")

Optimize hyperparameters automatically:

class RSIBot(Bot):
    # Define search space
    param_grid = {
        "rsi_buy": [25, 30, 35],
        "rsi_sell": [65, 70, 75],
    }

def init(self, rsibuy=30.0, rsisell=70.0, **kwargs): super().init("RSIBot", "AAPL", interval="1m", period="1d", **kwargs) self.rsibuy = rsibuy self.rsisell = rsisell

def decisionFunction(self, row): if row["momentumrsi"] < self.rsibuy: return 1 if row["momentumrsi"] > self.rsisell: return -1 return 0

Optimize and backtest

bot = RSIBot() bot.local_development() # Finds best params, then backtests

Key Features:

  • Data pre-fetching: Historical data fetched once, reused for all parameter combinations
  • Database caching: Data persisted to DB, subsequent runs are instant
  • Parallel execution: Uses multiple CPU cores automatically
  • Automatic QuantStats reports: Generated and uploaded to Google Cloud Storage with Sharpe/return optimizations and drawdown analysis

๐Ÿ“ˆ Dashboard & Monitoring

The framework includes a built-in visualization suite to track your bots' performance. Each backtest generates a professional QuantStats report with cumulative returns, drawdown analysis, monthly heatmaps, and more (see example).

Portfolio Overview

Overview Dashboard shows:

  • Current Worth, Total Return %, Annualized Return %
  • Sharpe Ratio, Sortino Ratio, Max Drawdown %
  • Volatility, Total Trades, Start Date
Bot Detail Page

Bot Detail Page includes:

  • Portfolio value charts, daily returns distribution
  • Monthly returns heatmap, drawdown visualization
  • Current holdings table, complete trade history
The dashboard is deployed automatically with the Helm chart. See Deployment for setup.

๐Ÿ— Deployment

Production (Kubernetes)

The system treats every bot as a CronJob. Define your schedule in values.yaml and deploy:

1. Create Kubernetes Secret:

# Create .env file with:

POSTGRES_PASSWORD=yourpassword

POSTGRES_URI=postgresql://postgres:yourpassword@psql-service:5432/postgres

OPENROUTERAPIKEY=yourkey (if using AI bots)

BASICAUTHPASSWORD=yourpassword (for dashboard)

Create namespace

kubectl create namespace tradingbots-2025

Create secret

kubectl create secret generic tradingbot-secrets \ --from-env-file=.env \ --namespace=tradingbots-2025

2. Configure Bots:

# helm/tradingbots/values.yaml
bots:
  - name: rsibot
    schedule: '/5    1-5' # Every 5 mins, Mon-Fri

3. Deploy:

helm upgrade --install tradingbots \
  ./helm/tradingbots \
  --create-namespace \
  --namespace tradingbots-2025

PostgreSQL is automatically deployed via Helm (if postgresql.enabled: true in values.yaml).

For detailed guides, see:

๐Ÿงฐ Developer Reference

Bot Implementation Levels

See the comprehensive Bot Implementation Levels guide for detailed explanations, examples, trade-offs, and pitfalls for each pattern.

Quick summary:

1. Simple (Recommended): decisionFunction(row) For single-row TA signals. The base class handles data fetching, self.data (full history slice), and buy/sell execution automatically โ€” no makeOneIteration needed:

def decisionFunction(self, row):
    if row["momentum_rsi"] < 30: return 1
    if row["momentum_rsi"] > 70: return -1
    return 0

If your signal needs the full historical DataFrame (e.g. Hurst exponent, rolling z-scores), use self.data โ€” the base class sets it automatically before calling decisionFunction:

def decisionFunction(self, row):
    lookback = self.data.tail(50)  # self.data is always the slice up to current bar
    zscore = (row["close"] - lookback["close"].mean()) / lookback["close"].std()
    return 1 if zscore < -2 else (-1 if zscore > 2 else 0)

1b. Multi-Asset: tickers=[...] + decisionFunction(row) For strategies that trade multiple instruments. The framework calls decisionFunction once per ticker per bar, sets self.currentticker and populates self.datas with all tickers' history up to the current bar:

def init(self):
    super().init("MyBot", tickers=["QQQ", "GLD", "TLT"], interval="1d", period="1y")

def decisionFunction(self, row): ticker = self.currentticker # self.datas[ticker] has history up to current bar for all tickers ... return 1 # -1 or 0

2. Override makeOneIteration() โ€” only when you need external APIs or portfolio-weight rebalancing that can't map to -1/0/1 signals:

def makeOneIteration(self):
    feargreed = getfeargreedindex()  # External API โ€” not backtestable
    if fear_greed >= 70: self.buy("QQQ")
    return 1
def makeOneIteration(self):
    data = self.getYFDataMultiple(["QQQ", "GLD", "TLT"])
    weights = optimize_portfolio(data)  # Portfolio optimizer โ€” outputs weights, not signals
    self.rebalancePortfolio(weights)
    return 0
Backtesting: Only bots using decisionFunction() (Levels 1 and 1b) are backtestable
via local_backtest(). Bots that override makeOneIteration() with external API calls,
AI models, or portfolio-weight optimizers cannot be replayed on historical data and must
be validated via live runs. If your strategy is yfinance-only, prefer decisionFunction.

Key Methods

| Method | Description | | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | | getYFDataWithTA() | Fetches OHLCV + 150 indicators. | | decisionFunction(row) | Logic applied to every candle. Return -1, 0, 1. | | makeOneIteration() | Override for custom logic. | | local_backtest() | Simulates strategy performance on historical data. | | local_development() | Optimize hyperparameters + backtest. | | buy(symbol) / sell(symbol) | Automated portfolio and DB logging. | | rebalancePortfolio(weights) | Rebalance to target weights. | | runai(systemprompt, usermessage) | Runs AI with tools (main LLM); returns model response. Requires OPENROUTERAPI_KEY. | | runaisimple(systemprompt, usermessage) | Single-turn, no tools (cheap LLM); for summarization, extraction, classification. | | runaisimplewithfallback(systemprompt, usermessage, sanitycheck=..., fallbackto_main=True) | Cheap LLM first; validates output; retries with main LLM if sanity check fails. |

AI Tools (LangChain + OpenRouter)

Two LLMs: main (OPENROUTERMAINMODEL, default deepseek/deepseek-v3.2) for tool-using flows; cheap (OPENROUTERCHEAPMODEL, default openrouter/free) for simple single-turn text tasks. Set OPENROUTERAPIKEY (required); optionally set the two model env vars.

With tools (main LLM):

response = bot.run_ai(
    system_prompt="You are a trading assistant.",
    user_message="Summarize my recent trades and portfolio."
)
print(response)  # Model response as string

Simple tasks, no tools (cheap LLM): summarization, extraction, classification, rewriting:

summary = bot.runaisimple(
    system_prompt="You summarize in one sentence.",
    user_message="Summarize: ..."
)

Cheap-first with fallback: Try cheap LLM first, validate output for sanity, and retry with main LLM if the result fails. Use for simple tasks when you want to save cost but guarantee sane results:

result = bot.runaisimplewithfallback(
    system_prompt="You classify sentiment.",
    user_message="Classify as buy/hold/sell: ...",
    sanity_check=None,   # optional; default rejects empty/refusal/error prefix
    fallbacktomain=True,
)

Tools available to the model (when using run_ai):

  • getmarketdata(symbol, period) โ€“ Market data (OHLCV), default last two weeks.
  • getportfoliostatus() โ€“ Current portfolio worth (USD) and holdings.
  • getrecenttrades(limit) โ€“ Recent trades; for sells, profit of the closed trade is shown.
  • getstocknews(symbol, limit) โ€“ Recent news for a symbol (title, link, publisher, published_at) from the database.
  • getstockearnings(symbol, limit) โ€“ Recent earnings dates and EPS (estimate, reported, surprise %) for a symbol from the database.
  • getstockinsider_trades(symbol, limit) โ€“ Recent insider transactions (insider, type, shares, value) for a symbol from the database.
See AI Tools Guide and AITools API for details.

Portfolio Structure

Portfolio is stored as JSON in the database:

portfolio = {
    "USD": 10000.0,      # Cash
    "QQQ": 5.5,          # Holdings (quantity, not value)
    "AAPL": 10.0,        # More holdings
}

Access via: bot.dbBot.portfolio.get("USD", 0)

Available Indicators

Access over 150 indicators via the row object:

  • Trend: trendmacd, trendadx, trendichimokua, trendsmafast, trendsmaslow
  • Momentum: momentumrsi, momentumstoch, momentumao, momentumroc, momentum_ppo
  • Volatility: volatilitybbh (Bollinger High), volatilitybbl (Bollinger Low), volatility_atr
  • Volume: volumevwap, volumeobv, volume_mfi
See Technical Analysis Guide for complete list.

๐Ÿ“– Documentation

Online Documentation: justinguese.github.io/pythontradingbot_framework/

Getting Started

  • Quick Start Guide - Complete local development workflow with PostgreSQL setup, bot creation at different abstraction levels, backtesting, and hyperparameter tuning
  • Installation - System requirements and dependency installation
  • Creating a Bot - Detailed bot creation patterns and examples

Deployment

Guides

API Reference

๐Ÿ“ก Telegram Channel Monitor

The framework includes an optional Telegram channel monitor that polls channels for new messages, summarizes them with AI, extracts the primary asset ticker, and writes results to the database.

# helm/tradingbots/values.yaml
telegramMonitor:
  enabled: true
  schedule: '/30    *' # Every 30 minutes
  channels: 'somenewschannel,-1001234567890'
  fetchLimit: '50'

How it works: Runs as a CronJob โ€” connects via a Telethon StringSession (no persistent process), fetches recent messages, skips already-stored ones, summarizes each with the cheap LLM, and persists to telegrammessages table with channel, text, summary, symbol, and publishedat.

Required secrets: TELEGRAMAPIID, TELEGRAMAPIHASH, TELEGRAMSESSIONSTRING (from my.telegram.org).

See Telegram Monitor Guide for full setup instructions.

๐Ÿ“ˆ Live Trading (Collective2, Interactive Brokers, eToro & Darwinex)

[!WARNING]
DISCLAIMER: This software is for educational and research purposes only. Trading involves significant risk of loss and is not suitable for all investors. Use of "Live Trading" features is strictly at your own risk. The authors and contributors are not liable for any financial losses, damages, or unintended trades incurred. Always test strategies thoroughly in a paper-trading environment before deploying real capital.

The framework can mirror your paper-bot portfolios to a live brokerage account. Supported brokers: Collective2 (World API v4), Interactive Brokers (via IB Gateway / ib_async), eToro (Public REST API), and Darwinex (DXtrade API).

1. Configure Environment

Add these to your .env or Kubernetes secrets:

# Collective2
COLLECTIVE2APIKEY=yourapikey
COLLECTIVE2SYSTEMID=12345678

Interactive Brokers (IB Gateway must be running)

IBGATEWAYHOST=127.0.0.1 IBGATEWAYPORT=4004 IBCLIENTID=17 IBACCOUNTID=DU1234567 # paper accounts start with DU; live with U

eToro (Public REST API)

ETOROAPIKEY=yourpublickey ETOROUSERKEY=youruserkey ETORO_DEMO=true # true for demo/paper, false for live

Darwinex (DXtrade API)

DARWINEXUSERNAME=yourusername DARWINEXPASSWORD=yourpassword DARWINEXACCOUNTID=12345 # optional DARWINEX_DEMO=true # true for demo/paper, false for live

Shared

LIVETRADEBOTWEIGHTS='{"adaptivemeanreversionbot": 1.0}' LIVETRADEDRYRUN=false

Inspect Account & Portfolio

Each broker module is runnable directly to print the account summary and current positions โ€” useful for sanity-checking credentials, account IDs, and mappings before running the copier:

# Collective2
uv run python tradingbot/livetrade/collective2.py

Interactive Brokers (read-only connection; uses IBCLIENTID=19 by default

so it won't collide with the cron client id 17 or vscode debug 18)

uv run python tradingbot/livetrade/interactive_brokers.py

eToro (reads ETOROAPIKEY, ETOROUSERKEY, ETORO_DEMO from .env)

uv run python tradingbot/livetrade/etoro.py

Darwinex (reads DARWINEXUSERNAME, DARWINEXPASSWORD, DARWINEX_DEMO from .env)

uv run python tradingbot/livetrade/darwinex.py

2. Map Your Tickers

Yfinance symbols often differ from broker symbols (e.g., EURUSD=X vs EURUSD). The framework includes an Assisted Ticker Discovery script to help you map them:

# 1. Discover unmapped tickers from your bots and trades
uv run python -m tradingbot.livetrade.discover_symbols

2. Edit symbol_map.review.json in your editor

Add "selectedsymbol" and "selectedtype" for the tickers you want to map.

3. Apply the approved mappings to the master symbol_map.json

uv run python -m tradingbot.livetrade.discover_symbols --apply

3. Deploy the Copier

The copier runs as a standalone script per broker. Deploy as a CronJob to run shortly after your trading bots:

# Collective2
uv run python tradingbot/livetrade_collective2.py

Interactive Brokers

uv run python tradingbot/livetradeinteractivebrokers.py

eToro

uv run python tradingbot/livetrade_etoro.py

Darwinex

uv run python tradingbot/livetrade_darwinex.py

Each broker is its own Helm CronJob gated by an independent flag in values.yaml โ€” enable them separately (liveTrade.collective2.enabled, liveTrade.interactiveBrokers.enabled, liveTrade.etoro.enabled, liveTrade.darwinex.enabled), so you can run any combination or none. All default to false. You can also cap how much of the account each broker mirrors via LIVETRADEPORTFOLIOFRACTION (default 1.0 = full account; e.g. 0.5 = half).

See the Live Trading Guide for advanced configuration and mapping rules.

๐ŸŽฏ Example Bots

  • eurusdtreebot.py - Decision tree-based strategy for EUR/USD
  • feargreedbot.py - Uses Fear & Greed Index API for market sentiment
  • swingtitaniumbot.py - Swing trading strategy
  • xauzenbot.py - Gold (XAU) trading bot
  • sharpeportfoliooptweekly.py - Portfolio optimization with Sharpe ratio
  • aihedgefundbot.py - AI-driven portfolio rebalancing
  • deepseektoolbot.py - AI with tools (research + submit weights); cheap LLM sanity-check and main-LLM retry
  • gptbasedstrategytabased.py - GPT-based strategy with technical analysis
See Example Bots for implementation details.
๐Ÿ”— More in this category

ยฉ 2026 GitRepoTrend ยท JustinGuese/python_tradingbot_framework ยท Updated daily from GitHub