Skip to content

Partial exits, design 2: opt-in software targets sold by the routines #584

Open

Description

@iAmGiG

Decision (owner, 2026-11-01)

Build design 2 from the research: software targets, opt-in per position, after the paper order test (#660). The research (#654, docs/08_research/03_strategy_research/partial_exits.md) found no gain. No design beats the live trailing exit by more than noise, and with SELL exits off every design lowers CAGR. The owner chose to build it anyway, knowing that.

The listing part of the original report is fixed: /partial reads saved plans (#588). What's left is registering plans and acting on them.

What it does

  • /partial set SYM registers a plan on a held position: half the shares (whole shares; a 1-share position gets no plan) sell at the mode's first target over the entry, and the rest keeps the trailing stop. /partial remove SYM drops a plan.
  • The morning and evening routines check each plan: at or past the target, they sell the target shares at market.
  • A sale goes through plan_close and close_position (feat(cli): sell a held position whatever the signal says #641). They cancel the stop, wait for Alpaca to release the shares, sell, and place a stop on the rest at the same level. For those few seconds no stop covers the position, because Alpaca refuses a sale of shares a stop holds. A failed sale puts the stop back.
  • Each sale goes in the report and the daily records. A failure raises a notice. A dry run sells nothing.

To settle in the plan

  • A plan registered when the price is already past the target would sell at the next routine. /partial set should say so and ask.
  • At 09:20 the market is closed, so a morning sale is a market order queued for the open. Decide whether the morning check sells or only the evening one does.

Fix on the way

  • PartialExitManager.from_mode_manager ignores the mode's target_1_pct (4%, 5% and 6% in trading_modes.yaml) and always uses 4%.

Done when

  • Unit tests cover registration, the check, a sale, a failed sale (stop back in place) and a dry run, with mocks only.
  • A dry run of both routines with a plan registered is clean.
  • One real paper sale runs with the owner watching.
Original text

What happens

Every /partial subcommand (list, summary, active, levels, modify, <symbol>) prints [ERROR] Could not access PartialExitManager.

Causes

  1. The manager can't be built. _get_partial_exit_manager() in src/cli/tools/partial_exit_tools.py calls PartialExitManager(), but the constructor requires order_manager. The TypeError is caught and logged, and the function returns None.
  2. The tools read the wrong attribute. With a manager in hand, the tools would read manager.positions, but PartialExitManager keeps its plans in exit_states.
  3. Nothing shares or saves plans. Each call would build a fresh, empty manager, and PartialExitManager has no persistence. Nothing outside these CLI tools constructs a PartialExitManager, so no trading path registers plans to begin with.

Also, modify_exit_target only changes the stored price. It doesn't replace the limit order at the broker or save the change.

Proposed design

  • Add a module-level get_partial_exit_manager() (like get_gtt_manager()), built with the session's order manager.
  • Persist exit_states in the shared state dir (a state/user.db table or state/partial_exits.json) through src/utils/state_paths, so tests stay isolated through AUTOTRADER_STATE_DIR.
  • Register a plan where bracket entries are placed, or on demand from /partial, and point the CLI tools at exit_states.
  • Make modify replace the live limit order at the broker as well as the stored target, then save.

Acceptance

  • /partial, /partial <symbol>, levels, active, summary and modify work against saved plans and survive a restart.
  • Unit tests use a temporary state dir and mocks, and place no paper orders.

Related

Activity

  1. added
    bugSomething isn't working
    tradingTrading execution, order management, and broker integration
    component:cliCLI/terminal interface and REPL features
    on Oct 14, 2026
  2. iAmGiG commented on Oct 14, 2026

    @iAmGiG
    OwnerAuthor

    Overlaps #524, which reported the constructor bug (cause 1 here) in Dec 2025 and proposes a get_partial_exit_manager() singleton. Causes 2 and 3 and the modify gap are only described here. A fix should close both.

  3. iAmGiG commented on Oct 20, 2026

    @iAmGiG
    OwnerAuthor

    Status 2026-10-20: #588 fixed most of this issue.

    Done in #588

    • /partial builds its manager with an order manager, and the views read exit_states.
    • Plans are saved to state/partial_exits.json (which honours AUTOTRADER_STATE_DIR) and survive a restart. The CLI re-reads the file on each call.
    • modify changes an unfilled limit target and saves it.

    Still open

    • Nothing in the trading flow registers a plan. No code path creates one, so /partial only shows plans saved some other way. This is the main remaining work.
    • modify with a live broker order: it refuses the change instead of replacing the broker's limit order, as the proposed design here intended.

    Design question to settle first

    Registering a plan places a partial limit sell. Alpaca rejects it while an existing stop holds all the shares (the stop counts against qty_available). That's true of every position the scheduler manages today. Options:

    1. Shrink the stop to the shares not covered by the partial target, and resize it after each partial fill.
    2. Keep the targets in software (GTT price triggers), and when a target is hit, shrink or cancel the stop before sending the sell.
    3. Register plans only on new entries, as a bracket-like OCO per tranche.

    Remaining acceptance

    • Plans are registered by the chosen path.
    • modify replaces a live target order, or the design records why it can't.
    • Tests use a temporary state dir and mocks, and place no paper orders.

    Nothing depends on this yet. The current positions exit by trailing stop only: their take-profit legs lapsed and aren't re-placed (accepted 2026-10-20).

  4. changed the title [-]/partial commands fail: no PartialExitManager is reachable from the CLI[/-] [+]/partial: register plans in the trading flow, and modify targets that have a live order[/+] on Oct 28, 2026
  5. iAmGiG commented on Oct 28, 2026

    @iAmGiG
    OwnerAuthor

    Retitled 2026-10-28. #588 made /partial work and #508 is closed; what's left here is the two items in the 2026-10-20 status comment above: nothing in the trading flow registers a plan, and modify refuses a target that has a live broker order. The design question stands: a partial limit sell is rejected while a stop holds all the shares.

  6. added a commit that references this issue on Nov 1, 2026
  7. added
    enhancementNew feature or request
    and removed
    bugSomething isn't working
    on Nov 3, 2026
  8. changed the title [-]/partial: register plans in the trading flow, and modify targets that have a live order[/-] [+]Partial exits, design 2: opt-in software targets sold by the routines[/+] on Nov 3, 2026
  9. added a commit that references this issue on Nov 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    component:cliCLI/terminal interface and REPL featuresenhancementNew feature or requesttradingTrading execution, order management, and broker integration

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions