Skip to content

About

Crypto Trading Simulator — A real-time, risk-free cryptocurrency trading platform built with Python and FastAPI. Demonstrates backend engineering skills including real-time data streaming, scalable APIs, async processing, caching, and cloud deployment.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Crypto Trading Simulator

A production-grade, real-time cryptocurrency trading platform built with Python and FastAPI — designed to demonstrate backend engineering depth including async systems, WebSocket streaming, caching architecture, and cloud deployment.

Python FastAPI Redis SQLite WebSockets AWS EC2 GitHub Actions License

🔗 Live Demo: https://crypto.jaayysoni.com

Application Screenshots


Dashboard View


Transactions


Portfolio


Trading Terminal View


Trading Terminal — Successful Order Execution


Table of Contents


Overview

The Crypto Trading Simulator is a real-time, risk-free cryptocurrency trading platform that mirrors the backend complexity of production trading systems — without involving real money.

It was built as a hands-on demonstration of:

  • Real-time systems engineering using WebSockets and live market data APIs
  • Async backend design with FastAPI for non-blocking, high-throughput request handling
  • Performance optimization using Redis caching to reduce redundant API calls
  • Cloud deployment on AWS EC2 with Nginx, HTTPS, and automated CI/CD

The platform streams live prices for the top 90 cryptocurrencies, supports portfolio management using FIFO (First In, First Out) cost basis tracking, and provides a complete trading terminal with buy/sell order execution.


Live Deployment

Property Value
URL https://crypto.jaayysoni.com
Cloud Provider AWS EC2 — Amazon Linux 2023 (Mumbai Region)
Reverse Proxy Nginx
SSL Let's Encrypt (HTTPS enforced)
CI/CD GitHub Actions (auto-deploy on push)
Realtime ~90 concurrent WebSocket connections

Key Engineering Highlights

These are the backend decisions most relevant to engineering and technical evaluation:

1. Async Architecture with FastAPI

All API endpoints involving live price lookups use async def, enabling non-blocking I/O. This is critical for a trading platform where price data is fetched from Redis and WebSocket streams under high concurrency.

@app.get("/dashboard/prices")
async def get_dashboard_prices(db: Session = Depends(get_db)) -> List[Dict]:
    data = await get_crypto_prices()  # Non-blocking Redis fetch
    ...

2. WebSocket Price Streaming (~90 Concurrent Connections)

The app connects to Binance's WebSocket API and maintains ~90 simultaneous streams — one per cryptocurrency — to receive real-time ticker updates. These updates are pushed into Redis cache for instant access by all HTTP endpoints.

asyncio.create_task(start_crypto_ws())  # Started on app startup

3. Redis Caching Layer

Live price data from WebSocket streams is stored in Redis. When an API endpoint needs a current price (e.g., portfolio valuation or order execution), it reads from Redis instead of making a fresh API call — keeping latency low and external API usage minimal.

price_data = await get_symbol_price(symbol)  # Read from Redis, not Binance API

4. FIFO Portfolio Tracking

Portfolio holdings are computed using a First In, First Out (FIFO) algorithm. Each buy creates a cost lot; each sell depletes the earliest lots. This gives accurate average cost basis and profit/loss calculations.

portfolio_lots = defaultdict(deque)
# BUY → append lot; SELL → consume from left

5. Race Condition Safety

Balance deduction and transaction insertion are handled in the same database transaction. Available quantity for sells is computed from the aggregate difference of all BUY and SELL records, preventing overselling.

available_qty = bought_qty - sold_qty
if available_qty < quantity:
    return JSONResponse(status_code=400, content={"error": "Not enough holdings"})

6. Graceful Startup & Shutdown

The app uses FastAPI lifecycle events to establish Redis, run DB migrations, and launch the WebSocket background task on startup — and cleanly closes Redis on shutdown.


Features

Dashboard

  • Live price and 24h change for the top 90 cryptocurrencies
  • Fetched from Redis-backed WebSocket stream, updated in real time
  • Clickable cards redirect to the Trading Terminal

Portfolio

  • Current holdings computed via FIFO across all historical transactions
  • Live P/L (profit/loss) per asset using real-time prices from Redis
  • Total portfolio value displayed dynamically

Transaction History

  • Complete timestamped record of every buy and sell
  • Sorted newest-first, with status, price, and quantity per trade

Virtual Balance

  • Default starting balance: $100,000 virtual USD
  • Users can add or remove funds at any time
  • Balance updates are persisted to SQLite

Trading Terminal

  • Interactive price chart per cryptocurrency
  • Buy and sell order forms with live price feedback
  • Order validation: checks balance (buy) and available holdings (sell) before execution

