Skip to Content
BackendTrading Module

Trading Module

The trading module handles all position-related operations in BattlesBit. Since this is a trading game (not real trading), positions are simulated using real-time market data.

Architecture

User Request GraphQL API (web service) GameUseCase.CreatePosition() Validation (balance, max positions) Position Created (status: pending) Published to NATS JetStream Arena Worker picks up CalculatePosition() loop Real-time price updates via Binance WebSocket SL/TP/Liquidation checks Position closed → Update participant v_balance

Position States

pending → open → closed ↘ liquidated pending → canceled
StateDescription
pendingCreated, waiting for entry condition (limit) or market open
openActive position, PnL being calculated
closedManually closed or SL/TP triggered
liquidatedMargin depleted, forced close
canceledUser canceled before fill

Position Schema

// From: internal/ent/schema/game_match_position.go Fields: - id: UUID - game_match_id: UUID - game_match_participant_id: UUID - base_token: string (e.g., "BTCUSDT") - quote_token: string (default: "USDT") - entry_price: float64 - close_price: float64 (nullable) - margin: float64 - leverage: uint8 - side: enum (buy, sell) - type: enum (market, limit) - status: enum (open, pending, canceled, liquidated, closed) - stop_loss: float64 (nullable) - target_point: float64 (nullable) - size: float64 (margin * leverage) - liquidation_price: float64 - profit: float64 - opened_at: time - closed_at: time

Key Operations

Create Position

File: game_usecase.go:310

func (u *gameUseCase) CreatePosition( ctx context.Context, userID uuid.UUID, input *ent.CreateGameMatchPositionInput, ) (*ent.GameMatchPosition, error)

Validations:

  1. User is participant in the match
  2. Max positions not exceeded
  3. Asset exists in market
  4. Sufficient virtual balance
  5. Leverage item exists (if >10x)

Liquidation Price Calculation:

// Buy side liquidationPrice = entryPrice * (1 - 1/leverage) // Sell side liquidationPrice = entryPrice * (1 + 1/leverage)

Calculate Position (Arena Worker)

File: game_usecase_calc.go:32

The arena worker subscribes to market updates and:

  1. Opens pending positions when conditions met
  2. Calculates unrealized PnL
  3. Checks SL/TP triggers
  4. Checks liquidation
  5. Sends real-time notifications

Close Position

File: game_usecase.go:458

Supports partial close via percentage:

func (u *gameUseCase) ClosePosition( ctx context.Context, userID uuid.UUID, positionID uuid.UUID, percent float64, // 1-100 ) (*ent.GameMatchPosition, error)

PnL Calculation

File: game_usecase_calc.go:550

func calculatePositionProfit(pos *ent.GameMatchPosition, marketPrice float64) float64 { baseAmount := pos.Size / pos.EntryPrice if isBuySide { profit = (marketPrice - pos.EntryPrice) * baseAmount } else if isSellSide { profit = (pos.EntryPrice - marketPrice) * baseAmount } return profit }

Market Data Integration

File: internal/adapters/clients/market/binance_client.go

Get Assets

REST API call to fetch current prices and exchange info.

Subscribe Assets

WebSocket connection for real-time price updates.

type AssetUpdate struct { ID string // Symbol Symbol string PriceUsd float64 }

Notifications

Position updates trigger real-time notifications:

EventNotification
Position openedNotificationEventPositionUpdated
PnL updateNotificationEventPositionUpdated (with pnl data)
Position closedNotificationEventPositionClosed
Position liquidatedNotificationEventPositionLiquidated
Position canceledNotificationEventPositionCanceled

Configuration

Slippage

const slippage = 0.1 // 10%

Free Leverage

const maximumFreeLeverage = 10 // >10x requires inventory item

Implemented Since Initial Docs

  • Risk per trade limits (max_loss_per_trade_pct on Game)
  • Per-challenge leverage limits (max_leverage on Game)
  • Symbol restrictions (allowed_symbols on Game)
  • Trade direction limits (allowed_directions on Game)
  • Drawdown auto-disqualification (daily + total)
  • Trading lock (admin kill switch)
  • Trade audit logging (TradeAuditLog entity)
  • Position update (SL/TP modification)
  • Profit correctly applied to v_balance on partial close (bugfix)

Remaining TODOs

  • Stop orders
  • Stop-limit orders
  • Reduce-only orders
  • Max order size limits
  • Cooldown between trades
  • Equity calculation
  • Free margin calculation
Last updated on