Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions openspec/changes/adaptive-market-engine/.openspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-08-17
119 changes: 119 additions & 0 deletions openspec/changes/adaptive-market-engine/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
## Context

EclipseTrader already has a feed seam (`StreamingConnector` / `FeedSubscription` with `setTrade`, `setBook`, `setQuote`, `setTodayOHL`) that the JESSX `BrokerConnector` currently populates by parsing XML messages. The new engine only needs to produce the same kinds of events; it does not need to reuse JESSX's server, sockets, or bots.

## Goals / Non-Goals

**Goals:**

- A self-contained, in-process market engine with a full-depth matcher, an adaptive price process, and a simulated daily clock.
- Deterministic, replayable runs via a seeded random generator.
- Emit trade/depth/quote events into the existing feed seam so charts and Level2 work unchanged.

**Non-Goals:**

- FIX or other external protocol connectivity.
- Retiring or modifying the existing `org.eclipsetrader.jessx` bundle in this change.
- Game scoring, portfolio valuation, or player UI — those build on top of the engine and are tracked separately.

## Decisions

### 1. Clean-room reimplementation, not a port of webcurvesim

Reimplement the matching and market-making ideas from webcurvesim from scratch under EPL.

- **Rationale**: webcurvesim's license carries a commercial-registration clause and bundles LGPL 2.1, which is awkward for an EPL project; its code is Java 5/6 style (`Vector`, `Hashtable`, slf4j 1.5). The matching logic is well understood and small enough to rewrite cleanly with modern generics and immutable events.
- **Alternative considered**: vendor webcurvesim's `exchange`/`common` classes. Rejected for licensing and modernization reasons.

### 2. In-process engine, no sockets

Run the engine in the same JVM as EclipseTrader and deliver events via listener callbacks.

- **Rationale**: the game is single-player in one process; JESSX's `NetworkCore`/XML-over-socket machinery exists only for its multi-machine lab origin and adds latency and complexity for no benefit.
- **Alternative considered**: keep a socket boundary between engine and UI. Rejected as unnecessary.

### 3. New bundle `org.eclipsetrader.market.sim`

Put the engine in its own OSGi bundle rather than modifying `org.eclipsetrader.jessx`.

- **Rationale**: keeps JESSX intact as a fallback while the new engine proves out; isolates the new code with its own lifecycle and tests.
- **Alternative considered**: replace JESSX's `OrderMarket`/`Order` in place. Rejected — higher blast radius and loses the working JESSX path.

### 4. Price model: fundamental value with emergent trend

Maintain per-asset `Fₜ = Fₜ₋₁ + driftₜ + newsₜ + noiseₜ`, where `driftₜ` is estimated by agents from recent trades (momentum) rather than set as a fixed parameter, and `newsₜ` is a procedural shock proportional to sentiment × magnitude.

- **Rationale**: the emergent drift makes "the market adapts to news and trend" a genuine mechanism rather than a scripted curve; the market maker anchors liquidity around `Fₜ`.
- **Alternative considered**: scripted per-scenario drift. Rejected — user wants emergent, not scripted.

### 5. Agents: market maker + informed traders

A market maker quotes `bid/ask = Fₜ ± spread/2` (spread widened by volatility/trading volume), and informed agents tilt quotes toward incoming news and the estimated trend.

- **Rationale**: the market maker guarantees continuous trades (fixing the "few deals" problem); informed agents create the news/trend reaction the player observes.
- **Alternative considered**: reuse JESSX's `Discreet`/`NotDiscreet` bots on the new engine. Rejected — their spread-only quoting produces no trades.

### 6. Simulated daily calendar

Each period is a trading day on a simulated clock; trades are timestamped from that clock. The day's real-time length and the start date are engine parameters.

- **Rationale**: gives charts meaningful day boundaries and removes the elapsed-vs-epoch ambiguity the JESSX path had (`fix-jessx-deal-timestamps`).
- **Alternative considered**: wall-clock time with fast-forward. Rejected — no clean day boundaries.

### 7. Determinism

All randomness (news timing, sentiment, magnitudes, agent noise) comes from a single seeded PRNG so a scenario can be replayed identically.

- **Rationale**: essential for testing the engine and for reproducible game scenarios.
- **Alternative considered**: unseeded `Math.random()`. Rejected — not replayable.

### 8. Feed integration

