コード例 #1
0
ファイル: ticker.py プロジェクト: ajmal017/btre_23rdfeb
 def hasBidAsk(self) -> bool:
     """
     See if this ticker has a valid bid and ask.
     """
     return (
         self.bid != -1 and not isNan(self.bid) and self.bidSize > 0 and
         self.ask != -1 and not isNan(self.ask) and self.askSize > 0)
コード例 #2
0
    def exit_positions(self):
        portfolio = self.get_portfolio()
        trades = self.get_trades()
        curr_date, curr_dt = get_datetime_for_logging()
        if self._nope_value > self.config["nope"]["long_exit"]:
            held_calls = self.get_held_contracts(portfolio, 'C')
            existing_call_order_ids = self.get_existing_order_ids(trades, 'C', 'SELL')
            remaining_calls = list(filter(lambda c: c['contract'].conId not in existing_call_order_ids, held_calls))

            if len(remaining_calls) > 0:
                remaining_calls.sort(key=lambda c: c['contract'].conId)
                remaining_call_contracts = [c['contract'] for c in remaining_calls]
                qualified_contracts = self.ib.qualifyContracts(*remaining_call_contracts)
                tickers = self.ib.reqTickers(*qualified_contracts)
                tickers.sort(key=lambda t: t.contract.conId)
                for idx, ticker in enumerate(tickers):
                    price = midpoint_or_market_price(ticker)
                    if not util.isNan(price):
                        quantity = remaining_calls[idx]['position']
                        order = LimitOrder("SELL", quantity, price,
                                           algoStrategy="Adaptive",
                                           algoParams=[TagValue(tag='adaptivePriority', value='Normal')],
                                           tif="DAY")
                        call_contract = ticker.contract
                        self.wait_for_trade_submitted(self.ib.placeOrder(call_contract, order))
                        with open(f"logs/{curr_date}-trade.txt", "a") as f:
                            f.write(f'Sold {quantity} {call_contract.strike}C{call_contract.lastTradeDateOrContractMonth} ({remaining_calls[idx]["avg"]} average) for {price * 100} each, {self._nope_value} | {self._underlying_price} | {curr_dt}\n')
                    else:
                        with open("logs/errors.txt", "a") as f:
                            f.write(f'Error selling call at {self._nope_value} | {self._underlying_price} | {curr_dt}\n')
        if self._nope_value < self.config["nope"]["short_exit"]:
            held_puts = self.get_held_contracts(portfolio, 'P')
            existing_put_order_ids = self.get_existing_order_ids(trades, 'P', 'SELL')
            remaining_puts = list(filter(lambda c: c['contract'].conId not in existing_put_order_ids, held_puts))

            if len(remaining_puts) > 0:
                remaining_puts.sort(key=lambda c: c['contract'].conId)
                remaining_put_contracts = [c['contract'] for c in remaining_puts]
                qualified_contracts = self.ib.qualifyContracts(*remaining_put_contracts)
                tickers = self.ib.reqTickers(*qualified_contracts)
                tickers.sort(key=lambda t: t.contract.conId)
                for idx, ticker in enumerate(tickers):
                    price = midpoint_or_market_price(ticker)
                    if not util.isNan(price):
                        quantity = remaining_puts[idx]['position']
                        order = LimitOrder("SELL", quantity, price,
                                           algoStrategy="Adaptive",
                                           algoParams=[TagValue(tag='adaptivePriority', value='Normal')],
                                           tif="DAY")
                        put_contract = ticker.contract
                        self.wait_for_trade_submitted(self.ib.placeOrder(put_contract, order))
                        with open(f"logs/{curr_date}-trade.txt", "a") as f:
                            f.write(f'Sold {quantity} {put_contract.strike}P{put_contract.lastTradeDateOrContractMonth} ({remaining_puts[idx]["avg"]} average) for {price * 100} each, {self._nope_value} | {self._underlying_price} | {curr_dt}\n')
                    else:
                        with open("logs/errors.txt", "a") as f:
                            f.write(f'Error selling put at {self._nope_value} | {self._underlying_price} | {curr_dt}\n')
