Skip to content

WebSocket API

The WebSocket API provides a bidirectional connection between client and marketplace where clients receive live updates from the market. Time sensistive updates including trading and market information are delivered through a WebSocket connection to the client immediately.

WebSocket Reference

The WebSocket API provides streaming connections to the marketplace for real-time messaging. Trading and market data endpoints are available to receive live updates from the marketplace.

The Trading API allows client to receive all order and trading related updates that are connected to the specific user account.

The Market Data API allows all clients to receive market data updates such as ticker messages or orderbook updates.

Websocket API base URL: wss://glocalflexmarket.com/api/v1/ws/ e.g.: wss://glocalflexmarket.com/api/v1/ws/trade

WebSocket API Endpoints

Endpoint URL Description Message Types
/trade Trading events receive all order and contract updates
/orderbook Orderbook events subscribe to orderbook live events
/ticker Price and flexibility events subscribe tick live events

Info

The data object of the WebSocket events uses mostly the message format described in the REST API reference. To check units or description of specific fields please consult the REST API reference for further information.

Control Messages

During the initial successful connection, the WebSocket server sends a control message to the client to confirm the connection.

control_message

{
    "type": "control",
    "status": "success",
    "data": {
        "message": "Connected to the WebSocket API"
    }
}

Trading

/trade

The trading endpoint sends order and trade updates to the client. The user sends the access token during the initial connection to authenticate the user. Based on the access token the client receives all order and trade updates that are connected to the specific user account.

Note

The user only receives updates connected to its user id.

Event Type Description
order_status_update Status of an existing order has changed
contract_status_update Status updates for the contract associated with your orders
Status name Description
awaiting_baseline_data Order is waiting for baseline data
accepted Order has been accepted
matching_active Order is active in the matching engine
filled Order has been fully filled

order_status_update

 {
    "type": "order_status_update",
    "name": str,
    "created_at": date-time, 
    "data": {
        // order object
    }
} 

contract_status_update

Status name Description
verification_initiated Contract verification started
verification_completed_success Contract verification succeeded
settlement_initiated Contract settlement started
settlement_completed_success Contract settlement succeeded
 {
    "type": "order_status_update",
    "name": str,
    "created_at": date-time, 
    "data": {
        // contract object
    }
} 

Market Data

/ticker

The ticker endpoint streams market updates of the last matched orders to all connected clients. The message contains the last price and basic information about the flexibility event. After connecting to the WebSocket endpoint the updates are streamed to the client.

Event Type Description
ticker_update Information about the last trade closed in the marketplace
expired_order_update Order that has been expired inside the orderbook

