QL-Dash Developer Guide
Connect, trade, and run live alphas on QL-Dash from Python. Read this page top to bottom and you'll go from "what is FIX?" to a running strategy with real market data.
Step 1 What is FIX?
QL-Dash speaks FIX 4.4 (Financial Information eXchange) over QuickFIX — the same industry-standard messaging protocol banks and brokers use worldwide for order entry and execution. Before you write any code, it helps to know what FIX gives you:
- Standardized messages for orders, execution reports, and market data
- Session management — logon, logout, and heartbeats keep the connection alive
- Reliability — message sequencing and recovery so nothing is silently lost
- Two key IDs you'll see everywhere:
ClOrdID(you generate it) vsOrderID(the server assigns it)
The full connection sequence, how to read raw FIX messages, debug fields, and common pitfalls live in the dedicated guide. Read it once and FIX stops feeling like a black box.
Step 2 Why you need the package
You could speak FIX by hand — but you'd spend your time on plumbing instead of strategy. The
paperbroker-client package wraps all of it behind a few Python calls.
| Task | Raw FIX, by hand | With paperbroker-client |
|---|---|---|
| Connect & stay alive | Build logon, manage heartbeats & sequence numbers, handle resend requests | client.connect() |
| Place an order | Assemble tag/value message 35=D|11=...|55=...|54=1|38=1|44=... | client.place_order(...) |
| Track a fill | Parse execution reports, match ClOrdID/OrderID, decode status enums | Subscribe to events, get typed callbacks |
| Reconnect after drop | Re-logon, recover sequence, re-query open orders | Handled + optional SQLite persistence |
Download & install
DAY. Preview track — production should pin v0.2.4.recover_pending_orders(). Recommended for production.First, create a virtual environment
Install the package inside a virtual environment (venv), never into your system Python.
A venv gives this project its own isolated set of dependencies, so paperbroker-client (and
its pinned version of quickfix) can't clash with other projects or with packages your OS
relies on. It also means you can wipe and rebuild the environment cleanly if anything breaks — without
touching the rest of your machine.
# Create & activate a virtual environment (Python 3.10+)
python -m venv .venv
# macOS / Linux
source .venv/bin/activate
# Windows (PowerShell)
# .venv\Scripts\Activate.ps1
# Upgrade pip inside the fresh venv
python -m pip install --upgrade pip# Install from the downloaded wheel (with the venv activated)
pip install paperbroker_client-0.2.7-py3-none-any.whl
# Optional: enable the YAML-driven alpha CLI
pip install 'paperbroker_client[cli]'
# For production, pin the stable track instead:
# pip install paperbroker_client-0.2.4-py3-none-any.whl
paperbroker-client depends on quickfix, which is a C++ extension that has
to be compiled at install time. On macOS this build can fail (toolchain / Xcode CLT mismatches,
Apple-Silicon vs. Intel, missing headers), and you may need to compile a quickfix build
specifically for your Mac instead of relying on a prebuilt wheel.
If the build fails or you're not sure how to produce a Mac-compatible quickfix,
contact an admin for support — don't fight the toolchain alone. (Internal — your
team knows who to reach.)
Step 3 How the API works
The PaperBrokerClient is event-based. You connect once, wait for logon, then place orders
and react to fills through callbacks. Here is the whole loop in one snippet:
from paperbroker.client import PaperBrokerClient
client = PaperBrokerClient(
default_sub_account="D1",
username="your_username",
password="your_password",
rest_base_url="http://localhost:9090",
socket_connect_host="localhost",
socket_connect_port=5001,
sender_comp_id="your-FIX",
target_comp_id="SERVER",
)
# React to session + execution events
client.on("fix:logon", lambda session_id, **kw: print(f"✅ Logged in: {session_id}"))
client.connect() # opens the FIX session
if client.wait_until_logged_on(timeout=10):
cash = client.get_cash_balance() # account query
cl_ord_id = client.place_order( # new order single (35=D)
full_symbol="HNXDS:VN30F2606", side="BUY", qty=1, price=1200.0,
) # tif defaults to "DAY" in v0.2.7
is_terminal, status = client.cancel_order(cl_ord_id, timeout=5.0)The mental model
- Order lifecycle: connect → logon →
place_order→ execution reports (new → fill / partial / cancelled). - Sub-accounts:
D1,D2, … let one login trade multiple books (see example 03, cross-matching). - Market data arrives separately from order flow — via Redis (one-shot query & pub/sub) or Kafka (recommended for multi-instrument).
- Alpha framework: subclass
SignalDrivenAlpha, override a few hooks, and bar aggregation, order placement, fill tracking, and state persistence are handled for you.
Configuration, Connection, Order Management, Account Queries, Events, Market Data, Alpha Framework, and CLI — every method and option.
Step 4 Learn from examples
Work through these in order — each builds on the last. Every file is runnable and downloadable.
Paper basics (01–08)
Alpha framework (09–15) v0.2.7
Full example tutorials with explanations & expected output
Step 5 Get & use an account
Accounts on QL-Dash are provisioned by an admin — there is no self-service signup. The admin sets up your login, your FIX trading account, and one or more sub-accounts, then hands you the credentials. Your job is to drop those credentials into the client and start trading.
The account model
- Trader account (login) — your main identity and QL-Dash dashboard login (e.g. the name shown after Hello, in the top bar).
- Environment — an isolated context such as
REALorPLAYBACK. One trader account can have several; each has its own configuration for testing vs production. - FIX account — the login gateway: the
username/passwordand connection details your Python client authenticates with over FIX. - Sub-account — an independent trading book under a FIX account (e.g.
main,D1,D2), each linked to one environment with its own balance, portfolio, and transactions. Cross-matching examples useD1vsD2. - Alias — an optional friendly display name for an account/sub-account on the dashboard and arena leaderboard.
How they fit together
The admin sets up this whole hierarchy for you — you just receive the credentials at the FIX-account / sub-account level.
What the admin gives you
When your account is ready you'll receive everything needed to connect:
usernameandpassword(your FIX account)sender_comp_id(your FIX session ID) —target_comp_idis alwaysSERVER- Host & port for the FIX gateway and the REST base URL
- Your sub-account id(s) and their starting balance
Fast path: copy your .env from the dashboard
You don't have to wire the values by hand. On the QL-Dash home page, every paper account row has a
.env
button in the Actions column (between the
rename and the PDF buttons). Click it and a
ready-to-use .env — already using the SDK v0.2.7 variable names — is copied to your clipboard.
It pre-fills everything that's known for that account; only the password is left for you to paste in:
# PaperBroker .env — sub account: main
# PAPER_PASSWORD is not filled in: contact the admin for this account's FIX password.
PAPER_ACCOUNT_ID=main # your sub-account id
PAPER_USERNAME=fix_yourname # your FIX account username
PAPER_PASSWORD=<contact_admin> # ← paste the password the admin gave you
SENDER_COMP_ID=<your FIX account id> # your FIX session id
PAPERBROKER_ENV_ID=main # environment id
PAPER_REST_BASE_URL=https://papertrade.algotrade.vn/accounting
SOCKET_HOST=papertrade.algotrade.vn
SOCKET_PORT=5001
TARGET_COMP_ID=SERVER
1. Click .env on your account row → it's copied.
2. Paste into your project's .env file.
3. Replace <contact_admin> in PAPER_PASSWORD with the password from your admin.
Need the market-data values too (MARKET_REDIS_*, PAPERBROKER_KAFKA_*)? Those aren't in the
copied .env — see Step 6 below.
Map credentials into the client
Prefer to wire it by hand? Each value the admin gives you maps to one PaperBrokerClient(...) argument from Step 3:
| What the admin gives you | PaperBrokerClient argument |
|---|---|
| FIX account username | username |
| FIX account password | password |
| FIX session ID | sender_comp_id |
| (fixed) | target_comp_id="SERVER" |
Sub-account id (e.g. D1) | default_sub_account |
| Gateway host / port | socket_connect_host / socket_connect_port |
| REST base URL | rest_base_url |
View your account state
Once connected you can read your portfolio, open orders, and transactions both on the QL-Dash dashboard and from code — see example 06 (account_state.py) for portfolio + orders + transactions in one call.
Contact an admin to get a login, a FIX account, and your sub-account(s) provisioned.
Step 6 Get the market data feed
The live market data feed (Redis / Kafka) isn't public — access is granted per user. This is the last step before you can run a strategy against real data.
What the feed provides
- Redis — one-shot quote queries and a pub/sub stream of live prices (examples 04 & 05).
- Kafka — a real-time market-data stream, recommended when you subscribe to many instruments at once (example 08).
The feed is independent of the FIX order flow: orders go out over FIX, while prices come in over Redis/Kafka. Your alpha and any market-data example read from it.
What you'll receive
Endpoints and credentials are environment-specific and not public. The admin will give you the
host, port, and password for the data Redis (and Kafka brokers if you use Kafka).
Plug it into your code
Pass the values you receive into the market-data helpers — the same parameters used in examples 04 / 05 / 08 and by the alpha framework:
from paperbroker.market_data import RedisMarketDataClient, QuoteSnapshot
# host / port / password come from the admin
client = RedisMarketDataClient(
host="REDIS_HOST_FROM_ADMIN",
port=6380,
password="REDIS_PASSWORD_FROM_ADMIN",
)
# one-shot query (example 04)
quote = await client.query("HNXDS:VN30F2606")
# pub/sub stream (example 05)
async def on_quote(instrument: str, quote: QuoteSnapshot):
print(instrument, quote)
await client.subscribe("HNXDS:VN30F2606", on_quote)Contact an admin to be granted access to the market data feed. (Internal — your team knows who to reach.)