コード例 #3
0
 def enter_positions(self):
     portfolio = self.get_portfolio()
     trades = self.get_trades()
     curr_date, curr_dt = get_datetime_for_logging()
     if self._nope_value < self.config["nope"]["long_enter"]:
         held_calls = self.get_total_position(portfolio, 'C')
         existing_order_quantity = self.get_total_buys(trades, 'C')
         total_buys = held_calls + existing_order_quantity
         if total_buys < self.config["nope"]["call_limit"]:
             contracts = self.find_eligible_contracts(self.SYMBOL, 'C')
             # TODO: Implement contract selection from eligible candidiates
             contract_to_buy = contracts[self.config["nope"]["call_strike_offset"]]
             qualified_contracts = self.ib.qualifyContracts(contract_to_buy)
             tickers = self.ib.reqTickers(*qualified_contracts)
             if len(tickers) > 0:
                 price = midpoint_or_market_price(tickers[0])
                 call_contract = qualified_contracts[0]
                 if not util.isNan(price):
                     quantity = self.config["nope"]["call_quantity"]
                     order = LimitOrder('BUY', quantity, price,
                                        algoStrategy="Adaptive",
                                        algoParams=[TagValue(tag='adaptivePriority', value='Normal')],
                                        tif="DAY")
                     self.wait_for_trade_submitted(self.ib.placeOrder(call_contract, order))
                     with open(f"logs/{curr_date}-trade.txt", "a") as f:
                         f.write(f'Bought {quantity} {call_contract.strike}C{call_contract.lastTradeDateOrContractMonth} for {price * 100} each, {self._nope_value} | {self._underlying_price} | {curr_dt}\n')
                 else:
                     with open("logs/errors.txt", "a") as f:
                         f.write(f'Error buying call at {self._nope_value} | {self._underlying_price} | {curr_dt}\n')
     elif self._nope_value > self.config["nope"]["short_enter"]:
         held_puts = self.get_total_position(portfolio, 'P')
         existing_order_quantity = self.get_total_buys(trades, 'P')
         total_buys = held_puts + existing_order_quantity
         if total_buys < self.config["nope"]["put_limit"]:
             contracts = self.find_eligible_contracts(self.SYMBOL, 'P')
             # TODO: Implement contract selection from eligible candidates
             contract_to_buy = contracts[-self.config["nope"]["put_strike_offset"] - 1]
             qualified_contracts = self.ib.qualifyContracts(contract_to_buy)
             tickers = self.ib.reqTickers(*qualified_contracts)
             if len(tickers) > 0:
                 price = midpoint_or_market_price(tickers[0])
                 put_contract = qualified_contracts[0]
                 if not util.isNan(price):
                     quantity = self.config["nope"]["put_quantity"]
                     order = LimitOrder('BUY', quantity, price,
                                        algoStrategy="Adaptive",
                                        algoParams=[TagValue(tag='adaptivePriority', value='Normal')],
                                        tif="DAY")
                     self.wait_for_trade_submitted(self.ib.placeOrder(put_contract, order))
                     with open(f"logs/{curr_date}-trade.txt", "a") as f:
                         f.write(f'Bought {quantity} {put_contract.strike}P{put_contract.lastTradeDateOrContractMonth} for {price * 100} each, {self._nope_value} | {self._underlying_price} | {curr_dt}\n')
                 else:
                     with open("logs/errors.txt", "a") as f:
                         f.write(f'Error buying put at {self._nope_value} | {self._underlying_price} | {curr_dt}\n')
コード例 #4
0
ファイル: util.py プロジェクト: hchen651/thetagang
def midpoint_or_market_price(ticker):
    # As per the ib_insync docs, marketPrice returns the last price first, but
    # we often prefer the midpoint over the last price. This function pulls the
    # midpoint first, then falls back to marketPrice() if midpoint is nan.
    if util.isNan(ticker.midpoint()):
        if util.isNan(ticker.marketPrice()):
            # Fallback to the model price
            return ticker.modelGreeks.optPrice
        else:
            return ticker.marketPrice()

    return ticker.midpoint()
コード例 #5
0
ファイル: ticker.py プロジェクト: dimitar-petrov/ib_insync
 def marketPrice(self):
     """
     Return the first available one of
     
     * last price
     * average of bid and ask
     * close price
     """
     price = self.last
     if isNan(price):
         price = (self.bid + self.ask) / 2
     if isNan(price):
         price = self.close
     return price
