Skip to content

Latest commit

 

History

15 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Automated TAIEX Options Premium-Selling System

A Taiwan index options trading prototype built with Python, Shioaji, and PyQt6. The system retrieves TX-series option contracts and live snapshots through Shioaji, selects call and put contracts, monitors open positions, and provides a graphical interface for triggering orders.

This project can connect to a live brokerage account and submit market orders. Validate it thoroughly in a simulation or isolated environment before use. This project does not constitute investment advice.

See the original author's Google Slides presentation for additional strategy background.

Project Status

The code contains the following TAIEX options trading functionality:

  • Filters TX-series and nearest-expiry contracts from Contracts.Options.
  • Builds candidate lists for both calls and puts.
  • Uses Shioaji to query market data and account positions and to submit option sell and closing orders.
  • Uses PyQt6 to display trading-session status, suggested contracts, current positions, and unrealized profit and loss.

The strategy is intended to establish a two-sided short call/put position. The current implementation selects each leg independently and does not require the call and put to share the same strike. It may therefore create a short strangle rather than a strict short straddle. A strict straddle strategy must add a call_strike == put_strike constraint to the signal module.

Modular Architecture

The system is designed around four isolated responsibility boundaries. The PyQt6 interface is responsible only for displaying state and emitting user actions:

Module Responsibility Current implementation
Market data Login, certificate activation, contract discovery, snapshots, and account positions api.py, getSymbol.py, snapshot.py, getPositionSS.py
Signal Score candidate strikes and produce call/put contract suggestions SuggestContract.py
Risk management Validate trading hours, positions, take-profit/stop-loss rules, exposure, and permission to trade Risk logic currently lives in the coordinator and should be extracted into a side-effect-free module
Execution Convert approved trade intents into Shioaji orders, closing orders, and execution reports order.py

The high-level data flow is:

Shioaji → Market Data → Signal → Risk Management → Execution
                         │             │
                         └──── PyQt6 Live Monitor ────┘

ui.py implements the monitoring interface. main.py uses QThread to separate backend updates from the GUI event loop so market-data polling does not block the interface.

Strike Selection

Current Baseline Rule

findContracts(snapshot, target) is not a trained statistical model. Among contracts whose bid is below the target premium, it selects the contract with the highest bid:

arg max(bid_i), subject to bid_i < target

Calls and puts use separate target premiums and may therefore produce different strikes. This is an explainable threshold-based baseline, but it does not yet account for implied volatility, Delta, liquidity, time to expiration, margin requirements, or tail risk.

Statistical Model Interface

A statistical implementation of the signal module should:

  1. Build features from the underlying price, historical and implied volatility, time to expiration, bid-ask spread, volume, and Greeks.
  2. Estimate the terminal-price distribution or the probability of each strike being breached.
  3. Calculate expected premium, expected loss, CVaR or stress-scenario losses, and capital usage for each candidate combination.
  4. Select the highest-scoring combination subject to liquidity, maximum-loss, margin, and position-size constraints.
  5. Enforce the same strike for both legs when trading a straddle. If strangles are allowed, explicitly constrain the distance between strikes and total Delta.

The statistical model should only produce recommendations. It must not call the brokerage API directly; every order must still pass through the independent risk-management module.

PyQt6 Live Monitor

The current interface displays:

  • Whether the market is within configured trading hours.
  • A countdown to the next data refresh.
  • Suggested call and put contracts with live prices.
  • Existing call and put positions with unrealized profit and loss.
  • A button for establishing the initial two-sided position.

A production interface should also show connection state, order and fill reports, rejection reasons, risk-lock status, and an emergency stop control.

Installation and Configuration

Python 3.11 or later is required. The recommended package manager is uv:

uv sync

Create a deployment-only .env file and never commit real values:

API_KEY=
SECRET_KEY=
CA_CERT_PATH=/secure/path/to/broker-certificate.pfx
CA_PASSWORD=

Restrict .env so that only the service account can read it, and store the brokerage certificate in a protected directory outside the repository. The current code logs into the live environment when api.py is imported. Before production deployment, replace this behavior with an explicit startup flow and add safeguards that clearly separate simulation from live trading.

Deployment on GCP Linux

Use a dedicated, least-privilege Linux service account on a GCE VM:

  1. Install Python 3.11, uv, and the system libraries required by PyQt6.
  2. Deploy the application to a directory such as /opt/sj-trading, while keeping .env and .pfx files outside the repository.
  3. Inject secrets through Secret Manager or an environment file with 0600 permissions.
  4. Use systemd for process supervision, automatic restarts, and log rotation.
  5. PyQt6 requires a display server. Use a protected VNC or remote-desktop session for interactive monitoring, or xvfb-run for headless execution.
  6. Open only the required firewall rules and apply least-privilege permissions to the VM, service account, and Secret Manager.

Example headless command:

xvfb-run -a uv run python -m sj_trading.main

main.py currently depends on a local coordinator module that is not tracked by Git. Before a fresh clone can run, that module must be sanitized and added to version control, or its coordination, risk-management, and notification responsibilities must be refactored into deployable modules.

Secret Management

The following must never be committed:

  • .env and environment-specific variants.
  • Shioaji API keys, secret keys, identity details, and account passwords.
  • Brokerage CA certificates and passwords, including .pfx, .p12, .pem, and .key files.
  • Telegram or other notification-service tokens.
  • Trading data and runtime logs.

Adding an entry to .gitignore is not enough if a secret has already appeared in a commit. Revoke or rotate the secret first, then remove it from history with git filter-repo or an equivalent tool and coordinate a fresh clone for every contributor.

About

Automated short-straddle system for Taiwan index options — data, risk control, signal, execution, and a live PyQt6 monitor

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages