Skip to main content

Python client library for GAME trading API

Project description

GAME API Client

Simple Python client for the GAME trading API.

Python Compatibility

  • Python 3.8+ (compatible 3.8 - 3.14)
  • Tested on: 3.8, 3.9, 3.10, 3.11, 3.12, 3.13, 3.14
  • Recommended: 3.11+ for best performance

Installation

Option 1: pip (recommended)

pip install game-api-client

Option 2: From source

git clone https://github.com/groupe-e/game-api-client.git
cd game-api-client
pip install -r requirements.txt
pip install -e .

Option 3: Poetry (for development)

git clone https://github.com/groupe-e/game-api-client.git
cd game-api-client
poetry install

Quick Start

# Copy environment template
cp .env.example .env

# Edit .env with your credentials
# GAME_API_TOKEN="your_personal_token"
# GAME_API_URL="https://your-game-api-url.com"
# GAME_API_VERIFY_SSL=false

# Run demo
python demo.py

Or programmatically:

import asyncio
from game_client import GameAPI

async def main():
    api = GameAPI(
        token="your_authentik_token",
        base_url="https://your-game-api-url.com"
    )
    
    # Test connection
    if await api.test_connection():
        print("Connected!")
    
    # Get data
    status = await api.get_status()
    trades = await api.get_trades()
    market = await api.get_market_data()

asyncio.run(main())

Environment Variables

Create a .env file:

GAME_API_TOKEN=your_authentik_personal_token
GAME_API_URL=https://your-game-api-url.com
GAME_API_VERIFY_SSL=false

Available Methods

Connection & Status

  • test_connection() - Test API connection and authentication
  • get_status() - Get API status and health information

Market Data

  • get_market_data() - Get market data points
  • get_trades(direction=None, duration=None) - Get trades with optional filters
    • direction: "buy" or "sell" (optional)
    • duration: Time duration like "15m", "1h" (optional)

Order Management

  • get_orders(direction=None) - Get orders with optional filter
    • direction: "buy" or "sell" (optional)
  • find_order_by_id(order_id) - Find specific order by ID
    • order_id: Order ID (string or numeric)
  • create_order(order_data) - Create new order (single or bulk)
    • order_data: Order object or {"orders": [...]} for bulk
  • delete_order(order_id) - Delete order by ID
    • order_id: Order ID (handles both string and numeric formats)

Spot Data

  • get_spot_data() - Get spot market data
  • get_spot_status() - Get spot API status

Usage Examples

Basic Usage

import asyncio
from game_client import GameAPI

async def main():
    api = GameAPI(
        token="your_authentik_token",
        base_url="https://your-game-api-url.com",
        verify_ssl=False  # Set to True for production
    )
    
    # Test connection
    if await api.test_connection():
        print("✅ Connected to API!")
    
    # Get basic data
    status = await api.get_status()
    market_data = await api.get_market_data()
    
    print(f"API Status: {status['status']}")
    print(f"Market data points: {len(market_data)}")

asyncio.run(main())

Order Management