コード例 #6
0
 def buy_contracts(self, right):
     action = "BUY"
     contracts = self.find_eligible_contracts(self.SYMBOL, right)
     ticker = self.select_contract(contracts, right)
     if ticker is not None:
         price = midpoint_or_market_price(ticker)
         quantity = (self.config["nope"]["call_quantity"] if right == "C"
                     else self.config["nope"]["put_quantity"])
         if not util.isNan(price) and self.check_acc_balance(
                 price, quantity):
             contract = ticker.contract
             order = LimitOrder(
                 action,
                 quantity,
                 price,
                 algoStrategy="Adaptive",
                 algoParams=[
                     TagValue(tag="adaptivePriority", value="Normal")
                 ],
                 tif="DAY",
             )
             self.cancel_order_type("SELL", "STP")
             trade = self.ib.placeOrder(contract, order)
             trade.filledEvent += log_fill
             trade.filledEvent += self.on_buy_fill
             self.log_order(contract, quantity, price, action)
         else:
             with open("logs/errors.txt", "a") as f:
                 f.write(
                     f"Error buying {right} at {self._nope_value} | {self._underlying_price}\n"
                 )
コード例 #7
0
ファイル: ticker.py プロジェクト: mchi/ib_insync
 def marketPrice(self):
     """
     Return the first available one of
     
     * last price if within current bid/ask;
     * average of bid and ask (midpoint);
     * close price.
     """
     midpoint = (self.bid + self.ask) / 2
     price = self.last if (isNan(midpoint)
                           or self.bid <= self.last <= self.ask) else nan
     if isNan(price):
         price = midpoint
     if isNan(price) or price == -1:
         price = self.close
     return price
コード例 #8
0
ファイル: nope_strategy.py プロジェクト: phall1/ib_nope
 def buy_contracts(self, right):
     action = "BUY"
     contracts = self.find_eligible_contracts(self.SYMBOL, right)
     # TODO: Improve contract selection https://github.com/ajhpark/ib_nope/issues/21
     offset = (self.config["nope"]["call_strike_offset"] if right == "C"
               else -self.config["nope"]["put_strike_offset"] - 1)
     contract_to_buy = contracts[offset]
     qualified_contracts = self.ib.qualifyContracts(contract_to_buy)
     tickers = self.ib.reqTickers(*qualified_contracts)
     if len(tickers) > 0:
         price = midpoint_or_market_price(tickers[0])
         if not util.isNan(price):
             contract = qualified_contracts[0]
             quantity = (self.config["nope"]["call_quantity"] if right
                         == "C" else self.config["nope"]["put_quantity"])
             order = LimitOrder(
                 action,
                 quantity,
                 price,
                 algoStrategy="Adaptive",
                 algoParams=[
                     TagValue(tag="adaptivePriority", value="Normal")
                 ],
                 tif="DAY",
             )
             trade = self.ib.placeOrder(contract, order)
             trade.filledEvent += log_fill
             self.log_order(contract, quantity, price, action)
         else:
             with open("logs/errors.txt", "a") as f:
                 f.write(
                     f"Error buying {right} at {self._nope_value} | {self._underlying_price}\n"
                 )
コード例 #9
0
        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"])
コード例 #10
0
 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)
     )
コード例 #11
0
ファイル: ticker.py プロジェクト: ajmal017/btre_23rdfeb
 def _on_timer(self, time):
     if self.bars:
         bar = self.bars[-1]
         if isNan(bar.close) and len(self.bars) > 1:
             bar.open = bar.high = bar.low = bar.close = \
                 self.bars[-2].close
         self.bars.updateEvent.emit(self.bars, True)
         self.emit(bar)
     self.bars.append(Bar(time))
コード例 #12
0
 def wait_for_market_price(self, ticker):
     try:
         while_n_times(
             lambda: util.isNan(ticker.marketPrice()),
             lambda: self.ib.waitOnUpdate(timeout=5),
             25,
         )
     except:
         return False
     return True
コード例 #13
0
 def wait_for_market_price(self, ticker):
     try:
         wait_n_seconds(
             lambda: util.isNan(ticker.marketPrice()),
             lambda: self.ib.waitOnUpdate(timeout=15),
             60,
         )
     except:
         return False
     return True
コード例 #14
0
 def wait_for_midpoint_price(self, ticker):
     try:
         wait_n_seconds(
             lambda: util.isNan(ticker.midpoint()),
             lambda: self.ib.waitOnUpdate(timeout=15),
             60,
         )
     except RuntimeError:
         return False
     return True
