A professional-grade market microstructure simulation game built with React + Vite. Play as a "Composite Man" — the institutional-grade market operator who accumulates, manipulates, and distributes across five real financial instruments using Wyckoff methodology and smart-money concepts.
The Operator Terminal puts you in the seat of a large-scale market participant managing a $1,000,000 simulated capital account. Unlike retail trading simulators, this game models the supply side of the market — the entity that creates liquidity traps, sweeps stop-loss clusters, and engineers sentiment to enter and exit positions invisibly.
All market mechanics are based on real institutional practices:
- Wyckoff Accumulation/Distribution cycle phases
- Smart Money Concepts (fair value gaps, order blocks, liquidity sweeps)
- Market Microstructure (order book depth, dark pool prints, TWAP/iceberg execution)
- Multi-timeframe analysis tied to a single coherent price source
All five instruments share a single-source-of-truth price model:
genBaseTicks(instrument, 600) ← 600 × 30-minute base ticks
↓
masterTicksRef[instrument] ← ref-stored, persists across TF switches
↓
aggregateTicks(baseTicks, tpc) ← derived candles for any timeframe
↓
30m / 3H / Daily / Weekly ← all consistent with each other
Why this matters: Switching timeframes never resets or regenerates price. A Daily candle is always the exact aggregation of 48 × 30-minute ticks. HTF support/resistance levels are calculated from actual price data, not random numbers.
Every 300ms a new base tick arrives:
- 95% of the time the current tick is updated in place (close, high, low, volume accumulate)
- 5% chance a new tick is opened (simulates candle close)
- Gentle mean-reversion keeps price near the instrument's base, preventing runaway drift
- All timeframe views update automatically via
useMemo
P&L is calculated correctly using each instrument's contractSize:
| Instrument | Contract Size | Example: 10 lots × 1 pip move |
|---|---|---|
| BTC/USD | 1 | 10 × 1 × $1 pip = $10 |
| EUR/USD | 100,000 | 10 × 100,000 × $0.0001 = $100 |
| XAU/USD | 100 | 10 × 100 × $0.10 = $100 |
| CL/USD | 1,000 | 10 × 1,000 × $0.01 = $100 |
| US T-Bond | 1,000 | 10 × 1,000 × $0.01 = $100 |
Formula:
P&L = (exitPrice - entryPrice) × direction × lots × contractSize
Margin Required = (price × lots × contractSize) / leverage
- Node.js ≥ 18
- npm ≥ 9
git clone https://github.com/your-username/stock-market-operator-game.git
cd stock-market-operator-game
npm install
npm run devOpen http://localhost:5173.
├── index.html
├── vite.config.js
├── package.json
├── src/
│ └── main.jsx # React entry point
└── operator_sim_v12.jsx # Main component (self-contained)
The entire simulator lives in a single file (operator_sim_v12.jsx). src/main.jsx is the thin Vite entry point:
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import OperatorSim from "../operator_sim_v12.jsx";
createRoot(document.getElementById("root")).render(
<StrictMode><OperatorSim /></StrictMode>
);- Starting capital: $1,000,000
- Margin call: triggered at 20% of starting capital ($200,000 equity)
- Max drawdown guard: configurable (default 30%) — auto-flattens all positions
| Symbol | Name | Base Price | Volatility | Contract |
|---|---|---|---|---|
| BTC | BTC/USD | $67,500 | High | 1 BTC |
| EURUSD | EUR/USD | 1.0842 | Low | 100,000 |
| GOLD | XAU/USD | $2,342 | Medium | 100 oz |
| CRUDE | CL/USD | $78.42 | Medium | 1,000 bbl |
| TBOND | US T-Bond | 118.25 | Low | $1,000 |
All derived from the same base tick source:
| TF | Aggregation | Visible Candles |
|---|---|---|
| 30M | 1 tick | up to 120 |
| 3H | 6 ticks | up to 80 |
| Daily | 48 ticks | up to 60 |
| Weekly | 336 ticks | up to 30 |
| Key | Action | Notes |
|---|---|---|
B |
Buy (Long) | At market, current orderSize |
S |
Sell (Short) | At market, current orderSize |
F |
Flatten | Close all positions immediately |
I |
Iceberg Order | Hidden size execution, +stealth |
Q |
Spoof Bid | Fake large bid, lures buyers |
W |
Spoof Ask | Fake large ask, lures sellers |
H |
Stop Hunt High | Sweeps buy stops above recent high |
L |
Stop Hunt Low | Flushes sell stops below recent low |
U |
Upthrust | Fake breakout above resistance |
P |
Spring | Fake breakdown below support |
A |
Absorb Supply | Invisible accumulation |
M |
Toggle Sound | Web Audio API engine |
J |
Toggle Journal | Trade notes panel |
D |
Draw Line | Chart annotation tool |
R |
Draw Rectangle | Chart zone marking |
Esc |
Cancel Draw | Exit drawing mode |
- Candlestick chart with zoom (scroll wheel) and pan (click-drag)
- Volume bars below price
- Crosshair with OHLCV tooltip on hover
- HTF levels (Daily/Weekly support & resistance derived from actual data)
- Toggleable overlays:
- Fair Value Gaps (bullish = green, bearish = red)
- Market structure labels (HH, HL, LH, LL)
- Liquidity heatmap
- Premium/discount zones
- Volume profile (horizontal histogram)
- Cumulative delta (buy vs sell pressure)
- Anchored VWAP
- TPO/Market profile
- Footprint candles
- Drawing tools: trend lines and rectangles
- 12 bid/ask levels, live-updating every 300ms
- Bid/ask depth visualization bars
- Spread display
| Action | Stealth Cost | Score | Effect |
|---|---|---|---|
| Buy/Sell | −8% | +50 on close | Directional position |
| Iceberg | −3% | +25 | Hidden accumulation |
| Spoof Bid | −5% | +30 | Lures buyers, then cancel |
| Spoof Ask | −5% | +30 | Pressures sellers |
| Stop Hunt High | −15% | +80 | Sweeps recent high |
| Stop Hunt Low | −15% | +80 | Sweeps recent low |
| Upthrust | −20% | +120 | 3-candle fake breakout |
| Spring | −20% | +120 | 3-candle fake breakdown |
| Absorption | −3% | +40 | Invisible supply absorption |
- Starts at 100%, decays with each action
- Passively regenerates +0.08% per tick
- Below 25% triggers a stealth warning (sound + visual)
- 5+ spoof orders trigger a regulatory flag (−30% stealth, log alert)
- Stealth affects mission completion and score multipliers
Three competing agents react to price movement in real time:
- Retail Herd (
👥) — trend-following, easily stressed, frequent stop-outs - Algo Funds (
🤖) — momentum-driven, low stress tolerance, large size - Rival Operator (
⚔️) — occasionally counter-trades your zones, front-runs liquidity
Each participant tracks:
- Net position (−500 to +500 scale)
- Stress level (0–100%)
- Average entry price
- Cumulative stop-outs
Select the current market phase to guide your actions:
| ID | Phase | Color |
|---|---|---|
| PS | Preliminary Support | 🟢 |
| SC | Selling Climax | 🔴 |
| AR | Automatic Rally | 🔵 |
| ST | Secondary Test | 🟠 |
| LPS | Last Point of Support | 🟣 |
| SOS | Sign of Strength | 🔵 |
| LPSY | Last Point of Supply | 🩷 |
| UTAD | Upthrust After Dist. | 🔴 |
Six recurring economic events (FOMC, NFP, CPI, ECB, OPEC+, Treasury Auction) with live countdown timers. When a high-impact event fires:
- Price spikes 0.3–1.5% in a random direction
- Volatility triples on the affected candle
- Sentiment shifts ±20 points
- Alert sound plays
Simulated off-exchange block trades appear with 2–8 second delays. Prints >30,000 lots trigger operator log alerts and sound cues.
| # | Name | Difficulty | Objective |
|---|---|---|---|
| 1 | Silent Accumulator | Easy | Long ≥50 lots with stealth >70% |
| 2 | Trap the Herd | Medium | Trap 500 participants total |
| 3 | Grow the Book | Hard | Capital to $1.1M |
| 4 | Ghost Operator | Hard | 200 pts with stealth >85% |
| 5 | Liquidity Raid | Medium | Trap 100+ buyers AND 100+ sellers |
| 6 | Capital Doubler | Legendary | Double capital to $2M |
| 7 | Retail Crusher | Medium | Stop out 300 retailers |
| 8 | Algo Slayer | Hard | Algo stress ≥90% while profitable |
| 9 | Spoofing Maestro | Hard | 300 trapped via spoof, no reg flag |
| 10 | Rival Dominator | Legendary | Rival stress ≥80% with stealth >60% |
| 11 | Perfect Spring | Medium | Execute Spring → reach $1.05M |
| 12 | Composite Man Arc | Legendary | Score 500 (full cycle) |
Each mission awards 1–3 ⭐ stars and +500 pts × stars on completion.
Positions held incur periodic costs (charged every 30 seconds of sim time):
- BTC: Funding rate × notional × direction (longs pay when rate > 0)
- FX/Commodities: Fixed per-lot swap (EURUSD: −$0.80/lot long, +$0.40/lot short)
- T-Bond: Positive carry long (+$1.80/lot) — reflects bond coupon
The following logical errors were corrected:
Before: A price gap upward was labelled bull: false and rendered red.
After: Gap up → bull: true (green), gap down → bull: false (red). Standard SMC convention.
// Fixed in detectFVG()
if (b.low > a.high && b.low > c.high)
fvgs.push({ ..., bull: true }); // ← was false
if (b.high < a.low && b.high < c.low)
fvgs.push({ ..., bull: false }); // ← was trueBefore: P&L for all instruments was calculated as (exit - entry) × lots, which drastically underreported profits/losses on EURUSD (should be ×100,000), GOLD (×100), CRUDE (×1,000), and T-Bond (×1,000).
After: All P&L calculations now use × inst.contractSize:
flattenPosition()- BUY case close-short logic
- SELL case close-long logic
- Unrealized P&L in live tick loop
Before: Margin required = (price × lots) / leverage — massively understated for FX instruments.
After: (price × lots × contractSize) / leverage — matches real broker margin math.
Before: Crypto funding cost was price × lots × fundingRate (missing contractSize=1 for BTC, harmless but inconsistent).
After: Explicitly price × lots × contractSize × fundingRate — consistent and correct.
# Margin Required
margin = (entryPrice × lots × contractSize) / leverage
# Realized P&L (Close Long)
pnl = (exitPrice - entryPrice) × lots × contractSize
# Realized P&L (Close Short)
pnl = (entryPrice - exitPrice) × lots × contractSize
# Unrealized P&L
unrealized = (currentPrice - entryPrice) × direction × lots × contractSize
# Equity
equity = capital + unrealizedPnl
# Drawdown
drawdown% = (peakCapital - equity) / peakCapital × 100
# Margin Ratio
marginRatio = (equity / marginUsed) × 100
# Margin Call
triggered when equity ≤ $200,000 (20% of $1M starting capital)
Built on the Web Audio API (no external dependencies). Toggle with M or the 🔊 button.
| Event | Sound |
|---|---|
| Market tick (large) | 660 Hz sine, short burst |
| Market tick (small) | 440 Hz sine, quiet burst |
| Trap executed | 220 Hz sawtooth |
| Alert / macro | 880 Hz square |
| Profitable close | 523→783 Hz triangle sweep |
| Stealth warning | 110 Hz sawtooth, long decay |
| Action | Points |
|---|---|
| Profitable close | +50 |
| Iceberg | +25 |
| Spoof (bid/ask) | +30 |
| Stop hunt | +80 |
| Upthrust / Spring | +120 |
| Absorption | +40 |
| Mission complete 1⭐ | +500 |
| Mission complete 2⭐ | +1,000 |
| Mission complete 3⭐ | +1,500 |
Score is tracked on an in-game leaderboard against three static AI operators.
| Layer | Technology |
|---|---|
| UI Framework | React 18 (hooks only, no class components) |
| Build Tool | Vite 5 |
| Styling | Inline styles + global CSS-in-JSX |
| Charts | Hand-rolled SVG — no chart library |
| Audio | Web Audio API (no external deps) |
| State | useState / useRef / useMemo / useCallback |
| Font | JetBrains Mono (Google Fonts) |
No external UI libraries, no charting libraries, no Redux — intentionally lean for portability.
MIT — free to use, fork, and extend.
This is a game and educational tool. It models market microstructure concepts in a simplified, gamified way. Nothing here constitutes financial advice. Spoofing and stop hunting are illegal in real markets — this simulator exists to understand how these mechanics work, not to encourage them.