async def order_examples():
    api = GameAPI(token="your_token", base_url="https://api.example.com")
    
    # Get all orders
    orders = await api.get_orders()
    print(f"Found {len(orders)} orders")
    
    # Get only buy orders
    buy_orders = await api.get_orders(direction="buy")
    
    # Create a single displayed order
    from datetime import datetime, timedelta
    future_time = datetime.now() + timedelta(hours=4)
    
    single_order = {
        "side": "buy",
        "price": 100.0,
        "volume": 1.0,
        "date": future_time.strftime("%Y-%m-%d"),
        "start_time": "14:00",
        "end_time": "15:00", 
        "duration": "15m",
        "tz_offset_hours": 0,
        "state": "displayed"  # Visible on the market (default)
    }
    
    result = await api.create_order(single_order)
    print(f"Created order: {result['order_id']}")
    
    # Create a hidden order (not visible to other participants)
    hidden_order = {
        "side": "buy",
        "price": 100.0,
        "volume": 1.0,
        "date": future_time.strftime("%Y-%m-%d"),
        "start_time": "14:00",
        "end_time": "15:00", 
        "duration": "15m",
        "tz_offset_hours": 0,
        "state": "hidden"  # Hidden from other participants
    }
    
    result = await api.create_order(hidden_order)
    print(f"Created hidden order: {result['order_id']}")
    
    # Create bulk orders with mixed states
    bulk_orders = {
        "orders": [
            {
                "side": "buy",
                "price": 100.0,
                "volume": 1.0,
                "date": future_time.strftime("%Y-%m-%d"),
                "start_time": "14:00",
                "end_time": "15:00",
                "duration": "15m",
                "tz_offset_hours": 0,
                "state": "displayed"
            },
            {
                "side": "sell", 
                "price": 200.0,
                "volume": 0.5,
                "date": future_time.strftime("%Y-%m-%d"),
                "start_time": "14:00",
                "end_time": "15:00",
                "duration": "15m",
                "tz_offset_hours": 0,
                "state": "hidden"
            }
        ]
    }
    
    bulk_result = await api.create_order(bulk_orders)
    print(f"Created {len(bulk_result['created'])} orders")
    
    # Find specific order
    order = await api.find_order_by_id(result['order_id'])
    if order:
        print(f"Found order: {order['direction']} {order['volume']} @ {order['price']}")
    
    # Delete order
    delete_result = await api.delete_order(result['order_id'])
    print(f"Order deleted: {delete_result['status']}")

Market Data with Filters

async def market_data_examples():
    api = GameAPI(token="your_token", base_url="https://api.example.com")
    
    # Get all trades
    all_trades = await api.get_trades()
    
    # Get only buy trades
    buy_trades = await api.get_trades(direction="buy")
    
    # Get trades with specific duration
    short_trades = await api.get_trades(duration="15m")
    
    # Get spot data
    spot_data = await api.get_spot_data()
    spot_status = await api.get_spot_status()
    
    print(f"Total trades: {len(all_trades)}")
    print(f"Buy trades: {len(buy_trades)}")
    print(f"Short duration trades: {len(short_trades)}")
    print(f"Spot data points: {len(spot_data)}")
    print(f"Spot status: {spot_status['status']}")

Error Handling

async def error_handling_example():
    api = GameAPI(token="your_token", base_url="https://api.example.com")
    
    try:
        # This might fail if order data is invalid
        result = await api.create_order({"invalid": "data"})
    except GameAPIError as e:
        print(f"API Error: {e}")
    
    try:
        # This might fail if order doesn't exist
        await api.delete_order("non_existent_id")
    except GameAPIError as e:
        print(f"Delete failed: {e}")
    
    # Safe order lookup
    order = await api.find_order_by_id("some_id")
    if order is None:
        print("Order not found")
    else:
        print(f"Found order: {order}")

Order Data Format

Required Fields

All orders must include these required fields:

{
    "side": "buy",           # REQUIRED: "buy" or "sell"
    "price": 100.0,          # REQUIRED: Any number (positive, negative, zero)
    "volume": 1.0,           # REQUIRED: Positive number (> 0)  
    "date": "2024-01-15",    # REQUIRED: Future date in YYYY-MM-DD format
    "start_time": "14:00",   # REQUIRED: HH:MM format (future time)
    "end_time": "15:00",     # REQUIRED: HH:MM format (after start_time)
    "duration": "15m",       # REQUIRED: Time duration ("15m", "30m", "1h", etc.)
    "tz_offset_hours": 0,    # OPTIONAL: Timezone offset from UTC (auto-detected if not provided)
    "options": {               # OPTIONAL: Additional order options
        "trader_suffixe": "my_bot"  # OPTIONAL: Suffix appended to trader name (max 50 chars)
    }
}

Field Constraints

side

  • Values: "buy" or "sell" (case-sensitive)
  • Required: Yes

price

  • Type: Number (float or int)
  • Required: Yes
  • Constraints: Can be positive, negative, or zero
  • Example: 100.0, -50.5, 0.0

