I Built a Trading Engine in Java — Here’s What It Took

NERV Trade Java trading engine architecture

I Built a Trading Engine in Java — Here’s What It Took

Generating a BUY or SELL signal was the easy part.

Then came multi-timeframe synchronization, partial fills, duplicate executions, order uncertainty, transaction boundaries, application crashes, restart recovery, and eventually connecting the same engine to a real exchange.

That's when a trading bot started becoming a trading engine.

This is NERV Trade.

It is a Java-based trading engine I've been building as part of the NERV ecosystem. What started as a system for analyzing market data gradually became something much more interesting: an exercise in designing a deterministic, recoverable, broker-neutral trading architecture.

And along the way, many of the hardest problems turned out to have very little to do with predicting the market.


The System We Have Today

Before going back to the beginning, let me show you where the project has reached.

At a high level, NERV Trade now looks something like this:

Market Data
    │
    ▼
M5 ──► M15 ──► H1 ──► H4
    │
    ▼
Analysis
    │
    ▼
Strategy
    │
    ▼
Risk
    │
    ▼
Trade Intent
    │
    ▼
Order Lifecycle
    │
    ├────────────────┐
    ▼                ▼
Simulator          Binance
                     │
                     ▼
               Executions
                     │
                     ▼
              Durable Lifecycle
              │      │       │
              ▼      ▼       ▼
          Position  P&L   Journal

Market data enters the system and is transformed into deterministic multi-timeframe views. Analysis produces decisions. Strategy interprets those decisions. Risk determines whether they can proceed.

From there, the problem changes.

We are no longer analyzing the market.

We are managing state.


A Signal Is Not a Trade

This distinction became increasingly important as the project evolved.

A trading strategy might say:

SELL TSLA

That looks simple.

But what does it actually mean?

Has an order been created?

Has the broker received it?

Has the broker acknowledged it?

Has anything actually executed?

Was it partially filled?

Was the remaining quantity cancelled?

Did another strategy already have exposure in the opposite direction?

Has the resulting trade actually finished?

Those are completely different states.

One of the important architectural changes in NERV Trade was therefore separating concepts that are often casually treated as the same thing:

  • analysis decision,
  • strategy decision,
  • trade intent,
  • order,
  • broker order,
  • execution or fill,
  • position,
  • and completed trade outcome.

Once these concepts became explicit, many other design decisions started to make sense.


A Position Is Not a Trade Either

This was another deceptively important realization.

Imagine two independent strategies trading the same instrument.

Strategy A owns:

+10

while Strategy B owns:

-4

The aggregate position might be:

+6

But neither strategy owns a trade of +6.

Each intent needs to own its own exposure, cost basis, realized profit and loss, orders, executions, and eventual outcome.

The aggregate position is a derived view.

That distinction became essential when we started testing reversals, partial exits, multiple orders, and independent strategies operating on the same instrument.


Then Partial Fills Arrive

Suppose NERV submits an order for 10 units.

The broker doesn't necessarily respond with one neat execution of 10.

Instead, the system might receive:

Fill #1: 3
Fill #2: 4
Fill #3: 3

Now every execution matters.

Order accounting must progress correctly.

Intent exposure must change correctly.

Cost basis must remain correct.

Realized P&L must remain correct.

And the lifecycle must know when the trade is actually finished.

This led to another important rule:

A trade lifecycle is complete only when its exposure is flat and all of its orders are terminal.

Closing the position alone isn't sufficient.


Then the Same Fill Arrives Twice

Distributed systems make another uncomfortable promise:

messages can repeat.

An execution can arrive through a live stream and later be rediscovered during reconciliation.

Or the application might crash after receiving an execution and encounter it again after restart.

If processing the same execution twice changes the position twice, a trading system has a serious problem.

So execution identity became a first-class concern.

NERV Trade now treats accepted executions as durable identities. Replaying an already accepted execution must not change:

  • filled quantity,
  • intent exposure,
  • position,
  • realized P&L,
  • lifecycle completion,
  • or journal outcome.

Idempotency isn't an optimization here.

It's part of correctness.


The Hardest Order Is the One You Don't Know Exists

Consider a more interesting failure.

NERV
  │
  │ submit order
  ▼
Broker
  │
  │ order accepted
  ▼
Network connection dies

NERV never receives the response.

What should it do?

Retrying sounds reasonable.

But perhaps the first order already exists.

A blind retry could create a second real order.

Marking the first attempt as failed isn't correct either, because we don't actually know that it failed.

The truthful state is:

uncertain.

NERV therefore preserves uncertain submissions and relies on stable order identities and broker reconciliation to determine what actually happened.

Until that uncertainty is resolved, it does not blindly resubmit the order.

This was one of those moments where modeling reality accurately mattered more than finding a convenient status value.


Transactions Became a Trading Problem

Eventually persistence entered the picture.

Processing an execution can modify several related pieces of state:

Execution
    │
    ├──► Order accounting
    │
    ├──► Intent exposure
    │
    ├──► Cost basis
    │
    ├──► Realized P&L
    │
    ├──► Lifecycle completion
    │
    └──► Journal outcome

