def executeJob(sc, market, granularity, tradingData=pd.DataFrame()): """Trading bot job which runs at a scheduled interval""" global action, buy_count, buy_sum, failsafe, iterations, last_action, last_buy, last_df_index, sell_count, sell_sum, x_since_buy, x_since_sell # increment iterations iterations = iterations + 1 if is_sim == 0: # retrieve the market data api = PublicAPI() tradingData = api.getHistoricalData(market, granularity) # analyse the market data tradingDataCopy = tradingData.copy() technicalAnalysis = TechnicalAnalysis(tradingDataCopy) technicalAnalysis.addAll() df = technicalAnalysis.getDataFrame() if len(df) != 300: # data frame should have 300 rows, if not retry print('error: data frame length is < 300 (' + str(len(df)) + ')') logging.error('error: data frame length is < 300 (' + str(len(df)) + ')') s.enter(300, 1, executeJob, (sc, market, granularity)) if is_sim == 1: # with a simulation df_last will iterate through data df_last = df.iloc[iterations - 1:iterations] else: # df_last contains the most recent entry df_last = df.tail(1) price = float(df_last['close'].values[0]) ema12gtema26 = bool(df_last['ema12gtema26'].values[0]) ema12gtema26co = bool(df_last['ema12gtema26co'].values[0]) macdgtsignal = bool(df_last['macdgtsignal'].values[0]) macdgtsignalco = bool(df_last['macdgtsignalco'].values[0]) ema12ltema26 = bool(df_last['ema12ltema26'].values[0]) ema12ltema26co = bool(df_last['ema12ltema26co'].values[0]) macdltsignal = bool(df_last['macdltsignal'].values[0]) macdltsignalco = bool(df_last['macdltsignalco'].values[0]) obv = float(df_last['obv'].values[0]) obv_pc = float(df_last['obv_pc'].values[0]) # candlestick detection hammer = bool(df_last['hammer'].values[0]) inverted_hammer = bool(df_last['inverted_hammer'].values[0]) hanging_man = bool(df_last['hanging_man'].values[0]) shooting_star = bool(df_last['shooting_star'].values[0]) three_white_soldiers = bool(df_last['three_white_soldiers'].values[0]) three_black_crows = bool(df_last['three_black_crows'].values[0]) morning_star = bool(df_last['morning_star'].values[0]) evening_star = bool(df_last['evening_star'].values[0]) three_line_strike = bool(df_last['three_line_strike'].values[0]) abandoned_baby = bool(df_last['abandoned_baby'].values[0]) morning_doji_star = bool(df_last['morning_doji_star'].values[0]) evening_doji_star = bool(df_last['evening_doji_star'].values[0]) two_black_gapping = bool(df_last['two_black_gapping'].values[0]) # criteria for a buy signal if ((ema12gtema26co == True and macdgtsignal == True and macdgtsignal > 0.1) or (ema12gtema26 == True and macdgtsignal == True and x_since_buy > 0 and x_since_buy <= 2)) and last_action != 'BUY': action = 'BUY' # criteria for a sell signal elif ((ema12ltema26co == True and macdltsignal == True) or (ema12ltema26 == True and macdltsignal == True and x_since_sell > 0 and x_since_sell <= 2)) and last_action not in ['', 'SELL']: action = 'SELL' failsafe = False # anything other than a buy or sell, just wait else: action = 'WAIT' # loss failsafe sell < -5% if last_buy > 0 and last_action == 'BUY': change_pcnt = ((price / last_buy) - 1) * 100 if (change_pcnt < -5): action = 'SELL' x_since_buy = 0 failsafe = True log_text = '! Loss Failsafe Triggered (< -5%)' print(log_text, "\n") logging.warning(log_text) # polling is every 5 minutes (even for hourly intervals), but only process once per interval if (last_df_index != df_last.index.format()): ts_text = str(df_last.index.format()[0]) precision = 2 if cryptoMarket == 'XLM': precision = 4 price_text = 'Price: ' + str( truncate(float(df_last['close'].values[0]), precision)) ema_text = compare(df_last['ema12'].values[0], df_last['ema26'].values[0], 'EMA12/26', precision) macd_text = compare(df_last['macd'].values[0], df_last['signal'].values[0], 'MACD', precision) obv_text = compare(df_last['obv_pc'].values[0], 0.1, 'OBV %', precision) counter_text = '[I:' + str(iterations) + ',B:' + str( x_since_buy) + ',S:' + str(x_since_sell) + ']' if hammer == True: log_text = '* Candlestick Detected: Hammer ("Weak - Reversal - Bullish Signal - Up")' print(log_text, "\n") logging.debug(log_text) if shooting_star == True: log_text = '* Candlestick Detected: Shooting Star ("Weak - Reversal - Bearish Pattern - Down")' print(log_text, "\n") logging.debug(log_text) if hanging_man == True: log_text = '* Candlestick Detected: Hanging Man ("Weak - Continuation - Bearish Pattern - Down")' print(log_text, "\n") logging.debug(log_text) if inverted_hammer == True: log_text = '* Candlestick Detected: Inverted Hammer ("Weak - Continuation - Bullish Pattern - Up")' print(log_text, "\n") logging.debug(log_text) if three_white_soldiers == True: log_text = '*** Candlestick Detected: Three White Soldiers ("Strong - Reversal - Bullish Pattern - Up")' print(log_text, "\n") logging.debug(log_text) if three_black_crows == True: log_text = '* Candlestick Detected: Three Black Crows ("Strong - Reversal - Bearish Pattern - Down")' print(log_text, "\n") logging.debug(log_text) if morning_star == True: log_text = '*** Candlestick Detected: Morning Star ("Strong - Reversal - Bullish Pattern - Up")' print(log_text, "\n") logging.debug(log_text) if evening_star == True: log_text = '*** Candlestick Detected: Evening Star ("Strong - Reversal - Bearish Pattern - Down")' print(log_text, "\n") logging.debug(log_text) if three_line_strike == True: log_text = '** Candlestick Detected: Three Line Strike ("Reliable - Reversal - Bullish Pattern - Up")' print(log_text, "\n") logging.debug(log_text) if abandoned_baby == True: log_text = '** Candlestick Detected: Abandoned Baby ("Reliable - Reversal - Bullish Pattern - Up")' print(log_text, "\n") logging.debug(log_text) if morning_doji_star == True: log_text = '** Candlestick Detected: Morning Doji Star ("Reliable - Reversal - Bullish Pattern - Up")' print(log_text, "\n") logging.debug(log_text) if evening_doji_star == True: log_text = '** Candlestick Detected: Evening Doji Star ("Reliable - Reversal - Bearish Pattern - Down")' print(log_text, "\n") logging.debug(log_text) if two_black_gapping == True: log_text = '*** Candlestick Detected: Two Black Gapping ("Reliable - Reversal - Bearish Pattern - Down")' print(log_text, "\n") logging.debug(log_text) ema_co_prefix = '' ema_co_suffix = '' if ema12gtema26co == True: ema_co_prefix = '*^ ' ema_co_suffix = ' ^*' elif ema12ltema26co == True: ema_co_prefix = '*v ' ema_co_suffix = ' v*' elif ema12gtema26 == True: ema_co_prefix = '^ ' ema_co_suffix = ' ^' elif ema12ltema26 == True: ema_co_prefix = 'v ' ema_co_suffix = ' v' macd_co_prefix = '' macd_co_suffix = '' if macdgtsignalco == True: macd_co_prefix = '*^ ' macd_co_suffix = ' ^*' elif macdltsignalco == True: macd_co_prefix = '*v ' macd_co_suffix = ' v*' elif macdgtsignal == True: macd_co_prefix = '^ ' macd_co_suffix = ' ^' elif macdltsignal == True: macd_co_prefix = 'v ' macd_co_suffix = ' v' obv_prefix = '' obv_suffix = '' if (obv_pc > 0.1): obv_prefix = '^ ' obv_suffix = ' ^' else: obv_prefix = 'v ' obv_suffix = ' v' if is_verbose == 0: if last_action != '': output_text = ts_text + ' | ' + price_text + ' | ' + ema_co_prefix + ema_text + ema_co_suffix + ' | ' + macd_co_prefix + macd_text + macd_co_suffix + ' | ' + obv_prefix + obv_text + obv_suffix + ' | ' + action + ' ' + counter_text + ' | Last Action: ' + last_action else: output_text = ts_text + ' | ' + price_text + ' | ' + ema_co_prefix + ema_text + ema_co_suffix + ' | ' + macd_co_prefix + macd_text + macd_co_suffix + ' | ' + obv_prefix + obv_text + obv_suffix + ' | ' + action + ' ' + counter_text if last_action == 'BUY': # calculate last buy minus fees fee = last_buy * 0.005 last_buy_minus_fees = last_buy + fee margin = str( truncate((((price - last_buy_minus_fees) / price) * 100), 2)) + '%' output_text += ' | ' + margin logging.debug(output_text) print(output_text) else: logging.debug('-- Iteration: ' + str(iterations) + ' --') logging.debug('-- Since Last Buy: ' + str(x_since_buy) + ' --') logging.debug('-- Since Last Sell: ' + str(x_since_sell) + ' --') if last_action == 'BUY': margin = str(truncate( (((price - last_buy) / price) * 100), 2)) + '%' logging.debug('-- Margin: ' + margin + '% --') logging.debug('price: ' + str(truncate(float(df_last['close'].values[0]), 2))) logging.debug('ema12: ' + str(truncate(float(df_last['ema12'].values[0]), 2))) logging.debug('ema26: ' + str(truncate(float(df_last['ema26'].values[0]), 2))) logging.debug('ema12gtema26co: ' + str(ema12gtema26co)) logging.debug('ema12gtema26: ' + str(ema12gtema26)) logging.debug('ema12ltema26co: ' + str(ema12ltema26co)) logging.debug('ema12ltema26: ' + str(ema12ltema26)) logging.debug('macd: ' + str(truncate(float(df_last['macd'].values[0]), 2))) logging.debug('signal: ' + str(truncate(float(df_last['signal'].values[0]), 2))) logging.debug('macdgtsignal: ' + str(macdgtsignal)) logging.debug('macdltsignal: ' + str(macdltsignal)) logging.debug('obv: ' + str(obv)) logging.debug('obv_pc: ' + str(obv_pc) + '%') logging.debug('action: ' + action) # informational output on the most recent entry print('') print( '================================================================================' ) txt = ' Iteration : ' + str(iterations) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' Since Last Buy : ' + str(x_since_buy) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' Since Last Sell : ' + str(x_since_sell) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' Timestamp : ' + str(df_last.index.format()[0]) print('|', txt, (' ' * (75 - len(txt))), '|') print( '--------------------------------------------------------------------------------' ) txt = ' EMA12 : ' + str( truncate(float(df_last['ema12'].values[0]), 2)) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' EMA26 : ' + str( truncate(float(df_last['ema26'].values[0]), 2)) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' Crossing Above : ' + str(ema12gtema26co) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' Currently Above : ' + str(ema12gtema26) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' Crossing Below : ' + str(ema12ltema26co) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' Currently Below : ' + str(ema12ltema26) print('|', txt, (' ' * (75 - len(txt))), '|') if (ema12gtema26 == True and ema12gtema26co == True): txt = ' Condition : EMA12 is currently crossing above EMA26' elif (ema12gtema26 == True and ema12gtema26co == False): txt = ' Condition : EMA12 is currently above EMA26 and has crossed over' elif (ema12ltema26 == True and ema12ltema26co == True): txt = ' Condition : EMA12 is currently crossing below EMA26' elif (ema12ltema26 == True and ema12ltema26co == False): txt = ' Condition : EMA12 is currently below EMA26 and has crossed over' else: txt = ' Condition : -' print('|', txt, (' ' * (75 - len(txt))), '|') print( '--------------------------------------------------------------------------------' ) txt = ' MACD : ' + str( truncate(float(df_last['macd'].values[0]), 2)) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' Signal : ' + str( truncate(float(df_last['signal'].values[0]), 2)) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' Currently Above : ' + str(macdgtsignal) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' Currently Below : ' + str(macdltsignal) print('|', txt, (' ' * (75 - len(txt))), '|') if (macdgtsignal == True and macdgtsignalco == True): txt = ' Condition : MACD is currently crossing above Signal' elif (macdgtsignal == True and macdgtsignalco == False): txt = ' Condition : MACD is currently above Signal and has crossed over' elif (macdltsignal == True and macdltsignalco == True): txt = ' Condition : MACD is currently crossing below Signal' elif (macdltsignal == True and macdltsignalco == False): txt = ' Condition : MACD is currently below Signal and has crossed over' else: txt = ' Condition : -' print('|', txt, (' ' * (75 - len(txt))), '|') print( '--------------------------------------------------------------------------------' ) txt = ' OBV : ' + str(truncate(obv, 4)) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' OBV Change : ' + str(obv_pc) + '%' print('|', txt, (' ' * (75 - len(txt))), '|') if (obv_pc >= 2): txt = ' Condition : Large positive volume changes' elif (obv_pc < 2 and obv_pc >= 0): txt = ' Condition : Positive volume changes' else: txt = ' Condition : Negative volume changes' print('|', txt, (' ' * (75 - len(txt))), '|') print( '--------------------------------------------------------------------------------' ) txt = ' Action : ' + action print('|', txt, (' ' * (75 - len(txt))), '|') print( '================================================================================' ) if last_action == 'BUY': txt = ' Margin : ' + margin + '%' print('|', txt, (' ' * (75 - len(txt))), '|') print( '================================================================================' ) # increment x since buy if (ema12gtema26 == True and failsafe == False): x_since_buy = x_since_buy + 1 # increment x since sell elif (ema12ltema26 == True): x_since_sell = x_since_sell + 1 # if a buy signal if action == 'BUY': buy_count = buy_count + 1 # reset x since sell x_since_sell = 0 last_buy = price # if live if is_live == 1: if is_verbose == 0: logging.info(ts_text + ' | ' + market + ' ' + str(granularity) + ' | ' + price_text + ' | BUY') print("\n", ts_text, '|', market, granularity, '|', price_text, '| BUY', "\n") else: print( '--------------------------------------------------------------------------------' ) print( '| *** Executing LIVE Buy Order *** |' ) print( '--------------------------------------------------------------------------------' ) # connect to coinbase pro api (authenticated) model = AuthAPI(config['api_key'], config['api_secret'], config['api_pass'], config['api_url']) # execute a live market buy resp = model.marketBuy(market, float(account.getBalance(fiatMarket))) logging.info(resp) #logging.info('attempt to buy ' + resp['specified_funds'] + ' (' + resp['funds'] + ' after fees) of ' + resp['product_id']) # if not live else: if is_verbose == 0: logging.info(ts_text + ' | ' + market + ' ' + str(granularity) + ' | ' + price_text + ' | BUY') print("\n", ts_text, '|', market, granularity, '|', price_text, '| BUY', "\n") else: print( '--------------------------------------------------------------------------------' ) print( '| *** Executing TEST Buy Order *** |' ) print( '--------------------------------------------------------------------------------' ) #print(df_last[['close','ema12','ema26','ema12gtema26','ema12gtema26co','macd','signal','macdgtsignal','obv','obv_pc']]) if save_graphs == 1: tradinggraphs = TradingGraphs(technicalAnalysis) ts = datetime.now().timestamp() filename = 'BTC-GBP_3600_buy_' + str(ts) + '.png' tradinggraphs.renderEMAandMACD(24, 'graphs/' + filename, True) # if a sell signal elif action == 'SELL': sell_count = sell_count + 1 # reset x since buy x_since_buy = 0 # if live if is_live == 1: if is_verbose == 0: logging.info(ts_text + ' | ' + market + ' ' + str(granularity) + ' | ' + price_text + ' | SELL') print("\n", ts_text, '|', market, granularity, '|', price_text, '| SELL', "\n") else: print( '--------------------------------------------------------------------------------' ) print( '| *** Executing LIVE Sell Order *** |' ) print( '--------------------------------------------------------------------------------' ) # connect to Coinbase Pro API live model = AuthAPI(config['api_key'], config['api_secret'], config['api_pass'], config['api_url']) # execute a live market sell resp = model.marketSell( market, float(account.getBalance(cryptoMarket))) logging.info(resp) #logging.info('attempt to sell ' + resp['size'] + ' of ' + resp['product_id']) # if not live else: if is_verbose == 0: sell_price = float( str( truncate(float(df_last['close'].values[0]), precision))) last_buy_price = float( str(truncate(float(last_buy), precision))) buy_sell_diff = round( np.subtract(sell_price, last_buy_price), precision) buy_sell_margin_no_fees = str( truncate( (((sell_price - last_buy_price) / sell_price) * 100), 2)) + '%' # calculate last buy minus fees buy_fee = last_buy_price * 0.005 last_buy_price_minus_fees = last_buy_price + buy_fee buy_sell_margin_fees = str( truncate((((sell_price - last_buy_price_minus_fees) / sell_price) * 100), 2)) + '%' logging.info(ts_text + ' | ' + market + ' ' + str(granularity) + ' | SELL | ' + str(sell_price) + ' | BUY | ' + str(last_buy_price) + ' | DIFF | ' + str(buy_sell_diff) + ' | MARGIN NO FEES | ' + str(buy_sell_margin_no_fees) + ' | MARGIN FEES | ' + str(buy_sell_margin_fees)) print("\n", ts_text, '|', market, granularity, '| SELL |', str(sell_price), '| BUY |', str(last_buy_price), '| DIFF |', str(buy_sell_diff), '| MARGIN NO FEES |', str(buy_sell_margin_no_fees), '| MARGIN FEES |', str(buy_sell_margin_fees), "\n") buy_sum = buy_sum + last_buy_price_minus_fees sell_sum = sell_sum + sell_price else: print( '--------------------------------------------------------------------------------' ) print( '| *** Executing TEST Sell Order *** |' ) print( '--------------------------------------------------------------------------------' ) #print(df_last[['close','ema12','ema26','ema12ltema26','ema12ltema26co','macd','signal','macdltsignal','obv','obv_pc']]) if save_graphs == 1: tradinggraphs = TradingGraphs(technicalAnalysis) ts = datetime.now().timestamp() filename = 'BTC-GBP_3600_buy_' + str(ts) + '.png' tradinggraphs.renderEMAandMACD(24, 'graphs/' + filename, True) # last significant action if action in ['BUY', 'SELL']: last_action = action last_df_index = df_last.index.format() if iterations == 300: print("\nSimulation Summary\n") if buy_count > sell_count: # calculate last buy minus fees fee = last_buy * 0.005 last_buy_minus_fees = last_buy + fee buy_sum = buy_sum + (float( truncate(float(df_last['close'].values[0]), precision)) - last_buy_minus_fees) print(' Buy Count :', buy_count) print(' Sell Count :', sell_count, "\n") print(' Buy Total :', buy_sum) print(' Sell Total :', sell_sum) print( ' Margin :', str(truncate( (((sell_sum - buy_sum) / sell_sum) * 100), 2)) + '%', "\n") else: # decrement ignored iteration iterations = iterations - 1 # if live if is_live == 1: # save csv with orders for market that are 'done' orders = account.getOrders(market, '', 'done') orders.to_csv('orders.csv', index=False) if is_sim == 1: if iterations < 300: if sim_speed == 'fast': # fast processing executeJob(sc, market, granularity, tradingData) else: # slow processing s.enter(1, 1, executeJob, (sc, market, granularity, tradingData)) else: # poll every 5 minutes s.enter(300, 1, executeJob, (sc, market, granularity))
def sell(self, cryptoMarket, fiatMarket, cryptoAmount, manualPrice=0.00000000): """Places a sell order either live or simulation Parameters ---------- cryptoMarket: str Crypto market you wish to purchase fiatMarket, str QUOTE market funding the purchase fiatAmount, float QUOTE amount of crypto currency to purchase manualPrice, float Used for simulations specifying the live price to purchase """ if self.app.getExchange() == 'binance': # validate crypto market is syntactically correct p = re.compile(r"^[A-Z]{3,8}$") if not p.match(cryptoMarket): raise TypeError('Binance crypto market is invalid.') # validate fiat market is syntactically correct p = re.compile(r"^[A-Z]{3,8}$") if not p.match(fiatMarket): raise TypeError('Binance fiat market is invalid.') else: # crypto market should be either BCH, BTC, ETH, LTC or XLM if cryptoMarket not in ['BCH', 'BTC', 'ETH', 'LTC', 'XLM']: raise Exception( 'Invalid crypto market: BCH, BTC, ETH, LTC, ETH, or XLM') # fiat market should be either EUR, GBP, or USD if fiatMarket not in ['EUR', 'GBP', 'USD']: raise Exception('Invalid QUOTE market: EUR, GBP, USD') # reconstruct the exchange market using crypto and fiat inputs if self.app.getExchange() == 'binance': market = cryptoMarket + fiatMarket else: market = cryptoMarket + '-' + fiatMarket # crypto amount must be an integer or float if not isinstance(cryptoAmount, float) and not isinstance( cryptoAmount, int): raise TypeError('Crypto amount not numeric.') # crypto amount must be positive if cryptoAmount <= 0: raise Exception('Invalid crypto amount.') if self.app.getExchange() == 'binance': if self.mode == 'live': # execute a live market buy resp = self.client.order_market_sell(symbol=market, quantity=cryptoAmount) # TODO: not finished print(resp) else: # crypto amount should exceed balance if cryptoAmount > self.getBalance(cryptoMarket): raise Exception('Insufficient funds.') # manual price must be an integer or float if not isinstance(manualPrice, float) and not isinstance( manualPrice, int): raise TypeError('Optional manual price not numeric.') # calculate purchase fees fee = cryptoAmount * 0.005 cryptoAmountMinusFee = cryptoAmount - fee price = manualPrice # if manualPrice is non-positive retrieve the current live price if manualPrice <= 0: resp = requests.get( 'https://api-public.sandbox.pro.coinbase.com/products/' + market + '/ticker') if resp.status_code != 200: raise Exception('GET /products/' + market + '/ticker {}'.format(resp.status_code)) resp.raise_for_status() json = resp.json() price = float(json['price']) total = price * cryptoAmountMinusFee # append dummy order into orders dataframe ts = pd.Timestamp.now() price = ((price * cryptoAmount) * 100) / (cryptoAmount * 100) order = pd.DataFrame([[ '', market, 'sell', 'market', cryptoAmountMinusFee, float('{:.8f}'.format(total)), 'done', '{:.8f}'.format( float(price)) ]], columns=[ 'created_at', 'market', 'action', 'type', 'size', 'value', 'status', 'price' ], index=[ts]) order['created_at'] = order.index self.orders = pd.concat( [self.orders, pd.DataFrame(order)], ignore_index=False) # update the dummy fiat balance self.balance.loc[ self.balance['currency'] == fiatMarket, 'balance'] = self.getBalance(fiatMarket) + total self.balance.loc[ self.balance['currency'] == fiatMarket, 'available'] = self.getBalance(fiatMarket) + total # update the dummy crypto balance self.balance.loc[ self.balance['currency'] == cryptoMarket, 'balance'] = self.getBalance(cryptoMarket) - cryptoAmount self.balance.loc[ self.balance['currency'] == cryptoMarket, 'available'] = self.getBalance(cryptoMarket) - cryptoAmount else: if self.mode == 'live': # connect to Coinbase Pro API live model = CBAuthAPI(self.app.getAPIKey(), self.app.getAPISecret(), self.app.getAPIPassphrase(), self.app.getAPIURL()) # execute a live market sell resp = model.marketSell(market, float(self.getBalance(cryptoMarket))) # TODO: not finished print(resp) else: # crypto amount should exceed balance if cryptoAmount > self.getBalance(cryptoMarket): raise Exception('Insufficient funds.') # manual price must be an integer or float if not isinstance(manualPrice, float) and not isinstance( manualPrice, int): raise TypeError('Optional manual price not numeric.') # calculate purchase fees fee = cryptoAmount * 0.005 cryptoAmountMinusFee = cryptoAmount - fee price = manualPrice if manualPrice <= 0: # if manualPrice is non-positive retrieve the current live price resp = requests.get( 'https://api-public.sandbox.pro.coinbase.com/products/' + market + '/ticker') if resp.status_code != 200: raise Exception('GET /products/' + market + '/ticker {}'.format(resp.status_code)) resp.raise_for_status() json = resp.json() price = float(json['price']) total = price * cryptoAmountMinusFee # append dummy order into orders dataframe ts = pd.Timestamp.now() price = ((price * cryptoAmount) * 100) / (cryptoAmount * 100) order = pd.DataFrame([[ market, 'sell', 'market', cryptoAmountMinusFee, float('{:.8f}'.format(total)), 'done', price ]], columns=[ 'market', 'action', 'type', 'size', 'value', 'status', 'price' ], index=[ts]) order['created_at'] = order.index self.orders = pd.concat( [self.orders, pd.DataFrame(order)], ignore_index=False) # update the dummy fiat balance self.balance.loc[ self.balance['currency'] == fiatMarket, 'balance'] = self.getBalance(fiatMarket) + total self.balance.loc[ self.balance['currency'] == fiatMarket, 'available'] = self.getBalance(fiatMarket) + total # update the dummy crypto balance self.balance.loc[ self.balance['currency'] == cryptoMarket, 'balance'] = self.getBalance(cryptoMarket) - cryptoAmount self.balance.loc[ self.balance['currency'] == cryptoMarket, 'available'] = self.getBalance(cryptoMarket) - cryptoAmount
def executeJob(sc, market, granularity, tradingData=pd.DataFrame()): """Trading bot job which runs at a scheduled interval""" global action, iterations, x_since_buy, x_since_sell, last_action, last_df_index # increment iterations iterations = iterations + 1 if is_sim == 0: # retrieve the market data api = PublicAPI() tradingData = api.getHistoricalData(market, granularity) # analyse the market data tradingDataCopy = tradingData.copy() technicalAnalysis = TechnicalAnalysis(tradingDataCopy) technicalAnalysis.addAll() df = technicalAnalysis.getDataFrame() if len(df) != 300: # data frame should have 300 rows, if not retry print('error: data frame length is < 300 (' + str(len(df)) + ')') logging.error('error: data frame length is < 300 (' + str(len(df)) + ')') s.enter(300, 1, executeJob, (sc, market, granularity)) if is_sim == 1: # with a simulation df_last will iterate through data df_last = df.iloc[iterations - 1:iterations] else: # df_last contains the most recent entry df_last = df.tail(1) ema12gtema26 = bool(df_last['ema12gtema26'].values[0]) ema12gtema26co = bool(df_last['ema12gtema26co'].values[0]) macdgtsignal = bool(df_last['macdgtsignal'].values[0]) macdgtsignalco = bool(df_last['macdgtsignalco'].values[0]) ema12ltema26 = bool(df_last['ema12ltema26'].values[0]) ema12ltema26co = bool(df_last['ema12ltema26co'].values[0]) macdltsignal = bool(df_last['macdltsignal'].values[0]) macdltsignalco = bool(df_last['macdltsignalco'].values[0]) obv = float(df_last['obv'].values[0]) obv_pc = float(df_last['obv_pc'].values[0]) # criteria for a buy signal if ((ema12gtema26co == True and macdgtsignal == True and obv_pc > 0.1) or (ema12gtema26 == True and macdgtsignal == True and obv_pc > 0.1 and x_since_buy > 0 and x_since_buy <= 2)) and last_action != 'BUY': action = 'BUY' # criteria for a sell signal elif (ema12ltema26co == True and macdltsignal == True) and last_action not in ['', 'SELL']: action = 'SELL' # anything other than a buy or sell, just wait else: action = 'WAIT' # polling is every 5 minutes (even for hourly intervals), but only process once per interval if (last_df_index != df_last.index.format()): ts_text = str(df_last.index.format()[0]) price_text = 'Price: ' + str( truncate(float(df_last['close'].values[0]), 2)) ema_text = compare(df_last['ema12'].values[0], df_last['ema26'].values[0], 'EMA12/26') macd_text = compare(df_last['macd'].values[0], df_last['signal'].values[0], 'MACD') obv_text = compare(df_last['obv_pc'].values[0], 0, 'OBV %') counter_text = '[I:' + str(iterations) + ',B:' + str( x_since_buy) + ',S:' + str(x_since_sell) + ']' ema_co_prefix = '' ema_co_suffix = '' if ema12gtema26co == True or ema12ltema26co == True: ema_co_prefix = '* ' ema_co_suffix = ' *' macd_co_prefix = '' macd_co_suffix = '' if macdgtsignalco == True or macdltsignalco == True: macd_co_prefix = '* ' macd_co_suffix = ' *' if is_verbose == 0: output_text = ts_text + ' | ' + price_text + ' | ' + ema_co_prefix + ema_text + ema_co_suffix + ' | ' + macd_co_prefix + macd_text + macd_co_suffix + ' | ' + obv_text + ' | ' + action + ' ' + counter_text logging.debug(output_text) print(output_text) else: logging.debug('-- Iteration: ' + str(iterations) + ' --') logging.debug('-- Since Last Buy: ' + str(x_since_buy) + ' --') logging.debug('-- Since Last Sell: ' + str(x_since_sell) + ' --') logging.debug('price: ' + str(truncate(float(df_last['close'].values[0]), 2))) logging.debug('ema12: ' + str(truncate(float(df_last['ema12'].values[0]), 2))) logging.debug('ema26: ' + str(truncate(float(df_last['ema26'].values[0]), 2))) logging.debug('ema12gtema26co: ' + str(ema12gtema26co)) logging.debug('ema12gtema26: ' + str(ema12gtema26)) logging.debug('ema12ltema26co: ' + str(ema12ltema26co)) logging.debug('ema12ltema26: ' + str(ema12ltema26)) logging.debug('macd: ' + str(truncate(float(df_last['macd'].values[0]), 2))) logging.debug('signal: ' + str(truncate(float(df_last['signal'].values[0]), 2))) logging.debug('macdgtsignal: ' + str(macdgtsignal)) logging.debug('macdltsignal: ' + str(macdltsignal)) logging.debug('obv: ' + str(obv)) logging.debug('obv_pc: ' + str(obv_pc) + '%') logging.debug('action: ' + action) # informational output on the most recent entry print('') print( '================================================================================' ) txt = ' Iteration : ' + str(iterations) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' Since Last Buy : ' + str(x_since_buy) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' Since Last Sell : ' + str(x_since_sell) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' Timestamp : ' + str(df_last.index.format()[0]) print('|', txt, (' ' * (75 - len(txt))), '|') print( '--------------------------------------------------------------------------------' ) txt = ' EMA12 : ' + str( truncate(float(df_last['ema12'].values[0]), 2)) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' EMA26 : ' + str( truncate(float(df_last['ema26'].values[0]), 2)) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' Crossing Above : ' + str(ema12gtema26co) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' Currently Above : ' + str(ema12gtema26) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' Crossing Below : ' + str(ema12ltema26co) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' Currently Below : ' + str(ema12ltema26) print('|', txt, (' ' * (75 - len(txt))), '|') if (ema12gtema26 == True and ema12gtema26co == True): txt = ' Condition : EMA12 is currently crossing above EMA26' elif (ema12gtema26 == True and ema12gtema26co == False): txt = ' Condition : EMA12 is currently above EMA26 and has crossed over' elif (ema12ltema26 == True and ema12ltema26co == True): txt = ' Condition : EMA12 is currently crossing below EMA26' elif (ema12ltema26 == True and ema12ltema26co == False): txt = ' Condition : EMA12 is currently below EMA26 and has crossed over' else: txt = ' Condition : -' print('|', txt, (' ' * (75 - len(txt))), '|') print( '--------------------------------------------------------------------------------' ) txt = ' MACD : ' + str( truncate(float(df_last['macd'].values[0]), 2)) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' Signal : ' + str( truncate(float(df_last['signal'].values[0]), 2)) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' Currently Above : ' + str(macdgtsignal) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' Currently Below : ' + str(macdltsignal) print('|', txt, (' ' * (75 - len(txt))), '|') if (macdgtsignal == True and macdgtsignalco == True): txt = ' Condition : MACD is currently crossing above Signal' elif (macdgtsignal == True and macdgtsignalco == False): txt = ' Condition : MACD is currently above Signal and has crossed over' elif (macdltsignal == True and macdltsignalco == True): txt = ' Condition : MACD is currently crossing below Signal' elif (macdltsignal == True and macdltsignalco == False): txt = ' Condition : MACD is currently below Signal and has crossed over' else: txt = ' Condition : -' print('|', txt, (' ' * (75 - len(txt))), '|') print( '--------------------------------------------------------------------------------' ) txt = ' OBV : ' + str(truncate(obv, 4)) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' OBV Change : ' + str(obv_pc) + '%' print('|', txt, (' ' * (75 - len(txt))), '|') if (obv_pc >= 2): txt = ' Condition : Large positive volume changes' elif (obv_pc < 2 and obv_pc >= 0): txt = ' Condition : Positive volume changes' else: txt = ' Condition : Negative volume changes' print('|', txt, (' ' * (75 - len(txt))), '|') print( '--------------------------------------------------------------------------------' ) txt = ' Action : ' + action print('|', txt, (' ' * (75 - len(txt))), '|') print( '================================================================================' ) if last_action == 'BUY': x_since_buy = x_since_buy + 1 elif last_action == 'SELL': x_since_sell = x_since_sell + 1 # if a buy signal if action == 'BUY': # increment x since buy x_since_buy = x_since_buy + 1 # reset x since sell x_since_sell = 0 # if live if is_live == 1: if is_verbose == 0: logging.info(ts_text + ' | ' + market + ' ' + str(granularity) + ' | ' + price_text + ' | BUY') print(ts_text, '|', market, granularity, '|', price_text, '| BUY') else: print( '--------------------------------------------------------------------------------' ) print( '| *** Executing LIVE Buy Order *** |' ) print( '--------------------------------------------------------------------------------' ) # connect to coinbase pro api (authenticated) model = AuthAPI(config['api_key'], config['api_secret'], config['api_pass'], config['api_url']) # execute a live market buy resp = model.marketBuy(market, float(account.getBalance(fiatMarket))) logging.info(resp) #logging.info('attempt to buy ' + resp['specified_funds'] + ' (' + resp['funds'] + ' after fees) of ' + resp['product_id']) # if not live else: if is_verbose == 0: logging.info(ts_text + ' | ' + market + ' ' + str(granularity) + ' | ' + price_text + ' | BUY') print(ts_text, '|', market, granularity, '|', price_text, '| BUY') else: print( '--------------------------------------------------------------------------------' ) print( '| *** Executing TEST Buy Order *** |' ) print( '--------------------------------------------------------------------------------' ) #print(df_last[['close','ema12','ema26','ema12gtema26','ema12gtema26co','macd','signal','macdgtsignal','obv','obv_pc']]) # if a sell signal elif action == 'SELL': # increment x since buy x_since_sell = x_since_sell + 1 # reset x since buy x_since_buy = 0 # if live if is_live == 1: if is_verbose == 0: logging.info(ts_text + ' | ' + market + ' ' + str(granularity) + ' | ' + price_text + ' | SELL') print(ts_text, '|', market, granularity, '|', price_text, '| SELL') else: print( '--------------------------------------------------------------------------------' ) print( '| *** Executing LIVE Sell Order *** |' ) print( '--------------------------------------------------------------------------------' ) # connect to Coinbase Pro API live model = AuthAPI(config['api_key'], config['api_secret'], config['api_pass'], config['api_url']) # execute a live market sell resp = model.marketSell( market, float(account.getBalance(cryptoMarket))) logging.info(resp) #logging.info('attempt to sell ' + resp['size'] + ' of ' + resp['product_id']) # if not live else: if is_verbose == 0: logging.info(ts_text + ' | ' + market + ' ' + str(granularity) + ' | ' + price_text + ' | SELL') print(ts_text, '|', market, granularity, '|', price_text, '| SELL') else: print( '--------------------------------------------------------------------------------' ) print( '| *** Executing TEST Sell Order *** |' ) print( '--------------------------------------------------------------------------------' ) #print(df_last[['close','ema12','ema26','ema12ltema26','ema12ltema26co','macd','signal','macdltsignal','obv','obv_pc']]) # last significant action if action in ['BUY', 'SELL']: last_action = action last_df_index = df_last.index.format() # if live if is_live == 1: # save csv with orders for market that are 'done' orders = account.getOrders(market, '', 'done') orders.to_csv('orders.csv', index=False) if is_sim == 1: if iterations < 300: if sim_speed == 'fast': # fast processing executeJob(sc, market, granularity, tradingData) else: # slow processing s.enter(1, 1, executeJob, (sc, market, granularity, tradingData)) else: # poll every 5 minutes s.enter(300, 1, executeJob, (sc, market, granularity))