volume

  • Type: Positive number (float or int)
  • Required: Yes
  • Constraints: Must be > 0
  • Example: 1.0, 0.5

date

  • Type: String
  • Format: YYYY-MM-DD
  • Required: Yes
  • Constraints: Must be a future date
  • Example: "2024-01-15"

start_time / end_time

  • Type: String
  • Format: HH:MM (24-hour)
  • Required: Yes
  • Constraints:
    • Must be future times
    • end_time must be after start_time
  • Example: "14:00", "15:30"

duration

  • Type: String
  • Required: Yes
  • Common values: "15m", "30m", "1h", "4h"
  • Example: "15m" (15 minutes)

tz_offset_hours

  • Type: Integer
  • Required: No (auto-detected if not provided)
  • Range: Typically -12 to +14
  • Example: 0 (UTC), 1 (UTC+1), -5 (UTC-5)
  • Note: If not provided, the system will auto-detect your timezone

state

  • Type: String
  • Required: No (defaults to "displayed")
  • Values: "displayed" or "hidden"
  • Description: Controls order visibility. A "displayed" order is visible on the market, while a "hidden" order is not shown to other participants.
  • Example: "displayed", "hidden"

options

  • Type: Dict (optional)
  • Required: No
  • Description: Additional order options passed to the API. Internal use only (Groupe-E).
  • Fields:
    • trader_suffixe (str, optional): Suffix appended to the trader name (e.g. Hermes becomes Hermes_my_bot). Max 50 characters. Only available for Groupe-E accounts.
  • Example: {"trader_suffixe": "my_bot"}, None, {}
  • Note: This option is ignored for non Groupe-E accounts.

Complete Order Examples

Valid Buy Order (displayed)

buy_order = {
    "side": "buy",
    "price": 150.25,
    "volume": 2.5,
    "date": "2024-12-15",
    "start_time": "14:00",
    "end_time": "14:15", 
    "duration": "15m",
    "tz_offset_hours": 1,  # CET (UTC+1)
    "state": "displayed"   # Visible on the market (default)
}

Valid Sell Order (hidden)

sell_order = {
    "side": "sell",
    "price": 200.0,
    "volume": 1.0,
    "date": "2024-12-15",
    "start_time": "15:30",
    "end_time": "16:00",
    "duration": "30m", 
    "tz_offset_hours": 0,  # UTC
    "state": "hidden"      # Hidden from other participants
}

Time Validation Rules

  1. Date must be in the future - Past dates will be rejected
  2. Times must be in the future - Current time already passed will be rejected
  3. End time > Start time - Duration must be positive
  4. Duration should match time window - (end_time - start_time) should equal duration

Common Validation Errors

# ❌ Missing required field
{"side": "buy", "price": 100}  # Missing volume, date, etc.

# ❌ Invalid side
{"side": "invalid", ...}  # Must be "buy" or "sell"

# ❌ Negative price/volume  
{"side": "buy", "price": -10, "volume": 1.0, ...}  # Negative prices are now allowed!

# ❌ Negative volume
{"side": "buy", "price": 100, "volume": -1.0, ...}  # Volume must still be positive

# ❌ Past date
{"side": "buy", "date": "2020-01-01", ...}

# ❌ Invalid time format
{"side": "buy", "start_time": "2:00 PM", ...}  # Must be HH:MM

# ❌ End time before start time
{"side": "buy", "start_time": "15:00", "end_time": "14:00", ...}

# ❌ Invalid state
{"side": "buy", "state": "invisible", ...}  # Must be "displayed" or "hidden"

Bulk Orders Format

bulk_orders = {
    "orders": [
        # Order 1 - displayed (default)
        {
            "side": "buy",
            "price": 100.0,
            "volume": 1.0,
            "date": "2024-12-15",
            "start_time": "14:00",
            "end_time": "14:15",
            "duration": "15m",
            "tz_offset_hours": 0
        },
        # Order 2 - hidden
        {
            "side": "sell",
            "price": 200.0,
            "volume": 0.5,
            "date": "2024-12-15", 
            "start_time": "14:30",
            "end_time": "15:00",
            "duration": "30m",
            "tz_offset_hours": 0,
            "state": "hidden"
        },
        # ... more orders (up to API limits)
    ]
}

