Toll Booth Trading
Covered Call

Covered Call Closing

High-level summary of the Toll Booth covered call position closing process.

Entry, pre-checks, and how positions are chosen

  • The top-level close action delegates immediately to the “close option positions” workflow.
  • If the account is a retirement account and option buying power is below the minimum threshold, the workflow stops and reports that closing is blocked due to insufficient buying power on a retirement account.
  • If the user is not allowed to place closing orders, the workflow stops and reports that closing orders are disabled for the user.
  • If a symbol is provided, positions for that symbol are selected. Otherwise, it selects option positions already marked as “close-ready,” orders them to prioritize ones tried fewer times, then reduces to one position per underlying and randomizes the order. If none are found, it reports that no ready positions were found.

First loop iteration (only the first position summarized)

  • It increments a “close attempts” counter on the position (and the related trade) and persists it, ensuring the same symbol won’t be starved in the next pass.
  • It calls the single-position close function:
    • Ensures the underlying option data is current (refreshes pricing when needed).
    • Obtains a “closing trade resource” for the position. If this resource is missing or returns a simple message instead of an object, that message is returned as the reason and the attempt for this position ends.
    • The resource runs its “close-order readiness” checks. If any check fails, it returns a plain-language reason indicating what blocked the close, and the attempt ends with that reason.
    • If the resource is ready:
      • The flow builds a “close order” from the resource. For non-spread single-leg options, it may automatically switch to a STOP_LIMIT order type if the pricing model indicates a stop-limit should be used.
      • Before sending the order, it proactively sets both the trade and the position to not close-ready to avoid loops and duplicate attempts.
      • It runs an “order-level readiness” pipeline on the built order that validates price sanity, checks for conflicts with other active orders, and validates the leg collection. If anything is off, a descriptive reason is returned and the attempt stops.
      • If the order passes that order-level readiness, it places the order with the broker and treats the attempt as successful.
  • After that first attempt result:
    • If successful, it increments a “closed” count, and records on the position that a closing order was placed today.
    • If unsuccessful, it records the reason (for example: a “not-ready” reason, or a quantitative threshold not met) and may suppress noisy notifications for common threshold messages.

How the position gets marked close-ready (“closeReady”)

Close-ready is set by the option close-ready pipelines that run during position reconciliation. The top-level “option close-ready” pipeline delegates to either a spread-specific close-ready pipeline or to a non-spread close-ready pipeline. The non-spread pipeline is the one that directly flips the position flags:

  • If the non-spread pipeline says “no” (returns a reason), it sets “non-spread close-ready” to false and stores the reason (and, if not already set, also stores it as the general close reason).
  • If the non-spread pipeline allows closing (returns no reason), it sets “non-spread close-ready” to true and also sets the position to close-ready true.

Top-level option close-ready checks (how the gate works)

  • If the user is not allowed to place closing orders, do not mark the position close-ready.
  • If there is ex-dividend risk on the trade, cancel any active closing orders tied to this risk and then skip setting the position close-ready (ex-dividend risk is handled outside normal closes).
  • If there are already any active orders on the trade, skip marking close-ready (prevents duplicates).
  • If there are already active closing orders, skip marking close-ready (prevents duplicates).
  • If the position’s available quantity is zero or below, skip marking close-ready.
  • If active closing-instruction order quantity is already greater than or equal to the position quantity, skip marking close-ready.
  • If the position is a spread:
    • Try the spread close-ready pipeline first (vertical spread readiness checks, then diagonal fallback). If it says “ready as a spread,” allow closing as a spread. If it says “not ready,” fall back to the non-spread pipeline.
  • Then run the non-spread close-ready pipeline (summarized below).

Non-spread option close-ready pipeline (single-leg close readiness)

This pipeline returns a string only when it wants to block closing; otherwise, it returns nothing to indicate “go ahead.” Key checks include:

  • Do not try to close a leg if, across currently active positions, the net position for that same option type (e.g., long puts, short puts, long calls, short calls) is already less than or equal to zero. This prevents closing a leg when it’s effectively already netted out by other legs.
  • Risk/buying-power checks that block closing in situations where closing would be inconsistent with risk policy. Examples include:
    • For long puts: if the “downside move” loss estimate is above certain thresholds, block the close (they’re protected hedges).
    • For long puts when buying power is too low: block to preserve hedges until buying power improves.
    • For short puts: if “upside move” risk isn’t sufficiently favorable (or configured as too high), block, unless other liquidation rules say to close.
    • For short puts with very low extrinsic value or where buying power rules force liquidation: allow closing earlier.
    • For very high mark prices on certain puts (e.g., deep in the money), allow closing with stop-limit logic.
  • Relationship to spreads and nearby partners:
    • If there are nearby vertical partners (e.g., you’re in part of a vertical), avoid setting close-ready in a way that would teardown spread structures unintentionally. Also prefer not to close one leg when it would over-close versus its related partner.
  • Earnings proximity and time-to-expiration:
    • If earnings are tomorrow or the next day, block some close actions to avoid poor executions from event volatility (with exceptions for low-value calls).
    • For near-expiration long or short legs, a series of “same-expiration” net-quantity rules ensure you don’t close the wrong side or leave a spread badly imbalanced.
  • Retirement account safeguards:
    • For retirement accounts, short positions with excessive mark values or over certain allowance thresholds will be evaluated to determine if closing should be forced or blocked per the policy.
  • If none of the above returns a blocking reason, the pipeline allows non-spread close-ready to be set true, and thus overall close-ready becomes true.

Single-position close attempt readiness checks

When the position is being closed, the workflow builds a “closing resource” and validates three categories of readiness before placing the order:

1) Instrument-level close-order readiness (base instrument)

  • Do not proceed if:
    • The trade or quote says “do not close.”
    • The trade has too many recent broker rejections.
    • The position doesn’t exist or its quantity is below 1.
    • The position is flagged “do not close.”
    • The position record is stale and needs refresh.
    • The trade record is missing.
    • The position is not actually marked close-ready anymore.
    • There are enough active closing-instruction orders to cover the entire position quantity already.
    • The close price basis is missing and cannot be populated.
    • The close price basis equals an active opening order on the same trade (to avoid crossing/matched orders).
    • The close price basis is zero or negative.
  • If all of the above checks pass, the instrument declares itself “close-order ready.”

2) Option-specific close-order readiness (base option)

  • Do not proceed if:
    • The option contract is in a “failed” state.
    • The position is already expired.
    • There are active rolling orders (those take precedence over simple close).
    • The resource’s put/call type does not match the trade type (safety check on leg type).
    • For near-expiration “very far out-of-the-money” contracts, skip some closes (to avoid noise and poor fills).
    • For non-spread short calls or short puts, if existing active closing-order quantity already exceeds your net short position for that symbol/type, block the close to prevent over-closing.
  • If none of the above triggers, the option-specific layer declares itself “close-order ready.”

3) Order-level readiness (the order object about to be sent)

  • Do not proceed if:
    • The order has already been marked “ready” or not; this avoids rechecking repeatedly within the same attempt.
    • The price type or numeric values are invalid for the broker (e.g., decimals in prohibited increments, bad limit/stop combos).
    • The close limit vs. mark price spread is excessive according to policy (too far off mid/mark).
    • The order’s computed mark/mark-calc check fails threshold sanity.
    • Another active order exists that would conflict (same or opposite side, either for this user or across users where applicable).
    • There is no leg collection, or the leg collection itself fails its own readiness pipeline.
  • If all pass, the order is “ready” to place.

4) Non-rolling order leg readiness (each leg of a non-rolling order)

  • Do not proceed on a leg if:
    • The leg object is missing or malformed.
    • The trade context isn’t loaded.
    • The leg quantity is not numeric or is zero/negative.
    • For closing instructions, the related position quantity is missing (indicates data problem).
    • You already have active closing-instruction orders with total quantity that meets or exceeds the position quantity (the system will cancel or block duplicates per policy to avoid over-closing).
    • The asset type for a leg is not an option where required (safety guard).
  • If all legs are fine, the leg collection is considered ready.

What “success” vs “failure” looks like (first loop)

  • Success path: The position’s trade resource and order pass all readiness checks, the order is placed, and the system marks the position and trade as not close-ready (to avoid duplicates), logging that a close order was placed.
  • Failure path: Any of the readiness checks (position, instrument, option, order-level, or leg-level) can return a message explaining why the close was not attempted. The position stores that reason. Certain “threshold-not-met” messages are intentionally not noisy to keep logs readable.

Notes about STOP_LIMIT behavior for single-leg closes

When closing a single-leg option and the pricing model signals a stop-limit exit should be used (for risk-managed exits), the workflow switches the close order to a STOP_LIMIT type automatically and sets the stop and limit prices according to that model, then validates and places the order as usual.

Overall flow shape (first pass only)

  • Gather candidate positions (close-ready or symbol-filtered).
  • For the first position:
    • Increment attempts, refresh pricing if needed, build the close resource.
    • Pass instrument-level readiness, then option-level readiness.
    • Construct the close order, mark the trade and position not close-ready to prevent duplicates.
    • Validate order-level readiness and leg-level readiness.
    • Place order if all checks pass; otherwise record the first reason why it’s blocked.

Process flow diagram

Visual summary of the covered call closing workflow described on this page.

%%{init: {"theme":"base","flowchart":{"curve":"basis"},"themeVariables":{
  "fontFamily":"Inter, Nunito, system-ui",
  "primaryTextColor":"#e5e7eb",
  "primaryColor":"#111827",
  "primaryBorderColor":"#94a3b8",
  "lineColor":"#94a3b8",
  "tertiaryColor":"#0e1729",
  "tertiaryBorderColor":"#22d3ee",
  "edgeLabelBackground":"#00000000"
}}}%%
flowchart TB
  A([Start]) --> E1[Entry and pre-checks]
  E1 -->|Fail| OUT[Stop: retirement buying power or closing disabled]
  E1 -->|Pass| SEL{Positions selected}
  SEL -->|None| OUT
  SEL -->|Has items| L1[First position tasks]
  L1 --> R1[Increment attempts; refresh pricing; build closing resource]
  R1 --> R1V{Resource valid}
  R1V -->|No| OUT
  R1V -->|Yes| C1[1 Instrument close-order readiness]
  C1 -->|Fail| OUT
  C1 --> C2[2 Option close-order readiness]
  C2 -->|Fail| OUT
  C2 --> C3[3 Order-level readiness]
  C3 -->|Fail| OUT
  C3 --> C4[4 Non-rolling leg readiness]
  C4 -->|Fail| OUT
  C4 --> ORD[Build close order; may switch to STOP_LIMIT; set trade and position not close-ready]
  ORD --> PLACE[Place order]
  PLACE --> SUC{Placed successfully}
  SUC -->|Yes| DONE([Outcome: close order placed; counts updated])
  SUC -->|No| OUT

  classDef step fill:#111827,stroke:#94a3b8,color:#e5e7eb,stroke-width:1px;
  classDef gate fill:#0e1729,stroke:#22d3ee,color:#e5e7eb,stroke-width:1px;
  classDef out fill:transparent,stroke:#94a3b8,color:#cbd5e1,stroke-dasharray:5 3;

  class A,E1,SEL,L1,R1,R1V,C1,C2,C3,C4,ORD,PLACE,SUC,DONE step;
  class SEL,R1V gate;
  class OUT out;