├── README.md ├── RiskyOptionsBot.py └── requirements.txt /README.md: -------------------------------------------------------------------------------- 1 | # RiskyOptionsBot 2 | Risky Options Bot (Python, Interactive Brokers) Buy 2 DTE SPY Contracts on 3 consecutive 5-min higher closes and profit target on next bar 3 | 4 | ## Instructions 5 | 1. git clone https://github.com/Jake0303/RiskyOptionsBot 6 | 2. pip install -r requirements.txt in RiskyOptionsBot folder. 7 | 3. Make sure you have TWS installed and port is 7496. 8 | 4. Run RiskyOptionsBot.py 9 | -------------------------------------------------------------------------------- /RiskyOptionsBot.py: -------------------------------------------------------------------------------- 1 | # Jacob Amaral Youtube 2 | # https://www.youtube.com/channel/UC3GoIgz6agdJzFNZWJrIR6g 3 | #Imports 4 | from datetime import datetime 5 | from ib_insync import * 6 | from apscheduler.schedulers.background import BackgroundScheduler 7 | import asyncio 8 | 9 | class RiskyOptionsBot: 10 | """ 11 | Risky Options Bot (Python, Interactive Brokers) 12 | 13 | Buy 2 DTE SPY Contracts on 3 consecutive 5-min higher closes and profit target on next bar 14 | """ 15 | #Initialize variables 16 | def __init__(self): 17 | print("Options Bot Running, connecting to IB ...") 18 | #Connect to IB 19 | try: 20 | self.ib = IB() 21 | self.ib.connect('127.0.0.1',7496,clientId=1) 22 | print("Successfully connected to IB") 23 | except Exception as e: 24 | print(str(e)) 25 | # Create SPY Contract 26 | self.underlying = Stock('SPY', 'SMART', 'USD') 27 | self.ib.qualifyContracts(self.underlying) 28 | print("Backfilling data to catchup ...") 29 | # Request Streaming bars 30 | self.data = self.ib.reqHistoricalData(self.underlying, 31 | endDateTime='', 32 | durationStr='2 D', 33 | barSizeSetting='1 min', 34 | whatToShow='TRADES', 35 | useRTH=False, 36 | keepUpToDate=True,) 37 | 38 | #Local vars 39 | self.in_trade = False 40 | 41 | #Get current options chains 42 | self.chains = self.ib.reqSecDefOptParams(self.underlying.symbol, '', self.underlying.secType, self.underlying.conId) 43 | #Update Chains every hour - can't update chains in event loop causes asyncio issues 44 | update_chain_scheduler = BackgroundScheduler(job_defaults={'max_instances': 2}) 45 | update_chain_scheduler.add_job(func=self.update_options_chains,trigger='cron', hour='*') 46 | update_chain_scheduler.start() 47 | print("Running Live") 48 | # Set callback function for streaming bars 49 | self.data.updateEvent += self.on_bar_update 50 | self.ib.execDetailsEvent += self.exec_status 51 | #Run forever 52 | self.ib.run() 53 | #Update options chains 54 | def update_options_chains(self): 55 | try: 56 | loop = asyncio.new_event_loop() 57 | asyncio.set_event_loop(loop) 58 | print("Updating options chains") 59 | #Get current options chains 60 | self.chains = self.ib.reqSecDefOptParams(self.underlying.symbol, '', self.underlying.secType, self.underlying.conId) 61 | print(self.chains) 62 | except Exception as e: 63 | print(str(e)) 64 | #On Bar Update, when we get new data 65 | def on_bar_update(self, bars: BarDataList, has_new_bar: bool): 66 | try: 67 | if has_new_bar: 68 | #Convert BarDataList to pandas Dataframe 69 | df = util.df(bars) 70 | # Check if we are in a trade 71 | if not self.in_trade: 72 | print("Last Close : " + str(df.close.iloc[-1])) 73 | #Check for 3 Consecutive Highs 74 | if df.close.iloc[-1] > df.close.iloc[-2] and df.close.iloc[-2] > df.close.iloc[-3]: 75 | #Found 3 consecutive higher closes get call contract that's $5 higher than underlying 76 | for optionschain in self.chains: 77 | for strike in optionschain.strikes: 78 | if strike > df.close.iloc[-1] + 5 : #Make sure the strike is $5 away so it's cheaper 79 | print("Found 3 consecutive higher closers, entering trade.") 80 | self.options_contract = Option(self.underlying.symbol, optionschain.expirations[1], strike, 'C', 'SMART', tradingClass=self.underlying.symbol) 81 | # We are not in a trade - Let's enter a trade 82 | options_order = MarketOrder('BUY', 1,account=self.ib.wrapper.accounts[-1]) 83 | trade = self.ib.placeOrder(self.options_contract, options_order) 84 | self.lastEstimatedFillPrice = df.close.iloc[-1] 85 | self.in_trade = not self.in_trade 86 | return # important so it doesn't keep looping 87 | else: #We are in a trade 88 | if df.close.iloc[-1] > self.lastEstimatedFillPrice: 89 | #Sell for profit scalping 90 | print("Scalping profit.") 91 | options_order = MarketOrder('SELL', 1,account=self.ib.wrapper.accounts[-1]) 92 | trade = self.ib.placeOrder(self.options_contract, options_order) 93 | except Exception as e: 94 | print(str(e)) 95 | #Order Status 96 | def exec_status(self,trade: Trade,fill: Fill): 97 | print("Filled") 98 | 99 | #Instantiate Class to get things rolling 100 | RiskyOptionsBot() 101 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | APScheduler==3.7.0 2 | ib_insync==0.9.69 3 | --------------------------------------------------------------------------------