コード例 #15
0
ファイル: wrapper.py プロジェクト: vishalbelsare/ib_insync
 def tickSize(self, reqId: int, tickType: int, size: float):
     ticker = self.reqId2Ticker.get(reqId)
     if not ticker:
         self._logger.error(f'tickSize: Unknown reqId: {reqId}')
         return
     price = -1.0
     # https://interactivebrokers.github.io/tws-api/tick_types.html
     if tickType in (0, 69):
         if size == ticker.bidSize:
             return
         price = ticker.bid
         ticker.prevBidSize = ticker.bidSize
         ticker.bidSize = size
     elif tickType in (3, 70):
         if size == ticker.askSize:
             return
         price = ticker.ask
         ticker.prevAskSize = ticker.askSize
         ticker.askSize = size
     elif tickType in (5, 71):
         price = ticker.last
         if isNan(price):
             return
         if size != ticker.lastSize:
             ticker.prevLastSize = ticker.lastSize
             ticker.lastSize = size
     elif tickType in (8, 74):
         ticker.volume = size
     elif tickType == 21:
         ticker.avVolume = size
     elif tickType == 27:
         ticker.callOpenInterest = size
     elif tickType == 28:
         ticker.putOpenInterest = size
     elif tickType == 29:
         ticker.callVolume = size
     elif tickType == 30:
         ticker.putVolume = size
     elif tickType == 34:
         ticker.auctionVolume = size
     elif tickType == 36:
         ticker.auctionImbalance = size
     elif tickType == 61:
         ticker.regulatoryImbalance = size
     elif tickType == 86:
         ticker.futuresOpenInterest = size
     elif tickType == 87:
         ticker.avOptionVolume = size
     elif tickType == 89:
         ticker.shortableShares = size
     if price or size:
         tick = TickData(self.lastTime, tickType, price, size)
         ticker.ticks.append(tick)
     ticker.marketDataType = self.reqId2MarketDataType.get(reqId, 0)
     self.pendingTickers.add(ticker)
コード例 #16
0
ファイル: ticker.py プロジェクト: ajmal017/btre_23rdfeb
 def on_source(self, time, price, size):
     if not self.bars:
         return
     bar = self.bars[-1]
     if isNan(bar.open):
         bar.open = bar.high = bar.low = price
     bar.high = max(bar.high, price)
     bar.low = min(bar.low, price)
     bar.close = price
     bar.volume += size
     bar.count += 1
     self.bars.updateEvent.emit(self.bars, False)
コード例 #17
0
ファイル: ticker.py プロジェクト: dwt1020/ib_insync
 def __repr__(self):
     attrs = {}
     for k, d in self.__class__.defaults.items():
         v = getattr(self, k)
         if v != d and not isNan(v):
             attrs[k] = v
     if self.ticks:
         # ticks can grow too large to display
         attrs.pop('ticks')
     clsName = self.__class__.__name__
     kwargs = ', '.join(f'{k}={v!r}' for k, v in attrs.items())
     return f'{clsName}({kwargs})'
コード例 #18
0
    def get_ticker_list_for(self, contracts):
        ticker_list = self.ib.reqTickers(*contracts)

        try:
            wait_n_seconds(
                lambda: any([util.isNan(t.midpoint()) for t in ticker_list]),
                lambda: self.ib.waitOnUpdate(timeout=15),
                API_RESPONSE_WAIT_TIME,
            )
        except RuntimeError:
            pass

        return ticker_list
コード例 #19
0
ファイル: ticker.py プロジェクト: ajmal017/btre_23rdfeb
    def marketPrice(self) -> float:
        """
        Return the first available one of

        * last price if within current bid/ask;
        * average of bid and ask (midpoint);
        * close price.
        """
        price = self.last if (
            self.hasBidAsk() and self.bid <= self.last <= self.ask) else \
            self.midpoint()
        if isNan(price):
            price = self.close
        return price
コード例 #20
0
ファイル: nope_strategy.py プロジェクト: phall1/ib_nope
    def sell_held_contracts(self, right):
        portfolio = self.get_portfolio()
        trades = self.get_trades()
        action = "SELL"

        held_contracts_info = self.get_held_contracts_info(portfolio, right)
        existing_contract_order_ids = self.get_existing_order_ids(
            trades, right, action)
        remaining_contracts_info = list(
            filter(
                lambda c: c["contract"].conId not in
                existing_contract_order_ids,
                held_contracts_info,
            ))

        if len(remaining_contracts_info) > 0:
            remaining_contracts_info.sort(key=lambda c: c["contract"].conId)
            remaining_contracts = [
                c["contract"] for c in remaining_contracts_info
            ]
            qualified_contracts = self.ib.qualifyContracts(
                *remaining_contracts)
            tickers = self.ib.reqTickers(*qualified_contracts)
            tickers.sort(key=lambda t: t.contract.conId)
            for idx, ticker in enumerate(tickers):
                price = midpoint_or_market_price(ticker)
                avg = remaining_contracts_info[idx]["avg"]
                if not util.isNan(price):
                    quantity = remaining_contracts_info[idx]["position"]
                    order = LimitOrder(
                        action,
                        quantity,
                        price,
                        algoStrategy="Adaptive",
                        algoParams=[
                            TagValue(tag="adaptivePriority", value="Normal")
                        ],
                        tif="DAY",
                    )
                    contract = ticker.contract
                    trade = self.ib.placeOrder(contract, order)
                    trade.filledEvent += log_fill
                    self.log_order(contract, quantity, price, action, avg)
                else:
                    with open("logs/errors.txt", "a") as f:
                        f.write(
                            f"Error selling {right} at {self._nope_value} | {self._underlying_price}\n"
                        )