ticker_update

 {
    "type": "ticker_update",
    "created_at": date-time, 
    "data": {
        "closed_at": date-time, // "2024-05-02T12:06:45.842304Z",
        "price": float,
        "delivery_start": date-time, // "2024-05-02T13:15:00.000Z",
        "delivery_end":  date-time, //"2024-05-02T14:15:00.000Z",
        "energy": float,
        "power": float,
        "location": {
            "location_id": [str],
            "country_code": str,
            "coordinates": {
                "latitude": float,
                "longitude": float
            }            
        }
    }
}
 {
    "type": "expired_order_update",
    "created_at": date-time, 
    "data": {
        "expired_at": data-time, // "2024-05-02T12:06:45.842304Z",
        "price": float,
        "delivery_start": date-time, // "2024-05-02T13:15:00.000Z",
        "delivery_end":  date-time, //"2024-05-02T14:15:00.000Z",
        "energy": float,
        "power": float,
        "location": {
            "location_id": [str],
            "country_code": str,
            "coordinates": {
                "latitude": float,
                "longitude": float
            }             
        }
    }

/orderbook

The orderbook endpoint streams the orderbook updates to all connected clients. The message contains the current orderbook state with bids and asks sort by price and time priority. The bids are sorted by price descending and the asks are sorted by price ascending.

orderbook_update

{
    "type": "orderbook_update",
    "created_at": date-time, 
    "data": {
        "bids": {
            "100": [
                {
                    "side": str, // buy
                    "power": float,
                    "price": float,
                    "delivery_start": date-time,
                    "delivery_end": date-time,
                    "location": {
                        "location_id": [
                            str
                        ],
                        "country_code": str
                    },
                    "energy": float
                },
                {
                    "side": str, // buy
                    "power": float,
                    "price": float,
                    "delivery_start": date-time,
                    "delivery_end": date-time,
                    "location": {
                        "location_id": [
                            str
                        ],
                    },
                    "energy": float
                },
                {
                    "side": str, // buy
                    "power": float,
                    "price": float,
                    "delivery_start": date-time,
                    "delivery_end": date-time,
                    "location": {
                        "location_id": [
                            str
                        ],
                        "country_code": str
                    },
                    "energy": float
                }
            ]
        },
        "asks": {
            "11": [
                {
                    "side": str, // sell
                    "power": float,
                    "price": float,
                    "delivery_start":  date-time,
                    "delivery_end": date-time,
                    "location": {
                        "location_id": [
                            str
                        ],
                        "country_code": str
                    },
                    "energy": float
                },
                { 
                    "side": str, // sell
                    "power": float,
                    "price": float,
                    "delivery_start": date-time,
                    "delivery_end": date-time,
                    "location": {
                        "location_id": [
                            str
                        ],
                        "country_code": str
                    },
                    "energy": float
                }
            ]
        }
    }
} 

WebSocket Client Example

WebSocket Example - Listen to all trading messages and price updates

This example demonstrates how to listen to all trading messages and price updates using the WebSocket API. If you use this example together with the previous Submit Order through the REST API, you can see the order status updates in real-time.

Info

Prerequisite:

Dependencies:

  • pip install "websocket-client>=1.7.0" "requests>=2.31.0"
"""
usage: ws_api_listener.py [-h] [--host] [-u] [-p] [-t]

WebSocket Example Client Listener

options:
  -h, --help        show this help message and exit
  --host            Host url, DEFAULT: glocalflexmarket.com
  -u , --username   Username for authentication, default: <username>
  -p , --password   Password for authentication, default: <password>
  -t , --endpoint   Order API endpoint, default: /api/v1/ws/trade

Python version: >= 3.10

Dependencies:

pip install websocket-client requests

"""

import argparse
import json
import multiprocessing
import os
import ssl
import threading
from dataclasses import dataclass
from time import sleep

import requests
import websocket

HOST = os.getenv("GFLEX_URL", "glocalflexmarket.com")
USERNAME = os.getenv("GFLEX_USER", "<username>")
PASSWORD = os.getenv("GFLEX_PASSWORD", "<password>")

CLIENT_ID = "glocalflexmarket_public_api"
AUTH_ENDPOINT = "/auth/oauth/v2/token"
ORDER_ENDPOINT = "/api/v1/ws/trade/"
SSL_VERIFY = True
PORT = 443
USER_MESSAGE = "Listen for messages, press Ctrl + c to quit): \n"

class WebSocketClient:
    def __init__(self, url, ssl_enabled=True, token=None):
        self.ws_url = url
        self.ssl_enabled = ssl_enabled
        self.token = token
        self.received_messages = []
        self.shutdown_pipe, self.shutdown_pipe2 = multiprocessing.Pipe()

        if self.token:
            headers = {
                "Authorization": f"Bearer {self.token}",
            }

        self.sslopt = (
            {
                "cert_reqs": ssl.CERT_NONE,
                "check_hostname": False,
                "ssl_context": ssl._create_unverified_context(),
            }
            if ssl_enabled
            else None
        )

        self.ws = websocket.WebSocketApp(
            url=self.ws_url,
            header=headers,
            on_message=self.on_message,
            on_ping=self.on_ping,
            on_close=self.on_close,
        )

        self.receive_thread = threading.Thread(target=self.receive_message, daemon=True)
        self.receive_thread.start()

    def receive_message(self):
        err = self.ws.run_forever(ping_interval=1, sslopt=self.sslopt)
        if err:
            # send exit message to shutdown the mmain thread
            self.shutdown_pipe.send("exit")
            print(f"WebSocket connection error: {err}\n")

    def on_close(self, ws):
        print("WebSocket closed by server")
        self.shutdown_pipe.send("exit")

    def on_message(self, ws, message):
        parsed_message = json.loads(message)
        print(f"Received message:\n {json.dumps(parsed_message, indent=4)} \n")
        print(USER_MESSAGE)
        self.received_messages.append(parsed_message)

    def on_ping(self, ws, data):
        ws.pong()

    def send_message(self, message):
        self.ws.send(message)

    def run(self):
        try:
            print(USER_MESSAGE)
            while True:
                # Check if shutdown signal is received from receive_message thread
                if self.shutdown_pipe2.poll():
                    if self.shutdown_pipe2.recv() == "exit":
                        print("Exiting...")
                        break
                sleep(0.1)

        except KeyboardInterrupt:
            pass
        print("Closing connection...")
        self.ws.close()


@dataclass
class Token:
    access_token: str
    refresh_token: str
    expires_in: int


def request_token(
    client_id: str,
    username: str,
    password: str,
    token_url: str,
    ssl_verify: bool = True,
) -> Token | None:

    payload = {
        "client_id": client_id,
        "grant_type": "password",
        "username": username,
        "password": password,
    }
    response = request_access_token(token_url, payload, ssl_verify=ssl_verify)
    return check_response(response)


def request_access_token(
    token_url: str, payload: dict, ssl_verify=True
) -> requests.Response:
    response = requests.post(
        token_url,
        data=payload,
        headers={"Content-Type": "application/x-www-form-urlencoded"},
        verify=ssl_verify,
    )
    return response


def check_response(response):

    if response.status_code != 200:
        print(f"Failed to get token from response {response}")
        return None

    oauth_resp = response.json()
    token = Token(
        oauth_resp["access_token"],
        oauth_resp["refresh_token"],
        oauth_resp["expires_in"],
    )
    return token


def cli_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description="WebSocket Example Client Listener")
    parser.add_argument("--host", default=HOST, dest="host", metavar="", help=f"Host url, DEFAULT: {HOST}")
    parser.add_argument("-u", "--username", dest="username", metavar="", default=USERNAME, help=f"Username for authentication, default: {USERNAME}")
    parser.add_argument("-p", "--password", dest="password", metavar="", default=PASSWORD, help=f"Password for authentication, default: {PASSWORD}")
    parser.add_argument("-t", "--endpoint", dest="endpoint", default=ORDER_ENDPOINT, metavar="", help=f"Order API endpoint, default: {ORDER_ENDPOINT}")
    return parser.parse_args()

def main():

    args = cli_args()
    host = args.host
    user = args.username
    secret = args.password
    ws_endpoint = args.endpoint

    auth_url = f"https://{host}:{PORT}{AUTH_ENDPOINT}"
    ws_url = f"wss://{host}:{PORT}{ws_endpoint}"

    token = request_token(CLIENT_ID, user, secret, auth_url, ssl_verify=SSL_VERIFY)

    if token is None:
        print("Failed to get token")
        return
    # token is used for authencation with the websocket endpoint
    access_token = token.access_token

    print("#############################################################")
    print(f"Connecting to Websocket endpoint {ws_url}")
    print("#############################################################")

    ws_client = WebSocketClient(ws_url, token=access_token)
    ws_client.run()


if __name__ == "__main__":
    main()