The Bjarkan SOR SDK provides smart order routing capabilities to execute trades across multiple exchanges for optimal pricing. This guide explains how to configure and use the order execution functionality.

Prerequisites

Before you can execute orders, you need to:

  1. Set up API credentials as environment variables:

    # For Binance
    export BINANCE_API_KEY="your_api_key"
    export BINANCE_API_SECRET="your_api_secret"
    
    # For Bybit
    export BYBIT_API_KEY="your_api_key"
    export BYBIT_API_SECRET="your_api_secret"
    
    # For OKX (requires password)
    export OKX_API_KEY="your_api_key"
    export OKX_API_SECRET="your_api_secret"
    export OKX_API_PASSWORD="your_password"
    
  2. Configure the orderbook with aggregated=True and a single symbol:

    await sdk.set_config(
        type="orderbook",
        aggregated=True,  # Must be True for order execution
        exchanges=["binance", "bybit", "okx"],
        symbols=["BTC/USDT"],  # Must contain only one symbol
        depth=20
    )
    
  3. Start the orderbook stream to get market data:

    await sdk.start_stream("orderbook")
    
  4. Initialize the order executor with your API configurations:

    from bjarkan.utils.helpers import get_api_configs
    
    # Get API configs from environment variables
    api_configs = get_api_configs(["binance", "bybit", "okx"])
    
    # Initialize executor
    await sdk.initialize_executor(api_configs)
    

Basic Order Execution

Once you’ve completed the prerequisites, you can execute orders:

from bjarkan.models import OrderConfig

# Create an order
order = OrderConfig(
    side="buy",  # or "sell"
    amount=0.001  # BTC amount
)

# Execute the order
result = await sdk.execute_order(order)

# Process the result
print(f"Status: {result['status']}")
print(f"Filled amount: {result['filled_amount']}")
print(f"Remaining amount: {result['remaining_amount']}")

# Examine execution details
for execution in result['execution_results']:
    print(f"\nExchange: {execution['exchange']}")
    print(f"Status: {execution['status']}")
    print(f"Filled amount: {execution['filled_amount']}")
    print(f"Execution time: {execution['execution_time']}ms")

Order Configuration

The OrderConfig class requires two parameters:

  • side: “buy” or “sell”
  • amount: The amount to trade in base currency (e.g., BTC for BTC/USDT)

Currently, the SDK supports market orders. Limit orders and other order types will be added in future releases.

How Smart Order Routing Works

When you execute an order, the Bjarkan SOR SDK:

  1. Analyzes the current aggregated orderbook data
  2. Creates an execution plan that splits the order across exchanges to get the best overall price
  3. Executes the parts of the order in parallel on each exchange
  4. Combines the results into a single response

For a buy order, the SOR finds the lowest asks across exchanges and allocates the order amount starting with the best price. For a sell order, it finds the highest bids.

Order Execution Response

The execute_order() method returns a comprehensive result with this structure:

{
    "status": "completed",  # or "partially_filled"
    "original_amount": 0.01,
    "filled_amount": 0.01,
    "remaining_amount": 0,
    "execution_plan": [
        # (exchange, amount)
        ("binance", 0.008),
        ("bybit", 0.002)
    ],
    "execution_results": [
        {
            "exchange": "binance",
            "status": "success",
            "planned_amount": 0.008,
            "filled_amount": 0.008,
            "execution_time": 234.5,  # milliseconds
            "order": {  # Exchange-specific order details
                "id": "123456789",
                "status": "filled",
                # ...other exchange-specific fields
            }
        },
        {
            "exchange": "bybit",
            "status": "success",
            "planned_amount": 0.002,
            "filled_amount": 0.002,
            "execution_time": 187.3,  # milliseconds
            "order": {  # Exchange-specific order details
                "id": "987654321",
                "status": "filled",
                # ...other exchange-specific fields
            }
        }
    ],
    "execution_times": {  # Timing information
        "total": 245.8,  # milliseconds
        "binance": 234.5,
        "bybit": 187.3
    }
}

Advanced Features

Margin Trading

To execute margin orders, enable margin mode during executor initialization:

# Initialize executor with margin trading enabled
await sdk.initialize_executor(api_configs, margin_mode=True)

The SDK automatically applies the appropriate margin parameters for each exchange:

  • Binance: Uses cross margin with auto borrowing
  • Bybit: Uses cross margin with auto borrowing
  • Gate.io: Uses cross margin with auto borrowing

Sandbox/Testnet Testing

