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_balancePosition States
pending → open → closed
↘ liquidated
pending → canceled| State | Description |
|---|---|
pending | Created, waiting for entry condition (limit) or market open |
open | Active position, PnL being calculated |
closed | Manually closed or SL/TP triggered |
liquidated | Margin depleted, forced close |
canceled | User 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: timeKey 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:
- User is participant in the match
- Max positions not exceeded
- Asset exists in market
- Sufficient virtual balance
- 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:
- Opens pending positions when conditions met
- Calculates unrealized PnL
- Checks SL/TP triggers
- Checks liquidation
- 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:
| Event | Notification |
|---|---|
| Position opened | NotificationEventPositionUpdated |
| PnL update | NotificationEventPositionUpdated (with pnl data) |
| Position closed | NotificationEventPositionClosed |
| Position liquidated | NotificationEventPositionLiquidated |
| Position canceled | NotificationEventPositionCanceled |
Configuration
Slippage
const slippage = 0.1 // 10%Free Leverage
const maximumFreeLeverage = 10 // >10x requires inventory itemImplemented Since Initial Docs
- Risk per trade limits (
max_loss_per_trade_pcton Game) - Per-challenge leverage limits (
max_leverageon Game) - Symbol restrictions (
allowed_symbolson Game) - Trade direction limits (
allowed_directionson Game) - Drawdown auto-disqualification (daily + total)
- Trading lock (admin kill switch)
- Trade audit logging (
TradeAuditLogentity) - 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