What happens if the process crashes halfway through?

Before durable persistence, failure testing exposed exactly this kind of weakness: in-memory state could be partially mutated before an unexpected exception occurred.

That discovery influenced the persistence architecture.

When an execution is accepted now, its identity and the lifecycle state affected by that execution are committed atomically in PostgreSQL.

Either the execution is processed as a unit, or it isn't.

That sounds obvious when stated afterward.

It wasn't obvious when the project began.


And Then the Application Restarts

Persistence isn't very useful if the application cannot reconstruct what it was doing.

Restart recovery therefore became another milestone.

NERV Trade persists enough lifecycle information to recover:

  • decisions,
  • trade intents,
  • orders,
  • broker mappings,
  • accepted executions,
  • exposure and accounting state,
  • and completed outcomes.

More importantly, recovery follows a conservative rule.

If an order was in an uncertain state when the application stopped, restarting the application does not automatically submit it again.

The external broker must first be reconciled.


The Simulator Became More Than a Simulator

One of the most useful pieces of NERV Trade has been its deterministic simulator.

Originally, simulation might sound like something used primarily to test strategies.

Instead, it became our reference implementation for the entire execution pipeline.

Given the same market data, we can verify the same analysis, strategy decision, risk decision, order, execution, and resulting position.

That allowed architectural changes to happen while continuously checking that the trading pipeline still behaved deterministically.

By the time persistence and recovery were introduced, hundreds of automated tests were exercising the architecture.

But there was still an important limitation.

Everything was happening inside an environment we controlled.


So We Connected It to Binance

This changed the nature of the project again.

Instead of teaching NERV Trade about Binance internally, the goal was to keep the trading engine broker-neutral.

Binance became an adapter around the existing architecture.

                 NERV Trade

 Analysis / Strategy / Risk / Lifecycle
                    │
                    ▼
                BrokerPort
                    │
                    ▼
           Binance Adapter
                    │
                    ▼
          Binance Spot Testnet
                    │
                 fills
                    │
                    ▼
          ExecutionReportPort
                    │
                    ▼
           Durable Lifecycle

Binance owns exchange-specific concerns:

  • authentication,
  • symbols,
  • exchange filters,
  • order submission,
  • exchange order identities,
  • WebSocket communication,
  • and reconciliation inputs.

NERV continues to own:

  • analysis,
  • strategy,
  • risk,
  • trade intent,
  • order lifecycle,
  • execution processing,
  • positions,
  • P&L,
  • and trade outcomes.

That boundary matters because Binance isn't intended to be the last broker.

A trading engine shouldn't need a new lifecycle architecture every time another venue is introduced.


935 Tests Later...

At the end of the first Binance Spot Testnet adapter milestone, NERV Trade had reached 935 automated tests with zero failures, errors, or skipped tests.

The original deterministic simulator still produced the expected result after the Binance integration was introduced.

That was important.

Adding a real exchange adapter should not silently change the behavior of the core trading engine.

But 935 green tests don't prove that an exchange integration works.

They prove that the system behaves according to the assumptions encoded in those tests.

The next question is much more interesting:

What happens when those assumptions meet a real exchange?

That's where the next stage of NERV Trade begins.


This Isn't a Series About Predicting the Market

There will certainly be articles about analysis, strategies, scoring, and multi-timeframe market data.

But the deeper theme of this series is software architecture.

We'll look at questions such as:

  • How do you build deterministic multi-timeframe analysis without accidentally using future data?
  • Who should actually own a trading decision?
  • Why isn't a position the same thing as a trade?
  • How should partial fills change lifecycle state?
  • How do you make execution processing idempotent?
  • What happens when a broker receives an order but your application never receives the response?
  • Where should transaction boundaries exist in a trading system?
  • What needs to survive an application restart?
  • How do live execution events and broker reconciliation coexist without double-counting fills?
  • Can the same architecture survive integration with fundamentally different brokers?

These are the problems that turned NERV Trade into a much more interesting engineering project than I originally expected.


Back to the Beginning

NERV Trade didn't start with this architecture.

It evolved as increasingly difficult problems exposed weaknesses in the previous design. Each one—multi-timeframe analysis, execution ownership, partial fills, idempotency, transaction boundaries, restart recovery, and eventually connecting to a real exchange—forced another architectural decision.

This series documents that journey.

Not just the final architecture.

The mistakes, the tests that exposed them, the alternatives we considered, the boundaries that had to move, and the reasoning behind the system that exists today.

In the next article, we'll go back to where NERV Trade actually started—and follow the architecture forward from there.


NERV Trade is part of NERV — Next-Generation Engineering for Runtime Velocity. The project explores production-oriented architecture for trading systems using Java and the broader NERV ecosystem.

0 Comments

Post a Comment

Post a Comment (0)

Previous Post Next Post
NERV Open Source
Building production Spring Boot systems?

Explore NERV — open-source Java libraries for audit trails, persistence, exception handling, and reliable event-driven architecture.