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¶
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¶
UserService¶
Authentication and account management.
Authenticate¶
Authenticate a user and obtain JWT tokens.
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.
Request:
| Field | Type | Description |
|---|---|---|
refresh_token |
string | Current refresh token |
GetBalances¶
Get wallet balances across exchanges.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Request:
| Field | Type | Description |
|---|---|---|
copy_trade_id |
string | Subscription ID to remove |
Enums¶
Exchange¶
Side¶
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?;
Related Documentation¶
- ADR-008: Control Interface - Architecture decision
- Telegram Bot - Mobile interface
- Environment Variables - Configuration reference