def update(self): ''' The main function that gets called every X contains all the child table updating functions Args: None Returns: None ''' def _success(worker): #Called when one of the workers is successfully completed return def _error(worker): #Called if there was an error logging.error('~~~~ Error with the {} ~~~~'.format(worker)) def _midCheck(): ''' Monitors the middle man list for unfilled orders Args: None Returns: None ''' headers = {'Accept': 'application/json', 'Authorization' : self.trader.headers['Authorization']} for tick in self.midTicks: if tick.transID: try: url = 'https://api.robinhood.com/orders' + '/' + tick.transID[1] res = requests.get(url, headers = headers).json() if tick.transID[0] == 'sell': if res['state'] in ['partially_filled', 'filled', 'confirmed']: self.sell(tick, fromMidPrice = float(res['price'])) else: if res['state'] in ['partially_filled', 'filled']: self.purchase(tick, fromMidPrice = float(res['price'])) except Exception as e: logging.info('~~~~ Mid Check Error: {} ~~~~'.format(e)) def _tickUpdate(curList): ''' Updates the tick objects in the respective list Args: curList (str): string name of list that is being updated Returns: None ''' listDict = {'Hold' : self.hTicks, 'Queue' : self.qTicks} tickData = robinTicks(self.trader, [tick.T for tick in listDict[curList]], self.afterHours()) if len(tickData) != len(listDict[curList]): logging.error('~~~~ {} and Fetch Lengths Do Not Match ~~~~'.format(curList)) return else: for tickDict in tickData: try: idx = [tick.T for tick in listDict[curList]].index(tickDict['Sym']) except ValueError: return listDict[curList][idx].update( data = tickDict['Data'], purPrice = self.purPrice.value(), spy = self.spy ) def _queueCall(): ''' Performs all the necessaries for the Queue table, is put in a worker and executes in the background Args: None Returns: None ''' if len(self.qTicks): _tickUpdate('Queue') #If actually trading, iterate through Queue and if the projected cost doesn't exceed budget see if #it meets purchasing criteria, else just update if not self.startBut.isEnabled(): for tick in self.qTicks: logging.info('Queue {}'.format(tick.T)) transPrice = tick.C * tick.PQ try: if self._dtCost + transPrice < self.budget and transPrice < float(self.buyingPower.text()) and transPrice < float(self.cash.text()): if tick.toBuy( purPrice = self.purPrice.value(), spy = self.spy ): self._executeOrder(tick, orderType = 'Buy') except TypeError: pass def _holdCall(): ''' Performs all the necessaries for the Holdings table, is put in a worker and executes in the background Args: None Returns: None ''' if len(self.hTicks): _tickUpdate('Hold') if not self.startBut.isEnabled(): for tick in self.hTicks: if tick.tradeable: logging.info('Hold {}'.format(tick.T)) if tick.toSell( purPrice = self.purPrice.value(), spy = self.spy ): self._executeOrder(tick, 'Sell') #Robinhood portfolio and account info, creates an empty one if an error is thrown #such as having 0 in the portfolio try: self.portfolio = self.trader.portfolios() self.account = self.trader.get_account()['margin_balances'] except IndexError: logging.info('~~~~ Portfolio Empty ~~~~') self.portfolio = { 'equity' : 0, 'extended_hours_equity' : 0, } self.account = { 'unsettled_funds' : 0, 'start_of_day_dtbp' : 0, 'unallocated_margin_cash': 0 } except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError, TimeoutError) as e: logging.info('~~~~ Connection Error: {} ~~~~'.format(e)) return #Updates the market tracker bar self.marketBar(fetchMarkets()) now = datetime.datetime.now(self.tz).time() #Set the Equity to current value depending on if it's aH or not if self.afterHours(now): self.equity.setText('%.2f' % (float(self.portfolio['extended_hours_equity']))) #Disable Trading aH if not TESTING: if not self.startBut.isEnabled(): self.tradeActs() else: self.equity.setText('%.2f' % (float(self.portfolio['equity']))) if self.portfolio['equity']: #Plt that stuff if it's during the trading day self.graphData[0].append(now.strftime('%H:%M:%S')) self.graphData[1].append(float(self.portfolio['equity'])) xdict = dict(enumerate(self.graphData[0])) ax = self.graph.getAxis('bottom') ax.setTicks([xdict.items()]) self.graph.plot(list(xdict.keys()), self.graphData[1], pen = self.ePen, clear = False) self.buyingPower.setText('%.2f' % (float(self.account['start_of_day_dtbp']))) self.cash.setText('%.2f' % (float(self.account['unallocated_margin_cash']))) self.uFund.setText('%.2f' % (float(self.account['unsettled_funds']))) if not TESTING: if not self.startBut.isEnabled(): #If end of day approaching, close out all positions regardless of profit if now > datetime.time(hour = 15, minute = 58, second = 0, tzinfo = self.tz): self.dump() #Safety-net for SEC guideline of >25000 on Non-Margin for day trading if self.marginSpin.value() < float(self.equity.text()) < self.marginSpin.value() + 100: if self.notYetWarned: self.warn('Near Thresh') self.notYetWarned = False if float(self.equity.text()) < self.marginSpin.value(): logging.info('~~~~ Equity Fell Below Threshold ~~~~') self.warn('Below Thresh') self.tradeActs() self.purPrice.setMaximum(float(self.cash.text())) else: #Allow for dumping of stocks at end of the day if just testing, if testing AH doesn't auto dump if not self.startBut.isEnabled(): if self.startTime < datetime.time(hour = 16, minute = 0, second = 0, tzinfo = self.tz): if now > datetime.time(hour = 15, minute = 58, second = 0, tzinfo = self.tz): self.dump() if len(self.hTicks) > 0: holdWorker = Worker(_holdCall) holdWorker.signals.finished.connect(lambda : _success('Hold')) holdWorker.signals.error.connect(lambda : _error('Hold')) self.pool.start(holdWorker) self.hModel.layoutChanged.emit() self.holding.viewport().update() #Only calls the update function if there's stuff in the table, saves memory if len(self.qTicks) > 0: queueWorker = Worker(_queueCall) queueWorker.signals.finished.connect(lambda : _success('Queue')) queueWorker.signals.error.connect(lambda : _error('Queue')) self.pool.start(queueWorker) self.qModel.layoutChanged.emit() self.queue.viewport().update() if len(self.midTicks) > 0: midWorker = Worker(_midCheck) midWorker.signals.finished.connect(lambda : _success('Middle')) midWorker.signals.error.connect(lambda : _error('Middle')) self.pool.start(midWorker)
def startup(self, data): ''' Defines the appropriate columns and types for the tables Also defines the models for the tables Args: data (dict): dictionary of config file Returns: None ''' qproperties = [ {'attr' : 'T', 'header' : 'Ticker'}, {'attr' : 'C', 'header' : 'Price'}, {'attr' : 'PQ', 'header' : 'Qty to Buy'}, ] hproperties = [ {'attr' : 'T', 'header' : 'Ticker'}, {'attr' : 'C', 'header' : 'Price'}, {'attr' : 'Q', 'header' : 'Quantity'}, {'attr' : 'AP', 'header' : 'Avg Price'}, {'attr' : 'SL', 'header' : 'Stop Loss'}, {'attr' : 'tradeable', 'header' : 'Tradeable'} ] #These models are neat because they actually contain the Tick objects themselves, not just #the object's data. When adding to a table, you're adding the actual Tick object to it self.qModel = ObjListTableModel(self.qTicks, qproperties, isRowObjects = True, isDynamic = True) self.hModel = ObjListTableModel(self.hTicks, hproperties, isRowObjects = True, isDynamic = True, templateObject = Tick()) self.holding.setModel(self.hModel) self.queue.setModel(self.qModel) #Sets the budget initial value self.budgetHandler(self.budgetBox.value()) #Initialization time of KStock self.startTime = datetime.datetime.now(self.tz).time() #Sets the market bar data labels self.marketBar(fetchMarkets()) #Gathers all current Robinhood holdings, this is mostly for if the program crashes #mid-day so it can pick back up where it left off positions = self.trader.positions()['results'] if positions: logging.info('Previous Items in Robinhood Found, Adding Them') for pos in positions: inst = self.trader.instrument(pos['instrument'].split('/')[-2]) if float(pos['quantity']) > 0: if inst['symbol'] not in [tick.T for tick in self.hTicks]: ticker = Tick(inst['symbol'], self.purPrice.value(), self.trader, self.spy) ticker.tradeable = False buyPrice = float(pos['average_buy_price']) if buyPrice > 1: buyPrice = round(buyPrice, 2) sl = round(buyPrice - (buyPrice * 0.1), 2) else: sl = buyPrice - (buyPrice * 0.1) rhood = ( int(float(pos['quantity'])), buyPrice, sl ) self.hTicks.append(ticker) ticker.toBuy( purPrice = self.purPrice, spy = self.spy, forced = True, rhood = rhood ) self.totalCost.setText('%.2f' % (float(self.totalCost.text()) + float(ticker.Q * ticker.AP))) for tick in set(data['Queue']): if tick not in [ticker.T for ticker in self.hTicks]: self.qTicks.append(Tick(tick, self.purPrice.value(), self.trader, self.spy)) self.qModel.layoutChanged.emit()