System Architecture

┌─────────────────────────────────────────────────────────┐
│                        Browser                          │
│         HTML / CSS / JS (Static Frontend)               │
└────────────────────┬────────────────────────────────────┘
│ HTTP / REST API
┌────────────────────▼────────────────────────────────────┐
│                  FastAPI Backend                         │
│                                                         │
│  /api/dashboard/prices   →  Redis Cache Read            │
│  /api/portfolio/holdings →  SQLite + Redis              │
│  /api/order/buy          →  Redis + SQLite Write        │
│  /api/order/sell         →  Redis + SQLite Write        │
│  /api/transactions       →  SQLite Read                 │
│  /api/balance            →  SQLite Read/Write           │
└──────────┬──────────────────────┬───────────────────────┘
│                      │
┌──────────▼──────┐    ┌──────────▼──────────────────────┐
│  SQLite (app.db) │    │         Redis Cache              │
│                  │    │  (live prices from WS stream)   │
│  - Balance       │    └──────────┬──────────────────────┘
│  - Transactions  │               │ subscribe / write
│  - Crypto list   │    ┌──────────▼──────────────────────┐
└──────────────────┘    │   Binance WebSocket API          │
│  (~90 concurrent streams)        │
└─────────────────────────────────┘

Data Flow Summary:

  1. On startup, a background async task opens ~90 WebSocket connections to Binance
  2. Each ticker update writes the latest price into Redis
  3. API endpoints read from Redis for live prices (no repeated external calls)
  4. Buy/sell orders validate against Redis prices and write to SQLite
  5. Portfolio and transaction history are computed from SQLite records

API Reference

All endpoints are prefixed with /api. Interactive docs available at /docs when running locally.

Balance

Method Endpoint Description
GET /api/balance Get current virtual balance
POST /api/balance/update Add or remove virtual funds

POST /api/balance/update payload:

{
  "action": "add",
  "amount": 5000.0
}

action accepts "add" or "remove". Returns updated balance or error.


Dashboard

Method Endpoint Description
GET /api/dashboard/prices Live prices + 24h change for all tracked cryptos

Sample response:

[
  { "name": "Bitcoin", "symbol": "BTCUSDT", "price": 62450.00, "change": "+1.24%" },
  { "name": "Ethereum", "symbol": "ETHUSDT", "price": 3120.50, "change": "-0.47%" }
]

Portfolio

Method Endpoint Description
GET /api/portfolio/holdings FIFO-computed holdings with live P/L

Sample response:

{
  "holdings": [
    {
      "symbol": "BTC",
      "quantity": 0.05,
      "avg_price": 60000.00,
      "live_price": 62450.00,
      "profit_loss": 122.50
    }
  ]
}

Orders

Method Endpoint Description
POST /api/order/buy Execute a simulated buy order
POST /api/order/sell Execute a simulated sell order

Request payload (both):

{
  "symbol": "BTCUSDT",
  "quantity": 0.01
}

Buy response:

{
  "message": "Order executed",
  "side": "BUY",
  "symbol": "BTCUSDT",
  "quantity": 0.01,
  "price": 62450.00,
  "spent": 624.50,
  "balance": 99375.50
}

Sell error (insufficient holdings):

{
  "error": "Not enough holdings",
  "available": 0.005
}

Transactions

Method Endpoint Description
GET /api/transactions Full history of all buy/sell transactions

Sample response:

[
  {
    "transaction_type": "buy",
    "crypto_symbol": "BTC",
    "quantity": 0.01,
    "price": 62450.00,
    "timestamp": "2025-04-01T10:32:00",
    "status": "completed"
  }
]

Tech Stack

Layer Technology Why
Backend Python 3.13.2 + FastAPI Async-first, fast, auto-docs via OpenAPI
Real-Time WebSockets (Binance API) Live price streaming at scale
Caching Redis Low-latency price reads, reduced API calls
Database SQLite Lightweight persistent storage for trades and balance
Frontend HTML, CSS, Vanilla JS Simple, no build tooling needed
Hosting AWS EC2 (Amazon Linux 2023) Full control, production-like environment
Proxy Nginx SSL termination, static file serving
SSL Let's Encrypt Free, auto-renewing HTTPS
CI/CD GitHub Actions Auto-deploy on push to main
Containers Docker + Docker Compose Portable local dev environment

Project Structure

