Skip to main content

Overview

The Tradovate integration uses OAuth 2.0 to securely connect your Tradovate demo account and automatically import fill pairs (completed trades) into Deltalytix.
Currently supports demo environment only. Live trading support coming soon.

Features

  • OAuth 2.0 authentication flow
  • Automatic access token renewal
  • Fill pair synchronization (buy/sell matched trades)
  • Commission tracking from fill fees
  • Support for long and short positions
  • Tick-based P&L calculation for futures
  • Batch API requests for performance

Setup Instructions

Prerequisites

  1. A Tradovate demo account
  2. Environment variables configured:
TRADOVATE_CLIENT_ID=your_client_id
TRADOVATE_CLIENT_SECRET=your_client_secret
TRADOVATE_REDIRECT_URI=https://your-app.com/callback

Step 1: Initiate OAuth Flow

Start the OAuth authentication process:
import { initiateTradovateOAuth } from '@/app/[locale]/dashboard/components/import/tradovate/actions'

const result = await initiateTradovateOAuth('default')

if (result.authUrl) {
  // Redirect user to Tradovate OAuth page
  window.location.href = result.authUrl
} else {
  console.error(result.error)
}
See app/[locale]/dashboard/components/import/tradovate/actions.ts:562-619

Step 2: Handle OAuth Callback

After user authorization, handle the callback:
import { handleTradovateCallback } from '@/app/[locale]/dashboard/components/import/tradovate/actions'

// Extract code and state from callback URL
const urlParams = new URLSearchParams(window.location.search)
const code = urlParams.get('code')
const state = urlParams.get('state')

if (code && state) {
  const result = await handleTradovateCallback(code, state)
  
  if (result.accessToken) {
    console.log('Authentication successful')
    console.log('Token expires at:', result.expiresAt)
  } else {
    console.error('Authentication failed:', result.error)
  }
}
See app/[locale]/dashboard/components/import/tradovate/actions.ts:642-760

Step 3: Sync Trades

Trigger trade synchronization via API:
// Client-side: Call the sync API endpoint
const response = await fetch('/api/tradovate/sync', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ accountId: 'your-account-id' })
})

const data = await response.json()

if (data.success) {
  console.log(`Synced ${data.savedCount} trades from ${data.ordersCount} orders`)
} else {
  console.error('Sync failed:', data.message)
}
See app/api/tradovate/sync/route.ts:7-51

API Reference

POST /api/tradovate/sync

Synchronize trades from Tradovate. Request Body:
{
  accountId: string  // The Tradovate account ID (prop firm name)
}
Response:
{
  success: boolean
  savedCount?: number      // Number of trades saved
  ordersCount?: number     // Number of orders processed
  message: string
}
Example:
const response = await fetch('/api/tradovate/sync', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ accountId: 'MyPropFirm' })
})

const result = await response.json()
console.log(`Saved ${result.savedCount} trades`)

GET /api/tradovate/synchronizations

Retrieve all Tradovate synchronization configurations. Response:
{
  success: boolean
  data: Array<{
    userId: string
    service: 'tradovate'
    accountId: string
    token: string
    tokenExpiresAt: Date
    lastSyncedAt: Date
  }>
}
See app/api/tradovate/synchronizations/route.ts:7-28

DELETE /api/tradovate/synchronizations

Remove a Tradovate synchronization. Request Body:
{
  accountId: string
}
Response:
{
  success: boolean
  message: string
}
See app/api/tradovate/synchronizations/route.ts:30-61

Authentication Flow

OAuth 2.0 Authorization Code Flow

  1. Authorization Request: User is redirected to Tradovate OAuth page
https://trader.tradovate.com/oauth?
  response_type=code&
  client_id=YOUR_CLIENT_ID&
  redirect_uri=YOUR_REDIRECT_URI&
  scope=read%20write&
  state=RANDOM_STATE
  1. Authorization Callback: Tradovate redirects back with authorization code
https://your-app.com/callback?code=AUTH_CODE&state=RANDOM_STATE
  1. Token Exchange: Exchange code for access token
POST https://demo.tradovateapi.com/auth/oauthtoken
Content-Type: application/x-www-form-urlencoded
Authorization: Basic base64(client_id:client_secret)

grant_type=authorization_code&
code=AUTH_CODE&
redirect_uri=YOUR_REDIRECT_URI&
client_id=YOUR_CLIENT_ID
  1. Token Storage: Access token is stored in database
See app/[locale]/dashboard/components/import/tradovate/actions.ts:1163-1207

Token Renewal

