def take_and_stop(self, symbol): last_quote_url = self.quote_url + symbol r = requests.get(last_quote_url, headers=self.headers, timeout=5) debug_logger.debug("""API called by take_and_stop() for '{}'""".format(symbol)) quote = json.loads(r.content) bid = quote['last']['bidprice'] ask = quote['last']['askprice'] take_profit = { 'limit_price': str((bid / 100) * 130) } stop_loss = { 'stop_price': str(round((ask / 100) * 90, 2)), 'limit_price': str(round((ask / 100) * 88, 2)) } return take_profit, stop_loss
def get_all_yahoo_watchlist_symbols(url_list): """ From a list of watchlist urls, retrieve symbols for potential stocks according to basic criteria """ global yahoo_home_url watchlist_symbols = set() stocks_analyzed = 0 for url in url_list: debug_logger.debug("Analyzing '{}'".format(url)) watchlist_url = yahoo_home_url + url watchlist_r = requests.get(watchlist_url).text watchlist_soup = BeautifulSoup(watchlist_r, 'lxml') watchlist_table = watchlist_soup.find(class_='cwl-symbols').tbody.contents for row in watchlist_table: filter_stocks(watchlist_symbols, row) stocks_analyzed += 1 sleep(0.2) debug_logger.debug("A Total of {} stocks analyzed".format(stocks_analyzed)) return watchlist_symbols
def place_order(self, symbol, side, qty, type='stop_limit', time_in_force='gtc', order_class='bracket'): params = { 'symbol': symbol, 'side': side, 'qty': str(qty), 'type': type, 'time_in_force': time_in_force, 'order_class': order_class, 'take_profit': self.take_and_stop(symbol)[0], 'stop_loss': self.take_and_stop(symbol)[1] } if self.is_tradable(symbol): try: r = requests.post(self.orders_url, params=params, headers=self.headers, timeout=5) except HTTPError: return False debug_logger.debug("""API called to place order for '{}'""".format(symbol)) return True
def get_tactical_data(self): """ - Get data for the second timeframe - Calculate stochastic values for the given time windows - Return last 20 rows """ if self.tactical_timeframe == '60Min': data = self.get_data('15Min', limit=0) data_resampled = self.alpaca_data_resample(data) tactical = self.get_stochastic(data_resampled) debug_logger.debug("Calculated Stochastic for '{}'".format( self.symbol)) return tactical.iloc[-20:] else: data = self.get_data(self.tactical_timeframe, limit=0) tactical = self.get_stochastic(data) debug_logger.debug("Calculated Stochastic for '{}'".format( self.symbol)) return tactical.iloc[-20:]
def get_tactical_potential(self): self.potential = 0 # Initialize potential signal tactical_data = self.get_tactical_data() debug_logger.debug("get_tactical_data() called for '{}'".format( self.symbol)) last_k = tactical_data['k'].iloc[-1] last_d = tactical_data['d'].iloc[-1] stock_logger.info("'{}' Last K: {}".format(self.symbol, last_k)) stock_logger.info("'{}' Last D: {}".format(self.symbol, last_d)) if self.is_in_range(last_k, last_d): # Weak buy signal self.potential = 1 if last_k >= last_d: # Strong buy signal self.potential = 2 stock_logger.info("'{}' potential is now: {}".format( self.symbol, self.potential))
def filter_stocks(symbol_set, row): """ RULES OUT: - stocks already in list - penny stocks - too expensive stocks - stocks with negative change Also, double checks for Crypto symbols (separated by '-') """ stock = {} stock['symbol'] = row.find('a')['title'] if '-' not in stock['symbol']: try: stock['price'] = float(row.find(class_='data-col2').text) stock['change'] = float(row.find(class_='data-col3').span.text) except (ValueError, AttributeError): pass else: if (20 < stock['price'] < 800 and stock['change'] > 0.5): symbol_set.add(stock['symbol']) debug_logger.debug("Added '{}' to watchlist".format(stock['symbol'])) return symbol_set
def close_position(self, symbol): params = { 'symbol': symbol, } r = requests.delete(positions_url, params=params, headers=self.headers, timeout=5) debug_logger.debug("""API called to close position for '{}'""".format(symbol))
def get_watchlist_symbols(self): r = requests.get(self.watchlist_url, headers=self.headers, timeout=5) debug_logger.debug("API called for watchlist") watchlist = json.loads(r.content) watchlist_symbols = {asset['symbol'] for asset in watchlist['assets']} return watchlist_symbols
def yahoo_watchlist(): global yahoo_home_url top_gainers_url = yahoo_home_url + '/watchlists/category/section-gainers' url_list = get_yahoo_watchlist_urls(top_gainers_url) symbol_set = get_all_yahoo_watchlist_symbols(url_list) debug_logger.debug("A total of {} stocks added to watchlist".format(len(symbol_set))) return symbol_set
def get_positions_symbols(self): r = requests.get(self.positions_url, headers=self.headers, timeout=5) debug_logger.debug("API called for positions") positions = json.loads(r.content) positions_symbols = {asset['symbol'] for asset in positions['assets']} return positions_symbols
def get_open_position(self): url = 'https://paper-api.alpaca.markets/v2/positions' params = { 'symbol': self.symbol, } r = requests.get(url, params=params, headers=self.headers, timeout=5) debug_logger.debug("API called for positions") position = json.loads(r.content) position['cost_basis'] = float(position['cost_basis']) position['unrealized_plpc'] = float(position['unrealized_plpc']) return position
def is_tradable(self, symbol): asset_url = self.assets_url + symbol r = requests.get(asset_url, headers=self.headers, timeout=5) debug_logger.debug("""API called by is_tradable() for '{}'""".format(symbol)) asset = json.loads(r.content) if (asset['status'] == 'active' and asset['tradable'] == True): return True else: return False
def initialize_data(): """ Create a dict of: - initial set of Stock objects from: - Manually created Alpaca watchlist - Automatic selection of stocks from Yahoo Finance watchlists - 3 empty sets to be populated by stocks that: - show strong potential after trend scan - show weak potential after tactical scan - show strong potential after tactical scan, are ready to buy - a set of dictionaries containing information on open positions - a list of completed trades to be saved and later analized """ trades = {} trades[datetime.now().strftime('%Y-%m-%d')] = set() a = Alpaca() watchlist = a.get_watchlist_symbols() watchlist.update(yahoo_watchlist()) stock_logger.info("Initialized watchlist with '{}' symbols".format( len(watchlist))) stocks = { 'initial': {Stock(symbol) for symbol in watchlist}, 'potential': set(), 'standby': set(), 'buy': set(), 'bought': record.get_open_positions(), 'trades': trades } debug_logger.debug("Created stocks dictionary") return stocks
def get_execution_potential(self): self.potential = 0 # Initialize potential signal execution_data = self.get_execution_data() debug_logger.debug("get_execution_data() called for '{}'".format( self.symbol)) last_three_highs = execution_data['high'].iloc[-3:].values stock_logger.info("'{}' Last 3 highs: {}".format( self.symbol, last_three_highs)) if self.is_trending_up(last_three_highs): # Strong buy signal self.potential = 2 stock_logger.info("'{}' potential is now: {}".format( self.symbol, self.potential))
def get_trend_potential(self): trend_data = self.get_trend_data() debug_logger.debug("get_trend_data() called for '{}'".format( self.symbol)) last_month_smas = trend_data['sma50'].iloc[-30:].values last_lows = trend_data['low'].iloc[-6:].values last_low = trend_data['low'].iloc[-1] last_sma = trend_data['sma50'].iloc[-1] if (trend_data['close'].ge(trend_data['sma50']).all() and self.is_trending_up(last_month_smas, step=10) and not self.is_trending_up(last_lows) and self.is_in_range(last_low, last_sma)): self.potential = 2 stock_logger.info("'{}' potential is now: {}".format( self.symbol, self.potential))
def standby_scan(stocks, lock, sleep_time=120): """ Re-scan the tactical timeframe for stocks that showed weak potential """ while market_open(): lock.acquire() debug_logger.debug("Lock acquired by standby_scan()") for stock in stocks['standby']: if not stock.open: stock.get_tactical_potential() debug_logger.debug( "get_tactical_potential() called for '{}'".format( stock.symbol)) if stock.potential == 2: stocks['buy'].add(stock) sleep(1) lock.release() debug_logger.debug("Lock released by standby_scan()") stock_logger.info("{} have potential after standby scan".format( len(stocks['buy']))) sleep(sleep_time)
def trend_scan(stocks, lock, sleep_time=1800): """ Scan the trend data timeframe for potential, then populate the potential stocks set and discard the stock if it shows no potential """ while market_open(): lock.acquire() debug_logger.debug("Lock acquired by trend_scan()") for stock in stocks['initial']: if not stock.open: stock.get_trend_potential() debug_logger.debug( "get_trend_potential() called for '{}'".format( stock.symbol)) if stock.potential == 2: stocks['potential'].add(stock) sleep(0.2) lock.release() debug_logger.debug("Lock released by trend_scan()") stock_logger.info("{} stocks have potential after trend scan".format( len(stocks['potential']))) sleep(sleep_time)
def execute_scan(stocks, lock, sleep_time=60): """ - Scan price action to find the optimal moment for placing BUY order - Place order - Create dictionary with information on the position """ while market_open(): lock.acquire() debug_logger.debug("Lock acquired by execute_scan()") for stock in stocks['buy']: if not stock.open: stock.get_execution_potential() debug_logger.debug( "get_execution_potential() called for '{}'".format( stock.symbol)) if stock.potential == 2: a = Alpaca() # TO-DO! Add functionality to calculate optimal position? # Temporarily, an arbitrary amount of 10 shares is established if a.place_order(stock.symbol, 'buy', 10): stock.open_position() stocks['bought'].add(stock) stock_logger.info( "Placed order of 10 stocks of '{}'".format( stock.symbol)) else: continue sleep(2) lock.release() debug_logger.debug("Lock released by execute_scan()") stock_logger.info("Stocks of {} symbol were bought".format( len(stocks['bought']))) sleep(sleep_time)
def tactical_scan(stocks, lock, sleep_time=600): """ Scan the tactical timeframe data of each stock for potential, discard the stock if it shows no potential, then populate the standby set or buy set """ while market_open(): lock.acquire() debug_logger.debug("Lock acquired by tactical_scan()") for stock in stocks['potential']: if not stock.open: stock.get_tactical_potential() debug_logger.debug( "get_tactical_potential() called for '{}'".format( stock.symbol)) if stock.potential == 2: stocks['buy'].add(stock) elif stock.potential == 1: stocks['standby'].add(stock) sleep(1) lock.release() debug_logger.debug("Lock released by tactical_scan()") stock_logger.info( "{} stocks have potential after tactical scan".format( len(stocks['buy']))) stock_logger.info( "{} stocks remain in standby after tactical scan".format( len(stocks['standby']))) sleep(sleep_time)
def sell_scan(stocks, lock, sleep_time=300): """ Scans open position's unrealized profit for optimal sell signal """ while market_open(): lock.acquire() debug_logger.debug("Lock acquired by sell_scan()") for stock in stocks['bought']: stock.get_sell_signal() debug_logger.debug("get_sell_signal() called for '{}'".format( stock.symbol)) if stock.sell: a = Alpaca() a.close_position(stock.symbol) stock.close_position() stock_logger.info( "Closed position of 10 stocks of '{}'".format( stock.symbol)) stocks['trades'].add(stock.position_record) else: continue sleep(2) lock.release() debug_logger.debug("Lock released by sell_scan()") stock_logger.info("{} stocks were sold".format(len(stocks['trades']))) sleep(sleep_time) record.store_new_trades(stocks['trades'])