Helper Function for Order Creation

from datetime import datetime, timedelta

def create_order_data(side, price, volume, hours_in_future=4):
    """Helper to create valid order data"""
    future_time = datetime.now() + timedelta(hours=hours_in_future)
    
    # Round to next quarter hour
    minutes = future_time.minute
    if minutes < 15:
        future_time = future_time.replace(minute=15, second=0, microsecond=0)
    elif minutes < 30:
        future_time = future_time.replace(minute=30, second=0, microsecond=0)
    elif minutes < 45:
        future_time = future_time.replace(minute=45, second=0, microsecond=0)
    else:
        future_time = future_time.replace(minute=0, second=0, microsecond=0) + timedelta(hours=1)
    
    future_end = future_time + timedelta(minutes=15)
    
    return {
        "side": side,
        "price": float(price),
        "volume": float(volume),
        "date": future_time.strftime("%Y-%m-%d"),
        "start_time": future_time.strftime("%H:%M"),
        "end_time": future_end.strftime("%H:%M"),
        "duration": "15m",
        "tz_offset_hours": 0,
        "state": "displayed"  # or "hidden"
    }

# Usage
order = create_order_data("buy", 100.0, 1.0)
result = await api.create_order(order)

Response Formats

Order Creation Response

# Single order
{
    "order_id": "ord_abc123",
    "created_at": "2024-01-15T14:00:00Z",
    "direction": "buy",
    "volume": 1.0,
    "price": 100.0,
    "state": "Displayed",
    "contract": "BASE-QUOTE",
    "trader": "your_trader_name",
    "date": "2024-01-15",
    "start_time": "14:00",
    "end_time": "14:15",
    "duration": "15m",
    "tz_offset_hours": 0,
    "updated_at": "2024-01-15T14:00:00Z"
}

# Bulk orders
{
    "requested": 2,
    "created": [
        {
            "index": 0,
            "result": {
                "order_id": "ord_abc123",
                "created_at": "2024-01-15T14:00:00Z",
                "direction": "buy",
                "volume": 1.0,
                "price": 100.0,
                "state": "Displayed",
                "contract": "BASE-QUOTE",
                "trader": "your_trader_name",
                "date": "2024-01-15",
                "start_time": "14:00",
                "end_time": "14:15",
                "duration": "15m",
                "tz_offset_hours": 0,
                "updated_at": "2024-01-15T14:00:00Z"
            }
        },
        {
            "index": 1,
            "result": {
                "order_id": "ord_def456",
                "created_at": "2024-01-15T14:00:00Z",
                "direction": "sell",
                "volume": 0.5,
                "price": 200.0,
                "state": "Displayed",
                "contract": "BASE-QUOTE",
                "trader": "your_trader_name",
                "date": "2024-01-15",
                "start_time": "14:30",
                "end_time": "15:00",
                "duration": "30m",
                "tz_offset_hours": 0,
                "updated_at": "2024-01-15T14:00:00Z"
            }
        }
    ],
    "failed": [
        {
            "index": 2,
            "error": "Invalid price: must be positive number"
        }
    ]
}

Order Data (from get_orders)

{
    "order_id": 1234,                    # Numeric ID for deletion
    "direction": "buy",                  # "buy" or "sell"
    "volume": 1.0,                       # Order volume
    "price": 100.0,                      # Order price
    "state": "Displayed",                 # Order state
    "contract": "BASE-QUOTE",             # Trading pair
    "trader": "your_trader_name",         # Trader identifier
    "date": "2024-01-15",                # Order date
    "start_time": "14:00",               # Start time
    "end_time": "14:15",                 # End time
    "duration": "15m",                   # Duration
    "tz_offset_hours": 0,                # Timezone offset
    "created_at": "2024-01-15T14:00:00Z", # Creation timestamp
    "updated_at": "2024-01-15T14:00:00Z", # Last update timestamp
    "executed_volume": 0.0,              # Volume already executed (if any)
    "remaining_volume": 1.0,             # Volume remaining to execute
    "average_price": null,               # Average execution price (if executed)
    "execution_count": 0,                # Number of executions
    "total_value": 100.0,                # Total order value (volume × price)
    "fees": 0.0,                         # Trading fees
    "metadata": {}                       # Additional metadata
}

