### Install Pre-commit Hooks and Run ThetaGang (Development) Source: https://github.com/brndnmtthws/thetagang/blob/main/README.md This console command sequence is used for development purposes. It first installs the pre-commit hooks using `uv run pre-commit install` and then runs the ThetaGang application with its help flag (`uv run thetagang -h`) to display available commands and options. ```console # Install the pre-commit hooks uv run pre-commit install # Run thetagang uv run thetagang -h ``` -------------------------------- ### Run ThetaGang Bot from Command Line Source: https://context7.com/brndnmtthws/thetagang/llms.txt Provides command-line instructions for installing and running the ThetaGang bot. Includes options for specifying the configuration file, enabling dry-run mode, running without IBC, and increasing verbosity for debugging. ```bash # Install via pip pip install thetagang # Run with default config thetagang --config thetagang.toml # Dry run to preview orders without execution thetagang --config thetagang.toml --dry-run # Run without IBC (manual TWS connection) thetagang --config thetagang.toml --without-ibc # Increase verbosity for debugging thetagang --config thetagang.toml -vv ``` -------------------------------- ### TOML Configuration: Growth Portfolio with Hedging Source: https://github.com/brndnmtthws/thetagang/blob/main/README.md This TOML configuration example targets a growth-oriented portfolio with a VIX call hedge for risk management. It includes higher-growth assets like QQQ and ARKK, with a small allocation to IWM, and a specific hedge using VIX options. ```toml [symbols.QQQ] weight = 0.60 delta = 0.30 [symbols.ARKK] weight = 0.30 delta = 0.35 [symbols.IWM] weight = 0.10 delta = 0.30 [vix_call_hedge] enabled = true allocation = 0.01 # 1% of buying power ``` -------------------------------- ### Start ThetaGang Trading Bot with Python Source: https://context7.com/brndnmtthws/thetagang/llms.txt Initializes and runs the ThetaGang trading bot using Python. Supports different startup modes: production with IBC managing TWS, dry-run for order simulation, and manual connection to an existing TWS instance. ```python from thetagang.thetagang import start # Start with IBC managing TWS gateway (production mode) start(config_path="thetagang.toml", without_ibc=False, dry_run=False) # Dry run mode - shows orders without executing start(config_path="thetagang.toml", without_ibc=False, dry_run=True) # Manual mode - connect to existing TWS/gateway instance start(config_path="thetagang.toml", without_ibc=True, dry_run=False) ``` -------------------------------- ### TOML Configuration: Market Hours Trading Source: https://github.com/brndnmtthws/thetagang/blob/main/README.md This TOML configuration example sets parameters for trading only during specific market hours. It includes delays after market open and before market close, and defines the action to take when the market is closed (e.g., 'exit'). ```toml [exchange_hours] delay_after_open = 3600 # Wait 1 hour after open delay_before_close = 3600 # Stop 1 hour before close action_when_closed = "exit" # Don't run outside hours ``` -------------------------------- ### Install ThetaGang Package (Console) Source: https://github.com/brndnmtthws/thetagang/blob/main/README.md This command installs the ThetaGang Python package using pip. It's a prerequisite for running the ThetaGang bot and requires Python 3.10 to 3.13 with the uv package manager. ```bash pip install thetagang ``` -------------------------------- ### TOML Configuration: Conservative Portfolio Source: https://github.com/brndnmtthws/thetagang/blob/main/README.md A TOML configuration example for a conservative investment portfolio. It prioritizes stability by allocating weights to major index ETFs like SPY and TLT, with lower delta values for safer strike selections. ```toml [symbols.SPY] weight = 0.50 delta = 0.20 # Lower delta for safer strikes [symbols.TLT] weight = 0.30 delta = 0.15 [symbols.GLD] weight = 0.20 delta = 0.15 ``` -------------------------------- ### Option Strike and Price Utilities Source: https://context7.com/brndnmtthws/thetagang/llms.txt Functions for calculating option prices, selecting strikes, and validating trades. This includes getting the midpoint price of an option, determining the best available prices for buying or selling, calculating the weighted average strike price of existing short positions, and checking if rolling an option spread would increase its width. ```python from thetagang.util import ( midpoint_or_market_price, get_higher_price, get_lower_price, weighted_avg_short_strike, would_increase_spread ) # Get midpoint price with fallback to market ticker = await ibkr.get_ticker_for_option(option_contract) price = midpoint_or_market_price(ticker) # Calculation: # if ticker.bid > 0 and ticker.ask > 0: # return (ticker.bid + ticker.ask) / 2 # else: # return ticker.marketPrice() # Higher price for selling (conservative) sell_price = get_higher_price(ticker) # Returns max(midpoint, market_price) - get best price when selling # Lower price for buying (conservative) buy_price = get_lower_price(ticker) # Returns min(midpoint, market_price) - get best price when buying # Calculate weighted average strike of short positions short_positions = get_short_positions(spy_positions, "C") avg_strike = weighted_avg_short_strike(short_positions) # Example with 2 positions: # Position 1: 5 contracts @ $450 strike # Position 2: 3 contracts @ $460 strike # avg_strike = (5*450 + 3*460) / (5+3) = $453.75 # Check if rolling would increase spread width old_long_strike = 450.0 old_short_strike = 460.0 new_long_strike = 450.0 new_short_strike = 465.0 increases = would_increase_spread( old_long_strike, old_short_strike, new_long_strike, new_short_strike ) # True - spread widened from $10 to $15 # Use case: prevent rolling spreads wider unintentionally # old_spread_width = abs(old_short_strike - old_long_strike) # new_spread_width = abs(new_short_strike - new_long_strike) # increases = new_spread_width > old_spread_width ``` -------------------------------- ### Python Option Position Rolling Logic Source: https://context7.com/brndnmtthws/thetagang/llms.txt Details the automatic rolling of options based on defined Profit & Loss (PnL) or Days To Expiration (DTE) thresholds. It outlines the configuration parameters for rolling and provides an example of evaluating a position against these thresholds. ```python # Rolling happens automatically during manage() # Configure roll thresholds in config: roll_config = { "dte": 15, # Roll when DTE <= 15 "pnl": 0.9, # Or when P&L >= 90% "min_pnl": 0.0, # Minimum P&L required at DTE threshold "max_dte": 180, # Don't roll to contracts beyond 180 DTE "close_at_pnl": 0.99, # Just close at 99% profit "calls": { "itm": True, # Roll ITM calls "always_when_itm": False, "credit_only": False, # Allow debit rolls "maintain_high_water_mark": True # Don't roll calls down }, "puts": { "itm": False, # Don't roll ITM puts (let them assign) "credit_only": False } } # Example position evaluation: # Current position: SPY Jan 19 '24 $450 Put, DTE=12, P&L=85% # Result: No roll yet (DTE <= 15 but P&L < 90%) # Current position: QQQ Feb 16 '24 $380 Call, DTE=35, P&L=92% # Result: Roll! (P&L >= 90%, finds next expiration) # Creates combo order: BUY TO CLOSE Feb $380, SELL TO OPEN Mar $380 # Check if position would be rolled: from thetagang.options import option_dte from thetagang.util import position_pnl position = portfolio_positions["SPY"][0] # PortfolioItem dte = option_dte(position.contract.lastTradeDateOrContractMonth) pnl = position_pnl(position) should_roll = (dte <= config.roll_when.dte and pnl >= config.roll_when.min_pnl) \ or pnl >= config.roll_when.pnl ``` -------------------------------- ### Download ThetaGang Configuration Files Source: https://github.com/brndnmtthws/thetagang/blob/main/README.md This command sequence downloads the necessary configuration files for ThetaGang and its IBC integration. It creates a directory and uses curl to fetch the `thetagang.toml` and `ibc-config.ini` files from GitHub. ```console mkdir ~/thetagang cd ~/thetagang curl -Lq https://raw.githubusercontent.com/brndnmtthws/thetagang/main/thetagang.toml -o ./thetagang.toml curl -Lq https://raw.githubusercontent.com/brndnmtthws/thetagang/main/ibc-config.ini -o ./config.ini ``` -------------------------------- ### Run ThetaGang with Docker Source: https://github.com/brndnmtthws/thetagang/blob/main/README.md This command executes the ThetaGang Docker container, mounting the local configuration directory to `/etc/thetagang` inside the container. It specifies the `brndnmtthws/thetagang:main` image and passes the configuration file path as an argument. ```docker docker run --rm -i --net host \ -v ~/thetagang:/etc/thetagang \ brndnmtthws/thetagang:main \ --config /etc/thetagang/thetagang.toml ``` -------------------------------- ### Sample TOML Configuration Structure Source: https://context7.com/brndnmtthws/thetagang/llms.txt Illustrates the structure of a TOML configuration file for the ThetaGang bot. Defines settings for account details, trading constants, option chain parameters, rolling conditions, target deltas and DTE, symbol-specific weights and strike limits, VIX call hedging, and cash management. ```toml [account] number = "DU1234567" margin_usage = 0.5 # Use 50% of net liquidation value cancel_orders = true market_data_type = 1 [constants] daily_stddev_window = "30 D" write_threshold = 0.01 # Write options 1% OTM [option_chains] expirations = 4 # Load 4 expiration cycles strikes = 15 # Load 15 strikes per chain [roll_when] pnl = 0.9 # Roll at 90% profit dte = 15 # Or when DTE <= 15 days min_pnl = 0.0 # Minimum P&L to roll close_at_pnl = 0.99 # Auto-close at 99% profit [target] dte = 45 # Target 45 DTE when writing delta = 0.3 # Target 0.3 delta minimum_open_interest = 10 maximum_new_contracts_percent = 0.05 # Max 5% of buying power [symbols.SPY] weight = 0.40 # 40% of portfolio delta = 0.25 # Symbol-specific delta primary_exchange = "ARCA" [symbols.SPY.puts] strike_limit = 400.0 # Don't write puts above $400 write_threshold_sigma = 1.5 # Use 1.5σ instead of percent [symbols.SPY.calls] strike_limit = 500.0 cap_factor = 0.5 # Only write calls on 50% of shares [vix_call_hedge] enabled = true allocation = 0.01 # Allocate 1% to VIX calls target_dte = 30 delta = 0.3 close_hedges_when_vix_exceeds = 50 [cash_management] enabled = true cash_fund = "SGOV" target_cash_balance = 0 buy_threshold = 10000 # Buy when excess cash > $10k sell_threshold = 10000 # Sell when cash needed ``` -------------------------------- ### Display ThetaGang Command-Line Help (Console) Source: https://github.com/brndnmtthws/thetagang/blob/main/README.md This command displays the help message for the ThetaGang command-line interface. It shows available subcommands, options, and their descriptions, aiding users in understanding how to operate the bot. ```bash thetagang -h ``` -------------------------------- ### Load and Validate Configuration with Python Source: https://context7.com/brndnmtthws/thetagang/llms.txt Loads configuration from a TOML file, normalizes it to handle deprecated fields, and validates it using Pydantic models. Provides methods to access account settings, symbol-specific parameters, and trading restrictions. Handles hierarchical parameter resolution and default fallbacks. ```python import toml from thetagang.config import Config, normalize_config # Load configuration from TOML file with open("thetagang.toml", "r", encoding="utf8") as file: raw_config = toml.load(file) # Normalize handles deprecated fields and converts "parts" to "weight" normalized = normalize_config(raw_config) # Parse and validate with Pydantic config = Config(**normalized) # Display configuration summary config.display("thetagang.toml") # Access account settings account_number = config.account.number margin_usage = config.account.margin_usage # e.g., 0.5 for 50% of NLV # Get symbol-specific settings with fallback to defaults spy_delta = config.get_target_delta("SPY", "P") # Put delta call_strike_limit = config.get_strike_limit("SPY", "C") # Check if trading is allowed for a symbol if config.trading_is_allowed("TSLA"): # Check special modes buy_only = config.is_buy_only_rebalancing("AAPL") sell_only = config.is_sell_only_rebalancing("MSFT") ``` -------------------------------- ### Configure Cash Management with VWAP Algorithm Source: https://context7.com/brndnmtthws/thetagang/llms.txt Configures automated cash management using a target balance and thresholds for buying or selling a cash fund (SGOV). It demonstrates how to place a market order with the VWAP algorithm for execution. Dependencies include the 'ib_async' library. ```python cash_mgmt_config = { "enabled": True, "cash_fund": "SGOV", # iShares 0-3 Month Treasury ETF "target_cash_balance": 0, # Keep minimal cash "buy_threshold": 10000, # Buy SGOV if cash > $10k "sell_threshold": 10000, # Sell SGOV if cash < -$10k "primary_exchange": "ARCA", "orders": { "exchange": "SMART", "algo": { "strategy": "Vwap", # Use VWAP algo for cash fund "params": [] } } } # Example cash management cycle: # 1. Check account cash balance current_cash = 45000 # From account summary target_cash = 0 excess_cash = current_cash - target_cash # $45,000 # 2. Determine action if excess_cash > cash_mgmt_config["buy_threshold"]: # Buy SGOV with excess cash sgov_price = 100.50 shares_to_buy = int(excess_cash / sgov_price) print(f"BUY {shares_to_buy} shares of SGOV @ ${sgov_price}") elif excess_cash < -cash_mgmt_config["sell_threshold"]: # Sell SGOV to raise cash sgov_shares = 200 # Current SGOV position shares_to_sell = min(sgov_shares, int(abs(excess_cash) / sgov_price)) print(f"SELL {shares_to_sell} shares of SGOV @ ${sgov_price}") # 3. Create market order with VWAP algo from ib_async import Stock, MarketOrder, TagValue sgov_stock = Stock("SGOV", "SMART", "USD", primaryExchange="ARCA") order = MarketOrder("BUY", shares_to_buy) order.algoStrategy = "Vwap" order.algoParams = [] # Default VWAP parameters ibkr.place_order(sgov_stock, order) # Cash fund is excluded from wheel strategy # It's managed independently of symbol weights ``` -------------------------------- ### Configure Algorithm Strategy and Parameters (TOML) Source: https://github.com/brndnmtthws/thetagang/blob/main/README.md This snippet shows how to configure the order execution algorithm, including setting the strategy type and its associated parameters like priority. The 'priority' parameter accepts 'Urgent', 'Normal', or 'Patient' options. ```toml [orders.algo] strategy = "Adaptive" params.priority = "Patient" # Options: "Urgent", "Normal", "Patient" ``` -------------------------------- ### Place and Manage Options Orders with IBKR API in Python Source: https://context7.com/brndnmtthws/thetagang/llms.txt This Python code outlines the process of creating and submitting options orders using the IBKR wrapper. It demonstrates building LimitOrder objects, adding them to an orders collection, and submitting them via the Trades class for execution. ```python from ib_async import LimitOrder, TagValue from thetagang.orders import Orders from thetagang.trades import Trades # Create orders container orders = Orders() # Build limit order for selling a put put_contract = Option("SPY", "20240119", 430.0, "P", "SMART") sell_order = LimitOrder( action="SELL", totalQuantity=4, lmtPrice=3.50, # Credit received tif="DAY", orderType="LMT", transmit=True ) # Add algo parameters (Adaptive with Patient priority) sell_order.algoStrategy = "Adaptive" sell_order.algoParams = [TagValue("adaptivePriority", "Patient")] # Add to orders collection orders.add_order(put_contract, sell_order) # Print order summary orders.print_summary() # Submit orders via Trades trades = Trades(ibkr) # Submit single order trades.submit_order(put_contract, sell_order) # Check if orders were submitted if not trades.is_empty(): trades.print_summary() ``` -------------------------------- ### Configure VIX Call Hedging Source: https://context7.com/brndnmtthws/thetagang/llms.txt Sets up configuration parameters for VIX call hedging, including target delta, expiration dates, and allocation weights based on VIX levels. The comments illustrate a sample calculation for hedge allocation and contract selection. ```python # Configure VIX hedging in config vix_hedge_config = { "enabled": True, "delta": 0.30, # Target 0.30 delta calls "target_dte": 30, # Next month expiration "ignore_dte": 5, # Don't buy if expiry within 5 days "max_dte": 60, # Don't buy calls beyond 60 days "close_hedges_when_vix_exceeds": 50, # Close when VIX > 50 "allocation": [ {"lower_bound": None, "upper_bound": 15.0, "weight": 0.0}, {"lower_bound": 15.0, "upper_bound": 30.0, "weight": 0.01}, {"lower_bound": 30.0, "upper_bound": 50.0, "weight": 0.005}, {"lower_bound": 50.0, "upper_bound": None, "weight": 0.0} ] } # Example hedge scenario: # Account buying power: $500,000 # VIXMO (VIX at month end): 22.5 # Allocation: 22.5 falls in [15.0, 30.0] range → 1% weight # Hedge allocation: $500,000 * 0.01 = $5,000 # Find VIX call contracts: # Target: ~30 DTE, 0.30 delta # Current VIX: 22.5 # Approximate call strike: 22.5 + (22.5 * 0.30) ≈ 29-30 strike # Contract: VIX Feb 14 '24 $30 Call @ $2.50 # Quantity: $5,000 / ($2.50 * 100) = 20 contracts # Hedging is managed automatically during manage() cycle # Access VIX positions: vix_positions = [p for p in portfolio_positions.get("VIX", []) if p.contract.right == "C"] # Close hedges when VIX spikes: # If VIXMO > 50, all VIX calls are closed # This locks in gains from the volatility spike ``` -------------------------------- ### Docker Deployment and Cronjob for Daily Execution Source: https://context7.com/brndnmtthws/thetagang/llms.txt This snippet shows how to run thetagang using a Docker container with volume mounts for configuration persistence. It also includes a cronjob entry to schedule the Docker container for daily execution between Monday and Friday at 9 AM. ```shell docker run --rm -i --net host \ -v ~/thetagang:/etc/thetagang \ brndnmtthws/thetagang:main \ --config /etc/thetagang/thetagang.toml ``` ```shell 0 9 * * 1-5 docker run --rm -i -v ~/thetagang:/etc/thetagang \ brndnmtthws/thetagang:main --config /etc/thetagang/thetagang.toml ``` -------------------------------- ### Run IBKR API Tests with Pytest and Mocked Responses Source: https://context7.com/brndnmtthws/thetagang/llms.txt Execute the test suite using pytest, which supports asynchronous operations for testing IBKR API interactions. Mocked responses are used to simulate API behavior without live connections. Development dependencies are managed with 'uv'. ```bash # Install development dependencies uv sync # Run all tests uv run pytest # Run specific test file uv run pytest tests/test_portfolio_manager.py # Run with coverage report uv run pytest --cov=thetagang --cov-report=html # Run specific test by name uv run pytest tests/test_config.py::test_symbol_weights_sum_to_one # Run with verbose output uv run pytest -vv # Run tests in watch mode (auto-rerun on changes) uv run pytest-watch # Run only fast tests (exclude integration tests) uv run pytest -m "not integration" ``` -------------------------------- ### Configure Buy-Only and Sell-Only Rebalancing Strategies Source: https://context7.com/brndnmtthws/thetagang/llms.txt Sets up and demonstrates buy-only and sell-only rebalancing strategies for portfolio management. These strategies ensure trades occur only for buying or selling, respectively, based on defined share, amount, and percentage thresholds. It also shows how to check the active rebalancing mode for symbols. Dependencies include the 'thetagang.config' module. ```python # Configure buy-only rebalancing for a symbol buy_only_config = { "AAPL": { "weight": 0.10, "buy_only_rebalancing": True, "buy_only_min_threshold_shares": 10, # Minimum 10 shares "buy_only_min_threshold_amount": 1000, # Minimum $1,000 "buy_only_min_threshold_percent": 0.05 # Buy when 5% short } } # Example buy-only scenario: # Target: 10% of $500k = $50,000 in AAPL # Current: 0 shares, AAPL @ $175 # Target shares: $50,000 / $175 = 285 shares # Shortfall: 285 shares = $49,875 # Check thresholds: shortfall_shares = 285 shortfall_amount = 49875 shortfall_pct = 49875 / 50000 # 0.9975 = 99.75% meets_threshold = ( shortfall_shares >= 10 and shortfall_amount >= 1000 and shortfall_pct >= 0.05 ) # True - all thresholds met # Action: BUY 285 shares of AAPL @ market # Uses VWAP algo for execution # Configure sell-only rebalancing sell_only_config = { "TSLA": { "weight": 0.05, "sell_only_rebalancing": True, "sell_only_min_threshold_shares": 20, "sell_only_min_threshold_amount": 5000, "sell_only_min_threshold_percent": 0.10 } } # Example sell-only scenario: # Target: 5% of $500k = $25,000 in TSLA # Current: 200 shares @ $250 = $50,000 # Excess: 100 shares = $25,000 # Check thresholds: excess_shares = 100 excess_amount = 25000 excess_pct = 25000 / 25000 # 1.0 = 100% meets_threshold = ( excess_shares >= 20 and excess_amount >= 5000 and excess_pct >= 0.10 ) # True # Action: SELL 100 shares of TSLA @ market # Check rebalancing mode if config.is_buy_only_rebalancing("AAPL"): print("AAPL uses buy-only rebalancing") if config.is_sell_only_rebalancing("TSLA"): print("TSLA uses sell-only rebalancing") ``` -------------------------------- ### Set Absolute and Percentage Limits for New Contracts (TOML) Source: https://github.com/brndnmtthws/thetagang/blob/main/README.md These settings define limits on the number of new contracts that can be opened in a trading run. You can set an absolute maximum number of contracts or a maximum percentage of the available capital or positions. ```toml [target] maximum_new_contracts = 10 # Absolute limit per run maximum_new_contracts_percent = 0.5 # Or limit by percentage ``` -------------------------------- ### Configure Options Writing Behavior in Python Source: https://context7.com/brndnmtthws/thetagang/llms.txt This Python code configures the behavior for writing new options contracts, including settings for calls and puts, threshold percentages, and symbol-specific adjustments. It also includes logic to check if writing conditions are met based on current portfolio values and thresholds. ```python write_config = { "calculate_net_contracts": False, # For spreads/PMCC "calls": { "green": True, # Write calls when underlying is up "red": False, # Don't write calls when underlying is down "cap_factor": 1.0, # Write calls on 100% of shares "excess_only": False, # Write on all shares, not just excess "min_threshold_percent": 0.05 # Need 5% shortfall to write }, "puts": { "green": False, # Don't write puts when underlying is up "red": True # Write puts when underlying is down } } # Symbol-specific write thresholds: symbol_config = { "SPY": { "write_threshold": 0.02, # Write when 2% below target "write_threshold_sigma": None, # Or use sigma instead "delta": 0.25, # Target 0.25 delta "primary_exchange": "ARCA" } } # Example scenario - writing a put: # Portfolio: $500k buying power, SPY weight=0.40, target=$200k # Current: 0 shares of SPY @ $450 # Action: Write 4 contracts of SPY puts at 0.25 delta, 45 DTE # Contract found: SPY Jan 19 '24 $430 Put (0.26 delta, 47 DTE) # Order: SELL TO OPEN 4 SPY Jan 19 '24 $430 Put @ $3.50 credit # Example scenario - writing a call: # Current: 400 shares of SPY (cost basis $435), price=$460 # Target calls: 4 contracts at strike >= $435, 0.30 delta, 45 DTE # Contract found: SPY Feb 16 '24 $465 Call (0.28 delta, 43 DTE) # Order: SELL TO OPEN 4 SPY Feb 16 '24 $465 Call @ $5.80 credit # Check if write conditions are met: current_value = 0 # Current position value target_value = 200000 shortfall_pct = (target_value - current_value) / target_value write_threshold = config.get_write_threshold_perc("SPY", "P") should_write_puts = shortfall_pct >= write_threshold ``` -------------------------------- ### Fetch Tickers and Option Chains with IBKR API in Python Source: https://context7.com/brndnmtthws/thetagang/llms.txt This Python code demonstrates how to use the IBKR wrapper to fetch market data, including stock tickers and option chains, from Interactive Brokers. It covers connecting to the API, setting market data types, qualifying contracts, and handling potential request timeouts. ```python from thetagang.ibkr import IBKR, TickerField from ib_async import IB, Stock, Option ib = IB() await ib.connectAsync('127.0.0.1', 7497, clientId=1) ibkr = IBKR(ib, api_response_wait_time=60, default_order_exchange="SMART") # Set market data type (1=Live, 2=Frozen, 3=Delayed, 4=Delayed-Frozen) ibkr.set_market_data_type(1) # Fetch stock ticker with validation stock_contract = Stock("SPY", "SMART", "USD", primaryExchange="ARCA") ticker = await ibkr.get_ticker_for_stock("SPY", "ARCA") print(f"Market price: {ticker.marketPrice()}") print(f"Bid: {ticker.bid}, Ask: {ticker.ask}") print(f"Close: {ticker.close}") # Qualify contract (resolve exchange/details) qualified = await ibkr.qualify_contract(stock_contract) # Get option chains chains = await ibkr.get_option_chains(stock_contract) for chain in chains: print(f"Exchange: {chain.exchange}") print(f"Expirations: {chain.expirations[:5]}") # First 5 print(f"Strikes: {chain.strikes[:10]}") # First 10 # Request specific option ticker with validation option_contract = Option("SPY", "20240119", 450.0, "P", "SMART") option_ticker = await ibkr.get_ticker_for_option( option_contract, fields=[TickerField.MIDPOINT, TickerField.GREEKS, TickerField.OPEN_INTEREST] ) print(f"Option midpoint: {option_ticker.midpoint()}") print(f"Delta: {option_ticker.modelGreeks.delta}") print(f"Theta: {option_ticker.modelGreeks.theta}") print(f"Open interest: {option_ticker.openInterest}") # Handle timeout errors from thetagang.ibkr import IBKRRequestTimeout try: ticker = await ibkr.get_ticker_for_stock("INVALID", "") except IBKRRequestTimeout as e: print(f"Request timed out: {e}") ``` -------------------------------- ### Enable VIX Call Hedging in Thetagang Source: https://github.com/brndnmtthws/thetagang/blob/main/README.md This configuration enables VIX call hedging to protect against market volatility. It allows customization of allocation, conditions for closing hedges, and ignoring short-dated options. Ensure the `thetagang.toml` file is correctly configured. ```toml [vix_call_hedge] enabled = true allocation = 0.01 # 1% of buying power close_hedges_when_vix_exceeds = 50 # Auto-close at high VIX ignore_dte = 5 # Don't hedge if expiry within 5 days ``` -------------------------------- ### Configure IBKR API Response Wait Time and Logging (TOML) Source: https://github.com/brndnmtthws/thetagang/blob/main/README.md This snippet configures the behavior of the Interactive Brokers (IBKR) asynchronous API. It allows setting the maximum time the system will wait for API responses in seconds and enables logging for the API communication, which is helpful for debugging. ```toml [ib_async] api_response_wait_time = 60 # Seconds to wait for API responses logfile = "ib_async.log" # Enable API logging for debugging ``` -------------------------------- ### Enforce Code Quality with Ruff and Pyright Source: https://context7.com/brndnmtthws/thetagang/llms.txt Utilize Ruff for linting and formatting, and Pyright for static type checking to maintain high code quality. Pre-commit hooks ensure these checks run automatically before committing code. Configuration files like .ruff.toml and .pre-commit-config.yaml define the specific rules and checks. ```bash # Run Ruff linter uv run ruff check . # Auto-fix linting issues uv run ruff check --fix . # Run Ruff formatter uv run ruff format . # Check formatting without changes uv run ruff format --check . # Run Pyright type checker uv run pyright # Run specific file uv run pyright thetagang/portfolio_manager.py # Run all pre-commit hooks uv run pre-commit run --all-files # Install pre-commit hooks (runs automatically on git commit) uv run pre-commit install ``` -------------------------------- ### Create Combo Order for Rolling Options Source: https://context7.com/brndnmtthws/thetagang/llms.txt Constructs a combo order to close an existing short put and open a new put at a later expiration. It defines individual legs (close and open) using ComboLeg and combines them into a Contract, then submits the order using LimitOrder. ```python from ib_async.contract import ComboLeg, Contract # Closing leg: BUY existing short put close_leg = ComboLeg( conId=old_contract.conId, ratio=1, action="BUY", exchange="SMART" ) # Opening leg: SELL new put at later expiration open_leg = ComboLeg( conId=new_contract.conId, ratio=1, action="SELL", exchange="SMART" ) # Combo contract (bag) combo = Contract( symbol="SPY", secType="BAG", currency="USD", exchange="SMART", comboLegs=[close_leg, open_leg] ) # Combo order with net credit/debit combo_order = LimitOrder( action="BUY", # Direction of first leg totalQuantity=4, lmtPrice=-0.50, # Negative = net credit of $0.50 tif="DAY" ) trades.submit_order(combo, combo_order) ``` -------------------------------- ### TOML Configuration: PMCC Strategy Source: https://github.com/brndnmtthws/thetagang/blob/main/README.md A TOML configuration snippet for implementing a Poor Man's Covered Call (PMCC) strategy. It emphasizes the `calculate_net_contracts` setting, which is crucial for managing spreads, and defines the weight for the primary symbol (e.g., SPY) while managing long calls separately. ```toml [write_when] calculate_net_contracts = true # Essential for spreads [symbols.SPY] weight = 1.0 # Manage long calls separately # ThetaGang will write short calls against them ``` -------------------------------- ### Python Target Position Calculation Logic Source: https://context7.com/brndnmtthws/thetagang/llms.txt Explains the internal logic for calculating target positions based on account buying power, symbol weights, and current market prices. It details how target shares and contracts are derived and how to access these targets after the 'manage()' method execution. ```python # Target calculation happens internally during manage() # The algorithm: # 1. Calculate total buying power = NLV * margin_usage # 2. For each symbol: # target_value = buying_power * symbol_weight # target_shares = target_value / current_price # target_contracts = floor(target_shares / 100) # Example calculation manually: buying_power = 100000 # Account NLV * margin_usage spy_weight = 0.40 # 40% allocation spy_price = 450.00 # Current SPY price spy_target_value = buying_power * spy_weight # $40,000 spy_target_shares = spy_target_value / spy_price # 88.89 shares spy_target_contracts = int(spy_target_shares / 100) # 0 contracts # For positions that need more than 100 shares: buying_power = 500000 spy_target_value = buying_power * 0.40 # $200,000 spy_target_shares = spy_target_value / 450 # 444.44 shares spy_target_contracts = int(444.44 / 100) # 4 contracts (400 shares) # Access targets after manage() calculates them pm = PortfolioManager(config, ib, completion_future, dry_run=False) # After manage() runs: target_spy_shares = pm.target_quantities.get("SPY", 0) ``` -------------------------------- ### Python PortfolioManager.manage() for Trading Workflow Source: https://context7.com/brndnmtthws/thetagang/llms.txt The main orchestration method for the trading workflow. It connects to IBKR, fetches account data, analyzes positions, and executes trades. It returns account summary, portfolio positions, target quantities, and pending orders. ```python from ib_async import IB from thetagang.config import Config from thetagang.portfolio_manager import PortfolioManager import asyncio # Initialize IB connection ib = IB() await ib.connectAsync('127.0.0.1', 7497, clientId=1) # Create portfolio manager config = Config(**config_dict) completion_future = asyncio.get_event_loop().create_future() pm = PortfolioManager(config, ib, completion_future, dry_run=False) # Main management cycle - this is called automatically when bot starts # The method performs these operations in sequence: # 1. Fetch account summary and portfolio positions # 2. Calculate target quantities for each symbol # 3. Check and roll existing positions # 4. Write new contracts if needed # 5. Manage VIX hedges and cash positions # 6. Submit all orders # Access portfolio data after manage() runs account_summary = pm.account_summary portfolio_positions = pm.portfolio_positions # Check calculated targets for symbol, quantity in pm.target_quantities.items(): print(f"{symbol}: target {quantity} shares") # View pending orders before submission for contract, order in pm.orders.records(): print(f"Order: {contract.symbol} {order.action} {order.totalQuantity}") ``` -------------------------------- ### Calculate Position P&L and Metrics Source: https://context7.com/brndnmtthws/thetagang/llms.txt Utility functions to calculate the Profit and Loss (P&L) for individual positions, retrieve specific types of short positions (calls or puts), and count the number of short option contracts. It also includes functions to determine net short positions for spreads and calculate target call contracts based on current holdings and a cap factor. ```python from thetagang.util import ( position_pnl, get_short_positions, count_short_option_positions, calculate_net_short_positions, get_target_calls, portfolio_positions_to_dict, account_summary_to_dict ) # Convert portfolio items to dictionary by symbol portfolio = ibkr.portfolio(account_number) positions_dict = portfolio_positions_to_dict(portfolio) # Access positions for a symbol spy_positions = positions_dict.get("SPY", []) # Calculate P&L for a position for position in spy_positions: pnl = position_pnl(position) print(f"{position.contract.localSymbol}: P&L = {pnl:.2%}") # P&L calculation: # market_value = position.marketValue # average_cost = position.averageCost * position.position # pnl = 1.0 - (market_value / average_cost) # For short positions (negative position count), formula adjusts # Get short call positions for a symbol short_calls = get_short_positions(spy_positions, "C") # Get short put positions short_puts = get_short_positions(spy_positions, "P") # Count short option contracts num_short_calls = count_short_option_positions(spy_positions, "C") num_short_puts = count_short_option_positions(spy_positions, "P") print(f"SPY: {num_short_calls} short calls, {num_short_puts} short puts") # Calculate net positions (short - long) for spreads/PMCC net_short_calls = calculate_net_short_positions(spy_positions, "C") net_short_puts = calculate_net_short_positions(spy_positions, "P") # Determine target calls accounting for cap_factor current_shares = 400 # Own 400 shares of SPY target_shares = 400 # Target is 400 shares cap_factor = 0.5 # Only write calls on 50% target_call_contracts = get_target_calls( current_shares, target_shares, cap_factor ) # Returns 2 (400 * 0.5 / 100) # With excess_only mode current_shares = 500 # Own 500 shares target_shares = 400 # Target is 400 shares excess_only = True cap_factor = 1.0 if excess_only: excess_shares = max(0, current_shares - target_shares) # 100 shares target_call_contracts = int(excess_shares * cap_factor / 100) # 1 else: target_call_contracts = int(current_shares * cap_factor / 100) # 5 # Convert account summary to dictionary account_values = await ibkr.account_summary(account_number) summary_dict = account_summary_to_dict(account_values) net_liq = float(summary_dict["NetLiquidation"]) buying_power = float(summary_dict["BuyingPower"]) total_cash = float(summary_dict["TotalCashValue"]) print(f"Net Liquidation: ${net_liq:, .2f}") print(f"Buying Power: ${buying_power:, .2f}") print(f"Cash: ${total_cash:, .2f}") ``` -------------------------------- ### Enable Net Contracts Calculation for Spread Strategies (TOML) Source: https://github.com/brndnmtthws/thetagang/blob/main/README.md This configuration enables the calculation of net contracts, which is particularly useful for spread strategies such as PMCCs (Poor Man's Covered Calls) and calendars. This setting ensures accurate contract accounting for complex options positions. ```toml [write_when] calculate_net_contracts = true ``` -------------------------------- ### Configure Cash Management in Thetagang Source: https://github.com/brndnmtthws/thetagang/blob/main/README.md Enables automated cash management by purchasing a specified fund (default 'SGOV') to optimize yield on idle cash. It includes thresholds for buying and selling, and uses VWAP strategy for order execution to minimize market impact. Refer to `thetagang.toml` for detailed options. ```toml [cash_management] enabled = true fund = "SGOV" # Default short-term treasury ETF buy_threshold = 0.01 # Buy when cash > 1% of buying power sell_threshold = 0.005 # Sell when cash < 0.5% [cash_management.orders] algo.strategy = "Vwap" # Use VWAP for cash fund orders ``` -------------------------------- ### Manage Exchange Hours with Wait or Exit Actions Source: https://context7.com/brndnmtthws/thetagang/llms.txt Manages trading activities based on exchange operating hours to prevent trading when markets are closed. It allows configuring actions like 'wait' or 'exit' and specifies delays around market open and close times. Dependencies include 'thetagang.exchange_hours' and 'thetagang.config', along with 'datetime'. ```python from thetagang.exchange_hours import need_to_exit, determine_action from thetagang.config import ExchangeHoursConfig from datetime import datetime, timezone # Configure exchange hours exchange_config = ExchangeHoursConfig( exchange="XNYS", # NYSE calendar action_when_closed="wait", # Options: "wait", "exit", "continue" delay_after_open=1800, # Wait 30 min after open delay_before_close=1800, # Stop 30 min before close max_wait_until_open=3600 # Max 1 hour wait ) # Check if should exit before starting if need_to_exit(exchange_config): print("Exchange closed, exiting") exit(0) # Determine action at specific time now = datetime.now(tz=timezone.utc) action = determine_action(exchange_config, now) # Example scenarios: # Scenario 1: Sunday 3pm EST # Result: action="exit" (market closed, exceeds max_wait) # Scenario 2: Monday 9:15am EST (market opened at 9:30am) # Result: action="wait" (wait 15 more minutes for delay_after_open) # Scenario 3: Monday 10:00am EST # Result: action="continue" (within trading window) # Scenario 4: Monday 3:45pm EST (market closes at 4pm) # Result: action="exit" (within delay_before_close window) ``` -------------------------------- ### Enable Buy-Only Rebalancing for Stocks Source: https://github.com/brndnmtthws/thetagang/blob/main/README.md Activates direct stock purchases for portfolio rebalancing on specific symbols, useful for stocks with limited options liquidity or for dollar-cost averaging. Requires defining minimum shares and dollar amount thresholds for purchases. ```toml [symbols.AAPL] buy_only_rebalancing = true buy_only_min_threshold_shares = 10 # Minimum shares to buy buy_only_min_threshold_amount = 1000 # Minimum dollar amount to buy ``` -------------------------------- ### Set Strike Price Limits for Options Source: https://github.com/brndnmtthws/thetagang/blob/main/README.md Defines boundaries for option strike prices to prevent writing options at unfavorable prices. Separate limits can be set for put and call options on specific symbols. ```toml [symbols.SPY.puts] strike_limit = 400 # Don't write puts above $400 [symbols.SPY.calls] strike_limit = 450 # Don't write calls below $450 ``` -------------------------------- ### Configure Exchange Hours Source: https://context7.com/brndnmtthws/thetagang/llms.txt Configuration object to define exchange hours and trading behavior. The `action_when_closed` parameter can be set to 'continue' to ignore market hours and trade regardless of the exchange's open or closed status. ```python always_trade_config = ExchangeHoursConfig( exchange="XNYS", action_when_closed="continue" # Ignore market hours ) ``` -------------------------------- ### Configure Write Threshold Sigma Source: https://github.com/brndnmtthws/thetagang/blob/main/README.md Utilizes standard deviation (sigma) to set thresholds for writing options, providing a dynamic alternative to fixed percentage-based thresholds. Sigma thresholds can be set globally or overridden for specific symbols. ```toml [constants] write_threshold_sigma = 1.0 # Write when 1 standard deviation from current price [symbols.QQQ.puts] write_threshold_sigma = 1.5 # More conservative for this symbol ``` -------------------------------- ### Access Trade Records and Order Status Source: https://context7.com/brndnmtthws/thetagang/llms.txt Iterates through trade records to print order status and filled quantities. This is useful for monitoring and verifying executed trades. ```python for trade in trades.records(): print(f"Status: {trade.orderStatus.status}") print(f"Filled: {trade.orderStatus.filled}/{trade.order.totalQuantity}") ```