예제 #1
0
    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)
예제 #2
0
    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()