Trade without fear, greed, or actual money — because every fake loss is a real step up.
This repository contains the full MockExchange paper-trading platform:
- MockX Engine – Matching engine, portfolio tracking, and API layer.
- MockX Periscope – Streamlit-based dashboard for visualizing portfolio and orders.
- MockX Oracle – Price feed service (e.g., Binance via CCXT → Valkey).
- MockX Gateway* (external repo) – Lightweight Python wrapper for the MockX Engine API, providing a ccxt-style interface for bots and scripts.
- TL;DR
- 📜 Story
- ✨ Core Features
- 🗺 Architecture & Ecosystem
- 📦 Packages in this Monorepo
- 🚀 Quick Start
- 🚀 How we ship
- 📦 Install from GitHub tags
- 📚 Examples
- 🔧 Environment Configuration
- 🗂 Monorepo Structure
- 📚 Documentation
- 🪪 License
- Stateless, deterministic, no-risk spot-exchange emulator.
- ccxt-compatible API — test bots without touching live markets.
- Externalized price feed (MockX Oracle) so you can swap sources.
- Companion Streamlit dashboard (MockX Periscope) for monitoring.
- Full CLI + REST API + Docker support.
It was 2013, and Bitcoin had just hit a jaw-dropping $300. Someone in our old engineering WhatsApp group brought it up. I asked innocently, “What’s that?”
The response came instantly, dripping with confidence: “You’re too late — this bubble is about to burst…”
Which, in hindsight, was probably the most confidently wrong (and overly cautious) financial advice I’ve ever received.
But something about it intrigued me. I didn’t fully understand it. I didn’t even think it would work — and yet, I bought in. Just 2/3 of a BTC, about 180 €, which, at the time, I mentally wrote off as “money I’ll never see again.” Spoiler: it was the best terrible financial decision I’ve ever made.
I held. And held. And held some more.
Then came 2017 — the year of Lambos, moon memes, and FOMO-induced insomnia. I began checking prices at night before bed, and again first thing in the morning — not for fun, but to confirm whether I was now rich… or still stuck working 9 to 5.
This, of course, led me to the classic rookie move: diversification. I dove into altcoins with names like LTC, TROY, and others I’ve repressed like a bad haircut from high school. Let’s just say: they didn’t go to the moon — they dug a tunnel.
Decision after decision, I watched my gains evaporate in slow motion. Eventually, I realized I needed support — not from a financial advisor (they’d only remind me of my poor decisions), but from something more aligned with my goals — not theirs.
Something logical. Emotionless. Free from fear and greed. Unimpressed by sudden price spikes or Twitter hype. A system that won’t panic sell or chase pumps.
I wanted an intelligent system that could make decisions based on data, not dopamine. Something that would just execute the plan, no matter how boring or unsexy that plan was. Something more disciplined than I’d ever been — able to stay locked on a single task for hours, without fatigue, distraction, or the urge to check the news.
In short, I wanted to build a trader with no feelings — like a psychopath, but helpful.
So in 2020, full of optimism and free time, I enrolled in an AI-for-trading program. I was ready to automate the pain away.
Then… I became a dad.
Suddenly, my trading ambitions were replaced with diapers, sleep deprivation, and learning the fine art of negotiating with toddlers. Needless to say, the bot went on standby — alongside my hobbies, ambitions, and most adult-level reasoning.
Fast forward to 2024. The kids sleep (sometimes), and my curiosity roared back to life. I decided it was time to build — for real. Not to get rich — but because this is what I do for fun: connect dots, explore computer science, study markets, and challenge my past self with fewer emotional trades and more intelligent systems.
But ideas need hardware. So I bought my first Raspberry Pi. Because if I was going to burn time, I wasn’t about to burn kilowatts. I needed something that could run 24/7 without turning my electricity bill into a second mortgage. Resilient, quiet, efficient — like a monk with a TPU, ready to meditate on market patterns in silence for as long as it takes. It wasn’t much, but it was enough to get started.
From there, the system began to grow — and spiral. Scraping prices in real time, keeping databases efficient, aggregating data, archiving old data, writing little scripts that somehow become immortal zombie processes needing to be killed by hand... I genuinely didn’t expect it to be so much.
And yet — I like it. This is how I relax: designing systems no one asked for, solving problems I created myself, and picking up strange new skills in the process — the kind you never set out to learn, but somehow end up mastering.
Which brings us to 2025, and MockExchange: a stateless, deterministic, no-risk spot-exchange emulator that speaks fluent ccxt, pretends it’s real, and stores the last price-tick, balance and order in Valkey (aka Redis) — instead of touching live markets — so you can test, dry-run, and debug your bot without risking a single satoshi.
No more fear. No more “should I have bought?” or “why did I sell?” Just logic, fake orders, and enough tooling to safely build the thing that trades smarter than I did.
- 🧩 Modular architecture — Engine, Periscope, Oracle, and Gateway can run independently or together.
- 🔌 Pluggable components — swap price feeds, dashboards, or clients without touching the core.
- 🌐 ccxt-inspired interface — follows familiar trading API patterns to simplify bot integration.
- 📊 Full visibility — Periscope dashboard for live monitoring of balances, orders, and performance metrics.
- 🔮 Realistic market simulation — Oracle injects live exchange prices into a safe, risk-free trading environment.
- 🚀 Ready for production — Dockerized services with pinned versions, path-filtered CI, and clear interface boundaries.
- 🛠 Developer-friendly — One-command setup, pre-commit hooks, comprehensive testing, and linting.
flowchart TB
subgraph Clients
periscope["MockX Periscope<br/>(Streamlit UI)"]
bot["Trading Bot / Script"]
end
subgraph Infra
redis[("Valkey / Redis")]
engine["MockX Engine 📈"]
end
subgraph External
binance["Binance (Live Market Data)"]
end
bot -->|ccxt-like wrapper| gateway["MockX Gateway 🛡 (external)"]
periscope -->|HTTP/REST| engine
gateway -->|HTTP/REST| engine
engine --> redis
oracle["MockX Oracle 🔮<br/>(ccxt → Redis)"] --> redis
binance -->|ccxt| oracle
%% Color styling for important components
style engine fill:#1976d2,color:#ffffff
style periscope fill:#7b1fa2,color:#ffffff
style oracle fill:#388e3c,color:#ffffff
style redis fill:#f57c00,color:#ffffff
style gateway fill:#d32f2f,color:#ffffff
| Package | Path | Description | README |
|---|---|---|---|
| MockX Valkey | packages/valkey/ |
Redis-compatible database for data persistence. | Valkey README |
| MockX Oracle | packages/oracle/ |
Market data feeder (ccxt → Valkey/Redis). | Oracle README |
| MockX Engine | packages/engine/ |
Core engine (core/), API layer (api/), CLI tools. | Engine README |
| MockX Periscope | packages/periscope/ |
Streamlit dashboard for portfolio and orders. | Periscope README |
Related (external):
- MockX Gateway – https://github.com/didac-crst/mockexchange-gateway
Minimal ccxt-style Python client to interact with the Engine API (install via
piporpoetry).
- Docker and Docker Compose installed
- Git for cloning the repository
# Clone the repository
git clone https://github.com/your-username/mockexchange.git
cd mockexchange
# Setup environment (first time only)
cp .env.example .env
# Edit .env if needed (defaults work for most cases)# Start all services in parallel (default)
make start
# Or start services in dependency order (recommended for first-time setup)
make start-sequentialWhat this launches:
- MockX Valkey (Redis-compatible database) on port 6379
- MockX Oracle (price feed service)
- MockX Engine (trading API) on port 8000
- MockX Periscope (dashboard) on port 8501
- 📊 Dashboard: http://localhost:8501
- 🔌 API: http://localhost:8000
- 📖 API Docs: http://localhost:8000/docs
- 📋 Logs:
make logs
If you prefer to start services individually or connect to external services:
# Start services in order (recommended)
make start-valkey # Database first
make start-oracle # Price feed
make start-engine # Trading API
make start-periscope # Dashboard
# Or stop/restart individual services
make stop-engine # Stop only the API
make restart-oracle # Restart price feed
make logs-periscope # View dashboard logs
# Connect to external services (update .env first)
# VALKEY_HOST=192.168.1.100
# API_URL=http://192.168.1.101:8000
make start-engine # Engine connects to external Valkey
make start-periscope # Periscope connects to external EngineFor contributors and developers:
# 🚀 Complete Development Cycle (Recommended)
make dev # Install deps + format + lint + type-check + test
# 🧪 Testing
make test # Run all unit tests
make test-integration # Run integration tests (requires running services)
make integration # Fresh restart + integration tests (no cache)
make integration-full # Full dev cycle + integration tests
# 🔧 Code Quality
make format # Format code with Ruff
make lint # Run linting with Ruff
make type-check # Run type checking with MyPy (smart filtering)
# 🐳 Service Management
make start # Start all services
make stop # Stop all services
make restart # Restart all services
make restart-no-cache # Restart with fresh builds (no cache)
make status # Show service status
# 📊 Logs
make logs # All service logs
make logs-engine # Engine logs only
make logs-oracle # Oracle logs only
make logs-periscope # Dashboard logs only
make logs-valkey # Database logs only
# 🏷️ Release Management
make release-branch # Create release branch (interactive)
make version # Show current version and tags
# 🔗 GitHub PR Tools
make export-pr-comments PR=123 # Export PR comments to JSON for LLM analysis
make analyze-pr-comments PR=123 # Analyze all comments and generate LLM prompt
make analyze-pr-comments-latest PR=123 # Analyze only latest review (recommended)
make export-and-analyze-pr PR=123 # Export and analyze all comments in one command
make export-and-analyze-pr-latest PR=123 # Export and analyze latest review only (recommended)make logs-valkey # Database logs only make logs-engine # Engine logs only make logs-oracle # Oracle logs only make logs-periscope # Dashboard logs only
make status # Show all service statuses
The MockExchange development workflow has been enhanced with comprehensive automation:
make dev # Does everything: install → format → lint → type-check → test- Installs dependencies (Poetry + pre-commit)
- Formats code (Ruff)
- Runs linting (Ruff)
- Type checking (MyPy with smart filtering)
- Runs tests (All unit tests)
make integration # Fresh restart + integration tests
make integration-full # Full dev cycle + integration tests- Fresh environment: No cached artifacts
- Real integration: Tests against running services
- Complete validation: Perfect for pre-release testing
make type-check # MyPy with smart filtering- Focuses on business logic (ignores framework limitations)
- Zero false positives (no framework noise)
- Catches real type issues in your code
make restart-no-cache # Fresh builds for debugging
make logs-engine # Service-specific logs- Individual service control
- Fresh rebuilds when needed
- Easy log access
make export-and-analyze-pr-latest PR=123 # One-shot latest review analysis (recommended)
make export-and-analyze-pr PR=123 # One-shot all reviews analysis
make export-pr-comments PR=123 # Export PR comments to JSON
make analyze-pr-comments-latest PR=123 # Analyze only latest review
make analyze-pr-comments PR=123 # Analyze all reviews- Export CodeRabbit comments for AI analysis
- Latest review focus - Avoid LLM confusion from multiple reviews
- Organized structure in
scripts/github-pr-tools/ - Cursor integration ready for LLM analysis
- One-shot workflow for quick analysis
- Requires GitHub token (set in
scripts/github-pr-tools/.env)
Setup:
# Create scripts/github-pr-tools/.env with your GitHub token
echo "GITHUB_TOKEN=your_github_token_here" > scripts/github-pr-tools/.env
# One-shot latest review analysis (recommended - less confusion for LLM)
make export-and-analyze-pr-latest PR=123
# Use with Cursor
# Open scripts/github-pr-tools/output/pr_123_comments_latest_review_llm_prompt.txt in Cursor
# Or copy-paste the content into Cursor's chat- Create a feature branch, implement changes.
- Open a Pull Request (PR). CI runs tests and lint.
- When green, merge into
main. - Create a release:
- GitHub UI (Recommended): Releases → Draft a new release → Tag
vX.Y.Z→ Publish - CLI:
git checkout main && git pull --ff-only git tag -a vX.Y.Z -m "MockExchange vX.Y.Z" git push origin vX.Y.Z
- GitHub UI (Recommended): Releases → Draft a new release → Tag
CI will run on the tag to validate the release commit.
For more control or when you need to freeze changes for QA/testing:
- Create feature branch → implement changes
- Open PR → CI runs tests
- Create release branch (automated):
# Interactive mode (recommended) make release-branch # Direct mode ./scripts/create-release-branch.sh patch # 0.1.0 → 0.1.1 ./scripts/create-release-branch.sh minor # 0.1.0 → 0.2.0 ./scripts/create-release-branch.sh major # 0.1.0 → 1.0.0 # Preview what would happen ./scripts/create-release-branch.sh patch --dry-run
- Push the release branch:
git push -u origin release/vX.Y.Z - Tag the release branch:
git tag -a vX.Y.Z -m "Release vX.Y.Z" - Push tag:
git push origin vX.Y.Z - Merge to main after release is validated
The scripts/create-release-branch.sh script provides:
- 🔄 Automatic version calculation - Fetches latest tags and calculates next version
- ✅ Git validation - Checks branch, working directory, and remote sync
- 🛡️ Safety features - Dry-run mode, confirmations, error handling
- 🎨 User-friendly - Colored output, clear instructions, help text
- 📋 Next steps - Shows what to do after branch creation
Requirements:
- Must be on
mainbranch (or confirm override) - Working directory must be clean
- Remote tags must be up to date
Examples:
# See what would happen
./scripts/create-release-branch.sh patch --dry-run
# Create a patch release branch
./scripts/create-release-branch.sh patch
# Interactive mode with menu
make release-branch# Check current version and tags
make version
# Create release branch (interactive)
make release-branch
# Create release branch (direct)
./scripts/create-release-branch.sh patch
# Create and push a tag
git tag -a v0.1.1 -m "Release v0.1.1"
git push origin v0.1.1
# Deploy specific version
VERSION=v0.1.1 docker-compose up -dUse a release branch when you need to:
- Freeze changes for QA/testing while
maincontinues development - Cherry-pick hotfixes onto a stable release candidate
- Maintain multiple release lines (e.g., v1.x and v2.x simultaneously)
Default workflow: Tag directly on main for simplicity.
You can install any service directly from a Git tag:
pip install "git+https://github.com/didac-crst/mockexchange.git@vX.Y.Z#subdirectory=packages/oracle"pip install "git+https://github.com/didac-crst/mockexchange.git@vX.Y.Z#subdirectory=packages/engine"pip install "git+https://github.com/didac-crst/mockexchange.git@vX.Y.Z#subdirectory=packages/periscope"Alternatively, clone at a tag and install with Poetry inside each package:
git clone --depth 1 --branch vX.Y.Z git@github.com:didac-crst/mockexchange.git
cd mockexchange/packages/engine && poetry install# Development workflow
make start-valkey # Start database first
make start-oracle # Start price feed
make start-engine # Then start the API
make logs-engine # Monitor engine logs
make restart-engine # Restart after code changes
# Debugging specific services
make logs-valkey # Check database connectivity
make logs-oracle # Check if price feed is working
make restart-periscope # Restart dashboard if UI is stuck
make status # See which services are running
# Selective deployment
make start-valkey make start-engine make start-periscope # Skip oracle if using external dataThe examples/ directory contains tools and examples that demonstrate how to use the MockExchange platform.
A Dockerized tool that generates random orders to test your MockExchange instance:
# Show available examples
make examples
# Show order generator help
make order-generator
# Start the order generator (fresh start with reset)
make order-generator-start-reset
# Start the order generator (start without reset)
make order-generator-start
# Continue without reset
make order-generator-restart
# Continue with reset
make order-generator-restart-reset
# View logs
make order-generator-logs
# Stop the generator
make order-generator-stop
# Check status
make order-generator-statusManual usage:
# Ensure MockExchange stack is running
make start
# Start the order generator
cd examples/order-generator
cp .env.example .env
# Edit .env with your API settings
./manage.sh start --resetFor more details, see examples/README.md.
All environment variables are centralized in the root .env file. This eliminates duplication and makes configuration management much easier.
# Copy the template and customize if needed
cp .env.example .env
# For external Valkey server, update VALKEY_HOST in .env:
# VALKEY_HOST=192.168.1.100 # Replace with your Valkey server IPNote: The .env.example is configured for Docker Compose. For external services, update:
VALKEY_HOST- Point to external Valkey/Redis serverENGINE_HOST- Point to external Engine API serverPERISCOPE_HOST- Point to external Periscope dashboard server
VERSION- MockExchange versionTEST_ENV- Test environment flag (enables API docs, disables auth)DEBUG- Development mode flag
VALKEY_HOST- Database host (default:valkeyfor Docker, use IP for external)VALKEY_PASSWORD- Database authenticationVALKEY_PORT- Database port (default: 6379)
EXCHANGE- Source exchange (binance, coinbase, etc.)SYMBOLS- Trading pairs to monitorINTERVAL_SEC- Price update frequencyQUOTES- Quote assets for discovery (comma-separated, e.g., "USDT,EUR")QUOTE- Fallback quote asset if QUOTES is empty (default: USDT)DISCOVER_QUOTES- Enable automatic market discovery (true/false)DISCOVER_LIMIT- Maximum markets per quote asset (0 = unlimited)
ENGINE_HOST- API server host (default:enginefor Docker, use IP for external)ENGINE_PORT- API server port (default: 8000)COMMISSION- Trading commission rateAPI_KEY- Authentication keyCASH_ASSET- Default cash/quote asset for the system (default: USDT)TICK_LOOP_SEC- Price-tick scanning interval in seconds (default: 10)PRUNE_EVERY_MIN- How often to prune old data in minutes (default: 60)STALE_AFTER_H- Data considered stale after hours (default: 24)EXPIRE_AFTER_H- Data expires after hours (default: 2)SANITY_CHECK_EVERY_MIN- Sanity check interval in minutes (default: 10)API_TIMEOUT_SEC- API request timeout for CLI in seconds (default: 10)
MIN_TIME_ANSWER_ORDER_MARKET- Minimum delay before processing market orders in seconds (default: 1)MAX_TIME_ANSWER_ORDER_MARKET- Maximum delay before processing market orders in seconds (default: 5)SIGMA_FILL_MARKET_ORDER- Slippage simulation for market order fills (default: 0.1)
ENGINE_HOST- Engine host for API URL construction (default:enginefor Docker, use IP for external)ENGINE_PORT- Engine port for API URL construction (default: 8000)PERISCOPE_HOST- Dashboard host for UI URL construction (default:localhost)PERISCOPE_PORT- Dashboard port for UI URL construction (default: 8501)APP_TITLE- Dashboard titleREFRESH_SECONDS- Auto-refresh intervalQUOTE_ASSET- Quote asset for portfolio valuation (default: USDT)FRESH_WINDOW_S- Fresh window for highlighting in seconds (default: 60)N_VISUAL_DEGRADATIONS- Number of fade-out levels for visual feedback (default: 60)SLIDER_MIN- Minimum value for order count slider (default: 10)SLIDER_MAX- Maximum value for order count slider (default: 1000)SLIDER_STEP- Step size for order count slider (default: 10)SLIDER_DEFAULT- Default value for order count slider (default: 50)LOCAL_TZ- Timezone for timestamps (default: Europe/Berlin)LOGO_FILE- Logo file for dashboard (optional)
-
✅ Single source of truth - All config in one place
-
✅ No duplication - Eliminates scattered
.envfiles -
✅ Easy customization - Change once, applies everywhere
-
✅ Better security - Centralized password management
-
✅ Simplified deployment - One config file to manage
-
✅ Automatic URL construction - API_URL and UI_URL built from HOST:PORT variables
The system automatically constructs URLs from HOST and PORT variables:
API_URL = http://${ENGINE_HOST}:${ENGINE_PORT}(for Periscope to connect to Engine)UI_URL = http://${PERISCOPE_HOST}:${PERISCOPE_PORT}(for dashboard links)
This means you only need to configure the individual HOST and PORT variables, not the full URLs.
mockexchange/
├── packages/
│ ├── valkey/ # MockX Valkey (Redis database)
│ ├── engine/ # MockX Engine (core/ + api/)
│ ├── periscope/ # MockX Periscope (dashboard)
│ └── oracle/ # MockX Oracle (price feeds)
├── examples/ # Examples and tools
│ ├── order-generator/ # Random order generator
│ └── README.md # Examples overview
├── .github/workflows/ # CI/CD pipelines
├── docker-compose.yml # Full stack orchestration
├── .env.example # Environment configuration template
├── Makefile # Development commands
├── pyproject.toml # Root workspace config
├── .pre-commit-config.yaml # Code quality hooks
├── scripts/ # Development and release scripts
│ └── create-release-branch.sh # Automated release branch creation
├── CHANGELOG.md # Version history and release notes
├── DOCKER_VERSIONS.md # Docker version pinning documentation
└── README.md # This file
- Valkey README - Database layer and data structures
- Oracle README - Price feed service and configuration
- Engine README - Trading engine and API reference
- Periscope README - Dashboard features and usage
- CHANGELOG.md - Version history and release notes
- Docker Version Pinning - Reproducible Docker builds
- Examples README - Usage examples and tools
- MockX Gateway - ccxt-style Python client
This project is licensed under the MIT License - see the LICENSE file for details.
Don’t risk real money. Spin up MockExchange, hammer it with tests, then hit live markets only when your algos are solid.