Skip to content

Latest commit

 

History

History
302 lines (223 loc) · 9 KB

File metadata and controls

302 lines (223 loc) · 9 KB

Quick Start

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 this framework (including "Step 5: Run Your Bot Live") 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.

Get your first trading bot up and running locally in minutes. This guide covers local development, testing, and running bots live.

Prerequisites

  • Python 3.12+
  • Docker (for local PostgreSQL)

Step 1: Set Up PostgreSQL

The trading bot system requires a PostgreSQL database to store bot state, trades, and historical data.

Run PostgreSQL with Docker

Start a PostgreSQL container:

docker run -d --name postgres-tradingbot \
  -e POSTGRES_PASSWORD=yourpassword \
  -e POSTGRES_DB=tradingbot \
  -p 5432:5432 \
  postgres:17-alpine

Set Environment Variable

Configure the database connection:

export POSTGRES_URI="postgresql://postgres:yourpassword@localhost:5432/tradingbot"

Note: For production deployment, see the Deployment Guide for Kubernetes/Helm setup.

Step 2: Install Dependencies

Install project dependencies using uv:

uv sync

Step 3: Write Your Own Bot

The trading bot framework supports three abstraction levels, depending on your strategy complexity.

Simple: decisionFunction(row) (Recommended)

For strategies that can be expressed as logic on a single data row with technical indicators.

How it works: The base class fetches data, applies your function to each row, averages the decisions, and executes trades automatically.

Example:

from tradingbot.utils.botclass import Bot

class MyBot(Bot):
    def __init__(self):
        super().__init__("MyBot", "QQQ", interval="1m", period="1d")
    
    def decisionFunction(self, row):
        """
        Trading decision based on technical indicators.
        
        Returns:
            -1: Sell signal
             0: Hold (no action)
             1: Buy signal
        """
        # Access 150+ technical indicators via row["indicator_name"]
        if row["momentum_rsi"] < 30:
            return 1  # Buy - oversold
        elif row["momentum_rsi"] > 70:
            return -1  # Sell - overbought
        return 0  # Hold

Available Indicators: After calling getYFDataWithTA(), you have access to ~150+ indicators:

  • Momentum: momentum_rsi, momentum_stoch, momentum_roc, etc.
  • Trend: trend_macd, trend_adx, trend_sma_fast, etc.
  • Volatility: volatility_bbh, volatility_bbl, volatility_atr, etc.
  • Volume: volume_obv, volume_mfi, volume_vwap, etc.

See Technical Analysis Guide for the complete list.

Medium Complexity: Override makeOneIteration()

For bots that need external APIs, custom data processing, or different timeframe handling.

Example (using external Fear & Greed Index API):

import fear_and_greed
from tradingbot.utils.botclass import Bot

class FearGreedBot(Bot):
    def __init__(self):
        super().__init__("FearGreedBot", "QQQ")
    
    def makeOneIteration(self):
        """
        Custom iteration logic with external API.
        
        Returns:
            -1: Sell signal
             0: Hold
             1: Buy signal
        """
        # Fetch external data
        fear_greed_index = fear_and_greed.get().value
        
        cash = self.dbBot.portfolio.get("USD", 0)
        holding = self.dbBot.portfolio.get("QQQ", 0)
        
        if fear_greed_index >= 70 and cash > 0:
            self.buy("QQQ")  # Extreme greed - buy
            return 1
        elif fear_greed_index <= 30 and holding > 0:
            self.sell("QQQ")  # Extreme fear - sell
            return -1
        return 0  # Hold

Complex: Portfolio Optimization

For multi-asset strategies, portfolio rebalancing, or complex optimization algorithms.

Example (Sharpe ratio portfolio optimization):

from pypfopt import EfficientFrontier, expected_returns, risk_models
from tradingbot.utils.botclass import Bot

