FAQ & Common Mistakes¶
Common questions and gotchas when starting with EduMatcher. For the first checks to make when something does not work at all, start with When Something Does Not Work. Questions that come up later — order types, saved orders, seed quotes, the scheduler, configuration files — are listed at the end under More questions.
Architecture & Setup¶
The engine started but gateways can't connect. What's wrong?¶
Check that:
1. The engine is running before the gateways try to connect.
2. ZMQ ports 5555 and 5556 are not blocked by a firewall or in use by another
process: lsof -i :5555 -i :5556
3. The participant ID exists in the deployed configuration (pm-config-show
lists them).
4. On the container route, the console runs inside the exchange
(./edumatcher.sh shell). The message bus is not published to your own
machine unless you set EM_ZMQ=1.
If the engine is down or unreachable, pm-alf-console exits after about three
seconds with Gateway authentication timed out.
The book already has liquidity before I submit any orders. Why?¶
If your engine_config.yaml has a market_maker_quotes block for the symbol,
the engine injects a two-sided seed quote on behalf of the configured market-maker
gateway at startup. This is intentional — it gives the book immediate liquidity
so participants always have something to trade against.
A side effect is that your first aggressive order may fill immediately against the seed quote, before a second participant has typed anything.
To run with a deliberately empty book (e.g. for a demo of price discovery from
scratch), either:
- deploy one of the -nomm example configurations, such as s3-basic-nomm
(pm-setup --config s3-basic-nomm --force, or
./edumatcher.sh config s3-basic-nomm on the container route), or
- generate your own with pm-config-gen --no-mm-seed-quotes.
On the container route, also note that the default mm-demo process profile
runs a market-maker bot that quotes every symbol; set EM_PROFILE=default in
.env to run without it.
Why is the engine not matching orders? It starts in CLOSED state¶
With sessions_enabled: true (the default), the engine starts in CLOSED state
and waits for pm-scheduler to drive it through the session phases before it
accepts orders.
Three ways to get to a matching state quickly:
- Start
pm-scheduleralongside the engine — it drives the transition from CLOSED → PRE_OPEN → OPENING_AUCTION → CONTINUOUS automatically at the configured wall-clock times, or immediately withpm-scheduler --now. - Move the phase by hand from the operator console:
pm-admin --id OPS01, thenSESSION|STATE=PRE_OPENandSESSION|STATE=CONTINUOUS. See the Quick Start's Run a Trading Session. - Disable sessions for testing or demos:
With
sessions_enabled: falsethe engine enters CONTINUOUS state immediately on startup andpm-schedulerhas no effect.
Why is my viewer empty even though it started correctly?¶
An empty viewer usually means the symbol has no resting orders yet. pm-viewer
subscribes to book.<SYMBOL> and then requests an initial snapshot, but if the
book is empty the snapshot is also empty.
Fix: Submit a resting order, enable market-maker quote seeds for that symbol, or verify that you are watching a configured symbol.
Do I really need a terminal window for every process?¶
No. Each process is an independent program communicating over ZeroMQ, and each
blocks on its own event loop, so started by hand each needs its own terminal.
But you rarely need to start them by hand: pm-opctl-cli start starts a whole
named profile of processes in the background, in the right order, and
pm-opctl-cli list shows their health. The container does exactly this for
you. Open terminals only for what you interact with: trader consoles, the
operator console and viewers. tmux or screen help when you do want many
side by side.
The source checkout also has tools/launch_all.sh, which opens every process
in its own macOS Terminal window. It uses osascript, so it only works on
macOS.
How do I reset all persisted state to start fresh?¶
Stop the exchange, then let pm-opctl-cli clear the data directory:
pm-opctl-cli stop
pm-opctl-cli clear --state # order books, GTC orders, stats, index and clearing state
pm-opctl-cli clear --all # ...and the logs and audit files as well
The deployed configuration under ref_data/ is never touched. On the
container route, run the same commands with ./edumatcher.sh shell while the
exchange is running, then ./edumatcher.sh restart.
After a reset the engine re-seeds market-maker quotes as if it were day 1
(because book_stats.json no longer exists).
Orders, Positions & Sessions¶
Why is my LIMIT order filling at a different price than I specified?¶
Your limit price is the worst price you'll accept — not necessarily the price you'll get. If better prices are available, you fill at those better prices.
Example: You submit BUY LIMIT QTY=100 PRICE=151.00 but the book has asks
at 149.90 and 150.20. You fill at 149.90 and 150.20 (both better than your
limit of 151.00), not at 151.00.
This is called price improvement — you get a better deal than the worst price you were willing to accept.
I submitted a MARKET order but it only partially filled. Where did the rest go?¶
A MARKET order sweeps the book until it runs out of liquidity. If the book does not have enough resting orders to fill the entire quantity, the unfilled remainder is discarded (not rested). You will receive one or more partial fill events, then a cancellation for the remaining quantity.
Fix: If you need guaranteed full execution at whatever price, use FOK and ensure liquidity is available. If you're comfortable with partial fills, MARKET is correct — just understand the remainder is discarded, not queued.
Why is my unrealized P&L different from my realized P&L?¶
They measure different things:
- Unrealized P&L: paper gain/loss on shares you still hold, calculated as
(current_price - avg_cost) × position. This changes every time a trade prints and updates the last trade price. - Realized P&L: profit/loss locked in when you closed part of your position (sold shares you were long, or bought back a short). This never changes after the trade.
Until you close your position, your gains are "on paper" — the market can take them back. Once realized, they are yours regardless of subsequent price moves.
Why do I have a negative position?¶
A negative position means you are short — you have sold shares you did not own. This can happen if you submit a SELL order without first having bought any shares. In a real brokerage this requires a margin account and stock borrowing; EduMatcher does not enforce these constraints, so short positions are allowed freely.
What happens to my open orders when the market closes?¶
It depends on their time-in-force:
| TIF | At market close |
|---|---|
DAY |
Expired and removed. You receive order.expired events. |
GTC |
Saved to data/gtc_orders.json and reloaded on next engine start. |
ATO |
Expired when OPENING_AUCTION phase ends. |
ATC |
Expired when CLOSING_AUCTION phase ends. |
Installation¶
Do I need Poetry or pipx?¶
It depends on your role:
| Role | Tool | Install command |
|---|---|---|
| Student / instructor (just want to run the exchange) | pipx | pipx install edumatcher |
| Developer (modifying the engine, running tests) | Poetry | poetry install --with dev |
End users should not need a source checkout at all. After pipx install edumatcher
all pm-* commands are on your PATH and ready to use.
I installed with pipx but pm-engine is not found. What do I do?¶
pipx installs scripts to ~/.local/bin, which may not be on your PATH yet.
pipx ensurepath # adds ~/.local/bin to your shell profile
exec $SHELL # reload your current shell
which pm-engine # should now print a path
If you just installed pipx itself for the first time, open a new terminal
window after running pipx ensurepath — the PATH change only takes effect
in new shells.
What does pm-setup do and do I have to run it?¶
pm-setup is a one-time bootstrap command for installed (pipx) users:
- Creates the data directory (
~/.local/share/edumatcherby default). - Compiles a bundled example configuration —
s10-basicunless you choose another with--config— and deploys it to<data directory>/ref_data/engine_config.json, keeping the YAML source beside it. - Prints the
export EDUMATCHER_DATA_DIR=...line to add to~/.zshrcor~/.bashrc.
Without a deployed configuration no process will start, so you need either
pm-setup or your own pm-config-deploy. An already-deployed configuration
is kept unless you pass --force. The container installation runs the
equivalent for you.
How do I upgrade EduMatcher to a newer version?¶
pipx upgrade edumatcher # Python package, from PyPI
./edumatcher.sh update # container installation, latest release
Your configuration and data files are not touched by the upgrade. If the new
version changes the configuration format, redeploy your configuration with
pm-config-deploy (or, for a bundled example, pm-setup --config <name>
--force). Do not run pm-setup --force otherwise: it replaces the deployed
configuration with a bundled example.
More questions¶
These questions are less common on a first day, so each lives in the book that covers its topic. Nothing has been dropped.
| Question | Where |
|---|---|
| Why was my IOC order rejected? | Participant Guide |
| Why didn't my STOP order trigger? | Participant Guide |
| Why did my STOP_LIMIT order trigger but not fill? | Participant Guide |
| My FOK order was rejected even though there was liquidity. Why? | Participant Guide |
| Why does my average cost change when I buy more shares? | Participant Guide |
| My GTC orders didn't reload after restart. Why? | Participant Guide |
| What is SMP and why did my order get cancelled? | Participant Guide |
| Why does the engine use integer ticks instead of float prices? | Participant Guide |
| Why did the seed MM quote disappear after the second day? | Operator's Guide |
Why did pm-scheduler exit immediately? |
Operator's Guide |
| How do I see what messages are flowing between processes? | Operator's Guide |
| Why is my order rejected with "Gateway not authenticated"? | Operator's Guide |
| The engine says it cannot find its configuration. Where should the file be? | Operator's Guide |
| Where are my data files stored? | Operator's Guide |