Market Data Response

[
    {
        "timestamp": "2024-01-15T14:00:00Z",
        "contract": "BASE-QUOTE",
        "price": 100.5,
        "volume": 1500.0,
        "bid": 100.4,
        "ask": 100.6,
        "spread": 0.2,
        "high_24h": 105.0,
        "low_24h": 95.0,
        "volume_24h": 50000.0,
        "change_24h": 2.5,
        "change_percent_24h": 2.56
    }
]

Trades Data Response

[
    {
        "trade_id": "trade_abc123",
        "contract": "BASE-QUOTE",
        "direction": "buy",
        "volume": 1.0,
        "price": 100.5,
        "timestamp": "2024-01-15T14:00:00Z",
        "buyer": "buyer_name",
        "seller": "seller_name",
        "fees": 0.25,
        "duration": "15m",
        "execution_time": "2024-01-15T14:07:30Z"
    }
]

Spot Data Response

{
    "contract": "BASE-QUOTE",
    "price": 100.5,
    "volume": 1500.0,
    "bid": 100.4,
    "ask": 100.6,
    "spread": 0.2,
    "high_24h": 105.0,
    "low_24h": 95.0,
    "volume_24h": 50000.0,
    "change_24h": 2.5,
    "change_percent_24h": 2.56,
    "timestamp": "2024-01-15T14:00:00Z",
    "order_book": {
        "bids": [
            {"price": 100.4, "volume": 500.0},
            {"price": 100.3, "volume": 300.0}
        ],
        "asks": [
            {"price": 100.6, "volume": 400.0},
            {"price": 100.7, "volume": 200.0}
        ]
    }
}

API Status Response

{
    "status": "healthy",
    "version": "1.2.3",
    "timestamp": "2024-01-15T14:00:00Z",
    "services": {
        "orders": "operational",
        "trades": "operational", 
        "market_data": "operational",
        "spot": "operational"
    },
    "uptime": 86400,
    "response_time_ms": 45,
    "rate_limit": {
        "requests_per_minute": 1000,
        "requests_remaining": 999,
        "reset_time": "2024-01-15T14:01:00Z"
    }
}

Spot Status Response

{
    "status": "operational",
    "timestamp": "2024-01-15T14:00:00Z",
    "active_contracts": ["BASE-QUOTE", "OTHER-PAIR"],
    "market_open": true,
    "next_update": "2024-01-15T14:01:00Z",
    "last_update": "2024-01-15T13:59:00Z"
}

Error Handling

The client provides comprehensive error handling:

GameAPIError

All API errors raise GameAPIError with descriptive messages:

from game_client import GameAPI, GameAPIError

async def example():
    api = GameAPI(token="token", base_url="https://api.example.com")
    
    try:
        await api.create_order({"invalid": "data"})
    except GameAPIError as e:
        print(f"API Error: {e}")
        # Output: "API Error: Single order: Missing required field 'side'"

Common Errors

  • Authentication: Invalid token or connection issues
  • Validation: Missing required fields or invalid values
  • Not Found: Order ID doesn't exist
  • Network: Connection timeout or server errors

Best Practices

  1. Always handle errors
try:
    result = await api.create_order(order_data)
except GameAPIError as e:
    logger.error(f"Order creation failed: {e}")
    return None
  1. Test connection first
if not await api.test_connection():
    print("Cannot connect to API")
    return
  1. Use find_order_by_id for safe lookups
order = await api.find_order_by_id(order_id)
if order is None:
    print("Order not found")