コード例 #21
0
ファイル: wrapper.py プロジェクト: toledy/ib_insync
 def tickSize(self, reqId, tickType, size):
     ticker = self.reqId2Ticker.get(reqId)
     if not ticker:
         self._logger.error(f'tickSize: Unknown reqId: {reqId}')
         return
     ticker.time = self.lastTime
     price = -1.0
     # https://interactivebrokers.github.io/tws-api/tick_types.html
     if tickType in (0, 69):
         price = ticker.bid
         if size != ticker.bidSize:
             ticker.prevBidSize = ticker.bidSize
             ticker.bidSize = size
     elif tickType in (3, 70):
         price = ticker.ask
         if size != ticker.askSize:
             ticker.prevAskSize = ticker.askSize
             ticker.askSize = size
     elif tickType in (5, 71):
         price = ticker.last
         if util.isNan(price):
             return
         if size != ticker.lastSize:
             ticker.prevLastSize = ticker.lastSize
             ticker.lastSize = size
     elif tickType in (8, 74):
         ticker.volume = size
     elif tickType == 21:
         ticker.avVolume = size
     elif tickType == 27:
         ticker.callOpenInterest = size
     elif tickType == 28:
         ticker.putOpenInterest = size
     elif tickType == 29:
         ticker.callVolume = size
     elif tickType == 30:
         ticker.putVolume = size
     elif tickType == 86:
         ticker.futuresOpenInterest = size
     if price or size:
         tick = TickData(self.lastTime, tickType, price, size)
         ticker.ticks.append(tick)
         self.pendingTickers.add(ticker)
コード例 #22
0
    def _get_market_prices(ib, contracts):

        # tickers = ib.reqTickers(*contracts)

        # Alternative to get live tickers
        for contract in contracts:
            ib.reqMktData(contract, '', False, False)

        # print('Waiting for tickers')
        ib.sleep(1)
        tickers = [ib.ticker(contract) for contract in contracts]
        # print(tickers)

        mkt_prices = [
            ticker.last
            if ticker.marketPrice() == ticker.close else ticker.marketPrice()
            for ticker in tickers
        ]
        if any([True for item in mkt_prices if isNan(item)]):
            mkt_prices = [ticker.marketPrice() for ticker in tickers]

        return mkt_prices
コード例 #23
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]
コード例 #24
0
    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]
コード例 #25
0
 def open_interest_is_not_ready():
     if right.startswith("P"):
         return util.isNan(ticker.putOpenInterest)
     if right.startswith("C"):
         return util.isNan(ticker.callOpenInterest)
コード例 #26
0
def midpoint_or_market_price(ticker):
    if util.isNan(ticker.midpoint()):
        return round(ticker.marketPrice(), 2)

    return round(ticker.midpoint(), 2)
コード例 #27
0
 def price_is_valid(ticker):
     return not util.isNan(ticker.midpoint()) or not util.isNan(
         ticker.marketPrice())
コード例 #28
0
 def wait_for_midpoint_price(self, ticker):
     while_n_times(
         lambda: util.isNan(ticker.midpoint()),
         lambda: self.ib.waitOnUpdate(timeout=3),
         10,
     )
コード例 #29
0
 def wait_for_market_price(self, ticker):
     while_n_times(
         lambda: util.isNan(ticker.marketPrice()),
         lambda: self.ib.waitOnUpdate(timeout=2),
         10,
     )
コード例 #30
0
 def delta_is_valid(ticker):
     return (ticker.modelGreeks
             and not util.isNan(ticker.modelGreeks.delta)
             and abs(ticker.modelGreeks.delta) <=
             self.config["target"]["delta"])