Engine listeners push `Trade`/`Book`/`Quote`/`TodayOHL` into the existing `FeedSubscription` setter path (the same seam `BrokerConnector.objectReceived` uses), marshaling onto the SWT display thread for UI-safe delivery.

- **Rationale**: no new feed plumbing; charts and Level2 behave as they do today.
- **Alternative considered**: a new `IFeedConnector`. Rejected — heavier, and the subscription seam already exists.

### 9. Full order-type set with contingent (stop) orders

Support LIMIT, MARKET, and the modern set: STOP, STOP-LIMIT, TRAILING-STOP, IOC, FOK, ICEBERG, and PEGGED.

- **Contingent orders** (stop, stop-limit, trailing-stop) rest in a separate off-book trigger queue; a trigger monitor checks each trade/quote against every active trigger and submits the resulting market/limit order when crossed. Trailing stops update their trigger from the best observed price.
- **Time-in-force modifiers** (IOC, FOK) are handled by the matching loop: IOC cancels the unfilled remainder after the sweep; FOK verifies the full quantity is available before executing (no partial fill).
- **Iceberg** keeps a hidden total and a separate display quantity that replenishes as the visible portion fills.
- **Pegged** orders are re-priced on every book change relative to a reference (best bid/ask/mid) plus an offset.

- **Rationale**: a trading game needs risk-management (stop/trailing) and execution-control (IOC/FOK) tools; iceberg and pegged add realistic depth without changing the core matcher.
- **Alternative considered**: limit + market only. Rejected — the game should expose the modern order types a real trading platform offers.

### 10. Signed positions with short selling

Model positions as a signed quantity per participant per asset (positive = long, negative = short). Buys open or increase long positions (and cover shorts first); sells reduce long positions (and open shorts beyond that) up to a configured per-participant borrow limit. Borrow availability is simplified to a numeric limit rather than a locate/borrow-cost model.

