Skip to content

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:

  1. Start pm-scheduler alongside the engine — it drives the transition from CLOSED → PRE_OPEN → OPENING_AUCTION → CONTINUOUS automatically at the configured wall-clock times, or immediately with pm-scheduler --now.
  2. Move the phase by hand from the operator console: pm-admin --id OPS01, then SESSION|STATE=PRE_OPEN and SESSION|STATE=CONTINUOUS. See the Quick Start's Run a Trading Session.
  3. Disable sessions for testing or demos:
    # engine_config.yaml
    sessions_enabled: false
    
    With sessions_enabled: false the engine enters CONTINUOUS state immediately on startup and pm-scheduler has 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:

  1. Creates the data directory (~/.local/share/edumatcher by default).
  2. Compiles a bundled example configuration — s10-basic unless you choose another with --config — and deploys it to <data directory>/ref_data/engine_config.json, keeping the YAML source beside it.
  3. Prints the export EDUMATCHER_DATA_DIR=... line to add to ~/.zshrc or ~/.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