Paper Trading Simulator Development
Paper trading is trading with virtual money on real market data. It allows you to test a strategy or familiarize yourself with a platform without the risk of losing real funds. Technically, it's an execution engine simulation using live prices, but without real transactions.
Simulator Architecture
Virtual Account
from decimal import Decimal
from dataclasses import dataclass, field
from typing import dict, list
@dataclass
class PaperAccount:
user_id: str
initial_balance: Decimal = Decimal('10000')
balances: dict[str, Decimal] = field(default_factory=lambda: {'USDT': Decimal('10000')})
open_orders: list['PaperOrder'] = field(default_factory=list)
trade_history: list['PaperTrade'] = field(default_factory=list)
def get_portfolio_value(self, prices: dict[str, float]) -> Decimal:
total = self.balances.get('USDT', Decimal(0))
for currency, amount in self.balances.items():
if currency != 'USDT' and currency in prices:
total += amount * Decimal(str(prices[currency]))
return total
def get_pnl_percent(self, current_prices: dict) -> float:
current_value = self.get_portfolio_value(current_prices)
return float((current_value - self.initial_balance) / self.initial_balance * 100)
Order Execution Simulation
The main task: realistically imitate exchange behavior.
class PaperTradingEngine:
def __init__(self, market_data_feed):
self.feed = market_data_feed
self.accounts: dict[str, PaperAccount] = {}
async def place_order(
self,
user_id: str,
symbol: str,
side: str,
order_type: str,
quantity: Decimal,
price: Decimal = None
) -> PaperOrder:
account = self.accounts[user_id]
current_price = await self.feed.get_price(symbol)
order = PaperOrder(
id=generate_id(),
symbol=symbol,
side=side,
order_type=order_type,
quantity=quantity,
price=price,
status='open',
created_at=datetime.utcnow()
)
if order_type == 'market':
# Market order executes immediately with slippage simulation
slippage = current_price * Decimal('0.0005') # 0.05% slippage
fill_price = current_price + slippage if side == 'buy' else current_price - slippage
await self.fill_order(account, order, fill_price)
elif order_type == 'limit':
# Limit order reserved and added to queue
await self.reserve_funds(account, order, price)
account.open_orders.append(order)
return order
async def fill_order(
self,
account: PaperAccount,
order: PaperOrder,
fill_price: Decimal
):
base_currency = order.symbol.replace('USDT', '')
fee = order.quantity * fill_price * Decimal('0.001') # 0.1% commission
if order.side == 'buy':
cost = order.quantity * fill_price + fee
account.balances['USDT'] -= cost
account.balances[base_currency] = account.balances.get(
base_currency, Decimal(0)
) + order.quantity
else:
proceeds = order.quantity * fill_price - fee
account.balances['USDT'] = account.balances.get('USDT', Decimal(0)) + proceeds
account.balances[base_currency] -= order.quantity
order.status = 'filled'
order.fill_price = fill_price
order.fee = fee
account.trade_history.append(PaperTrade.from_order(order, fill_price))
async def check_limit_orders(self, symbol: str, current_price: Decimal):
"""Check limit orders on each price update"""
for user_id, account in self.accounts.items():
triggered = []
for order in account.open_orders:
if order.symbol != symbol:
continue
should_fill = (
(order.side == 'buy' and current_price <= order.price) or
(order.side == 'sell' and current_price >= order.price)
)
if should_fill:
await self.fill_order(account, order, order.price)
triggered.append(order)
for order in triggered:
account.open_orders.remove(order)
Leaderboard and Competitive Element
async def get_leaderboard(
self,
period: str = '7d'
) -> list[dict]:
all_accounts = await self.db.get_all_accounts()
prices = await self.feed.get_all_prices()
rankings = []
for account in all_accounts:
pnl = account.get_pnl_percent(prices)
rankings.append({
'user': account.user_id,
'pnl_percent': pnl,
'portfolio_value': float(account.get_portfolio_value(prices)),
'trades_count': len(account.trade_history),
})
return sorted(rankings, key=lambda x: x['pnl_percent'], reverse=True)[:100]
Leaderboard adds a competitive element and motivates users to use the platform more actively — good tool for engagement and conversion to real trading.
Simulator Limitations
Paper trading has a fundamental limitation: your orders don't affect the market. In reality, a large order moves the price (market impact). The simulator overestimates results of strategies trading large volumes.
For more realistic simulation: add a slippage model dependent on volume (the larger the order relative to market volume, the worse execution). This significantly brings paper trading closer to the real world.







