Skip to main content

Signals

Signals are trading intentions that can be reviewed and processed into orders. They enable automation, flexibility, and clear audit trails.

Why Use Signals?

  1. Automation - Receive signals from TradingView, custom scripts, or other systems
  2. Review First - Create signals, verify them, then execute
  3. Flexible Sizing - Specify shares, percentage of equity, or dollar amounts
  4. Strategy Tracking - Link signals to strategies for performance analysis
  5. Scheduled Execution - Queue signals for future times

Signal Actions

Signals use four explicit actions that combine direction and intent:

ActionDescriptionResulting Order
openLongOpen a long positionBuy
closeLongClose a long positionSell
openShortOpen a short positionSell (short)
closeShortClose a short positionBuy (cover)
Why Not Just "Buy" and "Sell"?

"Buy" is ambiguous - it could mean opening a long OR closing a short. Signal actions make your intent explicit, reducing errors.

Creating Signals

Manual Creation

  1. Navigate to Signals in the menu
  2. Click New Signal
  3. Fill in the details:
FieldDescription
AccountWhich trading account
StrategyWhich strategy owns this signal. Defaults to your Manual strategy
SymbolTicker symbol (e.g., AAPL)
ActionopenLong, closeLong, openShort, closeShort
Quantity TypeHow to interpret the quantity
QuantityAmount based on quantity type
Limit PriceOptional - for limit orders
Stop PriceOptional - for stop orders
Source ID is optional

sourceId is a caller-supplied reference id that lets you correlate your own audit trail back to UTM signals. UTM does not use it for dedup. The manual UI omits it entirely.

  1. Click Save
  2. Signal enters "Queued" status

Quantity Types

TypeQuantity MeaningExample
FixedNumber of shares100 = buy 100 shares
Percent Equity% of account equity5 = invest 5% of equity
Dollar AmountDollar value to invest1000 = invest $1000

Percentage and dollar quantities are calculated at processing time using current prices and account equity.

Processing Signals

Converting a signal to an order:

Single Signal

  1. Find the signal in the Signals table
  2. Click Process
  3. Order is created and sent to broker
  4. Signal status changes to "Submitted"

Batch Processing

  1. Select multiple pending signals (checkboxes)
  2. Click Process Selected
  3. All signals convert to orders

Signal Statuses

A signal carries one of five statuses. The status answers the question "what did UTM do with this instruction?". Whether the broker filled the resulting order is a separate question, answered by the linked order status.

StatusMeaning
QueuedWaiting to be sent. Covers both immediate-mode signals waiting for a worker and scheduled-mode signals waiting on scheduledProcessAt. The two are distinguished by the scheduledProcessAt field.
ProcessingMid-submit to the broker. Transient state visible for a fraction of a second between claim and result.
TransmittedUTM put the order on the wire to the broker. The signal lifecycle ends here. Whether the broker filled, partially filled, rejected, or cancelled lives on the linked order.
CancelledUTM did not send. The errorCode field carries the reason: pre_flight_rule (validation/risk refused), user_cancelled (you cancelled before send), or expired (validUntil passed).
ErrorUTM tried to send and failed. The errorMessage field carries the underlying error. Covers transmission failures (network, broker auth) and broker rejections.
Reading the status

Cancelled with errorCode=pre_flight_rule means UTM stopped it before transmission (fix your account/broker config or risk settings). Cancelled with errorCode=expired means the signal's validUntil passed before it could be processed. Error means UTM tried to transmit and the attempt failed; the signal can be re-submitted as a fresh request.

Execution Modes

The executeMode field controls when a signal is processed:

ModeBehaviourInitial StatusResponse
immediateProcessed synchronously - order placed straight awayqueued201
scheduledProcessed at scheduledProcessAt in the supplied timezonequeued202

Use immediate to fire-and-forget and scheduled to defer to a specific future time. Both initial statuses are queued; the scheduledProcessAt field distinguishes the two.

Webhook Integration

For automated trading, signals can be received via HTTP webhook.

Setup

  1. Go to Settings > API Keys
  2. Click Create API Key
  3. Select signals:write scope
  4. Copy the generated key

Required fields

Every signal payload must include:

  • accountId — which account the signal targets.
  • strategyId — the strategy that owns this signal. Every user has a built-in Manual strategy created at signup which is a fine default if your integration is not strategy-aware. List strategies via GET /api/v1/strategies.
  • symbol, action, quantity, executeMode — the trade itself.

sourceId is optional. It is a caller-supplied reference id that lets you correlate your own audit trail back to UTM signals. UTM does not use it for dedup.

Duplicate intent

UTM is a translation layer. You own intent and retry semantics. UTM refuses only signals the broker would reject.

Specifically, UTM rejects a signal with HTTP 409 and code: DUPLICATE_INTENT when a same-direction signal for the same (accountId, strategyId, symbol, action) is already in a live status (queued, processing, or transmitted). The response carries the conflicting signal id in details.conflictingSignalId. Resolve that signal (cancel it, or wait for it to settle) before submitting another.

A cancelled or errored prior attempt does not block a fresh submission. Retry semantics, deduping inbound webhooks, and any "skip if I just sent this" logic live in your pipeline, not in UTM.

Disabling the guard

The duplicate-intent guard is on by default. If your caller already dedupes upstream and you would rather take responsibility for retries than have UTM refuse a same-key re-submission, you can turn the guard off per user under Settings -> Signals.

When the guard is off, UTM stops checking for in-flight duplicates on the (accountId, strategyId, symbol, action) key. Two close signals can both transmit, both reach the broker, and overshoot the position. Leave the guard on unless your signal source dedupes upstream and you accept that risk.

Every toggle is recorded in your audit log (user.settings.guard_duplicate_intent.changed) with the previous and new value, so the change has a clear paper trail if something goes wrong later. Re-enabling the guard is a silent save; disabling it requires confirmation in a modal.

Opposing positions on netting brokers

On brokers that net positions per symbol (Alpaca, TradeStation), UTM cannot hold both a long and a short trade for the same (account, symbol) because the broker collapses them into a single net. UTM rejects the second trade up-front with HTTP 409 and code: OPPOSING_POSITION_OPEN. The response carries details.conflictingTradeId.

The check is strategy-agnostic: two strategies running on the same account, one long and one short on the same symbol, hit this rule. Same-side stacking across strategies is allowed (two longs on the same symbol both succeed). Brokers that support hedging (e.g. Interactive Brokers) are unaffected.

To proceed, close the conflicting trade to flat first, then re-submit. Or route the opposing strategy to a different account.

Sending Signals Immediately

curl -X POST https://api.universaltrademanager.com/api/v1/signals \
-H "Content-Type: application/json" \
-H "X-API-Key: utm_your_api_key" \
-d '{
"accountId": "your-account-uuid",
"strategyId": "your-strategy-uuid",
"symbol": "AAPL",
"action": "openLong",
"quantity": 100,
"executeMode": "immediate"
}'

Sending Signals for Scheduled Processing

Provide scheduledProcessAt (ISO datetime, no Z suffix) and an IANA timezone. The scheduled time must be in the future.

Timezone requirement

timezone is required whenever scheduledProcessAt is set. It is also required whenever validUntil is set. Both fields are interpreted in the given timezone. Use IANA identifiers such as America/New_York or UTC.

curl -X POST https://api.universaltrademanager.com/api/v1/signals \
-H "Content-Type: application/json" \
-H "X-API-Key: utm_your_api_key" \
-d '{
"accountId": "your-account-uuid",
"strategyId": "your-strategy-uuid",
"symbol": "AAPL",
"action": "openLong",
"quantity": 100,
"executeMode": "scheduled",
"scheduledProcessAt": "2026-05-15T09:30:00",
"timezone": "America/New_York"
}'

See the Signals API reference for full documentation.

Exit Rules

Attach an automatic exit to an entry signal so UTM submits the closing order for you after the entry fills. Set the trigger fields directly on the signal payload. They are not nested under an exitRule object.

Exit rule fields

The shape is discriminated by exitTriggerType. The other fields are interpreted relative to that choice.

FieldTypeRequiredDescription
exitTriggerTypestringYesOne of minutesAfterEntry, minutesBeforeClose, atClockTime, or immediate. Picks the scheduling rule for the exit.
exitTriggerMinutesintegerConditionalMinutes count (0 to 60). Required when exitTriggerType is minutesAfterEntry or minutesBeforeClose.
exitTriggerTimestringConditionalHH:MM (24-hour) submit time in the broker's timezone. Required when exitTriggerType is atClockTime.
exitOrderTypestringYesExit order type: market, limit, stop, stopLimit, or moc (market on close).
exitLimitPricenumberConditionalLimit price for the exit order. Required when exitOrderType is limit or stopLimit.
exitStopPricenumberConditionalTrigger price for the exit order. Required when exitOrderType is stop or stopLimit.
exitTimeInForcestringConditionalday or cls. Optional for most order types; required as cls when exitOrderType is moc.

Trigger types

TriggerBehaviour
minutesAfterEntrySchedules the exit at entryFillTime + exitTriggerMinutes.
minutesBeforeCloseSchedules the exit at marketClose - exitTriggerMinutes. Rolls to the next session if the market is shut.
atClockTimeSchedules the exit at the next occurrence of exitTriggerTime (broker timezone).
immediateSubmits the exit as soon as the entry is terminal, with no scheduled delay.
Market on close

moc is for end-of-session exits. The broker only accepts it with exitTimeInForce: cls. When paired with minutesBeforeClose, the exit must sit at least 15 minutes before the close to give the broker time to route the order.

The exit waits for the entry to finish

UTM never cancels the entry to create the exit. The exit fires only once the entry order is terminal (fully filled, or cancelled or expired after a partial fill), then closes whatever filled. Use gtd (or day) time-in-force on the entry for an in-and-out trade. A gtc entry that only partially fills stays working, so its timed exit may never fire; UTM returns the exit_rule_tif_may_not_terminate advisory in that case. See the Exit Rules reference for detail.

Example: market exit 30 minutes after entry

Open a long and close it with a market order 30 minutes after the entry fills:

{
"accountId": "your-account-uuid",
"strategyId": "your-strategy-uuid",
"symbol": "AAPL",
"action": "openLong",
"quantity": 100,
"executeMode": "immediate",
"exitTriggerType": "minutesAfterEntry",
"exitTriggerMinutes": 30,
"exitOrderType": "market"
}

Example: limit exit at a fixed clock time

Submit a limit exit at 15:50 broker time:

{
"accountId": "your-account-uuid",
"strategyId": "your-strategy-uuid",
"symbol": "AAPL",
"action": "openLong",
"quantity": 100,
"executeMode": "immediate",
"exitTriggerType": "atClockTime",
"exitTriggerTime": "15:50",
"exitOrderType": "limit",
"exitLimitPrice": 180.0
}

Example: stop exit 30 minutes after entry

Submit a stop order at a protective trigger price 30 minutes after the entry fills. The broker triggers a market order once the symbol trades through exitStopPrice:

{
"accountId": "your-account-uuid",
"strategyId": "your-strategy-uuid",
"symbol": "AAPL",
"action": "openLong",
"quantity": 100,
"executeMode": "immediate",
"exitTriggerType": "minutesAfterEntry",
"exitTriggerMinutes": 30,
"exitOrderType": "stop",
"exitStopPrice": 175.0
}

Example: stop-limit exit 30 minutes after entry

Submit a stop-limit order with both a trigger price (exitStopPrice) and a worst-acceptable limit (exitLimitPrice). The broker converts to a limit order once the symbol trades through exitStopPrice and only fills at or above exitLimitPrice on a sell:

{
"accountId": "your-account-uuid",
"strategyId": "your-strategy-uuid",
"symbol": "AAPL",
"action": "openLong",
"quantity": 100,
"executeMode": "immediate",
"exitTriggerType": "minutesAfterEntry",
"exitTriggerMinutes": 30,
"exitOrderType": "stopLimit",
"exitStopPrice": 175.0,
"exitLimitPrice": 174.0
}

Example: market-on-close exit 15 minutes before close

Exit at the closing auction with an MOC order. exitTimeInForce must be cls, and the offset must be at least 15 minutes:

{
"accountId": "your-account-uuid",
"strategyId": "your-strategy-uuid",
"symbol": "AAPL",
"action": "openLong",
"quantity": 100,
"executeMode": "immediate",
"exitTriggerType": "minutesBeforeClose",
"exitTriggerMinutes": 15,
"exitOrderType": "moc",
"exitTimeInForce": "cls"
}

See Exit Rules Reference for the full field reference.