Quantitative Trading5 min read

A Signal Is Not a Decision

S

Suneet Malhotra

Sep 03, 2026

β€’
1 views
A Signal Is Not a Decision - Quantitative Trading blog post
πŸ”§PythonπŸ”§Signal ProcessingπŸ”§Risk ManagementπŸ”§Observability

A trading system can produce a valid signal and still make the correct decision by doing nothing.

That sounds obvious until the data model is inspected. Many systems record the signal, record the orders that were sent, and record the fills that arrived. The space between those records is where the strategy actually exercises judgment.

The signal is an observation about market conditions. The decision is a state transition made under constraints. Treating them as the same object makes every refusal look like a missing trade and every trade look like an inevitable consequence of the indicator.

The indicator does not know the portfolio

An indicator can say that its conditions are true. A moving-average relationship can hold. Volatility can be inside the strategy's accepted range. A regime classifier can label the session. None of those facts knows the current position, the remaining risk budget, the time until the session ends, or whether an earlier order is still unresolved.

The portfolio does.

The decision layer has to combine the new signal with state that the signal generator should not own. Is the instrument already held? Is the proposed exposure larger than the risk limit? Is the market open for this instrument? Has the same intent already been submitted? Did a previous order time out without a confirmed terminal state?

Those questions can turn the same signal into an entry, a resize, a no-op, a queued action, or a refusal. The signal did not change. The decision context did.

A missing trade is not necessarily a failure

Suppose a strategy emits ten signals and sends six orders. A report that counts only the six orders cannot distinguish the reasons for the other four. They may have been blocked by a position limit, deduplicated because the intent was already active, rejected because the market window was closed, or lost between the signal process and the order adapter.

The total is the same. The system meaning is not.

This is the same measurement problem that appears in a backtest with unrealistic fills. If the dataset contains only completed trades, it cannot explain whether the strategy had no opportunity, declined the opportunity, or failed to persist the opportunity. More analysis of the completed rows will not recover the missing branch.

The refusal needs a first-class record. I want an intent identifier, signal timestamp, decision timestamp, relevant position and risk state, selected action, refusal code when applicable, and the policy version that governed the outcome. A string such as "skipped" is not enough. It preserves the absence of an order while discarding the reason.

Decision state should be explicit

The useful state machine is small:

  1. Signal observed.
  2. Intent constructed.
  3. Decision evaluated.
  4. Action authorized or refused.
  5. Order submitted, if authorized.
  6. Broker state reconciled.
  7. Intent closed with a terminal reason.

The point is not to create ceremony around every indicator. It is to prevent a downstream status from rewriting the history of an upstream decision.

An order that was never authorized should not be labeled unfilled. An order that was authorized but never submitted should not be labeled rejected by the venue. An order submitted before a network timeout should not be treated as absent until reconciliation proves it absent.

These distinctions matter during review. A high refusal rate may indicate a useful risk boundary, a stale calendar, an overly restrictive position limit, or a broken adapter. Without the reason code, all four collapse into the same empty space in the trade log.

The signal table is not the audit trail

I used to think a signal table was enough because it gave the system a durable memory of what the strategy saw. It is only a memory of one layer. The table can answer, "What conditions were true?" It cannot answer, "What did the system decide, and why?"

That second question requires snapshots of the inputs that can change between detection and action. Position quantity, buying power, open intents, session state, risk limits, and adapter health are not commentary. They are decision inputs.

There is also a useful separation between reason and outcome. A decision can be correct even when its desired market outcome never occurs. A refusal because the risk budget was exhausted is still a valid decision. A submission that later fails at the venue is a different event. Mixing these makes the system look more active than it was and makes the risk controls impossible to evaluate.

What I would test

I would replay the same signal against several controlled states: flat portfolio, existing position, exhausted risk budget, closed session, duplicate active intent, and uncertain prior order status. The expected result is not one trade. The expected result is a set of explicit decisions.

The test should assert that every branch writes a terminal reason, that duplicate intents do not create duplicate side effects, and that an uncertain transport result triggers reconciliation before another submission. It should also assert that changing a policy version changes the recorded reason, not just the final order count.

The practical rule is simple: do not evaluate a strategy from signals and fills alone. The strategy lives in the decisions between them.

A signal tells me what the market offered the system. A decision record tells me what the system was permitted to do with that information. If the second record is missing, the trade history is not merely incomplete. It is unable to explain the strategy it claims to measure.

Share this post

You Might Also Like

Stay in the Loop

Get weekly insights on AI-driven QA, engineering leadership, and automation strategies.

No spam, ever. Unsubscribe anytime.