class PortfolioBot(Bot):
    def __init__(self):
        super().__init__(
            "PortfolioBot", 
            tickers=["QQQ", "GLD", "TLT", "AAPL"],
            interval="1d",
            period="3mo"
        )
    
    def makeOneIteration(self):
        """
        Rebalance portfolio using optimization.
        
        Returns:
            0: Rebalancing completed
        """
        # Fetch data for multiple symbols
        data = self.getYFDataMultiple(
            self.tickers, 
            interval="1d", 
            period="3mo", 
            saveToDB=True
        )
        
        # Convert to wide format for optimization
        df = self.convertToWideFormat(data, value_column="close")
        
        # Calculate optimal weights
        mu = expected_returns.mean_historical_return(df)
        S = risk_models.sample_cov(df)
        ef = EfficientFrontier(mu, S)
        ef.max_sharpe()
        weights = ef.clean_weights()
        
        # Rebalance portfolio
        self.rebalancePortfolio(weights)
        return 0

Step 4: Local Development Workflow

Before running your bot live, test and optimize it locally.

Backtesting

Test your bot's performance on historical data:

from tradingbot.utils.botclass import Bot

bot = MyBot()

# Run backtest with current parameters
results = bot.local_backtest(initial_capital=10000.0)

print(f"Yearly Return: {results['yearly_return']:.2%}")
print(f"Sharpe Ratio: {results['sharpe_ratio']:.2f}")
print(f"Max Drawdown: {results['maxdrawdown']:.2%}")
print(f"Number of Trades: {results['nrtrades']}")

Hyperparameter Tuning (Optional)

Optimize your bot's parameters automatically:

class MyBot(Bot):
    # Define hyperparameter search space
    param_grid = {
        "rsi_buy": [65, 70, 75],
        "rsi_sell": [25, 30, 35],
        "adx_threshold": [15, 20, 25],
    }
    
    def __init__(self, rsi_buy=70.0, rsi_sell=30.0, adx_threshold=20.0, **kwargs):
        super().__init__("MyBot", "QQQ", interval="1m", period="1d", **kwargs)
        self.rsi_buy = rsi_buy
        self.rsi_sell = rsi_sell
        self.adx_threshold = adx_threshold
    
    def decisionFunction(self, row):
        if row["momentum_rsi"] < self.rsi_buy:
            return 1
        elif row["momentum_rsi"] > self.rsi_sell:
            return -1
        return 0

# Optimize and backtest
bot = MyBot()
bot.local_development()
# Prints best parameters in copy-paste format
# Then backtests with those parameters
# Copy the printed parameters into __init__ defaults

Key Features:

  • Data pre-fetching: Historical data is fetched once and reused for all parameter combinations (dramatically faster)
  • Database caching: Data is saved to DB on first fetch, subsequent runs reuse cached data
  • Parallel execution: Uses multiple CPU cores by default
  • Automatic period adjustment: For minute-level intervals, automatically uses 7 days instead of 1 year (respects Yahoo Finance limits)

Methods:

  • bot.local_development() - Full workflow: optimize + backtest
  • bot.local_optimize() - Just optimize parameters
  • bot.local_backtest() - Just backtest current parameters

Step 5: Run Your Bot Live

Once you're satisfied with backtesting results, run your bot live:

from tradingbot.utils.botclass import Bot

bot = MyBot()
bot.run()  # Executes one iteration, makes trades, logs to database

What bot.run() does:

  1. Calls makeOneIteration() (or uses default implementation with decisionFunction())
  2. Executes buy/sell operations based on the decision
  3. Updates portfolio in the database
  4. Logs the result to RunLog table
  5. Handles errors gracefully (logs to database before re-raising)

Complete Example:

from tradingbot.utils.botclass import Bot

class MyBot(Bot):
    def __init__(self):
        super().__init__("MyBot", "QQQ", interval="1m", period="1d")
    
    def decisionFunction(self, row):
        if row["momentum_rsi"] < 30:
            return 1
        elif row["momentum_rsi"] > 70:
            return -1
        return 0

if __name__ == "__main__":
    bot = MyBot()
    
    # Optional: Test locally first
    # bot.local_backtest()
    # bot.local_development()  # If you have param_grid defined
    
    # Run live
    bot.run()

Save this to tradingbot/mybot.py and run:

python tradingbot/mybot.py

Next Steps