else:
    print(f"Found: {order}")
  1. Validate order data before sending
# The client validates automatically, but you can pre-validate
required_fields = ["side", "price", "volume", "date", "start_time", "end_time"]
for field in required_fields:
    if field not in order_data:
        raise ValueError(f"Missing required field: {field}")

Advanced Usage

Custom Timeout and SSL

api = GameAPI(
    token="your_token",
    base_url="https://api.example.com",
    verify_ssl=True,        # Enable SSL verification (production)
    timeout=60              # Custom timeout in seconds
)

Bulk Operations

# Create multiple orders efficiently
bulk_orders = {
    "orders": [
        {"side": "buy", "price": 100, "volume": 1, ...},
        {"side": "sell", "price": 200, "volume": 0.5, ...},
        # ... up to 100 orders
    ]
}

result = await api.create_order(bulk_orders)
print(f"Created: {len(result['created'])}, Failed: {len(result['failed'])}")

Rate Limiting

The client handles rate limiting automatically through token management, but you should implement your own rate limiting for bulk operations:

import asyncio

async def safe_bulk_create(api, orders_list, batch_size=10):
    """Create orders in batches to avoid rate limits"""
    results = []
    
    for i in range(0, len(orders_list), batch_size):
        batch = orders_list[i:i + batch_size]
        bulk_data = {"orders": batch}
        
        try:
            result = await api.create_order(bulk_data)
            results.extend(result.get('created', []))
            
            # Small delay between batches
            if i + batch_size < len(orders_list):
                await asyncio.sleep(1)
                
        except GameAPIError as e:
            print(f"Batch {i//batch_size + 1} failed: {e}")
    
    return results

Testing

# Install dependencies
pip install -r requirements.txt
pip install -e .

# Install test dependencies
pip install pytest pytest-asyncio pytest-cov

# Run tests
pytest tests/ -v --cov=game_client

Docker

# Build
docker build -f Dockerfile.demo -t game-api-client .

# Option 1: Run with environment variables (recommended for production)
docker run --rm \
  -e GAME_API_TOKEN="your_token" \
  -e GAME_API_URL="https://your-game-api-url.com" \
  -e GAME_API_VERIFY_SSL=false \
  game-api-client

# Option 2: Run with .env file (for development)
docker run --rm \
  -v $(pwd)/.env:/app/.env:ro \
  game-api-client

# Option 3: Run with custom command
docker run --rm \
  -e GAME_API_TOKEN="your_token" \
  -e GAME_API_URL="https://your-game-api-url.com" \
  -e GAME_API_VERIFY_SSL=false \
  game-api-client python example.py

Development

# Setup development environment
poetry install

# Run example
python example.py

# Run tests
poetry run pytest tests/ -v

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

game_api_client-0.0.8.tar.gz (16.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

game_api_client-0.0.8-py3-none-any.whl (11.4 kB view details)

Uploaded Python 3

File details

Details for the file game_api_client-0.0.8.tar.gz.

File metadata

  • Download URL: game_api_client-0.0.8.tar.gz
  • Upload date:
  • Size: 16.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for game_api_client-0.0.8.tar.gz
Algorithm Hash digest
SHA256 92800aa317445c736047e12d496880866be3ab8ea9e10878e26481ea8f1f9d55
MD5 d8dad3847df8a10fe783d2d7f26479a5
BLAKE2b-256 76fadbc19031ff049bee5e9b22eb2b2d718d0e8f69281b51a95553c2ab6d103f

See more details on using hashes here.

File details

Details for the file game_api_client-0.0.8-py3-none-any.whl.

File metadata

File hashes

Hashes for game_api_client-0.0.8-py3-none-any.whl
Algorithm Hash digest
SHA256 ad189db005e50ff47b78c65f7b1c943bae9ae90c2410e21818be59101b5d4a9d
MD5 0c44979a33dd771e5aa271bac8c0b1e0
BLAKE2b-256 61a17c64415851875366e3cb8fe6d998e6a3ce3b2cb4ab781532924451cccb10

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page