Skip to content

Instantly share code, notes, and snippets.

@jweinst1
Last active August 27, 2026 00:10
Show Gist options
  • Select an option

  • Save jweinst1/e86e1ce67507834ba0832b1b9a2bc6ac to your computer and use it in GitHub Desktop.

Select an option

Save jweinst1/e86e1ce67507834ba0832b1b9a2bc6ac to your computer and use it in GitHub Desktop.
bull put spreads script for alpaca py
from alpaca.data.historical import StockHistoricalDataClient
from alpaca.data.requests import StockSnapshotRequest
from alpaca.data.enums import DataFeed
from alpaca.data.historical import OptionHistoricalDataClient
from alpaca.data.requests import OptionChainRequest, StockBarsRequest, OptionBarsRequest, OptionSnapshotRequest, OptionLatestQuoteRequest
from alpaca.data.enums import OptionsFeed
from alpaca.trading.enums import ContractType, AssetClass
from alpaca.trading.enums import QueryOrderStatus, OrderSide, OrderClass, TimeInForce, OrderStatus, OrderType, PositionIntent
from alpaca.trading.client import TradingClient
from alpaca.trading.requests import GetOrdersRequest, LimitOrderRequest, TakeProfitRequest, StopLimitOrderRequest, GetOptionContractsRequest, OptionLegRequest, ReplaceOrderRequest
from alpaca.data.timeframe import TimeFrame
from itertools import combinations
from collections import defaultdict
import argparse
import os
from pathlib import Path
import statistics
from datetime import datetime, timezone, timedelta
import time
import json
import re
import pandas as pd
import numpy as np
from dataclasses import dataclass
from typing import List, Dict, Tuple
# Get the path of ~
# home_path = Path.home()
# todo json credentials load
def get_json_creds():
home_path = Path.home()
creds_path = home_path / "spread_creds.json"
with open(creds_path, "r", encoding="utf-8") as file:
data = json.load(file)
return data
account_credentials = get_json_creds()
@dataclass
class OptionPosition:
symbol: str
underlying: str
expiration: str
option_type: str
strike: float
qty: int
cost_basis: float
available:bool
def find_spread_orders(client, symbols, qty):
current_until = datetime.now(timezone.utc).isoformat()
found_orders = []
found_qty = 0
while found_qty < qty:
orders_request = GetOrdersRequest(
status=QueryOrderStatus.OPEN,
nested=True,
limit=500,
symbols=symbols,
until=current_until,
direction="desc"
)
chunk = client.get_orders(filter=orders_request)
if not chunk:
break
for order in chunk:
if order.order_class == OrderClass.MLEG and len(order.legs) == 2:
ordset = {str(order.legs[0].symbol), str(order.legs[1].symbol)}
if symbols[0] in ordset and symbols[1] in ordset:
found_qty += float(order.qty)
found_orders.append(order)
current_until = chunk[-1].submitted_at.isoformat()
return found_orders
@dataclass
class BullPutSpread:
underlying: str
expiration: str
short_leg: OptionPosition
long_leg: OptionPosition
qty: int
available:bool
def matches_lo(self, key):
return key in self.long_leg.symbol
def matches_sh(self, key):
return key in self.short_leg.symbol
def find_existing_orders(self, trade_client:TradingClient):
if self.available:
return []
return find_spread_orders(trade_client, [self.short_leg.symbol, self.long_leg.symbol], self.qty)
def cancel_existing_orders(self, trade_client, found_orders):
for forder in found_orders:
if forder.status == OrderStatus.PENDING_CANCEL:
continue
print(f"Canceling order by ID={forder.id}")
trade_client.cancel_order_by_id(forder.id)
def adjust_existing_orders(self, trade_client, found_orders, base_percent_debt):
net_credit_received = abs(self.short_leg.cost_basis) - abs(self.long_leg.cost_basis)
debt_lim = (net_credit_received/ 100) * base_percent_debt
debt_lim = round(debt_lim, 2)
assert debt_lim > 0.0
replace_request = ReplaceOrderRequest(limit_price=debt_lim)
for forder in found_orders:
if forder.status == OrderStatus.PENDING_REPLACE:
print(f"Skipping order ID={forder.id} due to pending replace")
continue
elif forder.status == OrderStatus.ACCEPTED:
print(f"Skipping order ID={forder.id} due to already accepted")
continue
elif forder.limit_price == debt_lim:
print(f"Skipping order ID={forder.id} due to already same price")
continue
print(f"Adjusting order by ID={forder.id} for Lim={debt_lim}")
trade_client.replace_order_by_id(forder.id, replace_request)
def calculate_pl_and_liq(
self, optdata_client: OptionHistoricalDataClient
) -> Tuple[float, float, float, float]:
"""
Fetches live bid/ask quotes to calculate:
- Mid Liquidation Debit per share
- Total Net Liquidation Cost ($)
- Net Credit Received ($)
- True Unrealized P/L ($)
"""
request = OptionLatestQuoteRequest(
symbol_or_symbols=[self.short_leg.symbol, self.long_leg.symbol]
)
quotes = optdata_client.get_option_latest_quote(request)
q_short = quotes[self.short_leg.symbol]
q_long = quotes[self.long_leg.symbol]
# Calculate Mid prices for both legs
short_mid = (q_short.ask_price + q_short.bid_price) / 2.0
long_mid = (q_long.ask_price + q_long.bid_price) / 2.0
# Mid debit to buy back short and sell long leg
mid_debit_per_share = short_mid - long_mid
total_liq_cost = mid_debit_per_share * 100.0 * self.qty
# Net initial credit received based on original position cost basis
net_credit_received = abs(self.short_leg.cost_basis) - abs(self.long_leg.cost_basis)
# True Unrealized P/L = Net Credit Received - Current Liquidation Cost
true_unrealized_pl = net_credit_received - total_liq_cost
return mid_debit_per_share, net_credit_received, total_liq_cost, true_unrealized_pl
def target_closing_debit(
self, net_credit_received: float, profit_target_pct: float = 0.50
) -> float:
"""Calculates target closing limit debit per share based on net initial credit received."""
net_credit_per_share = net_credit_received / (100.0 * self.qty)
return round(net_credit_per_share * (1.0 - profit_target_pct), 2)
def close_spread(self, client:TradingClient, base_percent_debt:float):
my_ords = self.find_existing_orders(client)
if my_ords:
self.adjust_existing_orders(client, my_ords, base_percent_debt)
else:
self.place_sell_order(client, base_percent_debt)
def place_sell_order(self, client:TradingClient, base_percent_debt:float):
net_credit_received = abs(self.short_leg.cost_basis) - abs(self.long_leg.cost_basis)
debt_lim = (net_credit_received/ 100) * base_percent_debt
debt_lim = round(debt_lim, 2)
assert debt_lim > 0.0
leg_buy_to_close = OptionLegRequest(
symbol=self.short_leg.symbol,
ratio_qty=1,
side=OrderSide.BUY,
position_intent=PositionIntent.BUY_TO_CLOSE)
leg_sell_to_close = OptionLegRequest(
symbol=self.long_leg.symbol,
ratio_qty=1,
side=OrderSide.SELL,
position_intent=PositionIntent.SELL_TO_CLOSE)
# Construct Multi-Leg Limit Order (Net Debit)
mleg_order_req = LimitOrderRequest(
qty=self.qty,
time_in_force=TimeInForce.GTC,
order_class=OrderClass.MLEG,
legs=[leg_buy_to_close, leg_sell_to_close],
limit_price=debt_lim)
try:
print("===============================================================================\n")
print(f"Submitting limit close for lim={debt_lim} order={self.underlying} exp={self.expiration}")
order_resp = client.submit_order(mleg_order_req)
print("✅ [CLOSE ORDER SUCCESSFULLY PLACED]")
print(f" Order ID: {order_resp.id}")
print(f" Status: {order_resp.status}")
print(f" Submitted At: {order_resp.submitted_at}")
print("===============================================================================\n")
except Exception as err:
print(f"❌ Close order submission failed at Alpaca: {err}")
print("===============================================================================\n")
def display(self, opclient, trdclient):
formatted_exp = f"20{self.expiration[:2]}-{self.expiration[2:4]}-{self.expiration[4:]}"
# Fetch live quote data & calculate true P/L
(
mid_debit_per_share,
net_credit_received,
total_liq_cost,
true_unrealized_pl,
) = self.calculate_pl_and_liq(opclient)
target_debit = self.target_closing_debit(net_credit_received, 50.0)
pl_pct = (true_unrealized_pl / net_credit_received * 100) if net_credit_received > 0 else 0.0
pl_sign = "+" if true_unrealized_pl >= 0 else ""
my_ords = self.find_existing_orders(trdclient)
mycurord = my_ords[0].limit_price if my_ords else "NA"
print(
f"| {self.underlying} Bull Put Spread ({self.qty} Contract{'s' if self.qty > 1 else ''})"
)
print(f" Expiration: {formatted_exp}")
print(
f" Short Leg: {self.short_leg.symbol} Sell {self.qty}x ${self.short_leg.strike:.2f} Put"
)
print(
f" Long Leg: {self.long_leg.symbol} Buy {self.qty}x ${self.long_leg.strike:.2f} Put"
)
print(
f" Net Credit: ${net_credit_received / (100 * self.qty):.2f}/sh (${net_credit_received:.2f} total)"
)
print(
f" Net Liquidity: ${mid_debit_per_share:.2f}/sh (${total_liq_cost:.2f} total cost to BTC @ Mid)"
)
print(
f" Real P/L: {pl_sign}${true_unrealized_pl:.2f} ({pl_sign}{pl_pct:.1f}% of max credit)"
)
print(
f" Available:{self.available} CurrentClose: {mycurord}"
)
print("=" * 80)
def parse_occ_symbol_data(symbol: str) -> Tuple[str, str, str, float]:
"""
Parses OCC option symbols from right to left to support variable-length tickers.
Format: [Ticker][YYMMDD][C/P][8-digit Strike]
"""
strike_raw = symbol[-8:]
option_type = symbol[-9]
exp_raw = symbol[-15:-9]
underlying = symbol[:-15]
strike = int(strike_raw) / 1000.0
return underlying, exp_raw, option_type, strike
def reconstruct_bull_put_spreads(positions) -> List[BullPutSpread]:
grouped_positions: Dict[Tuple[str, str], List[OptionPosition]] = {}
for pos in positions:
if len(pos.symbol) < 15:
continue
underlying, exp, opt_type, strike = parse_occ_symbol_data(pos.symbol)
if opt_type != "P":
continue
opt_pos = OptionPosition(
symbol=pos.symbol,
underlying=underlying,
expiration=exp,
option_type=opt_type,
strike=strike,
qty=int(float(pos.qty)),
cost_basis=float(pos.cost_basis),
available=pos.qty_available != '0'
)
key = (underlying, exp)
grouped_positions.setdefault(key, []).append(opt_pos)
spreads: List[BullPutSpread] = []
for (underlying, exp), pos_list in grouped_positions.items():
shorts = sorted(
[p for p in pos_list if p.qty < 0], key=lambda x: x.strike, reverse=True
)
longs = sorted(
[p for p in pos_list if p.qty > 0], key=lambda x: x.strike, reverse=True
)
for short_put in shorts:
available_short_qty = abs(short_put.qty)
if available_short_qty == 0:
continue
for long_put in longs:
available_long_qty = long_put.qty
if available_long_qty == 0 or long_put.strike >= short_put.strike:
continue
matched_qty = min(available_short_qty, available_long_qty)
# Calculate proportional cost basis for exact contract matching
short_ratio = matched_qty / abs(short_put.qty)
long_ratio = matched_qty / long_put.qty
short_leg_matched = OptionPosition(
symbol=short_put.symbol,
underlying=short_put.underlying,
expiration=short_put.expiration,
option_type=short_put.option_type,
strike=short_put.strike,
qty=-matched_qty,
cost_basis=short_put.cost_basis * short_ratio,
available=short_put.available
)
long_leg_matched = OptionPosition(
symbol=long_put.symbol,
underlying=long_put.underlying,
expiration=long_put.expiration,
option_type=long_put.option_type,
strike=long_put.strike,
qty=matched_qty,
cost_basis=long_put.cost_basis * long_ratio,
available=long_put.available
)
spreads.append(
BullPutSpread(
underlying=underlying,
expiration=exp,
short_leg=short_leg_matched,
long_leg=long_leg_matched,
qty=matched_qty,
available=short_put.available and long_put.available
)
)
short_put.qty += matched_qty # short_put.qty is negative
long_put.qty -= matched_qty
break
return spreads
def is_occ_symbol(symbol):
# Ticker (1-6 letters) + YYMMDD + C/P + 8-digit Strike
pattern = r"^[A-Z]{1,6}\d{6}[CP]\d{8}$"
return bool(re.fullmatch(pattern, symbol))
def format_occ_symbol(ticker: str, exp_date_str: str, option_type: str, strike: float) -> str:
"""Helper to format standardized 21-character OCC Option Symbols."""
d = datetime.strptime(exp_date_str, "%Y-%m-%d")
yy = d.strftime("%y")
mm = d.strftime("%m")
dd = d.strftime("%d")
strike_int = int(round(float(strike) * 1000))
return f"{ticker.upper()}{yy}{mm}{dd}{option_type.upper()}{strike_int:08d}"
def parse_occ_symbol(occ_symbol: str):
"""Parses standard OCC option ticker into (ticker, expiration_date, option_type, strike_price)."""
pattern = r"^([A-Z]{1,6})(\d{2})(\d{2})(\d{2})([CP])(\d{8})$"
match = re.match(pattern, occ_symbol.strip().upper())
if not match:
return None
ticker, yy, mm, dd, otype, strike_raw = match.groups()
exp_date = f"20{yy}-{mm}-{dd}"
strike = float(strike_raw) / 1000.0
return {
"occ":occ_symbol,
"ticker": ticker,
"exp_date": exp_date,
"type": otype,
"strike": strike
}
def retrieve_orders(client, symbols):
current_until = datetime.now(timezone.utc).isoformat()
all_orders = []
while True:
orders_request = GetOrdersRequest(
status=QueryOrderStatus.OPEN,
nested=True,
limit=500,
symbols=symbols,
until=current_until,
direction="desc"
)
chunk = client.get_orders(filter=orders_request)
if not chunk:
break
all_orders.extend(chunk)
current_until = chunk[-1].submitted_at.isoformat()
return all_orders
def get_snapshot_opt(client, symbol):
return client.get_option_snapshot(OptionSnapshotRequest(symbol_or_symbols=symbol))[symbol]
def parse_arguments():
parser = argparse.ArgumentParser(description="Alpaca Stock CLI")
parser.add_argument('--acc', type=str, help='Account identifier or name', required=True)
subparsers = parser.add_subparsers(
dest='command',
required=True, # Makes a subcommand mandatory
title='Commands',
metavar='COMMAND'
)
power = subparsers.add_parser('power', help='Check buying power', description='Check buying power')
orders = subparsers.add_parser('orders', help='current open orders', description='current open orders')
orders.add_argument('--sym', type=str, help='symbol to check', required=True)
owned = subparsers.add_parser('owned', help='current positions', description='current positions')
owned.add_argument('--long-str', type=str, help='string match against ticker', default="")
owned.add_argument('--short-str', type=str, help='string match against ticker', default="")
owned.add_argument('--place-close', type=float, help='percent based close')
owned.add_argument('--available', action='store_true', help='show only available pos')
searcher = subparsers.add_parser('search', help='find spreads to invest in', description='list puts')
searcher.add_argument('--tick', type=str, help='Stock Symbol', required=True)
searcher.add_argument("--type", type=str, default="PUT", choices=["PUT"], help="Option type: PUT (default: PUT)")
searcher.add_argument('--dte-max', type=int, help='day range', default=8)
searcher.add_argument('--dte-min', type=int, help='day begin', default=1)
searcher.add_argument('--str-max', type=float, help='strike max', default=0.98)
searcher.add_argument('--str-min', type=float, help='strike min', default=0.70)
searcher.add_argument('--str-width', type=int, default=1)
searcher.add_argument('--cred-nat', type=float, help='strike nat min', default=0.0)
searcher.add_argument('--cred-mid', type=float, help='strike mid min', default=0.0)
searcher.add_argument('--lim', type=int, help='max number to show', default=0)
searcher.add_argument('--submit-orders', type=str, default="NONE", choices=["NONE", "NAT", "MID", "HALF", "MID34"], help='submit buy orders for these spreads')
# todo make this on argment about mid or nat and a diff of a float from them
args = parser.parse_args()
# check command via arg.command
return args
class CreditSpread(object):
def __init__(self, sell_sym, buy_sym, cred_nat, cred_mid, theta):
self.sell_sym = sell_sym
self.buy_sym = buy_sym
self.cred_nat = cred_nat
self.cred_mid = cred_mid
self.theta = theta
def __repr__(self):
return f" short={self.sell_sym} long={self.buy_sym} mid={self.cred_mid} nat={self.cred_nat} theta={self.theta}"
def place_limit_order(self, client, credit_amount, qty = 1):
credit_amount = round(credit_amount, 2)
if credit_amount >= 0:
credit_amount = -credit_amount
leg_sold = OptionLegRequest(
symbol=self.sell_sym,
ratio_qty=1,
side=OrderSide.SELL,
position_intent=PositionIntent.SELL_TO_OPEN
)
# Leg 2: Buy Long Put to Open
leg_bought = OptionLegRequest(
symbol=self.buy_sym,
ratio_qty=1,
side=OrderSide.BUY,
position_intent=PositionIntent.BUY_TO_OPEN
)
assert credit_amount < 0
mleg_order_req = LimitOrderRequest(
qty=qty,
time_in_force=TimeInForce.DAY,
order_class=OrderClass.MLEG,
legs=[leg_sold, leg_bought],
limit_price=credit_amount
)
try:
print(f"Submitting Order at cred={credit_amount} " + str(self))
order_resp = client.submit_order(mleg_order_req)
print("✅ [ORDER SUCCESSFULLY PLACED]")
print(f" Order ID: {order_resp.id}")
print(f" Status: {order_resp.status}")
print(f" Submitted At: {order_resp.submitted_at}")
print("=========================================================================\n")
except Exception as err:
print(f"❌ Order submission failed at Alpaca: {err}")
print("=========================================================================\n")
def nat_limit_order(self, client, qty = 1):
self.place_limit_order(client, self.cred_nat, qty)
def mid_limit_order(self, client, qty = 1):
self.place_limit_order(client, self.cred_mid, qty)
def half_limit_order(self, client, qty = 1):
self.place_limit_order(client, (self.cred_mid + self.cred_nat) / 2, qty)
def three4_limit_order(self, client, qty = 1):
self.place_limit_order(client, self.cred_mid * 0.75, qty)
class OwnedCreditSpread(object):
def __init__(self, sh_sym, lo_sym, pl, basis, qty, delta_pc, theta_pc, delta_total, theta_total, cur_ord = None):
self.sh_sym = sh_sym
self.lo_sym = lo_sym
self.pl = pl
self.basis = basis
self.qty = qty
self.delta_pc = delta_pc
self.theta_pc = theta_pc
self.delta_total = delta_total
self.theta_total = theta_total
self.cur_ord = cur_ord
def active_sell_id(self):
return self.cur_ord.id if self.cur_ord is not None else None
def matches_sh(self, term):
return term in self.sh_sym
def matches_lo(self, term):
return term in self.lo_sym
def __repr__(self):
return (
f" short={self.sh_sym} long={self.lo_sym} qty={self.qty} "
f"pl={self.pl} cost={self.basis} liq={self.basis + self.pl} "
f"net_delta={self.delta_pc} net_theta={self.theta_pc} "
f"(Total Delta: {self.delta_total} shares, Total Theta: ${self.theta_total}/day) "
f"CurOrder={self.cur_ord.limit_price if self.cur_ord is not None else 'N'}"
)
def place_sell_order(self, client, base_percent_debt):
if self.cur_ord is not None:
id_to_delete = self.active_sell_id
if self.cur_ord.status != OrderStatus.PENDING_CANCEL:
client.cancel_order_by_id(id_to_delete)
debt_lim = -1 * (self.basis / 100) * base_percent_debt
debt_lim = round(debt_lim, 2)
assert debt_lim > 0.0
leg_buy_to_close = OptionLegRequest(
symbol=self.sh_sym,
ratio_qty=1,
side=OrderSide.BUY,
position_intent=PositionIntent.BUY_TO_CLOSE)
leg_sell_to_close = OptionLegRequest(
symbol=self.lo_sym,
ratio_qty=1,
side=OrderSide.SELL,
position_intent=PositionIntent.SELL_TO_CLOSE)
# Construct Multi-Leg Limit Order (Net Debit)
mleg_order_req = LimitOrderRequest(
qty=self.qty,
time_in_force=TimeInForce.GTC,
order_class=OrderClass.MLEG,
legs=[leg_buy_to_close, leg_sell_to_close],
limit_price=debt_lim)
try:
print("===============================================================================\n")
print(f"Submitting limit close for lim={debt_lim} order={str(self)}")
order_resp = client.submit_order(mleg_order_req)
print("✅ [CLOSE ORDER SUCCESSFULLY PLACED]")
print(f" Order ID: {order_resp.id}")
print(f" Status: {order_resp.status}")
print(f" Submitted At: {order_resp.submitted_at}")
print("===============================================================================\n")
except Exception as err:
print(f"❌ Close order submission failed at Alpaca: {err}")
print("===============================================================================\n")
def handle_orders(argobj):
trade_client = TradingClient(api_key=account_credentials[argobj.acc]["API"], secret_key=account_credentials[argobj.acc]["SECRET"], paper=argobj.acc == "paper")
got_ords = retrieve_orders(trade_client, [argobj.sym])
for order in got_ords:
if order.order_class == OrderClass.MLEG and len(order.legs) == 2:
print(f"price={order.limit_price} {order.legs[0].position_intent}={order.legs[0].symbol} {order.legs[1].position_intent}={order.legs[1].symbol} status={order.status}")
def handle_owned(argobj):
trade_client = TradingClient(api_key=account_credentials[argobj.acc]["API"], secret_key=account_credentials[argobj.acc]["SECRET"], paper=argobj.acc == "paper")
optclient = OptionHistoricalDataClient(account_credentials[argobj.acc]["API"], account_credentials[argobj.acc]["SECRET"])
all_positions = [ pos for pos in trade_client.get_all_positions() if pos.asset_class == AssetClass.US_OPTION]
mybps = reconstruct_bull_put_spreads(all_positions)
if argobj.available:
mybps = [bp for bp in mybps if bp.available]
for bp in mybps:
if bp.matches_lo(argobj.long_str) and bp.matches_sh(argobj.short_str):
if argobj.place_close is not None:
bp.close_spread(trade_client, argobj.place_close)
else:
bp.display(optclient, trade_client)
def handle_power(argobj):
trade_client = TradingClient(api_key=account_credentials[argobj.acc]["API"], secret_key=account_credentials[argobj.acc]["SECRET"], paper=argobj.acc == "paper")
account = trade_client.get_account()
print(f"POWER with_margin={account.buying_power} non_margin={account.non_marginable_buying_power} overnight={account.regt_buying_power} fees={account.accrued_fees} maint={account.maintenance_margin} equity={account.equity}")
def handle_search(argobj):
trade_client = TradingClient(api_key=account_credentials[argobj.acc]["API"], secret_key=account_credentials[argobj.acc]["SECRET"], paper=argobj.acc == "paper")
client = OptionHistoricalDataClient(account_credentials[argobj.acc]["API"], account_credentials[argobj.acc]["SECRET"])
current_date = datetime.now() + timedelta(days=argobj.dte_min)
end_date = datetime.now() + timedelta(days=argobj.dte_max)
data_client = StockHistoricalDataClient(account_credentials[argobj.acc]["API"], account_credentials[argobj.acc]["SECRET"])
request_params = StockSnapshotRequest(
symbol_or_symbols=argobj.tick,
feed=DataFeed.SIP
)
snapshot = data_client.get_stock_snapshot(request_params)
stock_data = snapshot[argobj.tick]
# latest_close_price = stock_data.minute_bar.close
latest_ask_price = stock_data.latest_quote.ask_price
latest_bid_price = stock_data.latest_quote.bid_price
mid_price = round((latest_ask_price + latest_bid_price) / 2, 2)
max_strike = mid_price * argobj.str_max
min_strike = mid_price * argobj.str_min
print(f"----[{argobj.tick}]----")
print(f"mid_price={mid_price} min={min_strike} max={max_strike} tick={argobj.tick}")
req = OptionChainRequest(underlying_symbol=argobj.tick, feed=OptionsFeed.OPRA,
expiration_date_gte=current_date.date().isoformat(), expiration_date_lte=end_date.date().isoformat(),
type=ContractType.PUT, strike_price_lte=max_strike, strike_price_gte=min_strike)
resp = client.get_option_chain(req)
chains = list(resp.values())
chains.sort(key=lambda x: x.symbol)
chains.reverse()
crspreads = []
print(len(chains))
for i in range(len(chains) - argobj.str_width):
short_opt = chains[i]
long_opt = chains[i + argobj.str_width]
short_key = short_opt.symbol[:short_opt.symbol.index('P')]
long_key = long_opt.symbol[:long_opt.symbol.index('P')]
if short_key != long_key:
print(f"passing due to key {short_key} {long_key}")
continue
nat_credit = round(float(short_opt.latest_quote.bid_price) - float(long_opt.latest_quote.ask_price), 2)
short_mid = (float(short_opt.latest_quote.bid_price) + float(short_opt.latest_quote.ask_price)) / 2
long_mid = (float(long_opt.latest_quote.bid_price) + float(long_opt.latest_quote.ask_price)) / 2
mid_credit = round(short_mid - long_mid, 2)
if mid_credit < argobj.cred_mid:
continue
if argobj.cred_nat != 0.0 and nat_credit < argobj.cred_nat:
continue
short_theta = short_opt.greeks.theta if short_opt.greeks else 0.0
long_theta = long_opt.greeks.theta if long_opt.greeks else 0.0
net_theta = round((-short_theta + long_theta) * 100, 4)
crspreads.append(CreditSpread(short_opt.symbol, long_opt.symbol, nat_credit, mid_credit, net_theta))
proc_lim = argobj.lim if argobj.lim > 0 and argobj.lim <= len(crspreads) else len(crspreads)
for i in range(proc_lim):
print(crspreads[i])
if argobj.submit_orders == "NAT":
crspreads[i].nat_limit_order(trade_client)
elif argobj.submit_orders == "MID":
crspreads[i].mid_limit_order(trade_client)
elif argobj.submit_orders == "HALF":
crspreads[i].half_limit_order(trade_client)
elif argobj.submit_orders == "MID34":
crspreads[i].three4_limit_order(trade_client)
handler_functions = {
"owned":handle_owned,
"search":handle_search,
"orders":handle_orders,
"power":handle_power
}
if __name__ == '__main__':
args = parse_arguments()
my_func = handler_functions[args.command]
my_func(args)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment