def find_eligible_contracts(self, symbol, right, min_strike=0, excluded_expirations=[]): click.echo() click.secho( f"Searching option chain for symbol={symbol} right={right}, this can take a while...", fg="green", ) click.echo() stock = Stock(symbol, "SMART", currency="USD") contracts = self.ib.qualifyContracts(stock) [ticker] = self.ib.reqTickers(stock) tickerValue = ticker.marketPrice() chains = self.ib.reqSecDefOptParams(stock.symbol, "", stock.secType, stock.conId) chain = next(c for c in chains if c.exchange == "SMART") def valid_strike(strike): if right.startswith("P"): return strike <= tickerValue if right.startswith("C"): return strike >= tickerValue and strike >= min_strike return False chain_expirations = self.config["option_chains"]["expirations"] strikes = sorted(strike for strike in chain.strikes if valid_strike(strike)) expirations = sorted( exp for exp in chain.expirations if option_dte(exp) >= self.config["target"]["dte"] and exp not in excluded_expirations)[:chain_expirations] rights = [right] def nearest_strikes(strikes): chain_strikes = self.config["option_chains"]["strikes"] if right.startswith("P"): return strikes[-chain_strikes:] if right.startswith("C"): return strikes[:chain_strikes] contracts = [ Option( symbol, expiration, strike, right, "SMART", tradingClass=chain.tradingClass, ) for right in rights for expiration in expirations for strike in nearest_strikes(strikes) ] contracts = self.ib.qualifyContracts(*contracts) tickers = self.ib.reqTickers(*contracts) # Filter out tickers which don't have a midpoint price tickers = [t for t in tickers if not util.isNan(t.midpoint())] def open_interest_is_valid(ticker): ticker = self.ib.reqMktData(ticker.contract, genericTickList="101") def open_interest_is_not_ready(): if right.startswith("P"): return util.isNan(ticker.putOpenInterest) if right.startswith("C"): return util.isNan(ticker.callOpenInterest) try: while_n_times( open_interest_is_not_ready, lambda: self.ib.waitOnUpdate(timeout=5), 25, ) except: return False self.ib.cancelMktData(ticker.contract) # The open interest value is never present when using historical # data, so just ignore it when the value is None if right.startswith("P"): return (ticker.putOpenInterest >= self.config["target"]["minimum_open_interest"]) if right.startswith("C"): return (ticker.callOpenInterest >= self.config["target"]["minimum_open_interest"]) def delta_is_valid(ticker): return (ticker.modelGreeks and not util.isNan(ticker.modelGreeks.delta) and abs(ticker.modelGreeks.delta) <= get_target_delta( self.config, symbol, right)) # Filter by delta and open interest tickers = [ticker for ticker in tickers if delta_is_valid(ticker)] tickers = [ ticker for ticker in tickers if open_interest_is_valid(ticker) ] tickers = sorted( reversed(sorted(tickers, key=lambda t: abs(t.modelGreeks.delta))), key=lambda t: option_dte(t.contract.lastTradeDateOrContractMonth), ) if len(tickers) == 0: raise RuntimeError( f"No valid contracts found for {symbol}. Aborting.") # Return the first result return tickers[0]
# asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) from ib_insync.contract import Stock, Forex, Index, Option, Future, CFD asyncio.get_event_loop().set_debug(True) util.logToConsole(logging.DEBUG) ib = IB() ib.connect('127.0.0.1', 7497, clientId=21) aex = Index('EOE', 'FTA') eurusd = Forex('EURUSD') intc = Stock('INTC', 'SMART', 'USD', primaryExchange='NASDAQ') amd = Stock('AMD', 'SMART', 'USD') aapl = Stock('AAPL', 'SMART', 'USD') tsla = Stock('TSLA', 'SMART', 'USD') spy = Stock('SPY', 'ARCA') wrongContract = Forex('lalala') option = Option('EOE', '20171215', 490, 'P', 'FTA', multiplier=100) if 0: cds = ib.reqContractDetails(aex) print(cds) cd = cds[0] print(cd) conId = cd.summary.conId ib.qualifyContracts(aex, eurusd, intc) print(aex, eurusd, intc) print(ib.reqContractDetails(wrongContract)) if 0: sub = ScannerSubscription(instrument='FUT.US', locationCode='FUT.GLOBEX', scanCode='TOP_PERC_GAIN') print(ib.reqScannerData(sub, [])) print(len(ib.reqScannerParameters()))
def find_eligible_contracts(self, symbol, right): stock = Stock(symbol, "SMART", currency="USD") contracts = self.ib.qualifyContracts(stock) [ticker] = self.ib.reqTickers(stock) tickerValue = ticker.marketPrice() chains = self.ib.reqSecDefOptParams( stock.symbol, "", stock.secType, stock.conId ) chain = next(c for c in chains if c.exchange == "SMART") def valid_strike(strike): if right.startswith("P"): return strike <= tickerValue if right.startswith("C"): return strike >= tickerValue return False chain_expirations = self.config["option_chains"]["expirations"] strikes = sorted(strike for strike in chain.strikes if valid_strike(strike)) expirations = sorted( exp for exp in chain.expirations if option_dte(exp) >= self.config["target"]["dte"] )[:chain_expirations] rights = [right] def nearest_strikes(strikes): chain_strikes = self.config["option_chains"]["strikes"] if right.startswith("P"): return strikes[-chain_strikes:] if right.startswith("C"): return strikes[:chain_strikes] contracts = [ Option( symbol, expiration, strike, right, "SMART", tradingClass=chain.tradingClass, ) for right in rights for expiration in expirations for strike in nearest_strikes(strikes) ] contracts = self.ib.qualifyContracts(*contracts) tickers = self.ib.reqTickers(*contracts) def open_interest_is_valid(ticker): ticker = self.ib.reqMktData(ticker.contract, genericTickList="101") while util.isNan(ticker.putOpenInterest) or util.isNan( ticker.callOpenInterest ): self.ib.waitOnUpdate(timeout=2) self.ib.cancelMktData(ticker.contract) # The open interest value is never present when using historical # data, so just ignore it when the value is None if right.startswith("P"): return ( ticker.putOpenInterest >= self.config["target"]["minimum_open_interest"] ) if right.startswith("C"): return ( ticker.callOpenInterest >= self.config["target"]["minimum_open_interest"] ) def delta_is_valid(ticker): return ( ticker.modelGreeks and ticker.modelGreeks.delta and abs(ticker.modelGreeks.delta) <= self.config["target"]["delta"] ) # Filter by delta and open interest tickers = [ticker for ticker in tickers if delta_is_valid(ticker)] tickers = [ticker for ticker in tickers if open_interest_is_valid(ticker)] tickers = sorted( reversed(sorted(tickers, key=lambda t: abs(t.modelGreeks.delta))), key=lambda t: option_dte(t.contract.lastTradeDateOrContractMonth), ) if len(tickers) == 0: raise RuntimeError(f"No valid contracts found for {symbol}. Aborting.") return tickers[0]
def find_eligible_contracts( self, symbol, primary_exchange, right, strike_limit, exclude_expirations_before=None, exclude_first_exp_strike=None, ): click.echo() click.secho( f"Searching option chain for symbol={symbol} " f"right={right}, this can take a while...", fg="green", ) click.echo() stock = Stock(symbol, "SMART", currency="USD", primaryExchange=primary_exchange) self.ib.qualifyContracts(stock) ticker = self.get_ticker_for(stock) tickerValue = ticker.marketPrice() chains = self.get_chains_for_stock(stock) chain = next(c for c in chains if c.exchange == "SMART") def valid_strike(strike): if right.startswith("P") and strike_limit: return strike <= tickerValue and strike <= strike_limit elif right.startswith("P"): return strike <= tickerValue elif right.startswith("C") and strike_limit: return strike >= tickerValue and strike >= strike_limit elif right.startswith("C"): return strike >= tickerValue return False chain_expirations = self.config["option_chains"]["expirations"] min_dte = (option_dte(exclude_expirations_before) if exclude_expirations_before else 0) strikes = sorted(strike for strike in chain.strikes if valid_strike(strike)) expirations = sorted( exp for exp in chain.expirations if option_dte(exp) >= self.config["target"]["dte"] and option_dte(exp) >= min_dte)[:chain_expirations] rights = [right] def nearest_strikes(strikes): chain_strikes = self.config["option_chains"]["strikes"] if right.startswith("P"): return strikes[-chain_strikes:] if right.startswith("C"): return strikes[:chain_strikes] contracts = [ Option( symbol, expiration, strike, right, "SMART", # tradingClass=chain.tradingClass, ) for right in rights for expiration in expirations for strike in nearest_strikes(strikes) ] contracts = self.ib.qualifyContracts(*contracts) # exclude strike, but only for the first exp if exclude_first_exp_strike: contracts = [ c for c in contracts if (c.lastTradeDateOrContractMonth == expirations[0] and c.strike != exclude_first_exp_strike) or c.lastTradeDateOrContractMonth != expirations[0] ] tickers = self.get_ticker_list_for(tuple(contracts)) # Filter out contracts which don't have a midpoint price tickers = [t for t in tickers if not util.isNan(t.midpoint())] def open_interest_is_valid(ticker): def open_interest_is_not_ready(): if right.startswith("P"): return util.isNan(ticker.putOpenInterest) if right.startswith("C"): return util.isNan(ticker.callOpenInterest) try: wait_n_seconds( open_interest_is_not_ready, lambda: self.ib.waitOnUpdate(timeout=15), API_RESPONSE_WAIT_TIME, ) except RuntimeError: click.secho( f"Timeout waiting on market data for " f"{ticker.contract}. Continuing...", fg="yellow", ) return False finally: self.ib.cancelMktData(ticker.contract) # The open interest value is never present when using historical # data, so just ignore it when the value is None if right.startswith("P"): return (ticker.putOpenInterest >= self.config["target"]["minimum_open_interest"]) if right.startswith("C"): return (ticker.callOpenInterest >= self.config["target"]["minimum_open_interest"]) def delta_is_valid(ticker): return (ticker.modelGreeks and not util.isNan(ticker.modelGreeks.delta) and ticker.modelGreeks.delta is not None and abs(ticker.modelGreeks.delta) <= get_target_delta( self.config, symbol, right)) def price_is_valid(ticker): return not util.isNan(ticker.midpoint()) or not util.isNan( ticker.marketPrice()) # Filter out tickers without prices tickers = [ticker for ticker in tickers if price_is_valid(ticker)] # Filter by delta and open interest tickers = [ticker for ticker in tickers if delta_is_valid(ticker)] # Fetch market data tickers = [ self.ib.reqMktData(ticker.contract, genericTickList="101") for ticker in tickers ] # Fetch open interest tickers = [ ticker for ticker in tickers if open_interest_is_valid(ticker) ] # Sort by delta first, then expiry date tickers = sorted( reversed(sorted(tickers, key=lambda t: abs(t.modelGreeks.delta))), key=lambda t: option_dte(t.contract.lastTradeDateOrContractMonth), ) if len(tickers) == 0: raise RuntimeError( f"No valid contracts found for {symbol}. Aborting.") # Return the first result return tickers[0]