Audience: Developers, contributors
Summary: This guide explains how to set up the development environment and contribute to Stonks Overwatch.
Note for AI Agents: For AI-specific guidance, code patterns, and validation workflows, see
AGENTS.mdin the project root.
Stonks Overwatch is a portfolio tracker integrating with multiple brokers (DEGIRO, Bitvavo, IBKR). The system features a unified broker architecture with factory patterns, dependency injection, interface-based design, and a centralized broker registry that simplifies development and maintenance.
The first step is to check out the code. It can be done with the GitHub CLI
gh repo clone ctasada/stonks-overwatchor plain git
git clone ctasada/stonks-overwatchYou can execute make start, it will install and configure everything needed to run.
To update all dependencies to their latest versions:
make updateThis command will:
- Update Poetry itself
- Update all Python dependencies
- Update Node.js dependencies
- Regenerate third-party licenses file
Note: Use
make updateinstead ofpoetry updatedirectly to ensure all dependencies and licenses are properly updated.
Alternatively, you can also use Docker
make docker-runThe application is available at http://127.0.0.1:8000 Open your browser in that URL.
If everything goes fine you should see a login screen for your configured brokers.
Before diving into development, it's important to understand the unified broker architecture that powers Stonks Overwatch:
- BrokerRegistry: Central registry managing all broker configurations and services
- BrokerFactory: Unified factory creating services with automatic dependency injection
- Service Interfaces: Type-safe contracts ensuring consistent broker implementations
- Configuration System: Registry-based configuration supporting multiple brokers
If you're adding new features or brokers, familiarize yourself with these key documents:
- Broker Integration Guide - Complete guide for adding new brokers
- Architecture Overview - System design and improvements
- Authentication System - Authentication patterns and flows
The unified broker architecture uses a factory pattern for creating broker services:
from stonks_overwatch.core.factories.broker_factory import BrokerFactory
from stonks_overwatch.core.service_types import ServiceType
# Create a factory instance
factory = BrokerFactory()
# Request a specific service for a broker
portfolio_service = factory.create_service("degiro", ServiceType.PORTFOLIO)This pattern ensures consistent service creation across all brokers with automatic dependency injection.
To develop the application, it's important to have installed in your system the next dependencies:
- Python 3.13 or higher
- Poetry 2.2.1 or higher
The application can run in docker, which will create all the needed dependencies in the container. In this case you only need to have a Docker daemon installed.
make docker-runmake runExecuting this command should install all the dependencies and run the application
make helpWill show all the available commands. The Makefile provides convenient wrappers to simplify the work, check the code if you are interested in the internals.
The most relevant commands are:
make run profile=true debug=truePassing the parameter profile=true will enable profiling, and debug=true will log in debugging mode
Those parameters are optional, most of the time make run is enough
make lint-check
make lint-fixThese commands will check the code linting or do the best effort to properly format the code
Make sure to execute make pre-commit-install to install the pre-commit hooks, so the code is automatically checked
before committing.
The project uses pre-commit hooks to maintain code quality. The hooks include:
- Code formatting (Ruff)
- Python linting (Ruff)
- YAML validation
- Markdown linting
- Link checking (Lychee)
- Poetry validation
- Django system checks
- Test execution
Installing hooks:
make pre-commit-installRunning hooks manually:
make pre-commit-runUpdating hook versions:
make pre-commit-updateNote on Lychee: The link checker (Lychee) uses a non-standard tag format (
lychee-vX.Y.Z) which is excluded from automatic updates. Themake pre-commit-updatecommand only updates thepre-commit-hooksandruff-pre-commitrepositories to prevent reverting Lychee to a mutablenightlybranch reference. If you need to update Lychee, manually edit.pre-commit-config.yamland change therevto the desired stable tag (e.g.,lychee-v0.22.0).
make testWill execute the test suite, generating coverage report
# Check broker registration status
python manage.py shell
>>> from stonks_overwatch.core.factories.broker_registry import BrokerRegistry
>>> registry = BrokerRegistry()
>>> print("Registered brokers:", registry.get_fully_registered_brokers())
>>> print("Broker capabilities:", {name: registry.get_broker_capabilities(name) for name in registry.get_registered_brokers()})# Verify service factory works correctly
from stonks_overwatch.core.factories.broker_factory import BrokerFactory
from stonks_overwatch.core.service_types import ServiceType
factory = BrokerFactory()
for broker in ["degiro", "bitvavo", "ibkr"]:
for service_type in [ServiceType.PORTFOLIO, ServiceType.TRANSACTION, ServiceType.ACCOUNT]:
try:
service = factory.create_service(broker, service_type)
print(f"✅ {broker} {service_type.value}: {service}")
except Exception as e:
print(f"❌ {broker} {service_type.value}: {e}")The demo database can be used with make run demo=true. The application will automatically route all database operations to the demo database using the built-in database routing system.
make briefcase-packageWill create the installer locally. Only the installer for the operating system where the code is being executed will be created. For example, if you are working on macOS, only the macOS package will be created.
make briefcase-runAs before, only the application for the current OS will be created and executed.
make cicdWill execute the GitHub Actions locally using ACT. It's a good way of validating changes in the CI/CD code before pushing to GitHub.
ACT requires a GitHub Personal Access Token to clone GitHub Actions (like actions/setup-python, actions/cache, etc.).
Initial Setup:
-
Create a GitHub Personal Access Token:
- Go to: https://github.com/settings/tokens/new
- Token name: "ACT Local Testing"
- Expiration: Your choice (90 days recommended)
- Select scopes: Check "public_repo" (for public repositories)
- Click "Generate token"
- Copy the token (you won't see it again!)
-
Create
.secretsfile:cp .secrets.example .secrets
-
Edit
.secretsand add your token:GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Replace
ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwith your actual token. -
Test it:
make cicd job=lint
Security Note: The
.secretsfile is already in.gitignoreand will never be committed to the repository.
Running Specific Jobs:
# Run the lint job
make cicd job=lint
# Run the test job
make cicd job=test
# List all available jobs
make cicdRunning Specific Workflows:
# Run the entire CI/CD workflow
make cicd workflow=cicd
# Run the pre-commit autoupdate workflow
make cicd workflow=precommit-autoupdateTroubleshooting ACT:
If you see authentication errors like authentication required: Invalid username or token, ensure:
- Your
.secretsfile exists and contains a validGITHUB_TOKEN - The token has the
public_reposcope enabled - You've copied the token correctly (no extra spaces or newlines)
Note on Lychee Link Checking: The link checking step (lychee) is automatically skipped when running with ACT due to architecture compatibility issues (ARM64 vs x86_64). The lychee check will still run in the actual GitHub Actions environment. You can verify links locally by running:
make markdown-links-checkOr through pre-commit hooks:
make pre-commit-runmake run profile=true debug=truePassing the parameter profile=true will enable profiling, and debug=true will log in debugging mode.
The debug=true parameter is useful to see the logs in the console, but it will also generate a lot of output, so use it only when needed.
The provided information may help you to identify the problem.
To troubleshoot performance issues, you can use the profile=true parameter. This parameter enables Pyinstrument
to help profiling the application. Profile a web request in Django
provides the most up-to-date information on how to profile a web request in Django.
TL;DR: Use
make run profile=trueand add?profileto the end of a request URL to activate the profiler
poetry run python ./scripts/init_db.py --helpThis command will create a new database with the initial data from your configured broker accounts (DEGIRO, Bitvavo, IBKR).
This script expects the
config/config.jsonfile to be present and properly configured with your broker credentials.
The configuration system supports multiple brokers simultaneously:
{
"degiro": {
"enabled": true,
"credentials": {
"username": "your_username",
"password": "your_password"
}
},
"bitvavo": {
"enabled": true,
"credentials": {
"key": "your_api_key",
"secret": "your_api_secret"
}
},
"ibkr": {
"enabled": false
}
}The demo database allows users to explore the application features without connecting to real broker accounts. It contains synthetic transaction data and market information for demonstration purposes.
To regenerate the demo database from scratch:
poetry run python -m scripts.generate_demo_db \
--start-date "2024-01-01" \
--num-transactions 150 \
--initial-deposit 10000 \
--monthly-deposit 500This command will:
- Create a fresh demo database with synthetic transaction data
- Generate realistic market data for popular stocks and ETFs
- Copy the database to
src/stonks_overwatch/fixtures/demo_db.sqlite3for bundling with Briefcase distributions - The bundled template should be committed to version control
For more details on available parameters:
poetry run python -m scripts.generate_demo_db --helpImportant: After generating a new demo database, commit the updated
src/stonks_overwatch/fixtures/demo_db.sqlite3file to git. This ensures the latest demo data is bundled with all distributions.
When users activate demo mode via the application menu:
- The application checks if a demo database exists in the user's data directory
- If the bundled demo database is different (detected by comparing SHA256 hashes):
- The existing demo database is backed up to
demo_db.sqlite3.backup - The new demo database is copied from the application bundle
- This ensures users always get the latest demo data after app updates
- The existing demo database is backed up to
- The application switches to demo mode and applies any pending migrations
- All broker API connections are disabled in demo mode
The demo database is distributed as a pre-populated SQLite file (approximately 384KB), providing instant access to demo features.
Note: When updating the application to a new version with updated demo data, the old demo database is automatically backed up. Users' actual portfolio data in the production database is never affected by demo mode operations.
- Bundled template:
src/stonks_overwatch/fixtures/demo_db.sqlite3(read-only, in git) - User's working copy:
$STONKS_OVERWATCH_DATA_DIR/demo_db.sqlite3(created on first demo activation)
The demo database can be used with make run demo=true. The application will automatically route all database operations to the demo database using the built-in database routing system.
The application features an advanced database routing system that allows seamless switching between production and demo databases without requiring server restarts.
The application supports two database configurations:
- Production Database (
db.sqlite3): Contains real user data - Demo Database (
demo_db.sqlite3): Contains demo/sample data for testing
The DatabaseRouter class automatically routes all database operations to the appropriate database based on the DEMO_MODE environment variable:
- When
DEMO_MODE=False(or unset): Routes to the production database - When
DEMO_MODE=True: Routes to the demo database
Both databases are defined in settings.py:
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": Path(STONKS_OVERWATCH_DATA_DIR).resolve().joinpath("db.sqlite3"),
# ... production database settings
},
"demo": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": Path(STONKS_OVERWATCH_DATA_DIR).resolve().joinpath("demo_db.sqlite3"),
# ... demo database settings
},
}
DATABASE_ROUTERS = ["stonks_overwatch.utils.database.db_router.DatabaseRouter"]- No Server Restart Required: Database switching happens instantly
- Data Isolation: Production and demo data are completely separate
- Transparent Operation: All existing code works without modification
- Consistent Schema: Both databases maintain the same structure through migrations
Both databases support migrations independently:
# Apply migrations to production database
python manage.py migrate --database=default
# Apply migrations to demo database
python manage.py migrate --database=demoThe router ensures migrations can be applied to both databases as needed, maintaining schema consistency.
poetry run python ./scripts/dump_db.py --help
poetry run python ./scripts/dump_db.py dump [--output filename.json]
poetry run python ./scripts/dump_db.py load --input filename.jsonAllows dumping the current database to a file and loading it back. This is useful for testing purposes or to share the database with other developers.
The application can run in offline mode for any broker, without needing to connect to their APIs. This is useful for development and testing.
Example configuration to enable offline mode for multiple brokers:
{
"degiro": {
"enabled": true,
"offline_mode": true
},
"bitvavo": {
"enabled": true,
"offline_mode": true
},
"ibkr": {
"enabled": true,
"offline_mode": true
}
}The offline mode can be used together with demo=true to load the demo database and run the application without any external API calls. The database routing system ensures that demo data is completely isolated from production data, making it safe to experiment with different configurations.
When developing features for specific brokers, you can selectively enable/disable them:
{
"degiro": {"enabled": true},
"bitvavo": {"enabled": false},
"ibkr": {"enabled": false}
}This allows you to focus on one broker at a time during development.
While working with the Native App, both while running from code with make briefcase-run or with the installed application,
the code will create files in the following path:
- STONKS_OVERWATCH_DATA_DIR:
/Users/$USER/Library/Application\ Support/com.caribay.stonks_overwatch - STONKS_OVERWATCH_CONFIG_DIR:
/Users/$USER/Library/Preferences/com.caribay.stonks_overwatch - STONKS_OVERWATCH_LOGS_DIR:
/Users/$USER/Library/Logs/com.caribay.stonks_overwatch - STONKS_OVERWATCH_CACHE_DIR:
/Users/$USER/Library/Caches/com.caribay.stonks_overwatch
It's possible to easily delete them with make briefcase-clean
To debug the native application, you can enable debugging mode by setting the environment variable STONKS_OVERWATCH_DEBUG=true.
When running the application with make briefcase-run, you can do it like this:
make briefcase-run debug=true demo=trueIt's also interesting to know that it's possible to debug the UI with:
defaults write com.caribay.stonks-overwatch WebKitDeveloperExtras -bool trueThen, when running your app:
- Open Safari
- Go to Develop menu
- Select your app's window
- Check the Console for errors when you click export