- **Rationale**: short selling is a core expectation of a realistic trading game and pairs with the stop/trailing risk tools; a numeric limit keeps the engine simple.
- **Alternative considered**: forbid shorts (JESSX's current behavior, where asks are rejected without sufficient holdings). Rejected — the game should let players go short.

### 11. Leverage via cash loans with margin enforcement

Track each participant's cash balance as signed (negative = borrowed). Buying power = cash × a configurable leverage multiplier; positions require posting margin (a fraction of notional). Equity = cash + long market value − short market value. When equity drops below a maintenance-margin threshold, the engine force-liquidates positions (submits market orders to close) until equity is restored. Borrowed cash accrues interest per period. This composes with short selling: shorts borrow the asset (bounded by the borrow limit in `market-positions`), while leverage borrows cash (bounded by the margin/equity model).

- **Rationale**: leverage is the "high risk" half of high-risk trading — the margin-call/liquidation loop is the payoff. A numeric equity/threshold model keeps it deterministic and testable.
- **Alternative considered**: unlimited leverage with no margin enforcement. Rejected — no risk, no game.

## Risks / Trade-offs

- [Risk] Forced liquidation feedback loop — liquidation sells move the price, which can trigger further margin calls. → Mitigation: evaluate and liquidate in a single pass per price update, and treat cascades as an intended, gameable dynamic rather than a bug.

- [Risk] Momentum can run away into an unrealistic bubble/crash → Mitigation: cap drift magnitude and add a mean-reversion term toward a long-run anchor.
- [Risk] Engine thread and SWT UI thread synchronization (listener callbacks touching the display) → Mitigation: marshal all feed updates through `Display.asyncExec`, matching the existing connector pattern.
- [Risk] Overly aggressive informed agents flatten the spread and stop the market maker → Mitigation: size agent orders as fractions of displayed liquidity and let the market maker dominate quoting.
- [Risk] Determinism is broken by wall-clock dependencies (e.g., `System.currentTimeMillis()` in timestamps) → Mitigation: the simulated clock is the only time source inside the engine.

## Migration Plan

- Additive: a new bundle plus a small wiring point. JESSX remains selectable, so rollback is reverting the wiring change or not enabling the new engine.
- No data migration; the engine is stateless across restarts except for the seed and any scenario parameters.

## Open Questions

- Real-time duration of a simulated day (e.g., 60s vs 300s) — a tunable parameter, does not change specs or task breakdown.
34 changes: 34 additions & 0 deletions openspec/changes/adaptive-market-engine/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## Why

EclipseTrader's bundled JESSX simulator produces a thin, unrealistic market: its matcher only ever matches a single best order, its bots place non-crossing orders inside the spread, and its news is a static scenario script. The result is very few trades, no price trend, and a market that never adapts to events — which defeats the purpose of a trading game in which the player adapts to a real, news-reactive market.

## What Changes

- Add a new OSGi bundle (`org.eclipsetrader.market.sim`) containing a clean-room, full-depth limit-order-book matching engine (inspired by webcurvesim's core, written under EPL).
- Add a procedural market layer: a fundamental-value process with an emergent trend regime, procedurally generated news (sentiment + magnitude), and adaptive agents (a market maker plus informed traders) that quote and trade continuously.
- Add a simulated daily market calendar: each period is a trading day; orders and trades carry simulated daily timestamps that flow into the existing EclipseTrader feed seam so charts show meaningful day boundaries.
- Support signed positions and short selling: participants may sell assets they do not hold up to a borrow limit, and buys cover existing shorts.
- Support leveraged trading via cash loans: buying power beyond available cash, margin requirements, and forced liquidation when equity falls below the maintenance margin.
- Integrate the engine with the existing feed pipeline (`StreamingConnector`/`FeedSubscription`) so trades, depth, and quotes render in charts and the Level2 view.
- Leave the existing JESSX bundle intact in this change.

## Capabilities

### New Capabilities

- `market-matching`: the limit-order-book matching semantics — limit, market, stop, stop-limit, trailing-stop, IOC, FOK, iceberg, and pegged orders; price-time priority; full-depth execution; amend/cancel; VWAP; and price steps.
- `market-adaptation`: the adaptive market behavior — procedural news, the emergent trend regime, and the market-maker/informed agents that turn news and trend into price movement.
- `market-calendar`: the simulated daily calendar — daily periods, simulated timestamps, and how they map onto the feed.
- `market-positions`: position tracking and short selling — signed positions, short limits, and buy-to-cover semantics.
- `market-leverage`: leveraged trading — cash loans, buying power, margin requirements, and forced liquidation.

### Modified Capabilities

None.

## Impact

- **Code**: new bundle `org.eclipsetrader.market.sim` (matching engine, agents, news generator, calendar). Wiring into the existing feed seam that `StreamingConnector`/`FeedSubscription` already expose.
- **Behavior**: charts and Level2 show continuous, trending, news-reactive simulated market data with daily timestamps; player orders interact with the same book.
- **Dependencies**: none new (plain Java plus existing EclipseTrader feed APIs).
- **Related work**: supersedes the sparse-trade behavior of JESSX and the `fix-jessx-deal-timestamps` elapsed/epoch workaround (the new engine controls its own clock).
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
## Purpose

Defines the adaptive market behavior: how a fundamental value, procedurally generated news, an emergent trend, and trading agents combine so the simulated market produces continuous, news-reactive, trending prices.

## ADDED Requirements

### Requirement: Fundamental value drives quoting

The market SHALL maintain a fundamental value for each asset, and quoted prices SHALL be derived from that value rather than from disconnected random levels.

#### Scenario: Quotes track the fundamental value

- **WHEN** the fundamental value of an asset changes
- **THEN** subsequent quotes and trades move toward the new value

### Requirement: Procedurally generated news

News events SHALL be generated procedurally during the simulation, each carrying an asset, a sentiment (positive or negative), and a magnitude.

#### Scenario: News event is generated

- **WHEN** the simulation is running
- **THEN** news events appear over time with an associated asset, sentiment, and magnitude

### Requirement: News reprices the market

A news event SHALL shift the fundamental value of its asset by an amount proportional to its sentiment and magnitude, so the market visibly reacts.

#### Scenario: Positive news moves the price up

- **WHEN** a positive news event for an asset is generated
- **THEN** the asset's fundamental value increases and its traded price trends upward

#### Scenario: Negative news moves the price down

- **WHEN** a negative news event for an asset is generated
- **THEN** the asset's fundamental value decreases and its traded price trends downward

### Requirement: Emergent trend

The market SHALL exhibit a price trend that emerges from agent behavior (momentum and news reaction) rather than being a constant offset, and that trend SHALL be able to persist and later reverse.

#### Scenario: Trend persists across many trades

- **WHEN** the market enters an upward trend
- **THEN** prices remain elevated over many successive trades rather than reverting immediately

#### Scenario: News reverses the trend

- **WHEN** a sufficiently strong opposite news event arrives during a trend
- **THEN** the market can reverse direction

### Requirement: Continuous market-making liquidity

A market maker SHALL continuously quote both a bid and an ask around the current value so that trades occur without the player having to initiate every transaction.

#### Scenario: Trades occur without player action

- **WHEN** the simulation is running and no external order is present
- **THEN** trades still occur between market participants at a regular rate

### Requirement: Informed agents adapt to news and trend

Informed trading agents SHALL adjust their quoted prices in the direction of incoming news and the current trend, amplifying the market's reaction.

#### Scenario: Agents lean with the trend

- **WHEN** the market is trending upward
- **THEN** informed agents quote higher prices than they would in a flat market
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
## Purpose

Defines the simulated daily calendar: each period is a trading day, and trades carry simulated daily timestamps that reach the platform's feed so charts show meaningful day boundaries.

## ADDED Requirements

### Requirement: Daily trading periods

The simulation SHALL advance in daily periods, each representing one trading day with an open and a close.

#### Scenario: Periods advance by day

- **WHEN** a trading period ends
- **THEN** the next period begins as a new trading day

### Requirement: Simulated timestamps

Trades and quotes generated by the simulation SHALL carry timestamps derived from the simulated daily clock, not from epoch millis or wall-clock arrival time.

#### Scenario: Trade carries a simulated day and time

- **WHEN** a trade occurs during a trading day
- **THEN** it is timestamped with that day's date and an intraday time

#### Scenario: No epoch dates

- **WHEN** simulated market data is displayed or persisted
- **THEN** no date from the simulation resolves to the 1970 epoch

### Requirement: Timestamps flow to the feed

The simulated timestamps SHALL propagate into the platform's feed so that charts and history display the simulated dates.

#### Scenario: Chart shows day boundaries

- **WHEN** trades occur across multiple simulated days
- **THEN** charts show those trades under distinct simulated days rather than a single wall-clock session

### Requirement: Day boundary reset

At the end of a trading day the market SHALL close (stop accepting orders) and reopen for the next day, carrying forward any relevant state defined for the game.

#### Scenario: Market closes and reopens

- **WHEN** the end of a trading day is reached
- **THEN** order entry pauses, the day advances, and the market reopens for the next day
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
## Purpose

Defines how participants borrow capital to trade with leverage and how margin is enforced, including buying power, margin requirements, and forced liquidation.

## ADDED Requirements

### Requirement: Leveraged buying power

A participant's maximum position size SHALL exceed their available cash by a configurable leverage multiplier.

#### Scenario: Buying beyond cash

- **WHEN** a participant has cash for one unit but a configured leverage multiplier of four
- **THEN** they may hold a position worth up to four times their cash

### Requirement: Borrowed capital

Buying beyond available cash SHALL be financed by a loan, leaving the participant's cash balance negative to represent the borrowed amount.

#### Scenario: Cash goes negative on a leveraged buy

- **WHEN** a leveraged buy exceeds available cash
- **THEN** the participant's cash balance becomes negative by the borrowed amount

### Requirement: Margin requirement

Opening a leveraged position SHALL require the participant to post margin equal to a configured fraction of the position's value.

#### Scenario: Insufficient margin rejects the order

- **WHEN** a position would require more margin than the participant can post
- **THEN** the order is rejected

### Requirement: Equity determines margin availability

A participant's equity SHALL be their cash plus the market value of long positions minus the market value of short positions, and it SHALL determine how much margin remains available.

#### Scenario: Equity falls as prices move against a position

- **WHEN** the price of a leveraged long position falls
- **THEN** the participant's equity falls by the same amount

### Requirement: Margin call and forced liquidation

When a participant's equity falls below a configured maintenance margin, their positions SHALL be force-liquidated until equity is restored.

#### Scenario: Forced liquidation on a margin call

- **WHEN** a leveraged position's loss drives equity below the maintenance margin
- **THEN** the engine force-closes positions to restore equity, realizing the loss

### Requirement: Loan interest

Borrowed cash SHALL accrue interest at a configured rate on a per-period basis.

#### Scenario: Interest accrues on a loan

- **WHEN** a participant holds a cash loan across a period boundary
- **THEN** interest is added to the borrowed amount
Loading
Loading