Skip to content

gRPC API Reference

Programmatic control interface for the Arbiter trading engine.

Overview

The gRPC API provides type-safe, low-latency access to the trading engine. It's used by:

  • Telegram Bot - Primary mobile interface
  • External Clients - Automation and third-party integrations
  • Dashboards - Real-time monitoring

Services

Service Purpose
UserService Authentication and account management
TradingService Order management
StrategyService Arbitrage and copy trading configuration

Connection

Default Address: 0.0.0.0:50051
Protocol: gRPC (HTTP/2)
Authentication: JWT Bearer Token

Authentication

All requests (except Authenticate) require a valid JWT token in the Authorization metadata header.

Token Flow

1. Client calls Authenticate(user_id, credential)
2. Server returns access_token + refresh_token
3. Client includes access_token in Authorization header
4. When access_token expires, call RefreshToken

Token Types

Type Expiry Use
Access Token 1 hour API requests
Refresh Token 7 days Obtain new access tokens

Request Authentication

# Python example with grpcio
import grpc

channel = grpc.insecure_channel('localhost:50051')
stub = UserServiceStub(channel)

# Authenticate
response = stub.Authenticate(AuthRequest(
    user_id="telegram:123456",
    credential="signed-challenge"
))
access_token = response.access_token

# Use token for subsequent requests
metadata = [('authorization', f'Bearer {access_token}')]
positions = stub.GetPositions(PositionsRequest(), metadata=metadata)

JWT Claims

{
  "sub": "telegram:123456",
  "exp": 1706097600,
  "iat": 1706094000,
  "token_type": "access"
}

UserService

Authentication and account management.

Authenticate

Authenticate a user and obtain JWT tokens.

rpc Authenticate(AuthRequest) returns (AuthResponse);

Request:

Field Type Description
user_id string User identifier (e.g., "telegram:123456")
credential string Authentication credential

Response:

Field Type Description
access_token string JWT access token
expires_at Timestamp Token expiration time
refresh_token string JWT refresh token

RefreshToken

Refresh an expired access token.

rpc RefreshToken(RefreshRequest) returns (AuthResponse);

Request:

Field Type Description
refresh_token string Current refresh token

GetBalances

Get wallet balances across exchanges.

rpc GetBalances(BalancesRequest) returns (BalancesResponse);

Request:

Field Type Description
exchange Exchange Optional: filter by exchange

Response:

Field Type Description
balances Balance[] List of balances

Balance:

Field Type Description
exchange Exchange Exchange identifier
currency string Currency code (e.g., "USDC")
available double Available balance
locked double Balance in open orders
total double Total balance

GetPositions

Get open positions.

rpc GetPositions(PositionsRequest) returns (PositionsResponse);

Request:

Field Type Description
exchange Exchange Optional: filter by exchange
market_id string Optional: filter by market

Response:

Field Type Description
positions Position[] List of positions

Position:

Field Type Description
id string Position identifier
exchange Exchange Exchange
market_id string Market identifier
market_name string Human-readable market name
side Side BUY or SELL
size double Position size
entry_price double Average entry price
current_price double Current market price
unrealized_pnl double Unrealized profit/loss
opened_at Timestamp When position was opened

TradingService

Order management operations.

PlaceOrder

Place a new order.

rpc PlaceOrder(PlaceOrderRequest) returns (PlaceOrderResponse);

Request:

Field Type Description
exchange Exchange Target exchange
market_id string Market identifier
side Side BUY or SELL
size double Order size
price double Limit price (0 = market order)
client_order_id string Client-generated ID for idempotency

Response:

Field Type Description
order_id string Exchange order ID
status OrderStatus Order status
message string Status message

CancelOrder

Cancel an existing order.

rpc CancelOrder(CancelOrderRequest) returns (CancelOrderResponse);

Request:

Field Type Description
exchange Exchange Exchange where order exists
order_id string Order ID to cancel

Response:

Field Type Description
success bool Whether cancellation succeeded
message string Result message

GetOrder

Get order status and details.

rpc GetOrder(GetOrderRequest) returns (OrderResponse);

Request:

Field Type Description
exchange Exchange Exchange where order exists
order_id string Order ID

Response (OrderResponse):

Field Type Description
order_id string Order ID
exchange Exchange Exchange
market_id string Market
side Side BUY or SELL
size double Order size
price double Limit price
filled_size double Filled quantity
average_fill_price double Average fill price
status OrderStatus Current status
created_at Timestamp Order creation time
updated_at Timestamp Last update time

ListOrders

List recent orders.

rpc ListOrders(ListOrdersRequest) returns (ListOrdersResponse);

Request:

Field Type Description
exchange Exchange Optional: filter by exchange
status OrderStatus Optional: filter by status
limit int32 Max results (default 50)

Response:

Field Type Description
orders OrderResponse[] List of orders

StrategyService

Arbitrage and copy trading configuration.

GetArbStatus

Get arbitrage engine status.

rpc GetArbStatus(ArbStatusRequest) returns (ArbStatusResponse);

Response:

Field Type Description
state ArbEngineState STOPPED, RUNNING, or PAUSED
active_pairs int32 Number of monitored market pairs
opportunities_today int32 Opportunities detected today
trades_today int32 Trades executed today
pnl_today double P&L today in USD
last_activity Timestamp Last activity time

SetArbEnabled

Start or stop the arbitrage engine.

rpc SetArbEnabled(SetArbEnabledRequest) returns (SetArbEnabledResponse);

Request:

Field Type Description
enabled bool true = start, false = stop

Response:

Field Type Description
state ArbEngineState New engine state
message string Status message

GetArbConfig

Get arbitrage configuration.

rpc GetArbConfig(ArbConfigRequest) returns (ArbConfigResponse);

Response:

Field Type Description
min_spread double Minimum spread to trigger (e.g., 0.02 = 2%)
max_position_size double Max position size per trade (USD)
max_total_exposure double Max total exposure (USD)
staleness_threshold_ms int64 Data staleness threshold
enabled_pairs string[] Enabled market pair IDs

UpdateArbConfig

Update arbitrage configuration.

rpc UpdateArbConfig(UpdateArbConfigRequest) returns (ArbConfigResponse);

Request:

Field Type Description
min_spread optional double New minimum spread
max_position_size optional double New max position size
max_total_exposure optional double New max exposure
staleness_threshold_ms optional int64 New staleness threshold
enabled_pairs string[] New enabled pairs list

Only set fields are updated.

ListCopyTrades

List copy trade subscriptions.

rpc ListCopyTrades(ListCopyTradesRequest) returns (ListCopyTradesResponse);

Response:

Field Type Description
copy_trades CopyTrade[] List of subscriptions

CopyTrade:

Field Type Description
id string Subscription ID
target_wallet string Wallet being copied
exchange Exchange Exchange
size_multiplier double Copy size multiplier (e.g., 0.5 = half)
max_position_size double Max position per copy
enabled bool Whether active
total_pnl double Total P&L from this subscription
created_at Timestamp Creation time

AddCopyTrade

Add a new copy trade subscription.

rpc AddCopyTrade(AddCopyTradeRequest) returns (CopyTradeResponse);

Request:

Field Type Description
target_wallet string Wallet address to copy
exchange Exchange Exchange where wallet is active
size_multiplier double Size multiplier
max_position_size double Max position size

RemoveCopyTrade

Remove a copy trade subscription.

rpc RemoveCopyTrade(RemoveCopyTradeRequest) returns (RemoveCopyTradeResponse);

Request:

Field Type Description
copy_trade_id string Subscription ID to remove

Enums

Exchange

enum Exchange {
  EXCHANGE_UNSPECIFIED = 0;
  EXCHANGE_POLYMARKET = 1;
  EXCHANGE_KALSHI = 2;
}

Side

enum Side {
  SIDE_UNSPECIFIED = 0;
  SIDE_BUY = 1;
  SIDE_SELL = 2;
}

OrderStatus

enum OrderStatus {
  ORDER_STATUS_UNSPECIFIED = 0;
  ORDER_STATUS_PENDING = 1;
  ORDER_STATUS_OPEN = 2;
  ORDER_STATUS_FILLED = 3;
  ORDER_STATUS_PARTIALLY_FILLED = 4;
  ORDER_STATUS_CANCELLED = 5;
  ORDER_STATUS_REJECTED = 6;
}

ArbEngineState

enum ArbEngineState {
  ARB_ENGINE_STATE_UNSPECIFIED = 0;
  ARB_ENGINE_STATE_STOPPED = 1;
  ARB_ENGINE_STATE_RUNNING = 2;
  ARB_ENGINE_STATE_PAUSED = 3;
}

Error Handling

gRPC status codes used:

Code Meaning
OK (0) Success
UNAUTHENTICATED (16) Missing or invalid token
PERMISSION_DENIED (7) Token valid but insufficient permissions
NOT_FOUND (5) Resource not found
INVALID_ARGUMENT (3) Invalid request parameters
INTERNAL (13) Server error

Client Examples

Python (grpcio)

import grpc
from arbiter_pb2 import AuthRequest, PositionsRequest
from arbiter_pb2_grpc import UserServiceStub

# Connect
channel = grpc.insecure_channel('localhost:50051')
stub = UserServiceStub(channel)

# Authenticate
auth_response = stub.Authenticate(AuthRequest(
    user_id="telegram:123456",
    credential="signature"
))

# Make authenticated request
metadata = [('authorization', f'Bearer {auth_response.access_token}')]
positions = stub.GetPositions(PositionsRequest(), metadata=metadata)

for pos in positions.positions:
    print(f"{pos.market_name}: {pos.size} @ {pos.entry_price}")

Rust (tonic)

use arbiter::proto::{user_service_client::UserServiceClient, AuthRequest};
use tonic::Request;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = UserServiceClient::connect("http://localhost:50051").await?;

    // Authenticate
    let response = client.authenticate(AuthRequest {
        user_id: "telegram:123456".to_string(),
        credential: "signature".to_string(),
    }).await?;

    let token = response.into_inner().access_token;

    // Make authenticated request
    let mut request = Request::new(PositionsRequest::default());
    request.metadata_mut().insert(
        "authorization",
        format!("Bearer {}", token).parse()?,
    );

    let positions = client.get_positions(request).await?;
    Ok(())
}

Configuration

Environment Variables

Variable Description Default
GRPC_ADDR Server bind address 0.0.0.0:50051
JWT_SECRET Secret for signing tokens Required in production
JWT_ACCESS_EXPIRY_SECS Access token expiry 3600
JWT_REFRESH_EXPIRY_SECS Refresh token expiry 604800

Starting the Server

The gRPC server starts automatically with the trading engine:

use arbiter_engine::api::server::{GrpcServerConfig, start_grpc_server};

let config = GrpcServerConfig {
    addr: "0.0.0.0:50051".parse().unwrap(),
    jwt_secret: std::env::var("JWT_SECRET")
        .expect("JWT_SECRET required")
        .into_bytes(),
};

start_grpc_server(config).await?;