def test_should_calculate_addChangePct(): """ Adds the close percentage to the DataFrame : close_pc Adds the cumulative returns the DataFrame : close_cpc Excellent video to understand cumulative returns : https://www.youtube.com/watch?v=fWHQwqT3lNY """ # GIVEN a series of values closes_list = [0.0003, 0.0004, 0.0010, 0.0020, 0.0009] df = pd.DataFrame({ 'date': [ '2021-10-10 14:30:00', '2021-10-10 14:31:00', '2021-10-10 14:32:00', '2021-10-10 14:33:00', '2021-10-10 14:34:00' ], 'close': closes_list }) df['date'] = pd.to_datetime(df['date'], format="%Y-%d-%m %H:%M:%S") df.set_index(['date']) ta = TechnicalAnalysis(df) # WHEN calculate the percentage evolution and cumulative returns percentage ta.addChangePct() # THEN percentage evolution and cumulative returns percentage should be added to dataframe actual = ta.getDataFrame() close_pc = [ calculate_percentage_evol(closes_list[0], closes_list[0]), calculate_percentage_evol(closes_list[0], closes_list[1]), calculate_percentage_evol(closes_list[1], closes_list[2]), calculate_percentage_evol(closes_list[2], closes_list[3]), calculate_percentage_evol(closes_list[3], closes_list[4]), ] close_cpc = [] close_cpc.append(0.000000) close_cpc.append((1 + close_pc[1]) * (1 + close_cpc[0]) - 1) close_cpc.append((1 + close_pc[2]) * (1 + close_cpc[1]) - 1) close_cpc.append((1 + close_pc[3]) * (1 + close_cpc[2]) - 1) close_cpc.append((1 + close_pc[4]) * (1 + close_cpc[3]) - 1) expected = pd.DataFrame({ 'date': [ '2021-10-10 14:30:00', '2021-10-10 14:31:00', '2021-10-10 14:32:00', '2021-10-10 14:33:00', '2021-10-10 14:34:00' ], 'close': closes_list, 'close_pc': close_pc, 'close_cpc': close_cpc }) expected['date'] = pd.to_datetime(df['date'], format="%Y-%d-%m %H:%M:%S") expected.set_index(['date']) assert_frame_equal(actual, expected)
def test_should_calculate_addCMA(): """ Adds the Cumulative Moving Average (CMA) to the DataFrame : cma """ # GIVEN a series of values closes_list = [0.0003, 0.0004, 0.0010, 0.0020, 0.0009] df = pd.DataFrame({ 'date': [ '2021-10-10 14:30:00', '2021-10-10 14:31:00', '2021-10-10 14:32:00', '2021-10-10 14:33:00', '2021-10-10 14:34:00' ], 'close': closes_list }) df['date'] = pd.to_datetime(df['date'], format="%Y-%d-%m %H:%M:%S") df.set_index(['date']) ta = TechnicalAnalysis(df) # WHEN calculate the cumulative moving average ta.addCMA() # THEN Cumulative Moving Average should be added to dataframe actual = ta.getDataFrame() expected = pd.DataFrame({ 'date': [ '2021-10-10 14:30:00', '2021-10-10 14:31:00', '2021-10-10 14:32:00', '2021-10-10 14:33:00', '2021-10-10 14:34:00' ], 'close': closes_list, 'cma': [ calculate_mean_on_range(0, 1, closes_list), calculate_mean_on_range(0, 2, closes_list), calculate_mean_on_range(0, 3, closes_list), calculate_mean_on_range(0, 4, closes_list), calculate_mean_on_range(0, 5, closes_list), ] }) expected['date'] = pd.to_datetime(df['date'], format="%Y-%d-%m %H:%M:%S") expected.set_index(['date']) assert_frame_equal(actual, expected)
def is6hEMA1226Bull(self, iso8601end: str = ''): try: if self.isSimulation() and isinstance(self.ema1226_6h_cache, pd.DataFrame): df_data = self.ema1226_6h_cache[(self.ema1226_6h_cache['date'] <= iso8601end)] elif self.exchange == 'coinbasepro': api = CBPublicAPI() df_data = api.getHistoricalData(self.market, 21600) self.ema1226_6h_cache = df_data elif self.exchange == 'binance': api = BPublicAPI() df_data = api.getHistoricalData(self.market, '6h') self.ema1226_6h_cache = df_data else: return False ta = TechnicalAnalysis(df_data) if 'ema12' not in df_data: ta.addEMA(12) if 'ema26' not in df_data: ta.addEMA(26) df_last = ta.getDataFrame().copy().iloc[-1, :] df_last['bull'] = df_last['ema12'] > df_last['ema26'] Logger.debug("---- EMA1226 6H Check----") if self.isSimulation(): Logger.debug("simdate: " + str(df_last['date'])) Logger.debug("ema12 6h: " + str(df_last['ema12'])) Logger.debug("ema26 6h: " + str(df_last['ema26'])) Logger.debug("bull 6h: " + str(df_last['ema12'] > df_last['ema26'])) return bool(df_last['bull']) except Exception: return False
def is1hSMA50200Bull(self, iso8601end: str = ''): try: if self.isSimulation() and isinstance(self.sma50200_1h_cache, pd.DataFrame): df_data = self.sma50200_1h_cache[( self.sma50200_1h_cache['date'] <= iso8601end)] elif self.exchange == 'coinbasepro': api = CBPublicAPI() df_data = api.getHistoricalData(self.market, 3600) self.sma50200_1h_cache = df_data elif self.exchange == 'binance': api = BPublicAPI() df_data = api.getHistoricalData(self.market, '1h') self.sma50200_1h_cache = df_data else: return False ta = TechnicalAnalysis(df_data) if 'sma50' not in df_data: ta.addSMA(50) if 'sma200' not in df_data: ta.addSMA(200) df_last = ta.getDataFrame().copy().iloc[-1, :] Logger.debug("---- SMA50200 1H Check----") if self.isSimulation(): Logger.debug("simdate: " + str(df_last['date'])) Logger.debug("sma50 1h: " + str(df_last['sma50'])) Logger.debug("sma200 1h: " + str(df_last['sma200'])) Logger.debug("bull 1h: " + str(df_last['sma50'] > df_last['sma200'])) df_last['bull'] = df_last['sma50'] > df_last['sma200'] return bool(df_last['bull']) except Exception: return False
def executeJob(sc=None, app: PyCryptoBot = None, state: AppState = None, trading_data=pd.DataFrame()): """Trading bot job which runs at a scheduled interval""" global technical_analysis # connectivity check (only when running live) if app.isLive() and app.getTime() is None: Logger.warning( 'Your connection to the exchange has gone down, will retry in 1 minute!' ) # poll every 5 minute list(map(s.cancel, s.queue)) s.enter(300, 1, executeJob, (sc, app, state)) return # increment state.iterations state.iterations = state.iterations + 1 if not app.isSimulation(): # retrieve the app.getMarket() data trading_data = app.getHistoricalData(app.getMarket(), app.getGranularity()) else: if len(trading_data) == 0: return None # analyse the market data if app.isSimulation() and len(trading_data.columns) > 8: df = trading_data # if smartswitch the get the market data using new granularity if app.sim_smartswitch: df_last = app.getInterval(df, state.iterations) if len(df_last.index.format()) > 0: current_df_index = str(df_last.index.format()[0]) current_sim_date = f'{current_df_index} 00:00:00' if len( current_df_index) == 10 else current_df_index dt = current_sim_date.split(' ') date = dt[0].split('-') time = dt[1].split(':') startDate = datetime(int(date[0]), int(date[1]), int(date[2]), int(time[0]), int(time[1]), int(time[2])) trading_data = app.getHistoricalData( app.getMarket(), app.getGranularity(), startDate.isoformat(timespec='milliseconds'), datetime.now().isoformat(timespec='milliseconds')) trading_dataCopy = trading_data.copy() technical_analysis = TechnicalAnalysis(trading_dataCopy) technical_analysis.addAll() df = technical_analysis.getDataFrame() state.iterations = 1 app.sim_smartswitch = False else: trading_dataCopy = trading_data.copy() technical_analysis = TechnicalAnalysis(trading_dataCopy) technical_analysis.addAll() df = technical_analysis.getDataFrame() if app.isSimulation(): df_last = app.getInterval(df, state.iterations) else: df_last = app.getInterval(df) if len(df_last.index.format()) > 0: current_df_index = str(df_last.index.format()[0]) else: current_df_index = state.last_df_index formatted_current_df_index = f'{current_df_index} 00:00:00' if len( current_df_index) == 10 else current_df_index current_sim_date = formatted_current_df_index # use actual sim mode date to check smartchswitch if app.getSmartSwitch() == 1 and app.getGranularity( ) == 3600 and app.is1hEMA1226Bull( current_sim_date) is True and app.is6hEMA1226Bull( current_sim_date) is True: Logger.info( '*** smart switch from granularity 3600 (1 hour) to 900 (15 min) ***' ) if app.isSimulation(): app.sim_smartswitch = True app.notifyTelegram( app.getMarket() + " smart switch from granularity 3600 (1 hour) to 900 (15 min)") app.setGranularity(900) list(map(s.cancel, s.queue)) s.enter(5, 1, executeJob, (sc, app, state)) # use actual sim mode date to check smartchswitch if app.getSmartSwitch() == 1 and app.getGranularity( ) == 900 and app.is1hEMA1226Bull( current_sim_date) is False and app.is6hEMA1226Bull( current_sim_date) is False: Logger.info( "*** smart switch from granularity 900 (15 min) to 3600 (1 hour) ***" ) if app.isSimulation(): app.sim_smartswitch = True app.notifyTelegram( app.getMarket() + " smart switch from granularity 900 (15 min) to 3600 (1 hour)") app.setGranularity(3600) list(map(s.cancel, s.queue)) s.enter(5, 1, executeJob, (sc, app, state)) if app.getExchange() == 'binance' and app.getGranularity() == 86400: if len(df) < 250: # data frame should have 250 rows, if not retry Logger.error('error: data frame length is < 250 (' + str(len(df)) + ')') list(map(s.cancel, s.queue)) s.enter(300, 1, executeJob, (sc, app, state)) else: if len(df) < 300: if not app.isSimulation(): # data frame should have 300 rows, if not retry Logger.error('error: data frame length is < 300 (' + str(len(df)) + ')') list(map(s.cancel, s.queue)) s.enter(300, 1, executeJob, (sc, app, state)) if len(df_last) > 0: now = datetime.today().strftime('%Y-%m-%d %H:%M:%S') # last_action polling if live if app.isLive(): last_action_current = state.last_action state.pollLastAction() if last_action_current != state.last_action: Logger.info( f'last_action change detected from {last_action_current} to {state.last_action}' ) app.notifyTelegram( f"{app.getMarket} last_action change detected from {last_action_current} to {state.last_action}" ) if not app.isSimulation(): ticker = app.getTicker(app.getMarket()) now = ticker[0] price = ticker[1] if price < df_last['low'].values[0] or price == 0: price = float(df_last['close'].values[0]) else: price = float(df_last['close'].values[0]) if price < 0.0001: raise Exception( app.getMarket() + ' is unsuitable for trading, quote price is less than 0.0001!') # technical indicators ema12gtema26 = bool(df_last['ema12gtema26'].values[0]) ema12gtema26co = bool(df_last['ema12gtema26co'].values[0]) goldencross = bool(df_last['goldencross'].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]) elder_ray_buy = bool(df_last['eri_buy'].values[0]) elder_ray_sell = bool(df_last['eri_sell'].values[0]) # if simulation, set goldencross based on actual sim date if app.isSimulation(): goldencross = app.is1hSMA50200Bull(current_sim_date) # if simulation interations < 200 set goldencross to true #if app.isSimulation() and state.iterations < 200: # goldencross = True # 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]) strategy = Strategy(app, state, df, state.iterations) state.action = strategy.getAction() immediate_action = False margin, profit, sell_fee = 0, 0, 0 if state.last_buy_size > 0 and state.last_buy_price > 0 and price > 0 and state.last_action == 'BUY': # update last buy high if price > state.last_buy_high: state.last_buy_high = price if state.last_buy_high > 0: change_pcnt_high = ((price / state.last_buy_high) - 1) * 100 else: change_pcnt_high = 0 # buy and sell calculations state.last_buy_fee = round(state.last_buy_size * app.getTakerFee(), 8) state.last_buy_filled = round( ((state.last_buy_size - state.last_buy_fee) / state.last_buy_price), 8) # if not a simulation, sync with exchange orders if not app.isSimulation(): exchange_last_buy = app.getLastBuy() if exchange_last_buy is not None: if state.last_buy_size != exchange_last_buy['size']: state.last_buy_size = exchange_last_buy['size'] if state.last_buy_filled != exchange_last_buy['filled']: state.last_buy_filled = exchange_last_buy['filled'] if state.last_buy_price != exchange_last_buy['price']: state.last_buy_price = exchange_last_buy['price'] if app.getExchange() == 'coinbasepro': if state.last_buy_fee != exchange_last_buy['fee']: state.last_buy_fee = exchange_last_buy['fee'] margin, profit, sell_fee = calculate_margin( buy_size=state.last_buy_size, buy_filled=state.last_buy_filled, buy_price=state.last_buy_price, buy_fee=state.last_buy_fee, sell_percent=app.getSellPercent(), sell_price=price, sell_taker_fee=app.getTakerFee()) # handle immedate sell actions if strategy.isSellTrigger(price, technical_analysis.getTradeExit(price), margin, change_pcnt_high, obv_pc, macdltsignal): state.action = 'SELL' state.last_action = 'BUY' immediate_action = True # handle overriding wait actions (do not sell if sell at loss disabled!) if strategy.isWaitTrigger(margin): state.action = 'WAIT' state.last_action = 'BUY' immediate_action = False bullbeartext = '' if app.disableBullOnly() is True or (df_last['sma50'].values[0] == df_last['sma200'].values[0]): bullbeartext = '' elif goldencross is True: bullbeartext = ' (BULL)' elif goldencross is False: bullbeartext = ' (BEAR)' # polling is every 5 minutes (even for hourly intervals), but only process once per interval if (immediate_action is True or state.last_df_index != current_df_index): precision = 4 if (price < 0.01): precision = 8 # Since precision does not change after this point, it is safe to prepare a tailored `truncate()` that would # work with this precision. It should save a couple of `precision` uses, one for each `truncate()` call. truncate = functools.partial(_truncate, n=precision) price_text = 'Close: ' + truncate(price) ema_text = '' if app.disableBuyEMA() is False: ema_text = app.compare(df_last['ema12'].values[0], df_last['ema26'].values[0], 'EMA12/26', precision) macd_text = '' if app.disableBuyMACD() is False: macd_text = app.compare(df_last['macd'].values[0], df_last['signal'].values[0], 'MACD', precision) obv_text = '' if app.disableBuyOBV() is False: obv_text = 'OBV: ' + truncate( df_last['obv'].values[0]) + ' (' + str( truncate(df_last['obv_pc'].values[0])) + '%)' state.eri_text = '' if app.disableBuyElderRay() is False: if elder_ray_buy is True: state.eri_text = 'ERI: buy | ' elif elder_ray_sell is True: state.eri_text = 'ERI: sell | ' else: state.eri_text = 'ERI: | ' if hammer is True: log_text = '* Candlestick Detected: Hammer ("Weak - Reversal - Bullish Signal - Up")' Logger.info(log_text) if shooting_star is True: log_text = '* Candlestick Detected: Shooting Star ("Weak - Reversal - Bearish Pattern - Down")' Logger.info(log_text) if hanging_man is True: log_text = '* Candlestick Detected: Hanging Man ("Weak - Continuation - Bearish Pattern - Down")' Logger.info(log_text) if inverted_hammer is True: log_text = '* Candlestick Detected: Inverted Hammer ("Weak - Continuation - Bullish Pattern - Up")' Logger.info(log_text) if three_white_soldiers is True: log_text = '*** Candlestick Detected: Three White Soldiers ("Strong - Reversal - Bullish Pattern - Up")' Logger.info(log_text) app.notifyTelegram(app.getMarket() + ' (' + app.printGranularity() + ') ' + log_text) if three_black_crows is True: log_text = '* Candlestick Detected: Three Black Crows ("Strong - Reversal - Bearish Pattern - Down")' Logger.info(log_text) app.notifyTelegram(app.getMarket() + ' (' + app.printGranularity() + ') ' + log_text) if morning_star is True: log_text = '*** Candlestick Detected: Morning Star ("Strong - Reversal - Bullish Pattern - Up")' Logger.info(log_text) app.notifyTelegram(app.getMarket() + ' (' + app.printGranularity() + ') ' + log_text) if evening_star is True: log_text = '*** Candlestick Detected: Evening Star ("Strong - Reversal - Bearish Pattern - Down")' Logger.info(log_text) app.notifyTelegram(app.getMarket() + ' (' + app.printGranularity() + ') ' + log_text) if three_line_strike is True: log_text = '** Candlestick Detected: Three Line Strike ("Reliable - Reversal - Bullish Pattern - Up")' Logger.info(log_text) app.notifyTelegram(app.getMarket() + ' (' + app.printGranularity() + ') ' + log_text) if abandoned_baby is True: log_text = '** Candlestick Detected: Abandoned Baby ("Reliable - Reversal - Bullish Pattern - Up")' Logger.info(log_text) app.notifyTelegram(app.getMarket() + ' (' + app.printGranularity() + ') ' + log_text) if morning_doji_star is True: log_text = '** Candlestick Detected: Morning Doji Star ("Reliable - Reversal - Bullish Pattern - Up")' Logger.info(log_text) app.notifyTelegram(app.getMarket() + ' (' + app.printGranularity() + ') ' + log_text) if evening_doji_star is True: log_text = '** Candlestick Detected: Evening Doji Star ("Reliable - Reversal - Bearish Pattern - Down")' Logger.info(log_text) app.notifyTelegram(app.getMarket() + ' (' + app.printGranularity() + ') ' + log_text) if two_black_gapping is True: log_text = '*** Candlestick Detected: Two Black Gapping ("Reliable - Reversal - Bearish Pattern - Down")' Logger.info(log_text) app.notifyTelegram(app.getMarket() + ' (' + app.printGranularity() + ') ' + log_text) ema_co_prefix = '' ema_co_suffix = '' if app.disableBuyEMA() is False: if ema12gtema26co is True: ema_co_prefix = '*^ ' ema_co_suffix = ' ^*' elif ema12ltema26co is True: ema_co_prefix = '*v ' ema_co_suffix = ' v*' elif ema12gtema26 is True: ema_co_prefix = '^ ' ema_co_suffix = ' ^' elif ema12ltema26 is True: ema_co_prefix = 'v ' ema_co_suffix = ' v' macd_co_prefix = '' macd_co_suffix = '' if app.disableBuyMACD() is False: if macdgtsignalco is True: macd_co_prefix = '*^ ' macd_co_suffix = ' ^*' elif macdltsignalco is True: macd_co_prefix = '*v ' macd_co_suffix = ' v*' elif macdgtsignal is True: macd_co_prefix = '^ ' macd_co_suffix = ' ^' elif macdltsignal is True: macd_co_prefix = 'v ' macd_co_suffix = ' v' obv_prefix = '' obv_suffix = '' if app.disableBuyOBV() is False: if float(obv_pc) > 0: obv_prefix = '^ ' obv_suffix = ' ^ | ' elif float(obv_pc) < 0: obv_prefix = 'v ' obv_suffix = ' v | ' if not app.isVerbose(): if state.last_action != '': output_text = formatted_current_df_index + ' | ' + app.getMarket() + bullbeartext + ' | ' + \ app.printGranularity() + ' | ' + price_text + ' | ' + ema_co_prefix + \ ema_text + ema_co_suffix + ' | ' + macd_co_prefix + macd_text + macd_co_suffix + \ obv_prefix + obv_text + obv_suffix + state.eri_text + ' | ' + state.action + \ ' | Last Action: ' + state.last_action else: output_text = formatted_current_df_index + ' | ' + app.getMarket() + bullbeartext + ' | ' + \ app.printGranularity() + ' | ' + price_text + ' | ' + ema_co_prefix + \ ema_text + ema_co_suffix + ' | ' + macd_co_prefix + macd_text + macd_co_suffix + \ obv_prefix + obv_text + obv_suffix + state.eri_text + ' | ' + state.action + ' ' if state.last_action == 'BUY': if state.last_buy_size > 0: margin_text = truncate(margin) + '%' else: margin_text = '0%' output_text += ' | ' + margin_text + ' (delta: ' + str( round(price - state.last_buy_price, precision)) + ')' Logger.info(output_text) # Seasonal Autoregressive Integrated Moving Average (ARIMA) model (ML prediction for 3 intervals from now) if not app.isSimulation(): try: prediction = technical_analysis.seasonalARIMAModelPrediction( int(app.getGranularity() / 60) * 3) # 3 intervals from now Logger.info( f'Seasonal ARIMA model predicts the closing price will be {str(round(prediction[1], 2))} at {prediction[0]} (delta: {round(prediction[1] - price, 2)})' ) except: pass if state.last_action == 'BUY': # display support, resistance and fibonacci levels Logger.info( technical_analysis. printSupportResistanceFibonacciLevels(price)) else: Logger.debug('-- Iteration: ' + str(state.iterations) + ' --' + bullbeartext) if state.last_action == 'BUY': if state.last_buy_size > 0: margin_text = truncate(margin) + '%' else: margin_text = '0%' Logger.debug('-- Margin: ' + margin_text + ' --') Logger.debug('price: ' + truncate(price)) Logger.debug('ema12: ' + truncate(float(df_last['ema12'].values[0]))) Logger.debug('ema26: ' + truncate(float(df_last['ema26'].values[0]))) Logger.debug('ema12gtema26co: ' + str(ema12gtema26co)) Logger.debug('ema12gtema26: ' + str(ema12gtema26)) Logger.debug('ema12ltema26co: ' + str(ema12ltema26co)) Logger.debug('ema12ltema26: ' + str(ema12ltema26)) Logger.debug('sma50: ' + truncate(float(df_last['sma50'].values[0]))) Logger.debug('sma200: ' + truncate(float(df_last['sma200'].values[0]))) Logger.debug('macd: ' + truncate(float(df_last['macd'].values[0]))) Logger.debug('signal: ' + truncate(float(df_last['signal'].values[0]))) Logger.debug('macdgtsignal: ' + str(macdgtsignal)) Logger.debug('macdltsignal: ' + str(macdltsignal)) Logger.debug('obv: ' + str(obv)) Logger.debug('obv_pc: ' + str(obv_pc)) Logger.debug('action: ' + state.action) # informational output on the most recent entry Logger.info('') Logger.info( '================================================================================' ) txt = ' Iteration : ' + str( state.iterations) + bullbeartext Logger.info(' | ' + txt + (' ' * (75 - len(txt))) + ' | ') txt = ' Timestamp : ' + str(df_last.index.format()[0]) Logger.info(' | ' + txt + (' ' * (75 - len(txt))) + ' | ') Logger.info( '--------------------------------------------------------------------------------' ) txt = ' Close : ' + truncate(price) Logger.info(' | ' + txt + (' ' * (75 - len(txt))) + ' | ') txt = ' EMA12 : ' + truncate( float(df_last['ema12'].values[0])) Logger.info(' | ' + txt + (' ' * (75 - len(txt))) + ' | ') txt = ' EMA26 : ' + truncate( float(df_last['ema26'].values[0])) Logger.info(' | ' + txt + (' ' * (75 - len(txt))) + ' | ') txt = ' Crossing Above : ' + str(ema12gtema26co) Logger.info(' | ' + txt + (' ' * (75 - len(txt))) + ' | ') txt = ' Currently Above : ' + str(ema12gtema26) Logger.info(' | ' + txt + (' ' * (75 - len(txt))) + ' | ') txt = ' Crossing Below : ' + str(ema12ltema26co) Logger.info(' | ' + txt + (' ' * (75 - len(txt))) + ' | ') txt = ' Currently Below : ' + str(ema12ltema26) Logger.info(' | ' + txt + (' ' * (75 - len(txt))) + ' | ') if (ema12gtema26 is True and ema12gtema26co is True): txt = ' Condition : EMA12 is currently crossing above EMA26' elif (ema12gtema26 is True and ema12gtema26co is False): txt = ' Condition : EMA12 is currently above EMA26 and has crossed over' elif (ema12ltema26 is True and ema12ltema26co is True): txt = ' Condition : EMA12 is currently crossing below EMA26' elif (ema12ltema26 is True and ema12ltema26co is False): txt = ' Condition : EMA12 is currently below EMA26 and has crossed over' else: txt = ' Condition : -' Logger.info(' | ' + txt + (' ' * (75 - len(txt))) + ' | ') txt = ' SMA20 : ' + truncate( float(df_last['sma20'].values[0])) Logger.info(' | ' + txt + (' ' * (75 - len(txt))) + ' | ') txt = ' SMA200 : ' + truncate( float(df_last['sma200'].values[0])) Logger.info(' | ' + txt + (' ' * (75 - len(txt))) + ' | ') Logger.info( '--------------------------------------------------------------------------------' ) txt = ' MACD : ' + truncate( float(df_last['macd'].values[0])) Logger.info(' | ' + txt + (' ' * (75 - len(txt))) + ' | ') txt = ' Signal : ' + truncate( float(df_last['signal'].values[0])) Logger.info(' | ' + txt + (' ' * (75 - len(txt))) + ' | ') txt = ' Currently Above : ' + str(macdgtsignal) Logger.info(' | ' + txt + (' ' * (75 - len(txt))) + ' | ') txt = ' Currently Below : ' + str(macdltsignal) Logger.info(' | ' + txt + (' ' * (75 - len(txt))) + ' | ') if (macdgtsignal is True and macdgtsignalco is True): txt = ' Condition : MACD is currently crossing above Signal' elif (macdgtsignal is True and macdgtsignalco is False): txt = ' Condition : MACD is currently above Signal and has crossed over' elif (macdltsignal is True and macdltsignalco is True): txt = ' Condition : MACD is currently crossing below Signal' elif (macdltsignal is True and macdltsignalco is False): txt = ' Condition : MACD is currently below Signal and has crossed over' else: txt = ' Condition : -' Logger.info(' | ' + txt + (' ' * (75 - len(txt))) + ' | ') Logger.info( '--------------------------------------------------------------------------------' ) txt = ' Action : ' + state.action Logger.info(' | ' + txt + (' ' * (75 - len(txt))) + ' | ') Logger.info( '================================================================================' ) if state.last_action == 'BUY': txt = ' Margin : ' + margin_text Logger.info(' | ' + txt + (' ' * (75 - len(txt))) + ' | ') Logger.info( '================================================================================' ) # if a buy signal if state.action == 'BUY': state.last_buy_price = price state.last_buy_high = state.last_buy_price # if live if app.isLive(): app.notifyTelegram(app.getMarket() + ' (' + app.printGranularity() + ') BUY at ' + price_text) if not app.isVerbose(): Logger.info(formatted_current_df_index + ' | ' + app.getMarket() + ' | ' + app.printGranularity() + ' | ' + price_text + ' | BUY') else: Logger.info( '--------------------------------------------------------------------------------' ) Logger.info( '| *** Executing LIVE Buy Order *** |' ) Logger.info( '--------------------------------------------------------------------------------' ) # display balances Logger.info(app.getBaseCurrency() + ' balance before order: ' + str(account.getBalance(app.getBaseCurrency()))) Logger.info( app.getQuoteCurrency() + ' balance before order: ' + str(account.getBalance(app.getQuoteCurrency()))) # execute a live market buy state.last_buy_size = float( account.getBalance(app.getQuoteCurrency())) if app.getBuyMaxSize( ) and state.last_buy_size > app.getBuyMaxSize(): state.last_buy_size = app.getBuyMaxSize() resp = app.marketBuy(app.getMarket(), state.last_buy_size, app.getBuyPercent()) Logger.debug(resp) # display balances Logger.info(app.getBaseCurrency() + ' balance after order: ' + str(account.getBalance(app.getBaseCurrency()))) Logger.info( app.getQuoteCurrency() + ' balance after order: ' + str(account.getBalance(app.getQuoteCurrency()))) # if not live else: app.notifyTelegram(app.getMarket() + ' (' + app.printGranularity() + ') TEST BUY at ' + price_text) # TODO: Improve simulator calculations by including calculations for buy and sell limit configurations. if state.last_buy_size == 0 and state.last_buy_filled == 0: state.last_buy_size = 1000 state.first_buy_size = 1000 state.buy_count = state.buy_count + 1 state.buy_sum = state.buy_sum + state.last_buy_size if not app.isVerbose(): Logger.info(formatted_current_df_index + ' | ' + app.getMarket() + ' | ' + app.printGranularity() + ' | ' + price_text + ' | BUY') bands = technical_analysis.getFibonacciRetracementLevels( float(price)) Logger.info(' Fibonacci Retracement Levels:' + str(bands)) technical_analysis.printSupportResistanceLevel( float(price)) if len(bands) >= 1 and len(bands) <= 2: if len(bands) == 1: first_key = list(bands.keys())[0] if first_key == 'ratio1': state.fib_low = 0 state.fib_high = bands[first_key] if first_key == 'ratio1_618': state.fib_low = bands[first_key] state.fib_high = bands[first_key] * 2 else: state.fib_low = bands[first_key] elif len(bands) == 2: first_key = list(bands.keys())[0] second_key = list(bands.keys())[1] state.fib_low = bands[first_key] state.fib_high = bands[second_key] else: Logger.info( '--------------------------------------------------------------------------------' ) Logger.info( '| *** Executing TEST Buy Order *** |' ) Logger.info( '--------------------------------------------------------------------------------' ) if app.shouldSaveGraphs(): tradinggraphs = TradingGraphs(technical_analysis) ts = datetime.now().timestamp() filename = app.getMarket() + '_' + app.printGranularity( ) + '_buy_' + str(ts) + '.png' tradinggraphs.renderEMAandMACD(len(trading_data), 'graphs/' + filename, True) # if a sell signal elif state.action == 'SELL': # if live if app.isLive(): app.notifyTelegram( app.getMarket() + ' (' + app.printGranularity() + ') SELL at ' + price_text + ' (margin: ' + margin_text + ', (delta: ' + str(round(price - state.last_buy_price, precision)) + ')') if not app.isVerbose(): Logger.info(formatted_current_df_index + ' | ' + app.getMarket() + ' | ' + app.printGranularity() + ' | ' + price_text + ' | SELL') bands = technical_analysis.getFibonacciRetracementLevels( float(price)) Logger.info(' Fibonacci Retracement Levels:' + str(bands)) if len(bands) >= 1 and len(bands) <= 2: if len(bands) == 1: first_key = list(bands.keys())[0] if first_key == 'ratio1': state.fib_low = 0 state.fib_high = bands[first_key] if first_key == 'ratio1_618': state.fib_low = bands[first_key] state.fib_high = bands[first_key] * 2 else: state.fib_low = bands[first_key] elif len(bands) == 2: first_key = list(bands.keys())[0] second_key = list(bands.keys())[1] state.fib_low = bands[first_key] state.fib_high = bands[second_key] else: Logger.info( '--------------------------------------------------------------------------------' ) Logger.info( '| *** Executing LIVE Sell Order *** |' ) Logger.info( '--------------------------------------------------------------------------------' ) # display balances Logger.info(app.getBaseCurrency() + ' balance before order: ' + str(account.getBalance(app.getBaseCurrency()))) Logger.info( app.getQuoteCurrency() + ' balance before order: ' + str(account.getBalance(app.getQuoteCurrency()))) # execute a live market sell resp = app.marketSell( app.getMarket(), float(account.getBalance(app.getBaseCurrency())), app.getSellPercent()) Logger.debug(resp) # display balances Logger.info(app.getBaseCurrency() + ' balance after order: ' + str(account.getBalance(app.getBaseCurrency()))) Logger.info( app.getQuoteCurrency() + ' balance after order: ' + str(account.getBalance(app.getQuoteCurrency()))) # if not live else: margin, profit, sell_fee = calculate_margin( buy_size=state.last_buy_size, buy_filled=state.last_buy_filled, buy_price=state.last_buy_price, buy_fee=state.last_buy_fee, sell_percent=app.getSellPercent(), sell_price=price, sell_taker_fee=app.getTakerFee()) if state.last_buy_size > 0: margin_text = truncate(margin) + '%' else: margin_text = '0%' app.notifyTelegram( app.getMarket() + ' (' + app.printGranularity() + ') TEST SELL at ' + price_text + ' (margin: ' + margin_text + ', (delta: ' + str(round(price - state.last_buy_price, precision)) + ')') # Preserve next buy values for simulator state.sell_count = state.sell_count + 1 buy_size = ((app.getSellPercent() / 100) * ((price / state.last_buy_price) * (state.last_buy_size - state.last_buy_fee))) state.last_buy_size = buy_size - sell_fee state.sell_sum = state.sell_sum + state.last_buy_size if not app.isVerbose(): if price > 0: margin_text = truncate(margin) + '%' else: margin_text = '0%' Logger.info(formatted_current_df_index + ' | ' + app.getMarket() + ' | ' + app.printGranularity() + ' | SELL | ' + str(price) + ' | BUY | ' + str(state.last_buy_price) + ' | DIFF | ' + str(price - state.last_buy_price) + ' | DIFF | ' + str(profit) + ' | MARGIN NO FEES | ' + margin_text + ' | MARGIN FEES | ' + str(round(sell_fee, precision))) else: Logger.info( '--------------------------------------------------------------------------------' ) Logger.info( '| *** Executing TEST Sell Order *** |' ) Logger.info( '--------------------------------------------------------------------------------' ) if app.shouldSaveGraphs(): tradinggraphs = TradingGraphs(technical_analysis) ts = datetime.now().timestamp() filename = app.getMarket() + '_' + app.printGranularity( ) + '_sell_' + str(ts) + '.png' tradinggraphs.renderEMAandMACD(len(trading_data), 'graphs/' + filename, True) # last significant action if state.action in ['BUY', 'SELL']: state.last_action = state.action state.last_df_index = str(df_last.index.format()[0]) if not app.isLive() and state.iterations == len(df): Logger.info("\nSimulation Summary: ") if state.buy_count > state.sell_count and app.allowSellAtLoss( ): # Calculate last sell size state.last_buy_size = ((app.getSellPercent() / 100) * ( (price / state.last_buy_price) * (state.last_buy_size - state.last_buy_fee))) # Reduce sell fee from last sell size state.last_buy_size = state.last_buy_size - state.last_buy_price * app.getTakerFee( ) state.sell_sum = state.sell_sum + state.last_buy_size state.sell_count = state.sell_count + 1 elif state.buy_count > state.sell_count and not app.allowSellAtLoss( ): Logger.info("\n") Logger.info( ' Note : "sell at loss" is disabled and you have an open trade, if the margin' ) Logger.info( ' result below is negative it will assume you sold at the end of the' ) Logger.info( ' simulation which may not be ideal. Try setting --sellatloss 1' ) Logger.info("\n") Logger.info(' Buy Count : ' + str(state.buy_count)) Logger.info(' Sell Count : ' + str(state.sell_count)) Logger.info(' First Buy : ' + str(state.first_buy_size)) Logger.info(' Last Sell : ' + str(state.last_buy_size)) app.notifyTelegram( f"Simulation Summary\n Buy Count: {state.buy_count}\n Sell Count: {state.sell_count}\n First Buy: {state.first_buy_size}\n Last Sell: {state.last_buy_size}\n" ) if state.sell_count > 0: Logger.info("\n") Logger.info(' Margin : ' + _truncate(( ((state.last_buy_size - state.first_buy_size) / state.first_buy_size) * 100), 4) + '%') Logger.info("\n") Logger.info( ' ** non-live simulation, assuming highest fees') app.notifyTelegram( f" Margin: {_truncate((((state.last_buy_size - state.first_buy_size) / state.first_buy_size) * 100), 4)}%\n ** non-live simulation, assuming highest fees\n" ) else: if state.last_buy_size > 0 and state.last_buy_price > 0 and price > 0 and state.last_action == 'BUY': # show profit and margin if already bought Logger.info(now + ' | ' + app.getMarket() + bullbeartext + ' | ' + app.printGranularity() + ' | Current Price: ' + str(price) + ' | Margin: ' + str(margin) + ' | Profit: ' + str(profit)) else: Logger.info(now + ' | ' + app.getMarket() + bullbeartext + ' | ' + app.printGranularity() + ' | Current Price: ' + str(price)) # decrement ignored iteration state.iterations = state.iterations - 1 # if live if not app.disableTracker() and app.isLive(): # update order tracker csv if app.getExchange() == 'binance': account.saveTrackerCSV(app.getMarket()) elif app.getExchange() == 'coinbasepro': account.saveTrackerCSV() if app.isSimulation(): if state.iterations < 300: if app.simuluationSpeed() in ['fast', 'fast-sample']: # fast processing list(map(s.cancel, s.queue)) s.enter(0, 1, executeJob, (sc, app, state, df)) else: # slow processing list(map(s.cancel, s.queue)) s.enter(1, 1, executeJob, (sc, app, state, df)) else: # poll every 1 minute list(map(s.cancel, s.queue)) s.enter(60, 1, executeJob, (sc, app, state))
def executeJob(sc, app=PyCryptoBot(), trading_data=pd.DataFrame()): """Trading bot job which runs at a scheduled interval""" global action, buy_count, buy_sum, iterations, last_action, last_buy, eri_text, last_df_index, sell_count, sell_sum, buy_state, fib_high, fib_low # increment iterations iterations = iterations + 1 if app.isSimulation() == 0: # retrieve the app.getMarket() data trading_data = app.getHistoricalData(app.getMarket(), app.getGranularity()) else: if len(trading_data) == 0: return None # analyse the market data trading_dataCopy = trading_data.copy() ta = TechnicalAnalysis(trading_dataCopy) ta.addAll() df = ta.getDataFrame() if app.isSimulation() == 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) if len(df_last.index.format()) > 0: current_df_index = str(df_last.index.format()[0]) else: current_df_index = last_df_index if app.getSmartSwitch() == 1 and app.getExchange() == 'binance' and app.getGranularity() == '1h' and app.is1hEMA1226Bull() == True and app.is6hEMA1226Bull() == True: print ("*** smart switch from granularity '1h' (1 hour) to '15m' (15 min) ***") # telegram if app.isTelegramEnabled(): telegram = Telegram(app.getTelegramToken(), app.getTelegramClientId()) telegram.send(app.getMarket() + " smart switch from granularity '1h' (1 hour) to '15m' (15 min)") app.setGranularity('15m') list(map(s.cancel, s.queue)) s.enter(5, 1, executeJob, (sc, app)) elif app.getSmartSwitch() == 1 and app.getExchange() == 'coinbasepro' and app.getGranularity() == 3600 and app.is1hEMA1226Bull() == True and app.is6hEMA1226Bull() == True: print ('*** smart switch from granularity 3600 (1 hour) to 900 (15 min) ***') # telegram if app.isTelegramEnabled(): telegram = Telegram(app.getTelegramToken(), app.getTelegramClientId()) telegram.send(app.getMarket() + " smart switch from granularity 3600 (1 hour) to 900 (15 min)") app.setGranularity(900) list(map(s.cancel, s.queue)) s.enter(5, 1, executeJob, (sc, app)) if app.getSmartSwitch() == 1 and app.getExchange() == 'binance' and app.getGranularity() == '15m' and app.is1hEMA1226Bull() == False and app.is6hEMA1226Bull() == False: print ("*** smart switch from granularity '15m' (15 min) to '1h' (1 hour) ***") # telegram if app.isTelegramEnabled(): telegram = Telegram(app.getTelegramToken(), app.getTelegramClientId()) telegram.send(app.getMarket() + " smart switch from granularity '15m' (15 min) to '1h' (1 hour)") app.setGranularity('1h') list(map(s.cancel, s.queue)) s.enter(5, 1, executeJob, (sc, app)) elif app.getSmartSwitch() == 1 and app.getExchange() == 'coinbasepro' and app.getGranularity() == 900 and app.is1hEMA1226Bull() == False and app.is6hEMA1226Bull() == False: print ("*** smart switch from granularity 900 (15 min) to 3600 (1 hour) ***") # telegram if app.isTelegramEnabled(): telegram = Telegram(app.getTelegramToken(), app.getTelegramClientId()) telegram.send(app.getMarket() + " smart switch from granularity 900 (15 min) to 3600 (1 hour)") app.setGranularity(3600) list(map(s.cancel, s.queue)) s.enter(5, 1, executeJob, (sc, app)) if app.getExchange() == 'binance' and str(app.getGranularity()) == '1d': if len(df) < 250: # data frame should have 250 rows, if not retry print('error: data frame length is < 250 (' + str(len(df)) + ')') logging.error('error: data frame length is < 250 (' + str(len(df)) + ')') list(map(s.cancel, s.queue)) s.enter(300, 1, executeJob, (sc, app)) else: 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)) + ')') list(map(s.cancel, s.queue)) s.enter(300, 1, executeJob, (sc, app)) if len(df_last) > 0: if app.isSimulation() == 0: price = app.getTicker(app.getMarket()) if price < df_last['low'].values[0] or price == 0: price = float(df_last['close'].values[0]) else: price = float(df_last['close'].values[0]) if price < 0.0001: raise Exception(app.getMarket() + ' is unsuitable for trading, quote price is less than 0.0001!') # technical indicators ema12gtema26 = bool(df_last['ema12gtema26'].values[0]) ema12gtema26co = bool(df_last['ema12gtema26co'].values[0]) goldencross = bool(df_last['goldencross'].values[0]) #deathcross = bool(df_last['deathcross'].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]) elder_ray_buy = bool(df_last['eri_buy'].values[0]) elder_ray_sell = bool(df_last['eri_sell'].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 goldencross == True and obv_pc > -5 and elder_ray_buy == True 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' last_buy_minus_fees = 0 if last_buy > 0 and last_action == 'BUY': change_pcnt = ((price / last_buy) - 1) * 100 # calculate last buy minus fees fee = last_buy * 0.005 last_buy_minus_fees = last_buy + fee margin = ((price - last_buy_minus_fees) / price) * 100 # loss failsafe sell at fibonacci band if app.allowSellAtLoss() and app.sellLowerPcnt() == None and fib_low > 0 and fib_low >= float(price): action = 'SELL' last_action = 'BUY' log_text = '! Loss Failsafe Triggered (Fibonacci Band: ' + str(fib_low) + ')' print (log_text, "\n") logging.warning(log_text) # telegram if app.isTelegramEnabled(): telegram = Telegram(app.getTelegramToken(), app.getTelegramClientId()) telegram.send(app.getMarket() + ' (' + str(app.getGranularity()) + ') ' + log_text) # loss failsafe sell at sell_lower_pcnt if app.allowSellAtLoss() and app.sellLowerPcnt() != None and change_pcnt < app.sellLowerPcnt(): action = 'SELL' last_action = 'BUY' log_text = '! Loss Failsafe Triggered (< ' + str(app.sellLowerPcnt()) + '%)' print (log_text, "\n") logging.warning(log_text) # telegram if app.isTelegramEnabled(): telegram = Telegram(app.getTelegramToken(), app.getTelegramClientId()) telegram.send(app.getMarket() + ' (' + str(app.getGranularity()) + ') ' + log_text) if app.getSmartSwitch() == 1 and app.getExchange() == 'binance' and app.getGranularity() == '15m' and change_pcnt >= 2: # profit bank at 2% in smart switched mode action = 'SELL' last_action = 'BUY' log_text = '! Profit Bank Triggered (Smart Switch 2%)' print (log_text, "\n") logging.warning(log_text) # telegram if app.isTelegramEnabled(): telegram = Telegram(app.getTelegramToken(), app.getTelegramClientId()) telegram.send(app.getMarket() + ' (' + str(app.getGranularity()) + ') ' + log_text) if app.getSmartSwitch() == 1 and app.getExchange() == 'coinbasepro' and app.getGranularity() == 900 and change_pcnt >= 2: # profit bank at 2% in smart switched mode action = 'SELL' last_action = 'BUY' log_text = '! Profit Bank Triggered (Smart Switch 2%)' print (log_text, "\n") logging.warning(log_text) # telegram if app.isTelegramEnabled(): telegram = Telegram(app.getTelegramToken(), app.getTelegramClientId()) telegram.send(app.getMarket() + ' (' + str(app.getGranularity()) + ') ' + log_text) # profit bank at sell_upper_pcnt if app.sellUpperPcnt() != None and change_pcnt > app.sellUpperPcnt(): action = 'SELL' last_action = 'BUY' log_text = '! Profit Bank Triggered (> ' + str(app.sellUpperPcnt()) + '%)' print (log_text, "\n") logging.warning(log_text) # telegram if app.isTelegramEnabled(): telegram = Telegram(app.getTelegramToken(), app.getTelegramClientId()) telegram.send(app.getMarket() + ' (' + str(app.getGranularity()) + ') ' + log_text) # profit bank at sell at fibonacci band if margin > 3 and app.sellUpperPcnt() != None and fib_high > fib_low and fib_high <= float(price): action = 'SELL' last_action = 'BUY' log_text = '! Profit Bank Triggered (Fibonacci Band: ' + str(fib_high) + ')' print (log_text, "\n") logging.warning(log_text) # telegram if app.isTelegramEnabled(): telegram = Telegram(app.getTelegramToken(), app.getTelegramClientId()) telegram.send(app.getMarket() + ' (' + str(app.getGranularity()) + ') ' + log_text) # profit bank when strong reversal detected if margin > 3 and obv_pc < 0 and macdltsignal == True: action = 'SELL' last_action = 'BUY' log_text = '! Profit Bank Triggered (Strong Reversal Detected)' print (log_text, "\n") logging.warning(log_text) # telegram if app.isTelegramEnabled(): telegram = Telegram(app.getTelegramToken(), app.getTelegramClientId()) telegram.send(app.getMarket() + ' (' + str(app.getGranularity()) + ') ' + log_text) # configuration specifies to not sell at a loss if not app.allowSellAtLoss() and margin <= 0: action = 'WAIT' last_action = 'BUY' log_text = '! Ignore Sell Signal (No Sell At Loss)' print (log_text, "\n") logging.warning(log_text) bullbeartext = '' if df_last['sma50'].values[0] == df_last['sma200'].values[0]: bullbeartext = '' elif goldencross == True: bullbeartext = ' (BULL)' elif goldencross == False: bullbeartext = ' (BEAR)' # polling is every 5 minutes (even for hourly intervals), but only process once per interval if (last_df_index != current_df_index): precision = 2 if (price < 0.01): precision = 8 price_text = 'Close: ' + str(app.truncate(price, precision)) ema_text = app.compare(df_last['ema12'].values[0], df_last['ema26'].values[0], 'EMA12/26', precision) macd_text = app.compare(df_last['macd'].values[0], df_last['signal'].values[0], 'MACD', precision) obv_text = 'OBV: ' + str(app.truncate(df_last['obv'].values[0], 4)) + ' (' + str(app.truncate(df_last['obv_pc'].values[0], 2)) + '%)' if elder_ray_buy == True: eri_text = 'ERI: buy' elif elder_ray_sell == True: eri_text = 'ERI: sell' else: eri_text = 'ERI:' 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) # telegram if app.isTelegramEnabled(): telegram = Telegram(app.getTelegramToken(), app.getTelegramClientId()) telegram.send(app.getMarket() + ' (' + str(app.getGranularity()) + ') ' + 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) # telegram if app.isTelegramEnabled(): telegram = Telegram(app.getTelegramToken(), app.getTelegramClientId()) telegram.send(app.getMarket() + ' (' + str(app.getGranularity()) + ') ' + 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) # telegram if app.isTelegramEnabled(): telegram = Telegram(app.getTelegramToken(), app.getTelegramClientId()) telegram.send(app.getMarket() + ' (' + str(app.getGranularity()) + ') ' + 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) # telegram if app.isTelegramEnabled(): telegram = Telegram(app.getTelegramToken(), app.getTelegramClientId()) telegram.send(app.getMarket() + ' (' + str(app.getGranularity()) + ') ' + 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) # telegram if app.isTelegramEnabled(): telegram = Telegram(app.getTelegramToken(), app.getTelegramClientId()) telegram.send(app.getMarket() + ' (' + str(app.getGranularity()) + ') ' + 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) # telegram if app.isTelegramEnabled(): telegram = Telegram(app.getTelegramToken(), app.getTelegramClientId()) telegram.send(app.getMarket() + ' (' + str(app.getGranularity()) + ') ' + 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) # telegram if app.isTelegramEnabled(): telegram = Telegram(app.getTelegramToken(), app.getTelegramClientId()) telegram.send(app.getMarket() + ' (' + str(app.getGranularity()) + ') ' + 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) # telegram if app.isTelegramEnabled(): telegram = Telegram(app.getTelegramToken(), app.getTelegramClientId()) telegram.send(app.getMarket() + ' (' + str(app.getGranularity()) + ') ' + 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) # telegram if app.isTelegramEnabled(): telegram = Telegram(app.getTelegramToken(), app.getTelegramClientId()) telegram.send(app.getMarket() + ' (' + str(app.getGranularity()) + ') ' + 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 float(obv_pc) > 0: obv_prefix = '^ ' obv_suffix = ' ^' elif float(obv_pc) < 0: obv_prefix = 'v ' obv_suffix = ' v' if app.isVerbose() == 0: if last_action != '': output_text = current_df_index + ' | ' + app.getMarket() + bullbeartext + ' | ' + str(app.getGranularity()) + ' | ' + price_text + ' | ' + ema_co_prefix + ema_text + ema_co_suffix + ' | ' + macd_co_prefix + macd_text + macd_co_suffix + ' | ' + obv_prefix + obv_text + obv_suffix + ' | ' + eri_text + ' | ' + action + ' | Last Action: ' + last_action else: output_text = current_df_index + ' | ' + app.getMarket() + bullbeartext + ' | ' + str(app.getGranularity()) + ' | ' + price_text + ' | ' + ema_co_prefix + ema_text + ema_co_suffix + ' | ' + macd_co_prefix + macd_text + macd_co_suffix + ' | ' + obv_prefix + obv_text + obv_suffix + ' | ' + eri_text + ' | ' + action + ' ' if last_action == 'BUY': if last_buy_minus_fees > 0: margin = str(app.truncate((((price - last_buy_minus_fees) / price) * 100), 2)) + '%' else: margin = '0%' output_text += ' | ' + margin logging.debug(output_text) print (output_text) else: logging.debug('-- Iteration: ' + str(iterations) + ' --' + bullbeartext) if last_action == 'BUY': margin = str(app.truncate((((price - last_buy) / price) * 100), 2)) + '%' logging.debug('-- Margin: ' + margin + '% --') logging.debug('price: ' + str(app.truncate(price, precision))) logging.debug('ema12: ' + str(app.truncate(float(df_last['ema12'].values[0]), precision))) logging.debug('ema26: ' + str(app.truncate(float(df_last['ema26'].values[0]), precision))) logging.debug('ema12gtema26co: ' + str(ema12gtema26co)) logging.debug('ema12gtema26: ' + str(ema12gtema26)) logging.debug('ema12ltema26co: ' + str(ema12ltema26co)) logging.debug('ema12ltema26: ' + str(ema12ltema26)) logging.debug('sma50: ' + str(app.truncate(float(df_last['sma50'].values[0]), precision))) logging.debug('sma200: ' + str(app.truncate(float(df_last['sma200'].values[0]), precision))) logging.debug('macd: ' + str(app.truncate(float(df_last['macd'].values[0]), precision))) logging.debug('signal: ' + str(app.truncate(float(df_last['signal'].values[0]), precision))) 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) + bullbeartext print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' Timestamp : ' + str(df_last.index.format()[0]) print('|', txt, (' ' * (75 - len(txt))), '|') print('--------------------------------------------------------------------------------') txt = ' Close : ' + str(app.truncate(price, precision)) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' EMA12 : ' + str(app.truncate(float(df_last['ema12'].values[0]), precision)) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' EMA26 : ' + str(app.truncate(float(df_last['ema26'].values[0]), precision)) 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))), '|') txt = ' SMA20 : ' + str(app.truncate(float(df_last['sma20'].values[0]), precision)) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' SMA200 : ' + str(app.truncate(float(df_last['sma200'].values[0]), precision)) print('|', txt, (' ' * (75 - len(txt))), '|') print('--------------------------------------------------------------------------------') txt = ' MACD : ' + str(app.truncate(float(df_last['macd'].values[0]), precision)) print('|', txt, (' ' * (75 - len(txt))), '|') txt = ' Signal : ' + str(app.truncate(float(df_last['signal'].values[0]), precision)) 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 = ' Action : ' + action print('|', txt, (' ' * (75 - len(txt))), '|') print('================================================================================') if last_action == 'BUY': txt = ' Margin : ' + margin + '%' print('|', txt, (' ' * (75 - len(txt))), '|') print('================================================================================') # if a buy signal if action == 'BUY': last_buy = price buy_count = buy_count + 1 fee = float(price) * 0.005 price_incl_fees = float(price) + fee buy_sum = buy_sum + price_incl_fees # if live if app.isLive() == 1: # telegram if app.isTelegramEnabled(): telegram = Telegram(app.getTelegramToken(), app.getTelegramClientId()) telegram.send(app.getMarket() + ' (' + str(app.getGranularity()) + ') BUY at ' + price_text) if app.isVerbose() == 0: logging.info(current_df_index + ' | ' + app.getMarket() + ' ' + str(app.getGranularity()) + ' | ' + price_text + ' | BUY') print ("\n", current_df_index, '|', app.getMarket(), str(app.getGranularity()), '|', price_text, '| BUY', "\n") else: print('--------------------------------------------------------------------------------') print('| *** Executing LIVE Buy Order *** |') print('--------------------------------------------------------------------------------') # display balances print (app.getBaseCurrency(), 'balance before order:', account.getBalance(app.getBaseCurrency())) print (app.getQuoteCurrency(), 'balance before order:', account.getBalance(app.getQuoteCurrency())) # execute a live market buy resp = app.marketBuy(app.getMarket(), float(account.getBalance(app.getQuoteCurrency()))) logging.info(resp) # display balances print (app.getBaseCurrency(), 'balance after order:', account.getBalance(app.getBaseCurrency())) print (app.getQuoteCurrency(), 'balance after order:', account.getBalance(app.getQuoteCurrency())) # if not live else: if app.isVerbose() == 0: logging.info(current_df_index + ' | ' + app.getMarket() + ' ' + str(app.getGranularity()) + ' | ' + price_text + ' | BUY') print ("\n", current_df_index, '|', app.getMarket(), str(app.getGranularity()), '|', price_text, '| BUY') bands = ta.getFibonacciRetracementLevels(float(price)) print (' Fibonacci Retracement Levels:', str(bands)) ta.printSupportResistanceLevel(float(price)) if len(bands) >= 1 and len(bands) <= 2: if len(bands) == 1: first_key = list(bands.keys())[0] if first_key == 'ratio1': fib_low = 0 fib_high = bands[first_key] if first_key == 'ratio1_618': fib_low = bands[first_key] fib_high = bands[first_key] * 2 else: fib_low = bands[first_key] elif len(bands) == 2: first_key = list(bands.keys())[0] second_key = list(bands.keys())[1] fib_low = bands[first_key] fib_high = bands[second_key] else: print('--------------------------------------------------------------------------------') print('| *** Executing TEST Buy Order *** |') print('--------------------------------------------------------------------------------') if app.shouldSaveGraphs() == 1: tradinggraphs = TradingGraphs(ta) ts = datetime.now().timestamp() filename = app.getMarket() + '_' + str(app.getGranularity()) + '_buy_' + str(ts) + '.png' tradinggraphs.renderEMAandMACD(len(trading_data), 'graphs/' + filename, True) # if a sell signal elif action == 'SELL': sell_count = sell_count + 1 fee = float(price) * 0.005 price_incl_fees = float(price) - fee sell_sum = sell_sum + price_incl_fees # if live if app.isLive() == 1: # telegram if app.isTelegramEnabled(): telegram = Telegram(app.getTelegramToken(), app.getTelegramClientId()) telegram.send(app.getMarket() + ' (' + str(app.getGranularity()) + ') SELL at ' + price_text) if app.isVerbose() == 0: logging.info(current_df_index + ' | ' + app.getMarket() + ' ' + str(app.getGranularity()) + ' | ' + price_text + ' | SELL') print ("\n", current_df_index, '|', app.getMarket(), str(app.getGranularity()), '|', price_text, '| SELL') bands = ta.getFibonacciRetracementLevels(float(price)) print (' Fibonacci Retracement Levels:', str(bands), "\n") if len(bands) >= 1 and len(bands) <= 2: if len(bands) == 1: first_key = list(bands.keys())[0] if first_key == 'ratio1': fib_low = 0 fib_high = bands[first_key] if first_key == 'ratio1_618': fib_low = bands[first_key] fib_high = bands[first_key] * 2 else: fib_low = bands[first_key] elif len(bands) == 2: first_key = list(bands.keys())[0] second_key = list(bands.keys())[1] fib_low = bands[first_key] fib_high = bands[second_key] else: print('--------------------------------------------------------------------------------') print('| *** Executing LIVE Sell Order *** |') print('--------------------------------------------------------------------------------') # display balances print (app.getBaseCurrency(), 'balance before order:', account.getBalance(app.getBaseCurrency())) print (app.getQuoteCurrency(), 'balance before order:', account.getBalance(app.getQuoteCurrency())) # execute a live market sell resp = app.marketSell(app.getMarket(), float(account.getBalance(app.getBaseCurrency()))) logging.info(resp) # display balances print (app.getBaseCurrency(), 'balance after order:', account.getBalance(app.getBaseCurrency())) print (app.getQuoteCurrency(), 'balance after order:', account.getBalance(app.getQuoteCurrency())) # if not live else: if app.isVerbose() == 0: sell_price = float(str(app.truncate(price, precision))) last_buy_price = float(str(app.truncate(float(last_buy), precision))) buy_sell_diff = round(np.subtract(sell_price, last_buy_price), precision) if (sell_price != 0): buy_sell_margin_no_fees = str(app.truncate((((sell_price - last_buy_price) / sell_price) * 100), 2)) + '%' else: buy_sell_margin_no_fees = '0%' # calculate last buy minus fees buy_fee = last_buy_price * 0.005 last_buy_price_minus_fees = last_buy_price + buy_fee if (sell_price != 0): buy_sell_margin_fees = str(app.truncate((((sell_price - last_buy_price_minus_fees) / sell_price) * 100), 2)) + '%' else: buy_sell_margin_fees = '0%' logging.info(current_df_index + ' | ' + app.getMarket() + ' ' + str(app.getGranularity()) + ' | 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", current_df_index, '|', app.getMarket(), str(app.getGranularity()), '| 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") else: print('--------------------------------------------------------------------------------') print('| *** Executing TEST Sell Order *** |') print('--------------------------------------------------------------------------------') if app.shouldSaveGraphs() == 1: tradinggraphs = TradingGraphs(ta) ts = datetime.now().timestamp() filename = app.getMarket() + '_' + str(app.getGranularity()) + '_sell_' + str(ts) + '.png' tradinggraphs.renderEMAandMACD(len(trading_data), 'graphs/' + filename, True) # last significant action if action in [ 'BUY', 'SELL' ]: last_action = action last_df_index = str(df_last.index.format()[0]) if iterations == len(df): print ("\nSimulation Summary\n") if buy_count > sell_count: fee = price * 0.005 last_price_minus_fees = price - fee sell_sum = sell_sum + last_price_minus_fees sell_count = sell_count + 1 print (' Buy Count :', buy_count) print (' Sell Count :', sell_count, "\n") if sell_count > 0: print (' Margin :', str(app.truncate((((sell_sum - buy_sum) / sell_sum) * 100), 2)) + '%', "\n") print (' ** non-live simulation, assuming highest fees', "\n") else: print (str(app.getTime()), '|', app.getMarket() + bullbeartext, '|', str(app.getGranularity()), '| Current Price:', price) # decrement ignored iteration iterations = iterations - 1 # if live if app.isLive() == 1: # update order tracker csv if app.getExchange() == 'binance': account.saveTrackerCSV(app.getMarket()) elif app.getExchange() == 'coinbasepro': account.saveTrackerCSV() if app.isSimulation() == 1: if iterations < 300: if app.simuluationSpeed() in [ 'fast', 'fast-sample' ]: # fast processing executeJob(sc, app, trading_data) else: # slow processing list(map(s.cancel, s.queue)) s.enter(1, 1, executeJob, (sc, app, trading_data)) else: # poll every 5 minute list(map(s.cancel, s.queue)) s.enter(300, 1, executeJob, (sc, app))
from models.PyCryptoBot import PyCryptoBot from models.Trading import TechnicalAnalysis app = PyCryptoBot() df = app.getHistoricalData(app.getMarket(), app.getGranularity()) model = TechnicalAnalysis(df) model.addATR(14) df = model.getDataFrame() print(df)
def technical_analysis(exchange: str, market: str, g1, g2, g3) -> str: if exchange == 'binance': if not isBinanceMarketValid(market): return f""" {header()} <h4>Invalid Market!</h4> <div class="d-grid gap-2 d-md-flex justify-content-md-end"> <a class="text-dark" href='/{exchange}'><button class="btn btn-primary me-md-2" type="button">Go Back</button></a> </div> {footer()} """ elif exchange == 'coinbasepro': if not isCoinbaseMarketValid(market): return f""" {header()} <h4>Invalid Market!</h4> <div class="d-grid gap-2 d-md-flex justify-content-md-end"> <a class="text-dark" href='/{exchange}'><button class="btn btn-primary me-md-2" type="button">Go Back</button></a> </div> {footer()} """ else: return "Invalid Exchange!" if exchange == 'binance': api = BPublicAPI() if exchange == 'coinbasepro': api = CPublicAPI() ticker = api.getTicker(market) ta = TechnicalAnalysis(api.getHistoricalData(market, g1, None)) ta.addAll() df_15m = ta.getDataFrame() df_15m_last = df_15m.tail(1) ta = TechnicalAnalysis(api.getHistoricalData(market, g2, None)) ta.addAll() df_1h = ta.getDataFrame() df_1h_last = df_1h.tail(1) ta = TechnicalAnalysis(api.getHistoricalData(market, g3, None)) ta.addAll() df_6h = ta.getDataFrame() df_6h_last = df_6h.tail(1) if exchange == 'binance': exchange_name = 'Binance' elif exchange == 'coinbasepro': exchange_name = 'Coinbase Pro' rsi14_15m_class = 'table-normal' rsi14_15m_desc = 'Uneventful' if df_15m_last['rsi14'].values[0] > 70: rsi14_15m_class = 'table-danger' rsi14_15m_desc = 'Overbought (Sell)' elif df_15m_last['rsi14'].values[0] < 30: rsi14_15m_class = 'table-success' rsi14_15m_desc = 'Oversold (Buy)' rsi14_1h_class = 'table-normal' rsi14_1h_desc = 'Uneventful' if df_1h_last['rsi14'].values[0] > 70: rsi14_1h_class = 'table-danger' rsi14_1h_desc = 'Overbought (Sell)' elif df_1h_last['rsi14'].values[0] < 30: rsi14_1h_class = 'table-success' rsi14_1h_desc = 'Oversold (Buy)' rsi14_6h_class = 'table-normal' rsi14_6h_desc = 'Uneventful' if df_6h_last['rsi14'].values[0] > 70: rsi14_6h_class = 'table-danger' rsi14_6h_desc = 'Overbought (Sell)' elif df_6h_last['rsi14'].values[0] < 30: rsi14_6h_class = 'table-success' rsi14_6h_desc = 'Oversold (Buy)' stochrsi14_15m_class = 'table-normal' stochrsi14_15m_desc = 'Uneventful' if df_6h_last['stochrsi14'].values[0] > 0.8: stochrsi14_6h_class = 'table-danger' stochrsi14_6h_desc = 'Overbought (Sell)' elif df_6h_last['stochrsi14'].values[0] < 0.2: stochrsi14_6h_class = 'table-success' stochrsi14_6h_desc = 'Oversold (Buy)' stochrsi14_1h_class = 'table-normal' stochrsi14_1h_desc = 'Uneventful' if df_1h_last['stochrsi14'].values[0] > 0.8: stochrsi14_1h_class = 'table-danger' stochrsi14_1h_desc = 'Overbought (Sell)' elif df_1h_last['stochrsi14'].values[0] < 0.2: stochrsi14_1h_class = 'table-success' stochrsi14_1h_desc = 'Oversold (Buy)' stochrsi14_6h_class = 'table-normal' stochrsi14_6h_desc = 'Uneventful' if df_6h_last['stochrsi14'].values[0] > 0.8: stochrsi14_6h_class = 'table-danger' stochrsi14_6h_desc = 'Overbought (Sell)' elif df_6h_last['stochrsi14'].values[0] < 0.2: stochrsi14_6h_class = 'table-success' stochrsi14_6h_desc = 'Oversold (Buy)' williamsr14_15m_class = 'table-normal' williamsr14_15m_desc = 'Uneventful' if df_15m_last['williamsr14'].values[0] > -20: williamsr14_15m_class = 'table-danger' williamsr14_15m_desc = 'Overbought (Sell)' elif df_15m_last['williamsr14'].values[0] < -80: williamsr14_15m_class = 'table-success' williamsr14_15m_desc = 'Oversold (Buy)' williamsr14_1h_class = 'table-normal' williamsr14_1h_desc = 'Uneventful' if df_1h_last['williamsr14'].values[0] > -20: williamsr14_1h_class = 'table-danger' williamsr14_1h_desc = 'Overbought (Sell)' elif df_1h_last['williamsr14'].values[0] < -80: williamsr14_1h_class = 'table-success' williamsr14_1h_desc = 'Oversold (Buy)' williamsr14_6h_class = 'table-normal' williamsr14_6h_desc = 'Uneventful' if df_6h_last['williamsr14'].values[0] > -20: williamsr14_6h_class = 'table-danger' williamsr14_6h_desc = 'Overbought (Sell)' elif df_6h_last['williamsr14'].values[0] < -80: williamsr14_6h_class = 'table-success' williamsr14_6h_desc = 'Oversold (Buy)' adx14_15m_class = 'table-normal' adx14_15m_desc = 'Normal Trend' if df_15m_last['adx14'].values[0] > 25 and df_15m_last['ema12'].values[0] >= df_15m_last['ema26'].values[0]: adx14_15m_class = 'table-success' adx14_15m_desc = 'Strong Trend Up' elif df_15m_last['adx14'].values[0] > 25 and df_15m_last['ema12'].values[0] < df_15m_last['ema26'].values[0]: adx14_15m_class = 'table-danger' adx14_15m_desc = 'Strong Trend Down' elif df_15m_last['adx14'].values[0] < 20 and df_15m_last['ema12'].values[0] >= df_15m_last['ema26'].values[0]: adx14_15m_class = 'table-success' adx14_15m_desc = 'Weak Trend Up' elif df_15m_last['adx14'].values[0] < 20 and df_15m_last['ema12'].values[0] < df_15m_last['ema26'].values[0]: adx14_15m_class = 'table-danger' adx14_15m_desc = 'Weak Trend Up' adx14_1h_class = 'table-normal' adx14_1h_desc = 'Normal Trend' if df_1h_last['adx14'].values[0] > 25 and df_1h_last['ema12'].values[0] >= df_1h_last['ema26'].values[0]: adx14_1h_class = 'table-success' adx14_1h_desc = 'Strong Trend Up' elif df_1h_last['adx14'].values[0] > 25 and df_1h_last['ema12'].values[0] < df_1h_last['ema26'].values[0]: adx14_1h_class = 'table-danger' adx14_1h_desc = 'Strong Trend Down' elif df_1h_last['adx14'].values[0] < 20 and df_1h_last['ema12'].values[0] >= df_1h_last['ema26'].values[0]: adx14_1h_class = 'table-success' adx14_1h_desc = 'Weak Trend Up' elif df_1h_last['adx14'].values[0] < 20 and df_1h_last['ema12'].values[0] < df_1h_last['ema26'].values[0]: adx14_1h_class = 'table-danger' adx14_1h_desc = 'Weak Trend Up' adx14_6h_class = 'table-normal' adx14_6h_desc = 'Normal Trend' if df_6h_last['adx14'].values[0] > 25 and df_6h_last['ema12'].values[0] >= df_6h_last['ema26'].values[0]: adx14_6h_class = 'table-success' adx14_6h_desc = 'Strong Trend Up' elif df_6h_last['adx14'].values[0] > 25 and df_6h_last['ema12'].values[0] < df_6h_last['ema26'].values[0]: adx14_6h_class = 'table-danger' adx14_6h_desc = 'Strong Trend Down' elif df_6h_last['adx14'].values[0] < 20 and df_6h_last['ema12'].values[0] >= df_6h_last['ema26'].values[0]: adx14_6h_class = 'table-success' adx14_6h_desc = 'Weak Trend Up' elif df_6h_last['adx14'].values[0] < 20 and df_6h_last['ema12'].values[0] < df_6h_last['ema26'].values[0]: adx14_6h_class = 'table-danger' adx14_6h_desc = 'Weak Trend Up' def arima_predictions(even_rows: bool = True): results_ARIMA = ta.seasonalARIMAModel() start_date = df_1h.last_valid_index() end_date = start_date + datetime.timedelta(days=3) arima_pred = results_ARIMA.predict( start=str(start_date), end=str(end_date), dynamic=True ) if even_rows: arima_pred_rows = arima_pred.iloc[::2] else: arima_pred_rows = arima_pred.iloc[1::2] html = "" for index, pred in arima_pred_rows.iteritems(): html += f""" <tbody> <tr class={'table-success' if pred >= ticker[1] else 'table-danger'}> <td>{index}</td> <td>{pred}</td> </tr> </tbody> """ return html return f"""
import pandas as pd from models.Trading import TechnicalAnalysis from models.CoinbasePro import PublicAPI api = PublicAPI() data = api.getHistoricalData('BCH-GBP', 3600) ta = TechnicalAnalysis(data) ta.addEMA(12) ta.addEMA(26) ta.addMACD() ta.addOBV() ta.addEMABuySignals() ta.addMACDBuySignals() df = ta.getDataFrame() print(df.iloc[247:291][[ 'close', 'ema12', 'ema26', 'ema12gtema26', 'ema12gtema26co', 'ema12ltema26', 'ema12ltema26co', 'macd', 'signal', 'macdgtsignal', 'macdltsignal', 'obv_pc' ]])
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))
import sys import pandas as pd sys.path.append('.') # pylint: disable=import-error from models.Trading import TechnicalAnalysis from models.CoinbasePro import PublicAPI api = PublicAPI() data = api.getHistoricalData('BCH-GBP', 3600) ta = TechnicalAnalysis(data) ta.addAll() df = ta.getDataFrame() df.to_csv('ml/data/BCH-GBP_3600.csv', index=False)
def test_should_calculate_addSMA_20(): """ Add the Simple Moving Average (SMA) to the DataFrame : """ # GIVEN a series of values closes_list = [ 0.0003, 0.0004, 0.0010, 0.0020, 0.0009, 0.0008, 0.0009, 0.0010, 0.0012, 0.0015, 0.0025, 0.0015, 0.0014, 0.0016, 0.0030, 0.0032, 0.0035, 0.0024, 0.0023, 0.0022, 0.0021, 0.0020 ] df = pd.DataFrame({ 'date': [ '2021-10-10 14:30:00', '2021-10-10 14:31:00', '2021-10-10 14:32:00', '2021-10-10 14:33:00', '2021-10-10 14:34:00', '2021-10-10 14:35:00', '2021-10-10 14:36:00', '2021-10-10 14:37:00', '2021-10-10 14:38:00', '2021-10-10 14:39:00', '2021-10-10 14:40:00', '2021-10-10 14:41:00', '2021-10-10 14:42:00', '2021-10-10 14:43:00', '2021-10-10 14:44:00', '2021-10-10 14:45:00', '2021-10-10 14:46:00', '2021-10-10 14:47:00', '2021-10-10 14:48:00', '2021-10-10 14:49:00', '2021-10-10 14:50:00', '2021-10-10 14:51:00' ], 'close': closes_list }) df['date'] = pd.to_datetime(df['date'], format="%Y-%d-%m %H:%M:%S") df.set_index(['date']) ta = TechnicalAnalysis(df) # WHEN calculate the cumulative moving average 20 ta.addSMA(20) # THEN actual = ta.getDataFrame() expected = pd.DataFrame({ 'date': [ '2021-10-10 14:30:00', '2021-10-10 14:31:00', '2021-10-10 14:32:00', '2021-10-10 14:33:00', '2021-10-10 14:34:00', '2021-10-10 14:35:00', '2021-10-10 14:36:00', '2021-10-10 14:37:00', '2021-10-10 14:38:00', '2021-10-10 14:39:00', '2021-10-10 14:40:00', '2021-10-10 14:41:00', '2021-10-10 14:42:00', '2021-10-10 14:43:00', '2021-10-10 14:44:00', '2021-10-10 14:45:00', '2021-10-10 14:46:00', '2021-10-10 14:47:00', '2021-10-10 14:48:00', '2021-10-10 14:49:00', '2021-10-10 14:50:00', '2021-10-10 14:51:00' ], 'close': closes_list, 'sma20': [ calculate_mean_on_range(0, 1, closes_list), calculate_mean_on_range(0, 2, closes_list), calculate_mean_on_range(0, 3, closes_list), calculate_mean_on_range(0, 4, closes_list), calculate_mean_on_range(0, 5, closes_list), calculate_mean_on_range(0, 6, closes_list), calculate_mean_on_range(0, 7, closes_list), calculate_mean_on_range(0, 8, closes_list), calculate_mean_on_range(0, 9, closes_list), calculate_mean_on_range(0, 10, closes_list), calculate_mean_on_range(0, 11, closes_list), calculate_mean_on_range(0, 12, closes_list), calculate_mean_on_range(0, 13, closes_list), calculate_mean_on_range(0, 14, closes_list), calculate_mean_on_range(0, 15, closes_list), calculate_mean_on_range(0, 16, closes_list), calculate_mean_on_range(0, 17, closes_list), calculate_mean_on_range(0, 18, closes_list), calculate_mean_on_range(0, 19, closes_list), calculate_mean_on_range(0, 20, closes_list), calculate_mean_on_range(1, 21, closes_list), calculate_mean_on_range(2, 22, closes_list) ] }) expected['date'] = pd.to_datetime(df['date'], format="%Y-%d-%m %H:%M:%S") expected.set_index(['date']) assert_frame_equal(actual, expected)