Tokens are automatically renewed before expiration using the renewAccessToken endpoint:
import { renewTradovateAccessToken } from '@/app/[locale]/dashboard/components/import/tradovate/actions'

const result = await renewTradovateAccessToken(currentToken, 'demo')

if (result.accessToken) {
  console.log('Token renewed successfully')
  console.log('New expiration:', result.expiresAt)
}
See app/[locale]/dashboard/components/import/tradovate/actions.ts:762-812

Trade Synchronization

How It Works

  1. Fetch Fill Pairs: Retrieve completed trades (buy/sell matches) from Tradovate
  2. Fetch Fill Details: Get detailed information for each fill including commission
  3. Fetch Contract Info: Resolve contract symbols and tick details
  4. Match Orders: Link fills to their originating orders
  5. Calculate P&L: Compute profit/loss using tick values
  6. Save Trades: Store trades in Deltalytix format

Fill Pair Processing

Fill pairs represent completed round-trip trades:
interface TradovateFillPair {
  id: number
  positionId: number
  buyFillId: number      // Buy fill ID
  sellFillId: number     // Sell fill ID
  qty: number            // Quantity
  buyPrice: number       // Entry price
  sellPrice: number      // Exit price
  active: boolean
}
See app/[locale]/dashboard/components/import/tradovate/actions.ts:134-144

Commission Calculation

Commissions are fetched from fill fees and calculated per unit:
// Get commission for both fills
const buyCommissionPerUnit = buyFillFee.commission / buyFill.qty
const sellCommissionPerUnit = sellFillFee.commission / sellFill.qty

// Apply to fill pair quantity
const totalCommission = 
  (buyCommissionPerUnit + sellCommissionPerUnit) * fillPair.qty
See app/[locale]/dashboard/components/import/tradovate/actions.ts:1088-1111

P&L Calculation

P&L is calculated using tick values for accurate futures pricing:
const tickSize = tickDetail?.tickSize || 0.25   // e.g., 0.25 for MES
const tickValue = tickDetail?.tickValue || 5.0  // e.g., $5 for MES

const priceDifference = exitPrice - entryPrice
const ticks = priceDifference / tickSize
let pnl = ticks * tickValue * quantity

// Reverse for short trades
if (!isBuyFirst) {
  pnl = -pnl
}
See app/[locale]/dashboard/components/import/tradovate/actions.ts:1051-1083

Error Handling

Common Errors

Error: Token expiredSolution: Tokens are automatically renewed. If renewal fails, re-authenticate:
const result = await getTradovateToken(accountId)
if (result.error === 'Token expired') {
  // Trigger re-authentication
  await initiateTradovateOAuth(accountId)
}
Error: Tradovate OAuth credentials not configuredSolution: Ensure environment variables are set:
TRADOVATE_CLIENT_ID=your_client_id
TRADOVATE_CLIENT_SECRET=your_client_secret
TRADOVATE_REDIRECT_URI=https://your-app.com/callback
Error: User not authenticatedSolution: Ensure user is logged in before calling sync:
const supabase = await createClient()
const { data: { user } } = await supabase.auth.getUser()

if (!user) {
  // Redirect to login
  window.location.href = '/login'
}
Error: No error, but savedCount: 0Reason: No completed trades in Tradovate accountSolution: This is normal if there are no trades. The sync completed successfully.

Debugging

Enable debug logging by setting environment variable:
TRADOVATE_DEBUG=true
Debug logs include:
  • OAuth flow details
  • API request/response data
  • Token renewal attempts
  • Fill pair processing
  • P&L calculations
See app/[locale]/dashboard/components/import/tradovate/actions.ts:41-60

API Endpoints Used

The integration calls these Tradovate API endpoints:
EndpointPurpose
GET /v1/user/listGet user information
GET /v1/account/listList trading accounts
GET /v1/organization/listGet prop firm name
GET /v1/fillPair/listFetch completed trades
GET /v1/fill/itemsGet fill details in batch
GET /v1/fillFee/itemsGet commission fees in batch
GET /v1/order/itemsGet order details in batch
GET /v1/contract/itemGet contract information
GET /auth/renewAccessTokenRenew OAuth token
All API calls use the demo environment base URL: https://demo.tradovateapi.com

Best Practices

Token Management

Always check token expiration before syncing and renew if needed

Error Recovery

Implement retry logic with exponential backoff for API failures

Batch Requests

Use batch endpoints to minimize API calls and improve performance

Rate Limiting

Respect Tradovate rate limits with delays between batch requests

Next Steps

View Synchronizations

Manage your Tradovate connections

Configure Webhooks

Set up automatic sync triggers

Build docs developers (and LLMs) love