# API Keys Source: https://docs.qfex.com/api-reference/api-keys Create and securely use account-scoped API keys with granular permissions. API keys authenticate private account and trading requests. Each key has a public key and a secret key, an account scope, and a set of permissions. ## Create an API key 1. Sign in at [qfex.com](https://qfex.com) and select your profile in the bottom right corner. ![](https://mintcdn.com/qfex/LD0EtLtcrjzAI_0J/images/api_key/1.png?fit=max\&auto=format\&n=LD0EtLtcrjzAI_0J\&q=85\&s=33b5ace9fa054bfb6270adb08a37a0e1) 2. Navigate to **Developer Settings** via the carousel. ![](https://mintcdn.com/qfex/LD0EtLtcrjzAI_0J/images/api_key/2.png?fit=max\&auto=format\&n=LD0EtLtcrjzAI_0J\&q=85\&s=8d6618d0c17a0bf5b5dd78f1985b660f) 3. Click **Generate public and secret API Keys**. ![](https://mintcdn.com/qfex/LD0EtLtcrjzAI_0J/images/api_key/3.png?fit=max\&auto=format\&n=LD0EtLtcrjzAI_0J\&q=85\&s=1b7b5ab90e72d02f9bcfa133c03695db) 4. Ensure you have 2FA enabled. ![](https://mintcdn.com/qfex/LD0EtLtcrjzAI_0J/images/api_key/4.png?fit=max\&auto=format\&n=LD0EtLtcrjzAI_0J\&q=85\&s=a8f712aee7ef85e4d78fd1abcbf14246) 5. Provide a descriptive name, choose the account access and permissions the key needs, and confirm. ![](https://mintcdn.com/qfex/LD0EtLtcrjzAI_0J/images/api_key/5.png?fit=max\&auto=format\&n=LD0EtLtcrjzAI_0J\&q=85\&s=be500c5f1c214ca058bc919521272a23) 6. **Copy** the generated key and store it securely—it will only be shown once. ![](https://mintcdn.com/qfex/LD0EtLtcrjzAI_0J/images/api_key/6.png?fit=max\&auto=format\&n=LD0EtLtcrjzAI_0J\&q=85\&s=cca987090f4920ab4953b408c9d2392f) ## Account access and permissions Each API key is limited by both its **account access** and its **permissions**. A request must satisfy both settings. For account access, choose either: * **A specific account**: The key can be used only with the selected primary account or subaccount. * **All accounts**: The key can be used with any account you own. Use this only when the integration genuinely needs access across accounts. Permissions control what the key can do: | Permission | Allows | | - | - | | Execute orders | Place, modify, and cancel orders, and perform related trading actions. | | View orders | Read orders, fills, and trading history. | | View positions | Read open position information. | | View balance (`view_balance`) | Read balances, equity, fees, deposit addresses with `GET /address`, and registered withdrawal addresses with `GET /withdrawal-addresses`. | | Deposit and withdraw (`deposit_withdraw`) | Use supported funding and withdrawal actions, rotate deposit addresses, and manage bank accounts. Crypto withdrawals require a registered destination. Available only to keys with All accounts access. | Create keys with only the permissions required for their intended use. For example, reporting software may need view permissions but should not need order execution or withdrawal access. ## Use an API key with a third party If you authorize a trusted third-party application to access QFEX with an API key: 1. Create a dedicated key for that application. Do not reuse your personal integration key. 2. Limit it to the required account and enable only the permissions the application needs. 3. Provide the public and secret key through a secure channel. The application never needs your QFEX password or MFA codes. 4. The application must keep the secret key on a secure server and use it to sign authenticated requests. It must never expose the secret in browser code, mobile applications, logs, URLs, or source control. 5. Revoke the key from Developer Settings when the integration is no longer needed or if either credential may have been exposed. The secret key is displayed only once. QFEX cannot display it again after creation; create a replacement key if it is lost. For direct integrations, see [WebSocket authentication](/websocket/channels/trade/authenticate) for the trading signing flow or [Programmatic deposits and withdrawals](/api-reference/programmatic-funding) for funding REST requests. A third-party application can also use [QFEX OAuth](/api-reference/builder-integration) or [wallet-signed builder registration](/api-reference/builder-wallet-registration) to connect a user account. > [!WARNING] > > Treat the secret key like a password. Anyone who has both key values can act within the account access and permissions you selected. ## Builder-issued API keys [Builder wallet registration](/api-reference/builder-wallet-registration) lets an enabled builder create a user's trading key after the user signs a wallet message. The builder authenticates the registration request with its own API key and `register_user` permission. The returned user key is restricted to the main account and grants trading, account reads, deposit-address reads, and crypto withdrawals to registered destinations. Its `view_balance` permission authorizes `GET /address` and `GET /withdrawal-addresses`. Registered-destination crypto withdrawals with `POST /withdraw` are enabled through builder key issuance and replacement; this access is not selectable in Developer Settings. It has `deposit_withdraw` disabled, so it cannot rotate deposit addresses, manage bank accounts, or withdraw USD. See [programmatic funding](/api-reference/programmatic-funding) for the requests. The key is labelled `builder-` followed by the builder's username. Creating another key for the same user and builder label replaces the previous key. Keys with labels starting with `builder-` or `managed` are excluded from the normal API-key listing. ## Create an API key programmatically Standard programmatic key creation uses a QFEX user session, not an existing API key. First [authenticate with a wallet](/api-reference/wallet-authentication) or another supported sign-in method, then complete MFA to obtain a recently elevated access token. Send the elevated token to the key-generation endpoint. Supply every permission explicitly so the key cannot gain permissions through a default-value change: ```javascript theme={null} const QFEX_AUTH_URL = "https://verify.qfex.com"; const QFEX_AUTH_CLIENT_KEY = "YOUR_QFEX_AUTH_CLIENT_KEY"; export async function createQfexApiKey({ accessToken, name, accountId = "*", permissions = {}, }) { const response = await fetch( `${QFEX_AUTH_URL}/rest/v1/rpc/generate_api_key_v3`, { method: "POST", headers: { "Content-Type": "application/json", apikey: QFEX_AUTH_CLIENT_KEY, Authorization: `Bearer ${accessToken}`, }, body: JSON.stringify({ p_name: name, p_account_id: accountId, p_execute_orders: permissions.executeOrders === true, p_view_orders: permissions.viewOrders === true, p_view_positions: permissions.viewPositions === true, p_view_balance: permissions.viewBalance === true, p_deposit_withdraw: accountId === "*" && permissions.depositWithdraw === true, }), }, ); const result = await response.json(); if (!response.ok) { throw new Error(result.message ?? "API key creation failed"); } return { publicKey: result.public_key, secretKey: result.secret_key, }; } ``` Create a key after the user has selected its scope and permissions: ```javascript theme={null} const apiKey = await createQfexApiKey({ accessToken: elevatedSession.access_token, name: "order-service", accountId: "11111111-1111-4111-8111-111111111111", permissions: { executeOrders: true, viewOrders: true, viewPositions: true, viewBalance: true, depositWithdraw: false, }, }); await saveToEncryptedCredentialStore(apiKey); ``` The `accountId` value must be one of: * `*` for all primary accounts and subaccounts owned by the user * The user's primary account ID * A subaccount ID owned by the user The deposit and withdrawal permission is available only when `accountId` is `*`. QFEX forces that permission to `false` for a key restricted to one account. > [!WARNING] > > Never return the generated secret to logs, analytics, browser storage, or an > untrusted caller. The response is the only time QFEX returns the plaintext > secret. ### Elevate a session with TOTP API-key creation requires recent MFA verification. If the user has a verified authenticator-app factor, challenge and verify that factor before creating the key: ```javascript theme={null} async function qfexAuthRequest(path, { accessToken, body }) { const response = await fetch(`${QFEX_AUTH_URL}${path}`, { method: "POST", headers: { "Content-Type": "application/json", apikey: QFEX_AUTH_CLIENT_KEY, Authorization: `Bearer ${accessToken}`, }, body: JSON.stringify(body), }); const result = await response.json(); if (!response.ok) { throw new Error(result.msg ?? result.message ?? "MFA request failed"); } return result; } export async function elevateQfexSessionWithTotp({ accessToken, factorId, code, }) { const challenge = await qfexAuthRequest( `/auth/v1/factors/${encodeURIComponent(factorId)}/challenge`, { accessToken, body: { factorId } }, ); return qfexAuthRequest( `/auth/v1/factors/${encodeURIComponent(factorId)}/verify`, { accessToken, body: { challenge_id: challenge.id, code, }, }, ); } ``` Use the `access_token` returned by the verification response for key creation. Obtain `factorId` from the signed-in user's verified factors. The user must provide the current authenticator code; do not store or attempt to generate it for them. ### HTTP request The raw key-generation request is: ```http theme={null} POST /rest/v1/rpc/generate_api_key_v3 Host: verify.qfex.com Content-Type: application/json apikey: Authorization: Bearer { "p_name": "order-service", "p_account_id": "*", "p_execute_orders": true, "p_view_orders": true, "p_view_positions": true, "p_view_balance": true, "p_deposit_withdraw": false } ``` A successful response contains `public_key` and `secret_key`. Persist the pair before discarding the response; the secret cannot be retrieved later. ### Common errors | Error | Resolution | | - | - | | `no_recent_mfa_auth` | Complete a new MFA challenge and retry with the elevated access token. | | `api_key_name_required` | Provide a non-empty `p_name`. | | Invalid account scope | Use `*`, the user's primary account ID, or an owned subaccount ID. | | Duplicate key name | Choose a different name for this user. | | `401` | Sign in again or refresh the user session. | # Builder Codes API Source: https://docs.qfex.com/api-reference/builder-codes Create and retrieve builder codes, attach them to Trade WebSocket sessions, and read builder rewards For an overview of builder codes and fee-share mechanics, see [Builder Codes](/qfex/builder-code). If you are building an aggregator, trading terminal, or other application that needs to authenticate end users and attribute their trading automatically, start with the [Builder Integration Guide](/api-reference/builder-integration). For wallet signatures that return main-account trading keys, use [Builder wallet registration](/api-reference/builder-wallet-registration). ## Create a builder code Each QFEX account may have **one active builder code**. An aggregator normally creates one code for its own account and reuses that same UUID across its end-user Trade WebSocket connections. Do not create one builder code per trader. `POST /user/builder-code`. HMAC headers are required. A second active create returns **409 Conflict**. Specify both fee shares in basis points: * `qfex_fee_share_bps`: QFEX's share of the trading fee. Minimum **5000** (50%). * `builder_fee_share_bps`: the builder's share of the trading fee. Must be **>= 0** and at most **5000** (50%). * The two shares need not sum to 10000, but their sum must be at most 10000. ```http theme={null} POST /user/builder-code x-qfex-public-key: qfex_pub_xxxxx x-qfex-hmac-signature: 5f2e... x-qfex-nonce: c0ffee... x-qfex-timestamp: 1760545414 Content-Type: application/json { "qfex_fee_share_bps": 5000, "builder_fee_share_bps": 3000 } ``` Example **201** response: ```json theme={null} { "builder_code": "11111111-1111-4111-8111-111111111111", "qfex_fee_share_bps": 5000, "qfex_fee_share_percent": 50.0, "builder_fee_share_bps": 3000, "builder_fee_share_percent": 30.0, "created_at": "2026-09-01T12:00:00Z", "updated_at": "2026-09-01T12:00:00Z" } ``` ## View your builder code Use `GET /user/builder-code` to retrieve the authenticated user's active builder code and configured fee shares. If the user has not created an active code, the API returns **404**. ```http theme={null} GET /user/builder-code x-qfex-public-key: qfex_pub_xxxxx x-qfex-hmac-signature: 5f2e... x-qfex-nonce: c0ffee... x-qfex-timestamp: 1760545414 ``` Store the returned `builder_code` UUID in your integration and attach it when authenticating attributed Trade WebSocket connections. Do not put it on individual order messages. ## Use a builder code on the Trade WebSocket The builder code belongs on the Trade WebSocket **auth message**. It applies to the entire connection. ### HMAC authentication Connect with your API public key: ```text theme={null} wss://trade.qfex.com?api_key=YOUR_PUBLIC_KEY ``` Then authenticate within one minute and add `builder_code` next to the HMAC block: ```json theme={null} { "type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." }, "builder_code": "11111111-1111-4111-8111-111111111111" } } ``` ### JWT or OAuth authentication When authenticating with a QFEX user JWT, including a token obtained through QFEX OAuth, connect with the JWT as the query parameter: ```text theme={null} wss://trade.qfex.com/?jwt=URL_ENCODED_JWT ``` Then send the same JWT and your builder code in the auth message: ```json theme={null} { "type": "auth", "params": { "jwt": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...", "builder_code": "11111111-1111-4111-8111-111111111111" } } ``` You can include optional `account_id` on the same auth payload if the session should trade on a subaccount. Browser WebSocket APIs cannot set custom handshake headers, so `params.builder_code` is the recommended way to attach the code. Native clients may send `x-qfex-builder-code` on the WebSocket upgrade instead; if the auth message also includes `builder_code`, the auth message wins. The code applies for the life of that connection. Reconnect and send auth again to change or drop it. Orders, stop orders, and TWAPs placed on that connection inherit the builder code automatically. If the code is missing, empty, inactive, or unknown, no builder rewards are attributed. See [Authenticate](/websocket/channels/trade/authenticate) for HMAC signing and full client samples, or [Builder Integration Guide](/api-reference/builder-integration) for the end-to-end OAuth flow. ## Builder rewards Credited builder share is exposed as `builder_rewards` on the builder-code owner's public user balance. * Trade WebSocket [balances](/websocket/channels/trade/balance) channel * REST `GET /user/positions` balance object The trader's balance does not show the builder's share. # Builder Integration Guide Source: https://docs.qfex.com/api-reference/builder-integration Choose OAuth or wallet-signed trading keys to onboard users and attribute their trading to your builder code This guide is for exchanges, DeFi aggregators, trading terminals, agents, and other applications that want to let their users trade on QFEX while earning builder rewards. ## Choose an authentication flow | Flow | Credential your integration receives | Guide | | - | - | - | | Wallet-signed builder registration | A main-account API key for trading, reads, and registered-address crypto withdrawals | [Builder wallet registration](/api-reference/builder-wallet-registration) | | OAuth authorization | The user's access and refresh tokens | Follow the OAuth flow below | Wallet-signed registration uses your existing builder API key with `register_user`. The user signs a QFEX-generated message and QFEX creates or resumes their wallet account before returning the trading key. It does not require an OAuth client. Both flows require your builder code on each Trade WebSocket connection for reward attribution. ## OAuth integration The OAuth integration has two independent pieces: 1. **OAuth identifies and authorises the QFEX user.** Each end user signs in to QFEX and authorises your application. New users create their QFEX account on QFEX rather than sharing QFEX credentials with your application. 2. **Your builder code identifies your integration.** Your application attaches the same builder code to each authenticated Trade WebSocket session it opens for those users. > [!NOTE] > > A builder code is **not assigned permanently to an end-user account**. It is > attached to a Trade WebSocket connection. Every eligible order sent over that > connection is attributed to the builder code for the life of the connection. ## Integration model A typical aggregator has: * one QFEX account owned by the aggregator; * one active builder code owned by that account; * one registered QFEX OAuth client for the aggregator application; and * one OAuth grant and token set for each QFEX user who connects their account. You do **not** create a new builder code for every trader. For example, if an aggregator has builder code `11111111-1111-4111-8111-111111111111`, it uses that same code when authenticating the QFEX Trade WebSocket for Alice, Bob, and every other user who trades through the aggregator. Each user still authenticates with their own QFEX OAuth access token. ## Before you start ### 1. Create your builder code Sign in to QFEX and create a builder code in **Developer Settings → Builder code**. You can also create it with `POST /user/builder-code`. Store the returned UUID as application configuration, for example: ```bash theme={null} QFEX_BUILDER_CODE=11111111-1111-4111-8111-111111111111 ``` The builder code is an identifier, not a user credential. Your QFEX API secrets and OAuth tokens are sensitive and must be stored separately. See [Builder Codes API](/api-reference/builder-codes) for fee-share configuration and builder-code lifecycle operations. ### 2. Register an OAuth client Contact [support@qfex.com](mailto:support@qfex.com) to register your integration as a QFEX OAuth client. Provide: * your application name; * your production redirect URI or URIs; * a logo URL, if available; and * whether the client is **public** or **confidential**. Use a **confidential** client when your application has a backend that can keep a client secret. Browser-only, mobile, and desktop applications must use a **public** client and must never embed a client secret. QFEX OAuth uses the OAuth 2.1 authorization-code flow with PKCE. | Endpoint | URL | | - | - | | Authorize | `https://verify.qfex.com/auth/v1/oauth/authorize` | | Token | `https://verify.qfex.com/auth/v1/oauth/token` | | Trade WebSocket | `wss://trade.qfex.com` | > [!WARNING] > > A QFEX OAuth access token is a user session credential and can authorise > trading as that user. Treat access and refresh tokens as secrets. Do not log > them, put them in analytics events, or expose a confidential client's tokens > to the browser. ## End-user flow ### Step 1: User selects Connect QFEX When the user selects **Connect QFEX** in your application: 1. Generate a high-entropy `state` value. 2. Generate a PKCE `code_verifier`. 3. Derive the SHA-256 `code_challenge` from the verifier. 4. Store `state` and `code_verifier` in the user's server-side session or another secure short-lived store. 5. Redirect the user to the QFEX authorization endpoint. Example in Node.js: ```javascript theme={null} import crypto from "node:crypto"; const QFEX_AUTHORIZE_URL = "https://verify.qfex.com/auth/v1/oauth/authorize"; function randomBase64Url(bytes = 32) { return crypto.randomBytes(bytes).toString("base64url"); } function createPkceChallenge(verifier) { return crypto.createHash("sha256").update(verifier).digest("base64url"); } const state = randomBase64Url(); const codeVerifier = randomBase64Url(48); const codeChallenge = createPkceChallenge(codeVerifier); // Persist these against the user's login/session until the callback completes. session.qfexOAuth = { state, codeVerifier }; const url = new URL(QFEX_AUTHORIZE_URL); url.searchParams.set("response_type", "code"); url.searchParams.set("client_id", process.env.QFEX_OAUTH_CLIENT_ID); url.searchParams.set("redirect_uri", "https://aggregator.example.com/oauth/qfex/callback"); url.searchParams.set("scope", "openid email profile"); url.searchParams.set("state", state); url.searchParams.set("code_challenge", codeChallenge); url.searchParams.set("code_challenge_method", "S256"); response.redirect(url.toString()); ``` Never omit `state` or PKCE. ### Step 2: QFEX handles sign-in or account creation The user is sent to QFEX, not to a credential form owned by your application. * If the user already has a QFEX account, they sign in and complete any required MFA. * If the user does not have an account, they create one with QFEX and complete the required QFEX onboarding steps. * The user reviews the OAuth authorisation request and approves or denies your application. Your application should **not** collect the user's QFEX password, MFA code, API secret, or service-role credentials. It should also not create users directly through the underlying authentication provider. Keeping account creation on QFEX ensures that QFEX controls account verification, terms, eligibility, and onboarding. > [!WARNING] > > During the current pre-release flow, OAuth continuation through brand-new > account signup and onboarding is still being completed. Existing QFEX users > can complete the OAuth round-trip end-to-end. A user who creates a new QFEX > account may need to finish onboarding, return to your application, and select > **Connect QFEX** again. This extra step is intended to be removed before the > builder integration is generally available. ### Step 3: Handle the OAuth callback After approval, QFEX redirects to the registered `redirect_uri` with an authorization `code` and the original `state`. ```text theme={null} https://aggregator.example.com/oauth/qfex/callback?code=...&state=... ``` On your callback endpoint: 1. Reject the request if `state` does not exactly match the value stored in the user's session. 2. Read the stored PKCE `code_verifier`. 3. Exchange the authorization code for tokens. 4. Delete the one-time `state` and `code_verifier` from the session. ### Step 4: Exchange the code for QFEX tokens For a confidential server-side client, authenticate the client with HTTP Basic auth and send the PKCE verifier: ```bash theme={null} curl -X POST 'https://verify.qfex.com/auth/v1/oauth/token' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -u "$QFEX_OAUTH_CLIENT_ID:$QFEX_OAUTH_CLIENT_SECRET" \ --data-urlencode 'grant_type=authorization_code' \ --data-urlencode 'code=AUTHORIZATION_CODE' \ --data-urlencode 'redirect_uri=https://aggregator.example.com/oauth/qfex/callback' \ --data-urlencode 'code_verifier=PKCE_CODE_VERIFIER' ``` A successful response contains an access token and refresh token. Store both securely against the connected user. For a public client, omit the client secret and include `client_id` in the form body instead. Public clients still use PKCE. ### Step 5: Open the Trade WebSocket and attach your builder code Use the user's OAuth `access_token` as the JWT. The WebSocket upgrade and the QFEX auth message both use the user's token; your builder code is added to the auth message. ```javascript theme={null} import WebSocket from "ws"; const accessToken = user.qfexAccessToken; const builderCode = process.env.QFEX_BUILDER_CODE; const ws = new WebSocket( `wss://trade.qfex.com/?jwt=${encodeURIComponent(accessToken)}`, ); ws.on("open", () => { ws.send( JSON.stringify({ type: "auth", params: { jwt: accessToken, builder_code: builderCode, }, }), ); }); ``` If the user selected a QFEX subaccount, include its ID on the same auth message: ```json theme={null} { "type": "auth", "params": { "jwt": "USER_OAUTH_ACCESS_TOKEN", "account_id": "22222222-2222-4222-8222-222222222222", "builder_code": "11111111-1111-4111-8111-111111111111" } } ``` The builder code is now fixed for the lifetime of that WebSocket connection. Do not add it to each individual order. ### Step 6: Trade normally After the auth response succeeds, place orders using the normal Trade WebSocket API. ```json theme={null} { "type": "auth", "result": "success" } ``` Orders, stop orders, and TWAPs submitted over the attributed connection inherit your builder code automatically. Builder rewards are credited to the QFEX account that owns the builder code. OAuth does not bypass QFEX account requirements. If the account still needs identity verification, updated terms acceptance, or another required onboarding step, trading can be rejected until the user completes it on QFEX. See: * [Authenticate](/websocket/channels/trade/authenticate) * [Add Order](/websocket/channels/trade/add_order) * [Builder Codes API](/api-reference/builder-codes) ### Step 7: Refresh tokens and reconnect OAuth access tokens expire. Use the refresh token to obtain a new access token before expiry or after an authentication failure. For a confidential client: ```bash theme={null} curl -X POST 'https://verify.qfex.com/auth/v1/oauth/token' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -u "$QFEX_OAUTH_CLIENT_ID:$QFEX_OAUTH_CLIENT_SECRET" \ --data-urlencode 'grant_type=refresh_token' \ --data-urlencode 'refresh_token=USER_REFRESH_TOKEN' ``` When you create a new Trade WebSocket with the refreshed access token, attach your builder code again in the new auth message. If the user revokes your application in QFEX, stop trading for that user and send them through the OAuth flow again if they choose to reconnect. ## Existing users and new users use the same entry point Your integration should expose one **Connect QFEX** action. Do not build separate "sign up" and "sign in" APIs on your side. | User state | What happens | | - | - | | No QFEX account | QFEX handles account creation and onboarding. During pre-release, the user may need to return to your application and select **Connect QFEX** again to complete OAuth. | | Existing QFEX account | QFEX asks the user to sign in if necessary, then authorise your app. | | Already signed in to QFEX | The user can go directly to the authorisation screen. | | Existing valid grant | QFEX may complete the authorisation flow without asking for credentials again. | | Grant revoked or refresh token invalid | Start a fresh authorization-code flow. | ## Builder attribution is connection-scoped Builder attribution is intentionally independent of account creation and OAuth. This means: * the end user's QFEX account is not permanently tied to a builder; * the same user can trade through different builders at different times; * a user trading directly on QFEX is not automatically attributed to your builder; * every Trade WebSocket your integration opens must include your `builder_code`; and * reconnecting without the code removes builder attribution from that new connection. This also means you should set `builder_code` from trusted application configuration rather than accepting an arbitrary value from the browser or end user. ## Security checklist Before going live: * use Authorization Code + PKCE for every OAuth flow; * validate `state` on every callback; * register exact HTTPS redirect URIs and avoid wildcard callbacks; * keep confidential-client secrets, access tokens, and refresh tokens server-side; * encrypt refresh tokens at rest; * never log access tokens, refresh tokens, authorization codes, or QFEX API secrets; * do not ask users for QFEX passwords or MFA codes inside your application; * reconnect the Trade WebSocket with the new access token after a refresh; * attach your configured builder code on every new Trade WebSocket; and * stop trading immediately if authorization is revoked. > [!NOTE] > > The standard OAuth scopes describe identity information returned by the OAuth > provider. They should not be treated as a read-only or trade-only permission > boundary for QFEX trading. An access token used with QFEX trading APIs must be > protected as a credential capable of acting as the user. ## End-to-end checklist A production integration is complete when you can test all of the following: 1. A user with no QFEX account can enter the QFEX account-creation flow from **Connect QFEX** and, once new-user OAuth continuation is released, return to the original authorisation request after onboarding. 2. An existing QFEX user can connect without sharing credentials with your application. 3. Your callback rejects an invalid `state` or PKCE verifier. 4. You can exchange the authorization code and securely store the returned token set. 5. You can authenticate `wss://trade.qfex.com` with the user's access token. 6. The Trade WebSocket auth message includes your builder UUID automatically. 7. A normal order, stop order, and TWAP placed through that connection are attributed to your builder. 8. Token refresh followed by WebSocket reconnection preserves builder attribution. 9. Revoking the connected application prevents continued use of the revoked authorization. If you need an OAuth client registered for an integration, contact [support@qfex.com](mailto:support@qfex.com). # Builder wallet registration Source: https://docs.qfex.com/api-reference/builder-wallet-registration Use a wallet signature to create a main-account trading key for a builder's users. Builders can onboard an Ethereum wallet user and receive a main-account QFEX API key for trading, reads, and registered-address crypto withdrawals with two endpoints: | Endpoint | Authentication | Result | | - | - | - | | `GET /builder/web3/message` | Public | The wallet message to sign | | `POST /builder/web3/api-key` | Builder API key with `register_user` | The user's trading key and account identifiers | The examples use the pre-production REST API at `https://api.qfex.io` and Trade WebSocket at `wss://trade.qfex.io`. Use the environment URLs and credentials supplied by QFEX for your integration. This flow returns API keys. [Wallet session authentication](/api-reference/wallet-authentication) returns access and refresh tokens; [OAuth](/api-reference/builder-integration) uses an application authorization grant. ## Before you start Your builder needs: * A QFEX account with an active [builder code](/api-reference/builder-codes). * A username on that account, used in the generated key's label. * An existing API public key and secret owned by the builder-code account. * The `register_user` permission enabled on that builder API key. Contact [support@qfex.com](mailto:support@qfex.com) to enable it; `subaccount_ops` does not grant registration access. Keep the builder secret on your backend. The browser needs the builder code and the selected wallet provider, such as Rabby discovered through EIP-6963. It sends the signed message to your backend, which authenticates the registration request to QFEX. ## 1. Fetch the wallet message Pass the user's Ethereum address and your builder-code UUID: ```sh theme={null} curl --get 'https://api.qfex.io/builder/web3/message' \ --data-urlencode 'address=0x0000000000000000000000000000000000000001' \ --data-urlencode 'builder_code=aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa' ``` The `200 OK` response contains a `message` string. For example: ```text theme={null} www.qfex.com wants you to sign in with your Ethereum account: 0x0000000000000000000000000000000000000001 Authorize QFEX to create a trading-only API key for builder aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa on my main account. URI: https://www.qfex.com Version: 1 Chain ID: 1 Nonce: 27c41a80ab6a61cf914075b1caffe3d8 Issued At: 2026-10-01T12:00:00Z Expiration Time: 2026-10-01T12:10:00Z ``` This example is illustrative. Sign the actual `message` returned by the endpoint, unchanged. The domain and URI are configured by QFEX for the environment; do not replace them with your application's URL. The endpoint generates a fresh nonce and a ten-minute validity window. Fetch a new message if signing or submission takes too long. Returning a message does not establish that a builder exists or is active; those checks happen during registration. ## 2. Ask the wallet to sign Use the EIP-1193 provider selected by the user. Rabby supports this interface. When multiple wallets are installed, use EIP-6963 discovery rather than assuming `window.ethereum` is the selected wallet. ```javascript theme={null} export async function signBuilderMessage({ provider, builderCode }) { const accounts = await provider.request({ method: "eth_requestAccounts" }); const address = accounts?.[0]; if (!address) throw new Error("The wallet did not return an address"); const chainId = await provider.request({ method: "eth_chainId" }); if (Number.parseInt(chainId, 16) !== 1) { await provider.request({ method: "wallet_switchEthereumChain", params: [{ chainId: "0x1" }], }); } const url = new URL("https://api.qfex.io/builder/web3/message"); url.searchParams.set("address", address); url.searchParams.set("builder_code", builderCode); const response = await fetch(url, { cache: "no-store", credentials: "omit" }); const result = await response.json(); if (!response.ok) { throw new Error(result.detail ?? "Could not fetch the wallet message"); } const message = result.message; const hexMessage = "0x" + Array.from( new TextEncoder().encode(message), (byte) => byte.toString(16).padStart(2, "0"), ).join(""); const signature = await provider.request({ method: "personal_sign", params: [hexMessage, address], }); return { message, signature }; } ``` Show the returned message to the user before they authorize it. Signing proves wallet ownership and authorizes trading-key creation; it does not submit an on-chain transaction or require gas. Send the returned `{ message, signature }` to your backend. Do not add the words `Wallet signature` or the signature itself to the message string. ## 3. Register the user and create the trading key Your backend calls `POST /builder/web3/api-key` using the builder's credentials and four HMAC headers: | Header | Value | | - | - | | `x-qfex-public-key` | Builder's existing API public key | | `x-qfex-nonce` | Fresh cryptographically random hex nonce for this HTTP request | | `x-qfex-timestamp` | Current Unix time in seconds | | `x-qfex-hmac-signature` | Hex-encoded HMAC-SHA256 of `nonce:timestamp`, using the builder's secret | The HTTP nonce is separate from the nonce inside the wallet message. Generate new HMAC headers for each request or retry. Example for a Node.js backend: ```javascript theme={null} import crypto from "node:crypto"; export async function registerWalletUser({ message, signature }) { const publicKey = process.env.QFEX_BUILDER_PUBLIC_KEY; const secretKey = process.env.QFEX_BUILDER_SECRET_KEY; if (!publicKey || !secretKey) { throw new Error("Builder credentials are not configured"); } const nonce = crypto.randomBytes(16).toString("hex"); const timestamp = Math.floor(Date.now() / 1000).toString(); const hmacSignature = crypto.createHmac("sha256", secretKey) .update(`${nonce}:${timestamp}`) .digest("hex"); const response = await fetch("https://api.qfex.io/builder/web3/api-key", { method: "POST", headers: { "Content-Type": "application/json", "x-qfex-public-key": publicKey, "x-qfex-nonce": nonce, "x-qfex-timestamp": timestamp, "x-qfex-hmac-signature": hmacSignature, }, body: JSON.stringify({ message, signature }), cache: "no-store", }); const result = await response.json(); if (!response.ok) { throw new Error(result.detail ?? "Could not create the trading key"); } return result; } ``` The body contains only `message` and `signature`. QFEX resolves the builder code from the authenticated builder account and requires the signed message to authorize that exact code. Use the builder's own API key for this request, rather than an end user's trading key. The wallet signature must match the address in the message. The message must have been issued within ten minutes, must not be expired or issued in the future, and must use a domain and URI accepted by QFEX Auth. A previously unseen wallet creates a QFEX user and main account through wallet signup. An existing wallet resumes its existing user. Account creation must complete before the key can be issued. ## Response and key permissions Successful creation returns `201 Created` with `Cache-Control: no-store`: ```json theme={null} { "user_id": "11111111-1111-4111-8111-111111111111", "account_id": "11111111-1111-4111-8111-111111111111", "public_key": "qfex_pub_EXAMPLE", "secret_key": "qfex_secret_EXAMPLE" } ``` | Property | Meaning | | - | - | | `user_id` | QFEX user associated with the signing wallet | | `account_id` | That user's main account; equal to `user_id` | | `public_key` | The end user's new API public key | | `secret_key` | The end user's new API secret, returned at creation | The key grants order execution and read access to orders, positions, and balances on the main account. Its `view_balance` permission also allows `GET /address` and `GET /withdrawal-addresses` on the funding service. Keys issued or replaced through `POST /builder/web3/api-key` also allow `POST /withdraw` to an address registered by the user on the requested network. This access is not selectable in Developer Settings and requires owner access to the selected account. `deposit_withdraw` remains disabled: this key cannot rotate deposit addresses, manage bank accounts, or withdraw USD. Subaccounts are outside its scope. Access and refresh tokens are not returned by this endpoint. See [programmatic deposits and withdrawals](/api-reference/programmatic-funding) for the funding URLs, authentication headers, and request examples. Store the response credentials securely and avoid logging or caching them. They authenticate subsequent trading requests as the wallet user; they do not replace the builder's registration credentials. ## Replacement and trading attribution The key is labelled `builder-` followed by the builder account's username, for example `builder-alice`. Issuing another key for the same user and builder label deletes the previous key and its stored secret. A failed key replacement rolls back and preserves the previous key. Keys for other users and other builder labels remain unaffected. There is no automatic revocation immediately after use. A successful replacement invalidates the previous key for subsequent authentication. Keys whose labels start with `builder-` or `managed` are excluded from the user's normal API-key listing. Use the returned user key with [HMAC Trade WebSocket authentication](/websocket/channels/trade/authenticate), and include your builder-code UUID in `params.builder_code`. The key label alone does not attribute orders to the builder. Attach the code again whenever you reconnect. ## Errors | Status | Meaning and next step | | - | - | | `400` | Invalid address, builder UUID, or wallet message. Fetch a fresh message for the correct wallet and authenticated builder code. | | `401` | Builder authentication or wallet authentication failed. Check HMAC credentials, the wallet signature, and message validity. The generic wallet error can also hide a signup failure; contact QFEX support if a fresh signed message still fails. | | `403` | The builder key lacks `register_user`, or its account has no active builder code. | | `422` | The request does not match the required query parameters or JSON schema. | | `502` | QFEX could not load the builder or issue the key. The builder username and user's main account must exist; contact QFEX support if the error persists. | For direct browser calls, the API must allow your origin and request headers through CORS. The backend handles the underlying Auth exchange; this builder flow does not require your frontend to call `/auth/v1/token?grant_type=web3` or hold an Auth client key. # CLI Source: https://docs.qfex.com/api-reference/cli JSON-native command-line access to QFEX market data, trading, and account state. The [QFEX CLI](https://github.com/QFEX-org/cli) provides JSON-only command-line access to QFEX for both human operators and AI agents. A background daemon manages the underlying WebSocket connections to QFEX, while CLI commands remain stateless from the caller's perspective. For a higher-level guide to using the CLI with AI agents, see [Agentic Trading](/qfex/agentic-trading). ## Installation ```sh theme={null} brew install QFEX-org/tap/qfex ``` ## Quick Start ```sh theme={null} # Start the background daemon qfex daemon start # Query market data (no credentials required) qfex market bbo AAPL-USD # Log in to trade or access account data qfex login qfex daemon restart # Place an order qfex order place --symbol AAPL-USD --side BUY --type LIMIT --tif GTC --qty 1 --price 200 # Stop the daemon when finished qfex daemon stop ``` ## Why Use The CLI * All command output is JSON, which makes it easy to pipe into `jq` or parse in code. * The daemon keeps persistent connections to market data and trade WebSockets, so scripts do not need to manage subscriptions, authentication, or socket lifecycle directly. * Market data commands work without credentials. * Trading commands use the API keys described in [API Keys](/api-reference/api-keys). ## Common Commands ```sh theme={null} # Market data qfex market bbo AAPL-USD qfex market orderbook AAPL-USD --depth 5 qfex market trades AAPL-USD --limit 50 # Orders qfex order place --symbol AAPL-USD --side BUY --type MARKET --tif IOC --qty 1 qfex order list qfex order cancel --symbol AAPL-USD --order-id # Positions and account state qfex position list qfex account balance qfex account leverage get # Live streams qfex watch bbo AAPL-USD qfex watch orders qfex watch fills ``` ## Environments The CLI supports both QFEX production and UAT environments: * `prod`: Connects to `qfex.com` with real funds. * `uat`: Connects to `qfex.io` for testing strategies and integrations without real funds. You can select the environment during `qfex login` or by editing `~/.config/qfex/config.yaml`, then restarting the daemon. ## Subaccounts If your account has subaccounts, `qfex login` will prompt you to choose which account should be active by default. The selected value is stored in `~/.config/qfex/config.yaml` as `selected_subaccount`. Use these commands to inspect or change it later: ```sh theme={null} qfex account subaccounts current qfex account subaccounts select qfex account subaccounts list ``` Selecting a different subaccount updates the config and restarts the daemon so authenticated trading and account requests use the **new account** context. You can also transfer money between subaccounts using: ```sh theme={null} qfex account subaccounts transfer ``` This command accepts `--from`, `--to`, and `--amount` (in USD). You can set either `--from` or `--to` to `"primary"` to refer to your main account. The other account should be a UUID returned by `qfex account subaccounts list`. ## Links * [Agentic Trading](/qfex/agentic-trading) * [QFEX CLI repository](https://github.com/QFEX-org/cli) * [API key setup instructions](/api-reference/api-keys) * [WebSocket overview](/websocket/main) # Enums Source: https://docs.qfex.com/api-reference/enums ## OrderType This defines how the order is executed by the engine. The WebSocket API uses the string names below; the numeric values are the corresponding internal values. | Name | Value | Accepted by `add_order` | Description | | - | - | - | - | | `LIMIT` | 0 | Yes | Limit order. | | `MARKET` | 1 | Yes | Market order. | | `ALO` | 3 | Yes | Add Liquidity Only order. If it would cross the book, the order is rejected. | | `TAKE_PROFIT` | 4 | Yes | Take-profit stop order. | | `STOP_LOSS` | 5 | Yes | Stop-loss stop order. | | `STOP_MARKET` | 6 | No | Market order generated when a stop order triggers; it may appear in order, fill, and trade responses. | | `TWAP_LIMIT` | 7 | No | System-generated child order for a TWAP; it may appear in order, fill, and trade responses. | Internal value `2` is reserved for liquidation orders and is not accepted as a client-facing `OrderType`. ## OrderDirection This defines the direction of the order. | Name | Value | Description | | - | - | - | | `BUY` | 0 | Buy order | | `SELL` | 1 | Sell order | ## OrderTimeInForce This defines the time in force of the order. | Name | Description | | - | - | | `GTC` | Good till canceled. This order will stay in the book until it is filled or canceled. | | `IOC` | Immediate or cancel. This order will fill as much as possible and then be canceled. If the order cannot be filled immediately, the remaining quantity will be canceled. | | `FOK` | Fill or kill. This order will be canceled if it cannot be filled immediately. If the order cannot be filled immediately, the entire order will be canceled. | ## OrderStatus This is returned on any order response. This will indicate the current status of the order. | Name | Description | | - | - | | `ACK` | Order accepted. | | `IOC_CANCELLED` | The unfilled portion of an immediate-or-cancel order was cancelled. | | `INVALID_PRICE` | Invalid price. | | `INVALID_QUANTITY` | Invalid quantity. | | `NOT_FOUND` | Requested order was not found. | | `IOC_PARTIALLY_FILLED` | Immediate-or-cancel order was partially filled and the remainder cancelled. | | `USER_NOT_FOUND` | User was not found. | | `PERMISSION_DENIED` | User is not permitted to perform this action. | | `FILLED` | Order filled, this could be partially filled. | | `MODIFIED` | Order successfully modified. | | `CANCELLED` | Order successfully cancelled. | | `CANCELLED_STP` | Order cancelled by self trade prevention. | | `REJECTED` | Order rejected | | `NO_SUCH_ORDER` | Cancel rejected, no such order exists. | | `INVALID_ORDER_TYPE` | Invalid order type. | | `BAD_SYMBOL` | Invalid symbol. | | `PRICE_LESS_THAN_MIN_PRICE` | Price less than minimum price for this symbol. | | `PRICE_GREATER_THAN_MAX_PRICE` | Price greater than maximum price for this symbol. | | `CANNOT_MODIFY_NO_SUCH_ORDER` | Failed to modify this order. Order does not exist. | | `CANNOT_MODIFY_PARTIAL_FILL` | Cannot modify partially filled order. Please cancel your current order and place a new order. | | `CANNOT_MODIFY_ALO_WOULD_CROSS` | Cannot modify an ALO order which would cause it cross the market. | | `FAILED_MARGIN_CHECK` | Failed margin check. Please check your available balance. | | `REJECTED_MUST_CHANGE_VALUE_MODIFY` | Modify request did not change a required value. | | `NO_CHANGE_NO_UPDATE` | No values changed, so the order was not updated. | | `INVALID_TICK_SIZE_PRECISION_PRICE` | Invalid tick size for this symbol. Check the price tick size in the reference data. | | `INVALID_TICK_SIZE_PRECISION_QUANTITY` | Invalid lot size for this symbol. Check the lot size in the reference data. | | `QUANTITY_LESS_THAN_MIN_QUANTITY` | Quantity less than minimum for this symbol. Check the minimum quantity in the reference data. | | `QUANTITY_GREATER_THAN_MAX_QUANTITY` | Quantity greater than maximum for this symbol. Check the maximum quantity in the reference data. | | `REJECTED_GREATER_THAN_MAX_PRICE_BAND` | Price is above the permitted price band. | | `REJECTED_LESS_THAN_MIN_PRICE_BAND` | Price is below the permitted price band. | | `INVALID_TIME_IN_FORCE` | Invalid time in force for this order type. | | `REJECTED_WOULD_BREACH_MAX_POSITION` | Order would breach the maximum position limit. | | `REJECTED_WOULD_BREACH_MAX_NOTIONAL` | Would breach max notional limits. | | `REJECTED_MARKET_CLOSED` | QFEX is currently not accepting orders for this symbol. | | `USER_IN_LIQUIDATION` | User is currently being liquidated. | | `REJECTED_FAILED_TO_PROCESS` | Failed to process order. | | `INVALID_TAKE_PROFIT_PRICE` | Invalid take-profit price. | | `INVALID_STOP_LOSS_PRICE` | Invalid stop-loss price. | | `INVALID_CLIENT_ORDER_ID` | Client order ID is invalid. | | `RATE_LIMITED` | You have sent too many requests to trade.qfex.com. Please contact [support@qfex.com](mailto:support@qfex.com) to raise your limits. | | `REJECTED_TOO_MANY_OPEN_ORDERS` | You have reached the max number of open orders. | | `REJECTED_OPEN_INTEREST_LIMIT` | You have reached the max Open Interest limit for a given symbol. | | `REJECTED_TOTAL_OPEN_INTEREST_LIMIT` | Order would breach the total open-interest limit. | | `REJECTED_LESS_THAN_MIN_NOTIONAL` | Order notional is below the permitted minimum. | ## SymbolStatus | Name | Description | | - | - | | `ACTIVE` | Symbol is active. | | `INACTIVE` | Symbol is temporarily suspended. | | `DELISTED` | Symbol is permanently suspended. | ## CandlesInterval Use the wire value when subscribing to the WebSocket candle channel. The symbolic name is the corresponding internal enum name. | Wire value | Symbolic name | Description | | - | - | - | | `1MIN` | `ONE_MINUTE` | 1 minute candle. | | `5MINS` | `FIVE_MINUTES` | 5 minute candle. | | `15MINS` | `FIFTEEN_MINUTES` | 15 minute candle. | | `30MINS` | `THIRTY_MINUTES` | 30 minute candle. | | `1HOUR` | `ONE_HOUR` | 1 hour candle. | | `4HOURS` | `FOUR_HOURS` | 4 hour candle. | | `1DAY` | `ONE_DAY` | 1 day candle. | ## ExecutionType | Name | Description | | - | - | | `NEW` | User placed order. | | `LIQUIDATION` | Forced liquidation by QFEX. | # Get a crypto deposit address Source: https://docs.qfex.com/api-reference/funding/funding/get-a-crypto-deposit-address /api-reference/funding-openapi.yaml get /address API keys require view_balance. Returns the existing deposit address or provisions one on the first call without rotating an existing address. ARBITRUM_ONE deposits USDC; TRON deposits USDT. POST /address/rotate requires deposit_withdraw. # List registered crypto withdrawal addresses Source: https://docs.qfex.com/api-reference/funding/funding/list-registered-crypto-withdrawal-addresses /api-reference/funding-openapi.yaml get /withdrawal-addresses API keys require view_balance. Returns all crypto withdrawal addresses registered by the authenticated user across networks. The user is resolved from authentication; no user-ID query parameter is used. This endpoint does not register, change, or delete addresses. # Request a crypto withdrawal to a registered address Source: https://docs.qfex.com/api-reference/funding/funding/request-a-crypto-withdrawal-to-a-registered-address /api-reference/funding-openapi.yaml post /withdraw Standard API keys require deposit_withdraw. Keys issued or replaced through POST /builder/web3/api-key also support crypto withdrawals to registered destinations when the user owns the selected account. This builder access is not selectable in Developer Settings. Every withdrawal, including all API keys and JWT sessions, must use a destination registered by the authenticated user on the requested network. Addresses are checked before debiting funds; Arbitrum matching is case-insensitive and TRON matching is exact. The user's primary account is debited. Requests are not idempotent; reconcile withdrawal history after a timeout before retrying. # Introduction Source: https://docs.qfex.com/api-reference/introduction QFEX provides a unified interface for trading, market data, and account operations via: * **REST API**: Account data, account management, and [programmatic deposits and withdrawals](/api-reference/programmatic-funding). * **CLI**: JSON-only command-line access for human operators and AI agents. * **WebSocket**: Low latency, real-time market data and order entry. If you want to integrate QFEX into an agentic trading workflow without managing WebSocket connections directly, use the [QFEX CLI](/api-reference/cli). ## Authentication Private account and trading requests require authentication. Use [API keys](/api-reference/api-keys) for long-running server integrations, or [wallet authentication](/api-reference/wallet-authentication) to create a user session by signing an Ethereum SIWE message. # Programmatic deposits and withdrawals Source: https://docs.qfex.com/api-reference/programmatic-funding Fund a QFEX account and request crypto or USD withdrawals with the REST API. QFEX exposes funding endpoints on a separate REST service: | Environment | Base URL | | - | - | | Production | `https://banker.qfex.com` | | UAT | `https://banker.qfex.io` | The API supports: * USDC deposits and withdrawals on Arbitrum One (`ARBITRUM_ONE`) * USDT deposits and withdrawals on TRON (`TRON`) * USD deposits by ACH push, wire, or FedNow * USD withdrawals by ACH or wire > [!WARNING] > > Always send the asset returned by the address endpoint on the exact network > returned by that endpoint. Funds sent with a different asset or network may > be lost. ## Before you start Complete account verification and accept the QFEX terms before using the funding API. You can authenticate with either a wallet session or an API key. For a wallet session, complete [wallet authentication](/api-reference/wallet-authentication) and send the returned access token on every request: ```text theme={null} Authorization: Bearer ``` For an API key created in Developer Settings, use the permissions required by each endpoint: | Endpoint | Required API-key permission | | - | - | | `GET /address` | `view_balance` | | `GET /withdrawal-addresses` | `view_balance` | | `POST /withdraw` | `deposit_withdraw` | | `POST /address/rotate` | `deposit_withdraw` | | USD funding and bank-account endpoints | `deposit_withdraw` | [Create a key](/api-reference/api-keys) with `view_balance` to read deposit and registered withdrawal addresses. The `deposit_withdraw` permission requires All accounts access. Funding applies to the primary account. To fund a subaccount, fund the primary account first and then [transfer funds to the subaccount](/api-reference/subaccounts-api#transfer-between-primary-account-and-subaccounts). ## Authenticate with an API key Every request requires these headers: ```text theme={null} x-qfex-public-key: x-qfex-hmac-signature: x-qfex-nonce: x-qfex-timestamp: ``` For each request: 1. Generate a cryptographically secure, hex-encoded nonce of at most 100 characters. 2. Read the current Unix time in seconds. 3. Sign the UTF-8 string `${nonce}:${timestamp}` with HMAC-SHA256, using the secret API key as the key. 4. Hex-encode the signature and send all four headers. The timestamp must be within five minutes of QFEX server time. Never reuse a nonce. The HTTP method, path, query, and request body are not part of the signed string. The following helper creates a fresh signature for every request and returns the decoded JSON response: ```python Python theme={null} import hashlib import hmac import json import os import secrets import time from urllib.error import HTTPError from urllib.request import Request, urlopen BASE_URL = os.getenv("QFEX_FUNDING_URL", "https://banker.qfex.com") PUBLIC_KEY = os.environ["QFEX_PUBLIC_KEY"] SECRET_KEY = os.environ["QFEX_SECRET_KEY"] def qfex_request(path, method="GET", body=None): nonce = secrets.token_hex(16) timestamp = str(int(time.time())) message = f"{nonce}:{timestamp}".encode() signature = hmac.new( SECRET_KEY.encode(), message, hashlib.sha256 ).hexdigest() headers = { "accept": "application/json", "x-qfex-public-key": PUBLIC_KEY, "x-qfex-hmac-signature": signature, "x-qfex-nonce": nonce, "x-qfex-timestamp": timestamp, } data = None if body is not None: headers["content-type"] = "application/json" data = json.dumps(body).encode() request = Request(BASE_URL + path, data=data, headers=headers, method=method) try: with urlopen(request) as response: response_body = response.read() return json.loads(response_body) if response_body else None except HTTPError as error: detail = error.read().decode() raise RuntimeError(f"QFEX returned {error.code}: {detail}") from error ``` ```javascript Node.js theme={null} import crypto from "node:crypto"; const baseUrl = process.env.QFEX_FUNDING_URL ?? "https://banker.qfex.com"; const publicKey = process.env.QFEX_PUBLIC_KEY; const secretKey = process.env.QFEX_SECRET_KEY; if (!publicKey || !secretKey) { throw new Error("Set QFEX_PUBLIC_KEY and QFEX_SECRET_KEY"); } async function qfexRequest(path, { method = "GET", body } = {}) { const nonce = crypto.randomBytes(16).toString("hex"); const timestamp = Math.floor(Date.now() / 1000).toString(); const signature = crypto .createHmac("sha256", secretKey) .update(`${nonce}:${timestamp}`) .digest("hex"); const response = await fetch(`${baseUrl}${path}`, { method, headers: { accept: "application/json", ...(body === undefined ? {} : { "content-type": "application/json" }), "x-qfex-public-key": publicKey, "x-qfex-hmac-signature": signature, "x-qfex-nonce": nonce, "x-qfex-timestamp": timestamp, }, body: body === undefined ? undefined : JSON.stringify(body), }); const text = await response.text(); if (!response.ok) { throw new Error(`QFEX returned ${response.status}: ${text}`); } return text ? JSON.parse(text) : undefined; } ``` Keep the secret key in a server-side secret store. Do not put it in browser code, a mobile application, logs, URLs, or source control. ## Deposit crypto Request the deposit address for the network you intend to use: ```http theme={null} GET /address?network=ARBITRUM_ONE ``` ```python theme={null} deposit = qfex_request("/address?network=ARBITRUM_ONE") print(deposit["address"]) ``` Example response: ```json theme={null} { "address": "0x0123456789abcdef0123456789abcdef01234567", "network": "ARBITRUM_ONE", "asset": "USDC" } ``` For USDT on TRON, use `network=TRON`. The response will contain `"asset": "USDT"` and `"network": "TRON"`. `GET /address` requires `view_balance` for API keys. It returns the account's existing address or provisions one on the first call, without rotating an existing address. After receiving it, submit the blockchain transfer from your wallet or custody provider. Address retrieval does not initiate a transfer. > [!NOTE] > > Check the QFEX funding screen for the current minimum deposit and required > confirmation count. A deposit is credited only after it has been detected > and confirmed. ## List registered withdrawal addresses Use `GET /withdrawal-addresses` with `view_balance` to list the authenticated user's registered crypto withdrawal addresses across networks. The user is resolved from authentication; there is no user-ID query parameter. ```http theme={null} GET /withdrawal-addresses ``` ```python theme={null} registered = qfex_request("/withdrawal-addresses") print(registered["addresses"]) ``` Example `200 OK` response: ```json theme={null} { "addresses": [ { "id": 42, "withdraw_address": "0x89abcdef0123456789abcdef0123456789abcdef", "network": "ARBITRUM_ONE", "nickname": "My wallet", "created_at": "2026-10-09T10:00:00Z" } ] } ``` `nickname` can be `null`. Users with no registered addresses receive `{"addresses": []}`. This endpoint lists addresses; it does not register, change, or delete them. Register the destination in QFEX before requesting a withdrawal. ## Withdraw crypto `POST /withdraw` requires `deposit_withdraw` for API keys created in Developer Settings. Keys issued through [builder wallet registration](/api-reference/builder-wallet-registration) also support crypto withdrawals to registered destinations. `view_balance` or `execute_orders` alone cannot authorize a withdrawal. Every crypto withdrawal must use an address registered by the authenticated user on the requested network. This applies to all API keys and wallet-session JWTs. QFEX checks the destination before debiting funds and returns `403` if it is not registered for that user and network. If QFEX cannot check the registered addresses, it rejects the request without debiting funds. Arbitrum addresses match without regard to letter case; TRON addresses must match exactly. Submit the gross amount to debit, a destination from `GET /withdrawal-addresses`, and its network: ```http theme={null} POST /withdraw Content-Type: application/json { "amount": 100, "address": "0x89abcdef0123456789abcdef0123456789abcdef", "network": "ARBITRUM_ONE" } ``` ```python theme={null} qfex_request( "/withdraw", method="POST", body={ "amount": 100, "address": "0x89abcdef0123456789abcdef0123456789abcdef", "network": "ARBITRUM_ONE", }, ) ``` On success, the endpoint returns `200 OK` with an empty body. `amount` is the gross amount debited from the QFEX account. QFEX deducts the applicable withdrawal fees and sends the remainder; check **Deposit / Withdraw** in QFEX for the current fees before submitting the request. For a TRON withdrawal, send a valid TRON address and set `network` to `TRON`. Always specify the network explicitly even though the API currently defaults to `ARBITRUM_ONE` when it is omitted. > [!WARNING] > > `POST /withdraw` is not idempotent. Retrying a request can create a second > withdrawal and debit the account again. If the client times out after sending > the request, reconcile account history before deciding whether to retry. A successful response means QFEX accepted and debited the withdrawal request. It does not guarantee that the on-chain transfer is already complete; a request may still be queued for submission or manual review. ## Deposit USD Fetch the required transfer reference before initiating a bank transfer: ```http theme={null} GET /fiat-address?currency=usd&payment_rail=ach_push ``` Valid deposit rails are `ach_push`, `wire`, and `fednow`. ```python theme={null} instructions = qfex_request( "/fiat-address?currency=usd&payment_rail=ach_push" ) print(instructions["deposit_message"]) ``` Example response: ```json theme={null} { "deposit_message": "YOUR-UNIQUE-REFERENCE" } ``` The endpoint returns the account-specific reference, not the complete beneficiary bank details. Obtain the current beneficiary details from **Deposit / Withdraw** in QFEX, initiate the transfer through your bank, and include `deposit_message` exactly in the transfer's reference, message, or note field. ## Withdraw USD USD withdrawals require a linked destination bank account. Create it once and store the returned QFEX bank-account ID. ### 1. Link a bank account ```http theme={null} POST /bank-account?currency=usd Content-Type: application/json { "first_name": "Jane", "last_name": "Doe", "routing_number": "123456789", "bank_name": "Example Bank", "account_number": "000123456789", "type": "checking", "street_line_1": "100 Main Street", "street_line_2": "Apt 4", "country": "USA", "state": "NY", "city": "New York", "postal_code": "10001" } ``` `type` must be `checking` or `savings`. `country` is an ISO 3166-1 alpha-3 code, and `state` is required for US addresses. The street address must include a street number. ```python theme={null} bank_account = qfex_request( "/bank-account?currency=usd", method="POST", body={ "first_name": "Jane", "last_name": "Doe", "routing_number": "123456789", "bank_name": "Example Bank", "account_number": "000123456789", "type": "checking", "street_line_1": "100 Main Street", "country": "USA", "state": "NY", "city": "New York", "postal_code": "10001", }, ) print(bank_account["id"]) ``` The API returns `201 Created`: ```json theme={null} { "id": "11111111-1111-4111-8111-111111111111" } ``` ### 2. Request the withdrawal ```http theme={null} POST /fiat-withdraw Content-Type: application/json { "amount": 100, "currency": "usd", "payment_rail": "ach_push", "account_id": "11111111-1111-4111-8111-111111111111" } ``` `amount` must be at least 10. Use `ach_push` or `wire` as the withdrawal rail. ```python theme={null} qfex_request( "/fiat-withdraw", method="POST", body={ "amount": 100, "currency": "usd", "payment_rail": "ach_push", "account_id": bank_account["id"], }, ) ``` On success, the endpoint returns `200 OK` with an empty body. As with crypto withdrawals, acceptance is not final settlement and the request may require manual review. Fiat fees are deducted from the requested amount before settlement. To remove a linked bank account, send `DELETE /bank-account/{id}` with a fresh set of authentication headers. ## Handle errors safely | Status | Meaning | | - | - | | `400` | Invalid amount, address, network, rail, bank details, or insufficient withdrawable balance | | `401` | Missing or invalid authentication, or terms not accepted | | `403` | Required endpoint permission is missing, the account cannot use funding, or the crypto destination is not registered for the authenticated user and network | | `404` | Bank-account ID does not exist or does not belong to the user | | `5xx` | QFEX or a funding provider could not complete the operation | Most structured errors include `title`, `status`, and `detail`; validation and provider errors may also include an `errors` array. Log the HTTP status and response body, but never log API secrets or full bank-account details. # Generate a wallet user's main-account trading key Source: https://docs.qfex.com/api-reference/rest/builder/generate-a-wallet-users-main-account-trading-key https://api.qfex.com/openapi.yaml post /builder/web3/api-key Requires the builder's existing API key with register_user. The signed wallet message must authorize the caller's active builder code and be issued within ten minutes. Replaces the wallet user's previous builder-{builder-username} key by deleting the previous key. The key permits trading, account and deposit-address reads, and withdrawals only to the user's registered addresses. # Get the wallet message for a builder trading key Source: https://docs.qfex.com/api-reference/rest/builder/get-the-wallet-message-for-a-builder-trading-key https://api.qfex.com/openapi.yaml get /builder/web3/message Public endpoint. Pass address and builder_code as query parameters. Returns a SIWE message with the supplied wallet address and the supplied builder code, a random nonce, and a ten-minute validity window. Sign the returned message unchanged and submit it to /builder/web3/api-key. # Candles Source: https://docs.qfex.com/api-reference/rest/market-data/candles https://api.qfex.com/openapi.yaml get /candles/{symbol} Return OHLCV candles for a symbol over a time range. # Contracts Source: https://docs.qfex.com/api-reference/rest/market-data/contracts https://api.qfex.com/openapi.yaml get /md/contracts Return mandatory contract metadata and current market data for all symbols. # DefiLlama Metrics Source: https://docs.qfex.com/api-reference/rest/market-data/defillama-metrics https://api.qfex.com/openapi.yaml get /defillama/metrics Return QFEX aggregate volume and USD open interest metrics for DefiLlama. # Funding Historic Source: https://docs.qfex.com/api-reference/rest/market-data/funding-historic https://api.qfex.com/openapi.yaml get /funding/{symbol} Return historic funding rates for a symbol over a time range with forward filling. # Open Interest Historic Source: https://docs.qfex.com/api-reference/rest/market-data/open-interest-historic https://api.qfex.com/openapi.yaml get /open-interest/{symbol} Return historic open interest for a symbol over a time range with forward filling. # Order Book Source: https://docs.qfex.com/api-reference/rest/market-data/order-book https://api.qfex.com/openapi.yaml get /md/orderbook/{ticker_id} Return current order book depth for a ticker. # Refdata Source: https://docs.qfex.com/api-reference/rest/market-data/refdata https://api.qfex.com/openapi.yaml get /refdata Return symbol reference data. # Settlement Calendar Source: https://docs.qfex.com/api-reference/rest/market-data/settlement-calendar https://api.qfex.com/openapi.yaml get /settlement-calendar Return the settlement calendar entry for a symbol at a given time, based on active_until. # Settlement Prices Source: https://docs.qfex.com/api-reference/rest/market-data/settlement-prices https://api.qfex.com/openapi.yaml get /settlement-prices Return current and historic settlement prices and ratios. # Symbol Long Short Historic Source: https://docs.qfex.com/api-reference/rest/market-data/symbol-long-short-historic https://api.qfex.com/openapi.yaml get /symbol-long-short/{symbol} Return historic long and short user counts for a symbol over a time range. # Taker Volume Historic Source: https://docs.qfex.com/api-reference/rest/market-data/taker-volume-historic https://api.qfex.com/openapi.yaml get /taker-volume/{symbol} Return historic taker volume metrics for a symbol over a time range. # Underlier Historic Source: https://docs.qfex.com/api-reference/rest/market-data/underlier-historic https://api.qfex.com/openapi.yaml get /underlier/{symbol} Return historic underlier OHLC data for a symbol over a time range. # Hourly PnL (approx. first position per hour) Source: https://docs.qfex.com/api-reference/rest/pnl/hourly-pnl-approx-first-position-per-hour https://api.qfex.com/openapi.yaml get /pnl Return approximate first position per symbol per hour for the user, including realised/unrealised pnl, net_funding, fees, quantity, and cost. # Public Leaderboard Source: https://docs.qfex.com/api-reference/rest/public/public-leaderboard https://api.qfex.com/openapi.yaml get /public/leaderboard Return the global top 100 participating master accounts by trading PnL or cash-flow-adjusted time-weighted return. Username filtering only reduces that selected top 100. Return can be null when no positive-equity subperiod exists; positive dollar PnL and negative TWR can coexist. # Account Equity Source: https://docs.qfex.com/api-reference/rest/user/account-equity https://api.qfex.com/openapi.yaml get /user/account-equity Return total account equity over time for preset durations. # Builder Code Details Source: https://docs.qfex.com/api-reference/rest/user/builder-code-details https://api.qfex.com/openapi.yaml get /user/builder-code Return the authenticated user's most recently created active builder code and configured QFEX/builder fee shares. # Create Builder Code Source: https://docs.qfex.com/api-reference/rest/user/create-builder-code https://api.qfex.com/openapi.yaml post /user/builder-code Create the authenticated user's builder code. The code is generated server-side as a UUID and stored with active=true. Specify both QFEX and builder fee shares in basis points; they need not sum to 10000. QFEX share must be at least 5000 (50%), builder share must be >= 0, and their sum must be at most 10000. After create, reload builder-code mappings on clearing house. # Create Subaccount Source: https://docs.qfex.com/api-reference/rest/user/create-subaccount https://api.qfex.com/openapi.yaml post /user/subaccounts Allocates a new subaccount UUID for the authenticated user and returns it in the response body. # Executions Source: https://docs.qfex.com/api-reference/rest/user/executions https://api.qfex.com/openapi.yaml get /executions Return executions (funding, deposits, withdrawals, referral rewards, fee rebates) for the authenticated account, globally sorted by created_at. # Historic Orders Source: https://docs.qfex.com/api-reference/rest/user/historic-orders https://api.qfex.com/openapi.yaml get /user/historic-orders Return historic orders (filled or terminally closed) for the authenticated user. # Historic TWAPs Source: https://docs.qfex.com/api-reference/rest/user/historic-twaps https://api.qfex.com/openapi.yaml get /user/historic-twaps Return historic TWAPs (cancelled or completed) for the authenticated user. # Historic User PnL Source: https://docs.qfex.com/api-reference/rest/user/historic-user-pnl https://api.qfex.com/openapi.yaml get /user/historic-pnl Return historic user pnl change over preset durations. Supports number and percent units, and optional comma-separated symbol filters. # List Account Balances Source: https://docs.qfex.com/api-reference/rest/user/list-account-balances https://api.qfex.com/openapi.yaml get /user/subaccounts/balance Returns the authenticated user's primary account and each secondary account together with current account equity and available balance from the latest user position balance snapshot. # List Account Equities Source: https://docs.qfex.com/api-reference/rest/user/list-account-equities https://api.qfex.com/openapi.yaml get /user/subaccounts/equity Returns the authenticated user's primary account and each secondary account together with current account equity from the latest user position balance snapshot. # List Subaccounts Source: https://docs.qfex.com/api-reference/rest/user/list-subaccounts https://api.qfex.com/openapi.yaml get /user/subaccounts Returns secondary account IDs for the authenticated user (accounts except the primary row where account_id equals user_id). # Reconcile Invitation Codes (Legacy Path) Source: https://docs.qfex.com/api-reference/rest/user/reconcile-invitation-codes-legacy-path https://api.qfex.com/openapi.yaml post /user/invitation-codes/job Immediately reconciles invitation-code rewards for the authenticated user. # Redeem Invitation Code Source: https://docs.qfex.com/api-reference/rest/user/redeem-invitation-code https://api.qfex.com/openapi.yaml post /redeem-invitation-code Redeem a valid invitation code for the authenticated user, create the referral link, and enable user access. # Set User Leverage Source: https://docs.qfex.com/api-reference/rest/user/set-user-leverage https://api.qfex.com/openapi.yaml post /user/leverage Set the leverage for the authenticated user. # Stream Historic Orders CSV Source: https://docs.qfex.com/api-reference/rest/user/stream-historic-orders-csv https://api.qfex.com/openapi.yaml get /historic-orders-csv Stream a CSV of all historic orders for the authenticated user. # Stream Historic TWAPs CSV Source: https://docs.qfex.com/api-reference/rest/user/stream-historic-twaps-csv https://api.qfex.com/openapi.yaml get /historic-twaps-csv Stream a CSV of all historic TWAPs for the authenticated user. # Stream Trades CSV Source: https://docs.qfex.com/api-reference/rest/user/stream-trades-csv https://api.qfex.com/openapi.yaml get /trades-csv Stream a CSV of all trades for the authenticated user. # Transfer Balance Source: https://docs.qfex.com/api-reference/rest/user/transfer-balance https://api.qfex.com/openapi.yaml post /user/transfer Transfer balance between two of the authenticated user's accounts (parent account or subaccounts). # User Fees Source: https://docs.qfex.com/api-reference/rest/user/user-fees https://api.qfex.com/openapi.yaml get /user/fees Return the fee schedule for the authenticated user. Fees are user-level and do not change when a subaccount is selected. # User Flat Funding Source: https://docs.qfex.com/api-reference/rest/user/user-flat-funding https://api.qfex.com/openapi.yaml get /user/flat-funding Return per-symbol net funding from the most recent snapshot where the authenticated account's position was flat. # User Positions Source: https://docs.qfex.com/api-reference/rest/user/user-positions https://api.qfex.com/openapi.yaml get /user/positions Return current positions for the authenticated user. # User Referral Notional Volume Source: https://docs.qfex.com/api-reference/rest/user/user-referral-notional-volume https://api.qfex.com/openapi.yaml get /user/referrals/notional-volume Return rolling 14-day and lifetime notional volume for the authenticated user and their direct referrals. # User Referral Rewards Source: https://docs.qfex.com/api-reference/rest/user/user-referral-rewards https://api.qfex.com/openapi.yaml get /user/referral-rewards Return daily referral rewards for the authenticated user. # User Trades Source: https://docs.qfex.com/api-reference/rest/user/user-trades https://api.qfex.com/openapi.yaml get /user/trade Return trades for the authenticated user. # User Volume Source: https://docs.qfex.com/api-reference/rest/user/user-volume https://api.qfex.com/openapi.yaml get /user/volume Return daily user notional volume for one of: 1w, 1m, ytd, or all time. Supports optional comma-separated symbol filters. # Wallet authentication Source: https://docs.qfex.com/api-reference/wallet-authentication Sign up or sign in to QFEX programmatically with an Ethereum wallet and use the resulting session. QFEX supports Sign-In with Ethereum (SIWE) using the EIP-4361 standard. A user proves ownership of an EVM address by signing a human-readable message. QFEX verifies the signature and returns a user session containing an access token and refresh token. For a builder integration that needs API keys for trading, reads, and registered-address crypto withdrawals, see [Builder wallet registration](/api-reference/builder-wallet-registration). That flow uses a QFEX-generated authorization message and returns keys instead of a user session. Wallet authentication is both sign-up and sign-in: * The first valid signature from an address creates a QFEX user. * Later signatures from the same address resume that user. * Signing the message does not submit a blockchain transaction, require gas, or grant QFEX permission to transfer assets. > [!NOTE] > > QFEX currently supports Ethereum-compatible wallets for authentication. Solana > wallet authentication is not enabled. The wallet may be connected to any EVM > chain because the signature proves address ownership rather than executing a > transaction. ## Before you start Install the SIWE utilities used by the QFEX web terminal: ```sh theme={null} npm install viem@2.55.10 ``` You also need: * The QFEX Auth URL: `https://verify.qfex.com` * A QFEX publishable Auth client key * A sign-in URL registered in the QFEX Auth redirect allowlist * CORS approval for any QFEX REST service called directly from that browser origin * A Cloudflare Turnstile token when CAPTCHA protection is enabled * An EIP-1193 wallet provider, such as a provider discovered through EIP-6963, wagmi, MetaMask, or Phantom Contact [support@qfex.com](mailto:support@qfex.com) for the current publishable Auth client key and to register an integration URL. The publishable key may be included in browser code. Never expose a QFEX secret or privileged server key. SIWE validates the message domain and URI against the registered redirect URLs. Use the sign-in page's real URL when signing. Do not claim a QFEX or third-party domain that does not host the sign-in page. ## Complete the SIWE flow The following example requests the wallet address and chain, constructs the EIP-4361 message, asks the wallet to sign it, and exchanges the signed message for a QFEX session: ```javascript theme={null} import { getAddress, stringToHex } from "viem"; import { createSiweMessage, generateSiweNonce } from "viem/siwe"; const QFEX_AUTH_URL = "https://verify.qfex.com"; const QFEX_AUTH_CLIENT_KEY = "YOUR_QFEX_AUTH_CLIENT_KEY"; export async function signInToQfex({ provider, signInUrl, captchaToken, }) { const accounts = await provider.request({ method: "eth_requestAccounts" }); if (!Array.isArray(accounts) || !accounts[0]) { throw new Error("The wallet did not return an account"); } const address = getAddress(accounts[0]); const chainIdHex = await provider.request({ method: "eth_chainId" }); const chainId = Number.parseInt(chainIdHex, 16); const pageUrl = new URL(signInUrl); const message = createSiweMessage({ address, chainId, domain: pageUrl.host, issuedAt: new Date(), nonce: generateSiweNonce(), statement: "Sign in to QFEX", uri: pageUrl.href, version: "1", }); const signature = await provider.request({ method: "personal_sign", params: [stringToHex(message), address], }); const response = await fetch( `${QFEX_AUTH_URL}/auth/v1/token?grant_type=web3`, { method: "POST", headers: { "Content-Type": "application/json", apikey: QFEX_AUTH_CLIENT_KEY, Authorization: `Bearer ${QFEX_AUTH_CLIENT_KEY}`, }, body: JSON.stringify({ chain: "ethereum", message, signature, ...(captchaToken ? { gotrue_meta_security: { captcha_token: captchaToken } } : {}), }), }, ); const result = await response.json(); if (!response.ok) { throw new Error(result.msg ?? result.message ?? "Wallet authentication failed"); } return result; } ``` In a browser, call the function with the current page URL: ```javascript theme={null} const session = await signInToQfex({ provider: selectedWalletProvider, signInUrl: window.location.href, captchaToken, }); ``` The provider must implement the EIP-1193 `request` method. The complete sequence is: 1. Call `eth_requestAccounts` to request an address. 2. Call `eth_chainId` to read the current chain. 3. Build an EIP-4361 message with the address, chain ID, registered URL, current time, fresh nonce, and the exact statement `Sign in to QFEX`. 4. Call `personal_sign` in the wallet. 5. Send the message and signature to `POST /auth/v1/token?grant_type=web3`. 6. Receive the existing user or create a new one, then receive a QFEX session. > [!WARNING] > > Display the wallet confirmation to the user and let the wallet perform the > signature. Do not ask for, transmit, or store the wallet's private key or seed > phrase. ## Choose the correct injected provider `window.ethereum` is acceptable when only one wallet extension is installed. If several wallets are installed, discover them with EIP-6963 or use a wallet library and pass the provider selected by the user. Do not silently choose the first wallet. The user should see which address and domain they are authorizing before signing. ## SIWE message requirements The signed message must contain: * The checksummed EVM address * The real host of the registered sign-in page * The complete registered sign-in URI * Version `1` * The wallet's current numeric chain ID * A fresh alphanumeric nonce * A current `Issued At` timestamp * The exact statement `Sign in to QFEX` Generate the message immediately before signing. QFEX rejects invalid signatures, reused or malformed messages, and messages whose domain or URI is not allowed. A message more than 10 minutes old is considered expired. In a non-browser integration, pass the registered sign-in URL explicitly and use a wallet or custody provider that exposes an EIP-1193-compatible signing interface. Never load a raw private key merely to automate this flow. ## Auth HTTP request The signed-credential exchange uses this request: ```http theme={null} POST /auth/v1/token?grant_type=web3 Host: verify.qfex.com Content-Type: application/json apikey: Authorization: Bearer { "chain": "ethereum", "message": "", "signature": "<0x-prefixed signature>" } ``` When CAPTCHA protection is active, also include the Turnstile result: ```json theme={null} { "gotrue_meta_security": { "captcha_token": "" } } ``` A successful response includes: ```json theme={null} { "access_token": "", "token_type": "bearer", "expires_in": 3600, "expires_at": 1790000000, "refresh_token": "", "user": { "id": "11111111-1111-4111-8111-111111111111" } } ``` Treat both tokens as credentials. Do not put them in URLs, logs, analytics events, or source control. ## Use the access token ### REST API Send the session access token as a bearer token: ```javascript theme={null} const response = await fetch("https://api.qfex.com/user/positions", { headers: { Authorization: `Bearer ${session.access_token}`, }, }); ``` The same bearer token works with the [programmatic funding API](/api-reference/programmatic-funding): ```javascript theme={null} const response = await fetch( "https://banker.qfex.com/address?network=ARBITRUM_ONE", { headers: { Authorization: `Bearer ${session.access_token}`, }, }, ); ``` Direct browser requests require the page's origin to be allowed by the target QFEX service. A backend can use the user's bearer token without browser CORS restrictions. ### Trade WebSocket Pass the access token in the connection URL and the authentication message: ```javascript theme={null} const token = session.access_token; const ws = new WebSocket( `wss://trade.qfex.com/?jwt=${encodeURIComponent(token)}`, ); ws.addEventListener("open", () => { ws.send( JSON.stringify({ type: "auth", params: { jwt: token }, }), ); }); ``` See [Trade WebSocket authentication](/websocket/channels/trade/authenticate) for account selection and builder attribution. ## Refresh the session Access tokens expire. Exchange the refresh token for a new session before continuing to call QFEX services: ```javascript theme={null} export async function refreshQfexSession(refreshToken) { const response = await fetch( `${QFEX_AUTH_URL}/auth/v1/token?grant_type=refresh_token`, { method: "POST", headers: { "Content-Type": "application/json", apikey: QFEX_AUTH_CLIENT_KEY, Authorization: `Bearer ${QFEX_AUTH_CLIENT_KEY}`, }, body: JSON.stringify({ refresh_token: refreshToken }), }, ); const result = await response.json(); if (!response.ok) { throw new Error(result.msg ?? result.message ?? "Session refresh failed"); } return result; } ``` Refresh tokens rotate. Store the newest refresh token returned by each successful refresh and discard the previous value. Browser applications should use secure storage appropriate to their threat model. Server applications should use an encrypted credential store. ## Account behavior and limitations * A new wallet address creates a new QFEX account and must complete QFEX onboarding before it can trade or move funds. * Wallet authentication does not bypass geographic restrictions, identity verification, terms acceptance, or 2FA. * If the user has enrolled a second factor, complete the existing QFEX MFA flow after wallet sign-in. * An address already registered with QFEX always resumes the same user. * QFEX does not currently let an email-, Google-, or Apple-first account attach a wallet identity. Starting wallet authentication with a different address may create a separate account. * A wallet-first user can add supported email or social sign-in methods from QFEX Security settings. * A wallet identity cannot currently be unlinked or replaced. ## Common errors | Error | Resolution | | - | - | | No compatible provider | Install or unlock an EVM wallet, or pass the selected EIP-1193 provider explicitly. | | User rejected the request | Stop the flow and let the user initiate it again. Do not retry the signature prompt automatically. | | Invalid SIWE message | Rebuild the message with version `1`, a current timestamp, a fresh alphanumeric nonce, and the checksummed address. | | Domain or URI rejected | Use the real sign-in page URL and ask QFEX support to register it. | | CAPTCHA required or invalid | Complete Turnstile and include the fresh token in `gotrue_meta_security.captcha_token`. | | Rate limited | Apply backoff and wait before allowing another attempt. | | Session succeeds but API access is denied | Complete QFEX onboarding, required terms, identity checks, and any enrolled MFA challenge. | # Bug Bounty Source: https://docs.qfex.com/legal/bug-bounty ## **Bug Bounty Program Overview** * QFEX encourages responsible disclosure of security vulnerabilities through our Bug Bounty program. * Researchers must follow the written policy. This policy is not negotiable. * Key rules: * Act in good faith and avoid policy violations. * Don’t do more than needed to prove a vulnerability. * Don’t make threats or ransom demands. * Report vulnerabilities, including instructions and proof of concept exploit, as soon as discovered and validated. * Researchers are responsible for complying with all applicable laws. * Attempts to subvert or violate our policy will result in immediate ineligibility for this program. Threats or extortion attempts may be referred to law enforcement. * If you’re unsure about something, notify [bugbounty@qfex.com](mailto:bugbounty@qfex.com) for clarification. ## **Policy** QFEX strongly believes in the value of security professionals and developers assisting in keeping our products and users safe. QFEX has established and encourages coordinated vulnerability disclosure (CVD) via our Bug Bounty Program. The Bug Bounty program serves the QFEX mission by helping protect customers in the digital currency market. By looking for bugs in QFEX systems, you agree to keep all data, information about vulnerabilities, your research, and communications with QFEX strictly confidential until QFEX has addressed the issue and granted permission for disclosure. Where the requirements of this Policy are complied with, QFEX agrees not to initiate legal action for security research performed following all posted QFEX Bug Bounty policies, including good faith, accidental violations. Please avoid deliberate privacy violations by creating test accounts whenever possible. Should you encounter personally identifiable information (‘PII’) or other sensitive data for accounts you do not have express written consent of the account owner to use to validate your findings, please stop accessing that data immediately, and report the issue to QFEX with a description of the PII or other sensitive data, not the data itself. In alignment with data protection regulations and our privacy policies, you must: * Not store or transmit other clients’ PII. If you should happen to capture any client PII, report it to QFEX immediately and then destroy all copies of PII that are not yours. * Minimize data collection and access during your research. Only collect and retain information absolutely necessary to demonstrate and report the vulnerability. * Immediately and securely delete all collected data once the report is submitted and QFEX has confirmed that it has received it. * Not disclose any vulnerabilities or associated information to third parties without QFEX's express written consent. This includes but is not limited to social media, other companies, or the press. * If you are reporting a data breach or the location of a data repository instead of a security vulnerability, please supply the location of the data and do not access it further, nor share the location of the data with others. A bug bounty submission must never contain threats or any attempts at extortion. We are open to paying bounties for legitimate findings, however ransom demands are not eligible for payment. For example, not releasing information about the vulnerability or otherwise hindering the ability to resolve the vulnerability until other demands are met will be deemed a ransom demand. We may be required by law or voluntarily decide to report to authorities any bug bounty submission that contains ransom demands. We believe activities conducted consistent with this policy constitute “authorized” conduct under the Computer Fraud and Abuse Act (CFAA), the Digital Millennium Copyright Act (DMCA), and applicable anti-hacking laws such as Cal. Penal Code 503(c). We will not bring a claim against researchers for circumventing the technological measures we have used to protect the applications in scope of the Bug Bounty Program. However, following this policy does not mean that QFEX nor any other individual organization or government can grant immunity from global laws. It is the responsibility of individual security researchers to understand and comply with all applicable local and international laws regarding anti-hacking, data and privacy, and export controls. If a third party brings legal action against you and you were following the terms in this policy, QFEX will inform the pertinent law enforcement agencies or civil plaintiffs that your research activities were, to the best of our knowledge, conducted pursuant to, and in compliance, with the terms and conditions of this program. It is required that each researcher submit a notification to us before engaging in conduct that may be inconsistent with or unaddressed by this policy. We welcome suggestions for policy clarifications that help researchers conduct their research and reporting with confidence. ## **Rewards** All bounty submissions are rated by QFEX and paid out based on vulnerability rating. All payouts will proceed in BTC to your verified QFEX Account and are defined as a guideline and subject to change. * All bug reports must be submitted to [bugbounty@qfex.com](mailto:bugbounty@qfex.com), the only official contact for this program. Please do not use external sites to submit vulnerability details. Any external sites or portals are unofficial and are not approved by QFEX. * To receive bug bounty payments, you must: * Register at the Intermediate level. See: Create an Account- [https://www.qfex.com/signup](https://www.qfex.com/signup) * Have an ACTIVE account * Provide documentation for verification. * Asking for payment or other acknowledgment in exchange for vulnerability details will result in immediate ineligibility of bounty payments. Not releasing vulnerability details will also result in immediate ineligibility of bounty payments. * Provide detailed instructions to reproduce the vulnerability and a Proof of Concept. * If we cannot reproduce your findings, your report will not be eligible for payout. Exploit only what is needed to prove a security vulnerability and promptly return any assets that have been extracted. * Disclosing vulnerability to other individuals is prohibited. * Any attempt to bypass the procedures outlined in this policy will result in immediate ineligibility of bounty payments. * Include your Bitcoin (BTC) Address for Payment. All rewards will be issued in Bitcoin. * Payment minimums are defined below. All payments may be modified at QFEX's discretion. * The minimum payout is Bitcoin (BTC)  equivalent of \$500 USD. ## **Submission Process** The following steps are taken to process a Bug Bounty submission: 1\. Report is submitted to bug bounty mailbox 2\. QFEX security acknowledges submission (SLA 1 Business Day) 3\. QFEX security triages the submission (SLA 10 Business Days) 4\. QFEX security sends response with determination, if deemed a vulnerability, notification includes severity level and amount of reward (we will ask for a BTC address) 5\. For security vulnerabilities, QFEX will send the reward (SLA 14 Business Days) | Payout Scale | Severity | Range | | - | - | - | | | Low Severity | \$500-\$1000 | | | Medium Severity | \$2,500-\$5,000 | | | High Severity | \$20,000-\$50,000 | In Scope | URL | Property Name | | - | - | | [www.qfex.com](http://www.qfex.com) | Main Website | | api.qfex.com | REST API | | http.qfex.com | REST API | | restapi.qfex.com | REST API | | trade.qfex.com | WebSockets API | | mds.qfex.com | WebSockets API | ## **Vulnerability Ratings** **High** High severity issues allow an attacker to read or modify highly sensitive data that they are not authorized to access. They are generally more narrow in scope than critical issues, though they may still grant an attacker extensive access. For example: * arbitrary code/command execution on a server in our production network. * arbitrary queries on a production database. * bypassing our sign-in process, either password or 2FA. * access to sensitive production user data or access to internal production systems. * XSS which bypasses CSP * Discovering sensitive user data in a publicly exposed resource * Gaining access to a non-critical, system to which an end user account should not have access **Medium** Medium severity issues allow an attacker to read or modify limited amounts of data that they are not authorized to access. They generally grant access to less sensitive information than high severity issues. For example: * Disclosing non-sensitive information from a production system to which the user should not have access * XSS that does not bypass CSP or does not execute sensitive actions in another user’s session * CSRF for low risk actions **Low** Low severity issues allow an attacker to access extremely limited amounts of data. They may violate an expectation for how something is intended to work, but it allows nearly no escalation of privilege or ability to trigger unintended behavior by an attacker. For example: * Triggering verbose or debug error pages without proof of exploitability or obtaining sensitive information. ### Ineligibility Reports in which we are not interested and are not eligible for reward include: * Vulnerabilities on sites hosted by third parties (status.qfex.com, etc) unless they lead to a vulnerability on the main website. Vulnerabilities and bugs on the QFEX blog (blog.qfex.com). * Vulnerabilities contingent on physical attack, social engineering, spamming, DDOS attack, etc. * Vulnerabilities affecting outdated or unpatched browsers. * Vulnerabilities in third party applications that make use of QFEX's API. * Vulnerabilities publicly disclosed in third party libraries or technology used in QFEX products, services, or infrastructure earlier than 30 days after the public disclosure of the issue. * Vulnerabilities that have been released publicly prior to QFEX issuing a comprehensive fix. * Vulnerabilities already known to us, or already reported by someone else (reward goes to first reporter). * Issues that aren't reproducible. * Vulnerabilities that require an improbable level of user interaction. * Vulnerabilities that require root/jailbreak on mobile. * Missing security headers without proof of exploitability. * TLS Cipher Suites offered. * Suggestions on best practices. * Software version disclosure. * Any report without detailed step-by-step instructions and an accompanying proof of concept exploit. * Issues that we can't reasonably be expected to do anything about, such as issues in technical specifications that QFEX must implement to conform to those standards. * The output from automated tools/scanners or AI-generated reports. * Issues without any security impact. ### Non-security Issues You can let us know about non-security issues at [support@qfex.com](mailto:support@qfex.com). # Terms of Use Source: https://docs.qfex.com/legal/client-agreement Last Updated: 20 October 2026 ## 1. Acceptance of the Terms of Use These terms of use are entered into by and between you and QFEX Inc., a company incorporated under the laws of the Republic of Panama (“Company,” “we” or “us,” and their variants, including “our” and “ours”). The following terms and conditions, together with any documents they expressly incorporate by reference (collectively, these “Terms of Use”), govern your access to and use of qfex.com (the “Website”), including any content, functionality, and services offered on or through the Website, and any account, trading interface, order entry, matching, execution, risk management, and related services made available by Company from time to time (collectively, the “Platform” and together with the Website, the “Services”), but excluding any third party materials or technology. If the user engages with the Services under authority from a different party or on another party’s behalf, then “you” (and its variants, including “your” and “yours”) as used herein refers to that person on whose behalf the Services are used (e.g., an employer). If the person engaging with the Services is an individual, acting in their own individual capacity, then “you” refers to that individual. If you have anyone using the Services on your behalf, you agree that you are responsible for the actions and inactions of all such persons as if they were your own. Please read the Terms of Use carefully before you start to use the Services. By using the Services or by clicking to accept or agree to the Terms of Use when this option is made available to you, you accept and agree to be bound and abide by these Terms of Use, including our [Privacy Policy](/legal/privacy-policy), which is incorporated herein by reference. If you do not want to agree to these Terms of Use or any part thereof, you must not access or use the Services. ## 2. Restricted Persons and Jurisdictions THE SERVICES WERE NOT DEVELOPED FOR, AND ARE NOT OFFERED TO PERSONS OR ENTITIES WHO RESIDE IN, ARE CITIZENS OF, ARE LOCATED IN, ARE INCORPORATED IN, OR HAVE A REGISTERED OFFICE OR PRINCIPAL PLACE OF BUSINESS IN THE UNITED STATES OR ANY RESTRICTED JURISDICTION OR COUNTRY SUBJECT TO ANY SANCTIONS OR RESTRICTIONS PURSUANT TO ANY APPLICABLE LAW, INCLUDING THE CRIMEA REGION, CUBA, IRAN, NORTH KOREA, SYRIA, RUSSIA, OR ANY OTHER COUNTRY TO WHICH THE UNITED STATES, THE UNITED KINGDOM, THE EUROPEAN UNION OR ANY OTHER JURISDICTION EMBARGOES GOODS OR IMPOSES SIMILAR SANCTIONS, OR ANY JURISDICTIONS IN WHICH THE TRANSACTING OF CRYPTOCURRENCIES IS PROHIBITED OR RESTRICTED IN ANY FORM OR MANNER (COLLECTIVELY, THE “RESTRICTED JURISDICTIONS” AND EACH A “RESTRICTED JURISDICTION”), OR ANY PERSON OWNED, CONTROLLED, LOCATED IN OR ORGANIZED UNDER THE LAWS OF ANY RESTRICTED JURISDICTION OR AFFILIATED WITH ANY SUCH PERSON, ANY PERSON LISTED ON ANY SANCTIONS LIST MAINTAINED BY THE UNITED STATES, THE UNITED KINGDOM, THE EUROPEAN UNION OR ANY OTHER JURISDICTION, OR A RESIDENT OF OR LOCATED IN ANY COUNTRY OR JURISDICTION THAT RESTRICTS ACCESS TO OR PROHIBITS USE OF DIGITAL ASSETS OR CRYPTO CURRENCIES (COLLECTIVELY, “RESTRICTED PERSONS”). THERE ARE NO EXCEPTIONS. IF YOU ARE A RESTRICTED PERSON OR LOCATED IN A RESTRICTED JURISDICTION, THEN DO NOT USE OR ATTEMPT TO USE THE SERVICES. USE OF ANY TECHNOLOGY OR MECHANISM, SUCH AS A VIRTUAL PRIVATE NETWORK, TO CIRCUMVENT OR ATTEMPT TO CIRCUMVENT THE RESTRICTIONS SET FORTH HEREIN IS STRICTLY PROHIBITED. Company also restricts access from the additional [restricted locations](/qfex/faq#what-are-the-restricted-locations) published in its documentation, as updated from time to time. You must not use the Services from any such location. BY USING THE SERVICES, YOU REPRESENT THAT (I) YOU ARE NOT A RESTRICTED PERSON; AND (II) YOU (INCLUDING, IF APPLICABLE, YOUR INDIVIDUAL OWNERS, REPRESENTATIVES, EMPLOYEES, OR ANY OTHER PERSON WITH ACCESS TO YOUR ACCOUNT) WILL NOT COORDINATE, CONDUCT OR CONTROL (INCLUDING BY, IN SUBSTANCE OR EFFECT, MAKING DECISIONS WITH RESPECT TO) YOUR USE OF THE SERVICES FROM WITHIN ANY RESTRICTED JURISDICTION. YOU ACKNOWLEDGE, UNDERSTAND AND AGREE THAT YOU ARE NOT PERMITTED TO MODIFY, DISASSEMBLE, DECOMPILE, ADAPT, ALTER, TRANSLATE, REVERSE ENGINEER OR CREATE DERIVATIVE WORKS OF THE SERVICES TO MAKE THEM AVAILABLE TO ANY RESTRICTED PERSON OR IN ANY RESTRICTED JURISDICTION. NEITHER WE NOR OUR SERVICE PROVIDERS ARE RESPONSIBLE FOR ANY ACTIVITIES THAT YOU ENGAGE IN WHEN USING ANY ONLINE SERVICES OR BLOCKCHAIN TECHNOLOGIES NOT PROVIDED BY US. These Services are offered and available to entities, and individual users who are 18 years of age or older. By using the Services, you represent and warrant that you are a valid legal entity in good standing in your jurisdiction of organization/formation, or if an individual, of legal age to form a binding contract with Company, and meet all of the foregoing eligibility requirements. If you do not meet all of these requirements, you must not access or use the Services. ## 3. Changes to the Terms of Use We may revise and update these Terms of Use from time to time in our sole discretion. All changes are effective immediately when we post them, and apply to all access to and use of the Services thereafter. However, any changes to the dispute resolution provisions set out in the Governing Law and Arbitration section below will not apply to any disputes for which the parties have actual notice on or before the date the change is posted on the Services. Your continued use of the Services following the posting of revised Terms of Use means that you accept and agree to the changes. You are expected to check this page from time to time so you are aware of any changes, as they are binding on you. ## 4. Accessing the Services and Account Security Prior to using the Services to trade, you agree to subject yourself to “know your customer” screenings, and to provide us with all information we request to perform such screenings. If in our discretion we find that your information cannot be verified, or you do not “pass” such screenings, we will not permit you to use the Services. You hereby agree that our decision in this regard is final and non-appealable. We also reserve the right, at any time and in our sole discretion, to conduct identity verification, compliance checks, or similar reviews, and transaction monitoring, and to restrict, suspend, or terminate access to the Services based on the results of such reviews or to comply with applicable law or our risk management policies. However, nothing in these Terms obligates us to conduct any such checks, and we do not undertake to do so. We reserve the right to withdraw or amend the Services, and any service or material we provide on the Services, in our sole discretion without notice. We will not be liable if for any reason all or any part of the Services is unavailable at any time or for any period. From time to time, we may also restrict access to some parts of the Services, or the entire Services, by any user in our discretion. You are responsible for: * Making all arrangements necessary for you to have access to the Services. * Ensuring that all persons who access the Services on your behalf are aware of these Terms of Use and comply with them. To access the Services or some of the resources they offer, you may be asked to provide certain registration details or other information, and you may be required to connect your bank account or digital wallet. You represent and warrant to us that you have permission to connect such bank account or digital wallet and use the funds therein, if required to access the Services. You are responsible for the use of your funds once you connect your bank account or wallet, and for any actions of anyone who uses your credentials to log into and use the Services. If you choose, or are provided with, a user name, password, or any other piece of information as part of our security procedures, you must treat such information as confidential, and you must not disclose it to any other person or entity. You also acknowledge that your account is personal to you and agree not to provide any other person with access to the Services or portions of it using your user name, password, or other security information. You agree to notify us immediately of any unauthorized access to or use of your wallet address or other login credentials you use to access the Services, or any other breach of security. You should use particular caution when accessing your account from a public or shared computer so that others are not able to view or record your seed phrase, keys, password or other sensitive information. It is a condition of your use of the Services that all the information you provide on the Services is correct, current, and complete. You agree that all information you provide to register with the Services or otherwise, including but not limited to through the use of any interactive features on the Services, is governed by our [Privacy Policy](/legal/privacy-policy), and you consent to all actions we take with respect to your information consistent with our [Privacy Policy](/legal/privacy-policy). We have the right to disable any user name, password, wallet address, or other identifier, whether chosen by you or provided by us, at any time in our sole discretion for any or no reason, including if, in our opinion, you have violated any provision of these Terms of Use. You understand that your content (not including credit card information), may be transferred unencrypted and may involve (i) transmissions over various networks; (ii) posting of supported digital asset deposits and withdrawals on public blockchains, which are available for the public to see; and (iii) changes to conform and adapt to technical requirements of connecting networks or devices. We are not responsible for securing your seed phrase or keys if you lose them, if someone steals them from you, or if someone obtains access to them somehow (whether in transit to you, or from your systems or networks, or because you otherwise misplaced them or disclosed them). You acknowledge that if someone has your keys or seed phrase, they have the ability to transfer your digital assets outside your control, and that you may be unable to trade or make use of them. It is your responsibility to establish a means for recovering your seed phrase and keys. We do not have access to or store your keys or seed phrases, so we cannot assist you in recovering your seed phrases, keys, or digital assets if they are lost or stolen, or if the backup or security measures that you have established are compromised. ## 5. Use of the Services The Services provide a trading platform that permits eligible users to trade in certain perpetual futures (collectively, “Perpetuals”), subject to certain rules which govern orders, matching priority, order types, and other parameters (the “Trading Rules”). Company may modify the Trading Rules at any time in its sole discretion. You acknowledge and agree that Company may reject, cancel, modify, or suspend any order or trading activity in accordance with the Trading Rules, these Terms of Use, or Company risk management policies. The Trading Rules are published in the Trading Rulebook, including the pages on [exchange architecture](/qfex/architecture), [order types](/qfex/order-types), [risk limits](/qfex/risk-limits), [margin and liquidations](/qfex/liquidation-margin-call), and [fees](/qfex/fees). These Trading Rules are incorporated into these Terms of Use by reference. Trades executed through the Services are recorded in Company's internal books and records. Company may cancel, void, adjust, or otherwise correct trades in accordance with the Trading Rules in effect from time to time. Perpetuals are derivative instruments. Trading Perpetuals does not confer any ownership interest in, right to acquire, or other proprietary right with respect to any underlying asset referenced by a Perpetual. You will not receive delivery of any underlying asset. Perpetuals are cash settled only, in accordance with the rules and methodologies described on the Services. Trading Perpetuals may involve margin and leverage. You agree to maintain the required margin and collateral levels as displayed on the Services. Company may establish and modify at any time initial margin, maintenance margin, position limits, leverage limits, risk tiers, concentration limits, and other requirements applicable to your account and positions, including on a per product or per user basis. If your account equity falls below applicable requirements, Company may take action in its discretion, including issuing margin calls (if any), restricting account activity, reducing leverage, closing some or all positions, or liquidating collateral. You may be liquidated without prior notice. You acknowledge, that liquidation may occur at prices that are materially worse than prices displayed immediately before liquidation due to volatility, low liquidity, gaps, order book conditions, system execution dynamics or other reasons. Company will determine pricing, valuation, and settlement methodologies for Perpetuals, including index price sources, mark price calculations, funding rate methodologies, and fallback procedures (collectively, the “Methodologies”) in its discretion and based on market dynamics. Company may modify the Methodologies at any time in its sole discretion with or without previous notice to you. You acknowledge and agree that Company may use mark prices or other pricing references (rather than last traded prices) for purposes of margin, liquidation, and valuation, and that pricing may differ from prices available in other markets, including when underlying markets are closed, halted, illiquid, or experiencing disruptions. Margin, liquidation, and valuation may be based on a mark price, index price, or other price reference determined under the Methodologies, rather than the last traded price. The mark price, index price, and last traded price may differ, including materially. You acknowledge and agree that Company’s application of the Methodologies will govern valuation, margin, and liquidation, including in circumstances where you believe a different price reference better reflects market conditions. The Methodologies are described in the pages on [definitions and formulae](/qfex/definitions), [contract specifications](/qfex/contract-specifications), [funding](/qfex/funding), [pre-IPO stocks](/qfex/pre-ipo-stocks), [roll schedules](/qfex/roll-schedules), and [corporate actions](/qfex/corporate-actions). These Methodologies are incorporated into these Terms of Use by reference. Certain Perpetuals may be subject to periodic funding or similar payments to align the price of a contract with the applicable reference or index price over time. Company may facilitate, intermediate, or apply such payments in accordance with the Methodologies, and may modify funding mechanics in its sole discretion, including during disruptions or extraordinary market conditions. You acknowledge and agree that assets and collateral used in connection with the Services will be held in custody accounts maintained by third party custodians designated by Company from time to time. Company may use one or more custodial arrangements, including omnibus custody arrangements. You authorize Company to engage in such arrangements, and use collateral funds as necessary to provide the Services, including to satisfy margin requirements, settle trades, pay fees, and execute liquidations. A deposit into your account will be credited when Company (or its designated service provider) confirms receipt and availability of the relevant funds or assets in accordance with Company policies. A withdrawal will be final when Company (or its designated service provider) has processed the withdrawal in accordance with Company policies and the withdrawal is reflected as completed in the applicable account records. Company may delay, suspend, or refuse any deposit or withdrawal request to comply with applicable law, to complete compliance or security reviews, to address suspected fraud or unauthorized activity, to satisfy risk management policies, or as otherwise necessary in its discretion. A hold or restriction imposed to investigate or recover amounts relating to Market Abuse is subject to the safeguards in Section 12, including limits on its amount, duration, and use. Company may implement market integrity and risk controls, including circuit breakers, trading halts, price bands, order throttles, speed controls, anti-manipulation measures, and anti-high frequency arbitrage protections from time to time. Company may apply these controls in its sole discretion, including on a product specific or account specific basis, and may change these controls at any time in its discretion. ## 6. Fees We may, in our discretion, impose fees for access to or use of certain functionality within the Services, including trading fees, funding related charges, liquidation fees, account fees, custody fees, deposit or withdrawal fees, conversion or spread based fees, and other charges (collectively, “Fees”). Fee amounts and structures may change at any time without prior notice, and the precise fee applicable to a particular transaction may not be disclosed in advance. In connection with supported digital asset deposits and withdrawals, third party protocols, blockchain networks, or service providers, including wallet providers and network validators, may impose additional fees, such as gas or transaction fees, which are your sole responsibility and may not be disclosed to you up front. You authorize Company to deduct such fees and charges, and our Fees, from your account or collateral balances as applicable. ## 7. Intellectual Property Rights Except for User Contributions, the Services and their entire contents, features, and functionality (including but not limited to all information, software, text, displays, images, video, and audio, and the design, selection, and arrangement thereof), are owned by Company, its licensors, or other providers of such material and are protected by copyright, trademark, patent, trade secret, and other intellectual property or proprietary rights laws. User Contributions are also protected by applicable laws, and you do not have the right to download, copy or use them in any way without the consent of the user who posted them. You must not reproduce, distribute, modify, create derivative works of, publicly display, publicly perform, republish, download, store, or transmit any of the material on our Services, except as follows: * Your computer may temporarily store copies of such materials in RAM incidental to your accessing and viewing those materials. * You may store files that are automatically cached by your Web browser for display enhancement purposes. * You may print or download one copy of a reasonable number of pages of the Services for your own personal, non-commercial use and not for further reproduction, publication, or distribution. * If we provide desktop, mobile, or other applications for download, you may download a single copy to your computer or mobile device solely for your own use, provided you agree to be bound by our end user license agreement for such applications. * If we provide social media features with certain content, you may take such actions as are enabled by such features. You must not: * Modify copies of any materials from the Services or their underlying software or systems. * Use any illustrations, photographs, video or audio sequences, or any graphics separately from the accompanying text. * Delete or alter any copyright, trademark, or other proprietary rights notices from copies of materials from the Services. If you print, copy, modify, download, or otherwise use or provide any other person with access to any part of the Services in breach of these Terms of Use, your right to use the Services will stop immediately and you must then immediately destroy any copies of the materials you have made. No right, title, or interest in or to the Services or any content on the Services is transferred to you, and all rights not expressly granted are reserved by Company. Any use of the Services not expressly permitted by these Terms of Use is a breach of these Terms of Use and may violate copyright, trademark, and other laws. ## 8. Trademarks Company’s name, Company’s logos, and all related names, logos, product and service names, designs, and slogans on the Services are trademarks of Company or its affiliates or licensors. You must not use any such marks, regardless of who posted them to the Services, without the prior written permission of Company. All other names, logos, product and service names, designs, and slogans on the Services are the trademarks of their respective owners. ## 9. Feedback You may from time to time provide us with comments, suggestions, or ideas relating to the Services (“Feedback”). You acknowledge and agree that we may use the Feedback in our business without restriction. All such Feedback will be considered non-confidential and non-proprietary. By providing any Feedback, you grant us and our vendors, and each of our and respective licensees, successors, and assigns a perpetual, unlimited, irrevocable, non-exclusive, worldwide, fully paid up and royalty free right and license to use, reproduce, modify, perform, display, distribute, create derivative works of, and otherwise exploit and disclose to third parties any such material for any purpose. ## 10. Prohibited Uses You may use the Services only for lawful purposes and in accordance with these Terms of Use. You agree not to use the Services: * In any way that violates any applicable law or regulation (including, without limitation, any laws regarding the export of data or software to and from one jurisdiction to another). * For the purpose of exploiting, harming, or attempting to exploit or harm minors in any way by exposing them to inappropriate content, asking for personally identifiable information, or otherwise. * To send, knowingly receive, upload, download, use, or re-use any material that does not comply with the Content Standards set out in these Terms of Use. * To transmit, or procure the sending of, any advertising or promotional material without our prior written consent, including any “junk mail,” “chain letter,” “spam,” or any other similar solicitation. * To impersonate or attempt to impersonate Company, a Company employee, another user, or any other person or entity (including, without limitation, by using email addresses or screen names associated with any of the foregoing). * To engage in any other conduct that restricts or inhibits anyone’s use or enjoyment of the Services, or which, as determined by us, may harm Company or users of the Services or expose them to liability. Additionally, you agree not to: * Use the Services in any manner that could disable, overburden, damage, or impair them, or interfere with any other party’s use of the Services, including their ability to engage in real time activities. * Use any content available on or via the Services, any portion thereof, or any output generated, for training or fine tuning any machine learning or artificial intelligence tool, software or platform. * Use any content available on or via the Services, any portion thereof, or any output generated, for the purpose of identifying a specific individual or extracting personal information. * Use any robot, spider, or other automatic device, process, or means to access the Services for any purpose, including monitoring or copying any of the material on the Services. * Use any manual process to monitor or copy any of the material on the Services or for any other unauthorized purpose without our prior written consent. * Use any device, software, or routine that interferes with the proper working of the Services. * Introduce any viruses, Trojan horses, worms, logic bombs, or other material that is malicious or technologically harmful. * Attempt to gain unauthorized access to, interfere with, damage, or disrupt any parts of the Services, the server on which the Services are stored (if applicable), or any server, computer, or database connected to the Services or used to make them available. * Copy, mirror or otherwise attempt to replicate or reproduce the Services. * Attack the Services via a denial-of-service attack or a distributed denial-of-service attack. * Otherwise attempt to interfere with the proper working of the Services. * Engage in, promote, or facilitate money laundering, terrorist financing, sanctions evasion, or any other unlawful financial activity. * Violate or circumvent any legally imposed compliance measures implemented by Company. * Transact on behalf of, or for the benefit of, any Restricted Person or any person or entity located in a Restricted Jurisdiction. * Use the Services as a mixer, tumbler, anonymization tool, or any other mechanism to obscure or disguise the source or destination of your funds. * Circumvent or attempt to circumvent any access controls, geo-blocking, or wallet restrictions implemented by Company, including by using virtual private networks, proxy servers, or false identifying information. * Engage in coordinated, collusive, or deceptive trading activity, including wash trading, spoofing, layering, front-running, or conduct intended to create a false or misleading impression of market prices, supply, demand, liquidity, or trading volume. * Use or attempt to use another user’s wallet or credentials without authorization or access the Services through unauthorized integrations or smart-contract calls. * Introduce, deploy, or interact with any smart contract or code designed to extract data or assets from other users or from the Services without consent. * Exploit or attempt to exploit any vulnerability, bug, or malfunction in the Services for any reason, including personal gain. * Use the Services for fundraising, staking pools, or token sales not expressly authorized by Company. * Collect, harvest, or attempt to collect wallet addresses, transaction data, or personal information from other users. * Attempt to deanonymize or re-identify any user or participant from blockchain data. * Provide false, inaccurate, or misleading information during registration, verification, or interaction with the Services. * Engage in any activity that, in Company’s sole judgment, poses a risk of legal liability, regulatory exposure, reputational harm, or disruption to the integrity or operation of the Services. * Use or attempt to use any currencies or digital assets that is not expressly supported or approved by Company in connection with the Services, including attempting to deposit, bridge, swap, wrap, mint, burn, or otherwise transact in or with any non-whitelisted asset through the Services, or using any workaround to cause the Services to recognize, route, display, or process any such asset. * Attempt to manipulate or distort any index, reference price, mark price, funding rate, settlement value, or other pricing methodology used for valuation, margin, liquidation, or settlement, including via coordinated trading or activity on or off the Services. * Any quote stuffing, message flooding, excessive order submissions and cancellations, or any activity intended to exploit matching engine behavior, queue priority, or latency characteristics to disrupt markets or gain an unfair advantage. ### 10.1 Market abuse For purposes of these Terms of Use, "Market Abuse" means market manipulation, spoofing, layering, wash trading, collusive or deceptive trading, deliberate exploitation of a vulnerability, bug, or malfunction, or manipulation of pricing inputs prohibited by this section. It includes attempting, arranging, or knowingly assisting such conduct, whether alone or in concert with others, through one or more accounts, on the Services or on another venue where the conduct affects or is intended to affect the Services or trading on them. In particular, you must not: * Submit, amend, or cancel orders for the purpose of creating a false or misleading impression of supply, demand, or liquidity and inducing another participant to quote or trade on that basis, including by placing orders with the intent to cancel them before execution or layering orders at multiple price levels. * Create or use artificially influenced trades, quotes, order books, or market data to manipulate a price, valuation, funding payment, liquidation, settlement, or trading signal used by the Services or a participant trading on them. * Use deceptive orders, coordinated transactions, or manipulated prices on or off the Services to induce a market maker or other participant, including its automated trading systems, to change quotes, execute trades, or enter or unwind hedges on an artificial basis, including to obtain favorable executions against the resulting quotes. * Use accounts, wallets, intermediaries, or coordinated persons to carry out or conceal Market Abuse or evade a restriction imposed in response to it. These prohibitions apply regardless of whether the affected participant is an independent user or market maker, Company, an affiliate of Company, or a liquidity provider acting for any of them, including where Company or an affiliate trades as principal through an automated market maker. The same standards of evidence and causation apply regardless of that relationship. Bona fide market making, arbitrage, hedging, authorized automated or API trading, and legitimate order amendment or cancellation do not constitute Market Abuse merely because they are profitable, respond quickly to market conditions, result in unfilled or cancelled orders, or cause losses to Company or another participant. This does not permit deceptive or manipulative conduct or circumvention of applicable Trading Rules and technical controls. ## 11. User Contributions The Services may provide the ability to upload, display, post, use, submit, publish, or transmit content or materials (collectively, “User Contributions”). All User Contributions must comply with the Content Standards set out in these Terms of Use. Any User Contribution you post to the Services will be considered non-confidential and non-proprietary. By providing any User Contribution on the Services, you grant us and our affiliates and service providers, and each of their and our respective licensees, successors, and assigns the right to use, reproduce, modify, perform, display, distribute, and otherwise disclose to third parties any such material for any purpose. You represent and warrant that: * You own or control all rights in and to the User Contributions or have the legal right to use the User Contributions and to grant the license granted above. * All of your User Contributions do and will comply with these Terms of Use. You understand and acknowledge that you are responsible for any User Contributions you submit or contribute, and you, not Company, have full responsibility for such content, including its legality, reliability, accuracy, and appropriateness. We are not responsible or liable to any third party for the content or accuracy of any User Contributions posted by you or any other user of the Services. ## 12. Monitoring and Enforcement; Termination We have the right to: * Remove or refuse to post any User Contributions for any or no reason in our sole discretion. * Take any action with respect to any User Contribution that we deem necessary or appropriate in our sole discretion, including if we believe that such User Contribution violates the Terms of Use, infringes any intellectual property right or other right of any person or entity, threatens the safety of users of the Services or the public, or could create liability for Company. * Disclose your identity or other information about you to any third party who claims that material posted by you violates their rights, including their intellectual property rights or their right to privacy. * Take legal action, including without limitation, referral to law enforcement, for any action we believe is illegal or constitutes unauthorized use of the Services. * Terminate or suspend your access to all or part of the Services for any or no reason, including without limitation, if we believe you have engaged in any violation of these Terms of Use. Without limiting the foregoing, we have the right to fully cooperate with any law enforcement authorities or court order requesting or directing us to disclose the identity or other information of anyone posting any materials on or through the Services. However, we do not undertake to review all material before it is posted on the Services, and cannot ensure prompt removal of objectionable material after it has been posted. Accordingly, we assume no liability for any action or inaction regarding transmissions, communications, or content provided by any user or third party. We have no liability or responsibility to anyone for performance or nonperformance of the activities described in this section. ### 12.1 Protective measures for market abuse Where specific facts give Company reasonable grounds to suspect Market Abuse, Company may act immediately, without prior notice where necessary to prevent further harm or dissipation of affected assets, to reject or cancel open orders, restrict or suspend trading and API access, limit transfers or withdrawals, or reduce or close positions where reasonably necessary to contain the resulting risk. Position management must follow the applicable Trading Rules and use a reasonable, documented execution or valuation method. Company may terminate access and prohibit re-registration where it reasonably determines on documented evidence that you have engaged in Market Abuse. Company may extend protective restrictions to accounts reasonably evidenced to be under your ownership or control or knowingly acting in concert in the same activity. A connection between accounts does not by itself establish liability or permit Company to use one person's assets to satisfy another person's obligations. You must preserve relevant records and reasonably cooperate with lawful requests for information needed to investigate your activity. ### 12.2 Temporary holds A hold is a temporary protective measure and does not transfer ownership of your assets to Company. Subject to applicable law and binding directions of competent authorities, Company will limit a hold to the reasonably substantiated amount in issue and any collateral needed for outstanding positions or obligations. If that amount cannot immediately be isolated, Company may impose a broader initial restriction while identifying it, but will narrow the restriction promptly as the evidence permits. Company will record the reasons for a hold, conduct its investigation diligently, review the necessity and amount of the hold at least every 14 calendar days, and promptly release any amount no longer reasonably required. A hold must not be maintained merely to avoid determining a claim or commencing appropriate proceedings. Unaffected balances remain available for withdrawal subject to ordinary margin requirements and other lawful restrictions. Further limits for disputed amounts are set out in Section 12.5. ### 12.3 Trade corrections and financial remedies If Company determines reasonably, in good faith, and on documented evidence that you engaged in Market Abuse, Company may, subject to Sections 12.4 and 12.5 and applicable law: * Cancel, void, reprice, or correct affected trades executed on the Services and the resulting positions, profit or loss, funding, fees, rebates, rewards, or other account entries to remove the demonstrated effect of the Market Abuse. Corrections must identify the affected transactions and use a reasonable, documented method based on contemporaneous reliable market data or other objective evidence of the outcome absent the abuse. Where a reliable corrective price cannot be established, Company may void the affected transaction with corresponding account adjustments or pursue a claim through the applicable dispute process. Company must account consistently for directly connected gains, losses, and charges and must not select only profitable trades for reversal. * Require repayment of net profits, funding receipts, rebates, rewards, or other benefits directly attributable to the Market Abuse, to the extent lawfully recoverable by Company, including benefits already withdrawn, transferred, or converted. Directly connected losses, fees, and amounts already reversed or repaid must be taken into account. * Require compensation for Company's actual, documented, and legally recoverable losses and reasonable costs directly caused by the Market Abuse, including net execution, hedging, hedge-unwind, funding, and settlement losses and reasonable investigation and recovery costs. Legal fees and dispute costs remain subject to Section 27 and applicable law. Company must identify the causal connection between the conduct and each correction or claim. A loss incurred by a market maker, a profitable strategy, or a difference between prices on different venues does not by itself establish Market Abuse. Corrections affecting counterparties must be recorded consistently in the corresponding accounts. Amounts recovered on behalf of an affected participant may be applied only to that participant's established entitlement, and must not be retained as unrelated Company revenue. These remedies are restorative and compensatory. There is no automatic forfeiture of an entire account balance or an arbitrary monetary penalty for Market Abuse. The calculation must credit all relevant reversals, recoveries, payments, and offsets, including recoveries by affected affiliates or third parties, so that the same benefit or loss is not recovered twice. Repayment or reversal of a benefit must be credited against compensation for the corresponding loss; Company must not recover both sides of the same economic transfer as separate amounts. Any additional claim under applicable law remains subject to these restrictions on account deductions and duplicate recovery. ### 12.4 Account adjustments and set-off You authorize Company to implement lawful account corrections under Section 12.3 and, only to the extent permitted by applicable law, to deduct or set off amounts legally due and payable to Company against balances legally available for that purpose. Any set-off must satisfy applicable requirements concerning reciprocal debts, maturity, ascertainable and enforceable amounts, and third-party rights. This authorization does not override restrictions on the restitution of deposits, protected custody assets, or other property that cannot lawfully be applied to a claim. Company may not avoid those restrictions by describing a recovery of damages as a trade correction or a fee. A claim belonging to an affiliate or another participant is not automatically a debt owed to Company. Company may recover such a claim as its own only on a valid legal basis, such as an effective assignment, subject to your applicable defenses and all restrictions in this section. Common ownership or participation in the same corporate group is insufficient. Company's suspicion, calculation, or internal decision does not by itself conclusively establish a liquid and enforceable debt or ownership of your assets. A disputed, unliquidated claim for compensation, repayment, or restitution, including a disputed trade correction to the extent it would recover value from your assets, may support a lawful temporary hold under Section 12.2, but must not be permanently appropriated through a unilateral account debit. Company must establish its entitlement through an express settlement with you, a binding arbitral award or court order, or another basis permitting recovery under applicable law. Any conversion or realization of assets for a lawful recovery must be limited to what is necessary, use a reasonable documented valuation and execution method, and account to you for the proceeds, reasonable costs, and surplus. ### 12.5 Notice, review, and release Company will notify you of a material restriction, proposed correction, or recovery as soon as reasonably practicable and legally permitted, identifying the relevant activity, the basis for the action, and the amount and method of calculation sufficiently to enable a meaningful response. Company may withhold details where disclosure would violate law, a binding direction, third-party rights, or legitimate security or investigation needs, but will provide the substance of its reasons to the extent lawful and practicable. Confidentiality does not make Company's decision immune from review. You may submit relevant information and request review at [legal@qfex.com](mailto:legal@qfex.com). Except where a binding legal direction requires earlier action or you expressly agree to a specific settlement, Company will allow at least 14 calendar days after providing the proposed financial determination and calculation for your response before making a final financial recovery. Immediate protective measures and provisional accounting entries needed for risk management may precede that review, but do not establish a final entitlement to disputed funds. Failure to respond is not an admission of liability or agreement to a settlement. Company will consider your submissions and provide a reasoned written determination. A person not directly involved in the affected trading activity will review a contested determination. If you dispute a retained amount and the dispute is not resolved, Company will, within 30 calendar days of receiving your dispute, release that disputed hold or commence proceedings under Section 27 or seek interim relief from a competent tribunal or court, unless you expressly agree to an extension or applicable law or a binding direction requires continued retention. Commencing proceedings does not itself authorize a hold otherwise prohibited by law. Holds remain subject to the periodic review and proportionality requirements in Section 12.2. An internal review is not a prerequisite to exercising your rights under Section 27 or seeking relief available under applicable law. Company will promptly correct an erroneous adjustment and return or release amounts to which it has no lawful entitlement, including after account termination. Neither the introductory provisions of this Section 12 nor the limitations and releases in Sections 24 and 26 limit these accounting, correction, and return obligations or the notice, review, and dispute rights in Sections 12.1 through 12.5. The specific safeguards in Sections 12.1 through 12.5 govern measures taken for Market Abuse notwithstanding a more general discretion or remedy elsewhere in these Terms of Use, and do not override mandatory law or binding directions of competent authorities. ## 13. Content Standards The content standards set forth in this section of the Terms of Use (the “Content Standards”) apply to any and all User Contributions. User Contributions must in their entirety comply with all applicable laws and regulations. Without limiting the foregoing, User Contributions must not: * Contain any material that is defamatory, obscene, indecent, abusive, offensive, harassing, violent, hateful, inflammatory, or otherwise objectionable. * Promote sexually explicit or pornographic material, violence, or discrimination based on race, sex, religion, nationality, disability, sexual orientation, or age. * Infringe any patent, trademark, trade secret, copyright, or other intellectual property or other rights of any other person. * Violate the legal rights (including the rights of publicity and privacy) of others or contain any material that could give rise to any civil or criminal liability under applicable laws or regulations or that otherwise may be in conflict with these Terms of Use and our [Privacy Policy](/legal/privacy-policy). * Contain any trademarks, logos or other material belonging to a third party without such party’s prior explicit consent. * Be likely to deceive any person. * Promote any illegal activity, or advocate, promote, or assist any unlawful act. * Cause annoyance, inconvenience, or needless anxiety or be likely to upset, embarrass, alarm, or annoy any other person. * Impersonate any person or misrepresent your identity or affiliation with any person or organization. * Involve commercial activities or sales, such as contests, sweepstakes and other sales promotions, barter, or advertising. * Give the impression that they emanate from or are endorsed by us or any other person or entity if this is not the case. * Include or imply any statement of expected financial return, investment performance, or guaranteed outcome related to your activity on the Services. * Provide instructions or encouragement to circumvent laws, sanctions, or technical restrictions, including access controls or geo-blocking implemented by the Company. ## 14. Copyright Infringement We respect the intellectual property rights of others and expect users of the Services to do the same. If you believe that any material available on or through the Services infringes your copyright or other intellectual property rights, please send the relevant information to [legal@qfex.com](mailto:legal@qfex.com). We reserve the right, but do not undertake the obligation, to review all requests and take any action deemed appropriate at our discretion, which may include removing or restricting access to the material and/or terminating the user accounts of repeat infringers. Please note that knowingly submitting false or misleading claims may result in liability under applicable law. ## 15. Reliance on Information Posted The information presented on or through the Services is made available solely for general information purposes. We do not warrant the accuracy, completeness, or usefulness of this information. Any reliance you place on such information is strictly at your own risk. We disclaim all liability and responsibility arising from any reliance placed on such materials by you or any other visitor to the Services, or by anyone who may be informed of any of its contents. These Services include content provided by third parties. All statements and/or opinions expressed in these materials, other than the content provided by Company, are solely the responsibility of the person or entity providing those materials. These materials do not necessarily reflect the opinion of Company. We are not responsible, or liable to you or any third party, for the content or accuracy of any materials provided by any third parties. WE DO NOT ENDORSE AND ARE NOT RESPONSIBLE OR LIABLE FOR ANY THIRD-PARTY MATERIALS, INFORMATION, PROTOCOLS, ORACLES, INDEXES, NETWORKS, INFRASTRUCTURE, RESOURCES, APPLICATIONS, DATA, CONTENT OR SERVICES, EVEN IF AVAILABLE ON, THROUGH OR FROM THE SERVICES. WE HAVE NO RESPONSIBILITY FOR, NO CONTROL OVER, AND MAKE NO GUARANTEES WITH RESPECT TO, THE QUALITY, SECURITY, ACCURACY, RELIABILITY, OR UPTIME OF SUCH THIRD-PARTY MATERIALS. ## 16. No Financial or Investment Advice You understand that we do not act as your financial advisor, investment manager, arranger, introducer, or commodity trading advisor, or otherwise interact with, or with respect to your activities on the Services. It is your responsibility and you are solely responsible for your decisions regarding how, when, and with whom you interact or transact through the Services. It is also your responsibility to ensure you understand Perpetuals, how they work, what their value is, and the risks involved in using the Services, including any blockchain networks or smart contracts used for supported deposits and withdrawals, as there are significant risks in doing so, all of which you solely assume. We may suspend or cancel your use of the Services for any reason, including if we believe you have engaged in or are about to engage in any kind of fraud, if required pursuant to applicable laws, or if you violate these Terms of Use. We may provide you with notice of suspension but do not undertake an obligation to do so. You agree and understand that all decisions you make when using the Services are made solely by you. You agree and understand that under no circumstances will the operation of our Services or your use of them be deemed to create a relationship that includes the provision of or tendering of investment advice. NO FINANCIAL, INVESTMENT, TAX, LEGAL, OR SECURITIES ADVICE IS GIVEN THROUGH OR IN CONNECTION WITH THE SERVICES. No content found on our Services, whether created by us, our service providers, our business partners, or another user, is or should be considered investment advice. You agree and understand that we accept no responsibility whatsoever for and shall in no circumstances be liable in connection with, your decisions or your use of the Services. ## 17. Risks You are solely responsible for any transactions or interactions you initiate on the Services, and for all fees that you may incur as a result of your use of Perpetuals. We are not a bank, and we have no fiduciary duty to you. Trading Perpetuals is speculative and involves substantial risk of loss. No statement or content on the Services should be interpreted as a guarantee of profit or limitation of loss. Use of our Services may be subject to expropriation, theft, or unauthorized access. Hackers or other malicious actors may attempt to interfere with our Services or your use thereof in a variety of ways, including, but not limited to, use of malware, denial of service attacks, Sybil attacks, and spoofing. We do not guarantee that our Services will be error free or available without interruption. The information on our Services may not always be entirely accurate, complete, or current. You should verify all information before relying on it. Indices, market data, and price feeds may be delayed, inaccurate, incomplete, disrupted, manipulated, or unavailable. Company may rely on third party sources for data and indices. Company may implement fallbacks, substitute data sources, pause certain calculations, or take other actions under the Methodologies during disruptions. These circumstances may adversely affect pricing, margin, funding, liquidation, and settlement. Perpetuals may be tradable 24 hours per day, 7 days per week, including when the primary markets for underlying assets are closed or halted. During such times, pricing may be based on limited information, proxies, or methodologies that may not reflect prices that would be available when primary markets reopen. Significant price gaps may occur, including gaps that trigger liquidations. You assume all risks of trading during all hours, including reduced liquidity, wider spreads, and heightened volatility. The Services are subject to system limitations, latency, outages, maintenance, and capacity constraints. You may experience delays, rejected orders, partial fills, slippage, or inability to access the Services, which may result in losses or liquidation. Customer assets may be held with a third party custodian or other service providers. You are exposed to the operational risks of such providers, including outages, insolvency, errors, and security incidents. Access to your collateral and the ability to deposit or withdraw may be delayed or limited due to actions or inactions of such third parties, compliance holds, security reviews, or risk management decisions. Extraordinary market events, including extreme volatility, illiquidity, disruptions, price dislocations, and other events may occur. In such circumstances, Company may take actions under these Terms of Use, the Trading Rules, or the Methodologies, including increasing margin requirements, restricting leverage, halting trading, canceling orders, modifying or suspending funding, closing positions, and liquidating collateral. You acknowledge and agree that these actions may be taken without prior notice and may adversely affect you. ## 18. Taxes You are solely responsible for determining and fulfilling any tax obligations that may arise from your use of the Services, including any transactions involving Perpetuals. Company does not report any taxes you may owe to any governmental authority. It is your responsibility to report and remit any taxes, duties, or similar assessments that may be imposed by any governmental authority, and to maintain accurate records as required by applicable law. ## 19. Changes to the Services We may update the content on the Services from time to time, but their content is not necessarily complete or up-to-date. Any of the material on the Services may be out of date at any given time, and we are under no obligation to update such material. ## 20. Information About You and Your Visits to the Services All information we collect on the Services is subject to our [Privacy Policy](/legal/privacy-policy). By using the Services, you consent to all actions taken by us with respect to your information in compliance with the [Privacy Policy](/legal/privacy-policy). ## 21. Linking to the Services and Social Media Features You may link to our Website homepage, provided you do so in a way that is fair and legal and does not damage our reputation or take advantage of it, but you must not establish a link in such a way as to suggest any form of association, approval, or endorsement on our part without our express written consent. The Website may provide certain social media features that enable you to: * Link from your own or certain third-party websites to certain content on the Services. * Send emails or other communications with certain content, or links to certain content, on the Services. * Cause limited portions of content on the Services to be displayed or appear to be displayed on your own or certain third-party websites. You may use these features solely as they are provided by us, and solely with respect to the content they are displayed with and otherwise in accordance with any additional terms and conditions we provide with respect to such features. Subject to the foregoing, you must not: * Establish a link from any website that is not owned by you. * Cause the Website or portions of it to be displayed on, or appear to be displayed by, any other site, for example, framing, deep linking, or in-line linking. * Link to any part of the Website other than the homepage. * Otherwise take any action with respect to the materials on the Services that is inconsistent with any other provision of these Terms of Use. The website from which you are linking, or on which you make certain content accessible, must comply in all respects with the Content Standards set out in these Terms of Use. You agree to cooperate with us in causing any unauthorized framing or linking immediately to stop. We reserve the right to withdraw linking permission without notice. We may disable all or any social media features and any links at any time without notice in our discretion. ## 22. Links from the Services If the Services contain links to other sites and resources provided by third parties, these links are provided for your convenience only. This includes links contained in advertisements, including banner advertisements and sponsored links, if any are posted on the Services. We have no control over the contents of those sites or resources, and accept no responsibility for them or for any loss or damage that may arise from your use of them. If you decide to access any of the third-party websites linked to the Services, you do so entirely at your own risk and subject to the terms and conditions of use for such websites. ## 23. Geographic Restrictions We make no claims that the Services or any of their content is accessible or appropriate in your jurisdiction. Access to the Services may not be legal by certain persons or in certain countries. You access the Services on your own initiative and are responsible for compliance with your local laws. ## 24. Disclaimer of Warranties and Limitation on Liability You understand that we cannot and do not guarantee or warrant that files available for downloading from the internet or the Services will be free of viruses or other destructive code. You are responsible for implementing sufficient procedures and checkpoints to satisfy your particular requirements for anti-virus protection, accuracy of data input and output, and for maintaining a means external to our Services for any reconstruction of any lost data. TO THE FULLEST EXTENT PROVIDED BY LAW, WE WILL NOT BE LIABLE FOR ANY LOSS OR DAMAGE CAUSED BY A DENIAL OF SERVICE ATTACK, DISTRIBUTED DENIAL-OF-SERVICE ATTACK, VIRUSES, OR OTHER TECHNOLOGICALLY HARMFUL MATERIAL THAT MAY INFECT YOUR DEVICES, COMPUTER EQUIPMENT, COMPUTER PROGRAMS, DATA, OR OTHER PROPRIETARY MATERIAL DUE TO YOUR USE OF THE SERVICES OR ANY SERVICES OR ITEMS OBTAINED THROUGH THE SERVICES OR TO YOUR DOWNLOADING OF ANY MATERIAL POSTED ON IT, OR ON ANY WEBSITE LINKED TO IT. YOUR USE OF THE SERVICES, THEIR CONTENT, CONTENT PROVIDED BY OTHER USERS, AND ANY SERVICES OR ITEMS OBTAINED THROUGH THE SERVICES IS AT YOUR OWN RISK. THE SERVICES, THEIR CONTENT, CONTENT PROVIDED BY OTHER USERS, AND ANY SERVICES OR ITEMS OBTAINED THROUGH THE SERVICES ARE PROVIDED ON AN “AS IS” AND “AS AVAILABLE” BASIS, WITHOUT ANY WARRANTIES OF ANY KIND, EITHER EXPRESS, STATUTORY, IMPLIED OR OTHERWISE. NEITHER COMPANY NOR ANY PERSON ASSOCIATED WITH COMPANY MAKES ANY WARRANTY OR REPRESENTATION WITH RESPECT TO THE COMPLETENESS, SECURITY, RELIABILITY, QUALITY, ACCURACY, OR AVAILABILITY OF THE SERVICES. WITHOUT LIMITING THE FOREGOING, NEITHER COMPANY NOR ANYONE ASSOCIATED WITH COMPANY REPRESENTS OR WARRANTS THAT THE SERVICES, THEIR CONTENT, CONTENT PROVIDED BY OTHER USERS, OR ANY SERVICES OR ITEMS OBTAINED THROUGH THE SERVICES WILL BE ACCURATE, RELIABLE, ERROR-FREE, OR UNINTERRUPTED, THAT DEFECTS WILL BE CORRECTED, THAT OUR SERVICES ARE FREE OF VIRUSES OR OTHER HARMFUL COMPONENTS, OR THAT THE SERVICES OR ANY SERVICES OR ITEMS OBTAINED THROUGH THE SERVICES WILL OTHERWISE MEET YOUR NEEDS OR EXPECTATIONS. TO THE FULLEST EXTENT PROVIDED BY LAW, COMPANY HEREBY DISCLAIMS ALL WARRANTIES OF ANY KIND, WHETHER EXPRESS OR IMPLIED, STATUTORY, OR OTHERWISE, INCLUDING ANY WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE. TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL THE TOTAL LIABILITY OF COMPANY, ITS AFFILIATES, OR ANY OF OUR OR THEIR RESPECTIVE OFFICERS, DIRECTORS, EMPLOYEES, CONTRACTORS, AGENTS, SERVICE PROVIDERS, LICENSORS, SUPPLIERS, SUCCESSORS, AND ASSIGNS (COLLECTIVELY, THE “COMPANY PARTIES”) HEREUNDER ARISING OUT OF OR RELATING TO THESE TERMS OF USE OR YOUR USE OF THE SERVICES EXCEED ONE HUNDRED U.S. DOLLARS (USD 100). THE FOREGOING DOES NOT AFFECT ANY LIABILITY THAT CANNOT BE EXCLUDED OR LIMITED UNDER APPLICABLE LAW. ## 25. Indemnification You agree to defend and indemnify the Company Parties from and against any and all third party claims, suits, actions, proceedings, damages, judgments, awards, losses, liabilities, costs, and expenses (including reasonable attorneys’ and legal fees) (collectively, the “Claims”) arising out of or relating to: (a) your breach of these Terms of Use, (b) your use of the Services, (c) your User Contributions, content, inputs, prompts, instructions, or other materials you submit, transmit, publish, or make available through the Services, (d) your violation of applicable law or regulation, or (e) your violation of any third party rights, including intellectual property or privacy rights. The direct recovery rights for Market Abuse are set out in Section 12.3. This indemnity does not expand the right to deduct or set off amounts from an account, avoid the safeguards in Section 12, or permit duplicate recovery of a benefit or loss. ## 26. Release and Hold Harmless TO THE MAXIMUM EXTENT PERMITTED BY LAW, COMPANY SHALL HAVE NO LIABILITY FOR, AND YOU HEREBY RELEASE, WAIVE, AND HOLD HARMLESS THE COMPANY PARTIES FROM AND AGAINST ANY AND ALL CLAIMS ARISING OUT OF OR RELATING TO: (A) YOUR USE OF, OR INABILITY TO USE, THE SERVICES, OR ANY PART THEREOF, INCLUDING ANY TRADING INTERFACES OR ORDER ENTRY TOOLS; (B) ANY ORDERS YOU SUBMIT, INCLUDING ANY REJECTED, CANCELLED, MODIFIED, PARTIALLY FILLED, OR UNFILLED ORDERS, ANY DELAYS IN SUBMISSION, MATCHING, OR EXECUTION, OR ANY SLIPPAGE, LATENCY, OR OTHER EXECUTION OUTCOMES; (C) ANY TRADES OR POSITIONS IN CONTRACTS, INCLUDING ANY GAINS OR LOSSES, ANY INABILITY TO OPEN, MODIFY, OR CLOSE POSITIONS WHEN DESIRED; (D) ANY USE OF LEVERAGE OR MARGIN, INCLUDING ANY MARGIN CALLS (IF ANY), CHANGES TO INITIAL OR MAINTENANCE MARGIN REQUIREMENTS, POSITION LIMITS, LEVERAGE LIMITS, RISK TIERS, OR OTHER RISK CONTROLS, AND ANY LIQUIDATION, FORCED CLOSEOUT, PARTIAL LIQUIDATION, AUTO DELEVERAGING, BACKSTOP LIQUIDATION MECHANISMS, OR SIMILAR COMPANY ACTIONS; (E) ANY PRICING, VALUATION, OR SETTLEMENT DETERMINATIONS, INCLUDING ANY INDEX PRICES, MARK PRICES, FUNDING RATES OR SIMILAR PERIODIC PAYMENTS, REFERENCE PRICES, SETTLEMENT VALUES, OR ANY DISCREPANCIES BETWEEN SUCH PRICES AND PRICES AVAILABLE IN OTHER MARKETS, INCLUDING DURING PERIODS WHEN UNDERLYING MARKETS ARE CLOSED, HALTED, ILLIQUID, OR EXPERIENCING DISRUPTIONS; (F) ANY INACCURACIES, DELAYS, INTERRUPTIONS, ERRORS, OR OMISSIONS IN ANY DATA, QUOTES, MARKET INFORMATION, INDICES, CHARTS, CALCULATIONS, OR OTHER CONTENT DISPLAYED THROUGH THE SERVICES, WHETHER PROVIDED BY COMPANY OR A THIRD PARTY; (G) ANY APPLICATION OF, OR FAILURE TO APPLY, ANY MARKET INTEGRITY OR RISK CONTROLS, INCLUDING CIRCUIT BREAKERS, TRADING HALTS, PRICE BANDS, ORDER THROTTLES, ANTI MANIPULATION MEASURES, ANTI HIGH FREQUENCY ARBITRAGE PROTECTIONS, AND ANY RELATED CANCELLATION, VOIDING, ADJUSTMENT, OR CORRECTION OF ORDERS OR TRADES, INCLUDING PURSUANT TO ANY OBVIOUS ERROR OR TRADE ADJUSTMENT POLICY; (H) ANY MODIFICATION, SUSPENSION, INTERRUPTION, OR DISCONTINUATION OF THE SERVICES OR ANY COMPONENT THEREOF FOR ANY REASON, INCLUDING ANY OUTAGES, MAINTENANCE, CAPACITY LIMITATIONS, OR DEGRADED PERFORMANCE; (I) ANY DEPOSITS, WITHDRAWALS, OR TRANSFERS OF FUNDS OR COLLATERAL, INCLUDING ANY DELAYS, FAILURES, REJECTIONS, REVERSALS, CHARGEBACKS, OR HOLDS, AND ANY ACTIONS TAKEN BY COMPANY OR ITS SERVICE PROVIDERS IN CONNECTION WITH COMPLIANCE, SECURITY, FRAUD PREVENTION, OR RISK MANAGEMENT REVIEWS; (J) ANY CUSTODY OR SAFEGUARDING OF CUSTOMER FUNDS OR COLLATERAL, INCLUDING ANY OUTAGES, ERRORS, DELAYS, INSOLVENCY EVENTS, OR OTHER OPERATIONAL FAILURES OF CUSTODIANS, OR ANY LIMITATIONS ON ACCESS TO OR AVAILABILITY OF YOUR FUNDS OR COLLATERAL RESULTING THEREFROM; (K) YOUR DEVICES, NETWORKS, SYSTEMS, SOFTWARE, OR INTERNET CONNECTIVITY, INCLUDING ANY MALFUNCTIONS, DELAYS, OR SECURITY BREACHES AFFECTING YOUR ACCESS TO THE SERVICES OR YOUR ABILITY TO SUBMIT OR CANCEL ORDERS OR MANAGE POSITIONS; (L) ANY USER CONTRIBUTION OR ANY USE, MODIFICATION, DISTRIBUTION, OR RELIANCE ON SUCH MATERIAL; (M) ANY THIRD PARTY MATERIALS, SOFTWARE, OPEN SOURCE COMPONENTS, OR THIRD PARTY SERVICES USED IN CONNECTION WITH THE SERVICES, INCLUDING ANY MARKET DATA PROVIDERS, PAYMENT PROCESSORS, BANKING PARTNERS, CUSTODIANS, OR OTHER SERVICE PROVIDERS; (N) ANY ACTION OR INACTION TAKEN BY ANY OF THE COMPANY PARTIES, OR BY LAW ENFORCEMENT OR GOVERNMENT AUTHORITIES, DURING OR AS A CONSEQUENCE OF ANY INVESTIGATION OR WHEN SEEKING TO OBTAIN INFORMATION RELATED TO THE SERVICES OR OUR USERS; (O) YOUR FAILURE TO COMPLY WITH THESE TERMS OF USE, OR ANY APPLICABLE LAWS, REGULATIONS, OR THIRD PARTY TERMS, INCLUDING THOSE RELATING TO DERIVATIVES, SECURITIES, COMMODITIES, FOREIGN EXCHANGE, MONEY TRANSMISSION, SANCTIONS, CONSUMER PROTECTION, TAX, OR ADVERTISING; (P) ANY FEES OR CHARGES, WHETHER IMPOSED BY COMPANY OR ANY THIRD PARTY, IN CONNECTION WITH YOUR USE OF THE SERVICES, INCLUDING TRADING FEES, FUNDING RELATED CHARGES (IF ANY), LIQUIDATION FEES, CUSTODY FEES, OR DEPOSIT OR WITHDRAWAL FEES; (Q) ANY DECISIONS YOU MAKE OR ACTIONS YOU TAKE BASED ON INFORMATION PROVIDED THROUGH THE SERVICES, OR YOUR USE OF, OR RELIANCE ON, ANY CONTENT, MATERIALS, OR TOOLS; (R) ANY DISPUTES WITH OTHER USERS OR THIRD PARTIES ARISING EITHER BY YOUR ACTION OR INACTION OR THEIRS; OR (S) THE RISKS IDENTIFIED IN THESE TERMS OF USE REGARDING THE SERVICES, CONTRACTS, LEVERAGE, MARGIN, LIQUIDATION, PRICING, MARKET DISRUPTIONS, THIRD PARTY SERVICE PROVIDERS, AND ANY OTHER RISKS ASSOCIATED WITH YOUR USE OF THE SERVICES. ## 27. Governing Law and Arbitration All matters relating to the Services and these Terms of Use and any dispute or claim arising therefrom or related thereto (in each case, including non-contractual disputes or claims), shall be governed by and construed in accordance with the internal laws of the Republic of Panama without giving effect to any choice or conflict of law provision or rule (whether of the Republic of Panama or any other jurisdiction). All such disputes, claims and controversies arising hereunder, including regarding breach, termination, enforcement, interpretation, or validity thereof (and including the determination of the scope or applicability of this arbitration provision) shall be resolved by final and binding arbitration administered by JAMS in accordance with its Comprehensive Arbitration Rules and Procedures (the “Rules”) then in effect. The arbitration shall be conducted in Panama City, the Republic of Panama, before a single arbitrator selected in accordance with the Rules. Judgment on the award may be entered in any court having jurisdiction. Each party shall bear its own costs and attorneys’ fees, and the parties shall share equally the arbitrator’s fees and JAMS administrative fees, unless the arbitrator determines otherwise. THE PARTIES AGREE TO ARBITRATE SOLELY ON AN INDIVIDUAL BASIS, AND THAT THESE TERMS OF USE DO NOT PERMIT CLASS ARBITRATION OR ANY CLAIMS BROUGHT AS A PLAINTIFF OR CLASS MEMBER IN ANY CLASS OR REPRESENTATIVE ARBITRATION PROCEEDING. THE ARBITRATOR MAY NOT CONSOLIDATE MORE THAN ONE PERSON’S CLAIMS, AND MAY NOT OTHERWISE PRESIDE OVER ANY FORM OF A REPRESENTATIVE OR CLASS PROCEEDING. ## 28. Limitation on Time to File Claims ANY CAUSE OF ACTION OR CLAIM YOU MAY HAVE ARISING OUT OF OR RELATING TO THESE TERMS OF USE OR THE SERVICES MUST BE COMMENCED WITHIN ONE (1) YEAR AFTER THE CAUSE OF ACTION ACCRUES, OTHERWISE, SUCH CAUSE OF ACTION OR CLAIM IS PERMANENTLY BARRED. ## 29. Waiver and Severability No waiver by Company of any term or condition set out in these Terms of Use or other legal right shall be deemed a further or continuing waiver of such term, condition or right, or a waiver of any other term or condition, and any failure of Company to assert a right or provision under these Terms of Use shall not constitute a waiver of such right or provision. If any provision of these Terms of Use is held by a court or other tribunal of competent jurisdiction to be invalid, illegal or unenforceable for any reason, such provision shall be eliminated or limited to the minimum extent such that the remaining provisions of the Terms of Use will continue in full force and effect. ## 30. Entire Agreement The Terms of Use, our [Privacy Policy](/legal/privacy-policy) and any other terms or conditions incorporated herein by reference, collectively constitute the sole and entire agreement between you and the Company regarding the Services and supersede all prior and contemporaneous understandings, agreements, representations, and warranties, both written and oral, regarding the Services. ## 31. Your Comments and Concerns All notices of copyright infringement claims should be sent to [legal@qfex.com](mailto:legal@qfex.com) in the manner and by the means set out therein. All other feedback, comments, requests for technical support, and other communications relating to the Services should be directed to: [support@qfex.com](mailto:support@qfex.com). Company contact address: QFEX Inc., PH Atlantic Plaza, Cuatro Altos, Piso 2, Colón, Panamá. Legal enquiries and notices: [legal@qfex.com](mailto:legal@qfex.com). # Cookie Policy Source: https://docs.qfex.com/legal/cookie-policy Last Updated: 28 November 2025 # Cookie Policy This Cookie Policy explains how QFEX Inc. ("QFEX," "we," "us," or "our") uses cookies and similar technologies when you visit our website [qfex.com](https://qfex.com) and its subdomains. This policy explains what these technologies are, why we use them, and your rights to control our use of them. ## What are Cookies? Cookies are small data files that are placed on your computer or mobile device when you visit a website. Cookies are widely used by website owners to make their websites work more efficiently and to provide reporting information. Cookies set by the website owner (in this case, QFEX) are called **first-party cookies**. Cookies set by parties other than the website owner are called **third-party cookies**. Third-party cookies enable third-party features or functionality to be provided on or through the website (e.g., analytics, interactive content, and informational services). ## Why Do We Use Cookies? We use first-party and third-party cookies for several reasons: * **Essential Cookies:** Some cookies are required for technical reasons for our website to operate. * **Performance and Analytics:** We use cookies to analyze how our website is used. * **Functionality:** We use cookies to provide enhanced functionality and personalization. * **Informational Services:** We use cookies to provide relevant informational content and services. ## Types of Cookies We Use ### Essential Cookies | Cookie Name | Purpose | Duration | Type | | - | - | - | - | | Session ID | Maintains your session while using the website | Session | First-party | | Authentication | Keeps you logged in to your account | 30 days | First-party | | Security | Protects against cross-site request forgery | Session | First-party | | Load Balancing | Ensures optimal website performance | Session | First-party | **Legal Basis:** These cookies are necessary for the performance of our contract with you and to comply with legal obligations. ### Performance and Analytics Cookies | Cookie Name | Purpose | Duration | Type | | - | - | - | - | | Google Analytics | Analyzes website usage and performance | 2 years | Third-party | | Internal Analytics | Tracks page views and user journeys | 1 year | First-party | **Legal Basis:** We use these cookies based on our legitimate interest in improving our website and services. ### Functionality Cookies | Cookie Name | Purpose | Duration | Type | | - | - | - | - | | Language Preference | Remembers your language selection | 1 year | First-party | | Theme Preference | Remembers your display preferences | 1 year | First-party | | Form Data | Temporarily stores form information | Session | First-party | | Referral Tracking | Tracks referral program participation | 90 days | First-party | **Legal Basis:** We use these cookies based on our legitimate interest in providing you with a personalized experience. ### Informational and Educational Cookies | Cookie Name | Purpose | Duration | Type | | - | - | - | - | | Content Preferences | Remembers your informational content preferences | 90 days | First-party | | Educational Progress | Tracks progress through educational materials | 1 year | First-party | | Information Delivery | Enables delivery of relevant information | 90 days | Third-party | | Content Analytics | Measures engagement with informational content | 90 days | First-party | **Legal Basis:** We use these cookies based on your consent, which you can withdraw at any time. ## How to Control Cookies You have several options for managing cookies: ### Browser Settings Most web browsers allow you to control cookies through their settings preferences. You can set your browser to: * Block all cookies * Block third-party cookies only * Delete cookies when you close your browser * Notify you when a cookie is set > **Note:** If you choose to block or delete cookies, some features of our website may not work properly. ### Cookie Consent Manager We provide a cookie consent manager that allows you to: * Accept or reject different categories of cookies * Change your preferences at any time * View detailed information about each cookie You can access the cookie consent manager by clicking the **“Cookie Settings”** link in our website footer. ### Opt-Out Links You can opt out of certain third-party cookies by visiting: * [Google Analytics Opt-out](https://tools.google.com/dlpage/gaoptout) * Other Analytics: Contact us for information about opting out of other analytics services ### Do Not Track Some browsers include a “Do Not Track” feature. Currently, there is no industry standard for how to respond to Do Not Track signals, so we do not currently respond to these signals. ## Other Tracking Technologies In addition to cookies, we may use other tracking technologies: * **Web Beacons:** Small graphics with unique identifiers that track user behavior and email interactions. * **Local Storage:** Used to store information locally on your device for enhanced functionality and performance. * **Fingerprinting:** Collects information about your device and browser configuration to help identify and prevent fraud. ## Third-Party Services We work with third-party service providers who may set cookies on our website: ### Analytics Providers * **Google Analytics:** Provides website analytics and user behavior insights. ### Informational Service Providers * **Content Delivery Networks:** Provides efficient delivery of informational content. * **Educational Platforms:** Provides educational content and progress tracking. ### Customer Support * **Intercom:** Provides customer support chat functionality. * **Zendesk:** Provides customer support ticketing. ### Security Providers * **Cloudflare:** Provides security and performance optimization. * **reCAPTCHA:** Provides bot protection and security. ## International Transfers Some of our third-party service providers may transfer your data internationally. We ensure appropriate safeguards are in place for such transfers, including: * Standard Contractual Clauses * Adequacy decisions * Other appropriate safeguards as required by law ## Data Retention We retain cookie data for different periods depending on the type of cookie: * **Session cookies:** Deleted when you close your browser * **Persistent cookies:** Retained for the period specified in the cookie table above * **Analytics data:** Typically retained for 26 months * **Informational data:** Typically retained for 90 days to 2 years ## Your Rights Depending on your location, you may have certain rights regarding cookies and personal data: * **Right to withdraw consent:** You can withdraw consent for non-essential cookies at any time. * **Right to object:** You can object to processing based on legitimate interests. * **Right to access:** You can request information about cookies and data processing. * **Right to deletion:** You can request deletion of your data in certain circumstances. ## Children's Privacy Our website is not directed to children under 18, and we do not knowingly collect personal information from children under 18 through cookies. ## Updates to This Policy We may update this Cookie Policy from time to time to reflect changes in our practices or applicable law. We will notify you of material changes by: * Posting the updated policy on our website * Sending you an email notification * Displaying a notice on our website ## Contact Us If you have questions about this Cookie Policy or our use of cookies, please contact us: **Email:** [privacy@qfex.com](mailto:privacy@qfex.com) This Cookie Policy should be read in conjunction with our [Privacy Policy](https://qfex.com/privacy). # Privacy Policy Source: https://docs.qfex.com/legal/privacy-policy Last Updated: 6 October 2026 ## Introduction QFEX Inc. (“Company,” “we,” “us,” or “our”) respects your privacy and is committed to protecting it through our compliance with this privacy policy. This policy describes the types of information we may collect about you or that you may provide when you use our Services, and our data collection and handling practices with respect to the Services. This privacy policy is part of the [Terms of Use](/legal/client-agreement) applicable to the use of our Services. Please review this policy together with the [Terms of Use](/legal/client-agreement) closely, to understand all of your rights and obligations, and how we operate our Services. Any capitalized terms used but not defined in this policy shall have the meanings ascribed to them in the [Terms of Use](/legal/client-agreement). YOU ACKNOWLEDGE THAT WE MAY USE SERVICE PROVIDERS AND THIRD-PARTY TECHNOLOGIES, INCLUDING COOKIES, PIXELS, WEB BEACONS, ANALYTICS SERVICES, AND OTHER TECHNOLOGIES THAT TRACK YOUR ACTIVITIES AND COLLECT DATA ABOUT YOU AS YOU NAVIGATE OUR SERVICES. THESE THIRD PARTIES AND SERVICE PROVIDERS MAY INTERCEPT YOUR COMMUNICATIONS, COLLECT DATA ABOUT YOU, MONITOR YOUR INTERACTIONS WITH THE SERVICES, TRACK YOUR ACTIVITIES ON OUR SERVICES AND ACROSS DIFFERENT CHANNELS, AND MAY COLLECT ENOUGH INFORMATION ABOUT YOU AS YOU USE OUR SERVICES TO BE ABLE TO IDENTIFY YOU ON THEIR OWN PLATFORMS (E.G., SOCIAL MEDIA PIXELS). BY USING THE SERVICES, YOU SPECIFICALLY CONSENT TO THIS INTERCEPTION, TRACKING AND DATA COLLECTION. This privacy policy applies to information we collect: * On the Services. * In email, text, and other electronic messages between you and the Services, and other activities you engage in on the Services. * Through mobile and desktop applications, if any, that you download from the Services, if this policy is posted to them. * When you interact with our advertising and applications on third-party websites and services, if such advertising or applications include links to this policy. * Through any other means associated with or relating to the Services. This policy does not apply to information collected by: * Us offline or through any other means, including on any other website operated by Company or any third party (including our affiliates and subsidiaries). * Any third party, including through any application or content (including advertising) that may link to or be accessible from or on the Services. Please read this policy carefully to understand our policies and practices regarding your information and how we will treat it. If you do not agree with our policies and practices, your choice is not to use our Services. By accessing or using the Services, you agree to the terms of this privacy policy and consent to use of your personal information as described herein. **This privacy policy may change from time to time. Your continued use of the Services after we make changes is deemed to be acceptance of those changes, so please check the privacy policy periodically for updates.** ## 1. Age Restrictions Our Services are not intended for children under eighteen (18) years of age. No one under age eighteen (18) may provide any personal information to us or on or through the Services. We do not knowingly collect personal information from or about children under the age of sixteen (16). If you are under sixteen (16), do not use or provide any information on the Services or on or through any of their features or register on the Services (if such feature is available), engage in any transactions on the Services, use any of the interactive or public comment features of the Services (if such feature is available), or provide any information about yourself to us, including your name, address, telephone number, email address, or any screen name or user name you may use. If we learn we have collected or received personal information from a child under sixteen (16) years of age, we will delete that information, to the extent technologically feasible. If you believe we might have any information from or about a child under the age of sixteen (16), please contact us at [privacy@qfex.com](mailto:privacy@qfex.com). ## 2. Information We Collect About You and How We Collect It We and our service providers may collect several types of information from and about users of our Services (some of which is considered “personal information” pursuant to applicable law), including: * Identifiers, such as name, mailing address, e-mail address, telephone number, username, password, digital wallet address, or any other information that the Services collect, which applicable law may consider personally identifiable, personal information, personal data, and other such designations. * Your Feedback and quotations. * Usernames, screennames, handles, or other identifiers relating to your social media or blockchain accounts. * Information about the devices you use to access the Services. * Your internet protocol (IP) address. * Device identifiers such as an Apple Identifier for Advertisers or Android/Google Advertising ID, or another unique identifier. * Your device characteristics and functionality (including information about your operating system, hardware, mobile network, browser, browser language, etc.). * Information about software you may be using when connecting to or using the Services, including the type and version thereof. * Information about our Services that you are using (e.g., what version of our command line code). * Referring and exit web pages and URLs. * Your browsing history, including the areas within our Services that you visit and your activities there, including remembering you and your preferences. * Your digital wallet activity relating to supported deposits and withdrawals. * Information about your transaction history and associated fees paid or received. * Information about your digital assets. * Your device location or other geolocation information. * Certain other device data, including the time of day you visit our Services. * Information about your internet connection and internet provider. * Information included in any User Contribution. * Your activity on the Services, including your posts and any information contained therein. * Information collected through links you interact with across our Services (including in our communications sent to you). We and our service providers may collect this information: * Directly from you when you provide it to us and when you interact with the Services, such as when filling in forms on the Website or otherwise on the Services (including when you register for an account, or request something from us, if any such features are available to you). * Automatically as you use or navigate through the Services, including through the use of cookies, web beacons, and other tracking technologies (including information about your network or computing device) and analytics services. * From third parties, for example, our business partners and service providers. * Records and copies of your correspondence (including email addresses), if you contact us through the Services. * When you engage in transactions on our Services. * When you run searches on our Website. * When you contact our customer service agents, if available. ## 3. Automatic Data Collection Technologies As you navigate through and interact with our Services, we may use third-party automatic data collection technologies to collect certain information about you, your equipment, your technology providers, and your activities, including: * Details of your activities on and visits to the Services, including traffic data, browsing patterns, location data, logs, and other communication data, and the resources that you access and use on the Services. * Information about the software you use (including your own and ours). * Information about your device and about your internet connection and service provider, as set forth above. You acknowledge and understand that our service providers will be collecting this information about you, and may monitor and intercept your communications and actions on our Services as you browse, interact with or use certain functionality, such as chat features. These service providers may use the information they collect for their own benefit, without any restriction. You specifically agree to this arrangement, and consent to your communications and information being intercepted by our third party providers. We also may use these technologies to collect information about your online activities over time and across third-party websites or other online services (behavioral tracking). The information we collect automatically may be statistical data and may also include personal information, or we may maintain it or associate it with personal information we collect in other ways or receive from third parties. This information enables us to: * Allow you to use and access the Services. * Prevent fraudulent activity and improve security functionality. * Assess the performance of the Services, including as part of our analytic practices or otherwise to improve the content, products or services offered through the Services. * Offer you enhanced functionality when accessing the Services, including identifying you when you return or sign into our Services, and keeping track of your specified preferences. * Market our, our affiliates’ or third parties’ products or services to you. * Deliver content relevant to your interests on our Services and third-party sites based on how you interact with our advertisements and/or content. * Estimate our audience size and usage patterns. * Speed up your searches. * Analyze our services and products and perform market research. When you visit or leave our Services by clicking a hyperlink or when you view a third-party site that includes our plugins or cookies (or similar technology), we may automatically receive the URL of the site from which you came or the one to which you are directed. We may also receive location data passed to us from third-party services or GPS-enabled devices that you have set up, which we may use for fraud prevention and security purposes. We may also use this information to provide customized services, content, and other information that may be of interest to you, among other uses. If you no longer wish for us, our affiliates, or our service providers to collect and use location information, you may disable the location features on your device. Consult your device manufacturer settings for instructions on how to do this. Please note that if you disable such features, your ability to access certain features, services, content, or products may be limited or disabled. The technologies we use for this automatic data collection may include: * Cookies (or browser cookies). A cookie is a small file placed on the hard drive of your computer. Note that while you can set your browser to not allow cookies, we may not be able to honor that request, and may track your activity and collect information about you and your online activities even when the browser is set to “do not track.” * Flash Cookies. Certain features of our Services may use local stored objects (or Flash cookies) to collect and store information about your preferences and navigation to, from, and on our Services. Flash cookies are not managed by the same browser settings as are used for browser cookies and you may not be able to shut down our collection of and use of information through this technology. * Web Beacons, Pixels and Tags. Pages of our Services and our e-mails may contain small electronic files known as web beacons (also referred to as clear gifs, pixel tags, and single-pixel gifs) that permit us, for example, to count users who have visited those pages or opened an email and for other related website statistics (for example, recording the popularity of certain website content and verifying system and server integrity). * Embedded Scripts. An embedded script is programming code that is designed to collect information about your interactions with the Services, such as information about the links you click on. * ETag, or entity tag. An ETag, or entity tag, is a feature of the cache in browsers. It is an opaque identifier assigned by a web server to a specific version of a resource found at a URL. It is one of several mechanisms that HTTP provides for web cache validation. These allow websites to be more efficient and not serve content again when data is already cached and ready to view. * Fingerprinting. Fingerprinting refers to the collection and analysis of information from your device, such as your operating system, plug-ins, system fonts and other data, for purposes of identification. * Recognition Technologies. Recognition technologies refers to various technology features used by websites, including application of statistical probability to data sets, which attempt to recognize or make assumptions about users and devices (e.g., that a user of multiple devices is the same user). * Log Files. These track actions occurring on our Services, and which help us collect your IP address, browser type, Internet service provider, the webpages from which you came or to which you go before and after visiting our Services, and the date and time of your visits. ## 4. Behavioral Advertising We may use your personal information to provide you with targeted advertisements or marketing communications we believe may be of interest to you. For more information about how targeted advertising works, you can visit the Network Advertising Initiative’s (“NAI”) educational page at [http://www.networkadvertising.org/understanding-online-advertising/how-does-it-work](http://www.networkadvertising.org/understanding-online-advertising/how-does-it-work). You can opt out of certain targeted advertising, and learn more about your options related to such advertising, by using the links below: * NAI: Opt-Out of Interest Based Advertising by participating NAI members at: [https://optout.networkadvertising.org/](https://optout.networkadvertising.org/) * Digital Advertising Alliance: Opt-Out of Interest Based Advertising by participating DAA members at: [https://optout.aboutads.info/](https://optout.aboutads.info/) ## 5. Do Not Track Do Not Track (“DNT”) is a concept promoted by certain regulatory authorities and industry groups for development and implementation of a mechanism that would allow internet users to control the tracking of their online activities across websites. Currently, various browsers (including Internet Explorer, Firefox, and Safari) offer a DNT option that allows a user to set a preference in the browser to not have his/her activities on the internet tracked. You can usually access your browser's DNT option in your browser's preferences. When a user’s browser is set to DNT, some cookies and other tracking technologies may become inactive, depending on how the website visited responds to DNT browser settings. If that occurs, the website visited will not recognize you upon return to that website, save your passwords or user names, and some other features of a website may become unavailable or not function properly. Given lack of standards in the industry, and the industry’s move to newer technologies to track user preferences, our Services may not honor DNT settings at this time. ## 6. Third-Party Use of Cookies and Other Tracking Technologies Some content or applications, including advertisements, on the Services may be served by third-parties, including advertisers, ad networks and servers, content providers, and application providers. These third parties may use cookies alone or in conjunction with web beacons or other tracking technologies to collect information about you when you use our Services. The information they collect may be associated with your personal information or they may collect information, including personal information, about your online activities over time and across different websites and other online services. They may use this information to provide you with interest-based (behavioral) advertising or other targeted content. We do not control these third parties' technologies or how they may be used. If you have any questions about their use of your information, you should contact the responsible provider directly. ## 7. How We Use Your Information We may use information that we collect about you or that you provide to us, including any personal information: * To make the Services available. * To provide you with information, products, or services that you request from us. * To market our, our affiliates’ or third parties’ products or services to you, including by sending you marketing emails. * To enable our affiliates and third parties to serve you with advertisements on or through the Website. * To fulfill any other purpose for which you provide it. * To provide you with notices about your activities, account, and/or the Services. * To carry out our obligations and enforce our rights arising from any contracts entered into between you and us, including for billing and collection, if needed. * To notify you about changes to the Services, or any products or services we offer or provide through them. * To allow you to participate in interactive features on our Services, if any. * To develop and improve our products and services. * For behavioral tracking, profiling and advertising. * For any other purpose as needed for our business. * To provide customer support, if available. * In any other way we may describe when you provide the information. * For any other purpose with your consent. * To contact you about our own and third-parties' goods and services that we think may be of interest to you. * To enable us and our service providers to display advertisements to our advertisers' target audiences. ## 8. Disclosure of Your Information We may disclose aggregated information about our users, and information that does not identify any individual, without restriction. We may disclose personal information: * To our affiliates. * To contractors, service providers, and other third parties we use to support our business and assist us in providing services and offering our products and performing KYC. * To a buyer or other successor in the event of a merger, divestiture, restructuring, reorganization, dissolution, or other sale or transfer of some or all of Company's assets or stock, whether as a going concern or as part of bankruptcy, liquidation, or similar proceeding, in which personal information held by Company about our Services users is among the assets transferred. * To third parties to market their products or services to you. * To fulfill the purpose for which you provide it. * To provide customer support if such features are available. * For any other purpose disclosed by us when you provide the information. * With your consent. We may also disclose your personal information: * To comply with any court order, law, or legal process, including responding to any government or regulatory request. * To enforce or apply the [Terms of Use](/legal/client-agreement), including this privacy policy, and any other agreements between us, including for billing and collection purposes. * If we believe disclosure is necessary or appropriate to protect the rights, property, or safety of Company, our customers, or others. This includes exchanging information with other companies and organizations for the purposes of fraud protection and credit risk reduction. ## 9. Data Security We have implemented measures intended to secure your personal information from accidental loss and from unauthorized access, use, alteration, and disclosure. However, the safety and security of your information also depends on you. Where we have given you (or where you have chosen) a password for access to certain parts of our Website, you are responsible for keeping this password confidential. We ask you not to share your password with anyone and to change your password from time to time. We also highly recommend that you use a password that is dissimilar to and cannot be easily found by unauthorized third parties who may have obtained your login credentials to other sites. Keep in mind that if you use the same password for all websites, if someone obtains your credentials for one site, they may be able to then use those credentials to log into any other site you use. If you connect your digital wallet to the Services to access your account, you are responsible for keeping control of any seed phrase or keys confidential. We are not responsible for securing your seed phrase or keys if you lose them, if someone steals them from you, or if someone obtains access to them somehow (whether in transit to you, or from your systems or networks, or because you otherwise misplaced them or disclosed them). You also acknowledge, that, unfortunately, the transmission of information via the internet is not completely secure. Although we try to protect your personal information, we cannot guarantee the security of your personal information transmitted through or collected through the use of our Services. Any transmission of personal information is at your own risk. Also note that any deposits, withdrawals, or other transfers you make on public blockchains will be available to the public to see. We are not responsible for circumvention of any privacy settings or security measures contained on the Services. ## 10. California Residents California Civil Code Section § 1798.83, known as the “Shine The Light” law, permits users of our Services who are California residents to request and obtain from us a list of what personal information (if any) we disclosed to third parties for direct marketing purposes in the preceding year, and the names and addresses of those third parties. You may request this information from us no more than once a year, but such request will be handled by us free of charge to you. To make such a request, please send an email to [privacy@qfex.com](mailto:privacy@qfex.com). ## 11. Changes to Our Privacy Policy It is our policy to post any changes we make to our privacy policy on this page. If we make material changes to how we treat our users' personal information, we may post a notice on the Website home page. The date this privacy policy was last revised is identified at the top of this page. You are responsible for periodically visiting our Services and this privacy policy to check for any changes. ## 12. Contact Information To ask questions or comment about this privacy policy and our privacy practices, contact us at: E-mail: [privacy@qfex.com](mailto:privacy@qfex.com) Company: QFEX Inc. Contact address: PH Atlantic Plaza, Cuatro Altos, Piso 2, Colón, Panamá. # About QFEX Source: https://docs.qfex.com/qfex/about How Markets Should Be # What is QFEX? QFEX is the first hybrid perpetual futures exchange to allow **high leverage, performant trading on traditional financial assets**. Markets on QFEX operate 24/7 like crypto venues, with external market makers, allowing users to trade outside of market hours. QFEX utilizes a Central Limit Order Book, or ‘CLOB’, and maintains CEX-like features such as **microsecond-level latency, native fiat and crypto payment rails, anti-HFT arbitrage protections and circuit breakers**. In the long-term, QFEX aims to replace the entire \$100bn TradFi exchange, clearing and brokerage industry, with an ambition to be the trading exchange of choice for global banks, hedge funds and family offices, offering 24/7 access to a huge range of assets, both traditional and complex. # Who is the team? QFEX was founded by Cambridge mathematicians [Annanay Kapila](https://x.com/annanay) and [Joshua Wharton](https://x.com/joshuawharton). Annanay was previously one of the first members of [Flow Traders'](https://www.flowtraders.com) crypto team, and then a Quant at [Tower Research Capital](https://tower-research.com/), where he was responsible for double digit market share across all major crypto venues. Josh was an Engineer at [Citadel](https://www.citadel.com/). The rest of the team comes from [Jump](https://jumptrading.com), [HRT](https://www.hudsonrivertrading.com/), [Jane Street](https://janestreet.com), [Optiver](https://optiver.com), [SIG](https://sig.com) and more. # Who are your backers? QFEX has raised money exclusively from Silicon Valley VC firms, including General Catalyst and Y Combinator. ![](https://mintcdn.com/qfex/CKwerotS9HIYubBU/logo/fundraise.png?fit=max\&auto=format\&n=CKwerotS9HIYubBU\&q=85\&s=98705ff634e29ef597869cea71a91d51) # Agentic Trading Source: https://docs.qfex.com/qfex/agentic-trading Use AI agents to research markets, manage positions, and trade on QFEX. # Agentic Trading QFEX can be traded directly by AI agents. Agents can read live market data, inspect account state and positions, and place or manage orders using the [QFEX CLI](/api-reference/cli). The CLI is designed to work cleanly inside agent workflows: every command returns JSON, commands are stateless from the caller's perspective, and a background daemon manages the underlying QFEX WebSocket connections. This lets an agent interact with the exchange through simple shell commands rather than implementing exchange connectivity itself. ## Getting started Install the CLI: ```sh theme={null} brew install QFEX-org/tap/qfex ``` Then configure it for your agent: ```sh theme={null} # Global setup for Claude Code and Codex qfex agents init # Or add agent instructions to the current project only qfex agents init --local ``` `qfex agents init` configures Claude Code and Codex so they can use the QFEX CLI without repeatedly prompting for permission. The local mode writes agent instructions into the current project. Market data can be queried without credentials. To allow an agent to trade or access private account data, log in first: ```sh theme={null} qfex login ``` For the full installation guide, command reference, environments, authentication and subaccount controls, see the [QFEX CLI documentation](/api-reference/cli). ## What an agent can do An agent using the CLI can, among other things: * query BBOs, order books, trades, candles, mark prices, funding rates and open interest; * place, modify and cancel orders; * inspect open orders, positions, balances and leverage; * watch live market-data, order and fill streams; and * work with QFEX subaccounts to separate trading strategies or risk. For example: ```sh theme={null} qfex market bbo AAPL-USD qfex market orderbook AAPL-USD --depth 5 qfex position list qfex account balance qfex order place --symbol AAPL-USD --side BUY --type MARKET --tif IOC --qty 1 ``` ## Developing and testing agents QFEX provides both production and UAT environments. UAT uses the same interface as production but does not use real funds, making it the appropriate environment for testing agent behaviour and order flow before deploying a strategy to production. > [!WARNING] > > Production trading uses real funds. An agent with authenticated trading access > can place and cancel orders on your behalf. Test agent behaviour carefully in > UAT before granting it production access. ## Building agentic trading products Developers can also build trading agents or applications for other QFEX users. [Builder Codes](/qfex/builder-code) are designed to attribute trading activity to an integration and allow the builder to earn a share of the trading fees generated through that application. Builder Codes are separate from the referral program: referral codes attribute a user at signup, while Builder Codes attribute trading activity generated through an integration. For developers who need lower-level control, the QFEX API Reference also documents the REST and WebSocket interfaces used underneath the CLI. # Exchange Source: https://docs.qfex.com/qfex/architecture Technical Description of the Exchange ## Exchange Architecture QFEX operates as a perpetual futures exchange. Order matching, position management, margin calculations and liquidations are performed by QFEX’s offchain exchange infrastructure, while supported stablecoin deposits and withdrawals are executed through public blockchain networks. This architecture is designed to combine blockchain-native funding rails with the latency, throughput and order functionality required by professional market participants. QFEX operates as a Central Limit Order Book (“CLOB”) exchange, aggregating all buy and sell orders into a single transparent order book that displays the best available prices and quantities to all participants simultaneously. Orders are executed based on strict price-time priority rules, ensuring that the best-priced orders execute first, and among orders at the same price, those received earlier have priority (subject to a speed bump which is implemented to protect against latency arbitrage - see below). This architecture provides price transparency, guarantees fair execution, and promotes efficient price discovery by bringing all market participants together in a single venue. The exchange code is written primarily in Rust and runs on Amazon Web Services hosted in Tokyo. Both market data and order entry access is via GUI and API (websockets), with equal access on all terms for all clients. ## Emergency Exit QFEX separates its trading infrastructure from its custody and withdrawal rails. In normal operation, order matching, positions, margin calculations and liquidations are handled by QFEX’s infrastructure, while client stablecoins are held with US regulated custodians and withdrawals are paid over supported public blockchain networks. If the trading system becomes unavailable, or QFEX determines that normal trading cannot safely continue, QFEX may enter an Emergency Exit state. In this state, new order entry can be disabled, open positions can be closed or settled using the latest valid Mark Price under the Trading Rulebook, and client balances can be reconciled for withdrawal. The purpose of the Emergency Exit is to ensure that a failure of the matching or trading infrastructure does not require normal trading operations to continue before client collateral can be returned. ## Speed Bump Implementation QFEX implements a 500-millisecond speed bump on all limit orders to protect against latency arbitrage and create a level playing field for all participants. When a limit order is received, it is held in a queue before being sent to the order book. This delay applies uniformly regardless of the participant's technological sophistication or network connectivity. The speed bump applies to all non-ALO orders including new submissions and modifications, but not to any other request. This implementation reduces advantages gained through technological superiority alone and encourages broader market participation, typically leading to improved liquidity and more efficient price discovery. It is in line with standard industry practice to create a fair market place, and encourage the provision of liquidity. ## Self-trade Prevention Traders may not self-trade on QFEX. In the event of two orders from the same trader matching, the passive order will be canceled. # Bank Transfer Source: https://docs.qfex.com/qfex/bank-transfer Pay via Bank Transfer We support bank transfer funding in USD. Transfers must be domestic. ## Deposits * ACH deposit available (USD only). * Processing time: 1-3 business days. ### How To Deposit USD 1. Open **Deposit / Withdraw** from your account header. ![](https://mintcdn.com/qfex/RiwFQp0JUS2lYLtv/images/deposit-1.png?fit=max\&auto=format\&n=RiwFQp0JUS2lYLtv\&q=85\&s=d4daca0eba1dde4bd920209185f13141) 2. Select **Deposit funds**. ![](https://mintcdn.com/qfex/RiwFQp0JUS2lYLtv/images/deposit-2.png?fit=max\&auto=format\&n=RiwFQp0JUS2lYLtv\&q=85\&s=e7ce10bd205255ee249699c05eb0adad) 3. Choose **USD** to continue with bank transfer funding. ![](https://mintcdn.com/qfex/RiwFQp0JUS2lYLtv/images/deposit-3.png?fit=max\&auto=format\&n=RiwFQp0JUS2lYLtv\&q=85\&s=88aef2d2af6806742382fd66b8b7469e) ## Withdrawals * ACH withdrawal: 0.1% fee + \$1. * Wire withdrawal: 0.1% fee + \$10. * Processing time (standard): ACH 1-3 business days; wire same day to 1 business day. # Builder Codes Source: https://docs.qfex.com/qfex/builder-code Create a builder code, integrate QFEX trading, and earn builder rewards Builder codes let integrators and partners earn a share of trading fees when users trade through their application. Fills attributed to a builder code generate **builder rewards** for the code owner. This is separate from the [referral program](/qfex/referral). A referral code is used at signup. A builder code is attached when a user trades through an integration. ## How it works 1. A builder creates one builder code for its own QFEX account. QFEX generates the code as a UUID. 2. The builder configures two fee shares in basis points (1 bp = 0.01%): * QFEX's share of the trading fee must be at least **50%**. * The builder's share can be set up to **50%**. * The two shares do not need to add up to 100%. 3. The builder authenticates each end user separately, normally through QFEX OAuth. 4. The builder automatically attaches its builder-code UUID when it authenticates that user's Trade WebSocket connection. 5. Orders placed over that connection generate builder rewards according to the configured share. The trader still pays the full trading fee. You do **not** create a builder code for every trader, and a trader's QFEX account is not permanently assigned to a builder. Builder attribution is scoped to the Trade WebSocket connection. If the code is missing or invalid, the trader pays the full fee and nobody receives builder rewards. ## Create a builder code Each QFEX account may have **one active builder code**. 1. Sign in at [qfex.com](https://qfex.com) and open your profile. 2. Open **Developer Settings**. 3. Use the **Builder code** section to create your code and set the QFEX and builder fee shares. QFEX generates the UUID. Store it in your application's configuration and attach it automatically to attributed Trade WebSocket connections. ## Building an aggregator or trading application If your application needs to let users connect or create QFEX accounts and then trade through your integration, see the [Builder Integration Guide](/api-reference/builder-integration). The recommended flow is: 1. The user selects **Connect QFEX** in your application. 2. QFEX handles sign-in, account creation, MFA, and onboarding. 3. Your application receives a user OAuth token after authorisation. 4. Your application opens the user's authenticated Trade WebSocket. 5. Your application adds **your** builder UUID to that connection's auth message. 6. Orders, stop orders, and TWAPs sent over the connection are attributed automatically. Your application should never ask the user to hand over their QFEX password, MFA code, or API secret. ## Builder rewards The trader continues to pay the normal trading fee. The configured builder share is credited to the builder-code owner's account as builder rewards. For example, if QFEX's share is 50% and the builder share is 30%, QFEX keeps 50% of the trading fee, the builder receives 30% as builder rewards, and the remaining 20% is not paid out as builder rewards. ## For developers For the full OAuth-to-trading integration, see the [Builder Integration Guide](/api-reference/builder-integration). To create or retrieve builder codes over REST, attach them to Trade WebSocket sessions, or read builder rewards programmatically, see the [Builder Codes API reference](/api-reference/builder-codes). # Contract Specifications Source: https://docs.qfex.com/qfex/contract-specifications Perpetual Futures Adapted for Traditional Assets ## What are QFEX Perpetual Futures? A perpetual future is a derivative contract that allows you to speculate on the price of an underlying asset without ever having to physically own it. Unlike traditional futures contracts, **perpetuals do not have an expiry date**. You can hold a position for as long as you like, provided you maintain sufficient margin. Perps are the end state of leveraged finance. They are the most efficient and user-friendly way of trading all manner of asset classes. To ensure the price of the perpetual contract (Mark Price) stays close to the spot price of the underlying asset (Index Price), a mechanism called **Funding** is used. All QFEX Perpetual Futures are margined in [USDC](https://www.circle.com/usdc) on Arbitrum. USDC is compliant with [US Law](https://en.wikipedia.org/wiki/GENIUS_Act) and backed by [US Treasuries](https://www.circle.com/transparency). ## Index Price Sources The Index Price is the reference spot price for the asset. We aggregate data from high-quality sources: * **Equities:** Real-time feeds from US stock exchanges. * **Indices:** Derived from futures data. * **Precious Metals:** An aggregate of the most liquid ECNs. ## Oracle Pricing Our *default pricing* is: For non-equity indices: $$ P_t^{\text{oracle}} = P_t^{\text{index}} $$ For equity indices: $$ P_t^{\text{oracle}} = P_{\text{last close}}^{\text{equity index}} + \Delta F_t $$ where $\Delta F_t$ is the corresponding change in the equity index future. ## Futures-Based Oracle Rolls Some QFEX products derive oracle pricing from a dated futures contract and therefore roll from one contract month to the next on the published [Roll Schedules](/qfex/roll-schedules). For `CL-USD`, `COPPER-USD`, and `NATGAS-USD`, the contract blend shifts by 20 percentage points at each of the final five CME business-day boundaries before the roll completes. The [Roll Schedules](/qfex/roll-schedules) page contains the exact weights and published contract boundaries. Use `GET /settlement-calendar` to resolve the active dated future for a supported QFEX symbol. The response returns: * `future_symbol`: the futures contract currently used by the oracle. * `active_until`: the last timestamp at which that contract remains active for the QFEX symbol. * `expiration`: the expiration timestamp of that dated future. If `time` is omitted, the endpoint returns the currently active contract. For live integrations, use the QFEX settlement calendar endpoint rather than hardcoding roll dates. If the last known Index Price is *stale* (ie, the market is closed or otherwise down), we do the following: ### 1. Impact Notional Calculation The system calculates the Volume-Weighted Average Price (VWAP) to fill a specific "Impact Notional" amount (defined below) from the top of the order book. * **Impact Bid**: The average price to sell the impact notional amount into the bids. * **Impact Ask**: The average price to buy the impact notional amount from the asks. ### 2. Impact Price Deviation (IPD) IPD measures the pressure on the price relative to the *last traded price* (or last calculated price). It is calculated as: $$ IPD = \max(ImpactBid - P_{last}, 0) - \max(P_{last} - ImpactAsk, 0) $$ Where: * $P_{last}$ is the latest Index price. * Only "improving" deviations contribute to the IPD (i.e., if the Impact Bid is higher than current price, it pushes price up; if Impact Ask is lower, it pushes price down). ### 3. Price Update The price is updated using an Exponential Moving Average (EMA) model to smooth out volatility while drifting towards the order book pressure. The formula used is: $$ S_t = \beta_t \cdot S_{t-1} + (1 - \beta_t) \cdot x_t $$ Where: * $S_t$: New price. * $S_{t-1}$: Previous price. * $x_t$: Target price based on IPD, defined as $x_t = S_{t-1} + IPD_t$. * $\beta_t$: Decay factor, defined as $\beta_t = e^{-\Delta t^* / \tau}$. #### Time and Constants * **$\tau$ (Tau)**: Time constant, set to **30 minutes** (`1800` seconds). This controls the "inertia" of the price. * **$\Delta t$**: Time elapsed since the last update. * **$c$ (Clamp Factor)**: Set to **0.1**. * **$\Delta t^*$**: Clamped time delta, defined as $\min(\Delta t, c \cdot \tau)$. This prevents large jumps if updates are sparse. # Corporate Actions Source: https://docs.qfex.com/qfex/corporate-actions QFEX Policy on Equity Corporate Actions ## Overview Corporate actions are events initiated by a public company that affect the securities (equity or debt) issued by that company. For users familiar with crypto markets: * **Dividends** are analogous to **airdrops** or **staking rewards**. * **Stock Splits** are similar to **token re-denominations** (like the DOT split). ## General Policy QFEX **does not adjust** positions or margin for most corporate actions, including cash dividends. We rely on the efficient market hypothesis: the **market prices in** these events. The impact of a corporate action is naturally reflected in the Mark Price as traders adjust their valuations leading up to the event. ## Stock Splits and Reverse Splits Exceptions are made for stock splits and reverse stock splits to ensure position continuity and accurate PnL tracking. ### The Procedure When a stock split or reverse split occurs, QFEX executes the following process: 1. **Trading Halt**: Trading is suspended at the close of the post-market session on the day before the split effective date. 2. **PnL Realization**: All open positions have their PnL realized (settled) at the last known Mark Price. 3. **Re-opening**: Positions are immediately re-opened with adjusted parameters: * **Quantity**: Multiplied (or divided) by the split ratio. * **Mark Price / Entry Price**: Divided (or multiplied) by the split ratio. This mechanism ensures that the **notional value** of your position remains neutral across the split, preventing artificial liquidation or profit spikes. # Definitions and Formulae Source: https://docs.qfex.com/qfex/definitions Common Terms Used in the Rulebook ### Market Data Price The price of the underlying asset, derived from the relevant external oracle source.\ Denoted as $P_t^{\text{market}}$. ### Index Price When the current market price stays within the allowed band, the index price is: $$ P_t^{\text{index}} = P_t^{\text{market}} $$ when $$ P_t^{\text{market}} \in [0.5 P_{t-1}^{\text{market}},\; 1.5 P_{t-1}^{\text{market}}] $$ Otherwise, the index price is held at the prior market price: $$ P_t^{\text{index}} = P_{t-1}^{\text{market}} $$ That is, the Index Price equals the current Market Data Price if it lies within ±50% of the previous value, otherwise the prior price is used. ### Oracle Price Defined in [Contract Specifications](/qfex/contract-specifications): ### Mark Price The Mark Price $P_t^{\text{mark}}$ is computed as: $$ P_t^{\text{mark}} = \text{Median} \Big( P_t^{\text{oracle}},\; P_t^{\text{oracle}} + \text{EMA}_{150s}(M_t - P_t^{\text{oracle}}),\; \text{Median}(B_t,\; A_t,\; T_t) \Big) $$ where $M_t$ is the mid price, $B_t$ the best bid, $A_t$ the best ask, and $T_t$ the last traded price. ### Unrealized PnL $$ PnL_{\text{unrealized}} = \sum_i q_i \cdot (P_t^{\text{mark}} - P_{i,\text{entry}}) $$ for all open positions $i$ with quantity $q_i$. ### Realized PnL $$ PnL_{\text{realized}}= \sum_j (P_{j,\text{exit}} - P_{j,\text{entry}}) \cdot q_j $$ for all closed positions $j$. ### Cash $$ \text{Cash} = D - W + F_{\text{net}} + RR - \text{Fees} $$ where $D$ = deposits, $W$ = withdrawals, $F_{\text{net}}$ = net funding, $RR$ = referral rewards. ### Margin The total equity required to support open positions and reserve for open orders: $$ \text{Margin} = \text{Position Margin} + \text{Order Margin} $$ ### Position Margin The maintenance margin required to keep filled positions open, recalculated at the current Mark Price. Account Position Margin is the sum of the requirements for all open positions: $$ \text{Position Margin} = \sum_i |q_i| \times P_{i,t}^{\text{mark}} \times \frac{2}{3L_i} $$ where $q_i$ is the net position quantity and $L_i$ is the selected leverage for symbol $i$. The initial margin rate is $1/L_i$; the maintenance margin rate is two-thirds of that rate. Position Margin changes as positions are filled, reduced, or closed, and as Mark Prices change. It is used to assess whether the account has sufficient equity to maintain its positions; see [Margin and Liquidations](/qfex/liquidation-margin-call). ### Order Margin The equity reserved for the unfilled quantity of open orders, covering initial margin and a taker-fee reserve: $$ \text{Order Margin} = \sum_j |q_j^{\text{remaining}} \times P_j^{\text{order}}| \times \left(\frac{1}{L_j} + f_j^{\text{taker}}\right) $$ where $P_j^{\text{order}}$ is the order price, $L_j$ is the selected leverage for the order's symbol, and $f_j^{\text{taker}}$ is the applicable taker-fee rate. The sum includes only orders that require a margin reservation. Reduce-only orders reserve no Order Margin because they cannot increase exposure. Order Margin reduces Available Balance while an order is open. As an order fills, its reservation is released for the filled quantity and Position Margin is recalculated for the resulting position. Canceling an order releases its remaining reservation. The fee reserve is part of Order Margin; actual trading fees are charged on execution. For example, at 10× leverage, a filled position worth USD 1,500 at Mark Price requires USD 100 of Position Margin. An additional open order with USD 1,000 of unfilled notional and a 0.05% taker-fee rate reserves USD 100.50 of Order Margin. Total Margin is USD 200.50. ### Account Equity $$ \text{Equity} = \text{Cash} + PnL_{\text{realized}} + PnL_{\text{unrealized}} $$ ### Available Balance $$ \text{Available Balance} = \text{Account Equity} - \text{Margin} $$ ### Withdrawable Balance $$ \text{Withdrawable Balance} = \text{Cash} + PnL_{\text{realized}} + \min(PnL_{\text{unrealized}}, 0) - 1.05 \times \text{Margin} $$ ### Position Value $$ V = P_t^{\text{mark}} \times Q $$ where $Q$ is the position quantity in the symbol. ### Funding Rate The rate $r_t^{\text{funding}}$ paid between long and short positions when the futures price deviates from the underlying index. ### Funding Fee $$ \text{Funding Fee} = P_t^{\text{mark}} \times Q \times r_t^{\text{funding}} $$ ### Liquidation Spread The maximum spread $\Delta_{\text{liq}}$ from $P_t^{\text{mark}}$ at which an orderly **Immediate-or-Cancel (IOC)** liquidation order is executed. ### DLP Fee A fee $f_{\text{DLP}}$ paid to **Designated Liquidity Providers (DLPs)** who absorb liquidated positions. ### Mark-to-Market (MTM) All portfolios are re-evaluated every **200 ms** using the Mark Price $P_t^{\text{mark}}$ to: * Recalculate Unrealized PnL, * Detect margin requirement breaches, * Trigger liquidation procedures. # FAQ Source: https://docs.qfex.com/qfex/faq Frequently Asked Questions #### What is QFEX? QFEX is the first traditional assets exchange to offer high leverage, 24/7 trading of perpetual futures. #### How do I join QFEX? Anyone who meets QFEX's onboarding requirements can create an account without an invitation code. If you have an invitation code, enter it at signup to receive a trading-fee discount. See [Invitation code benefits](/qfex/referral#invitation-code-benefits) for details. #### What is QFEX's mission? QFEX aims to bring its proprietary, innovative exchange and risk infrastructure design to allow capital-efficient trading of futures even when the underlier is closed. In the long-term, QFEX aims to replace the entire \$100bn exchange, clearing and brokerage industry, with an ambition to be the trading exchange of choice for global banks, institutions and hedge funds, offering 24/7 access to a deep liquidity pool on a huge range of assets. #### Why are current tradfi exchanges broken? Modern market structure is still led by the Commodites Exchange Act in the US, which ultimately optimized for midwestern farmers. These farmers want expiries and don't care about leverage so much. The vast majority of futures volume now is done by speculators and institutions seeking efficiency. The products that have to cater to them and comply with the CFTC's idea of a fair market - like ES or GC - are missing the point. People don't want expiry; they want leverage, they want instant settlements, they want 24/7 access. #### What are the biggest architectural constraints in the traditional model? Intermediation. Traditional exchanges developed in a haphazard way, when the physical nature of brokers (pit traders), clearers (accountants) and exchanges (a physical pit) were totally different. Now, they are all software, and therefore capable of integration. Intermediation prevents true capital efficiency, adds cost and makes 24/7 operation extremely difficult. #### What are perpetuals and why are they different to futures? Perps were made popular by Bitmex, but are not necessarily anything to do with crypto. If you forget about calendar futures, perps are the natural way to facilitate trading with leverage. If you want to compare them to expiring futures, imagine an 8-hourly future that rolls without any cost. Why would you have a future expiring anyway? The answer is if the expiry corresponds to something - like the harvest of a crop, or arrival time of an oil tanker. This use case is now a minuscule fraction of futures trading volume. ### What charting do you use? The charting technology of QFEX is powered by TradingView, the most reputable platform offering advanced charts, a [stock screener](https://www.tradingview.com/screener/) and other features. #### How is QFEX different to a CFD platform? QFEX removes the broker from trading – brokers can increase costs for investors (by widening the spread), and manipulate the market, like by hunting for your stop losses. With QFEX, traders and investors can become the broker, able to set prices themselves and not rely on a market-maker. #### How do you incorporate dividends and other corporate actions? Dividends etc are handled much in the same way they are handled in crypto perps (eg, dividends are just handled by market makers), as all of these things have an analog in the crypto world (eg airdrops, hard forks etc). #### Does QFEX have LULD bounds and circuit breakers? LULD is an unsatisfactory way to slow down disorderly trading in equities. We follow the 'futures paradigm' of price limits at a fixed width around the last known underlier price. If the underlying equities hit a volatility stop, we will still allow trading around the last known price before the stop. The funding will be 'marked' to that last price, so people will have to pay funding in order to trade away from this price. #### What about tokenized equities? Tokenized equities and equity perps can exist side-by-side as they serve different use cases, as in all spot and derivatives markets (trading/ hedging vs long-term investing). The incremental value add of tokenizing equities vs holding the equities themselves is very low, and offset by blockchain security issues. QFEX concentrates solely on perps. #### Do you have historical data? Historical data will be available for download soon. #### What kinds of market maker protections do you offer? * A 500ms speed bump on all non-ALO add orders to prevent latency arbitrage (cancels and liquidity-adding orders are not affected). * A robust market manipulation detection system with 24/7/365 monitoring. #### How are your funding rates computed? Our funding formula is modelled on Binance's, with three notable differences: * Funding is computed and paid every 1 hour, not 8 hours. * There is no 1 basis point bias in the formula. * The final funding rate is scaled by `1/100` after the premium/deadband step. For full details on funding hours, why funding can be `0`, and calculation logic, see [Funding](https://docs.qfex.com/qfex/funding). Formula details are also available in [Contract Specifications](https://docs.qfex.com/qfex/contract-specifications). #### What are the restricted locations? Due to legal and regulatory reasons in certain major jurisdictions, we currently ban the onboarding of new users from many countries and locations, including but not limited to the following: * United States * United Kingdom * Spain * Ukraine * Syria * Iran * Russia * North Korea * Myanmar * Belarus * Venezuela * Sudan #### What products do you offer? We are rolling out USDC-margined perpetual futures across large cap US equities, indices and commodities. For the latest product set, always refer to the [Trading UI](https://qfex.com/trade). # Fees Source: https://docs.qfex.com/qfex/fees > [!NOTE] > > **Lifetime lowest fee tier for existing depositors** > > Starting at **00:01 UTC on Monday, 24 August 2026**, all users who have > already deposited will receive our lowest fee tier for life on the following > products: > > `USA500`, `SKHYNIX`, `US100`, `SPCX`, `SNDK`, `GOOGL`, `NVDA`, `MU`, `SKHY`, > `INTC`, `SAMSUNG`, `NBIS`, `CRCL`, `CBRS`, `META`, `TSLA`, `MRVL`, `AAPL`, > `AMZN`, `MSFT`, and `MSTR`. > > This means **0 bps maker fees** on all listed products, with taker fees of > **1.5 bps for equities** and **0.6 bps for indices**. QFEX uses a maker-taker fee model for determining its trading fees. Orders that provide liquidity (maker orders) are charged different fees than orders that take liquidity (taker orders). Fees are calculated based on the current pricing tier you are in when the order is placed, and not on the tier you would be in after a trade is completed. Your fee tier is based upon total USD trading volume over the trailing 30 day period. Transactions made on books quoted in USD, e.g. EUR-USD, are counted as the total USD amount of each filled order. When you place an order at the market price that gets filled immediately, you are considered a taker and will pay a fee between 0.006% and 0.10%. When you place an order which is not immediately matched by an existing order, that order is placed on the order book. If another customer places an order that matches yours, you are considered the maker and will pay a fee between 0.00% and 0.10%. When you place an order that gets partially matched immediately, you pay a taker fee for that portion. The remainder of the order is placed on the order book and, when matched, is considered a maker order. You pay a maker fee for this remaining portion of the total order. Please note that the pricing tiers can take some time to update. For future orders, we recommend checking your fee tier before you trade to ensure you are in the updated pricing tier. Integrators can earn a share of trading fees with a [builder code](/qfex/builder-code). Traders attach the code on the Trade WebSocket [auth message](/websocket/channels/trade/authenticate); the trader still pays the full fee, and the builder's configured share is credited as builder rewards. There are five tiers of fees, based on your Effective Volume for a trailing 30-day period traded on the platform. Effective Volume is calculated as follows: $$ \text{Effective Volume} = 2 \times \text{Commodities} + 2 \times \text{Indices} + 5 \times \text{Single stocks} $$ Where: * Commodities = volume traded against commodities futures (eg Gold) * Indices = volume traded against equity index futures (eg US100) * Single stocks = volume traded against single stock futures (eg PLTR) | Taker fees (%) / Tier | Effective Volume (volume traded on the platform in last 30 days) | Commodities | Indices | Single stocks | | :- | :- | - | - | - | | Tier 5 | \<2m\$ | 0.05% | 0.05% | 0.10% | | Tier 4 | \$2m - \$10m | 0.04% | 0.04% | 0.08% | | Tier 3 | \$10m - \$100m | 0.02% | 0.02% | 0.05% | | Tier 2 | \$100m - \$400m | 0.01% | 0.01% | 0.02% | | Tier 1 | \$400m+ | 0.006% | 0.006% | 0.015% | | Maker fees (%) / Tier | Effective Volume (volume traded on the platform in last 30 days) | Commodities | Indices | Single stocks | | :- | :- | - | - | - | | Tier 5 | \<2m\$ | 0.02% | 0.02% | 0.05% | | Tier 4 | \$2m - \$10m | 0.015% | 0.015% | 0.04% | | Tier 3 | \$10m - \$100m | 0.01% | 0.01% | 0.025% | | Tier 2 | \$100m - \$400m | 0.005% | 0.005% | 0.01% | | Tier 1 | \$400m+ | 0% | 0% | 0% | # Funding Source: https://docs.qfex.com/qfex/funding When funding is active, why it can be zero, and how rates are computed. Funding keeps perpetual prices anchored to their underlier. QFEX supports 24/7 trading, but funding is only active when we can reliably consume underlier prices from primary venues. ## At a Glance * Funding is calculated and settled every **60 minutes**. * Funding is only active during a symbol's configured `market_hours`. * Outside those hours, funding rate is **`0`** and no funding payment is exchanged. * The live websocket `funding` stream shows the **current implied final rate** if the current window ended now. ## Funding Hours (Current) | Markets | Underlier venue/source | Funding-active hours | | - | - | - | | Most USD equities | US securities exchanges | Sunday 20:00–Friday 20:00 ET | | Korean equities | Korea Exchange | Weekdays 09:00–15:30 KST | | `SIVE-SEK` | Nasdaq Stockholm | Weekdays 09:00–17:00 Europe/Stockholm | | `GOLD-USD` and `SILVER-USD` | Relevant metals venues | Sunday 17:00–Friday 17:00 ET | | `CL-USD`, `US100-USD`, and `US500-USD` | US futures exchanges | Sunday 17:00–Friday 16:00 CT, with a daily 16:00–17:00 pause | | Other active commodities and indices | Relevant reference venues | Sunday 17:00–Friday 20:00 ET | See the [Specification Index](/qfex/specification-index) for the consolidated per-symbol schedule. ## Per-Symbol Funding Hours in Refdata Always use refdata as the source of truth for symbol-specific funding hours: * REST: `https://api.qfex.com/refdata`, docs [here](/websocket/channels/mds/refdata) Look at the `market_hours` object for each symbol. ```json theme={null} { "symbol": "AAPL-USD", "market_hours": { "tz": "America/New_York", "monday": [ { "open": "00:00:00", "close": "23:59:59", "session": "open" } ] } } ``` ## Methodology Our calculation methodology closely follows the industry standard, with simplified parameters for greater transparency and efficiency. **Key Parameters:** * **Funding Interval:** Every **60 minutes**. * **Impact Notional:** **1,000 USDC** by default. `GOLD-USD` and `US100-USD` use **10,000 USDC**. * **Interest Rate:** **None**. ### 1. Premium Index The Premium Index ($P$) represents the premium or discount of the contract relative to the spot price. It is calculated using the **Impact Bid** and **Impact Ask** prices from the orderbook. $$ P = \frac{\max(0, \text{Impact Bid} - \text{Index}) - \max(0, \text{Index} - \text{Impact Ask})}{\text{Index}} $$ Where: * **Impact Bid:** The average fill price to sell the Impact Notional amount. * **Impact Ask:** The average fill price to buy the Impact Notional amount. ### 2. Funding Rate The final Funding Rate applied at the end of each 60-minute interval is based on a **Time-Weighted Average (TWAP)** of the Premium Index over that interval. This method assigns higher weight to more recent premium observations. > **Note:** In the formula below, $\bar{P}$ and $F_{\text{raw}}$ are expressed in **basis points (bps)**. $$ \bar{P} = \frac{\sum_{i=0}^{N-1} (i+1) \cdot P_i}{\sum_{i=0}^{N-1} (i+1)} $$ $$ F_{\text{final}} = \frac{\bar{P}}{100} $$ Where: * $P_i$ represents the premium samples. * The weight $(i+1)$ increases for more recent samples (where $i=N-1$ is the most recent). * $F_{\text{final}}$ is the funding rate applied for payments and published on the funding stream. *Note: Unlike other exchanges, we do not apply a fixed interest rate component. The rate is purely the market-driven premium.* ## Why Funding May Be 0 Funding can be zero for normal reasons: * The symbol is outside its `market_hours` (market closed, weekend, or holiday). ## Historical Funding Data We are actively improving historical data coverage, including funding history by product. For live values, use the websocket `funding` channel. # Margin and Liquidations Source: https://docs.qfex.com/qfex/liquidation-margin-call A Detailed Description of the Liquidation Cascade QFEX is disrupting the traditional way that futures are cleared to bring capital efficiency and leverage never seen before to traditional markets. Traditional, intermediated markets hide the risk of leverage amongst their brokers, and try to regulate away tail cases with high capital requirements, leading to poor UX. We have created innovative, fair and transparent **multi-stage de-risking procedure** that prioritizes the safety of a user's wallet balance in the event of a margin call. Users can also set their leverage level to be lower than the maximum to improve their safety. All QFEX products are **cross-margined**. ## Margin Call and Close-Out Procedure Margin is monitored continuously: * All users are evaluated simultaneously and on equal terms. * An account that restores sufficient equity is not liquidated. ### Initial Steps **Condition:** `Account Equity < Maintenance Margin` #### Order margin release and soft liquidation When the account enters the liquidation procedure, QFEX can convert its resting orders to reduce-only to release order margin. This happens automatically, without an order modification request from you. In the most severe cases, resting orders are cancelled outright before the netting and off-orderbook steps below. For the reduce-only conversion, the flow is: 1. Each affected resting order is modified in place. Its order ID, price, remaining quantity and price/time priority are preserved; it now may only reduce an existing position. 2. QFEX sends an order modification update for each newly converted order. Orders already marked reduce-only do not receive another conversion update. 3. Order margin is released. If this restores sufficient margin, no further liquidation action is needed; otherwise, the liquidation procedure continues below. 4. At execution, the order can fill only against the position still available to reduce. It cannot increase a position or reverse it. If there is no position to reduce, or the order is on the wrong side, it can be cancelled when it would otherwise match. If only part can reduce the position, that part can fill and the remaining quantity is cancelled. Restoring sufficient margin does not automatically switch converted orders back to ordinary orders. Check their current reduce-only setting before relying on them to open or increase a position. For example, you are long 5 units and have a resting sell order for 8 units. After conversion, the order can sell at most the 5 units still available to close; the remaining 3 are cancelled. If another trade or liquidation closes the position first, the sell order can be cancelled without a fill even when the market reaches its price. A resting buy order would also be unable to fill while you are long, because it would increase the position. ##### What website users see * In the trading terminal's Open Orders table, the affected order remains open and its Reduce Only column changes to Yes. * Subject to your trading notification preferences, the conversion produces an “Order modified” notification. This notification can appear even though you did not edit the order; it does not specifically say “soft liquidation”. * If the order later cannot reduce your position, it leaves Open Orders when cancelled. Trading notifications may show “Order cancelled” or “Order partially filled and cancelled”, depending on whether any quantity filled. Check Order History and your current position to understand the outcome. A price touching your order is therefore not enough to guarantee a fill: the order must still be able to reduce your position at execution time. ##### What programmatic users receive Subscribe to the authenticated WebSocket `order_responses` channel. Each conversion is pushed as an `order_response` with `status: "MODIFIED"`, `reduce_only: true`, and `order_origin: "SET_REDUCE_ONLY_ALL"`, using the existing `order_id` and `client_order_id`. Process this unsolicited update even if you have no pending modify request. A later cancellation is a separate `CANCELLED` update; the conversion itself is not a cancellation or a fill. See [Order responses — automatic reduce-only conversion](/websocket/channels/trade/order#automatic-reduce-only-conversion) for a message example, client handling and reconnect guidance. #### Netting of Liquidated Positions * Opposite liquidated positions are **netted internally** between accounts. * Trades are booked at current Mark Price. **Example:** > Account 1: Long 100 units of Symbol Y\ > Account 2: Short 100 units of Symbol Y\ > → Positions netted, no order book impact. ### Next Steps ##### Orderbook Liquidations **Condition:** `Maintenance Margin / 2 < Account Equity < Maintenance Margin` * Compute notional amount to liquidate. * Cap notional amount to ensure that the overall amount of liquidation quantity submitted to the orderbook does not cause the 60-second EMA of this quantity to exceed the Orderbook Liquidation Limits. * Enter limit IOC orders at `Mark Price ± Liquidation Spread`. ##### Off-Orderbook Liquidation **Condition:** `Account Equity ≤ Maintenance Margin / 2` * Positive Account Equity drained into the **Reserve Fund**. * Negative equity covered from the Reserve Fund (if sufficient). * Positions transferred to Designated Liquidity Provider (DLP) participant at Mark Price, up to the maximum quantity such that the DLP Fee can be covered by the Reserve Fund. * Reserve Fund covers DLP Fees (which are 0 if the trade was risk-decreasing for the Designated Liquidity Provider). ##### Auto-Deleveraging **Condition:** `Account Equity ≤ Maintenance Margin /2` and Off-Orderbook Liquidation incomplete. Procedure: * Rank accounts with the opposite-side position on the required symbol by `(Account Equity / Position Maintenance Margin)`. * Net off positions against the opposite side accounts, in rank order, ensuring that the liquidated account is flattened with 0 Account Equity. ## Margin Call Notifications Clients receive alerts (email + browser) when margin falls below: * **75%** of Initial Margin (warning) * **66.6%** of Initial Margin (close out notification) ## Designated Liquidity Provider (DLP) Program An opt-in program for qualified market makers to absorb liquidated positions efficiently when the orderbook cannot. Allocation is **pro-rata** to Designated Liquidity Providers by available margin capacity. * Provider positions must remain within position limits. * Providers receive a **DLP Fee** for each liquidation absorbed. * Pesignated Liquidity Provider operations follow strict risk control and monitoring standards. ## Orderbook Liquidation Limits Orderbook liquidation limits vary by symbol and market condition. They are enforced against a 60-second exponential moving average of liquidation notional. ## Reserve Fund and Stress Testing Regular stress testing of the Liquidation Protocol ensures that the system can handle extreme market conditions while maintaining adequate Reserve Fund levels. The exchange monitors liquidation frequency and effectiveness to optimize protocol parameters and ensure continued system resilience. # Managed Account Strategy Source: https://docs.qfex.com/qfex/managed-account Invite-only systematic market-neutral trading for eligible high-volume QFEX traders. # Managed Account Strategy QFEX launched its managed account strategy on **1 August 2026**. The strategy is available on an **invite-only basis** to high-volume traders who have deposited at least **USD 50,000 on QFEX**. ## Overview The strategy is a systematic, market-neutral long/short strategy traded through QFEX perpetual futures. It is designed to target relative-value opportunities across the cross-section of markets rather than taking a directional view on the equity market. The portfolio is constructed end to end by systematic models. Signals are generated from more than 200 features spanning areas including momentum, mean reversion, volatility, relationships between markets, and liquidity. Forecasts are normalized across markets, positions are ranked and sized according to signal strength and inverse volatility, and the resulting book is neutralized to target zero net exposure and zero index beta before orders are sent. The book is closed out by **16:00 ET each day**, with nothing intentionally held overnight. ## Why QFEX perpetuals QFEX perpetual futures allow long and short positions to share one cross-margined collateral pool. This means a market-neutral book can generally use capital more efficiently than an equivalent portfolio held through a traditional prime broker, where long and short legs may consume margin separately and short positions can require stock borrow. The strategy typically operates at approximately **2-4x gross notional per dollar of equity**, while remaining market-neutral by construction. ## Account structure The strategy is run as a separately managed account: * **Your collateral remains in your own QFEX account.** QFEX receives trading access only and does not receive withdrawal rights. * **Positions are transparent.** You can view positions in real time, and the book is flattened each day. * **There is no lock-up.** Because the strategy holds nothing overnight, participation can be stopped within a single trading session. ## Manage your account with API keys Invited users can create a managed account, deposit from their main account, withdraw back to it, and change portfolio leverage through `https://api.qfex.com`. Create an [API key](/api-reference/api-keys) with All accounts access, which grants the required `subaccount_ops` permission. Use your own key and keep its secret on your server. Fund your primary QFEX account first using [programmatic deposits](/api-reference/programmatic-funding). Managed account funding transfers existing USD collateral from your primary account. Check `GET /managed-accounts/config` for the current `min_funding` and `max_leverage`: initial funding and equity after each deposit must meet the minimum, and `leverage_ratio` must be an integer from `1` to the maximum. Set `QFEX_PUBLIC_KEY`, `QFEX_SECRET_KEY`, and `QFEX_MAIN_ACCOUNT_ID` (your primary account UUID) in your environment, then run this Python example. The example assumes a minimum funding requirement of USD 50,000 and a maximum leverage of at least `3`. Run the withdrawal outside managed account trading hours. ```python theme={null} import hashlib import hmac import json import os import secrets import time from urllib.request import Request, urlopen def post(path, body): nonce = secrets.token_hex(16) timestamp = str(int(time.time())) signature = hmac.new( os.environ["QFEX_SECRET_KEY"].encode(), f"{nonce}:{timestamp}".encode(), hashlib.sha256, ).hexdigest() request = Request( "https://api.qfex.com" + path, data=json.dumps(body).encode(), headers={ "Content-Type": "application/json", "x-qfex-public-key": os.environ["QFEX_PUBLIC_KEY"], "x-qfex-hmac-signature": signature, "x-qfex-nonce": nonce, "x-qfex-timestamp": timestamp, }, method="POST", ) with urlopen(request) as response: data = response.read() return json.loads(data) if data else None # Create and fund with USD 50,000 at 2x portfolio leverage. account = post("/managed-accounts/create", {"amount": 50000, "leverage_ratio": 2}) main_id = os.environ["QFEX_MAIN_ACCOUNT_ID"] managed_id = account["account_id"] # Deposit USD 1,000 from the main account into the managed account. post( f"/user/transfer?src_account_id={main_id}&dst_account_id={managed_id}", {"amount": 1000}, ) # Withdraw USD 500 from the managed account back to the main account. post( f"/user/transfer?src_account_id={managed_id}&dst_account_id={main_id}", {"amount": 500}, ) # Change portfolio leverage to 3x. post("/managed-accounts/leverage", {"leverage_ratio": 3}) ``` Creation returns `201` with `created: true`. You can also add funds with `POST /managed-accounts/create` and `{"amount": 1000}`; it returns `200` with `created: false` and preserves leverage. Both responses include `account_id` and `leverage_ratio`. `POST /user/transfer` returns `200` with an empty body. Managed account transfers must be between your main account and its managed account. Withdrawals return `409` while managed account trading is active. To withdraw externally, transfer back to the main account first, then use [programmatic withdrawals](/api-reference/programmatic-funding#withdraw-crypto). Each deposit or withdrawal call transfers money again, so check balances before retrying a timed-out request. ## Historical strategy performance The brochure for the strategy reports the following net statistics over 249 trading days from 2 September 2025 to 27 August 2026 on an unlevered book: | Metric | Net | | - | -: | | Annualized return | 29.6% | | Sharpe ratio | 3.01 | | Sortino ratio | 4.64 | | Daily hit rate | 59.0% | | Largest single-day loss | -3.01% | | Index beta | 0.00 | > [!NOTE] > > These figures pre-date the launch of the managed account product itself. They are based on the production model using live market data on an unlevered book and are not audited returns of a pooled investment vehicle. ## Eligibility Managed accounts are currently available only by invitation. To be eligible, a trader must: * be a high-volume QFEX trader; and * have deposited at least **USD 50,000** on QFEX. Eligibility does not by itself constitute an invitation to participate. > [!WARNING] > > Past performance is not indicative of future results. Trading perpetual futures with leverage involves substantial risk of loss. This page is for informational purposes only and is not an offer to sell or a solicitation to buy any security or investment interest. # Market Makers Source: https://docs.qfex.com/qfex/market-makers Institutional connectivity enquiries # Market makers If you are a market maker and want to discuss institutional connectivity to QFEX, contact [mm@qfex.com](mailto:mm@qfex.com). Please include relevant details about your firm, your integration requirements, and the markets you are interested in. # Order Entry Source: https://docs.qfex.com/qfex/order-types Supported Order Types and Times in Force ## Order Types ### Market Orders * Execute immediately at the best available price. * Provide **certainty of execution** but not certainty of price. * Cannot include **Good-Till-Cancelled (GTC)** instructions. ### Limit Orders * Specify both **quantity** and **price limit** (max for buy, min for sell). * If not filled immediately, rest in the order book at the specified price. ### Add Liquidity Only (ALO) Orders * Execute **only if they add liquidity**. * If an ALO order would immediately match with an existing order, it is **cancelled** instead. Any of the above types can optionally specify a `take_profit` and `stop_loss` price. This stages *Stop Order(s)* at the exchange: ### Stop Orders * Must be either `take_profit` or `stop_loss`. * Convert to a Market Order when the Mark Price reaches the specified price. * Act only to decrease position at the time of execution; otherwise, the order could be rejected, or other stop orders cancelled. ## Reduce-only orders A reduce-only order can close or decrease an existing position, but cannot increase it or open a position in the opposite direction. Execution is limited by your position at the time of the match. An order can therefore be partially filled and have its remainder cancelled, or be cancelled without a fill if it no longer reduces your position. QFEX can also automatically convert resting orders to reduce-only during soft liquidation, even if you submitted them as ordinary orders. See [order margin release and soft liquidation](/qfex/liquidation-margin-call#order-margin-release-and-soft-liquidation) for the sequence of events, what appears on the website and what API clients receive. ## Time In Force ### Fill-or-Kill (FOK) * Must execute **entirely and immediately**, or be cancelled. * Suitable for **large block trades** or hedging strategies. ### Immediate-or-Cancel (IOC) * Executes any **immediately available portion**, cancelling the rest. * Reduces risk of resting orders in volatile markets. ### Good-Till-Cancelled (GTC) * Remains active until executed or manually cancelled. * Applies only to **limit** and **ALO** orders (not market orders). * Persists across sessions. ### Instruction Availability Table | Order Type | FOK | IOC | GTC | | - | - | - | - | | **Market Orders** | ❌ | ✅ | ❌ | | **Limit Orders** | ✅ | ✅ | ✅ | | **ALO Orders** | ❌ | ❌ | ✅ | | **Stop Orders** | ❌ | ✅ | ❌ | # Pre-IPO Stock Perpetuals Source: https://docs.qfex.com/qfex/pre-ipo-stocks How QFEX prices, funds, and adjusts perpetuals referencing companies before an IPO. QFEX may list perpetual futures referencing private companies before their initial public offering (IPO). These contracts let traders take a view on a company's implied equity valuation before a public share price exists. A pre-IPO stock perpetual is a derivative. It does **not** represent ownership of the underlying company and does not provide shares, voting rights, dividends, IPO allocations, or any other shareholder rights. ## At a Glance * Pre-IPO stock perpetuals trade **24 hours a day, 7 days a week**. * Funding is calculated and settled every **60 minutes**, including nights and weekends. * The corresponding **Binance pre-IPO perpetual Mark Price** is used as the oracle. * The initial Reference Share Count is **1,000,000,000 shares**. * A stock-split-style adjustment is made only when an updated share count differs from the current Reference Share Count by **more than 3%**. * Any adjustment changes price and position quantity in opposite directions so that position notional and PnL are not changed merely by the revised share count. ## Pricing Convention Before the IPO, one contract is priced as a per-share equivalent based on a Reference Share Count of: $$ S_{\text{ref}} = 1{,}000{,}000{,}000 $$ If $V_t$ is the company's implied equity valuation, the per-share-equivalent price is: $$ P_t = \frac{V_t}{S_{\text{ref}}} $$ Equivalently, the valuation implied by the contract price is: $$ V_t = P_t \times S_{\text{ref}} $$ For example, a contract price of **\$200** implies an equity valuation of **\$200 billion** under the one-billion-share convention. The Reference Share Count is a contract-pricing convention. It is not a representation or guarantee of the number of shares that the company will actually have at IPO, its IPO valuation, or its eventual public trading price. ## Oracle and Funding For each QFEX pre-IPO stock perpetual, the oracle price is the Mark Price of the corresponding Binance pre-IPO perpetual: $$ P_t^{\text{oracle}} = P_t^{\text{Binance Mark}} $$ The Binance Mark Price is used as the external reference price for QFEX's Mark Price, funding, margin, and liquidation calculations. It is distinct from the last traded price on QFEX. Pre-IPO stock perpetuals have continuous `market_hours`, so funding remains active **24/7**. Funding is calculated and settled every 60 minutes using the standard QFEX [Funding methodology](/qfex/funding), with the Binance Mark Price serving as the oracle input. ## Share-Count Adjustments When QFEX adopts an updated share count, it compares that number with the contract's current Reference Share Count. Let: * $S_{\text{old}}$ be the current Reference Share Count. * $S_{\text{new}}$ be the updated Reference Share Count. A stock-split-style adjustment occurs only if: $$ \left|\frac{S_{\text{new}}}{S_{\text{old}}} - 1\right| > 3\% $$ For the initial one-billion-share convention, no adjustment is made when the updated share count is between **970,000,000 and 1,030,000,000 shares**, inclusive. QFEX will announce the updated Reference Share Count and the effective time of any adjustment. The updated count may be informed by official IPO filings and changes to the corresponding Binance contract specification. ### Adjustment Method Define the adjustment ratio as: $$ R = \frac{S_{\text{new}}}{S_{\text{old}}} $$ At the effective time: $$ Q_{\text{new}} = Q_{\text{old}} \times R $$ $$ P_{\text{new}} = \frac{P_{\text{old}}}{R} $$ Entry prices and other per-contract price values are adjusted by the same price factor. This preserves position notional: $$ Q_{\text{new}} \times P_{\text{new}} = Q_{\text{old}} \times P_{\text{old}} $$ It also preserves unrealized PnL immediately across the adjustment, apart from immaterial rounding differences. ### Example: Share Count Increases Assume the Reference Share Count changes from **1.0 billion** to **1.25 billion** shares. The adjustment ratio is: $$ R = \frac{1.25}{1.00} = 1.25 $$ A position of **100 contracts** with an entry price of **\$180** and a Mark Price of **\$200** becomes: * **125 contracts**; * an adjusted entry price of **\$144**; and * an adjusted Mark Price of **\$160**. Before the adjustment, the position notional is \$20,000 and unrealized PnL is \$2,000. Immediately after the adjustment, both amounts remain the same. If the Reference Share Count decreases, the process operates in reverse: position quantity decreases while per-contract prices increase proportionally. This treatment follows the economic logic of a stock split or reverse stock split. See [Corporate Actions](/qfex/corporate-actions) for the general QFEX approach to split-style adjustments. ## IPO and Oracle Risk Pre-IPO companies may change their capital structure, delay or cancel an IPO, or list with a valuation materially different from the valuation implied by the contract. The Binance Mark Price may also differ significantly from the eventual public share price. QFEX will announce separately any post-IPO transition, oracle change, contract adjustment, suspension, or delisting. Traders should not assume that a pre-IPO contract will automatically convert into a standard stock perpetual on unchanged terms. # Blocked Countries and Restrictions Source: https://docs.qfex.com/qfex/prohibited-jurisdictions Location plays a major role determining which customers a platform is allowed to serve. For details on the countries and regions where QFEX cannot onboard users, see the list of [restricted locations](./faq#what-are-the-restricted-locations). # Referral and Affiliate Program Source: https://docs.qfex.com/qfex/referral Two commission programs: open referrals and approved affiliates QFEX has 2 programs: **Referrals**, which anyone can join, and **Affiliates**, which require approval. Commission tiers are assessed daily using the previous 30 days of **direct referred trading volume**. | | Referrals | Affiliates | | - | - | - | | Access | Open to everyone | Whitelisted by QFEX | | Referral link | Random link | Custom username link | | Invitations | Own trading earns more invitations | Unlimited referred users | | Commission | Multi-level retail tiers | Choose the network or performance model | ## Referrals Anyone can share their referral link. Your own trading volume earns you more invitations under the existing invitation system. The dashboard shows **invitations generated** and **invitations claimed**; you do not need to create a new code for each invitation. The person you refer receives a **10% fee discount, processed as a refund at or around 00:10 UTC.**. Your commission tier depends on the trading volume of your **direct referrals** over the rolling 30-day period. Your own trading volume and the volume of level 2 or level 3 referrals do not count toward the tier thresholds. | Referral tier | Requirements in the same rolling 30 days | Direct (L1) | Level 2 | Level 3 | | - | - | -: | -: | -: | | Default | No minimum | 10% | 2% | 1% | | Tier 2 | \$15M+ in direct referred volume **and** at least 5 direct referrals trading \$500K+ each | 15% | 3% | 2% | | Tier 3 | \$30M+ in direct referred volume **and** at least 10 direct referrals trading \$1M+ each | 20% | 4% | 2% | The volume and trader count requirements must both be met to reach a tier. If your rolling 30-day results fall below a tier's requirements, your rate moves to the tier you currently qualify for, down to the 10% direct baseline. ## Affiliates QFEX approves affiliates individually based on factors including, but not limited to, protocol usage, feedback and (legitimate) social media presence. Approved affiliates receive a custom referral link with their username and can refer unlimited users. They may choose **1** of the following commission models. Learn more or get started on the [QFEX Affiliates page](https://www.qfex.com/affiliates). ### A. Network model | Direct (L1) | Level 2 | Level 3 | | -: | -: | -: | | 30% | 6% | 3% | ### B. Performance model This model pays commission on direct referrals only. It has **no level 2 or level 3 commission**. | Rolling 30-day direct referred volume | Direct commission | Additional requirement | | - | -: | - | | Below \$25M or not qualified for a higher tier | 30% | None | | \$25M+ | 35% | 10 direct referrals trading \$1M+ each | | \$50M+ | 40% | 10 direct referrals trading \$1M+ each | | \$75M+ | 45% | 10 direct referrals trading \$1M+ each | | \$150M+ | 50% | 10 direct referrals trading \$1M+ each | To earn **more than 30%**, the affiliate must have at least **10 distinct direct referrals**, each trading **\$1M+ in the same rolling 30-day window**. If either the volume or trader count falls below the requirements, the affiliate receives the rate for the tier they currently qualify for, with a **30% floor**. All affiliates will be subject to an initial 90-day trial period, during which their performance will be evaluated. Affiliates who fail to meet the required criteria may be downgraded. QFEX reserves the right to modify the Affiliate Program at any time at the team's sole discretio ## How daily assessment and payment work Around 00:10 UTC, QFEX checks the previous 30 days of direct referred volume and, where required, the number of qualifying direct traders. The resulting tier is applied to **that day's referred trading**, and referral income is paid **at end of day (EOD)**. The assessment repeats daily, so rates can rise or fall as the rolling window changes. **Levels:** L1 means a user you referred directly. L2 means a user referred by your L1 referral. L3 means a user referred by your L2 referral. Only L1 trading volume determines your tier. # Risk Limits Source: https://docs.qfex.com/qfex/risk-limits Stay Safe We allow trading including overnight and on weekends, when some underlier markets are closed or less liquid. For now, this may lead to lower liquidity conditions that could make the exchange less safe for users. To ensure safety, we impose some limits on positions, prices and open interest in the early days of the exchange. ## Definitions Market Close refers to periods when major reference markets (e.g., NYSE, CME) are closed. | Asset Class | Weekday Overnight Hours | Weekend Hours | | - | - | - | | **Single Stocks** | NYSE closed (Mon–Fri ET)\* | Fri afternoon → Mon morning ET | | **US Equity Indices** | CME closed (Sun–Fri ET)\* | Fri afternoon → Sun evening ET | | **Commodities** | CME closed (Sun–Fri ET)\* | Fri afternoon → Sun evening ET | > *U.S. bank holidays are treated as normal business days for Market-Close logic.* ## Leverage Limits | Markets | Maximum Leverage | | - | - | | **Active Korean equities** | 7:1 | | **Other active equities, `CL-USD`, and `DRAM-USD`** | 10:1 | | **Commodities other than `CL-USD`** | 20:1 | | **Indices and funds other than `DRAM-USD`** | 30:1 | > Example: 30× leverage requires approximately **3.33% Initial Margin**. See the [Specification Index](/qfex/specification-index) for per-symbol leverage and execution parameters. ## Position Limits | Markets | Maximum Position (USD Notional) | | - | - | | **Active equities** | \$5,000,000 | | **Active commodities** | \$5,000,000 | | **Active indices and funds** | \$10,000,000 | | **`BMNR-USD`, `GLXY-USD`, and `GOLD-USD` overrides** | \$10,000,000 | These are limits for the default margin group. Participant-specific margin groups and other risk controls may impose different limits. During Market Close: * Position size per participant is limited to the **Max Position Percentage** of normal size. * Participants exceeding this limit can only **reduce exposure**: * For long positions: only sell orders accepted. * For short positions: only buy orders accepted. | Trading Period | Max Position % of Normal Limit | | - | - | | **Overnight** | 50% | | **Weekend** | 10% | ## Price Bands We implement price bands to protect users against: * their own fat finger errors; * manipulative practices from other market participants. For all products, buy orders above the upper band and sell orders below the lower band are rejected before reaching the matching engine. Price bands are calculated around the latest Market Data Price and are refreshed as market data updates. ### Dynamic price bands Price-band width is dynamic: each product is assigned a market-hours preset, and the applicable width changes with that product's current trading session. The lower and upper limits are calculated as: $$ \text{lower band} = P \times (1 - f), \qquad \text{upper band} = P \times (1 + f) $$ where $P$ is the latest Market Data Price and $f$ is the session factor below. Sessions are determined from the product's configured market calendar, including weekends; they are not a single UTC schedule shared by every product. | Product preset | Pre-market | Open | Post-market | Close | Weekend | | - | -: | -: | -: | -: | -: | | US, Hong Kong, China, Korean, and Stockholm equities | 7.5% | 10% | 7.5% | 5% | 5% | | CME products | 3.3% | 3.3% | 3.3% | 3.3% | 3.3% | | Spot metals | 1.65% | 3.3% | 1.65% | 1.65% | 1.65% | This approach gives actively traded markets a band appropriate to their primary session while using the configured close, overnight, and weekend widths when the reference market is less liquid or closed. A product's current bands are available in the market-data price-band response; integrations should use that live value rather than hardcoding a percentage. Price bands may be relaxed at the sole discretion of QFEX for the following reasons: * Exceptional market volatility * Stock splits or reverse stock split of equities ## Open Interest Limits Once a product exceeds **\$10m** of open interest, a participant's potential open interest is limited to **10%** of total open interest in that market. There is currently no absolute market-wide open-interest cap configured. See the [Specification Index](/qfex/specification-index) for the consolidated policy. # Roll Schedules Source: https://docs.qfex.com/qfex/roll-schedules Published dated-futures contracts and roll boundaries used by QFEX oracle pricing. Some QFEX perpetual futures derive their oracle price from a dated futures contract. This page lists all 36 calendar entries returned by the production settlement-calendar API from **2026-01-01 00:00 UTC** onward. The latest published roll boundary in this data is **2027-04-13 21:30 UTC**. > [!NOTE] > > Times are UTC. `active_until` is the boundary through which the listed dated future remains active for the QFEX symbol. Query the API at execution time instead of hardcoding this page because the published calendar can change. ## Published calendar ### CL-USD | Dated future | Active until (UTC) | Futures expiration (UTC) | | - | - | - | | `CLJ6` | 2026-03-12 21:30 UTC | 2026-03-20 21:30 UTC | | `CLK6` | 2026-04-14 21:30 UTC | 2026-04-21 21:30 UTC | | `CLM6` | 2026-05-13 21:30 UTC | 2026-05-18 21:30 UTC | | `CLN6` | 2026-06-11 21:30 UTC | 2026-06-10 21:30 UTC | | `CLQ6` | 2026-07-14 21:30 UTC | 2026-07-11 21:30 UTC | | `CLU6` | 2026-08-14 21:30 UTC | 2026-08-20 21:30 UTC | | `CLV6` | 2026-09-14 21:30 UTC | 2026-09-22 21:30 UTC | | `CLX6` | 2026-10-13 21:30 UTC | 2026-10-20 21:30 UTC | | `CLZ6` | 2026-11-12 22:30 UTC | 2026-11-20 22:30 UTC | | `CLF7` | 2026-12-11 22:30 UTC | 2026-12-21 22:30 UTC | | `CLG7` | 2027-01-14 22:30 UTC | 2027-01-20 22:30 UTC | ### COPPER-USD | Dated future | Active until (UTC) | Futures expiration (UTC) | | - | - | - | | `HGK6` | 2026-04-14 21:30 UTC | 2026-04-21 21:30 UTC | | `HGN6` | 2026-06-11 21:30 UTC | 2026-06-16 21:30 UTC | | `HGU6` | 2026-08-13 21:30 UTC | 2026-09-28 21:30 UTC | | `HGZ6` | 2026-11-12 22:30 UTC | 2026-12-29 22:30 UTC | | `HGH7` | 2027-02-11 22:30 UTC | 2027-03-29 21:30 UTC | | `HGK7` | 2027-04-13 21:30 UTC | 2027-05-26 17:00 UTC | ### NATGAS-USD | Dated future | Active until (UTC) | Futures expiration (UTC) | | - | - | - | | `NGJ26` | 2026-03-12 21:30 UTC | 2026-03-27 21:30 UTC | | `NGK26` | 2026-04-14 21:30 UTC | 2026-04-28 21:30 UTC | | `NGM26` | 2026-05-13 21:30 UTC | 2026-05-27 21:30 UTC | | `NGN26` | 2026-06-11 21:30 UTC | 2026-06-10 21:30 UTC | | `NGQ26` | 2026-07-14 21:30 UTC | 2026-07-20 21:30 UTC | | `NGU26` | 2026-08-14 21:30 UTC | 2026-08-27 21:30 UTC | | `NGV26` | 2026-09-14 21:30 UTC | 2026-09-28 21:30 UTC | | `NGX26` | 2026-10-13 21:30 UTC | 2026-10-28 21:30 UTC | | `NGZ26` | 2026-11-12 22:30 UTC | 2026-11-25 22:30 UTC | | `NGF27` | 2026-12-11 22:30 UTC | 2026-12-29 22:30 UTC | | `NGG27` | 2027-01-14 22:30 UTC | 2027-01-27 22:30 UTC | ### US100-USD | Dated future | Active until (UTC) | Futures expiration (UTC) | | - | - | - | | `NQH6` | 2026-03-13 21:30 UTC | 2026-03-18 13:30 UTC | | `NQM6` | 2026-06-14 21:30 UTC | 2026-06-19 13:30 UTC | | `NQU6` | 2026-09-13 21:30 UTC | 2026-09-18 13:30 UTC | | `NQZ6` | 2026-12-13 21:30 UTC | 2026-12-18 13:30 UTC | ### US500-USD | Dated future | Active until (UTC) | Futures expiration (UTC) | | - | - | - | | `ESH6` | 2026-03-13 21:30 UTC | 2026-03-18 13:30 UTC | | `ESM6` | 2026-06-14 21:30 UTC | 2026-06-19 13:30 UTC | | `ESU6` | 2026-09-13 21:30 UTC | 2026-09-18 13:30 UTC | | `ESZ6` | 2026-12-13 21:30 UTC | 2026-12-18 13:30 UTC | ## Roll behavior `US100-USD` and `US500-USD` switch to the next listed contract after the prior contract's `active_until` boundary. For `CL-USD`, `COPPER-USD`, and `NATGAS-USD`, the oracle blends the front and next contracts over the final five CME business-day boundaries before `active_until`. Weekends and configured CME holidays do not advance the blend. Each business-day boundary lands at **17:30 America/New\_York**, the same wall-clock time as `active_until`. In the published calendar above that time appears as **21:30 UTC** while US Eastern is on daylight time and **22:30 UTC** while it is on standard time. The blend advances by exactly one row of the table below at each of these daily boundaries, so the changeover to 100% next contract completes at the `active_until` timestamp itself. | CME business days remaining until `active_until` | Front contract | Next contract | | -: | -: | -: | | 5 or more | 100% | 0% | | 4 | 80% | 20% | | 3 | 60% | 40% | | 2 | 40% | 60% | | 1 | 20% | 80% | | 0 | 0% | 100% | The first step off 100% front therefore occurs at the 17:30 ET boundary five CME business days before `active_until`, and each subsequent business day shifts another 20% until the next contract is fully active. ## Query the live calendar With the [QFEX CLI](/api-reference/cli): ```sh theme={null} qfex market settlement-calendar --symbol CL-USD qfex market settlement-calendar --symbol CL-USD --time 2026-09-01T00:00:00Z ``` Or call the production REST endpoint directly: ```sh theme={null} curl --get 'https://api.qfex.com/settlement-calendar' \ --data-urlencode 'symbol=CL-USD' \ --data-urlencode 'time=2026-09-01T00:00:00Z' ``` The response contains `qfex_symbol`, `future_symbol`, `active_until`, and `expiration`. If `time` is omitted, the endpoint uses the current UTC time. # Specification Index Source: https://docs.qfex.com/qfex/specification-index A consolidated view of active QFEX production contract, execution, session, and risk parameters. This index contains the 199 markets marked active in the production refdata configuration. Quantities are expressed in units of the underlying asset and notionals are expressed in USD unless stated otherwise. > [!NOTE] > > All listed products are perpetual futures, cross-margined in USDC, and available for trading 24/7. The reference-price hours below are the periods when QFEX actively consumes underlier prices and [funding](/qfex/funding) can accrue. Funding is `0` outside these hours. ## Active markets ### Commodities | Instrument | Underlying | Max leverage | Default position limit | Tick / lot | Quantity range | Reference-price hours | | - | - | -: | -: | -: | -: | - | | `CL-USD` | CL / USD | 20× | USD 100m | 0.01 / 0.001 | 0.1–10,000,000 | Sun 17:00–Fri 16:00 CT; daily 16:00–17:00 pause | | `COPPER-USD` | COPPER / USD | 20× | USD 100m | 0.001 / 0.1 | 1–100,000,000 | Sun 17:00–Fri 20:00 ET | | `GOLD-USD` | GOLD / USD | 30× | USD 100m | 0.1 / 0.0001 | 0.001–10,000,000 | Sun 17:00–Fri 17:00 ET | | `NATGAS-USD` | NATGAS / USD | 20× | USD 100m | 0.001 / 0.01 | 1–100,000,000 | Sun 17:00–Fri 20:00 ET | | `SILVER-USD` | SILVER / USD | 20× | USD 100m | 0.01 / 0.001 | 0.001–10,000,000 | Sun 17:00–Fri 17:00 ET | | `URANIUM-USD` | URANIUM / USD | 20× | USD 100m | 0.01 / 0.001 | 0.1–10,000,000 | Sun 17:00–Fri 20:00 ET | ### Indices and funds | Instrument | Underlying | Max leverage | Default position limit | Tick / lot | Quantity range | Reference-price hours | | - | - | -: | -: | -: | -: | - | | `DRAM-USD` | DRAM / USD | 30× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 17:00–Fri 20:00 ET | | `HSI-USD` | HSI / USD | 30× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 17:00–Fri 20:00 ET | | `IGV-USD` | IGV / USD | 30× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 17:00–Fri 20:00 ET | | `KOSPI-USD` | KOSPI / USD | 30× | USD 100m | 0.01 / 0.001 | 0.1–10,000,000 | Sun 17:00–Fri 20:00 ET | | `NIKKEI-USD` | NIKKEI / USD | 30× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 17:00–Fri 20:00 ET | | `SOXL-USD` | SOXL / USD | 30× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 17:00–Fri 20:00 ET | | `TAIEX-USD` | TAIEX / USD | 30× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 17:00–Fri 20:00 ET | | `US100-USD` | US100 / USD | 30× | USD 100m | 1 / 0.0001 | 0.001–10,000,000 | Sun 17:00–Fri 16:00 CT; daily 16:00–17:00 pause | | `US500-USD` | US500 / USD | 30× | USD 100m | 0.1 / 0.001 | 0.001–10,000,000 | Sun 17:00–Fri 16:00 CT; daily 16:00–17:00 pause | | `XLE-USD` | XLE / USD | 30× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 17:00–Fri 20:00 ET | | `XLF-USD` | XLF / USD | 30× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 17:00–Fri 20:00 ET | ### Equities | Instrument | Underlying | Max leverage | Default position limit | Tick / lot | Quantity range | Reference-price hours | | - | - | -: | -: | -: | -: | - | | `AAOI-USD` | AAOI / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `AAPL-USD` | AAPL / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `ABCL-USD` | ABCL / USD | 10× | USD 100m | 0.01 / 0.1 | 1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `ABSI-USD` | ABSI / USD | 10× | USD 100m | 0.01 / 0.1 | 1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `ADEA-USD` | ADEA / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `AEHR-USD` | AEHR / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `ALAB-USD` | ALAB / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `ALNT-USD` | ALNT / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `AMC-USD` | AMC / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `AMD-USD` | AMD / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `AMZN-USD` | AMZN / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `ANTHROPIC-USD` | ANTHROPIC / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–100,000 | 24/7 | | `ARM-USD` | ARM / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `ASST-USD` | ASST / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `ASTS-USD` | ASTS / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `AVGO-USD` | AVGO / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `AXTI-USD` | AXTI / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `B-USD` | B / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `BA-USD` | BA / USD | 10× | USD 100m | 0.01 / 0.001 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `BABA-USD` | BABA / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `BB-USD` | BB / USD | 10× | USD 100m | 0.01 / 0.1 | 1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `BE-USD` | BE / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `BEAM-USD` | BEAM / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `BFLY-USD` | BFLY / USD | 10× | USD 100m | 0.01 / 0.1 | 1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `BMNP-USD` | BMNP / USD | 10× | USD 100m | 0.01 / 0.001 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `BMNR-USD` | BMNR / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `BNC-USD` | BNC / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `BOT-USD` | BOT / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `BRK.B-USD` | BRK.B / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `BWET-USD` | BWET / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `BX-USD` | BX / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `CAT-USD` | CAT / USD | 10× | USD 100m | 0.01 / 0.001 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `CBRS-USD` | CBRS / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `CCXI-USD` | CCXI / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `CGNX-USD` | CGNX / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `CHYM-USD` | CHYM / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `CIEN-USD` | CIEN / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `CIFR-USD` | CIFR / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `COHR-USD` | COHR / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `COIN-USD` | COIN / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `COPX-USD` | COPX / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `COST-USD` | COST / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `CRCL-USD` | CRCL / USD | 10× | USD 100m | 0.01 / 0.001 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `CRDO-USD` | CRDO / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `CRM-USD` | CRM / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `CRML-USD` | CRML / USD | 10× | USD 100m | 0.01 / 0.1 | 1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `CRON-USD` | CRON / USD | 10× | USD 100m | 0.01 / 0.1 | 1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `CRWD-USD` | CRWD / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `CRWV-USD` | CRWV / USD | 10× | USD 100m | 0.01 / 0.001 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `CVNA-USD` | CVNA / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `CXMT-CNY` | CXMT / CNY | 10× | USD 100m | 0.01 / 0.01 | 0.1–100,000 | No scheduled hours | | `CYPH-USD` | CYPH / USD | 10× | USD 100m | 0.01 / 0.1 | 1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `DFTX-USD` | DFTX / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `DGXX-USD` | DGXX / USD | 10× | USD 100m | 0.01 / 0.1 | 1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `DJT-USD` | DJT / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `DLR-USD` | DLR / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `DUOL-USD` | DUOL / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `DXYZ-USD` | DXYZ / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `EBAY-USD` | EBAY / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `EQIX-USD` | EQIX / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `FCEL-USD` | FCEL / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `FLEX-USD` | FLEX / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `FSLY-USD` | FSLY / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `FWDI-USD` | FWDI / USD | 10× | USD 100m | 0.01 / 0.1 | 1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `GD-USD` | GD / USD | 10× | USD 100m | 0.01 / 0.001 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `GLW-USD` | GLW / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `GLXY-USD` | GLXY / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `GNRC-USD` | GNRC / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `GOOGL-USD` | GOOGL / USD | 10× | USD 100m | 0.01 / 0.001 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `GPRO-USD` | GPRO / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `GRAB-USD` | GRAB / USD | 10× | USD 100m | 0.01 / 0.1 | 1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `GRND-USD` | GRND / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `GS-USD` | GS / USD | 10× | USD 100m | 0.01 / 0.001 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `HIMS-USD` | HIMS / USD | 10× | USD 100m | 0.01 / 0.01 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `HOOD-USD` | HOOD / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `HSAI-USD` | HSAI / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `HUBS-USD` | HUBS / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `HUT-USD` | HUT / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `HYUNDAI-KRW` | HYUNDAI / KRW | 10× | USD 100m | 100 / 0.000001 | 0.00001–10,000,000 | Mon–Wed, Fri 09:00–15:30 KST | | `IBIT-USD` | IBIT / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `IBM-USD` | IBM / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `ILMN-USD` | ILMN / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `INTC-USD` | INTC / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `IONQ-USD` | IONQ / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `IOVA-USD` | IOVA / USD | 10× | USD 100m | 0.01 / 0.1 | 1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `IREN-USD` | IREN / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `JBL-USD` | JBL / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `JPM-USD` | JPM / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `KEEL-USD` | KEEL / USD | 10× | USD 100m | 0.01 / 0.1 | 1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `KIOXIA-JPY` | KIOXIA / JPY | 10× | USD 100m | 1 / 0.00001 | 0.0001–10,000,000 | Weekdays 09:00–11:30, 12:30–15:30 JST | | `LITE-USD` | LITE / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `LLY-USD` | LLY / USD | 10× | USD 100m | 0.01 / 0.0001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `LMT-USD` | LMT / USD | 10× | USD 100m | 0.01 / 0.001 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `LPTH-USD` | LPTH / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `LRCX-USD` | LRCX / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `LSCC-USD` | LSCC / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `LULU-USD` | LULU / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `MDT-USD` | MDT / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `META-USD` | META / USD | 10× | USD 100m | 0.01 / 0.001 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `MITK-USD` | MITK / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `MRNA-USD` | MRNA / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `MRVL-USD` | MRVL / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `MSFT-USD` | MSFT / USD | 10× | USD 100m | 0.01 / 0.001 | 0.02–10,000,000 | Sun 20:00–Fri 20:00 ET | | `MSTR-USD` | MSTR / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `MTCH-USD` | MTCH / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `MU-USD` | MU / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `MX-USD` | MX / USD | 10× | USD 100m | 0.01 / 0.1 | 1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `NBIS-USD` | NBIS / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `NBR-USD` | NBR / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `NCLD-USD` | NCLD / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `NEM-USD` | NEM / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `NKE-USD` | NKE / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `NOC-USD` | NOC / USD | 10× | USD 100m | 0.01 / 0.001 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `NOW-USD` | NOW / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `NUAI-USD` | NUAI / USD | 10× | USD 100m | 0.01 / 0.1 | 1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `NVDA-USD` | NVDA / USD | 10× | USD 100m | 0.01 / 0.001 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `NVO-USD` | NVO / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `NVTS-USD` | NVTS / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `OPENAI-USD` | OPENAI / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–100,000 | 24/7 | | `ORCL-USD` | ORCL / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `OURA-USD` | OURA / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–100,000 | 24/7 | | `OUST-USD` | OUST / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `PANW-USD` | PANW / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `PENG-USD` | PENG / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `PL-USD` | PL / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `PLTR-USD` | PLTR / USD | 10× | USD 100m | 0.01 / 0.001 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `PLUG-USD` | PLUG / USD | 10× | USD 100m | 0.01 / 0.1 | 1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `PRE-USD` | PRE / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `PRLB-USD` | PRLB / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `PURR-USD` | PURR / USD | 10× | USD 100m | 0.01 / 0.001 | 1–1,000,000,000 | 24/7 | | `QCOM-USD` | QCOM / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `QURE-USD` | QURE / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `RCAT-USD` | RCAT / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `RDCM-USD` | RDCM / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `RDDT-USD` | RDDT / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `RDW-USD` | RDW / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `RIOT-USD` | RIOT / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `RKLB-USD` | RKLB / USD | 10× | USD 100m | 0.01 / 0.001 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `RSP-USD` | RSP / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `RTX-USD` | RTX / USD | 10× | USD 100m | 0.01 / 0.001 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `SAMSUNG-KRW` | SAMSUNG / KRW | 10× | USD 100m | 10 / 0.00001 | 0.0001–10,000,000 | Mon–Wed, Fri 09:00–15:30 KST | | `SBET-USD` | SBET / USD | 10× | USD 100m | 0.01 / 0.1 | 1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `SECZ-USD` | SECZ / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `SERV-USD` | SERV / USD | 10× | USD 100m | 0.01 / 0.1 | 1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `SHAZ-USD` | SHAZ / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `SIVE-SEK` | SIVE / SEK | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Weekdays 09:00–17:00 Europe/Stockholm | | `SKHY-USD` | SKHY / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–100,000 | Sun 20:00–Fri 20:00 ET | | `SKHYNIX-KRW` | SKHYNIX / KRW | 10× | USD 100m | 100 / 0.000001 | 0.00001–10,000,000 | Mon–Wed, Fri 09:00–15:30 KST | | `SKM-USD` | SKM / USD | 10× | USD 100m | 0.01 / 0.01 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `SMCI-USD` | SMCI / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `SMH-USD` | SMH / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `SNDK-USD` | SNDK / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `SOFTBANK-JPY` | SOFTBANK / JPY | 10× | USD 100m | 0.1 / 0.0001 | 0.001–10,000,000 | Weekdays 09:00–11:30, 12:30–15:30 JST | | `SPCE-USD` | SPCE / USD | 10× | USD 100m | 0.01 / 0.1 | 1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `SPCX-USD` | SPCX / USD | 10× | USD 100m | 0.01 / 0.001 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `STRC-USD` | STRC / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `SUIG-USD` | SUIG / USD | 10× | USD 100m | 0.01 / 0.1 | 1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `TE-USD` | TE / USD | 10× | USD 100m | 0.01 / 0.1 | 1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `TEM-USD` | TEM / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `TJX-USD` | TJX / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `TLT-USD` | TLT / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `TMDX-USD` | TMDX / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `TMO-USD` | TMO / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `TSLA-USD` | TSLA / USD | 10× | USD 100m | 0.01 / 0.001 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `TTWO-USD` | TTWO / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `UBER-USD` | UBER / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `UNH-USD` | UNH / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `UNITREE-CNY` | UNITREE / CNY | 10× | USD 100m | 0.01 / 0.001 | 0.01–100,000 | No scheduled hours | | `USDE-USD` | USDE / USD | 10× | USD 100m | 0.01 / 0.1 | 1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `VCX-USD` | VCX / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `VST-USD` | VST / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `WDC-USD` | WDC / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `WMT-USD` | WMT / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `WOLF-USD` | WOLF / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `WPM-USD` | WPM / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `WULF-USD` | WULF / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `WYFI-USD` | WYFI / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `XMTR-USD` | XMTR / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `XOM-USD` | XOM / USD | 10× | USD 100m | 0.01 / 0.001 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | | `ZBRA-USD` | ZBRA / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `ZM-USD` | ZM / USD | 10× | USD 100m | 0.01 / 0.001 | 0.01–10,000,000 | Sun 20:00–Fri 20:00 ET | | `ZROZ-USD` | ZROZ / USD | 10× | USD 100m | 0.01 / 0.01 | 0.1–10,000,000 | Sun 20:00–Fri 20:00 ET | ## How to read the index * **Max leverage** is the highest leverage tier available to the default margin group. Participant-specific groups may differ. * **Default position limit** is the maximum notional available in the default margin group. Participant-specific margin groups and other risk controls may impose a different limit. * **Tick / lot** shows the minimum price increment followed by the minimum quantity increment, both in the instrument's quoted units. * **Quantity range** shows the minimum and maximum order quantity. * **Reference-price hours** follow the named local timezone and may change on market holidays. All active markets support Limit, Market, and Add Liquidity Only (ALO) orders. Supported time-in-force instructions are Good-Till-Cancelled (GTC), Immediate-or-Cancel (IOC), and Fill-or-Kill (FOK), subject to the [order type compatibility rules](/qfex/order-types). ## Open-interest controls Each active production market uses the following open-interest policy: * The participant concentration check activates when total open interest exceeds **USD 10m**. * Above that threshold, a participant's potential open interest is limited to **10% of total market open interest**. * No absolute market-wide open-interest cap is configured. ## Machine-readable specifications Use reference data for the latest machine-readable market status, increments, limits, and session schedule: ```sh theme={null} qfex market refdata ``` Reference data is also available from [`GET /refdata`](https://api.qfex.com/refdata) and the [Reference Data websocket channel](/websocket/channels/mds/refdata). # Stablecoins Source: https://docs.qfex.com/qfex/stablecoins Pay via Stablecoins QFEX supports funding via [USDC](https://circle.com) on Arbitrum. Client funds are held by BitGo Bank & Trust, National Association, a national banking association chartered under the laws of the United States and authorized by the Office of the Comptroller of the Currency to exercise fiduciary and custodial powers. ## How To Deposit USDC 1. Open **Deposit / Withdraw** from your account header. ![](https://mintcdn.com/qfex/RiwFQp0JUS2lYLtv/images/deposit-1.png?fit=max\&auto=format\&n=RiwFQp0JUS2lYLtv\&q=85\&s=d4daca0eba1dde4bd920209185f13141) 2. Select **Deposit funds**. ![](https://mintcdn.com/qfex/RiwFQp0JUS2lYLtv/images/deposit-2.png?fit=max\&auto=format\&n=RiwFQp0JUS2lYLtv\&q=85\&s=e7ce10bd205255ee249699c05eb0adad) 3. Choose **USDC** from the available deposit currencies. ![](https://mintcdn.com/qfex/RiwFQp0JUS2lYLtv/images/deposit-3.png?fit=max\&auto=format\&n=RiwFQp0JUS2lYLtv\&q=85\&s=88aef2d2af6806742382fd66b8b7469e) 4. Send USDC on **Arbitrum One** to the deposit address shown in the app. ![](https://mintcdn.com/qfex/RiwFQp0JUS2lYLtv/images/deposit-4-usdc.png?fit=max\&auto=format\&n=RiwFQp0JUS2lYLtv\&q=85\&s=7af92f6704bb3820c88c8fd792524096) > [!NOTE] > > Only deposit USDC via the Arbitrum One network. Deposits sent using a > different asset or network may be lost. # Subaccounts Source: https://docs.qfex.com/qfex/subaccounts Separate your margin and risk QFEX supports subaccounts as a means to separate margin and risk without creating multiple accounts. All subaccounts share a common email and login, but act as separate accounts when it comes to order entry and margin. Deposits all go into the main account, and cash balances can be transferred between the main account and subaccounts using the [UI](https://qfex.com/trade), [REST API](../api-reference/subaccounts-api) or [CLI](../api-reference/cli). # Changelog Source: https://docs.qfex.com/websocket/changelog Product updates and announcements **23rd October 2026** New Endpoint **Isolated positions now expose cash collateral separately from equity.** Endpoints: `GET /user/positions`. Isolated positions include isolated\_collateral, which excludes unrealised PnL and includes settled fees, funding and realised PnL. Their existing margin field continues to report equity including unrealised PnL. **22nd October 2026** New Endpoint **Symbol reference data now includes isolated\_only.** Endpoints: `GET /refdata`. Each symbol returns an explicit boolean indicating whether it requires isolated margin. Flagged symbols require isolated route registration before trading. Existing cross exposure can still be cancelled or closed with reduce-only orders. **14th October 2026** API Update **Initial invitation qualification now uses lifetime personal trading volume.** Endpoints: `POST /user/invitation-codes/job`, `GET /user/referrals/notional-volume`. Creators receive their initial five invitations after reaching \$100,000 of all-time personal trading volume, including subaccounts, rather than volume in the last 14 days. The qualification responses add self\_lifetime\_notional\_volume; existing self\_notional\_volume remains the 14-day value. Referred volume does not count toward initial qualification. Existing grants and recurring invitation rules are unchanged. **13th October 2026** Docs Update **REST API release notes now follow production deployments.** Public REST API changes now appear in the REST API changelog after the corresponding API revision is available in production. The reference is rebuilt from the production OpenAPI document with each tracked release. **13th October 2026** Breaking Change **Builder Web3 signed messages now link to the QFEX Client Agreement.** Endpoints: `GET /builder/web3/message`, `POST /builder/web3/api-key`. The legal consent statement includes [https://docs.qfex.com/legal/client-agreement](https://docs.qfex.com/legal/client-agreement) beside the Client Agreement. Key issuance requires the updated statement, including this link. **Migration:** Request and sign a fresh message from GET /builder/web3/message. Submit that string unchanged to POST /builder/web3/api-key; messages without the agreement link are rejected. **13th October 2026** Breaking Change **Builder Web3 registration now includes legal consent and an Arbitrum withdrawal address.** Endpoints: `GET /builder/web3/message`, `POST /builder/web3/api-key`. The signed message now authorizes a trading API key and includes acceptance of the QFEX Client Agreement, Privacy Policy, and Cookie Policy. Messages without this consent are rejected. Successful key issuance records the acceptance date and registers the verified wallet address on ARBITRUM\_ONE without duplicating an existing address. **Migration:** Request a fresh message from GET /builder/web3/message and sign the returned string unchanged before submitting it to POST /builder/web3/api-key. Clients that construct messages themselves must include the updated consent statement. **14th October 2026** Market Data Update `market_stats` wildcard subscriptions (`symbols: ["*"]`) now receive all-symbol snapshots every 10 seconds. Named-symbol subscriptions continue to update once per second. An initial snapshot is sent on subscription when data is available. The [Market Stats reference](/websocket/channels/mds/market_stats) and MDS Playground schema now document funding rates, rolling 24-hour traded quantity, and rolling 24-hour USD notional volume. **9th September 2026** Breaking Change `modify_order` now requires the `reduce_only` field. It must match the order being modified, as a modify cannot change reduce-only. Requests that omit it are rejected with `InvalidJSONFormat`, and a value that does not match the resting order is rejected with `CANNOT_MODIFY_NO_SUCH_ORDER`. See [here](/websocket/channels/trade/modify_order) for further details **7th September 2026** Docs Update Added builder codes: create and view a code over REST, then attach it on the Trade WebSocket auth message as `params.builder_code`. Public balances expose credited share as `builder_rewards`. See [here](/qfex/builder-code) and [here](/websocket/channels/trade/authenticate) for further details **17th February 2026** Docs Update Added default\_max\_leverage field to refdata response. See [here](/websocket/channels/mds/refdata) for further details **9th February 2026** Docs Update Added funding scaling documentation. The final funding rate is now documented as scaled by `1/100`, and the funding stream reflects the post-scaling value. See [here](/websocket/channels/mds/funding_rate), [here](/qfex/contract-specifications), and [here](/qfex/faq) for further details **26th January 2026** Docs Update Added the `fills` channel and fill response payload documentation. See [here](/websocket/channels/trade/fills) for further details **12th November 2025** Docs Update Updated trades response to include `execution_type`. See [here](/websocket/channels/mds/trades) for further details **12th November 2025** Docs Update Updated order response to include trade\_id. This is used for order responses with a status of FILLED, it provides the unique trade id of that fill. See [here](/websocket/channels/trade/order) for further details **11th November 2025** Docs Update Updated get\_user\_trades endpoint to support more filtering. See [here](/websocket/channels/trade/get_user_trades) for further details **30th October 2025** Docs Update Updated stop order response type. Type is now `STOP_LOSS` and `TAKE_PROFIT`. Stop orders are shown in the `orders` key in `get_orders_response` See [here](/websocket/channels/trade/get_user_orders) and [here](/api-reference/enums) for further details **27th October 2025** Docs Update Added documentation for stop orders. See [here](/websocket/channels/trade/stop_add_order), [here](/websocket/channels/trade/stop_modify_order) and [here](/websocket/channels/trade/stop_cancel_order) for further details **17th October 2025** Docs Update Added Close Position request. See [here](/websocket/channels/trade/close_position) for further details This is only intended for use in the frontend as a quick way to close a position. **15th October 2025** Docs Update Added HMAC-SHA256 authentication requirements for the trade WebSocket, including refreshed client samples. See [here](/websocket/channels/trade/authenticate) for further details Documented the new cancel-on-disconnect channel with end-to-end examples for Python, Node.js, Go, and Java. See [here](/websocket/channels/trade/cancel_on_disconnect) for further details **13th October 2025** v0.0.10 You must now include order type in a modify order See [here](/websocket/channels/trade/modify_order) for further details. **25th September 2025** v0.0.10 You must now subscribe to order responses. See [here](/websocket/channels/trade/order) for further details. # Pulsed BBO Source: https://docs.qfex.com/websocket/channels/mds/bbo QFEX provides a **pulsed best bid/offer (BBO) stream** for all symbols via websocket. ## How it works Connect to the public websocket: * URL: **wss\://mds.qfex.com** Then send a subscribe message: ```json theme={null} { "type": "subscribe", "channels": ["bbo"], "symbols": ["AAPL-USD", "US500-USD"] } ``` Or subscribe to all symbols with a wildcard: ```json theme={null} { "type": "subscribe", "channels": ["bbo"], "symbols": ["*"] } ``` ## Sample Code ```python python theme={null} # Python (websocket-client) # pip install websocket-client import json import websocket def on_open(ws): sub = { "type": "subscribe", "channels": ["bbo"], "symbols": ["US500-USD"] # or ["*"] } ws.send(json.dumps(sub)) def on_message(ws, message): data = json.loads(message) print("BBO update:", json.dumps(data, indent=2)) def on_error(ws, error): print("Error:", error) def on_close(ws, close_status_code, close_msg): print("Closed:", close_status_code, close_msg) if __name__ == "__main__": ws = websocket.WebSocketApp( "wss://mds.qfex.com", on_open=on_open, on_message=on_message, on_error=on_error, on_close=on_close, ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) // npm i ws import WebSocket from "ws"; const ws = new WebSocket("wss://mds.qfex.com"); ws.on("open", () => { const sub = { type: "subscribe", channels: ["bbo"], symbols: ["US500-USD"], // or ["*"] }; ws.send(JSON.stringify(sub)); }); ws.on("message", (msg) => { try { const data = JSON.parse(msg.toString()); console.log("BBO update:", data); } catch (e) { console.error("Parse error:", e); } }); ws.on("error", (err) => console.error("WS error:", err)); ws.on("close", (code, reason) => console.log("Closed:", code, reason?.toString()), ); ``` ```go go theme={null} // Go (gorilla/websocket) // go get github.com/gorilla/websocket package main import ( "fmt" "log" "os" "os/signal" "github.com/gorilla/websocket" ) func main() { c, _, err := websocket.DefaultDialer.Dial("wss://mds.qfex.com", nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() sub := map[string]interface{}{ "type": "subscribe", "channels": []string{"bbo"}, "symbols": []string{"US500-USD"}, // or []string{"*"} } if err := c.WriteJSON(sub); err != nil { log.Fatal("write:", err) } done := make(chan struct{}) go func() { defer close(done) for { _, message, err := c.ReadMessage() if err != nil { log.Println("read:", err) return } fmt.Printf("BBO update: %s\n", message) } }() // keep running until Ctrl+C interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, os.Interrupt) <-interrupt } ``` ```java java theme={null} // Java (OkHttp WebSocket) // Gradle: implementation("com.squareup.okhttp3:okhttp:4.12.0") import java.util.concurrent.TimeUnit; import okhttp3.*; public class BboWs { public static void main(String[] args) { OkHttpClient client = new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS) .build(); Request request = new Request.Builder() .url("wss://mds.qfex.com") .build(); WebSocketListener listener = new WebSocketListener() { @Override public void onOpen(WebSocket webSocket, Response response) { String sub = "{\"type\":\"subscribe\",\"channels\":[\"bbo\"],\"symbols\":[\"US500-USD\"]}"; webSocket.send(sub); } @Override public void onMessage(WebSocket webSocket, String text) { System.out.println("BBO update: " + text); } @Override public void onFailure(WebSocket webSocket, Throwable t, Response response) { System.err.println("WS error: " + t.getMessage()); } }; client.newWebSocket(request, listener); // Keep JVM alive try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ignored) {} } } ``` ## Example Response ```json theme={null} { "type": "bbo", "symbol": "AAPL-USD", "bid": [["10101.10", "0.45054140"]], "ask": [["10102.55", "0.57753524"]], "sequence": 30272539, "time": "2025-09-04T09:26:39.545268322Z" } ``` ### Unsubscribe (optional) ```json theme={null} { "type": "unsubscribe", "channels": ["bbo"], "symbols": ["US500-USD"] } ``` *** **Notes** * Similar to Level 2 but only contains **top-of-book** (best bid/ask). * Updates are **pulsed**. * Supports wildcard `*` to receive BBO for all symbols. # Funding Rate Source: https://docs.qfex.com/websocket/channels/mds/funding_rate QFEX provides a **stream of the current funding rate** for any symbol, representing the rate if the funding window were to end *right now*.\ At the end of each funding window, the final rate is used to compute **funding payments**. The streamed value is the post-scaling final rate: $F_{\text{final}} = F_{\text{raw}} / 100$. > [!NOTE] > > Funding is only active during each symbol's configured market hours. Outside > those hours, funding rate is `0`. See [`/qfex/funding`](/qfex/funding). ## How it works Connect to the public websocket: * URL: **wss\://mds.qfex.com** Then send a subscribe message: ```json theme={null} { "type": "subscribe", "channels": ["funding"], "symbols": ["AAPL-USD", "US500-USD"] } ``` ## Sample Code ```python python theme={null} # Python (websocket-client) # pip install websocket-client import json import websocket def on_open(ws): sub = { "type": "subscribe", "channels": ["funding"], "symbols": ["US500-USD"] # or ["*"] } ws.send(json.dumps(sub)) def on_message(ws, message): data = json.loads(message) print("Funding update:", json.dumps(data, indent=2)) def on_error(ws, error): print("Error:", error) def on_close(ws, close_status_code, close_msg): print("Closed:", close_status_code, close_msg) if __name__ == "__main__": ws = websocket.WebSocketApp( "wss://mds.qfex.com", on_open=on_open, on_message=on_message, on_error=on_error, on_close=on_close, ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) // npm i ws import WebSocket from "ws"; const ws = new WebSocket("wss://mds.qfex.com"); ws.on("open", () => { const sub = { type: "subscribe", channels: ["funding"], symbols: ["US500-USD"], // or ["*"] }; ws.send(JSON.stringify(sub)); }); ws.on("message", (msg) => { try { const data = JSON.parse(msg.toString()); console.log("Funding update:", data); } catch (e) { console.error("Parse error:", e); } }); ws.on("error", (err) => console.error("WS error:", err)); ws.on("close", (code, reason) => console.log("Closed:", code, reason?.toString()) ); ``` ```go go theme={null} // Go (gorilla/websocket) // go get github.com/gorilla/websocket package main import ( "fmt" "log" "os" "os/signal" "github.com/gorilla/websocket" ) func main() { c, _, err := websocket.DefaultDialer.Dial("wss://mds.qfex.com", nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() sub := map[string]interface{}{ "type": "subscribe", "channels": []string{"funding"}, "symbols": []string{"US500-USD"}, // or []string{"*"} } if err := c.WriteJSON(sub); err != nil { log.Fatal("write:", err) } done := make(chan struct{}) go func() { defer close(done) for { _, message, err := c.ReadMessage() if err != nil { log.Println("read:", err) return } fmt.Printf("Funding update: %s\n", message) } }() // keep running until Ctrl+C interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, os.Interrupt) <-interrupt } ``` ```java java theme={null} // Java (OkHttp WebSocket) // Gradle: implementation("com.squareup.okhttp3:okhttp:4.12.0") import java.util.concurrent.TimeUnit; import okhttp3.*; public class FundingWs { public static void main(String[] args) { OkHttpClient client = new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS) .build(); Request request = new Request.Builder() .url("wss://mds.qfex.com") .build(); WebSocketListener listener = new WebSocketListener() { @Override public void onOpen(WebSocket webSocket, Response response) { String sub = "{\"type\":\"subscribe\",\"channels\":[\"funding\"],\"symbols\":[\"US500-USD\"]}"; webSocket.send(sub); } @Override public void onMessage(WebSocket webSocket, String text) { System.out.println("Funding update: " + text); } @Override public void onFailure(WebSocket webSocket, Throwable t, Response response) { System.err.println("WS error: " + t.getMessage()); } }; client.newWebSocket(request, listener); // Keep JVM alive try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ignored) {} } } ``` ## Example Response ```json theme={null} { "type": "funding", "sequence": 50, "time": "2014-11-07T08:19:27.028459Z", "symbol": "AAPL-USD", "time_remaining": 30, "funding_rate": "0.021" } ``` ### Unsubscribe (optional) ```json theme={null} { "type": "unsubscribe", "channels": ["funding"], "symbols": ["US500-USD"] } ``` *** **Notes** * Represents the **current implied funding rate** if the window ended immediately. * Pulsed during funding calculation window. * Outside symbol market hours, funding rate = `0`. # Mark Price Source: https://docs.qfex.com/websocket/channels/mds/mark_price Subscribe to real-time mark price updates for symbols. ## How it works Connect to the public websocket: * URL: **wss\://mds.qfex.com** Then send a subscribe message: ```json theme={null} { "type": "subscribe", "channels": ["mark_price"], "symbols": ["AAPL-USD"] } ``` ## Sample Code ```python python theme={null} # Python (websocket-client) import json import websocket def on_open(ws): sub = { "type": "subscribe", "channels": ["mark_price"], "symbols": ["AAPL-USD"] } ws.send(json.dumps(sub)) def on_message(ws, message): print("Update:", message) if __name__ == "__main__": ws = websocket.WebSocketApp( "wss://mds.qfex.com", on_open=on_open, on_message=on_message ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) import WebSocket from "ws"; const ws = new WebSocket("wss://mds.qfex.com"); ws.on("open", () => { const sub = { type: "subscribe", channels: ["mark_price"], symbols: ["AAPL-USD"] }; ws.send(JSON.stringify(sub)); }); ws.on("message", (msg) => { console.log("Update:", msg.toString()); }); ``` ## Example Response ```json theme={null} { "type": "mark_price", "symbol": "AAPL-USD", "price": "437.45", "time": "2025-05-08T20:52:48.000Z" } ``` # Market Stats Source: https://docs.qfex.com/websocket/channels/mds/market_stats Consolidated BBO, mark price, open interest, funding, and rolling 24-hour volume snapshots. Subscribe to consolidated market statistics for one symbol or all symbols. The `market_stats` stream combines the latest BBO, mark price, open interest, funding rate, and rolling 24-hour traded quantity and notional volume into one message. On subscribe, MDS sends a `subscribed` acknowledgement, followed by a snapshot when data is available. Named-symbol subscriptions refresh once per second. Wildcard subscriptions (`symbols: ["*"]`) now refresh every 10 seconds. Each message is a complete snapshot for the requested symbol or symbols, so clients can replace the previous snapshot rather than applying a delta. ## How it works Connect to the public websocket: * URL: `wss://mds.qfex.com` * Authentication: not required. Then send a subscribe message: ```json theme={null} { "type": "subscribe", "channels": ["market_stats"], "symbols": ["AAPL-USD"] } ``` Or subscribe to all symbols with a wildcard: ```json theme={null} { "type": "subscribe", "channels": ["market_stats"], "symbols": ["*"] } ``` When `market_stats` is the only channel in the request, omitting `symbols` or sending an empty array also subscribes to all symbols: ```json theme={null} { "type": "subscribe", "channels": ["market_stats"] } ``` For multiple named symbols, MDS sends a separate snapshot for each symbol. A wildcard subscription receives one snapshot containing all available symbols, with the top-level `symbol` set to `"*"`. ## Sample code ```python python theme={null} # Python (websocket-client) import json import websocket def on_open(ws): sub = { "type": "subscribe", "channels": ["market_stats"], "symbols": ["*"] } ws.send(json.dumps(sub)) def on_message(ws, message): print("Market stats:", message) if __name__ == "__main__": ws = websocket.WebSocketApp( "wss://mds.qfex.com", on_open=on_open, on_message=on_message ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) import WebSocket from "ws"; const ws = new WebSocket("wss://mds.qfex.com"); ws.on("open", () => { const sub = { type: "subscribe", channels: ["market_stats"], symbols: ["*"], }; ws.send(JSON.stringify(sub)); }); ws.on("message", (msg) => { console.log("Market stats:", msg.toString()); }); ``` ## Example response This illustrative wildcard snapshot contains one symbol: ```json theme={null} { "type": "market_stats", "sequence": 30272540, "time": "2025-09-04T09:26:40Z", "symbol": "*", "bbos": [ { "time": "2025-09-04T09:26:39.545268322Z", "symbol": "AAPL-USD", "bid": [["10101.1", "0.4505414"]], "ask": [["10102.55", "0.57753524"]] } ], "mark_prices": [ { "time": "2025-09-04T09:26:39.545268322Z", "symbol": "AAPL-USD", "price": "10102" } ], "open_interests": [ { "time": "2025-09-04T09:26:39.545268322Z", "symbol": "AAPL-USD", "open_interest": "12.5" } ], "funding_rates": [ { "time": "2025-09-04T09:26:39.545268322Z", "symbol": "AAPL-USD", "funding_rate": "0.00001", "annualised_funding_rate": "0.0876", "time_remaining": 200 } ], "daily_volumes": [ { "symbol": "AAPL-USD", "volume": "12.5" } ], "daily_volumes_notional": [ { "symbol": "AAPL-USD", "volume": "126275" } ] } ``` ## Response fields | Field | Description | | - | - | | `type` | Always `"market_stats"`. | | `sequence` | Sequence number shared with other market-data messages on this connection. | | `time` | UTC timestamp when the snapshot was built. Individual components retain their own latest timestamps. | | `symbol` | The requested symbol, or `"*"` for an all-symbol snapshot. | | `bbos` | Latest [best bid and offer](/websocket/channels/mds/bbo) for each available symbol. Each quote is a `[price, quantity]` pair of decimal strings. | | `mark_prices` | Latest [mark price](/websocket/channels/mds/mark_price) for each available symbol. | | `open_interests` | Latest [open interest](/websocket/channels/mds/open_interest) for each available symbol. | | `funding_rates` | Latest [funding](/websocket/channels/mds/funding_rate) for each available symbol: `funding_rate`, `annualised_funding_rate`, and `time_remaining` in seconds. Rates are decimal strings, not percentage strings. | | `daily_volumes` | Rolling 24-hour traded quantity. Each entry contains `symbol` and a decimal-string `volume`. | | `daily_volumes_notional` | Rolling 24-hour traded notional in USD. Each entry contains `symbol` and a decimal-string `volume`. | The six component fields are always arrays and can be empty when a latest value is unavailable. Join entries by `symbol`; the arrays may contain different sets of symbols. Prices, quantities, open interest, volumes, and funding rates are serialized as decimal strings. ### Unsubscribe (optional) ```json theme={null} { "type": "unsubscribe", "channels": ["market_stats"], "symbols": ["*"] } ``` *** **Notes** * `symbols` supports a single symbol, multiple symbols, or wildcard `"*"`. * The `daily_volumes` and `daily_volumes_notional` names are retained for compatibility. Both measure the previous 24 hours and have no fixed daily reset. They can decrease as older trades leave the window. * Snapshots are sent only when at least one component has data. Component timestamps can be older than the snapshot timestamp. * `market_stats` ignores `intervals` and `sig_figs`; it always uses the default subscription filter. # Minmax Price Source: https://docs.qfex.com/websocket/channels/mds/minmax_price Subscribe to real-time minimum and maximum price updates for symbols. ## How it works Connect to the public websocket: * URL: **wss\://mds.qfex.com** Then send a subscribe message: ```json theme={null} { "type": "subscribe", "channels": ["minmax_price"], "symbols": ["AAPL-USD"] } ``` ## Sample Code ```python python theme={null} # Python (websocket-client) import json import websocket def on_open(ws): sub = { "type": "subscribe", "channels": ["minmax_price"], "symbols": ["AAPL-USD"] } ws.send(json.dumps(sub)) def on_message(ws, message): print("Update:", message) if __name__ == "__main__": ws = websocket.WebSocketApp( "wss://mds.qfex.com", on_open=on_open, on_message=on_message ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) import WebSocket from "ws"; const ws = new WebSocket("wss://mds.qfex.com"); ws.on("open", () => { const sub = { type: "subscribe", channels: ["minmax_price"], symbols: ["AAPL-USD"] }; ws.send(JSON.stringify(sub)); }); ws.on("message", (msg) => { console.log("Update:", msg.toString()); }); ``` ## Example Response ```json theme={null} { "type": "minmax_price", "symbol": "AAPL-USD", "min_price": "400.00", "max_price": "450.00", "time": "2025-05-08T20:52:48.000Z" } ``` # Open Interest Source: https://docs.qfex.com/websocket/channels/mds/open_interest Subscribe to real-time open interest updates for symbols. ## How it works Connect to the public websocket: * URL: **wss\://mds.qfex.com** Then send a subscribe message: ```json theme={null} { "type": "subscribe", "channels": ["open_interest"], "symbols": ["AAPL-USD"] } ``` ## Sample Code ```python python theme={null} # Python (websocket-client) import json import websocket def on_open(ws): sub = { "type": "subscribe", "channels": ["open_interest"], "symbols": ["AAPL-USD"] } ws.send(json.dumps(sub)) def on_message(ws, message): print("Update:", message) if __name__ == "__main__": ws = websocket.WebSocketApp( "wss://mds.qfex.com", on_open=on_open, on_message=on_message ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) import WebSocket from "ws"; const ws = new WebSocket("wss://mds.qfex.com"); ws.on("open", () => { const sub = { type: "subscribe", channels: ["open_interest"], symbols: ["AAPL-USD"] }; ws.send(JSON.stringify(sub)); }); ws.on("message", (msg) => { console.log("Update:", msg.toString()); }); ``` ## Example Response ```json theme={null} { "type": "open_interest", "symbol": "AAPL-USD", "open_interest": "1000000", "time": "2025-05-08T20:52:48.000Z" } ``` # Pulsed Order Book Source: https://docs.qfex.com/websocket/channels/mds/orderbook QFEX provides a **500ms pulsed stream** of the order book up to 20 levels deep via websocket. ## How it works Connect to the public websocket: * URL: **wss\://mds.qfex.com** Then send a subscribe message: ```json theme={null} { "type": "subscribe", "channels": ["level2"], "symbols": ["AAPL-USD", "US500-USD"] } ``` You can also use a wildcard to subscribe to **all** symbols: ```json theme={null} { "type": "subscribe", "channels": ["level2"], "symbols": ["*"] } ``` ### Significant Figures (Optional) You can optionally specify `sig_figs` to aggregate order book levels: ```json theme={null} { "type": "subscribe", "channels": ["level2"], "symbols": ["AAPL-USD"], "sig_figs": [1] } ``` * **Default**: `0` (no aggregation) * **Valid values**: `0`, `1`, or `2` * **Purpose**: Aggregates individual levels to the next significant figure from the tick size * Bids round down * Offers round up * **Response format**: Unchanged (same structure as without `sig_figs`) ## Sample Code ```python python theme={null} # Python (websocket-client) # pip install websocket-client import json import websocket def on_open(ws): sub = { "type": "subscribe", "channels": ["level2"], "symbols": ["US500-USD"] # or ["*"] } ws.send(json.dumps(sub)) def on_message(ws, message): data = json.loads(message) print("OrderBook update:", json.dumps(data, indent=2)) def on_error(ws, error): print("Error:", error) def on_close(ws, close_status_code, close_msg): print("Closed:", close_status_code, close_msg) if __name__ == "__main__": ws = websocket.WebSocketApp( "wss://mds.qfex.com", on_open=on_open, on_message=on_message, on_error=on_error, on_close=on_close, ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) // npm i ws import WebSocket from "ws"; const ws = new WebSocket("wss://mds.qfex.com"); ws.on("open", () => { const sub = { type: "subscribe", channels: ["level2"], symbols: ["US500-USD"], // or ["*"] }; ws.send(JSON.stringify(sub)); }); ws.on("message", (msg) => { try { const data = JSON.parse(msg.toString()); console.log("OrderBook update:", data); } catch (e) { console.error("Parse error:", e); } }); ws.on("error", (err) => console.error("WS error:", err)); ws.on("close", (code, reason) => console.log("Closed:", code, reason?.toString()), ); ``` ```go go theme={null} // Go (gorilla/websocket) // go get github.com/gorilla/websocket package main import ( "fmt" "log" "os" "os/signal" "github.com/gorilla/websocket" ) func main() { c, _, err := websocket.DefaultDialer.Dial("wss://mds.qfex.com", nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() sub := map[string]interface{}{ "type": "subscribe", "channels": []string{"level2"}, "symbols": []string{"US500-USD"}, // or []string{"*"} } if err := c.WriteJSON(sub); err != nil { log.Fatal("write:", err) } done := make(chan struct{}) go func() { defer close(done) for { _, message, err := c.ReadMessage() if err != nil { log.Println("read:", err) return } fmt.Printf("OrderBook update: %s\n", message) } }() // keep running until Ctrl+C interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, os.Interrupt) <-interrupt } ``` ```java java theme={null} // Java (OkHttp WebSocket) // Gradle: implementation("com.squareup.okhttp3:okhttp:4.12.0") import java.util.concurrent.TimeUnit; import okhttp3.*; public class OrderBookWs { public static void main(String[] args) { OkHttpClient client = new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS) .build(); Request request = new Request.Builder() .url("wss://mds.qfex.com") .build(); WebSocketListener listener = new WebSocketListener() { @Override public void onOpen(WebSocket webSocket, Response response) { String sub = "{\"type\":\"subscribe\",\"channels\":[\"level2\"],\"symbols\":[\"US500-USD\"]}"; webSocket.send(sub); } @Override public void onMessage(WebSocket webSocket, String text) { System.out.println("OrderBook update: " + text); } @Override public void onFailure(WebSocket webSocket, Throwable t, Response response) { System.err.println("WS error: " + t.getMessage()); } }; client.newWebSocket(request, listener); // Keep JVM alive try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ignored) {} } } ``` ## Example Response ```json theme={null} { "type": "level2", "symbol": "AAPL-USD", "bid": [ ["10101.10", "0.45054140"], ["10101.00", "0.55054140"] ], "ask": [ ["10102.55", "0.57753524"], ["10102.65", "0.57753524"] ], "sequence": 30272539, "time": "2025-09-04T09:26:39.545268322Z" } ``` ### Unsubscribe (optional) ```json theme={null} { "type": "unsubscribe", "channels": ["level2"], "symbols": ["US500-USD"] } ``` *** **Notes** * Pulsed at 500ms intervals. * Depth: up to 20 levels. * `symbols` accepts a wildcard `*` to stream all symbols. # Reference Data Source: https://docs.qfex.com/websocket/channels/mds/refdata Reference Data returns a list of all available symbols and their reference data. > [!NOTE] > > This is available via REST at `https://api.qfex.com/refdata`. For funding > behavior by symbol, use `market_hours`. See [`/qfex/funding`](/qfex/funding). ## Example Response Shape > The websocket payloads mirror the REST fields. A single message may include one or more symbols. ```json theme={null} { "type": "refdata", "data": [ { "clobPairId": "4", "symbol": "GOOG-USD", "underlier_price": "154.16", "price_change_24h": "-2.1", "tick_size": "0.05", "lot_size": "0.001", "min_price": "112", "max_price": "209", "min_quantity": "0.001", "max_quantity": "100000000", "base_asset": "GOOG", "quote_asset": "USD", "margin_asset": "USD", "order_time_in_force": ["GTC", "IOC", "FOK"], "order_types": ["LIMIT", "MARKET", "ALO"], "default_max_leverage": 20, "market_hours": { "tz": "America/New_York", "monday": { "open": "04:00:00", "close": "20:00:00" }, "tuesday": { "open": "04:00:00", "close": "20:00:00" }, "wednesday": { "open": "04:00:00", "close": "20:00:00" }, "thursday": { "open": "04:00:00", "close": "20:00:00" }, "friday": { "open": "04:00:00", "close": "20:00:00" } } } ] } ``` # Public Trades Source: https://docs.qfex.com/websocket/channels/mds/trades QFEX provides a **realtime stream of executed trades** across all symbols via websocket. ## How it works Connect to the public websocket: * URL: **wss\://mds.qfex.com** Then send a subscribe message: ```json theme={null} { "type": "subscribe", "channels": ["trade"], "symbols": ["AAPL-USD", "US500-USD"] } ``` ## Sample Code ```python python theme={null} # Python (websocket-client) # pip install websocket-client import json import websocket def on_open(ws): sub = { "type": "subscribe", "channels": ["trade"], "symbols": ["US500-USD"] # or ["*"] } ws.send(json.dumps(sub)) def on_message(ws, message): data = json.loads(message) print("Trade update:", json.dumps(data, indent=2)) def on_error(ws, error): print("Error:", error) def on_close(ws, close_status_code, close_msg): print("Closed:", close_status_code, close_msg) if __name__ == "__main__": ws = websocket.WebSocketApp( "wss://mds.qfex.com", on_open=on_open, on_message=on_message, on_error=on_error, on_close=on_close, ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) // npm i ws import WebSocket from "ws"; const ws = new WebSocket("wss://mds.qfex.com"); ws.on("open", () => { const sub = { type: "subscribe", channels: ["trade"], symbols: ["US500-USD"], // or ["*"] }; ws.send(JSON.stringify(sub)); }); ws.on("message", (msg) => { try { const data = JSON.parse(msg.toString()); console.log("Trade update:", data); } catch (e) { console.error("Parse error:", e); } }); ws.on("error", (err) => console.error("WS error:", err)); ws.on("close", (code, reason) => console.log("Closed:", code, reason?.toString()) ); ``` ```go go theme={null} // Go (gorilla/websocket) // go get github.com/gorilla/websocket package main import ( "fmt" "log" "os" "os/signal" "github.com/gorilla/websocket" ) func main() { c, _, err := websocket.DefaultDialer.Dial("wss://mds.qfex.com", nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() sub := map[string]interface{}{ "type": "subscribe", "channels": []string{"trade"}, "symbols": []string{"US500-USD"}, // or []string{"*"} } if err := c.WriteJSON(sub); err != nil { log.Fatal("write:", err) } done := make(chan struct{}) go func() { defer close(done) for { _, message, err := c.ReadMessage() if err != nil { log.Println("read:", err) return } fmt.Printf("Trade update: %s\n", message) } }() // keep running until Ctrl+C interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, os.Interrupt) <-interrupt } ``` ```java java theme={null} // Java (OkHttp WebSocket) // Gradle: implementation("com.squareup.okhttp3:okhttp:4.12.0") import java.util.concurrent.TimeUnit; import okhttp3.*; public class TradeWs { public static void main(String[] args) { OkHttpClient client = new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS) .build(); Request request = new Request.Builder() .url("wss://mds.qfex.com") .build(); WebSocketListener listener = new WebSocketListener() { @Override public void onOpen(WebSocket webSocket, Response response) { String sub = "{\"type\":\"subscribe\",\"channels\":[\"trade\"],\"symbols\":[\"US500-USD\"]}"; webSocket.send(sub); } @Override public void onMessage(WebSocket webSocket, String text) { System.out.println("Trade update: " + text); } @Override public void onFailure(WebSocket webSocket, Throwable t, Response response) { System.err.println("WS error: " + t.getMessage()); } }; client.newWebSocket(request, listener); // Keep JVM alive try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ignored) {} } } ``` ## Example Response ```json theme={null} { "type": "trade", "trade_id": "ac928c66-ca53-498f-9c13-a110027a60e8", "sequence": 50, "time": "2014-11-07T08:19:27.028459Z", "symbol": "AAPL-USD", "size": "5.23512", "price": "400.23", "side": "sell", "execution_type": "NEW" } ``` Please see [Enums](/api-reference/enums) for further details on `execution_type` and others. ### Unsubscribe (optional) ```json theme={null} { "type": "unsubscribe", "channels": ["trade"], "symbols": ["US500-USD"] } ``` *** **Notes** * Messages are pushed in realtime as trades occur. * Supports multiple symbols or a wildcard `*`. * Fields mirror REST and gRPC trade objects. # Underlier Price Source: https://docs.qfex.com/websocket/channels/mds/underlier QFEX provides a **pulsed underlier price stream**, aggregated across multiple exchanges.\ It is used in the **funding calculation** for perpetual markets. > [!NOTE] > > Outside of extended market hours, the underlier price will be returned with > `source: internal`. See > [`/qfex/contract-specifications#oracle-pricing`](/qfex/contract-specifications#oracle-pricing) > for oracle details and [`/qfex/funding`](/qfex/funding) for funding-hour > behavior. ## How it works Connect to the public websocket: * URL: **wss\://mds.qfex.com** Then send a subscribe message: ```json theme={null} {"type": "subscribe","channels": ["underlier"],"symbols": ["AAPL-USD","US500-USD"]} ``` ## Sample Code ```python python theme={null} # Python (websocket-client) # pip install websocket-client import json import websocket def on_open(ws): sub = { "type": "subscribe", "channels": ["underlier"], "symbols": ["US500-USD"] # or ["*"] } ws.send(json.dumps(sub)) def on_message(ws, message): data = json.loads(message) print("Underlier update:", json.dumps(data, indent=2)) def on_error(ws, error): print("Error:", error) def on_close(ws, close_status_code, close_msg): print("Closed:", close_status_code, close_msg) if __name__ == "__main__": ws = websocket.WebSocketApp( "wss://mds.qfex.com", on_open=on_open, on_message=on_message, on_error=on_error, on_close=on_close, ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) // npm i ws import WebSocket from "ws"; const ws = new WebSocket("wss://mds.qfex.com"); ws.on("open", () => { const sub = { type: "subscribe", channels: ["underlier"], symbols: ["US500-USD"], // or ["*"] }; ws.send(JSON.stringify(sub)); }); ws.on("message", (msg) => { try { const data = JSON.parse(msg.toString()); console.log("Underlier update:", data); } catch (e) { console.error("Parse error:", e); } }); ws.on("error", (err) => console.error("WS error:", err)); ws.on("close", (code, reason) => console.log("Closed:", code, reason?.toString())); ``` ```go go theme={null} // Go (gorilla/websocket) // go get github.com/gorilla/websocket package main import ( "fmt" "log" "os" "os/signal" "github.com/gorilla/websocket" ) func main() { c, _, err := websocket.DefaultDialer.Dial("wss://mds.qfex.com", nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() sub := map[string]interface{}{ "type": "subscribe", "channels": []string{"underlier"}, "symbols": []string{"US500-USD"}, // or []string{"*"} } if err := c.WriteJSON(sub); err != nil { log.Fatal("write:", err) } done := make(chan struct{}) go func() { defer close(done) for { _, message, err := c.ReadMessage() if err != nil { log.Println("read:", err) return } fmt.Printf("Underlier update: %s\n", message) } }() // keep running until Ctrl+C interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, os.Interrupt) <-interrupt } ``` ```java java theme={null} // Java (OkHttp WebSocket) // Gradle: implementation("com.squareup.okhttp3:okhttp:4.12.0") import java.util.concurrent.TimeUnit; import okhttp3.*; public class UnderlierWs { public static void main(String[] args) { OkHttpClient client = new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS) .build(); Request request = new Request.Builder() .url("wss://mds.qfex.com") .build(); WebSocketListener listener = new WebSocketListener() { @Override public void onOpen(WebSocket webSocket, Response response) { String sub = "{\"type\":\"subscribe\",\"channels\":[\"underlier\"],\"symbols\":[\"US500-USD\"]}"; webSocket.send(sub); } @Override public void onMessage(WebSocket webSocket, String text) { System.out.println("Underlier update: " + text); } @Override public void onFailure(WebSocket webSocket, Throwable t, Response response) { System.err.println("WS error: " + t.getMessage()); } }; client.newWebSocket(request, listener); // Keep JVM alive try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ignored) {} } } ``` ## Example Response ```json theme={null} { "type": "underlier", "sequence": 50, "time": "2014-11-07T08:19:27.028459Z", "symbol": "AAPL-USD", "price": "400.23", "source": "external" } ``` ### Unsubscribe (optional) ```json theme={null} {"type":"unsubscribe","channels":["underlier"],"symbols":["US500-USD"]} ``` *** **Notes** * Pulsed stream (aggregated over multiple exchanges). * Used in funding rate calculation. * Outside extended hours, prices may continue with `source: internal`. # Order Entry — Add Order Source: https://docs.qfex.com/websocket/channels/trade/add_order Order entry is performed over **WebSocket**. * **Endpoint:** `wss://trade.qfex.com?api_key=YOUR_API_KEY` * **Authenticate within 1 minute of connecting.** ## 1) Authenticate Send this immediately after you connect: ```json theme={null} { "type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." } } } ``` ## 2) Add Order ```json theme={null} { "type": "add_order", "params": { "symbol": "AAPL-USD", "side": "BUY", "order_type": "LIMIT", "order_time_in_force": "GTC", "quantity": 1, "price": 200, "take_profit": 0, "stop_loss": 0, "reduce_only": 0 } } ``` ## Parameters | Field | Type | Required | Description | | - | - | - | - | | `symbol` | string | ✅ | The market symbol (e.g. `AAPL-USD`). | | `side` | enum | ✅ | Order direction. See [OrderDirection](/api-reference/enums#orderdirection). | | `order_type` | enum | ✅ | Type of order. See [OrderType](/api-reference/enums#ordertype). | | `order_time_in_force` | enum | ✅ | Time in force policy. See [OrderTimeInForce](/api-reference/enums#ordertimeinforce). | | `quantity` | number | ✅ | Quantity of the order. Must respect lot size and min/max limits (see reference data). | | `price` | number | ⚠️ (Limit/ALO) | Price for limit/ALO orders. Ignored for market orders. Must respect tick size and price bands. | | `take_profit` | number | Optional | Optional take-profit price. Set `0` if unused. | | `stop_loss` | number | Optional | Optional stop-loss price. Set `0` if unused. | | `client_order_id` | string | Optional | Optional client-assigned ID for tracking orders. If empty, one is generated. | > ⚠️ Notes: > > * `price` is required for `LIMIT` and `ALO` orders, but ignored for `MARKET`. > * Tick size, lot size, and min/max order quantity are symbol-specific. See **Reference Data API**. > * An order may be rejected with [OrderStatus](/api-reference/enums#orderstatus) codes if parameters are invalid. ## Sample Code ```python python theme={null} # Python (websocket-client) # pip install websocket-client import json import websocket API_KEY = "YOUR_API_KEY" def send(ws, obj): ws.send(json.dumps(obj)) def on_open(ws): # 1) Authenticate # https://docs.qfex.com/websocket/channels/trade/authenticate#how-it-works send(ws, {"type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." }}}) # 2) Sub to order responses # https://docs.qfex.com/websocket/channels/trade/order send(ws, {"type": "subscribe", "params": {"channels": ["order_responses"]}}) # 3) Add order send(ws, { "type": "add_order", "params": { "symbol": "AAPL-USD", "side": "BUY", "order_type": "LIMIT", "order_time_in_force": "GTC", "quantity": 1, "price": 200, "take_profit": 0, # Optional "stop_loss": 0, # Optional "resuce_only": True # Optional } }) ws = websocket.WebSocketApp( "wss://trade.qfex.com?api_key=YOUR_API_KEY", on_open=on_open, on_message=lambda _, m: print(m), on_error=lambda _, e: print("Error:", e), on_close=lambda *_: print("Closed"), ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) // npm i ws const WebSocket = require("ws"); const ws = new WebSocket("wss://trade.qfex.com?api_key=YOUR_API_KEY"); ws.on("open", () => { // 1) Authenticate // https://docs.qfex.com/websocket/channels/trade/authenticate#how-it-works ws.send(JSON.stringify({"type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." }}})); // 2) Sub to order responses // https://docs.qfex.com/websocket/channels/trade/order ws.send( JSON.stringify({ type: "subscribe", params: { channels: ["order_responses"] }, }) ); }); // 3) Add order ws.send(JSON.stringify({ type: "add_order", params: { symbol: "AAPL-USD", side: "BUY", order_type: "LIMIT", order_time_in_force: "GTC", quantity: 1, price: 200, take_profit: 0, // Optional stop_loss: 0, // Optional reduce_only: true // Optional } })); }); ws.on("message", (m) => console.log(m.toString())); ws.on("error", (e) => console.error("WS error:", e)); ws.on("close", (code, reason) => console.log("Closed:", code, reason?.toString()) ); ``` ```go go theme={null} // Go (gorilla/websocket) // go get github.com/gorilla/websocket package main import ( "log" "github.com/gorilla/websocket" ) func mustWriteJSON(c *websocket.Conn, v any) { if err := c.WriteJSON(v); err != nil { log.Fatal("write:", err) } } func main() { c, _, err := websocket.DefaultDialer.Dial("wss://trade.qfex.com?api_key=YOUR_API_KEY", nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() // 1) Authenticate mustWriteJSON(c, map[string]any{ "type": "auth", "params": map[string]any{"hmac": map[string]any{"public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..."}} }) // 2) Sub to order responses // https://docs.qfex.com/websocket/channels/trade/order mustWriteJSON(c, map[string]any{ "type": "subscribe", "params": map[string]any{"channels": []string{"order_responses"}}, }) // 3) Add order mustWriteJSON(c, map[string]any{ "type": "add_order", "params": map[string]any{ "symbol": "AAPL-USD", "side": "BUY", "order_type": "LIMIT", "order_time_in_force": "GTC", "quantity": 1, "price": 200, "take_profit": 0, // Optional "stop_loss": 0, // Optional "reduce_only": true, // Optional }, }) } ``` ```java java theme={null} // Java (OkHttp WebSocket) // Gradle: implementation("com.squareup.okhttp3:okhttp:4.12.0") import java.util.concurrent.TimeUnit; import okhttp3.*; public class AddOrderWs { public static void main(String[] args) { OkHttpClient client = new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS) .build(); Request req = new Request.Builder() .url("wss://trade.qfex.com?api_key=YOUR_API_KEY") .build(); WebSocketListener li = new WebSocketListener() { @Override public void onOpen(WebSocket ws, Response r) { // 1) Authenticate ws.send("{\"type\": \"auth\", \"params\": { \"hmac\": { \"public_key\": \"qfex_pub_xxxxx\", \"nonce\": \"c0ffee...\..\", \"unix_ts\": 1760545414, \"signature\": \"5f2e...\" }}}"); // 2) Sub to order responses // https://docs.qfex.com/websocket/channels/trade/order ws.send("{\"type\":\"subscribe\",\"params\":{\"channels\":[\"order_responses\"]}}"); // 3) Add order ws.send("{\"type\":\"add_order\",\"params\":{\"symbol\":\"AAPL-USD\",\"side\":\"BUY\",\"order_type\":\"LIMIT\",\"order_time_in_force\":\"GFD\",\"quantity\":1,\"price\":200,\"take_profit\":0,\"stop_loss\":0}}"); } @Override public void onMessage(WebSocket ws, String text) { System.out.println(text); } @Override public void onFailure(WebSocket ws, Throwable t, Response r) { System.err.println("WS error: " + t.getMessage()); } }; client.newWebSocket(req, li); try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ignored) {} } } ``` ## Example Response When you place an order, you will receive an `order_response` with status `ACK`: ```json theme={null} { "order_response": { "order_id": "5b309929-206f-40ec-804d-cbe46e81afc1", "symbol": "AAPL-USD", "status": "ACK", "quantity": 1.0, "price": 200.0, "take_profit": 0.0, "stop_loss": 0.0, "side": "BUY", "type": "LIMIT", "time_in_force": "GTC", "user_id": "0020ce8e-eaee-480e-8d7f-b9241d756ee5", "client_order_id": "", "quantity_remaining": 1.0, "trade_id": null } } ``` # Order Entry — Add TWAP Source: https://docs.qfex.com/websocket/channels/trade/add_twap TWAP order entry is performed over **WebSocket**. * **Endpoint:** `wss://trade.qfex.com?api_key=YOUR_API_KEY` * **Authenticate within 1 minute of connecting.** ## 1) Authenticate Send this immediately after you connect: ```json theme={null} { "type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." } } } ``` ## 2) Add TWAP ```json theme={null} { "type": "add_twap", "params": { "symbol": "AAPL-USD", "side": "BUY", "total_quantity": 10, "num_orders": 5, "order_interval_secs": 30, "reduce_only": false, "client_twap_id": "rebalance-aapl-001", "start_time": 1785499200.25, "worst_price": 250 } } ``` ## Parameters | Field | Type | Required | Description | | - | - | - | - | | `symbol` | string | ✅ | The market symbol (for example `AAPL-USD`). | | `side` | enum | ✅ | TWAP direction. See [OrderDirection](/api-reference/enums#orderdirection). | | `total_quantity` | number | ✅ | Total quantity to execute across the full TWAP. It is validated against the symbol's minimum child-order size requirement. | | `num_orders` | number | ✅ | Number of child orders the engine will schedule. | | `order_interval_secs` | number | ✅ | Delay in seconds between child orders. | | `reduce_only` | bool | ✅ | If `true`, the TWAP may only reduce an existing position. | | `client_twap_id` | string | Optional | Optional client-assigned TWAP identifier for tracking and cancellation. | | `start_time` | number | Optional | Unix timestamp in seconds, with optional fractional precision, at which the first child order may be placed. If omitted or in the past, execution starts immediately. | | `worst_price` | number | Optional | Exact IOC limit price used for every child order: the maximum execution price for buys or minimum execution price for sells. If omitted, the limit is calculated from the mark price. | > ⚠️ Notes: > > * TWAP orders are submitted to `trade.qfex.com` using the `add_twap` incoming message. > * A successful TWAP is returned as `twap_response` and also appears in `all_orders_response.twaps`. > * TWAPs can be cancelled with `cancel_order` using `cancel_order_id_type` set to `twap_id` or `client_twap_id`. > * The account must satisfy the same T\&C requirements as standard order entry. ## Validation Rules The server validates `add_twap` requests before forwarding them to the TWAP service. | Rule | Behavior | | - | - | | Symbol must be active | Inactive or unknown symbols are rejected. | | `order_interval_secs >= 30` | The minimum interval is **30 seconds**. | | `num_orders >= 2` | A TWAP must contain at least **2 child orders**. | | `total_quantity / num_orders >= min_quantity` | The average child order size must not fall below the symbol's `min_quantity`. | | `client_twap_id` length | If provided, it must be within the same configured min/max length limits used for `client_order_id`. | | `start_time` format | If provided, it must be a finite Unix timestamp in seconds within the supported protobuf timestamp range. A past timestamp is treated as the current time. | | `worst_price` price bounds | If provided, it must be finite and within the permitted price bounds: no greater than the symbol maximum for buys, and no lower than the symbol minimum or greater than the platform maximum for sells. | | `worst_price` tick alignment | If provided, it must align with the symbol's `tick_size`. | Additional implementation notes: * `total_quantity` is scaled using the symbol quantity precision before validation. * The current validation shown here does **not** document any explicit requirement that `total_quantity` align to `lot_size`, only that the average child size is not below `min_quantity`. * Errors from local validation are returned as `err` with `error_code: "invalid_parameter"` and a descriptive `message`. ## Sample Code ```python python theme={null} # Python (websocket-client) # pip install websocket-client import json import websocket def send(ws, obj): ws.send(json.dumps(obj)) def on_open(ws): send(ws, {"type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." }}}) send(ws, { "type": "add_twap", "params": { "symbol": "AAPL-USD", "side": "BUY", "total_quantity": 10, "num_orders": 5, "order_interval_secs": 30, "reduce_only": False, "client_twap_id": "rebalance-aapl-001", "start_time": 1785499200.25, "worst_price": 250 } }) ws = websocket.WebSocketApp( "wss://trade.qfex.com?api_key=YOUR_API_KEY", on_open=on_open, on_message=lambda _, m: print(m), ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) // npm i ws import WebSocket from "ws"; const ws = new WebSocket("wss://trade.qfex.com?api_key=YOUR_API_KEY"); ws.on("open", () => { ws.send(JSON.stringify({ type: "auth", params: { hmac: { public_key: "qfex_pub_xxxxx", nonce: "c0ffee...", unix_ts: 1760545414, signature: "5f2e...", }, }, })); ws.send(JSON.stringify({ type: "add_twap", params: { symbol: "AAPL-USD", side: "BUY", total_quantity: 10, num_orders: 5, order_interval_secs: 30, reduce_only: false, client_twap_id: "rebalance-aapl-001", start_time: 1785499200.25, worst_price: 250, }, })); }); ws.on("message", (m) => console.log(m.toString())); ``` ```go go theme={null} // Go (gorilla/websocket) package main import ( "log" "github.com/gorilla/websocket" ) func mustWriteJSON(c *websocket.Conn, v any) { if err := c.WriteJSON(v); err != nil { log.Fatal("write:", err) } } func main() { c, _, err := websocket.DefaultDialer.Dial("wss://trade.qfex.com?api_key=YOUR_API_KEY", nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() mustWriteJSON(c, map[string]any{ "type": "auth", "params": map[string]any{"hmac": map[string]any{"public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..."}}, }) mustWriteJSON(c, map[string]any{ "type": "add_twap", "params": map[string]any{ "symbol": "AAPL-USD", "side": "BUY", "total_quantity": 10, "num_orders": 5, "order_interval_secs": 30, "reduce_only": false, "client_twap_id": "rebalance-aapl-001", "start_time": 1785499200.25, "worst_price": 250, }, }) } ``` ```java java theme={null} // Java (OkHttp WebSocket) import java.util.concurrent.TimeUnit; import okhttp3.*; public class AddTwapWs { public static void main(String[] args) { OkHttpClient client = new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS) .build(); Request req = new Request.Builder() .url("wss://trade.qfex.com?api_key=YOUR_API_KEY") .build(); WebSocketListener li = new WebSocketListener() { @Override public void onOpen(WebSocket ws, Response r) { ws.send("{\"type\":\"auth\",\"params\":{\"hmac\":{\"public_key\":\"qfex_pub_xxxxx\",\"nonce\":\"c0ffee...\",\"unix_ts\":1760545414,\"signature\":\"5f2e...\"}}}"); ws.send("{\"type\":\"add_twap\",\"params\":{\"symbol\":\"AAPL-USD\",\"side\":\"BUY\",\"total_quantity\":10,\"num_orders\":5,\"order_interval_secs\":30,\"reduce_only\":false,\"client_twap_id\":\"rebalance-aapl-001\",\"start_time\":1785499200.25,\"worst_price\":250}}"); } @Override public void onMessage(WebSocket ws, String text) { System.out.println(text); } }; client.newWebSocket(req, li); try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ignored) {} } } ``` ## TWAP Response Shape Successful TWAP requests are returned as a `twap_response` object: ```json theme={null} { "twap_response": { "twap_id": "0c7c8e4d-f67e-4aa5-9c64-36a1a622ac35", "client_twap_id": "rebalance-aapl-001", "user_id": "0020ce8e-eaee-480e-8d7f-b9241d756ee5", "status": "ENGINE_STATUS", "symbol": "AAPL-USD", "total_quantity": 10, "filled_quantity": 0, "average_fill_price": 0, "worst_price": 250, "total_num_orders": 5, "order_interval_secs": 30, "reduce_only": false, "side": "BUY", "start_time": 1785499200.25, "updated_at": 1760545414.123, "created_at": 1760545414.123 } } ``` | Field | Type | Description | | - | - | - | | `twap_id` | string | Server-generated TWAP identifier. | | `client_twap_id` | string or null | Optional client-provided TWAP identifier. | | `user_id` | string | Account identifier for the TWAP owner. | | `status` | string | Current engine status for the TWAP. | | `symbol` | string | Market symbol. | | `total_quantity` | number | Total requested quantity across the TWAP. | | `filled_quantity` | number | Quantity already filled. | | `average_fill_price` | number | Average execution price across all fills so far. | | `worst_price` | number or null | Configured IOC limit price for every child order, or `null` when the limit is calculated from the mark price. | | `total_num_orders` | number | Total number of scheduled child orders. | | `order_interval_secs` | number | Interval between child orders in seconds. | | `reduce_only` | bool | Whether the TWAP is reduce-only. | | `side` | enum | TWAP direction. | | `start_time` | number or null | Effective scheduled start time as Unix seconds with fractional precision. Omitted or past request values are returned as the time at which the TWAP was accepted. | | `updated_at` | number or null | Last update timestamp in seconds with fractional precision. | | `created_at` | number or null | Creation timestamp in seconds with fractional precision. | # Authenticate Source: https://docs.qfex.com/websocket/channels/trade/authenticate Connections to `trade.qfex.com` require authentication. You must authenticate **within 1 minute** of connecting or the server will close the connection. Authentication uses an HMAC-SHA256 signature or a valid JWT token. ## How it works ### HMAC 1. Connect to **wss\://trade.qfex.com?api\_key=YOUR\_PUBLIC\_KEY**. 2. Generate a cryptographically secure random nonce (hex encoded, max 100 characters) and capture the current Unix timestamp. 3. Build the string `${nonce}:${unix_ts}` and compute an HMAC-SHA256 using your secret key. Hex-encode the result to get the signature. 4. Send the auth payload with the `hmac` block shown below. The nonce must be unique within a 15 minute window. ```json theme={null} { "type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." }, "account_id": "11111111-1111-1111-1111-111111111111" } } ``` ### JWT For a JWT session, including a token obtained through QFEX OAuth, connect with the JWT as the query parameter: ```text theme={null} wss://trade.qfex.com/?jwt=URL_ENCODED_JWT ``` Then authenticate with the same JWT: ```json theme={null} { "type": "auth", "params": { "jwt": "eyJhbGciOiJFUzI1NiIsImtpZCI6ImNiYzVmZWNmLTlhODItNGFlNy04NDFkLTBkMTdjMjUzMWM3OCIsInR5cCI6IkpXVCJ9...", "account_id": "11111111-1111-1111-1111-111111111111" } } ``` If you want the authenticated Trade WebSocket session to use a subaccount, include an optional `account_id` field alongside either `hmac` or `jwt`. When omitted, the connection uses the primary account. ## Builder attribution To attribute trading fees to a builder, include optional `builder_code` on the same auth payload. The builder code is a UUID created by the builder in Developer Settings or through the [Builder Codes API](/api-reference/builder-codes). ```json theme={null} { "type": "auth", "params": { "jwt": "USER_JWT", "builder_code": "11111111-1111-4111-8111-111111111111" } } ``` The code applies for the life of the connection. Orders, stop orders, and TWAPs sent over the connection inherit it automatically. Do not include the builder code on every individual order. Browser WebSocket clients cannot set custom handshake headers, so `params.builder_code` is the supported way to attach it. See the [Builder Integration Guide](/api-reference/builder-integration) for the full OAuth-to-trading flow. ## Sample Code ```python python theme={null} # Python (websocket-client) # pip install websocket-client import hashlib import hmac import json import secrets import time import websocket PUB_KEY = "qfex_pub_xxxxx" SECRET_KEY = "qfex_secret_yyyyyy" def build_auth_message(): nonce = secrets.token_hex(16) unix_ts = int(time.time()) payload = f"{nonce}:{unix_ts}".encode() signature = hmac.new(SECRET_KEY.encode(), payload, hashlib.sha256).hexdigest() return { "type": "auth", "params": { "hmac": { "public_key": PUB_KEY, "nonce": nonce, "unix_ts": unix_ts, "signature": signature, } }, } def on_open(ws): ws.send(json.dumps(build_auth_message())) def on_message(_, message): print("Message:", message) def on_error(_, error): print("Error:", error) def on_close(_, code, reason): print("Closed:", code, reason) ws = websocket.WebSocketApp( "wss://trade.qfex.com?api_key=" + PUB_KEY, on_open=on_open, on_message=on_message, on_error=on_error, on_close=on_close, ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) // npm i ws import crypto from "crypto"; import WebSocket from "ws"; const pubKey = "qfex_pub_xxxxx"; const secretKey = "qfex_secret_yyyyyy"; function buildAuthMessage() { const nonce = crypto.randomBytes(16).toString("hex"); const unixTs = Math.floor(Date.now() / 1000); const payload = `${nonce}:${unixTs}`; const signature = crypto .createHmac("sha256", secretKey) .update(payload) .digest("hex"); return { type: "auth", params: { hmac: { public_key: pubKey, nonce, unix_ts: unixTs, signature, }, }, }; } const ws = new WebSocket(`wss://trade.qfex.com?api_key=${pubKey}`); ws.on("open", () => { ws.send(JSON.stringify(buildAuthMessage())); }); ws.on("message", (msg) => { console.log("Message:", msg.toString()); }); ws.on("error", (err) => console.error("WS error:", err)); ws.on("close", (code, reason) => { console.log("Closed:", code, reason?.toString() ?? ""); }); ``` ```go go theme={null} // Go (gorilla/websocket) // go get github.com/gorilla/websocket package main import ( "crypto/hmac" "crypto/rand" "crypto/sha256" "encoding/hex" "fmt" "log" "os" "os/signal" "time" "github.com/gorilla/websocket" ) func main() { pubKey := "qfex_pub_xxxxx" secretKey := []byte("qfex_secret_yyyyyy") c, _, err := websocket.DefaultDialer.Dial("wss://trade.qfex.com?api_key="+pubKey, nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() nonceBytes := make([]byte, 16) if _, err := rand.Read(nonceBytes); err != nil { log.Fatal("nonce:", err) } nonce := hex.EncodeToString(nonceBytes) unixTs := time.Now().Unix() message := fmt.Sprintf("%s:%d", nonce, unixTs) h := hmac.New(sha256.New, secretKey) h.Write([]byte(message)) signature := hex.EncodeToString(h.Sum(nil)) auth := map[string]any{ "type": "auth", "params": map[string]any{ "hmac": map[string]any{ "public_key": pubKey, "nonce": nonce, "unix_ts": unixTs, "signature": signature, }, }, } if err := c.WriteJSON(auth); err != nil { log.Fatal("write auth:", err) } go func() { for { _, message, err := c.ReadMessage() if err != nil { log.Println("read:", err) return } fmt.Printf("Message: %s\n", message) } }() interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, os.Interrupt) <-interrupt } ``` ```java java theme={null} // Java (OkHttp WebSocket) // Gradle: implementation("com.squareup.okhttp3:okhttp:4.12.0") import java.nio.charset.StandardCharsets; import java.security.SecureRandom; import java.util.concurrent.TimeUnit; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import okhttp3.*; public class AuthWs { private static final String PUB_KEY = "qfex_pub_xxxxx"; private static final String SECRET_KEY = "qfex_secret_yyyyyy"; public static void main(String[] args) { OkHttpClient client = new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS) .build(); Request request = new Request.Builder() .url("wss://trade.qfex.com?api_key=" + PUB_KEY) .build(); WebSocketListener listener = new WebSocketListener() { @Override public void onOpen(WebSocket ws, Response response) { ws.send(buildAuthPayload()); } @Override public void onMessage(WebSocket ws, String text) { System.out.println("Message: " + text); } @Override public void onFailure(WebSocket ws, Throwable t, Response r) { System.err.println("WS error: " + t.getMessage()); } }; client.newWebSocket(request, listener); try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ignored) { Thread.currentThread().interrupt(); } } private static String buildAuthPayload() { SecureRandom random = new SecureRandom(); byte[] nonceBytes = new byte[16]; random.nextBytes(nonceBytes); String nonce = bytesToHex(nonceBytes); long unixTs = System.currentTimeMillis() / 1000; String data = nonce + ":" + unixTs; String signature; try { Mac mac = Mac.getInstance("HmacSHA256"); SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(StandardCharsets.UTF_8), "HmacSHA256"); mac.init(keySpec); signature = bytesToHex(mac.doFinal(data.getBytes(StandardCharsets.UTF_8))); } catch (Exception e) { throw new IllegalStateException("Unable to sign payload", e); } return String.format( "{\"type\":\"auth\",\"params\":{\"hmac\":{\"public_key\":\"%s\",\"nonce\":\"%s\",\"unix_ts\":%d,\"signature\":\"%s\"}}}", PUB_KEY, nonce, unixTs, signature); } private static String bytesToHex(byte[] bytes) { StringBuilder sb = new StringBuilder(bytes.length * 2); for (byte b : bytes) { sb.append(String.format("%02x", b)); } return sb.toString(); } } ``` ## Example Response ```json theme={null} { "type": "auth", "result": "success" } ``` > [!NOTE] > > After the client successfully authenticates, the server keeps the connection > alive by periodically sending heartbeat messages in the form of [WebSocket > ping > frames](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#pings-and-pongs-the-heartbeat-of-websockets). # Subscribe — Balances Source: https://docs.qfex.com/websocket/channels/trade/balance Subscribe to the **balances** channel to receive pulsed account balance updates.\ Messages are pushed every **60 second**. ## 1) Authenticate Send this immediately after connecting to `wss://trade.qfex.com?api_key=YOUR_API_KEY`: ```json theme={null} { "type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." } } } ``` ## 2) Subscribe ```json theme={null} { "type": "subscribe", "params": { "channels": ["balances"] } } ``` ## Sample Code ```python python theme={null} # Python (websocket-client) # pip install websocket-client import json, websocket API_KEY = "YOUR_API_KEY" def send(ws, obj): ws.send(json.dumps(obj)) def on_open(ws): # 1) Authenticate # https://docs.qfex.com/websocket/channels/trade/authenticate#how-it-works send(ws, {"type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." }}}) # 2) Subscribe to balances send(ws, {"type": "subscribe", "params": {"channels": ["balances"]}}) ws = websocket.WebSocketApp( "wss://trade.qfex.com?api_key=YOUR_API_KEY", on_open=on_open, on_message=lambda _, m: print("Message:", m), on_error=lambda _, e: print("Error:", e), on_close=lambda *_: print("Closed"), ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) // npm i ws import WebSocket from "ws"; const API_KEY = "YOUR_API_KEY"; const ws = new WebSocket("wss://trade.qfex.com?api_key=YOUR_API_KEY"); ws.on("open", () => { ws.send(JSON.stringify({ type: "auth", params: { api_key: API_KEY } })); ws.send( JSON.stringify({ type: "subscribe", params: { channels: ["balances"] } }), ); }); ws.on("message", (msg) => console.log("Message:", msg.toString())); ws.on("error", (e) => console.error("WS error:", e)); ``` ```go go theme={null} // Go (gorilla/websocket) // go get github.com/gorilla/websocket package main import ( "log" "github.com/gorilla/websocket" ) func mustWriteJSON(c *websocket.Conn, v any) { if err := c.WriteJSON(v); err != nil { log.Fatal("write:", err) } } func main() { c, _, err := websocket.DefaultDialer.Dial("wss://trade.qfex.com?api_key=YOUR_API_KEY", nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() // 1) Authenticate mustWriteJSON(c, map[string]any{ "type": "auth", "params": map[string]any{"hmac": map[string]any{"public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..."}} }) // 2) Subscribe to balances mustWriteJSON(c, map[string]any{ "type": "subscribe", "params": map[string]any{"channels": []string{"balances"}}, }) // Read messages (optional) for { _, data, err := c.ReadMessage() if err != nil { log.Fatal("read:", err) } log.Println(string(data)) } } ``` ```java java theme={null} // Java (OkHttp WebSocket) // Gradle: implementation("com.squareup.okhttp3:okhttp:4.12.0") import java.util.concurrent.TimeUnit; import okhttp3.*; public class BalancesSub { public static void main(String[] args) { OkHttpClient client = new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS).build(); Request req = new Request.Builder().url("wss://trade.qfex.com?api_key=YOUR_API_KEY").build(); WebSocketListener li = new WebSocketListener() { @Override public void onOpen(WebSocket ws, Response r) { ws.send("{\"type\": \"auth\", \"params\": { \"hmac\": { \"public_key\": \"qfex_pub_xxxxx\", \"nonce\": \"c0ffee...\..\", \"unix_ts\": 1760545414, \"signature\": \"5f2e...\" }}}"); ws.send("{\"type\":\"subscribe\",\"params\":{\"channels\":[\"balances\"]}}"); } @Override public void onMessage(WebSocket ws, String text) { System.out.println(text); } @Override public void onFailure(WebSocket ws, Throwable t, Response r) { System.err.println("WS error: " + t.getMessage()); } }; client.newWebSocket(req, li); try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ignored) {} } } ``` ## Example Responses ```json theme={null} { "subscribed": "balances" } ``` ```json theme={null} { "balance_response": { "id": "0020ce8e-eaee-480e-8d7f-b9241d756ee5", "user_id": "0020ce8e-eaee-480e-8d7f-b9241d756ee5", "deposit": 9990000000.0, "realised_pnl": 0.0, "order_margin": 0.0, "position_margin": 0.0, "unrealised_pnl": 0.0, "net_funding": 0.0, "available_balance": 9990000000.0, "referral_rewards": 0.0, "builder_rewards": 0.0, "fees": 0.0 } } ``` > [!NOTE] > > Balance updates are **pulsed every 60 seconds**. Balance object is also sent > whenever a trade/deposit/withdrawal/funding/referral reward action occurs # Cancel All Orders Source: https://docs.qfex.com/websocket/channels/trade/cancel_all_orders You can cancel **all active orders** for your account (optionally filtered by **symbol**) over **WebSocket** using the `cancel_all_orders` message. ## Example Request ```json theme={null} { "type": "cancel_all_orders", "params": { "symbol": "AAPL-USD" } } ``` ## Sample Code ```python python theme={null} # Python (websocket-client) import json, websocket API_KEY = "YOUR_API_KEY" def send(ws, obj): ws.send(json.dumps(obj)) def on_open(ws): # Authenticate # https://docs.qfex.com/websocket/channels/trade/authenticate#how-it-works send(ws, {"type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." }}}) # Cancel all orders send(ws, {"type": "cancel_all_orders", "params": {}}) ws = websocket.WebSocketApp( "wss://trade.qfex.com?api_key=YOUR_API_KEY", on_open=on_open, on_message=lambda _, m: print("Message:", m) ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) import WebSocket from "ws"; const ws = new WebSocket("wss://trade.qfex.com?api_key=YOUR_API_KEY"); const API_KEY = "YOUR_API_KEY"; ws.on("open", () => { // Authenticate ws.send(JSON.stringify({ type: "auth", params: { api_key: API_KEY } })); // Cancel all orders ws.send(JSON.stringify({ type: "cancel_all_orders", params: {} })); }); ws.on("message", (msg) => console.log("Message:", msg.toString())); ``` ```go go theme={null} // Go (gorilla/websocket) package main import ( "log" "github.com/gorilla/websocket" ) func mustWriteJSON(c *websocket.Conn, v any) { if err := c.WriteJSON(v); err != nil { log.Fatal("write:", err) } } func main() { c, _, err := websocket.DefaultDialer.Dial("wss://trade.qfex.com?api_key=YOUR_API_KEY", nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() // Authenticate mustWriteJSON(c, map[string]any{ "type": "auth", "params": map[string]any{"hmac": map[string]any{"public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..."}} }) // Cancel all orders mustWriteJSON(c, map[string]any{ "type": "cancel_all_orders", "params": map[string]any{}, }) } ``` ```java java theme={null} // Java (OkHttp WebSocket) import java.util.concurrent.TimeUnit; import okhttp3.*; public class CancelAllOrdersWs { public static void main(String[] args) { OkHttpClient client = new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS).build(); Request req = new Request.Builder() .url("wss://trade.qfex.com?api_key=YOUR_API_KEY").build(); WebSocketListener li = new WebSocketListener() { @Override public void onOpen(WebSocket ws, Response r) { // Authenticate ws.send("{\"type\": \"auth\", \"params\": { \"hmac\": { \"public_key\": \"qfex_pub_xxxxx\", \"nonce\": \"c0ffee...\..\", \"unix_ts\": 1760545414, \"signature\": \"5f2e...\" }}}"); // Cancel all orders ws.send("{\"type\":\"cancel_all_orders\",\"params\":{}}"); } @Override public void onMessage(WebSocket ws, String text) { System.out.println(text); } }; client.newWebSocket(req, li); try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ignored) {} } } ``` ## Example Response Every cancelled order will generate an **order\_response** with status `CANCELLED`. ```json theme={null} { "order_response": { "order_id": "5b309929-206f-40ec-804d-cbe46e81afc1", "symbol": "AAPL-USD", "status": "CANCELLED", "quantity": 1.0, "price": 200.0, "side": "BUY", "type": "LIMIT", "time_in_force": "GTC", "user_id": "0020ce8e-eaee-480e-8d7f-b9241d756ee5", "client_order_id": "my-client-oid", "quantity_remaining": 1.0, "trade_id": null } } ``` > [!NOTE] > > This cancels **all open orders across all symbols** for the authenticated > account, unless a `symbol` is specified. If you only want to cancel specific > orders by ID, use the [Cancel Order](/websocket/channels/trade/cancel_order) > endpoint instead. # Cancel On Disconnect Source: https://docs.qfex.com/websocket/channels/trade/cancel_on_disconnect Mark a WebSocket session with cancel-on-disconnect to make sure the trade engine closes **every open order** for the account (not just orders placed via this connection) if the connection drops unexpectedly. The flag is scoped to a single connection, so you need to opt in again whenever you reconnect. Send a `cancel_on_disconnect` message with `cancel_on_disconnect` set to `true` immediately after authenticating on any connection you want protected. ## Example Request ```json theme={null} { "type": "cancel_on_disconnect", "params": { "cancel_on_disconnect": true } } ``` ## Sample Code ```python python theme={null} # Python (websocket-client) import json import websocket API_KEY = "YOUR_API_KEY" def send(ws, payload): ws.send(json.dumps(payload)) def on_open(ws): # Authenticate # https://docs.qfex.com/websocket/channels/trade/authenticate#how-it-works send(ws, {"type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." }}}) # Enable cancel-on-disconnect for this connection send(ws, {"type": "cancel_on_disconnect", "params": {"cancel_on_disconnect": True}}) ws = websocket.WebSocketApp( "wss://trade.qfex.com?api_key=YOUR_API_KEY", on_open=on_open, on_message=lambda _, message: print("Message:", message), ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) import WebSocket from "ws"; const API_KEY = "YOUR_API_KEY"; const ws = new WebSocket("wss://trade.qfex.com?api_key=YOUR_API_KEY"); ws.on("open", () => { // Authenticate ws.send(JSON.stringify({ type: "auth", params: { api_key: API_KEY } })); // Enable cancel-on-disconnect for this connection ws.send( JSON.stringify({ type: "cancel_on_disconnect", params: { cancel_on_disconnect: true }, }) ); }); ws.on("message", (msg) => console.log("Message:", msg.toString())); ``` ```go go theme={null} // Go (gorilla/websocket) package main import ( "log" "github.com/gorilla/websocket" ) func mustWriteJSON(c *websocket.Conn, v any) { if err := c.WriteJSON(v); err != nil { log.Fatal("write:", err) } } func main() { c, _, err := websocket.DefaultDialer.Dial("wss://trade.qfex.com?api_key=YOUR_API_KEY", nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() // Authenticate mustWriteJSON(c, map[string]any{ "type": "auth", "params": map[string]any{"hmac": map[string]any{"public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..."}} }) // Enable cancel-on-disconnect for this connection mustWriteJSON(c, map[string]any{ "type": "cancel_on_disconnect", "params": map[string]any{"cancel_on_disconnect": true}, }) } ``` ```java java theme={null} // Java (OkHttp WebSocket) import java.util.concurrent.TimeUnit; import okhttp3.*; public class CancelAllOrdersWs { private static final String API_KEY = "YOUR_API_KEY"; public static void main(String[] args) { OkHttpClient client = new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS) .build(); Request req = new Request.Builder() .url("wss://trade.qfex.com?api_key=" + API_KEY) .build(); WebSocketListener listener = new WebSocketListener() { @Override public void onOpen(WebSocket ws, Response response) { // Authenticate ws.send("{\"type\":\"auth\",\"params\":{\"api_key\":\"" + API_KEY + "\"}}"); // Enable cancel-on-disconnect for this connection ws.send("{\"type\":\"cancel_on_disconnect\",\"params\":{\"cancel_on_disconnect\":true}}"); } @Override public void onMessage(WebSocket ws, String text) { System.out.println(text); } }; client.newWebSocket(req, listener); try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException interrupted) { Thread.currentThread().interrupt(); } } } ``` ## Example Response Cancelling with this flag immediately returns an **order\_response** event for each order with status `CANCELLED`. ```json theme={null} { "order_response": { "order_id": "5b309929-206f-40ec-804d-cbe46e81afc1", "symbol": "AAPL-USD", "status": "CANCELLED", "quantity": 1.0, "price": 200.0, "side": "BUY", "type": "LIMIT", "time_in_force": "GTC", "user_id": "0020ce8e-eaee-480e-8d7f-b9241d756ee5", "client_order_id": "my-client-oid", "quantity_remaining": 1.0, "trade_id": null } } ``` > [!NOTE] > > Enabling cancel-on-disconnect cancels **all open orders across all symbols** > for the authenticated account. If you only want to cancel specific orders, use > the [Cancel Order](/websocket/channels/trade/cancel_order) endpoint instead. # Order Entry — Cancel Order Source: https://docs.qfex.com/websocket/channels/trade/cancel_order Order cancellation is performed over **WebSocket**. * **Endpoint:** `wss://trade.qfex.com?api_key=YOUR_API_KEY` * **Authenticate within 1 minute of connecting.** ## 1) Authenticate Send this immediately after you connect: ```json theme={null} { "type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." } } } ``` ## 2) Cancel Order You can cancel by **`order_id`**, **`client_order_id`**, **`twap_id`**, or **`client_twap_id`** by setting the `order_id` field and using `cancel_order_id_type` to specify how the value should be interpreted. * `order_id` — always a **UUID v4** and **unique**. Cancels exactly one order. * `client_order_id` — **not guaranteed unique**. Cancels **all orders for that symbol** that share this client order id. * `twap_id` — server-generated TWAP id. Cancels the matching TWAP. * `client_twap_id` — client-supplied TWAP id. Cancels the matching TWAP for that symbol. ### Cancel by order id (single order) ```json theme={null} { "type": "cancel_order", "params": { "order_id": "REPLACE_WITH_ORDER_UUID_V4", "symbol": "AAPL-USD", "cancel_order_id_type": "order_id" } } ``` ### Cancel by client order id (may cancel multiple) ```json theme={null} { "type": "cancel_order", "params": { "order_id": "my-client-oid-123", "symbol": "AAPL-USD", "cancel_order_id_type": "client_order_id" } } ``` ### Cancel by TWAP id ```json theme={null} { "type": "cancel_order", "params": { "order_id": "0c7c8e4d-f67e-4aa5-9c64-36a1a622ac35", "symbol": "AAPL-USD", "cancel_order_id_type": "twap_id" } } ``` ### Cancel by client TWAP id ```json theme={null} { "type": "cancel_order", "params": { "order_id": "rebalance-aapl-001", "symbol": "AAPL-USD", "cancel_order_id_type": "client_twap_id" } } ``` ## Parameters | Field | Type | Required | Description | | - | - | - | - | | `order_id` | string | Optional | UUID v4 of the order to cancel. Required if `cancel_order_id_type` = `"order_id"`. Cancels exactly one order. | | `client_order_id` | string | Optional | Client-specified ID to cancel orders. Required if `cancel_order_id_type` = `"client_order_id"`. May cancel multiple orders. | | `symbol` | string | ✅ | Market symbol of the order(s) (e.g. `AAPL-USD`). | | `cancel_order_id_type` | enum | ✅ | Must be one of `"order_id"`, `"client_order_id"`, `"twap_id"`, or `"client_twap_id"`. Tells the engine how to interpret the ID. | > ⚠️ Notes: > > * Put the identifier value in the `order_id` field for all four modes. The engine uses `cancel_order_id_type` to decide whether that value is an order id, client order id, TWAP id, or client TWAP id. > * `order_id` is always unique (UUID v4). > * `client_order_id` is not guaranteed unique and may cancel multiple orders for the same symbol. > * `twap_id` and `client_twap_id` are used to cancel TWAPs. > * Responses will include [OrderStatus](/api-reference/enums#orderstatus) codes, e.g. `CANCELLED`, `NO_SUCH_ORDER`, or `REJECTED`. ## Sample Code ```python python theme={null} # Python (websocket-client) # pip install websocket-client import json import websocket API_KEY = "YOUR_API_KEY" ORDER_ID = "REPLACE_WITH_ORDER_UUID_V4" # for order_id flow CLIENT_OID = "my-client-oid-123" # for client_order_id flow def send(ws, obj): ws.send(json.dumps(obj)) def on_open(ws): # 1) Authenticate # https://docs.qfex.com/websocket/channels/trade/authenticate#how-it-works send(ws, {"type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." }}}) # 2a) Cancel by order_id (exactly one order) send(ws, { "type": "cancel_order", "params": { "order_id": ORDER_ID, "symbol": "AAPL-USD", "cancel_order_id_type": "order_id" } }) # 2b) Cancel by client_order_id (may cancel multiple) # send(ws, { # "type": "cancel_order", # "params": { # "client_order_id": CLIENT_OID, # "symbol": "AAPL-USD", # "cancel_order_id_type": "client_order_id" # } # }) ws = websocket.WebSocketApp( "wss://trade.qfex.com?api_key=YOUR_API_KEY", on_open=on_open, on_message=lambda _, m: print(m), on_error=lambda _, e: print("Error:", e), on_close=lambda *_: print("Closed"), ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) // npm i ws const WebSocket = require("ws"); const API_KEY = "YOUR_API_KEY"; const ORDER_ID = "REPLACE_WITH_ORDER_UUID_V4"; // for order_id flow const CLIENT_OID = "my-client-oid-123"; // for client_order_id flow const ws = new WebSocket("wss://trade.qfex.com?api_key=YOUR_API_KEY"); ws.on("open", () => { // 1) Authenticate ws.send(JSON.stringify({ type: "auth", params: { api_key: API_KEY } })); // 2a) Cancel by order_id (exactly one order) ws.send( JSON.stringify({ type: "cancel_order", params: { order_id: ORDER_ID, symbol: "AAPL-USD", cancel_order_id_type: "order_id", }, }) ); // 2b) Cancel by client_order_id (may cancel multiple) // ws.send(JSON.stringify({ // type: "cancel_order", // params: { // client_order_id: CLIENT_OID, // symbol: "AAPL-USD", // cancel_order_id_type: "client_order_id" // } // })); }); ws.on("message", (m) => console.log(m.toString())); ws.on("error", (e) => console.error("WS error:", e)); ws.on("close", (code, reason) => console.log("Closed:", code, reason?.toString()) ); ``` ```go go theme={null} // Go (gorilla/websocket) // go get github.com/gorilla/websocket package main import ( "log" "github.com/gorilla/websocket" ) func mustWriteJSON(c *websocket.Conn, v any) { if err := c.WriteJSON(v); err != nil { log.Fatal("write:", err) } } func main() { c, _, err := websocket.DefaultDialer.Dial("wss://trade.qfex.com?api_key=YOUR_API_KEY", nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() // 1) Authenticate mustWriteJSON(c, map[string]any{ "type": "auth", "params": map[string]any{"hmac": map[string]any{"public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..."}} }) // 2a) Cancel by order_id (exactly one order) mustWriteJSON(c, map[string]any{ "type": "cancel_order", "params": map[string]any{ "order_id": "REPLACE_WITH_ORDER_UUID_V4", "symbol": "AAPL-USD", "cancel_order_id_type": "order_id", }, }) // 2b) Cancel by client_order_id (may cancel multiple) // mustWriteJSON(c, map[string]any{ // "type": "cancel_order", // "params": map[string]any{ // "client_order_id": "my-client-oid-123", // "symbol": "AAPL-USD", // "cancel_order_id_type": "client_order_id", // }, // }) } ``` ```java java theme={null} // Java (OkHttp WebSocket) // Gradle: implementation("com.squareup.okhttp3:okhttp:4.12.0") import java.util.concurrent.TimeUnit; import okhttp3.*; public class CancelOrderWs { public static void main(String[] args) { OkHttpClient client = new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS) .build(); Request req = new Request.Builder() .url("wss://trade.qfex.com?api_key=YOUR_API_KEY") .build(); WebSocketListener li = new WebSocketListener() { @Override public void onOpen(WebSocket ws, Response r) { // 1) Authenticate ws.send("{\"type\": \"auth\", \"params\": { \"hmac\": { \"public_key\": \"qfex_pub_xxxxx\", \"nonce\": \"c0ffee...\..\", \"unix_ts\": 1760545414, \"signature\": \"5f2e...\" }}}"); // 2a) Cancel by order_id (exactly one order) ws.send("{\"type\":\"cancel_order\",\"params\":{\"order_id\":\"REPLACE_WITH_ORDER_UUID_V4\",\"symbol\":\"AAPL-USD\",\"cancel_order_id_type\":\"order_id\"}}"); // 2b) Cancel by client_order_id (may cancel multiple) // ws.send("{\"type\":\"cancel_order\",\"params\":{\"client_order_id\":\"my-client-oid-123\",\"symbol\":\"AAPL-USD\",\"cancel_order_id_type\":\"client_order_id\"}}"); } @Override public void onMessage(WebSocket ws, String text) { System.out.println(text); } @Override public void onFailure(WebSocket ws, Throwable t, Response r) { System.err.println("WS error: " + t.getMessage()); } }; client.newWebSocket(req, li); try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ignored) {} } } ``` ## Example Response Placing an order returns `ACK`. Cancelling it returns the same structure but with status `CANCELLED`. ```json theme={null} { "order_response": { "order_id": "5b309929-206f-40ec-804d-cbe46e81afc1", "symbol": "AAPL-USD", "status": "CANCELLED", "quantity": 1.0, "price": 200.0, "take_profit": 0.0, "stop_loss": 0.0, "side": "BUY", "type": "LIMIT", "time_in_force": "GTC", "user_id": "0020ce8e-eaee-480e-8d7f-b9241d756ee5", "client_order_id": "", "quantity_remaining": 1.0, "trade_id": null } } ``` # Order Entry — Close Position Source: https://docs.qfex.com/websocket/channels/trade/close_position Closing a position is performed over **WebSocket**.\ This command instructs the engine to place a `LIMIT IOC` order on the **opposite side** of your current position, pegged near the mark price to maximise the chance of a complete close-out.\ The engine automatically sizes the order to match your open position, so you always close in full. * **Endpoint:** `wss://trade.qfex.com?api_key=YOUR_API_KEY` * **Authenticate within 1 minute of connecting.** ## 1) Authenticate Send this immediately after you connect: ```json theme={null} { "type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." } } } ``` ## 2) Close a Position ```json theme={null} { "type": "close_position", "params": { "symbol": "AAPL-USD" } } ``` ## Parameters | Field | Type | Required | Description | | - | - | - | - | | `symbol` | string | ✅ | Market symbol whose position you want to close (e.g. `AAPL-USD`). | > ⚠️ Notes: > > * The generated order is always `LIMIT` + `IOC`, reduce-only, and inherits the appropriate closing side. > * Subscribe to [`fills`](/websocket/channels/trade/fills) to receive executions. > * Subscribe to [`order_responses`](/websocket/channels/trade/order) to receive final status updates. ## Sample Code ```python python theme={null} # Python (websocket-client) # pip install websocket-client import json import websocket API_KEY = "YOUR_API_KEY" def send(ws, obj): ws.send(json.dumps(obj)) def on_open(ws): # 1) Authenticate # https://docs.qfex.com/websocket/channels/trade/authenticate#how-it-works send(ws, {"type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." }}}) # 2) Close position send(ws, { "type": "close_position", "params": { "symbol": "AAPL-USD", }, }) ws = websocket.WebSocketApp( "wss://trade.qfex.com?api_key=YOUR_API_KEY", on_open=on_open, on_message=lambda _, m: print(m), on_error=lambda _, e: print("Error:", e), on_close=lambda *_: print("Closed"), ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) // npm i ws const WebSocket = require("ws"); const API_KEY = "YOUR_API_KEY"; const ws = new WebSocket("wss://trade.qfex.com?api_key=YOUR_API_KEY"); ws.on("open", () => { // 1) Authenticate ws.send(JSON.stringify({ type: "auth", params: { api_key: API_KEY } })); // 2) Close position ws.send( JSON.stringify({ type: "close_position", params: { symbol: "AAPL-USD", }, }) ); }); ws.on("message", (m) => console.log(m.toString())); ws.on("error", (e) => console.error("WS error:", e)); ws.on("close", (code, reason) => console.log("Closed:", code, reason?.toString()) ); ``` ```go go theme={null} // Go (gorilla/websocket) // go get github.com/gorilla/websocket package main import ( "log" "github.com/gorilla/websocket" ) func mustWriteJSON(c *websocket.Conn, v any) { if err := c.WriteJSON(v); err != nil { log.Fatal("write:", err) } } func main() { c, _, err := websocket.DefaultDialer.Dial("wss://trade.qfex.com?api_key=YOUR_API_KEY", nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() // 1) Authenticate mustWriteJSON(c, map[string]any{ "type": "auth", "params": map[string]any{"hmac": map[string]any{"public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..."}} }) // 2) Close position mustWriteJSON(c, map[string]any{ "type": "close_position", "params": map[string]any{ "symbol": "AAPL-USD", }, }) } ``` ```java java theme={null} // Java (OkHttp WebSocket) // Gradle: implementation("com.squareup.okhttp3:okhttp:4.12.0") import java.util.concurrent.TimeUnit; import okhttp3.*; public class ClosePositionWs { public static void main(String[] args) { OkHttpClient client = new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS) .build(); Request req = new Request.Builder() .url("wss://trade.qfex.com?api_key=YOUR_API_KEY") .build(); WebSocketListener li = new WebSocketListener() { @Override public void onOpen(WebSocket ws, Response r) { // 1) Authenticate ws.send("{\"type\": \"auth\", \"params\": { \"hmac\": { \"public_key\": \"qfex_pub_xxxxx\", \"nonce\": \"c0ffee...\..\", \"unix_ts\": 1760545414, \"signature\": \"5f2e...\" }}}"); // 2) Close position ws.send("{\"type\":\"close_position\",\"params\":{\"symbol\":\"AAPL-USD\"}}"); } @Override public void onMessage(WebSocket ws, String text) { System.out.println(text); } @Override public void onFailure(WebSocket ws, Throwable t, Response r) { System.err.println("WS error: " + t.getMessage()); } }; client.newWebSocket(req, li); try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ignored) {} } } ``` ## Example Responses You will receive an `order_response` acknowledgement immediately, followed by fill updates (if any). ```json theme={null} { "order_response": { "order_id": "5b309929-206f-40ec-804d-cbe46e81afc1", "symbol": "AAPL-USD", "status": "ACK", "quantity": 2.0, "price": 199.8, "take_profit": 0.0, "stop_loss": 0.0, "side": "SELL", "type": "LIMIT", "time_in_force": "IOC", "user_id": "0020ce8e-eaee-480e-8d7f-b9241d756ee5", "client_order_id": "", "quantity_remaining": 2.0 } } ``` ```json theme={null} { "order_response": { "order_id": "5b309929-206f-40ec-804d-cbe46e81afc1", "symbol": "AAPL-USD", "status": "FILLED", "quantity": 2.0, "price": 199.75, "take_profit": 0.0, "stop_loss": 0.0, "side": "SELL", "type": "LIMIT", "time_in_force": "IOC", "user_id": "0020ce8e-eaee-480e-8d7f-b9241d756ee5", "client_order_id": "", "quantity_remaining": 0.0 } } ``` > [!NOTE] > > Any unfilled remainder is cancelled automatically because the engine always > uses `IOC`. If you have no open position for the symbol, the command is > rejected. # Subscribe — Fills Source: https://docs.qfex.com/websocket/channels/trade/fills Subscribe to the **fills** channel to receive real-time fill updates for executed trades.\ Messages are pushed in **realtime**. ## 1) Authenticate Send this immediately after connecting to `wss://trade.qfex.com?api_key=YOUR_API_KEY`: ```json theme={null} { "type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." } } } ``` ## 2) Subscribe ```json theme={null} { "type": "subscribe", "params": { "channels": ["fills"] } } ``` ## Sample Code ```python python theme={null} # Python (websocket-client) # pip install websocket-client import json, websocket API_KEY = "YOUR_API_KEY" def send(ws, obj): ws.send(json.dumps(obj)) def on_open(ws): # 1) Authenticate # https://docs.qfex.com/websocket/channels/trade/authenticate#how-it-works send(ws, {"type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." }}}) # 2) Subscribe to fills send(ws, {"type": "subscribe", "params": {"channels": ["fills"]}}) ws = websocket.WebSocketApp( "wss://trade.qfex.com?api_key=YOUR_API_KEY", on_open=on_open, on_message=lambda _, m: print("Message:", m), on_error=lambda _, e: print("Error:", e), on_close=lambda *_: print("Closed"), ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) // npm i ws import WebSocket from "ws"; const API_KEY = "YOUR_API_KEY"; const ws = new WebSocket("wss://trade.qfex.com?api_key=YOUR_API_KEY"); ws.on("open", () => { ws.send(JSON.stringify({ type: "auth", params: { api_key: API_KEY } })); ws.send( JSON.stringify({ type: "subscribe", params: { channels: ["fills"] }, }) ); }); ws.on("message", (msg) => console.log("Message:", msg.toString())); ws.on("error", (e) => console.error("WS error:", e)); ``` ```go go theme={null} // Go (gorilla/websocket) // go get github.com/gorilla/websocket package main import ( "log" "github.com/gorilla/websocket" ) func mustWriteJSON(c *websocket.Conn, v any) { if err := c.WriteJSON(v); err != nil { log.Fatal("write:", err) } } func main() { c, _, err := websocket.DefaultDialer.Dial("wss://trade.qfex.com?api_key=YOUR_API_KEY", nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() // 1) Authenticate mustWriteJSON(c, map[string]any{ "type": "auth", "params": map[string]any{"hmac": map[string]any{"public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..."}} }) // 2) Subscribe to fills mustWriteJSON(c, map[string]any{ "type": "subscribe", "params": map[string]any{"channels": []string{"fills"}}, }) // Read messages (optional) for { _, data, err := c.ReadMessage() if err != nil { log.Fatal("read:", err) } log.Println(string(data)) } } ``` ```java java theme={null} // Java (OkHttp WebSocket) // Gradle: implementation("com.squareup.okhttp3:okhttp:4.12.0") import java.util.concurrent.TimeUnit; import okhttp3.*; public class FillsSub { public static void main(String[] args) { OkHttpClient client = new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS).build(); Request req = new Request.Builder().url("wss://trade.qfex.com?api_key=YOUR_API_KEY").build(); WebSocketListener li = new WebSocketListener() { @Override public void onOpen(WebSocket ws, Response r) { ws.send("{\"type\": \"auth\", \"params\": { \"hmac\": { \"public_key\": \"qfex_pub_xxxxx\", \"nonce\": \"c0ffee...\..\", \"unix_ts\": 1760545414, \"signature\": \"5f2e...\" }}}"); ws.send("{\"type\":\"subscribe\",\"params\":{\"channels\":[\"fills\"]}}"); } @Override public void onMessage(WebSocket ws, String text) { System.out.println(text); } @Override public void onFailure(WebSocket ws, Throwable t, Response r) { System.err.println("WS error: " + t.getMessage()); } }; client.newWebSocket(req, li); try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ignored) {} } } ``` ## Example Responses ```json theme={null} { "subscribed": "fills" } ``` ```json theme={null} { "fill_response": { "trade_id": "REDACTED", "user_id": "REDACTED", "symbol": "US100-USD", "price": 25242.0, "quantity": 0.004, "side": "SELL", "aggressor_side": "SELL", "order_id": "0aeae7d0-4a6e-4672-ad69-82b8811d4e0b", "fee": 0.00045, "order_type": "MARKET", "tif": "IOC", "order_price": 25547.0, "client_order_id": "REDACTED", "remaining_quantity": 0.0, "take_profit": 0.0, "stop_loss": 0.0, "execution_type": "NEW", "timestamp": 1769439642.8484282, "realised_pnl": 0.0 } } ``` > [!NOTE] > > Fill updates are **live**. # Get Available Leverage Levels Source: https://docs.qfex.com/websocket/channels/trade/get_available_leverage You can request the leverage levels available for each symbol for your account. > [!NOTE] > > You cannot change the leverage level if you have any open orders or a non zero > position. Leverage management is performed over **WebSocket**. * **Endpoint:** `wss://trade.qfex.com?api_key=YOUR_API_KEY` * **Authenticate within 1 minute of connecting.** ## Example Request ```json theme={null} { "type": "get_available_leverage_levels", "params": { "limit": 10, "offset": 0 } } ``` * `limit` *(optional)* — maximum number of leverage records to return (default **1000**) * `offset` *(optional)* — pagination offset (default **0**) ## Sample Code ```python python theme={null} # Python (websocket-client) import json, websocket API_KEY = "YOUR_API_KEY" def send(ws, obj): ws.send(json.dumps(obj)) def on_open(ws): # Authenticate # https://docs.qfex.com/websocket/channels/trade/authenticate#how-it-works send(ws, {"type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." }}}) # Request user trades send(ws, {"type": "get_available_leverage_levels", "params": {"limit": 10, "offset": 0}}) ws = websocket.WebSocketApp( "wss://trade.qfex.com?api_key=YOUR_API_KEY", on_open=on_open, on_message=lambda _, m: print("Message:", m) ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) import WebSocket from "ws"; const ws = new WebSocket("wss://trade.qfex.com?api_key=YOUR_API_KEY"); const API_KEY = "YOUR_API_KEY"; ws.on("open", () => { ws.send(JSON.stringify({ type: "auth", params: { api_key: API_KEY } })); ws.send( JSON.stringify({ type: "get_available_leverage_levels", params: { limit: 10, offset: 0 }, }) ); }); ws.on("message", (msg) => console.log("Message:", msg.toString())); ``` ```go go theme={null} // Go (gorilla/websocket) package main import ( "log" "github.com/gorilla/websocket" ) func mustWriteJSON(c *websocket.Conn, v any) { if err := c.WriteJSON(v); err != nil { log.Fatal("write:", err) } } func main() { c, _, err := websocket.DefaultDialer.Dial("wss://trade.qfex.com?api_key=YOUR_API_KEY", nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() // Authenticate mustWriteJSON(c, map[string]any{ "type": "auth", "params": map[string]any{"hmac": map[string]any{"public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..."}} }) // Get user leverage mustWriteJSON(c, map[string]any{ "type": "get_available_leverage_levels", "params": map[string]any{"limit": 10, "offset": 0}, }) } ``` ```java java theme={null} // Java (OkHttp WebSocket) import java.util.concurrent.TimeUnit; import okhttp3.*; public class GetUserTradesWs { public static void main(String[] args) { OkHttpClient client = new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS).build(); Request req = new Request.Builder() .url("wss://trade.qfex.com?api_key=YOUR_API_KEY").build(); WebSocketListener li = new WebSocketListener() { @Override public void onOpen(WebSocket ws, Response r) { ws.send("{\"type\": \"auth\", \"params\": { \"hmac\": { \"public_key\": \"qfex_pub_xxxxx\", \"nonce\": \"c0ffee...\..\", \"unix_ts\": 1760545414, \"signature\": \"5f2e...\" }}}"); ws.send("{\"type\":\"get_available_leverage_levels\",\"params\":{\"limit\":10,\"offset\":0}}"); } @Override public void onMessage(WebSocket ws, String text) { System.out.println(text); } }; client.newWebSocket(req, li); try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ignored) {} } } ``` ## Example Response ```json theme={null} { "available_leverage_levels_response": [ { "id": "04349aaf-b29c-456d-b255-7b9de6e180c4", "symbol": "META-USD", "initial_margin": 1.0, "maintenance_margin": 0.6666666666666666, "max_notional": 195000.0, "leverage": "1" }, { "id": "9dc9cc77-5222-47f3-80fe-16d48fa1082e", "symbol": "META-USD", "initial_margin": 0.5, "maintenance_margin": 0.3333333333333333, "max_notional": 190000.0, "leverage": "2" } ] } ``` # Get Leverage Levels Source: https://docs.qfex.com/websocket/channels/trade/get_leverage You can request the leverage levels applied for each symbol for your account. > [!NOTE] > > You cannot change the leverage level if you have any open orders or a non zero > position. Leverage management is performed over **WebSocket**. * **Endpoint:** `wss://trade.qfex.com?api_key=YOUR_API_KEY` * **Authenticate within 1 minute of connecting.** ## Example Request ```json theme={null} { "type": "get_user_leverage", "params": { "limit": 10, "offset": 0 } } ``` * `limit` *(optional)* — maximum number of leverage records to return (default **1000**) * `offset` *(optional)* — pagination offset (default **0**) ## Sample Code ```python python theme={null} # Python (websocket-client) import json, websocket API_KEY = "YOUR_API_KEY" def send(ws, obj): ws.send(json.dumps(obj)) def on_open(ws): # Authenticate # https://docs.qfex.com/websocket/channels/trade/authenticate#how-it-works send(ws, {"type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." }}}) # Request user trades send(ws, {"type": "get_user_leverage", "params": {"limit": 10, "offset": 0}}) ws = websocket.WebSocketApp( "wss://trade.qfex.com?api_key=YOUR_API_KEY", on_open=on_open, on_message=lambda _, m: print("Message:", m) ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) import WebSocket from "ws"; const ws = new WebSocket("wss://trade.qfex.com?api_key=YOUR_API_KEY"); const API_KEY = "YOUR_API_KEY"; ws.on("open", () => { ws.send(JSON.stringify({ type: "auth", params: { api_key: API_KEY } })); ws.send( JSON.stringify({ type: "get_user_leverage", params: { limit: 10, offset: 0 }, }) ); }); ws.on("message", (msg) => console.log("Message:", msg.toString())); ``` ```go go theme={null} // Go (gorilla/websocket) package main import ( "log" "github.com/gorilla/websocket" ) func mustWriteJSON(c *websocket.Conn, v any) { if err := c.WriteJSON(v); err != nil { log.Fatal("write:", err) } } func main() { c, _, err := websocket.DefaultDialer.Dial("wss://trade.qfex.com?api_key=YOUR_API_KEY", nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() // Authenticate mustWriteJSON(c, map[string]any{ "type": "auth", "params": map[string]any{"hmac": map[string]any{"public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..."}} }) // Get user leverage mustWriteJSON(c, map[string]any{ "type": "get_user_leverage", "params": map[string]any{"limit": 10, "offset": 0}, }) } ``` ```java java theme={null} // Java (OkHttp WebSocket) import java.util.concurrent.TimeUnit; import okhttp3.*; public class GetUserTradesWs { public static void main(String[] args) { OkHttpClient client = new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS).build(); Request req = new Request.Builder() .url("wss://trade.qfex.com?api_key=YOUR_API_KEY").build(); WebSocketListener li = new WebSocketListener() { @Override public void onOpen(WebSocket ws, Response r) { ws.send("{\"type\": \"auth\", \"params\": { \"hmac\": { \"public_key\": \"qfex_pub_xxxxx\", \"nonce\": \"c0ffee...\..\", \"unix_ts\": 1760545414, \"signature\": \"5f2e...\" }}}"); ws.send("{\"type\":\"get_user_leverage\",\"params\":{\"limit\":10,\"offset\":0}}"); } @Override public void onMessage(WebSocket ws, String text) { System.out.println(text); } }; client.newWebSocket(req, li); try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ignored) {} } } ``` ## Example Response ```json theme={null} { "user_leverage_response": [ { "id": "96eedb94-bf74-4efe-bd64-82e7a541c7f4", "symbol": "META-USD", "initial_margin": 0.05, "maintenance_margin": 0.03333333333333333, "max_notional": 100000.0, "leverage": "20" } ] } ``` # Get Order Source: https://docs.qfex.com/websocket/channels/trade/get_order You can request a specific order by its `order_id` for the authenticated account. ## Example Request ```json theme={null} { "type": "get_order", "params": { "symbol": "US500-USD", "order_id": "57cc1941-8ad1-4df3-b34c-bb556518befc" } } ``` * `symbol` *(required)* — the symbol for the order * `order_id` *(required)* — the order ID to retrieve ## Sample Code ```python python theme={null} # Python (websocket-client) import json, websocket API_KEY = "YOUR_API_KEY" def send(ws, obj): ws.send(json.dumps(obj)) def on_open(ws): # Authenticate # https://docs.qfex.com/websocket/channels/trade/authenticate#how-it-works send(ws, {"type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." }}}) # Get order send(ws, {"type": "get_order", "params": {"symbol": "AAPL-USD", "order_id": "57cc1941-8ad1-4df3-b34c-bb556518befc"}}) ws = websocket.WebSocketApp( "wss://trade.qfex.com?api_key=YOUR_API_KEY", on_open=on_open, on_message=lambda _, m: print("Message:", m) ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) import WebSocket from "ws"; const ws = new WebSocket("wss://trade.qfex.com?api_key=YOUR_API_KEY"); const API_KEY = "YOUR_API_KEY"; ws.on("open", () => { // Authenticate ws.send(JSON.stringify({ type: "auth", params: { api_key: API_KEY } })); // Get order ws.send( JSON.stringify({ type: "get_order", params: { symbol: "AAPL-USD", order_id: "57cc1941-8ad1-4df3-b34c-bb556518befc", }, }) ); }); ws.on("message", (msg) => console.log("Message:", msg.toString())); ``` ```go go theme={null} // Go (gorilla/websocket) package main import ( "log" "github.com/gorilla/websocket" ) func mustWriteJSON(c *websocket.Conn, v any) { if err := c.WriteJSON(v); err != nil { log.Fatal("write:", err) } } func main() { c, _, err := websocket.DefaultDialer.Dial("wss://trade.qfex.com?api_key=YOUR_API_KEY", nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() // Authenticate mustWriteJSON(c, map[string]any{ "type": "auth", "params": map[string]any{"hmac": map[string]any{"public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..."}} }) // Get order mustWriteJSON(c, map[string]any{ "type": "get_order", "params": map[string]any{"symbol": "AAPL-USD", "order_id": "57cc1941-8ad1-4df3-b34c-bb556518befc"}, }) } ``` ```java java theme={null} // Java (OkHttp WebSocket) import java.util.concurrent.TimeUnit; import okhttp3.*; public class GetOrderWs { public static void main(String[] args) { OkHttpClient client = new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS).build(); Request req = new Request.Builder() .url("wss://trade.qfex.com?api_key=YOUR_API_KEY").build(); WebSocketListener li = new WebSocketListener() { @Override public void onOpen(WebSocket ws, Response r) { // Authenticate ws.send("{\"type\": \"auth\", \"params\": { \"hmac\": { \"public_key\": \"qfex_pub_xxxxx\", \"nonce\": \"c0ffee...\..\", \"unix_ts\": 1760545414, \"signature\": \"5f2e...\" }}}"); // Get order ws.send("{\"type\":\"get_order\",\"params\":{\"symbol\":\"AAPL-USD\",\"order_id\":\"57cc1941-8ad1-4df3-b34c-bb556518befc\"}}"); } @Override public void onMessage(WebSocket ws, String text) { System.out.println(text); } }; client.newWebSocket(req, li); try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ignored) {} } } ``` ## Example Response ```json theme={null} { "all_orders_response": { "orders": [ { "order_id": "2f05173c-992a-426b-bcaf-6b341644bb62", "symbol": "GOOG-USD", "status": "ACK", "quantity": 2.708, "price": 251.25, "take_profit": 0.0, "stop_loss": 0.0, "side": "SELL", "type": "LIMIT", "time_in_force": "GTC", "user_id": "0020ce8e-eaee-480e-8d7f-b9241d756ee5", "client_order_id": "7a257796-a9c1-49bd-a80b-bcccdc81c103", "quantity_remaining": 2.708, "update_time": 1758208770.5512846, "trade_id": null } ], "stop_orders": [] } } ``` > [!NOTE] > > Only **open orders** (including partially filled) are returned. Filled and > fully cancelled orders are not included. # Get User Orders Source: https://docs.qfex.com/websocket/channels/trade/get_user_orders You can request all **open orders** (including partially filled orders) and active **TWAPs** for the authenticated account. ## Example Request ```json theme={null} { "type": "get_user_orders", "params": { "limit": 10, "offset": 0, "symbol": "AAPL-USD" } } ``` * `limit` *(optional)* — maximum number of orders to return (default **1000**) * `offset` *(optional)* — pagination offset (default **0**) * `symbol` *(optional)* — symbol to filter by. Will return all orders if not specified. ## Sample Code ```python python theme={null} # Python (websocket-client) import json, websocket API_KEY = "YOUR_API_KEY" def send(ws, obj): ws.send(json.dumps(obj)) def on_open(ws): # Authenticate # https://docs.qfex.com/websocket/channels/trade/authenticate#how-it-works send(ws, {"type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." }}}) # Request user orders send(ws, {"type": "get_user_orders", "params": {"limit": 10, "offset": 0, "symbol": "AAPL-USD"}}) ws = websocket.WebSocketApp( "wss://trade.qfex.com?api_key=YOUR_API_KEY", on_open=on_open, on_message=lambda _, m: print("Message:", m) ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) import WebSocket from "ws"; const ws = new WebSocket("wss://trade.qfex.com?api_key=YOUR_API_KEY"); const API_KEY = "YOUR_API_KEY"; ws.on("open", () => { // Authenticate ws.send(JSON.stringify({ type: "auth", params: { api_key: API_KEY } })); // Get user orders ws.send( JSON.stringify({ type: "get_user_orders", params: { limit: 10, offset: 0, symbol: "AAPL-USD" }, }) ); }); ws.on("message", (msg) => console.log("Message:", msg.toString())); ``` ```go go theme={null} // Go (gorilla/websocket) package main import ( "log" "github.com/gorilla/websocket" ) func mustWriteJSON(c *websocket.Conn, v any) { if err := c.WriteJSON(v); err != nil { log.Fatal("write:", err) } } func main() { c, _, err := websocket.DefaultDialer.Dial("wss://trade.qfex.com?api_key=YOUR_API_KEY", nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() // Authenticate mustWriteJSON(c, map[string]any{ "type": "auth", "params": map[string]any{"hmac": map[string]any{"public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..."}} }) // Get user orders mustWriteJSON(c, map[string]any{ "type": "get_user_orders", "params": map[string]any{"limit": 10, "offset": 0, "symbol": "AAPL-USD"}, }) } ``` ```java java theme={null} // Java (OkHttp WebSocket) import java.util.concurrent.TimeUnit; import okhttp3.*; public class GetUserOrdersWs { public static void main(String[] args) { OkHttpClient client = new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS).build(); Request req = new Request.Builder() .url("wss://trade.qfex.com?api_key=YOUR_API_KEY").build(); WebSocketListener li = new WebSocketListener() { @Override public void onOpen(WebSocket ws, Response r) { // Authenticate ws.send("{\"type\": \"auth\", \"params\": { \"hmac\": { \"public_key\": \"qfex_pub_xxxxx\", \"nonce\": \"c0ffee...\..\", \"unix_ts\": 1760545414, \"signature\": \"5f2e...\" }}}"); // Get user orders ws.send("{\"type\":\"get_user_orders\",\"params\":{\"limit\":10,\"offset\":0,\"symbol\":\"AAPL-USD\"}}"); } @Override public void onMessage(WebSocket ws, String text) { System.out.println(text); } }; client.newWebSocket(req, li); try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ignored) {} } } ``` ## Example Response ```json theme={null} { "all_orders_response": { "orders": [ { "order_id": "2f05173c-992a-426b-bcaf-6b341644bb62", "symbol": "GOOG-USD", "status": "ACK", "quantity": 2.708, "price": 251.25, "take_profit": 0.0, "stop_loss": 0.0, "side": "SELL", "type": "LIMIT", "time_in_force": "GTC", "user_id": "0020ce8e-eaee-480e-8d7f-b9241d756ee5", "client_order_id": "7a257796-a9c1-49bd-a80b-bcccdc81c103", "quantity_remaining": 2.708, "update_time": 1758208770.5512846, "trade_id": null } ], "twaps": [ { "twap_id": "0c7c8e4d-f67e-4aa5-9c64-36a1a622ac35", "client_twap_id": "rebalance-aapl-001", "user_id": "0020ce8e-eaee-480e-8d7f-b9241d756ee5", "status": "ENGINE_STATUS", "symbol": "GOOG-USD", "total_quantity": 5, "filled_quantity": 1, "average_fill_price": 251.25, "worst_price": 250, "total_num_orders": 5, "order_interval_secs": 30, "reduce_only": false, "side": "SELL", "start_time": 1758208700.5512846, "updated_at": 1758208770.5512846, "created_at": 1758208700.5512846 } ] } } ``` > [!NOTE] > > Only **open orders** and active **TWAPs** are returned. Filled and fully > cancelled orders are not included. # Get User Trades Source: https://docs.qfex.com/websocket/channels/trade/get_user_trades You can request the most recent **executed trades** (fills) for the authenticated account. ## Example Request ```json theme={null} { "type": "get_user_trades", "params": { "limit": 10, "offset": 0, "order_id": "5b309929-206f-40ec-804d-cbe46e81afc1", "start_ts": 1746900000, "end_ts": 1746908400 } } ``` * `limit` *(optional)* — maximum number of trades to return (default **1000**) * `offset` *(optional)* — pagination offset (default **0**) * `order_id` *(optional)* — filter trades by a specific exchange order ID * `start_ts` *(optional)* — filter trades from this Unix timestamp (inclusive) * `end_ts` *(optional)* — filter trades up to this Unix timestamp (inclusive) ## Sample Code ```python python theme={null} # Python (websocket-client) import json, websocket API_KEY = "YOUR_API_KEY" def send(ws, obj): ws.send(json.dumps(obj)) def on_open(ws): # Authenticate # https://docs.qfex.com/websocket/channels/trade/authenticate#how-it-works send(ws, {"type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." }}}) # Request user trades with optional filters send(ws, { "type": "get_user_trades", "params": { "limit": 10, "offset": 0, "start_ts": 1746900000, "end_ts": 1746908400 } }) ws = websocket.WebSocketApp( "wss://trade.qfex.com?api_key=YOUR_API_KEY", on_open=on_open, on_message=lambda _, m: print("Message:", m) ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) import WebSocket from "ws"; const ws = new WebSocket("wss://trade.qfex.com?api_key=YOUR_API_KEY"); const API_KEY = "YOUR_API_KEY"; ws.on("open", () => { ws.send(JSON.stringify({ type: "auth", params: { api_key: API_KEY } })); ws.send( JSON.stringify({ type: "get_user_trades", params: { limit: 10, offset: 0, start_ts: 1746900000, end_ts: 1746908400, }, }) ); }); ws.on("message", (msg) => console.log("Message:", msg.toString())); ``` ```go go theme={null} // Go (gorilla/websocket) package main import ( "log" "github.com/gorilla/websocket" ) func mustWriteJSON(c *websocket.Conn, v any) { if err := c.WriteJSON(v); err != nil { log.Fatal("write:", err) } } func main() { c, _, err := websocket.DefaultDialer.Dial("wss://trade.qfex.com?api_key=YOUR_API_KEY", nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() // Authenticate mustWriteJSON(c, map[string]any{ "type": "auth", "params": map[string]any{"hmac": map[string]any{"public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..."}} }) // Get user trades with optional filters mustWriteJSON(c, map[string]any{ "type": "get_user_trades", "params": map[string]any{ "limit": 10, "offset": 0, "start_ts": 1746900000, "end_ts": 1746908400, }, }) } ``` ```java java theme={null} // Java (OkHttp WebSocket) import java.util.concurrent.TimeUnit; import okhttp3.*; public class GetUserTradesWs { public static void main(String[] args) { OkHttpClient client = new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS).build(); Request req = new Request.Builder() .url("wss://trade.qfex.com?api_key=YOUR_API_KEY").build(); WebSocketListener li = new WebSocketListener() { @Override public void onOpen(WebSocket ws, Response r) { ws.send("{\"type\": \"auth\", \"params\": { \"hmac\": { \"public_key\": \"qfex_pub_xxxxx\", \"nonce\": \"c0ffee...\..\", \"unix_ts\": 1760545414, \"signature\": \"5f2e...\" }}}"); ws.send("{\"type\":\"get_user_trades\",\"params\":{\"limit\":10,\"offset\":0,\"start_ts\":1746900000,\"end_ts\":1746908400}}"); } @Override public void onMessage(WebSocket ws, String text) { System.out.println(text); } }; client.newWebSocket(req, li); try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ignored) {} } } ``` ## Example Response ```json theme={null} { "user_trades": [ { "trade_id": "92adf4db-1e48-40cf-9e2a-b0ecf079a16a", "symbol": "AAPL-USD", "price": 200.0, "quantity": 1.0, "aggressor_side": "BUY", "order_id": "5b309929-206f-40ec-804d-cbe46e81afc1", "client_order_id": "my-client-oid", "fee": 0.002, "timestamp": 1746908400 } ], "count": 1 } ``` > [!NOTE] > > Only **executed trades (fills)** are returned. Open orders that have not > filled are excluded. # Order Entry — Modify Order Source: https://docs.qfex.com/websocket/channels/trade/modify_order Orders can be modified over **WebSocket**.\ Only **`price`**, **`quantity`**, **`take_profit`**, and **`stop_loss`** may be changed. * You **must** specify the order’s **`order_id`** (UUID v4). * You **must** specify the **`symbol`**. * You **must** specify the **`side`**. * You **must** specify the **`order_type`**. * You **must** specify **`reduce_only`**, matching the order you are modifying. * `client_order_id` cannot be used for modifies. > [!NOTE] > > Soft liquidation can change a resting order's `reduce_only` flag to `true` without a client request. Use its current flag, not the value originally submitted. Sending `false` after conversion is rejected with `CANNOT_MODIFY_NO_SUCH_ORDER`. The automatic conversion preserves the order ID; the ID replacement described below applies to client-requested modifications. See [automatic reduce-only conversion](/websocket/channels/trade/order#automatic-reduce-only-conversion). ## 1) Authenticate Send this immediately after you connect: ```json theme={null} { "type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." } } } ``` ## 2) Modify Order ```json theme={null} { "type": "modify_order", "params": { "order_id": "510333ac-3f7b-4d88-934a-4396d48824cc", "symbol": "AAPL-USD", "side": "BUY", "order_type": "LIMIT", "quantity": 2, "price": 200, "take_profit": 200, "stop_loss": 150, "reduce_only": false } } ``` ## Parameters | Field | Type | Required | Description | | - | - | - | - | | `order_id` | string | ✅ | UUID v4 of the order to modify. | | `symbol` | string | ✅ | The market symbol (e.g. `AAPL-USD`). | | `side` | enum | ✅ | Must match the original order’s side. See [OrderDirection](/api-reference/enums#orderdirection). | | `order_type` | enum | ✅ | Must match the original order’s type. See [OrderType](/api-reference/enums#ordertype). | | `reduce_only` | bool | ✅ | Must match the resting order’s current reduce-only flag. A modify cannot change it. | | `quantity` | number | Optional | New order quantity. Must respect symbol lot size, min, and max constraints. | | `price` | number | Optional | New price for limit/ALO orders. Ignored for market orders. Must respect tick size and price bands. | | `take_profit` | number | Optional | Updated take-profit price. Set `0` if unused. | | `stop_loss` | number | Optional | Updated stop-loss price. Set `0` if unused. | > ⚠️ Notes: > > * Only `price`, `quantity`, `take_profit`, and `stop_loss` may be modified. > * `reduce_only` is required on every modify. Omitting it rejects the request with an `InvalidJSONFormat` error, and sending a value that does not match the resting order is rejected with `CANNOT_MODIFY_NO_SUCH_ORDER`. > * `client_order_id` cannot be used for modifies. > * **The `order_id` changes on modification.** The response will contain a **new** `order_id`. The old `order_id` is replaced. This is because multiple in-flight amends are not currently supported. > * Modifications that would cause an invalid state (e.g. reducing below partially filled quantity) will be rejected with [OrderStatus](/api-reference/enums#orderstatus) codes such as `CANNOT_MODIFY_PARTIAL_FILL` or `CANNOT_MODIFY_NO_SUCH_ORDER`. ## Sample Code ```python python theme={null} # Python (websocket-client) import json, websocket API_KEY = "YOUR_API_KEY" def send(ws, obj): ws.send(json.dumps(obj)) def on_open(ws): # Authenticate # https://docs.qfex.com/websocket/channels/trade/authenticate#how-it-works send(ws, {"type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." }}}) # Modify order send(ws, { "type": "modify_order", "params": { "order_id": "510333ac-3f7b-4d88-934a-4396d48824cc", "symbol": "AAPL-USD", "side": "BUY", "order_type": "LIMIT", "quantity": 2, "price": 200, "take_profit": 200, "stop_loss": 150, "reduce_only": False } }) ws = websocket.WebSocketApp( "wss://trade.qfex.com?api_key=YOUR_API_KEY", on_open=on_open, on_message=lambda _, m: print("Message:", m) ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) import WebSocket from "ws"; const ws = new WebSocket("wss://trade.qfex.com?api_key=YOUR_API_KEY"); const API_KEY = "YOUR_API_KEY"; ws.on("open", () => { // Authenticate ws.send(JSON.stringify({ type: "auth", params: { api_key: API_KEY } })); // Modify order ws.send( JSON.stringify({ type: "modify_order", params: { order_id: "510333ac-3f7b-4d88-934a-4396d48824cc", symbol: "AAPL-USD", side: "BUY", order_type: "LIMIT", quantity: 2, price: 200, take_profit: 200, stop_loss: 150, reduce_only: false, }, }) ); }); ws.on("message", (msg) => console.log("Message:", msg.toString())); ``` ```go go theme={null} // Go (gorilla/websocket) package main import ( "log" "github.com/gorilla/websocket" ) func mustWriteJSON(c *websocket.Conn, v any) { if err := c.WriteJSON(v); err != nil { log.Fatal("write:", err) } } func main() { c, _, err := websocket.DefaultDialer.Dial("wss://trade.qfex.com?api_key=YOUR_API_KEY", nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() // Authenticate mustWriteJSON(c, map[string]any{ "type": "auth", "params": map[string]any{"hmac": map[string]any{"public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..."}} }) // Modify order mustWriteJSON(c, map[string]any{ "type": "modify_order", "params": map[string]any{ "order_id": "510333ac-3f7b-4d88-934a-4396d48824cc", "symbol": "AAPL-USD", "side": "BUY", "order_type": "LIMIT", "quantity": 2, "price": 200, "take_profit": 200, "stop_loss": 150, "reduce_only": false, }, }) } ``` ```java java theme={null} // Java (OkHttp WebSocket) import java.util.concurrent.TimeUnit; import okhttp3.*; public class ModifyOrderWs { public static void main(String[] args) { OkHttpClient client = new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS).build(); Request req = new Request.Builder() .url("wss://trade.qfex.com?api_key=YOUR_API_KEY").build(); WebSocketListener li = new WebSocketListener() { @Override public void onOpen(WebSocket ws, Response r) { // Authenticate ws.send("{\"type\": \"auth\", \"params\": { \"hmac\": { \"public_key\": \"qfex_pub_xxxxx\", \"nonce\": \"c0ffee...\..\", \"unix_ts\": 1760545414, \"signature\": \"5f2e...\" }}}"); // Modify order ws.send("{\"type\":\"modify_order\",\"params\":{\"order_id\":\"510333ac-3f7b-4d88-934a-4396d48824cc\",\"symbol\":\"AAPL-USD\",\"side\":\"BUY\",\"order_type\":\"LIMIT\",\"original_quantity\":1,\"quantity\":2,\"price\":200,\"take_profit\":200,\"stop_loss\":150,\"reduce_only\":false}}"); } @Override public void onMessage(WebSocket ws, String text) { System.out.println(text); } }; client.newWebSocket(req, li); try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ignored) {} } } ``` ## Example Response When an order is successfully modified, you receive the same `order_response` object as with add, but with status **`MODIFIED`** and a **new** `order_id` (the old one is replaced). ```json theme={null} { "order_response": { "order_id": "88888888-4444-4444-4444-1234567890ab", "symbol": "AAPL-USD", "status": "MODIFIED", "quantity": 2.0, "price": 200.0, "take_profit": 200.0, "stop_loss": 150.0, "side": "BUY", "type": "LIMIT", "time_in_force": "GTC", "user_id": "0020ce8e-eaee-480e-8d7f-b9241d756ee5", "client_order_id": "", "quantity_remaining": 2.0, "trade_id": null } } ``` # Subscribe — Order Responses Source: https://docs.qfex.com/websocket/channels/trade/order Subscribe to the **order\_responses** channel to receive order responses.\ Messages are pushed in **realtime**. ## 1) Authenticate Send this immediately after connecting to `wss://trade.qfex.com?api_key=YOUR_API_KEY`: ```json theme={null} { "type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." } } } ``` ## 2) Subscribe ```json theme={null} { "type": "subscribe", "params": { "channels": ["order_responses"] } } ``` ## Sample Code ```python python theme={null} # Python (websocket-client) # pip install websocket-client import json, websocket API_KEY = "YOUR_API_KEY" def send(ws, obj): ws.send(json.dumps(obj)) def on_open(ws): # 1) Authenticate # https://docs.qfex.com/websocket/channels/trade/authenticate#how-it-works send(ws, {"type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." }}}) # 2) Subscribe to order responses send(ws, {"type": "subscribe", "params": {"channels": ["order_responses"]}}) ws = websocket.WebSocketApp( "wss://trade.qfex.com?api_key=YOUR_API_KEY", on_open=on_open, on_message=lambda _, m: print("Message:", m), on_error=lambda _, e: print("Error:", e), on_close=lambda *_: print("Closed"), ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) // npm i ws import WebSocket from "ws"; const API_KEY = "YOUR_API_KEY"; const ws = new WebSocket("wss://trade.qfex.com?api_key=YOUR_API_KEY"); ws.on("open", () => { ws.send(JSON.stringify({ type: "auth", params: { api_key: API_KEY } })); ws.send( JSON.stringify({ type: "subscribe", params: { channels: ["order_responses"] }, }) ); }); ws.on("message", (msg) => console.log("Message:", msg.toString())); ws.on("error", (e) => console.error("WS error:", e)); ``` ```go go theme={null} // Go (gorilla/websocket) // go get github.com/gorilla/websocket package main import ( "log" "github.com/gorilla/websocket" ) func mustWriteJSON(c *websocket.Conn, v any) { if err := c.WriteJSON(v); err != nil { log.Fatal("write:", err) } } func main() { c, _, err := websocket.DefaultDialer.Dial("wss://trade.qfex.com?api_key=YOUR_API_KEY", nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() // 1) Authenticate mustWriteJSON(c, map[string]any{ "type": "auth", "params": map[string]any{"hmac": map[string]any{"public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..."}} }) // 2) Subscribe to order responses mustWriteJSON(c, map[string]any{ "type": "subscribe", "params": map[string]any{"channels": []string{"order_responses"}}, }) // Read messages (optional) for { _, data, err := c.ReadMessage() if err != nil { log.Fatal("read:", err) } log.Println(string(data)) } } ``` ```java java theme={null} // Java (OkHttp WebSocket) // Gradle: implementation("com.squareup.okhttp3:okhttp:4.12.0") import java.util.concurrent.TimeUnit; import okhttp3.*; public class BalancesSub { public static void main(String[] args) { OkHttpClient client = new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS).build(); Request req = new Request.Builder().url("wss://trade.qfex.com?api_key=YOUR_API_KEY").build(); WebSocketListener li = new WebSocketListener() { @Override public void onOpen(WebSocket ws, Response r) { ws.send("{\"type\": \"auth\", \"params\": { \"hmac\": { \"public_key\": \"qfex_pub_xxxxx\", \"nonce\": \"c0ffee...\..\", \"unix_ts\": 1760545414, \"signature\": \"5f2e...\" }}}"); ws.send("{\"type\":\"subscribe\",\"params\":{\"channels\":[\"order_responses\"]}}"); } @Override public void onMessage(WebSocket ws, String text) { System.out.println(text); } @Override public void onFailure(WebSocket ws, Throwable t, Response r) { System.err.println("WS error: " + t.getMessage()); } }; client.newWebSocket(req, li); try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ignored) {} } } ``` ## Example Responses ```json theme={null} { "subscribed": "order_responses" } ``` ```json theme={null} { "order_response": { "order_id": "5b309929-206f-40ec-804d-cbe46e81afc1", "symbol": "AAPL-USD", "status": "ACK", "quantity": 1.0, "price": 200.0, "take_profit": 0.0, "stop_loss": 0.0, "side": "BUY", "type": "LIMIT", "time_in_force": "GTC", "user_id": "0020ce8e-eaee-480e-8d7f-b9241d756ee5", "client_order_id": "", "quantity_remaining": 1.0, "trade_id": null } } ``` > [!NOTE] > > Order responses are **live**. ## Automatic reduce-only conversion During [soft liquidation and order margin release](/qfex/liquidation-margin-call#order-margin-release-and-soft-liquidation), QFEX can convert resting orders to reduce-only without a client request. Subscribers to `order_responses` receive one `MODIFIED` update for each newly converted order. No additional conversion update is sent for orders already reduce-only. The following example shows the relevant fields; other standard order-response fields are omitted: ```json theme={null} { "order_response": { "order_id": "5b309929-206f-40ec-804d-cbe46e81afc1", "client_order_id": "maker-sell-001", "symbol": "AAPL-USD", "status": "MODIFIED", "order_origin": "SET_REDUCE_ONLY_ALL", "reduce_only": true, "side": "SELL", "type": "LIMIT", "time_in_force": "GTC", "quantity": 8.0, "quantity_remaining": 8.0, "price": 200.0, "update_time": 1792756800.123, "trade_id": null } } ``` | Field | Meaning for this update | | - | - | | `status` | `MODIFIED`: the existing order has changed; this is not a fill or cancellation. | | `order_origin` | `SET_REDUCE_ONLY_ALL`: identifies the exchange-initiated conversion. | | `reduce_only` | `true`: any future execution must reduce the current position. | | `order_id`, `client_order_id` | Unchanged by this conversion. | | `price`, `quantity`, `quantity_remaining` | Unchanged by this conversion, including any remainder from earlier partial fills. | | `update_time` | Timestamp of the order update, in Unix seconds. | The conversion preserves price/time priority. Unlike a client-requested [modify](/websocket/channels/trade/modify_order), it does not replace the order ID. ### Handling the lifecycle 1. Subscribe before relying on live order state. Handle unsolicited updates for your account; do not discard a `MODIFIED` message just because it does not match a pending request. 2. Update your local order's `reduce_only` flag and retain the conversion event for diagnostics. Do not assume the flag remains the value sent in `add_order`, or that margin recovery resets it to `false`. 3. Continue processing fills and cancellations. A converted order may fill only up to the position available to reduce at execution time. If it would increase or reverse the position, the unfillable quantity is cancelled with `status: "CANCELLED"`. This can happen when the order's price would otherwise match, including after another order or liquidation has reduced or closed the position. Use the earlier conversion update to diagnose the cancellation; `CANCELLED` alone does not identify its cause. 4. If modifying a converted order, send `reduce_only: true`. Sending the old `false` value is rejected with `CANNOT_MODIFY_NO_SUCH_ORDER`; a client modify cannot turn reduce-only off. 5. After reconnecting, authenticate and resubscribe, then reconcile open orders using [Get user orders](/websocket/channels/trade/get_user_orders) or [Get order](/websocket/channels/trade/get_order). Read the current `reduce_only` flag from each returned order. Live updates are not a replay of missed events, and these queries exclude filled and fully cancelled orders. For example, a converted sell order for 8 units against a long position of 5 can fill at most 5 units and then receive a cancellation for the remainder. If the position is already flat when matching is attempted, the order can receive `CANCELLED` without a fill. The order's original acknowledgement does not guarantee it remains eligible to execute its original quantity. # Subscribe — Positions Source: https://docs.qfex.com/websocket/channels/trade/positions Subscribe to the **positions** channel to receive pulsed position updates for all symbols.\ Messages are pushed every **1 second**. ## 1) Authenticate ```json theme={null} { "type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." } } } ``` ## 2) Subscribe ```json theme={null} { "type": "subscribe", "params": { "channels": ["positions"] } } ``` ## Sample Code ```python python theme={null} # Python (websocket-client) import json, websocket API_KEY = "YOUR_API_KEY" def send(ws, obj): ws.send(json.dumps(obj)) def on_open(ws): # https://docs.qfex.com/websocket/channels/trade/authenticate#how-it-works send(ws, {"type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." }}}) send(ws, {"type": "subscribe", "params": {"channels": ["positions"]}}) ws = websocket.WebSocketApp( "wss://trade.qfex.com?api_key=YOUR_API_KEY", on_open=on_open, on_message=lambda _, m: print("Message:", m), ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) import WebSocket from "ws"; const API_KEY = "YOUR_API_KEY"; const ws = new WebSocket("wss://trade.qfex.com?api_key=YOUR_API_KEY"); ws.on("open", () => { ws.send(JSON.stringify({ type: "auth", params: { api_key: API_KEY } })); ws.send( JSON.stringify({ type: "subscribe", params: { channels: ["positions"] } }) ); }); ws.on("message", (msg) => console.log("Message:", msg.toString())); ``` ```go go theme={null} // Go (gorilla/websocket) package main import ( "log" "github.com/gorilla/websocket" ) func mustWriteJSON(c *websocket.Conn, v any) { if err := c.WriteJSON(v); err != nil { log.Fatal("write:", err) } } func main() { c, _, err := websocket.DefaultDialer.Dial("wss://trade.qfex.com?api_key=YOUR_API_KEY", nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() mustWriteJSON(c, map[string]any{ "type": "auth", "params": map[string]any{"hmac": map[string]any{"public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..."}} }) mustWriteJSON(c, map[string]any{ "type": "subscribe", "params": map[string]any{"channels": []string{"positions"}}, }) for { _, data, err := c.ReadMessage() if err != nil { log.Fatal("read:", err) } log.Println(string(data)) } } ``` ```java java theme={null} // Java (OkHttp WebSocket) import java.util.concurrent.TimeUnit; import okhttp3.*; public class PositionsSub { public static void main(String[] args) { OkHttpClient client = new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS).build(); Request req = new Request.Builder().url("wss://trade.qfex.com?api_key=YOUR_API_KEY").build(); WebSocketListener li = new WebSocketListener() { @Override public void onOpen(WebSocket ws, Response r) { ws.send("{\"type\": \"auth\", \"params\": { \"hmac\": { \"public_key\": \"qfex_pub_xxxxx\", \"nonce\": \"c0ffee...\..\", \"unix_ts\": 1760545414, \"signature\": \"5f2e...\" }}}"); ws.send("{\"type\":\"subscribe\",\"params\":{\"channels\":[\"positions\"]}}"); } @Override public void onMessage(WebSocket ws, String text) { System.out.println(text); } }; client.newWebSocket(req, li); try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ignored) {} } } ``` ## Example Responses ```json theme={null} { "subscribed": "positions" } ``` ```json theme={null} { "position_response": { "id": "c003ad60-ded4-401b-9b4c-e255a53d32ea", "symbol": "AAPL-USD", "position": 0.0, "margin_alloc": 0.0, "realised_pnl": 0.0, "unrealised_pnl": 0.0, "net_funding": 0.0, "open_orders": 0.0, "open_quantity": 0.0, "leverage": 20.0, "initial_margin": 0.05, "maintenance_margin": 0.03333333333333333, "average_price": 0.0 } } ``` > [!NOTE] > > Position updates are **pulsed every 10 seconds**. # Set Leverage Source: https://docs.qfex.com/websocket/channels/trade/set_leverage You can set the leverage level for each symbol for your account. > [!NOTE] > > You cannot change the leverage level if you have any open orders or a non zero > position. Leverage management is performed over **WebSocket**. * **Endpoint:** `wss://trade.qfex.com?api_key=YOUR_API_KEY` * **Authenticate within 1 minute of connecting.** ## Example Request ```json theme={null} { "type": "set_user_leverage", "params": { "symbol": "AAPL-USD", "leverage": 20.0 } } ``` * `symbol` — the symbol to set the leverage for * `leverage` — the leverage level to set ## Sample Code ```python python theme={null} # Python (websocket-client) import json, websocket API_KEY = "YOUR_API_KEY" def send(ws, obj): ws.send(json.dumps(obj)) def on_open(ws): # Authenticate # https://docs.qfex.com/websocket/channels/trade/authenticate#how-it-works send(ws, {"type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." }}}) # Set leverage send(ws, {"type": "set_user_leverage", "params": {"symbol": "AAPL-USD", "leverage": 20.0}}) ws = websocket.WebSocketApp( "wss://trade.qfex.com?api_key=YOUR_API_KEY", on_open=on_open, on_message=lambda _, m: print("Message:", m) ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) import WebSocket from "ws"; const ws = new WebSocket("wss://trade.qfex.com?api_key=YOUR_API_KEY"); const API_KEY = "YOUR_API_KEY"; ws.on("open", () => { ws.send(JSON.stringify({ type: "auth", params: { api_key: API_KEY } })); ws.send( JSON.stringify({ type: "set_user_leverage", params: { symbol: "AAPL-USD", leverage: 20.0 }, }) ); }); ws.on("message", (msg) => console.log("Message:", msg.toString())); ``` ```go go theme={null} // Go (gorilla/websocket) package main import ( "log" "github.com/gorilla/websocket" ) func mustWriteJSON(c *websocket.Conn, v any) { if err := c.WriteJSON(v); err != nil { log.Fatal("write:", err) } } func main() { c, _, err := websocket.DefaultDialer.Dial("wss://trade.qfex.com?api_key=YOUR_API_KEY", nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() // Authenticate mustWriteJSON(c, map[string]any{ "type": "auth", "params": map[string]any{"hmac": map[string]any{"public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..."}} }) // Set leverage mustWriteJSON(c, map[string]any{ "type": "set_user_leverage", "params": map[string]any{"symbol": "AAPL-USD", "leverage": 20.0}, }) } ``` ```java java theme={null} // Java (OkHttp WebSocket) import java.util.concurrent.TimeUnit; import okhttp3.*; public class GetUserTradesWs { public static void main(String[] args) { OkHttpClient client = new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS).build(); Request req = new Request.Builder() .url("wss://trade.qfex.com?api_key=YOUR_API_KEY").build(); WebSocketListener li = new WebSocketListener() { @Override public void onOpen(WebSocket ws, Response r) { ws.send("{\"type\": \"auth\", \"params\": { \"hmac\": { \"public_key\": \"qfex_pub_xxxxx\", \"nonce\": \"c0ffee...\..\", \"unix_ts\": 1760545414, \"signature\": \"5f2e...\" }}}"); ws.send("{\"type\":\"set_user_leverage\",\"params\":{\"symbol\":\"AAPL-USD\",\"leverage\":20.0}}"); } @Override public void onMessage(WebSocket ws, String text) { System.out.println(text); } }; client.newWebSocket(req, li); try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ignored) {} } } ``` ## Example Response ```json theme={null} { "ack_response": true } ``` # Order Entry — Add Stop Order Source: https://docs.qfex.com/websocket/channels/trade/stop_add_order Order entry is performed over **WebSocket**. * **Endpoint:** `wss://trade.qfex.com?api_key=YOUR_API_KEY` * **Authenticate within 1 minute of connecting.** ## 1) Authenticate Send this immediately after you connect: ```json theme={null} { "type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." } } } ``` ## 2) Add Order Adding a stop order follows the same pattern as adding a typical order. The direction given must be the eventual direction of the triggered order that is placed into the book. TAKE\_PROFIT and STOP\_LOSS orders cannot exceed your current position, attempting to do this could cause your order to be rejected or other stop orders to be cancelled. ```json theme={null} { "type": "add_order", "params": { "symbol": "AAPL-USD", "side": "BUY", "order_type": "TAKE_PROFIT", "order_time_in_force": "GTC", "quantity": 1, "price": 200 } } ``` ## Parameters | Field | Type | Required | Description | | - | - | - | - | | `symbol` | string | ✅ | The market symbol (e.g. `AAPL-USD`). | | `side` | enum | ✅ | Order direction. See [OrderDirection](/api-reference/enums#orderdirection). | | `order_type` | enum | ✅ | Type of order. See [OrderType](/api-reference/enums#ordertype). | | `order_time_in_force` | enum | ✅ | Time in force policy. Required for legacy reasons, the value will not be read. | | `quantity` | number | ✅ | Quantity of the order. Must respect lot size but not min/max limits. | | `price` | number | ✅ | Price for when the order is triggered. | > ⚠️ Notes: > > * Tick size, lot size, and min/max order quantity are symbol-specific. See **Reference Data API**. > * An order may be rejected with [OrderStatus](/api-reference/enums#orderstatus) codes if parameters are invalid. ## Sample Code ```python python theme={null} # Python (websocket-client) # pip install websocket-client import json import websocket API_KEY = "YOUR_API_KEY" def send(ws, obj): ws.send(json.dumps(obj)) def on_open(ws): # 1) Authenticate # https://docs.qfex.com/websocket/channels/trade/authenticate#how-it-works send(ws, {"type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." }}}) # 2) Add stop order send(ws, { "type": "add_order", "params": { "symbol": "AAPL-USD", "side": "BUY", "order_type": "TAKE_PROFIT", "order_time_in_force": "GTC", "quantity": 1, "price": 200 } }) ws = websocket.WebSocketApp( "wss://trade.qfex.com?api_key=YOUR_API_KEY", on_open=on_open, on_message=lambda _, m: print(m), on_error=lambda _, e: print("Error:", e), on_close=lambda *_: print("Closed"), ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) // npm i ws const WebSocket = require("ws"); const ws = new WebSocket("wss://trade.qfex.com?api_key=YOUR_API_KEY"); ws.on("open", () => { // 1) Authenticate ws.send( JSON.stringify({ type: "auth", params: { api_key: "YOUR_API_KEY" } }) ); // 2) Add stop order ws.send( JSON.stringify({ type: "add_order", params: { symbol: "AAPL-USD", side: "BUY", order_type: "TAKE_PROFIT", order_time_in_force: "GTC", quantity: 1, price: 200, }, }) ); }); ws.on("message", (m) => console.log(m.toString())); ws.on("error", (e) => console.error("WS error:", e)); ws.on("close", (code, reason) => console.log("Closed:", code, reason?.toString()) ); ``` ```go go theme={null} // Go (gorilla/websocket) // go get github.com/gorilla/websocket package main import ( "log" "github.com/gorilla/websocket" ) func mustWriteJSON(c *websocket.Conn, v any) { if err := c.WriteJSON(v); err != nil { log.Fatal("write:", err) } } func main() { c, _, err := websocket.DefaultDialer.Dial("wss://trade.qfex.com?api_key=YOUR_API_KEY", nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() // 1) Authenticate mustWriteJSON(c, map[string]any{ "type": "auth", "params": map[string]any{"hmac": map[string]any{"public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..."}} }) // 2) Add stop order mustWriteJSON(c, map[string]any{ "type": "add_order", "params": map[string]any{ "symbol": "AAPL-USD", "side": "BUY", "order_type": "TAKE_PROFIT", "order_time_in_force": "GTC", "quantity": 1, "price": 200, }, }) } ``` ```java java theme={null} // Java (OkHttp WebSocket) // Gradle: implementation("com.squareup.okhttp3:okhttp:4.12.0") import java.util.concurrent.TimeUnit; import okhttp3.*; public class AddOrderWs { public static void main(String[] args) { OkHttpClient client = new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS) .build(); Request req = new Request.Builder() .url("wss://trade.qfex.com?api_key=YOUR_API_KEY") .build(); WebSocketListener li = new WebSocketListener() { @Override public void onOpen(WebSocket ws, Response r) { // 1) Authenticate ws.send("{\"type\": \"auth\", \"params\": { \"hmac\": { \"public_key\": \"qfex_pub_xxxxx\", \"nonce\": \"c0ffee...\..\", \"unix_ts\": 1760545414, \"signature\": \"5f2e...\" }}}"); // 2) Add stop order ws.send("{\"type\":\"add_order\",\"params\":{\"symbol\":\"AAPL-USD\",\"side\":\"BUY\",\"order_type\":\"TAKE_PROFIT\",\"order_time_in_force\":\"GTC\",\"quantity\":1,\"price\":200}}"); } @Override public void onMessage(WebSocket ws, String text) { System.out.println(text); } @Override public void onFailure(WebSocket ws, Throwable t, Response r) { System.err.println("WS error: " + t.getMessage()); } }; client.newWebSocket(req, li); try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ignored) {} } } ``` ## Example Response When you place an order, you will receive an `order_response` with status `ACK`: ```json theme={null} { "order_response": { "order_id": "5b309929-206f-40ec-804d-cbe46e81afc1", "symbol": "AAPL-USD", "status": "ACK", "quantity": 1.0, "price": 200.0, "take_profit": 0.0, "stop_loss": 0.0, "side": "BUY", "type": "TAKE_PROFIT", "time_in_force": "GTC", "user_id": "0020ce8e-eaee-480e-8d7f-b9241d756ee5", "client_order_id": "", "quantity_remaining": 1.0 } } ``` # Order Entry — Cancel Stop Order Source: https://docs.qfex.com/websocket/channels/trade/stop_cancel_order Stop Orders can be cancelled over **WebSocket**. * You **must** specify the stop\_order's **`stop_order_id`** (UUID v4). ## 1) Authenticate Send this immediately after you connect: ```json theme={null} { "type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." } } } ``` ## 2) Cancel Order ```json theme={null} { "type": "cancel_stop_order", "params": { "stop_order_id": "510333ac-3f7b-4d88-934a-4396d48824cc" } } ``` ## Parameters | Field | Type | Required | Description | | - | - | - | - | | `stop_order_id` | string | ✅ | UUID v4 of the stop order to cancel. | ## Sample Code ```python python theme={null} # Python (websocket-client) import json, websocket API_KEY = "YOUR_API_KEY" def send(ws, obj): ws.send(json.dumps(obj)) def on_open(ws): # Authenticate # https://docs.qfex.com/websocket/channels/trade/authenticate#how-it-works send(ws, {"type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." }}}) # Cancel stop order send(ws, { "type": "cancel_stop_order", "params": { "stop_order_id": "510333ac-3f7b-4d88-934a-4396d48824cc" } }) ws = websocket.WebSocketApp( "wss://trade.qfex.com?api_key=YOUR_API_KEY", on_open=on_open, on_message=lambda _, m: print("Message:", m) ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) import WebSocket from "ws"; const ws = new WebSocket("wss://trade.qfex.com?api_key=YOUR_API_KEY"); const API_KEY = "YOUR_API_KEY"; ws.on("open", () => { // Authenticate ws.send(JSON.stringify({ type: "auth", params: { api_key: API_KEY } })); // Cancel stop order ws.send( JSON.stringify({ type: "cancel_stop_order", params: { stop_order_id: "510333ac-3f7b-4d88-934a-4396d48824cc", }, }) ); }); ws.on("message", (msg) => console.log("Message:", msg.toString())); ``` ```go go theme={null} // Go (gorilla/websocket) package main import ( "log" "github.com/gorilla/websocket" ) func mustWriteJSON(c *websocket.Conn, v any) { if err := c.WriteJSON(v); err != nil { log.Fatal("write:", err) } } func main() { c, _, err := websocket.DefaultDialer.Dial("wss://trade.qfex.com?api_key=YOUR_API_KEY", nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() // Authenticate mustWriteJSON(c, map[string]any{ "type": "auth", "params": map[string]any{"hmac": map[string]any{"public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..."}} }) // Cancel stop order mustWriteJSON(c, map[string]any{ "type": "cancel_stop_order", "params": map[string]any{ "stop_order_id": "510333ac-3f7b-4d88-934a-4396d48824cc", }, }) } ``` ```java java theme={null} // Java (OkHttp WebSocket) import java.util.concurrent.TimeUnit; import okhttp3.*; public class CancelStopOrderWs { public static void main(String[] args) { OkHttpClient client = new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS).build(); Request req = new Request.Builder() .url("wss://trade.qfex.com?api_key=YOUR_API_KEY").build(); WebSocketListener li = new WebSocketListener() { @Override public void onOpen(WebSocket ws, Response r) { // Authenticate ws.send("{\"type\": \"auth\", \"params\": { \"hmac\": { \"public_key\": \"qfex_pub_xxxxx\", \"nonce\": \"c0ffee...\..\", \"unix_ts\": 1760545414, \"signature\": \"5f2e...\" }}}"); // Cancel stop order ws.send("{\"type\":\"cancel_stop_order\",\"params\":{\"stop_order_id\":\"510333ac-3f7b-4d88-934a-4396d48824cc\"}}"); } @Override public void onMessage(WebSocket ws, String text) { System.out.println(text); } }; client.newWebSocket(req, li); try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ignored) {} } } ``` ## Example Response When a stop order is successfully cancelled, you receive an `order_response` with status **`CANCELLED`**: ```json theme={null} { "order_response": { "order_id": "510333ac-3f7b-4d88-934a-4396d48824cc", "symbol": "AAPL-USD", "status": "CANCELLED", "quantity": 0.0, "price": 200.0, "side": "BUY", "type": "TAKE_PROFIT", "time_in_force": "GTC", "user_id": "0020ce8e-eaee-480e-8d7f-b9241d756ee5", "client_order_id": "", "quantity_remaining": 0.0 } } ``` # Order Entry — Modify Stop Order Source: https://docs.qfex.com/websocket/channels/trade/stop_modify_order Stop Orders can be modified over **WebSocket**.\ Only **`price`** and **`quantity`** may be changed. * You **must** specify the stop\_order’s **`stop_order_id`** (UUID v4). * You **must** specify the **`symbol`**. * You **must** specify the **`side`**. * You **must** specify the **`order_type`**. * `client_order_id` cannot be used for modifies. ## 1) Authenticate Send this immediately after you connect: ```json theme={null} { "type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." } } } ``` ## 2) Modify Order ```json theme={null} { "type": "modify_stop_order", "params": { "stop_order_id": "510333ac-3f7b-4d88-934a-4396d48824cc", "symbol": "AAPL-USD", "side": "BUY", "order_type": "TAKE_PROFIT", "quantity": 2, "price": 200 } } ``` ## Parameters | Field | Type | Required | Description | | - | - | - | - | | `stop_order_id` | string | ✅ | UUID v4 of the order to modify. | | `symbol` | string | ✅ | The market symbol (e.g. `AAPL-USD`). | | `side` | enum | ✅ | Must match the original stop order's side. See [OrderDirection](/api-reference/enums#orderdirection). | | `order_type` | enum | ✅ | Must match the original stop order's type. See [OrderType](/api-reference/enums#ordertype). | | `quantity` | number | Optional | New stop order quantity. Must respect symbol lot size, min, and max constraints. | | `price` | number | Optional | New price for when the order is triggered. Must respect tick size but not price bands. | > ⚠️ Notes: > > * Only `price` and `quantity` may be modified. ## Sample Code ```python python theme={null} # Python (websocket-client) import json, websocket API_KEY = "YOUR_API_KEY" def send(ws, obj): ws.send(json.dumps(obj)) def on_open(ws): # Authenticate # https://docs.qfex.com/websocket/channels/trade/authenticate#how-it-works send(ws, {"type": "auth", "params": { "hmac": { "public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..." }}}) # Modify stop order send(ws, { "type": "modify_stop_order", "params": { "stop_order_id": "510333ac-3f7b-4d88-934a-4396d48824cc", "symbol": "AAPL-USD", "side": "BUY", "order_type": "TAKE_PROFIT", "quantity": 2, "price": 200 } }) ws = websocket.WebSocketApp( "wss://trade.qfex.com?api_key=YOUR_API_KEY", on_open=on_open, on_message=lambda _, m: print("Message:", m) ) ws.run_forever() ``` ```javascript node theme={null} // Node.js (ws) import WebSocket from "ws"; const ws = new WebSocket("wss://trade.qfex.com?api_key=YOUR_API_KEY"); const API_KEY = "YOUR_API_KEY"; ws.on("open", () => { // Authenticate ws.send(JSON.stringify({ type: "auth", params: { api_key: API_KEY } })); // Modify stop order ws.send( JSON.stringify({ type: "modify_stop_order", params: { stop_order_id: "510333ac-3f7b-4d88-934a-4396d48824cc", symbol: "AAPL-USD", side: "BUY", order_type: "TAKE_PROFIT", quantity: 2, price: 200, }, }) ); }); ws.on("message", (msg) => console.log("Message:", msg.toString())); ``` ```go go theme={null} // Go (gorilla/websocket) package main import ( "log" "github.com/gorilla/websocket" ) func mustWriteJSON(c *websocket.Conn, v any) { if err := c.WriteJSON(v); err != nil { log.Fatal("write:", err) } } func main() { c, _, err := websocket.DefaultDialer.Dial("wss://trade.qfex.com?api_key=YOUR_API_KEY", nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() // Authenticate mustWriteJSON(c, map[string]any{ "type": "auth", "params": map[string]any{"hmac": map[string]any{"public_key": "qfex_pub_xxxxx", "nonce": "c0ffee...", "unix_ts": 1760545414, "signature": "5f2e..."}} }) // Modify stop order mustWriteJSON(c, map[string]any{ "type": "modify_stop_order", "params": map[string]any{ "stop_order_id": "510333ac-3f7b-4d88-934a-4396d48824cc", "symbol": "AAPL-USD", "side": "BUY", "order_type": "TAKE_PROFIT", "quantity": 2, "price": 200, }, }) } ``` ```java java theme={null} // Java (OkHttp WebSocket) import java.util.concurrent.TimeUnit; import okhttp3.*; public class ModifyOrderWs { public static void main(String[] args) { OkHttpClient client = new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS).build(); Request req = new Request.Builder() .url("wss://trade.qfex.com?api_key=YOUR_API_KEY").build(); WebSocketListener li = new WebSocketListener() { @Override public void onOpen(WebSocket ws, Response r) { // Authenticate ws.send("{\"type\": \"auth\", \"params\": { \"hmac\": { \"public_key\": \"qfex_pub_xxxxx\", \"nonce\": \"c0ffee...\..\", \"unix_ts\": 1760545414, \"signature\": \"5f2e...\" }}}"); // Modify stop order ws.send("{\"type\":\"modify_stop_order\",\"params\":{\"stop_order_id\":\"510333ac-3f7b-4d88-934a-4396d48824cc\",\"symbol\":\"AAPL-USD\",\"side\":\"BUY\",\"order_type\":\"TAKE_PROFIT\",\"quantity\":2,\"price\":200}}"); } @Override public void onMessage(WebSocket ws, String text) { System.out.println(text); } }; client.newWebSocket(req, li); try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException ignored) {} } } ``` ## Example Response When an order is successfully modified, you receive the same `order_response` object as with add, but with status **`MODIFIED`**: ```json theme={null} { "order_response": { "order_id": "510333ac-3f7b-4d88-934a-4396d48824cc", "symbol": "AAPL-USD", "status": "MODIFIED", "quantity": 2.0, "price": 200.0, "side": "BUY", "type": "TAKE_PROFIT", "time_in_force": "GTC", "user_id": "0020ce8e-eaee-480e-8d7f-b9241d756ee5", "client_order_id": "", "quantity_remaining": 2.0 } } ``` # Errors Source: https://docs.qfex.com/websocket/errors All websocket channels may return an **error frame** instead of the expected response. Error frames share a single, consistent schema. ## Error Envelope ```json theme={null} { "err": { "error_code": "RateLimited", "message": "You have exceeded the allowed message rate.", "incoming_message": { "type": "add_order", "params": { "symbol": "AAPL", "side": "BUY", "order_type": "LIMIT", "order_time_in_force": "GTC", "quantity": 10.0, "price": 200.0, "client_order_id": "my-order-001", "take_profit": null, "stop_loss": null } } } } ``` ### Fields | Field | Type | Required | Description | | - | - | - | - | | `error_code` | string | Yes | Machine-readable error code (see list below). Matches server enum variant exactly. | | `message` | string/null | No | Human-readable context. May be `null`. | | `incoming_message` | object/null | No | Echo of the request that caused the error (your original message), when available. | > Note: `incoming_message` uses the same tagged format as requests (`{ "type": "", "params": { ... } }`). ## Error Codes These values map 1:1 to the server enum: | Code | When it happens | Suggested Client Action | | - | - | - | | `RateLimited` | You’ve exceeded the allowed message rate (weighted per message type). | Backoff and retry later. Consider batching and respecting published limits. | | `InvalidJSONFormat` | Message is not valid JSON or doesn’t match the expected schema. | Validate JSON and schema before sending. Include required fields and correct types/enum values. | | `AlreadyAuthenticated` | You attempted to authenticate again after a successful auth on the same connection. | Do not re-auth on an already authenticated socket. | | `InvalidParameter` | One or more parameters are out of range, missing, or invalid for the specified message. | Correct the parameter(s) and retry. | | `PermissionDenied` | The API key or user lacks permission for the requested action. | Check key scopes/roles; contact support if needed. | | `ServerError` | Unexpected internal error. | Safe to retry after a short delay; if persistent, contact support with timestamp and `incoming_message`. | | `InvalidOrder` | The order is invalid or malformed. | Check order parameters and format; correct the order and retry. | | `InvalidOrderId` | The supplied order ID is invalid. | Check that the order ID is a valid order UUID. | | `KycRequired` | The user must complete KYC before performing this action. | Complete KYC and retry the request. | | `TncRequired` | The user needs to agree to the latest Terms and Conditions. | Agree to the latest Terms and Conditions. | ### Rate limiting notes The server computes **message weight** by the message `"type"` (e.g., `"add_order"`, `"cancel_order"`) and applies limits over a sliding window. Some messages are heavier than others. If you see `RateLimited`, delay and reduce frequency; prefer fewer, larger requests when possible. ## Examples ### 1) Invalid JSON ```json theme={null} { "err": { "error_code": "InvalidJSONFormat", "message": "Expected field `symbol` (string).", "incoming_message": { "type": "add_order", "params": { "side": "BUY", "order_type": "LIMIT", "order_time_in_force": "GTC", "quantity": 10.0, "price": 200.0 } } } } ``` ### 2) Already authenticated ```json theme={null} { "err": { "error_code": "AlreadyAuthenticated", "message": "Connection is already authenticated.", "incoming_message": { "type": "auth", "params": {} } } } ``` ### 3) Invalid parameter ```json theme={null} { "err": { "error_code": "InvalidParameter", "message": "price must be >= min_tick and within price bands.", "incoming_message": { "type": "modify_order", "params": { "symbol": "AAPL", "order_id": "cd1a2b0c-...-f3", "quantity": 10.0, "price": 0.0001, "take_profit": 0.0, "stop_loss": 0.0, "side": "BUY" } } } } ``` ### 4) Permission denied ```json theme={null} { "err": { "error_code": "PermissionDenied", "message": "API key does not allow trading on this account.", "incoming_message": { "type": "transfer_balance", "params": { "amount": 100.0, "to_api_key": "****" } } } } ``` ### 5) Rate limited ```json theme={null} { "err": { "error_code": "RateLimited", "message": "Too many requests. Please retry after 200ms.", "incoming_message": { "type": "cancel_all_orders", "params": {} } } } ``` ### 6) Server error ```json theme={null} { "err": { "error_code": "ServerError", "message": "Unexpected error processing request.", "incoming_message": { "type": "get_user_trades", "params": { "limit": 50 } } } } ``` ## Client Handling Patterns > **Python** > > ```python theme={null} > import json > import websocket > > def on_message(ws, message): > msg = json.loads(message) > if msg.get("type") == "error": > code = msg.get("error_code") > print(f"[ERROR] {code}: {msg.get('message')}") > # basic strategy > if code == "RateLimited": > # implement backoff before retrying your last request > return > elif code in ("InvalidJSONFormat", "InvalidParameter"): > # fix your payload; don't auto-retry > return > elif code in ("PermissionDenied", "AlreadyAuthenticated"): > # adjust flow; no retry > return > else: > # ServerError or unknown: small backoff and optional retry > return > else: > print("OK:", msg) > ``` > **Node** > > ```javascript theme={null} > import WebSocket from "ws"; > > const ws = new WebSocket("wss://trade.qfex.com?api_key=YOUR_API_KEY"); > > ws.on("message", (raw) => { > const msg = JSON.parse(raw.toString()); > if (msg.type === "error") { > const { error_code, message } = msg; > console.error(`[ERROR] ${error_code}: ${message || ""}`); > > switch (error_code) { > case "RateLimited": > // schedule retry w/ backoff > break; > case "InvalidJSONFormat": > case "InvalidParameter": > // fix payload, do not blind-retry > break; > case "PermissionDenied": > case "AlreadyAuthenticated": > // update client state/flow > break; > default: > // transient server error: optional retry with jitter > break; > } > return; > } > > // handle normal messages… > }); > ``` > **Go** > > ```go theme={null} > type ErrorFrame struct { > Type string `json:"type"` > ErrorCode string `json:"error_code"` > Message *string `json:"message,omitempty"` > IncomingMessage json.RawMessage `json:"incoming_message,omitempty"` > } > > func handleMessage(raw []byte) { > // peek at "type" > var meta struct{ Type string `json:"type"` } > if err := json.Unmarshal(raw, &meta); err != nil { return } > > if meta.Type == "error" { > var ef ErrorFrame > if err := json.Unmarshal(raw, &ef); err == nil { > log.Printf("[ERROR] %s: %s", ef.ErrorCode, deref(ef.Message)) > // handle per code... > } > return > } > > // normal flow... > } > ``` ## Authentication & Errors * **trade.qfex.com** requires authentication within 1 minute of connecting. * Attempting to re-authenticate an already authenticated connection yields `AlreadyAuthenticated`. ## Troubleshooting Checklist 1. Validate JSON (types/enums/required fields). 2. Respect rate limits (reduce burstiness; batch where possible). 3. Ensure your API key has the right permissions. 4. Log `incoming_message` from error frames to quickly reproduce issues. 5. For persistent `ServerError`, contact **[support@qfex.com](mailto:support@qfex.com)** with timestamp and offending `incoming_message`. # Websocket Overview Source: https://docs.qfex.com/websocket/main QFEX provides 2 endpoints for websocket: * [mds.qfex.com](wss://mds.qfex.com) for public market and reference data. * [trade.qfex.com](wss://trade.qfex.com) for order entry and private data. Market Data is public and does not require authentication. Trade requires an API key and a connection must [authenticate](/websocket/channels/trade/authenticate) within 1 minute of connecting. If you want the Trade WebSocket session to operate on a subaccount, include `account_id` in the auth payload. When omitted, the connection uses the primary account. To attribute trading fees to a builder, include `builder_code` in the same auth payload. See [Builder Codes](/qfex/builder-code) and [Authenticate](/websocket/channels/trade/authenticate). ## Getting Started To access private data or place orders, you must connect to `trade.qfex.com` and authenticate. The following examples show how to connect, authenticate using HMAC-SHA256, and maintain the connection. > **Setup Python** > > ```python theme={null} > # pip install websocket-client > import hashlib > import hmac > import json > import secrets > import time > import websocket > > PUB_KEY = "qfex_pub_xxxxx" > SECRET_KEY = "qfex_secret_yyyyyy" > > def build_auth_message(): > nonce = secrets.token_hex(16) > unix_ts = int(time.time()) > payload = f"{nonce}:{unix_ts}".encode() > signature = hmac.new(SECRET_KEY.encode(), payload, hashlib.sha256).hexdigest() > return { > "type": "auth", > "params": { > "hmac": { > "public_key": PUB_KEY, > "nonce": nonce, > "unix_ts": unix_ts, > "signature": signature, > } > }, > } > > def on_open(ws): > print("Connected") > ws.send(json.dumps(build_auth_message())) > > def on_message(ws, message): > print("Received:", message) > > ws = websocket.WebSocketApp( > "wss://trade.qfex.com?api_key=" + PUB_KEY, > on_open=on_open, > on_message=on_message > ) > > ws.run_forever() > ``` > **Setup Node** > > ```javascript theme={null} > // npm i ws > import crypto from "crypto"; > import WebSocket from "ws"; > > const pubKey = "qfex_pub_xxxxx"; > const secretKey = "qfex_secret_yyyyyy"; > > function buildAuthMessage() { > const nonce = crypto.randomBytes(16).toString("hex"); > const unixTs = Math.floor(Date.now() / 1000); > const payload = `${nonce}:${unixTs}`; > const signature = crypto > .createHmac("sha256", secretKey) > .update(payload) > .digest("hex"); > > return { > type: "auth", > params: { > hmac: { > public_key: pubKey, > nonce, > unix_ts: unixTs, > signature, > }, > }, > }; > } > > const ws = new WebSocket(`wss://trade.qfex.com?api_key=${pubKey}`); > > ws.on("open", () => { > console.log("Connected"); > ws.send(JSON.stringify(buildAuthMessage())); > }); > > ws.on("message", (msg) => { > console.log("Received:", msg.toString()); > }); > ``` > **Setup Go** > > ```go theme={null} > // go get github.com/gorilla/websocket > package main > > import ( > "crypto/hmac" > "crypto/rand" > "crypto/sha256" > "encoding/hex" > "fmt" > "log" > "time" > "github.com/gorilla/websocket" > ) > > func main() { > pubKey := "qfex_pub_xxxxx" > secretKey := []byte("qfex_secret_yyyyyy") > > c, _, err := websocket.DefaultDialer.Dial("wss://trade.qfex.com?api_key="+pubKey, nil) > if err != nil { > log.Fatal("dial:", err) > } > defer c.Close() > > // Build Auth Message > nonceBytes := make([]byte, 16) > rand.Read(nonceBytes) > nonce := hex.EncodeToString(nonceBytes) > unixTs := time.Now().Unix() > message := fmt.Sprintf("%s:%d", nonce, unixTs) > > h := hmac.New(sha256.New, secretKey) > h.Write([]byte(message)) > signature := hex.EncodeToString(h.Sum(nil)) > > auth := map[string]any{ > "type": "auth", > "params": map[string]any{ > "hmac": map[string]any{ > "public_key": pubKey, > "nonce": nonce, > "unix_ts": unixTs, > "signature": signature, > }, > }, > } > > c.WriteJSON(auth) > > for { > _, message, err := c.ReadMessage() > if err != nil { > log.Println("read:", err) > return > } > fmt.Printf("recv: %s\n", message) > } > } > ``` ## Authentication Authentication is required for the `trade.qfex.com` endpoint. 1. **Connect**: Include your public API key as a query parameter: ``` wss://trade.qfex.com?api_key=YOUR_PUBLIC_KEY ``` 2. **Authenticate**: Send an authentication message signed with your secret key within 1 minute. You can use either an **HMAC-SHA256 signature** (recommended) or a valid **JWT token**. For full details on the authentication payload and parameters, see the [Authenticate](/websocket/channels/trade/authenticate) documentation. # MDS Playground Source: https://docs.qfex.com/websocket/mds Single channel for orderbook, trades, candles # API Rate Limits Source: https://docs.qfex.com/websocket/rate This page explains how request throttling works on our **WebSocket trading API**. If you’re writing a client, read this to avoid `RateLimited` errors. ## Summary * Rate limits are **per user** and apply across **all** of your concurrent WebSocket connections. * The limit is **12,000 weight units per 60 seconds**. * We use an **Exponential Moving Average (EMA)** model to smooth bursts rather than a simple per-second counter. * There are **two independent buckets** per user: * **General bucket** for orders, queries, subscriptions, etc. * **Cancel bucket** for cancel-related messages. * Each message type has an internal **weight**. Heavier actions (e.g., placing orders) consume more budget than light ones (e.g., subscribing). > Limits and weights may change over time to protect system stability. Always handle `RateLimited` responses gracefully rather than assuming fixed throughput. *** ## Message weights Current values: | Message | Weight | | - | :-: | | add\_order | 1.0 | | cancel\_order | 1.0 | | modify\_order | 1.0 | | get\_order | 2.0 | | get\_user\_orders | 5.0 | | cancel\_all\_orders | 2.0 | | get\_user\_trades | 0.5 | | subscribe | 0.1 | | unsubscribe | 0.1 | | get\_user\_leverage | 0.1 | | get\_available\_leverage\_levels | 0.1 | | set\_user\_leverage | 0.1 | | cancel\_stop\_order | 1.0 | | modify\_stop\_order | 1.0 | | cancel\_on\_disconnect | 0.1 | The current limit is **12,000 units per 60 seconds** (200 units/sec). An `add_order` (1.0) uses 1 unit, so you can sustainably place \~200 orders/sec. *** ## What’s limited These limits apply to **WebSocket inbound messages** after successful authentication. HTTP endpoints and the initial WebSocket handshake may be subject to separate controls. *** ## How it works We maintain an **EMA of your weighted message rate**. Each incoming message adds its weight; between messages the EMA decays automatically. * If your EMA rises above the allowed threshold for a bucket, the next message for that bucket is rejected with a `RateLimited` error. * The error includes a **retry-after** hint so your client knows when it is safe to try again. Because cancels use a **separate bucket**, you can often cancel even when your general bucket is momentarily saturated (subject to the cancel bucket’s own limits). *** ## Per-user, cross-connection If you open multiple WebSocket connections, their traffic **aggregates** into the same per-user budgets (one general, one cancel). Opening more sockets does **not** increase your effective allowance. *** ## Error you may see When a message is throttled, you’ll receive a unicast error on your WebSocket: ```json theme={null} { "type": "Err", "error_code": "RateLimited", "message": "Rate limit exceeded, retry after N seconds", "incoming_message": {} } ``` Use the `retry after` value as guidance for when to resend. *** ## Client best practices 1. **Backoff with jitter**\ On `RateLimited`, wait the suggested number of seconds **plus a small random jitter** (e.g., 50–200 ms) before retrying. This avoids thundering herds. 2. **Coalesce & batch**\ Prefer fewer, purposeful messages over many tiny ones (especially modifies). 3. **Respect cancel bucket**\ Cancels have their own allowance to help you unwind risk quickly. It’s separate, not unlimited—avoid bursty cancel storms. 4. **Stagger across connections**\ If you run multiple connections, **stagger** bursts. They share the same per-user budgets. 5. **Idempotency**\ Use client order IDs where supported so a retry doesn’t create duplicates if the first attempt actually succeeded server-side. 6. **Subscription hygiene**\ Subscriptions are light but not free. Avoid repeatedly subscribing/unsubscribing in short intervals. *** ## FAQs **Is the limit per IP or per API key?**\ Per **user**. Multiple API keys tied to the same user share the same budgets. **Do reads count against the limit?**\ Yes, but they are lighter than writes. **Can I burst?**\ Short bursts are tolerated due to EMA smoothing. Sustained rates above the threshold will be throttled. **Can I request higher limits?**\ If you have a production use case that requires more throughput, contact us to discuss dedicated quotas. *** ## Change policy We may adjust rate-limit thresholds and message weights from time to time to maintain platform reliability. Such changes do not require client updates, provided your integration handles `RateLimited` responses and performs backoff as described above. # Trade Playground Source: https://docs.qfex.com/websocket/trade Single channel for order commands, positions, balances, fills