For testing without using real funds, use sandbox mode:

await sdk.set_config(
    type="orderbook",
    aggregated=True,
    exchanges=["binance", "bybit"],
    symbols=["BTC/USDT"],
    depth=20,
    sandbox_mode={
        "binance": True,
        "bybit": True
    }
)

Make sure your API keys are set up for the testnet of each exchange.

Complete Example

Here’s a complete example of order execution:

import asyncio
from bjarkan import BjarkanSDK, OrderConfig
from bjarkan.utils.helpers import get_api_configs

async def main():
    async with BjarkanSDK() as sdk:
        # 1. Configure orderbook
        await sdk.set_config(
            type="orderbook",
            aggregated=True,
            exchanges=["binance", "bybit"],
            symbols=["BTC/USDT"],
            depth=20
        )
        
        # 2. Start orderbook stream
        await sdk.start_stream("orderbook")
        
        # 3. Wait for initial data
        print("Waiting for initial orderbook data...")
        await asyncio.sleep(2)
        
        # 4. Get API configurations
        api_configs = get_api_configs(["binance", "bybit"])
        
        # 5. Initialize order executor
        await sdk.initialize_executor(api_configs)
        
        # 6. Create and execute order
        order = OrderConfig(
            side="buy",
            amount=0.001  # BTC amount
        )
        
        print("Executing order...")
        result = await sdk.execute_order(order)
        
        # 7. Process execution results
        print(f"\nOrder execution status: {result['status']}")
        print(f"Original amount: {result['original_amount']}")
        print(f"Filled amount: {result['filled_amount']}")
        print(f"Remaining amount: {result['remaining_amount']}")
        
        print("\nExecution plan:")
        for exchange, amount in result['execution_plan']:
            print(f"  {exchange}: {amount}")
        
        print("\nExecution results:")
        for execution in result['execution_results']:
            print(f"\n  Exchange: {execution['exchange']}")
            print(f"  Status: {execution['status']}")
            if execution['status'] == 'success':
                print(f"  Planned amount: {execution['planned_amount']}")
                print(f"  Filled amount: {execution['filled_amount']}")
                print(f"  Execution time: {execution['execution_time']}ms")
            else:
                print(f"  Error: {execution.get('error', 'Unknown error')}")
        
        print("\nExecution times:")
        for exchange, time in result['execution_times'].items():
            if exchange != 'total':
                print(f"  {exchange}: {time}ms")
        print(f"  Total execution time: {result['execution_times']['total']}ms")

if __name__ == "__main__":
    asyncio.run(main())

Building Custom Execution Algorithms

You can build your own execution algorithms using the Bjarkan SOR SDK by:

  1. Getting the latest orderbook data with get_latest_data("orderbook")
  2. Analyzing the data using your custom logic
  3. Creating orders with your own parameters
  4. Executing those orders with execute_order()

This allows you to implement sophisticated strategies like TWAP (Time-Weighted Average Price), VWAP (Volume-Weighted Average Price), iceberg orders, or custom smart routing algorithms.

Error Handling

Always implement proper error handling when executing orders:

try:
    result = await sdk.execute_order(order)
    if result['status'] == 'partially_filled':
        print(f"Warning: Only {result['filled_amount']} of {result['original_amount']} was filled")
except Exception as e:
    print(f"Error executing order: {e}")

Important Notes

  1. Orderbook Requirement: The orderbook configuration must have aggregated=True and contain exactly one symbol.
  2. Active Stream: You must start the orderbook stream before executing orders.
  3. API Keys: Ensure your API keys have trading permissions on the exchanges.
  4. Minimum Order Sizes: Each exchange has minimum order size requirements. Orders below these minimums will be excluded from the execution plan.
  5. Network Connectivity: Ensure stable internet connectivity for reliable order execution.
  6. Fee Awareness: The SOR considers exchange fees when making routing decisions if you provide fee information in the configuration.

Best Practices

  1. Start Small: Begin with small order sizes when testing.
  2. Use Sandbox Mode: Test your trading logic in sandbox mode before using real funds.
  3. Monitor Executions: Regularly check execution results and adjust your strategies accordingly.
  4. Handle Partial Fills: Implement logic to handle partially filled orders.
  5. Consider Timeouts: Implement timeout handling for long-running operations.
  6. Keep Logs: Maintain detailed logs of all order executions for auditing and analysis.

By following these guidelines, you can effectively use the Bjarkan SOR SDK’s order execution capabilities to optimize your trading across multiple exchanges.