Crypto-Trading-Simulator/
│
├── app/
│   ├── main.py                      # FastAPI app, all route definitions
│   ├── config.py                    # App-wide configuration
│   ├── constants/
│   │   └── coin.py                  # List of tracked cryptocurrencies
│   ├── database/
│   │   ├── db.py                    # SQLAlchemy engine and Base
│   │   ├── init_db.py               # Table creation
│   │   └── session.py               # DB session dependency
│   ├── models/
│   │   ├── balance.py               # Balance ORM model
│   │   ├── crypto.py                # Crypto ORM model
│   │   └── transaction.py           # Transaction ORM model
│   ├── schemas/
│   │   ├── portfolio_schemas.py     # Pydantic schemas for portfolio
│   │   └── transaction_schema.py    # Pydantic schemas for transactions
│   ├── services/
│   │   ├── crypto_ws.py             # Binance WebSocket manager
│   │   ├── price_service.py         # Price fetch logic
│   │   ├── balance_transaction.py   # Balance update service
│   │   └── transaction_services.py  # Trade execution logic
│   ├── tasks/
│   │   └── scheduler.py             # Background task scheduler
│   └── utils/
│       ├── cache.py                 # Redis read helpers (get_symbol_price, etc.)
│       └── redis_client.py          # Redis connection lifecycle
│
├── static/                          # Frontend assets
│   ├── dashboard.html / .js / .css
│   ├── portfolio.html / .js / .css
│   ├── tradingterminal.html / .js / .css
│   └── transaction.html / .js / .css
│
├── deploy/
│   ├── fastapi_nginx.conf           # Nginx reverse proxy config
│   └── setup_nginx.sh               # EC2 setup script
│
├── docs/
│   ├── api_docs.md
│   └── setup_guide.md
│
├── insert_crypto.py                 # Seed script for initial crypto data
├── app.db                           # SQLite database
├── Dockerfile
├── docker-compose.yml
├── requirements.txt
└── README.md

Getting Started

Prerequisites

  • Python 3.13.2
  • Redis running locally (redis-server)
  • Git

Local Setup

# 1. Clone the repository
git clone https://github.com/jaayysoni/Crypto-Trading-Simulator.git
cd Crypto-Trading-Simulator

# 2. Create a virtual environment
python -m venv venv
source venv/bin/activate        # Mac/Linux
# venv\Scripts\activate         # Windows

# 3. Install dependencies
pip install -r requirements.txt

# 4. Seed the crypto list (first time only)
python insert_crypto.py

# 5. Start the server
uvicorn app.main:app --reload

Visit http://localhost:8000 in your browser.

Redis must be running before starting the server. The app will fail to start if Redis is unreachable (it retries 3 times with 2s delay before raising RuntimeError).


Docker Setup

Run the full stack (FastAPI + Redis) with one command:

docker-compose up --build

Visit http://localhost:8000. Redis is configured automatically via Docker Compose networking.


CI/CD Pipeline

The project uses GitHub Actions for continuous deployment:

  • On every push to main, the workflow SSHs into the EC2 instance
  • Pulls the latest code
  • Restarts the FastAPI service via systemd
  • Nginx serves the updated app with zero downtime

Pipeline config is in .github/workflows/.


Design Decisions

Why FastAPI over Flask/Django? FastAPI's native async/await support is essential for managing ~90 concurrent WebSocket connections and non-blocking Redis reads. Flask would require external async wrappers; Django is too heavyweight for this use case.

Why SQLite over PostgreSQL? This is a single-user simulator. SQLite removes infrastructure overhead while still providing ACID guarantees for trade and balance operations. Swapping to PostgreSQL for multi-user support would be a straightforward change to the SQLAlchemy connection string.

Why Redis for price caching? Calling Binance's REST API on every price request would hit rate limits almost immediately under real load. Redis acts as a write-through cache: the WebSocket streams update it continuously, and all endpoints read from it in sub-millisecond time.

Why FIFO for portfolio tracking? FIFO is the most commonly used cost basis method and maps directly to how real brokerages calculate capital gains. It was implemented using collections.deque for O(1) pop from the front.


Known Limitations & Future Improvements

Limitation Planned Fix
Single-user (no auth) Add JWT-based authentication and per-user portfolios
SQLite Migrate to PostgreSQL for multi-user concurrency
No order types (limit, stop-loss) Implement limit order queue with background task
Frontend is vanilla JS Migrate to React for component-based UI
No test coverage Add pytest unit tests for services and API endpoints
CORS set to * Restrict to known origins in production

License

This project is licensed under the MIT License.


Author

Jay Soni — GitHub | Open to backend, full-stack, and platform engineering roles.

Built to demonstrate real-time systems design, async Python, and production cloud deployment skills.

About

Crypto Trading Simulator — A real-time, risk-free cryptocurrency trading platform built with Python and FastAPI. Demonstrates backend engineering skills including real-time data streaming, scalable APIs, async processing, caching, and cloud deployment.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages