Skip to content

feat(strategy_ladder_buys): cap consecutive rungs per drawdown#7

Open
peeyush20 wants to merge 1 commit into
khoks:mainfrom
peeyush20:change/ladder-buys-rung-cap-20260428
Open

feat(strategy_ladder_buys): cap consecutive rungs per drawdown#7
peeyush20 wants to merge 1 commit into
khoks:mainfrom
peeyush20:change/ladder-buys-rung-cap-20260428

Conversation

@peeyush20
Copy link
Copy Markdown

Closes a gap surfaced in the README review: ladder_buys has nothing stopping it from buying every drop in a sustained downtrend until cash runs out. Adds a per-stock cap on both rung count and cumulative notional invested, both reset on any sell.

New tunables (persistence/config/strategy_defaults.json)

  • ladder_buys.max_rungs = 4 — entry counts as rung 1, so this
    means entry + 3 dip adds before
    the strategy pauses on this stock.
  • ladder_buys.max_position_usd = 2000 — additive belt-and-suspenders. Even
    if max_rungs were misconfigured,
    cumulative invested-since-last-sell
    on one stock is bounded.

State (lib/pool.sh)

  • New per-stock fields strategy_config.ladder_buys.consecutive_buys and consecutive_invested_usd. Initialised to 0 in pool_add_stock.
  • New helper pool_increment_ladder_consecutive bumps both atomically. Uses // 0 fallbacks so it's safe against the pre-counter empty {} layout in existing pool.json files.
  • pool_set_last_sell now resets both counters. Triggers on any sell — partial (profit_take) or full (trailing_stop) — so each new accumulation cycle starts with a fresh budget.

Strategy enforcement (.claude/skills/strategy_ladder_buys/scripts/apply.sh)

  • Two new short-circuit checks before notional sizing: rung_cap_hit — emits status:"skipped" reason "max_rungs reached (N/M)"
    pos_cap_hit — emits "max_position_usd reached ($X/$Y)"
  • The notional sizing min() is extended with remaining_pos = max_pos -
    consecutive_invested, so the final rung uses a partial notional rather
    than overshooting the cap.
  • Successful orders' reason now includes "(rung N/M)" — operator can see
    cap pressure at a glance in the snapshot.

Doc (.claude/skills/strategy_ladder_buys/SKILL.md)

  • New "Consecutive-rung cap" section explaining both knobs, reset-on-sell semantics, the per-stock override path, and the manual-close caveat (closing a position in the Alpaca web UI does NOT reset the counter — same divergence problem as elsewhere; pointed at so operators hitting it know what to do).
  • First-rung handling note updated to flag that cold-start counts as rung 1.

Backward compat

  • Existing pool.json entries with ladder_buys: {} still work. The // 0 fallbacks treat missing fields as zero on read; the next pool_increment populates them.
  • pool_strategy_config already merges defaults into per-stock overrides, so the new max_rungs / max_position_usd defaults automatically apply to stocks added before this change.

Verified

  • bash -n on pool.sh and apply.sh; jq parse on strategy_defaults.json.
  • 7-step functional test of pool_add_stock / pool_increment_ladder_consecutive / pool_set_last_sell / pool_strategy_config in a sandbox repo, including a backward-compat path and cap-hit arithmetic. All pass.

Closes a gap surfaced in the README review: ladder_buys has nothing
stopping it from buying every drop in a sustained downtrend until cash
runs out. Adds a per-stock cap on both rung count and cumulative
notional invested, both reset on any sell.

New tunables (persistence/config/strategy_defaults.json)
- ladder_buys.max_rungs        = 4    — entry counts as rung 1, so this
                                        means entry + 3 dip adds before
                                        the strategy pauses on this stock.
- ladder_buys.max_position_usd = 2000 — additive belt-and-suspenders. Even
                                        if max_rungs were misconfigured,
                                        cumulative invested-since-last-sell
                                        on one stock is bounded.

State (lib/pool.sh)
- New per-stock fields strategy_config.ladder_buys.consecutive_buys and
  consecutive_invested_usd. Initialised to 0 in pool_add_stock.
- New helper pool_increment_ladder_consecutive bumps both atomically.
  Uses // 0 fallbacks so it's safe against the pre-counter empty {}
  layout in existing pool.json files.
- pool_set_last_sell now resets both counters. Triggers on any sell —
  partial (profit_take) or full (trailing_stop) — so each new
  accumulation cycle starts with a fresh budget.

Strategy enforcement (.claude/skills/strategy_ladder_buys/scripts/apply.sh)
- Two new short-circuit checks before notional sizing:
    rung_cap_hit  — emits status:"skipped" reason "max_rungs reached (N/M)"
    pos_cap_hit   — emits "max_position_usd reached (\$X/\$Y)"
- The notional sizing min() is extended with remaining_pos = max_pos -
  consecutive_invested, so the final rung uses a partial notional rather
  than overshooting the cap.
- Successful orders' reason now includes "(rung N/M)" — operator can see
  cap pressure at a glance in the snapshot.

Doc (.claude/skills/strategy_ladder_buys/SKILL.md)
- New "Consecutive-rung cap" section explaining both knobs, reset-on-sell
  semantics, the per-stock override path, and the manual-close caveat
  (closing a position in the Alpaca web UI does NOT reset the counter —
  same divergence problem as elsewhere; pointed at so operators hitting
  it know what to do).
- First-rung handling note updated to flag that cold-start counts as
  rung 1.

Backward compat
- Existing pool.json entries with ladder_buys: {} still work. The
  // 0 fallbacks treat missing fields as zero on read; the next
  pool_increment populates them.
- pool_strategy_config already merges defaults into per-stock overrides,
  so the new max_rungs / max_position_usd defaults automatically apply
  to stocks added before this change.

Verified
- bash -n on pool.sh and apply.sh; jq parse on strategy_defaults.json.
- 7-step functional test of pool_add_stock / pool_increment_ladder_consecutive
  / pool_set_last_sell / pool_strategy_config in a sandbox repo, including
  a backward-compat path and cap-hit arithmetic. All pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant