Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
3451cd1
Update app config
piiq Jun 28, 2025
99077df
Migrate the app to use openbb-platform-api and uv
piiq Jun 28, 2025
316761c
Implement storing data in S3 bucket in addition to a local file
piiq Jun 28, 2025
b2b2685
Migrate the agent to use openbb-ai, latest magentic and deepseek via …
piiq Jun 28, 2025
3cda1f3
Update project description
piiq Jun 28, 2025
1c04498
Add llm instructions
piiq Jun 28, 2025
71d9df0
Add tests
piiq Jun 28, 2025
bd6a49b
Move storage related code to a separate file
piiq Jun 28, 2025
ec12a94
Aggregate storage interfaces in relevant classes
piiq Jun 29, 2025
69e19a2
Ensure s3 config is validated before local storage config
piiq Jun 29, 2025
65a39ac
Update the app and tests to be fully async
piiq Jun 30, 2025
96dc203
Rename test file
piiq Jun 30, 2025
b43b18f
Replace ugly nested context managers with patch.multiple
piiq Jun 30, 2025
bb9154c
Set FMP key from environment if it's not picked up from local settings
piiq Jul 1, 2025
e96c6fb
Update app configuration
piiq Jul 7, 2025
71fe0c3
Make sure each artifact is unique
piiq Jul 7, 2025
ea9ca9c
Expose task history to the UI
piiq Jul 7, 2025
c1a9c02
Implement fetcher-only data retrieval via openbb platform
piiq Jul 7, 2025
4dae478
Update tests
piiq Jul 7, 2025
7989be9
Fix non awaited function bug and linting errors
piiq Jul 7, 2025
e132223
Add columndefs to the allocation widget
piiq Jul 7, 2025
57a27af
Update llm rules
piiq Jul 7, 2025
56d7167
Update widgets settings
piiq Jul 7, 2025
e6773a2
Add apps.json endpoint
piiq Jul 7, 2025
0ac90d4
Update README.md
piiq Jul 7, 2025
e0e3c32
Add app/agent logo image
piiq Jul 7, 2025
d4a9717
Add dockerfile
piiq Jul 7, 2025
fb82872
Update README screenshot
piiq Jul 7, 2025
b7f6d7d
Update usage instructions to prioritize docker
piiq Jul 7, 2025
cd8d54c
Add example k8s manifests
piiq Jul 7, 2025
ddbfd37
Only build image on push to main
piiq Jul 7, 2025
394989e
Add action to lint and test
piiq Jul 7, 2025
bb50e78
Fix tests
piiq Jul 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cursorrules
33 changes: 33 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
*venv*
.vscode
**/*.env
**/*.pyc
**/data/**/*
*.log
.git
.gitignore
.github
__pycache__
.pytest_cache
*.pyc
*.pyo
*.pyd
.coverage
htmlcov/
.tox/
.mypy_cache/
.DS_Store
Thumbs.db

LICENSE

tests/
.codex/
.claude/
.gemini/
.aider/
llms.md
CLAUDE.md
GEMINI.md
Dockerfile
.dockerignore
20 changes: 14 additions & 6 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
# Application configuration
HOST_URL=http://localhost:4322 # The host URL and port number where the app is running.
API_KEYS_FILE_PATH=api_keys.txt # The path to the file containing API keys to access the bot.
APP_API_KEY=my_api_key # The API key to access the bot.
DATA_FOLDER_PATH=data # The path to the folder that will store the allocation data.

# AI configuration
OPENAI_API_KEY=<your-openai-key>
MAGENTIC_OPENAI_MAX_TOKENS=4096
MAGENTIC_OPENAI_TEMPERATURE=0.42
MAGENTIC_BACKEND=openai
MAGENTIC_OPENAI_MODEL=gpt-4o
OPENROUTER_API_KEY=

# S3 configuration
S3_ENABLED=false # Set to true to enable S3 storage
S3_ENDPOINT= # S3 endpoint URL
S3_ACCESS_KEY= # S3 access key
S3_SECRET_KEY= # S3 secret key
S3_BUCKET_NAME= # S3 bucket name
ALLOCATION_DATA_FILE=allocations.json # File name to store allocations
TASK_DATA_FILE=tasks.json # File name to store tasks

# Data retrieval configuration
FMP_API_KEY=
54 changes: 54 additions & 0 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Build and Publish Docker Image

on:
push:
branches:
- main

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
35 changes: 35 additions & 0 deletions .github/workflows/lint_and_test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Run Lints and Tests

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
test:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v4
with:
version: "latest"

- name: Set up Python
run: uv python install

- name: Install dependencies
run: uv sync --extra dev

- name: Run linting
run: uv run ruff check .

- name: Check code formatting
run: uv run black --check .

- name: Run tests
run: uv run pytest
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
venv
*venv*
.vscode
**/*.env
**/*.pyc
**/data/**/*
*.log
*api_keys*.*
.coverage
.claude
4 changes: 4 additions & 0 deletions .markdownlint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"MD033": false,
"MD013": false
}
1 change: 1 addition & 0 deletions CLAUDE.md
33 changes: 33 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Multi-stage Dockerfile for allocator-bot
# Build stage
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS builder
ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy
ENV UV_PYTHON_DOWNLOADS=0

RUN apt-get update && apt-get install -y build-essential git

WORKDIR /app
COPY . /app
RUN uv sync --locked --no-dev

# Runtime stage
FROM python:3.12-slim-bookworm
WORKDIR /app

# Create non-root user
RUN groupadd --gid 1000 app && useradd --uid 1000 --gid app --shell /bin/bash --create-home app

# Copy application from builder
COPY --from=builder --chown=app:app /app /app

# Switch to non-root user
USER app

# Place executables in PATH
ENV PATH="/app/.venv/bin:$PATH"

# Expose port
EXPOSE 4299

# Run the application
CMD ["openbb-api", "--app", "allocator_bot.__main__:get_app", "--factory", "--host", "0.0.0.0", "--port", "4299"]
1 change: 1 addition & 0 deletions GEMINI.md
86 changes: 64 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
# Allocator Bot

<p align="center">
<img width="250px" alt="allocator-bot" src="https://github.com/user-attachments/assets/2baee09d-0813-4e44-bc72-b7ef74a818b2" />
</p>

A portfolio optimization copilot for OpenBB that uses PyPortfolioOpt to generate efficient frontier allocations.

## Features

<p align="center">
<img width="1482" alt="dashboard" src="https://github.com/user-attachments/assets/b380c409-6569-47a1-ba73-cfd00d890e6f" />
</p>

- **OpenBB Integration**:
- Plugs into OpenBB Workspace's copilot and widget interfaces
- Fetches price data from FMP via OpenBB Platform's python library
Expand All @@ -21,57 +29,91 @@ A portfolio optimization copilot for OpenBB that uses PyPortfolioOpt to generate
- FMP API access (via [OpenBB Platform](https://docs.openbb.co/platform))
- OpenAI API access
- Dependencies listed in `pyproject.toml`

- **Tech Stack**:

- FastAPI with Starlette SSE for real-time updates
- OpenBB Platform Python library for data access
- PyPortfolioOpt for optimization algorithms
- Pydantic for data validation
- Magentic for LLM interactions

## Installation
## Installation & Usage

### Docker (Recommended)

The easiest way to run the Allocator Bot is using Docker:

```bash
docker run --rm -it --name allocator-bot \
-e HOST_URL=http://localhost:4322 \
-e APP_API_KEY=your_api_key \
-e OPENROUTER_API_KEY=your_openrouter_key \
-e FMP_API_KEY=your_fmp_key \
-e DATA_FOLDER_PATH=data \
-e S3_ENABLED=false \
-p 4299:4299 \
ghcr.io/piiq/allocator-bot:latest
```

**Required Environment Variables:**

- `HOST_URL`: The host URL where the app is running (e.g., `http://localhost:4322`)
- `APP_API_KEY`: Your API key to access the bot
- `OPENROUTER_API_KEY`: Your OpenRouter API key for LLM access
- `FMP_API_KEY`: Your Financial Modeling Prep API key for market data

**Optional Environment Variables:**

- `DATA_FOLDER_PATH`: Local storage path (default: `data`)
- `S3_ENABLED`: Enable S3 storage (default: `false`)
- `S3_ENDPOINT`, `S3_ACCESS_KEY`, `S3_SECRET_KEY`, `S3_BUCKET_NAME`: S3 configuration (if enabled)

### Alternative Installation Methods

#### Install with pip from GitHub

```bash
pip install git+https://github.com/piiq/allocator-bot.git
```

#### Development Installation

1. Clone the repository:

```bash
git clone https://github.com/yourusername/allocator-bot.git
git clone https://github.com/piiq/allocator-bot.git
cd allocator-bot
```

2. Install dependencies using Poetry:
2. Install dependencies:

```bash
poetry install
uv sync --extra dev
```

## Configuration

- Copy `.env.example` to `.env` and fill in the values.

## Usage
3. Copy `.env.example` to `.env` and fill in the values
4. Start the server:

Start the server on localhost:

```bash
python main.py
```
```bash
allocator-bot
```

### Adding to OpenBB Workspace

1. **Add as a Copilot**:

- Click on the OpenBB Copilot dropdown
- Click "Add Copilot"
- Enter the server URL (e.g., `http://localhost:4322` for local deployment)
- Add authorization header with the API key from `API_KEYS_FILE_PATH`
- Enter the server URL (e.g., `http://localhost:4299` for Docker deployment)
- Add authorization header with your API key
- Header name: `Authorization`
- Header value: `Bearer <API_KEY>`
- Header value: `Bearer <your_api_key>` (same as `APP_API_KEY` environment variable)
- Click "Create"

2. **Add as a Widget Source**:
- Click "Add Data" on your dashboard
- Go to "Custom Backends"
- Select "Allocator Bot Backend"
2. **Add as a an App and Widget Source**:

- Click "Apps" on your dashboard
- Click "Connect Backend" on the Apps page
- Enter the same URL and API key used for the copilot
- Click "Add"

Expand Down
27 changes: 27 additions & 0 deletions allocator_bot/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import logging

# Set up logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[logging.StreamHandler()],
)


def get_app():
"""Get the FastAPI app instance."""
import os

from dotenv import load_dotenv

load_dotenv(os.path.join(os.path.dirname(__file__), "..", ".env"))

from .api import app

return app


if __name__ == "__main__":
print(
"Launch the app with `openbb-api --app allocator_bot.__main__:get_app --factory"
)
Loading