Skip to content

feat(orders): add configurable per-symbol option price strategy#684

Open
junyuanz1 wants to merge 1 commit into
brndnmtthws:mainfrom
junyuanz1:feat/per-symbol-price-strategy
Open

feat(orders): add configurable per-symbol option price strategy#684
junyuanz1 wants to merge 1 commit into
brndnmtthws:mainfrom
junyuanz1:feat/per-symbol-price-strategy

Conversation

@junyuanz1

Copy link
Copy Markdown
Collaborator

Summary

Adds an opt-in, per-symbol option price strategy so users can quote orders off the midpoint, latest, bid, or ask instead of the default midpoint/model price. Quoting off the bid/ask captures more of the spread on less-liquid options, at the cost of slower or missed fills.

  • Configurable globally under [runtime.orders] and overridable per symbol under [portfolio.symbols.<SYMBOL>].
  • New sell_price / buy_price (midpoint|latest|bid|ask) plus optional sell_price_adjustment / buy_price_adjustment offsets.
  • Resolution order: symbol override → global default → preserve current behavior.

Backward compatibility

Fully backward compatible. When sell_price/buy_price are unset, each order site computes today's price (get_higher_price/get_lower_price) and passes it as a fallback; the resolver only overrides it when a strategy is explicitly configured and the chosen bid/ask/last is valid. If the chosen price is unavailable (NaN/negative), it falls back to the existing price. So existing configs behave exactly as before.

Behavioral impact

  • Routed through the four options-engine order sites: write_calls, write_puts, and both branches of close_positions (buy-to-close / sell-to-close).
  • VIX call-hedge pricing (post_engine.py) is intentionally left unchanged.
  • A MINIMUM_PRICE (0.01) floor prevents a negative adjustment from pushing the limit price to zero/negative.

Config example

[runtime.orders]
sell_price = "ask"
buy_price = "bid"
sell_price_adjustment = 0.0
buy_price_adjustment = 0.0

[portfolio.symbols.ABC]
sell_price = "ask"   # quote illiquid names at the ask; overrides the global default

Tests / validation

  • uv run pytest → 289 passed (incl. 11 new tests in tests/test_order_pricing.py covering bid/ask/midpoint/latest, adjustments, the MINIMUM_PRICE floor, NaN fallback, global default, and symbol-overrides-global).
  • uv run ty check → clean
  • uv run ruff check . / ruff format --check → clean

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings June 23, 2026 02:09

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an opt-in, per-symbol configurable option order price strategy (midpoint/latest/bid/ask) with optional per-side adjustments, wiring it into the options engine’s limit-price creation while preserving existing fallback behavior when unset.

Changes:

  • Introduces sell_price / buy_price and *_price_adjustment at both global ([runtime.orders]) and per-symbol ([portfolio.symbols.<SYMBOL>]) levels.
  • Adds a centralized resolver (Config.get_order_limit_price) and routes option order sites through it.
  • Updates documentation (README.md, thetagang.toml) and adds a new unit test module for pricing resolution.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
thetagang/strategies/options_engine.py Routes option limit-price calculation through the new config-driven resolver.
thetagang/config.py Implements order limit price resolution logic and adds display of configured strategies.
thetagang/config_models.py Extends config schema with global + per-symbol price strategy and adjustments.
thetagang.toml Documents new runtime order pricing configuration options.
tests/test_order_pricing.py Adds unit tests for price strategy resolution and adjustment floor behavior.
README.md Documents global and per-symbol configuration examples and fallback behavior.

Comment thread thetagang/config.py
Comment on lines +611 to +612
if ib_util.isNan(price) or price < 0:
return fallback_price
Comment on lines +126 to +129
def test_global_price_adjustment_applies() -> None:
config = _config(orders={"sell_price": "ask", "sell_price_adjustment": -0.10})
ticker = FakeTicker(ask=3.00)
assert config.get_order_limit_price("AAA", ticker, "SELL", 2.0) == 2.90
Add sell_price/buy_price (midpoint|latest|bid|ask) plus optional
sell/buy price adjustments, configurable globally under [runtime.orders]
and overridable per symbol under [portfolio.symbols.<SYMBOL>].

When unset, order pricing falls back to the existing midpoint/model-price
behavior, so the feature is fully opt-in and backward compatible. This
lets users quote less-liquid options off the bid/ask to capture more of
the spread, at the cost of slower or missed fills.

Wired into the options engine order sites (write puts/calls and close
positions). Includes config display, TOML/README docs, and unit tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@junyuanz1
junyuanz1 force-pushed the feat/per-symbol-price-strategy branch from ea2ffc2 to eb794c3 Compare June 23, 2026 02:15
Comment thread thetagang/config.py
else: # pragma: no cover - guarded by the Literal type
return fallback_price

if ib_util.isNan(price) or price < 0:

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One edge case: sell_price = "bid" can sneak under minimum_credit.

Example: midpoint 0.08 passes a 0.05 floor, but bid 0.01 becomes the submitted limit. Could we clamp opening sells to minimum_credit or fall back when the resolved price is below it?

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.

3 participants