def minutes_till_trading(): ''' Time till NZX is open for trading ''' now = util.get_nz_time() open_dt = datetime.strptime(config.open_time, '%I:%M%p') close_dt = datetime.strptime(config.close_time, '%I:%M%p') # market is open today if now.strftime('%A').lower() not in config.days_closed: # open now if now.time() >= open_dt.time() and now.time() <= close_dt.time(): return 0 # hasn't open yet if now.time() < open_dt.time(): open_time = datetime.combine(now, open_dt.time()) - now return open_time.total_seconds() / 60 # market has closed for i in range(7): future = now + timedelta(days=(i + 1)) if future.strftime('%A').lower() not in config.days_closed: next_open = datetime.combine(future, open_dt.time()) - now return next_open.total_seconds() / 60 util.log("market is never open according to config", error=True)
def should_buy(market_price, history, margin_percent): ''' Decides if the bot should buy or not ''' # ignore zero divide errors np.seterr(divide='ignore', invalid='ignore') try: # calculate vwap history = history.groupby(history.index.date, group_keys=False) history = history.apply(Market.vwap).fillna( 0) ## Fill NaN's in df with 0's # calculate direction moves = np.gradient(history['vwap']) median = np.median(moves) average = np.average(moves) # calculate margin price margin_price = history['vwap'][-1] margin_price -= (margin_price * (margin_percent / 100)) print(f'median:\t {median:.4g}') print(f'averag:\t {average:.4g}') print(f'market:\t {market_price:.4g}') print(f'margin:\t {margin_price:.4g}') # agree if going up and below margin if median > 0 and average > 0 and market_price <= margin_price: # might like to consider changing the 0's? return True except Exception as e: util.log(f'Warning: {e}') return False
def should_buy(market_price, history, margin_percent): ''' Decides if the bot should buy or not ''' # ignore zero divide errors np.seterr(divide='ignore', invalid='ignore') try: # calculate vwap history = history.groupby(history.index.date, group_keys=False) history = history.apply(Market.vwap) # calculate direction moves = np.gradient(history['vwap']) median = np.median(moves) average = np.average(moves) # calculate margin price margin_price = history['vwap'][-1] margin_price -= (margin_price * (margin_percent/100)) # agree if going up and below margin if median > 0 and average > 0 and market_price <= margin_price: return True except Exception as e: util.log(f'Warning: {e}') return False
def perform_buying(client, investments, companies, balance): ''' Find new stocks to buy from the companies ''' for company in companies: # don't double invest if company['id'] in investments: continue # ignore penny stocks price = float(company['market_price']) if price < config.minimum_stock_price: continue c = company['code'] sleep(2) print() print(f'Considering buying {c}') symbol = company['code'] + '.NZ' stock = yfinance.Ticker(symbol) try: history = stock.history(period='1mo', interval='15m') except RuntimeError: print('Probably not enough data at this resolution. Skipping') continue # buy if it is a bargain should_buy = Market.should_buy(price, history, 0.4) # 0.4 is margin %, i.e. 0.4% print(should_buy) if should_buy: buy_amount = config.buy_amount # value shares more as dividends are upcoming if company['dividends']: dividends_soon = util.dividends_soon(company['dividends']) if dividends_soon and config.dividends_bonus > 1: buy_amount *= config.dividends_bonus # check we have balance if balance < buy_amount: util.log(f'Want to buy {symbol} but not enough $$$') break # submit the buy order util.log(f'Buying ${buy_amount} of {symbol}') client.buy(company, buy_amount) balance -= buy_amount
def perform_selling(client, portfolio, companies, dividends): ''' Look to sell stocks from the portfolio ''' for company in portfolio: fund_id = company['fund_id'] contribution = float(company['contribution']) current_value = float(company['value']) # does company give dividends? if fund_id in dividends: util.log(f'Not selling {fund_id}:{code} due to dividends') continue # sell if we're making a profit if Market.should_sell(contribution, current_value): code = util.get_code_from_id(fund_id, companies) util.log(f'Selling ${current_value} of {code}') client.sell(company, float(company['shares']))
def perform_buying(client, investments, companies, balance): ''' Find new stocks to buy from the companies ''' for company in companies: # don't double invest if company['id'] in investments: continue # ignore penny stocks price = float(company['market_price']) if price < config.minimum_stock_price: continue symbol = company['code'] + '.NZ' stock = yfinance.Ticker(symbol) history = stock.history(period='1mo', interval='15m') # buy if it is a bargain if Market.should_buy(price, history, 0.4): buy_amount = config.buy_amount # value shares more as dividends are upcoming if company['dividends']: dividends_soon = util.dividends_soon(company['dividends']) if dividends_soon and config.dividends_bonus > 1: buy_amount *= config.dividends_bonus # check we have balance if balance < buy_amount: util.log(f'Want to buy {symbol} but not enough $$$') break # submit the buy order util.log(f'Buying ${buy_amount} of {symbol}') client.buy(company, buy_amount) balance -= buy_amount
balance = float(profile['user']['wallet_balance']) portfolio = profile['portfolio'] dividends = profile['upcoming_dividends'] investments = util.get_fund_ids(portfolio) companies = client.get_companies() # it's show time perform_selling(client, portfolio, companies, dividends) perform_buying(client, investments, companies, balance) if __name__ == '__main__': # config util.log('Loaded config') # init client client = sharesies.Client() if client.login(config.username, config.password): util.log('Connected to Sharesies') else: util.log('Failed to login', error=True) # trade loop while True: minutes_till_open = Market.minutes_till_trading() if minutes_till_open == 0: util.log('Market is currently open!') scan_market(client)