def get_signal(pair: str, signal: SignalType) -> bool: """ Calculates current signal based several technical analysis indicators :param pair: pair in format BTC_ANT or BTC-ANT :return: True if pair is good for buying, False otherwise """ ticker_hist = get_ticker_history(pair) if not ticker_hist: logger.warning('Empty ticker history for pair %s', pair) return False try: dataframe = analyze_ticker(ticker_hist) except ValueError as ex: logger.warning('Unable to analyze ticker for pair %s: %s', pair, str(ex)) return False except Exception as ex: logger.exception('Unexpected error when analyzing ticker for pair %s: %s', pair, str(ex)) return False if dataframe.empty: return False latest = dataframe.iloc[-1] # Check if dataframe is out of date signal_date = arrow.get(latest['date']) if signal_date < arrow.now() - timedelta(minutes=10): return False result = latest[signal.value] == 1 logger.debug('%s_trigger: %s (pair=%s, signal=%s)', signal.value, latest['date'], pair, result) return result
def test_get_ticker_history(default_conf, mocker): api_mock = MagicMock() tick = 123 api_mock.get_ticker_history = MagicMock(return_value=tick) mocker.patch('freqtrade.exchange._API', api_mock) # retrieve original ticker ticks = get_ticker_history('BTC_ETH', int(default_conf['ticker_interval'])) assert ticks == 123 # change the ticker tick = 999 api_mock.get_ticker_history = MagicMock(return_value=tick) mocker.patch('freqtrade.exchange._API', api_mock) # ensure caching will still return the original ticker ticks = get_ticker_history('BTC_ETH', int(default_conf['ticker_interval'])) assert ticks == 123
def start(args): # Initialize logger logging.basicConfig( level=args.loglevel, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', ) exchange._API = Bittrex({'key': '', 'secret': ''}) logger.info('Using config: %s ...', args.config) config = misc.load_config(args.config) logger.info('Using ticker_interval: %s ...', args.ticker_interval) data = {} pairs = config['exchange']['pair_whitelist'] if args.live: logger.info('Downloading data for all pairs in whitelist ...') for pair in pairs: data[pair] = exchange.get_ticker_history(pair, args.ticker_interval) else: logger.info('Using local backtesting data (using whitelist in given config) ...') data = optimize.load_data(args.datadir, pairs=pairs, ticker_interval=args.ticker_interval, refresh_pairs=args.refresh_pairs) logger.info('Using stake_currency: %s ...', config['stake_currency']) logger.info('Using stake_amount: %s ...', config['stake_amount']) max_open_trades = 0 if args.realistic_simulation: logger.info('Using max_open_trades: %s ...', config['max_open_trades']) max_open_trades = config['max_open_trades'] # Monkey patch config from freqtrade import main main._CONF = config preprocessed = preprocess(data) # Print timeframe min_date, max_date = get_timeframe(preprocessed) logger.info('Measuring data from %s up to %s ...', min_date.isoformat(), max_date.isoformat()) # Execute backtest and print results results = backtest( stake_amount=config['stake_amount'], processed=preprocessed, max_open_trades=max_open_trades, realistic=args.realistic_simulation, sell_profit_only=config.get('experimental', {}).get('sell_profit_only', False), stoploss=config.get('stoploss'), use_sell_signal=config.get('experimental', {}).get('use_sell_signal', False) ) logger.info( '\n==================================== BACKTESTING REPORT ====================================\n%s', # noqa generate_text_table(data, results, config['stake_currency'], args.ticker_interval) )
def get_signal(self, pair: str, interval: int) -> Tuple[bool, bool]: """ Calculates current signal based several technical analysis indicators :param pair: pair in format BTC_ANT or BTC-ANT :param interval: Interval to use (in min) :return: (Buy, Sell) A bool-tuple indicating buy/sell signal """ ticker_hist = get_ticker_history(pair, interval) if not ticker_hist: logger.warning('Empty ticker history for pair %s', pair) return False, False try: dataframe = self.analyze_ticker(ticker_hist) except ValueError as error: logger.warning( 'Unable to analyze ticker for pair %s: %s', pair, str(error) ) return False, False except Exception as error: logger.exception( 'Unexpected error when analyzing ticker for pair %s: %s', pair, str(error) ) return False, False if dataframe.empty: logger.warning('Empty dataframe for pair %s', pair) return False, False latest = dataframe.iloc[-1] # Check if dataframe is out of date signal_date = arrow.get(latest['date']) if signal_date < arrow.utcnow() - timedelta(minutes=(interval + 5)): logger.warning( 'Outdated history for pair %s. Last tick is %s minutes old', pair, (arrow.utcnow() - signal_date).seconds // 60 ) return False, False (buy, sell) = latest[SignalType.BUY.value] == 1, latest[SignalType.SELL.value] == 1 logger.debug( 'trigger: %s (pair=%s) buy=%s sell=%s', latest['date'], pair, str(buy), str(sell) ) return buy, sell
def analyze_ticker(pair: str) -> DataFrame: """ Get ticker data for given currency pair, push it to a DataFrame and add several TA indicators and buy signal to it :return DataFrame with ticker data and indicator data """ ticker_hist = get_ticker_history(pair) if not ticker_hist: logger.warning('Empty ticker history for pair %s', pair) return DataFrame() dataframe = parse_ticker_dataframe(ticker_hist) dataframe = populate_indicators(dataframe) dataframe = populate_buy_trend(dataframe) return dataframe
def download_backtesting_testdata(datadir: str, pair: str, interval: int = 5) -> bool: """ Download the latest 1 and 5 ticker intervals from Bittrex for the pairs passed in parameters Based on @Rybolov work: https://github.com/rybolov/freqtrade-data :param pairs: list of pairs to download :return: bool """ path = make_testdata_path(datadir) logger.info('Download the pair: "{pair}", Interval: {interval} min'.format( pair=pair, interval=interval, )) filepair = pair.replace("-", "_") filename = os.path.join( path, '{pair}-{interval}.json'.format( pair=filepair, interval=interval, )) filename = filename.replace('USDT_BTC', 'BTC_FAKEBULL') if os.path.isfile(filename): with open(filename, "rt") as fp: data = json.load(fp) logger.debug("Current Start: {}".format(data[1]['T'])) logger.debug("Current End: {}".format(data[-1:][0]['T'])) else: data = [] logger.debug("Current Start: None") logger.debug("Current End: None") new_data = get_ticker_history(pair=pair, tick_interval=int(interval)) for row in new_data: if row not in data: data.append(row) logger.debug("New Start: {}".format(data[1]['T'])) logger.debug("New End: {}".format(data[-1:][0]['T'])) data = sorted(data, key=lambda data: data['T']) with open(filename, "wt") as fp: json.dump(data, fp) return True
def analyze_ticker(pair: str, tick_interval: int, exchange_name: str, analyzer: object) -> DataFrame: """ Get ticker data for given currency pair, push it to a DataFrame and add several TA indicators and buy signal to it :return DataFrame with ticker data and indicator data """ data = get_ticker_history(pair, tick_interval) dataframe = parse_ticker_dataframe(data, exchange_name) if dataframe.empty: logger.warning('Empty dataframe for pair %s', pair) return dataframe dataframe = populate_indicators(dataframe) dataframe = populate_buy_trend(dataframe, analyzer) return dataframe
def analyze_ticker(pair: str) -> DataFrame: """ Get ticker data for given currency pair, push it to a DataFrame and add several TA indicators and buy signal to it :return DataFrame with ticker data and indicator data """ minimum_date = arrow.utcnow().shift(hours=-24) data = get_ticker_history(pair, minimum_date) dataframe = parse_ticker_dataframe(data['result'], minimum_date) if dataframe.empty: logger.warning('Empty dataframe for pair %s', pair) return dataframe dataframe = populate_indicators(dataframe) dataframe = populate_buy_trend(dataframe) return dataframe
def get_ema_signal(pair: str, tick_interval: int, exchange_name: str) -> bool: """ Get ticker data for given currency pair, push it to a DataFrame and add several TA indicators and buy signal to it :return DataFrame with ticker data and indicator data """ data = get_ticker_history(pair, tick_interval) dataframe = parse_ticker_dataframe(data, exchange_name) if dataframe.empty: logger.warning('Empty dataframe for pair %s', pair) return False dataframe = populate_indicators(dataframe) ema5_diff = dataframe['ema5'][len(dataframe) - 1] - dataframe['ema5'][len(dataframe) - 2] print(ema5_diff > 0.) return ema5_diff > 0.
def analyze_ticker(pair: str) -> DataFrame: """ Get ticker data for given currency pair, push it to a DataFrame and add several TA indicators and buy signal to it :return DataFrame with ticker data and indicator data """ ticker_hist = get_ticker_history(pair) if not ticker_hist: logger.warning('Empty ticker history for pair %s', pair) return DataFrame() dataframe = parse_ticker_dataframe(ticker_hist) dataframe = populate_indicators(dataframe) dataframe = populate_buy_trend(dataframe) dataframe = populate_sell_trend(dataframe) # TODO: buy_price and sell_price are only used by the plotter, should probably be moved there dataframe.loc[dataframe['buy'] == 1, 'buy_price'] = dataframe['close'] dataframe.loc[dataframe['sell'] == 1, 'sell_price'] = dataframe['close'] return dataframe
def download_backtesting_testdata(datadir: str, pair: str, interval: int = 5) -> bool: """ Download the latest 1 and 5 ticker intervals from Bittrex for the pairs passed in parameters Based on @Rybolov work: https://github.com/rybolov/freqtrade-data :param pairs: list of pairs to download :return: bool """ path = make_testdata_path(datadir) logger.info('Download the pair: "{pair}", Interval: {interval} min'.format( pair=pair, interval=interval, )) filepair = pair.replace("-", "_") filename = os.path.join(path, '{pair}-{interval}.json'.format( pair=filepair, interval=interval, )) filename = filename.replace('USDT_BTC', 'BTC_FAKEBULL') if os.path.isfile(filename): with open(filename, "rt") as fp: data = json.load(fp) logger.debug("Current Start: {}".format(data[1]['T'])) logger.debug("Current End: {}".format(data[-1:][0]['T'])) else: data = [] logger.debug("Current Start: None") logger.debug("Current End: None") new_data = get_ticker_history(pair=pair, tick_interval=int(interval)) for row in new_data: if row not in data: data.append(row) logger.debug("New Start: {}".format(data[1]['T'])) logger.debug("New End: {}".format(data[-1:][0]['T'])) data = sorted(data, key=lambda data: data['T']) with open(filename, "wt") as fp: json.dump(data, fp) return True
def test_backtest(backtest_conf, mocker): print('') exchange._API = Bittrex({'key': '', 'secret': ''}) # Load configuration file based on env variable conf_path = os.environ.get('BACKTEST_CONFIG') if conf_path: print('Using config: {} ...'.format(conf_path)) config = load_config(conf_path) else: config = backtest_conf # Parse ticker interval ticker_interval = int(os.environ.get('BACKTEST_TICKER_INTERVAL') or 5) print('Using ticker_interval: {} ...'.format(ticker_interval)) data = {} if os.environ.get('BACKTEST_LIVE'): print('Downloading data for all pairs in whitelist ...') for pair in config['exchange']['pair_whitelist']: data[pair] = exchange.get_ticker_history(pair, ticker_interval) else: print( 'Using local backtesting data (ignoring whitelist in given config)...' ) data = load_backtesting_data(ticker_interval) print('Using stake_currency: {} ...\nUsing stake_amount: {} ...'.format( config['stake_currency'], config['stake_amount'])) # Print timeframe min_date, max_date = get_timeframe(data) print('Measuring data from {} up to {} ...'.format(min_date.isoformat(), max_date.isoformat())) # Execute backtest and print results results = backtest(config, preprocess(data), mocker) print( '====================== BACKTESTING REPORT ======================================\n\n' 'NOTE: This Report doesn\'t respect the limits of max_open_trades, \n' ' so the projected values should be taken with a grain of salt.\n' ) print(generate_text_table(data, results, config['stake_currency']))
def plot_analyzed_dataframe(args) -> None: """ Calls analyze() and plots the returned dataframe :param pair: pair as str :return: None """ pair = args.pair # Init Bittrex to use public API exchange._API = exchange.Bittrex({'key': '', 'secret': ''}) ticker = exchange.get_ticker_history(pair) dataframe = analyze.analyze_ticker(ticker) dataframe.loc[dataframe['buy'] == 1, 'buy_price'] = dataframe['close'] dataframe.loc[dataframe['sell'] == 1, 'sell_price'] = dataframe['close'] # Two subplots sharing x axis fig, (ax1, ax2, ax3) = plt.subplots(3, sharex=True) fig.suptitle(pair, fontsize=14, fontweight='bold') ax1.plot(dataframe.index.values, dataframe['close'], label='close') # ax1.plot(dataframe.index.values, dataframe['sell'], 'ro', label='sell') ax1.plot(dataframe.index.values, dataframe['sma'], '--', label='SMA') ax1.plot(dataframe.index.values, dataframe['tema'], ':', label='TEMA') ax1.plot(dataframe.index.values, dataframe['blower'], '-.', label='BB low') ax1.plot(dataframe.index.values, dataframe['buy_price'], 'bo', label='buy') ax1.legend() ax2.plot(dataframe.index.values, dataframe['adx'], label='ADX') ax2.plot(dataframe.index.values, dataframe['mfi'], label='MFI') # ax2.plot(dataframe.index.values, [25] * len(dataframe.index.values)) ax2.legend() ax3.plot(dataframe.index.values, dataframe['fastk'], label='k') ax3.plot(dataframe.index.values, dataframe['fastd'], label='d') ax3.plot(dataframe.index.values, [20] * len(dataframe.index.values)) ax3.legend() # Fine-tune figure; make subplots close to each other and hide x ticks for # all but bottom plot. fig.subplots_adjust(hspace=0) plt.setp([a.get_xticklabels() for a in fig.axes[:-1]], visible=False) plt.show()
def download_backtesting_testdata(datadir: str, pair: str, interval: int = 5) -> None: """ Download the latest 1 and 5 ticker intervals from Bittrex for the pairs passed in parameters Based on @Rybolov work: https://github.com/rybolov/freqtrade-data """ path = make_testdata_path(datadir) logger.info('Download the pair: "%s", Interval: %s min', pair, interval) filename = os.path.join( path, '{pair}-{interval}.json'.format( pair=pair.replace("-", "_"), interval=interval, )) if os.path.isfile(filename): with open(filename, "rt") as file: data = json.load(file) else: data = [] logger.debug('Current Start: %s', data[1]['T'] if data else None) logger.debug('Current End: %s', data[-1:][0]['T'] if data else None) # Extend data with new ticker history data.extend([ row for row in get_ticker_history(pair=pair, tick_interval=int(interval)) if row not in data ]) data = sorted(data, key=lambda _data: _data['T']) logger.debug('New Start: %s', data[1]['T']) logger.debug('New End: %s', data[-1:][0]['T']) misc.file_dump_json(filename, data)
# ensure directory exists base_path = os.path.join(os.path.expanduser('~'), 'freqtrade') # get configuration with open(os.path.join(base_path, 'config.json')) as file: _CONF = json.load(file) # initialize the exchange exchange.init(_CONF) # get ticker data = exchange.get_ticker(pair='ETH_BTC') print(data) # get ticker history df = exchange.get_ticker_history(pair='ETH_BTC', tick_interval=1) print(pd.DataFrame(df)) # get markets data = exchange.get_markets() print(data) # get name print(exchange.get_name()) print(exchange.get_sleep_time()) print(exchange.get_fee())
def start(self) -> None: """ Run a backtesting end-to-end :return: None """ data = {} pairs = self.config['exchange']['pair_whitelist'] logger.info('Using stake_currency: %s ...', self.config['stake_currency']) logger.info('Using stake_amount: %s ...', self.config['stake_amount']) if self.config.get('live'): logger.info('Downloading data for all pairs in whitelist ...') for pair in pairs: data[pair] = exchange.get_ticker_history(pair, self.ticker_interval) else: logger.info('Using local backtesting data (using whitelist in given config) ...') timerange = Arguments.parse_timerange(self.config.get('timerange')) data = optimize.load_data( self.config['datadir'], pairs=pairs, ticker_interval=self.ticker_interval, refresh_pairs=self.config.get('refresh_pairs', False), timerange=timerange ) # Ignore max_open_trades in backtesting, except realistic flag was passed if self.config.get('realistic_simulation', False): max_open_trades = self.config['max_open_trades'] else: logger.info('Ignoring max_open_trades (realistic_simulation not set) ...') max_open_trades = 0 preprocessed = self.tickerdata_to_dataframe(data) print(preprocessed) # Print timeframe min_date, max_date = self.get_timeframe(preprocessed) logger.info( 'Measuring data from %s up to %s (%s days)..', min_date.isoformat(), max_date.isoformat(), (max_date - min_date).days ) # Execute backtest and print results sell_profit_only = self.config.get('experimental', {}).get('sell_profit_only', False) use_sell_signal = self.config.get('experimental', {}).get('use_sell_signal', False) results = self.backtest( { 'stake_amount': self.config.get('stake_amount'), 'processed': preprocessed, 'max_open_trades': max_open_trades, 'realistic': self.config.get('realistic_simulation', False), 'sell_profit_only': sell_profit_only, 'use_sell_signal': use_sell_signal, 'record': self.config.get('export') } ) logger.info( '\n==================================== ' 'BACKTESTING REPORT' ' ====================================\n' '%s', self._generate_text_table( data, results ) )
#!/usr/bin/env python3 """This script generate json data from bittrex""" import json from os import path from freqtrade import exchange from freqtrade.exchange import Bittrex PAIRS = [ 'BTC_BCC', 'BTC_ETH', 'BTC_MER', 'BTC_POWR', 'BTC_ETC', 'BTC_OK', 'BTC_NEO', 'BTC_EMC2', 'BTC_DASH', 'BTC_LSK', 'BTC_LTC', 'BTC_XZC', 'BTC_OMG', 'BTC_STRAT', 'BTC_XRP', 'BTC_QTUM', 'BTC_WAVES', 'BTC_VTC', 'BTC_XLM', 'BTC_MCO' ] TICKER_INTERVAL = 5 # ticker interval in minutes (currently implemented: 1 and 5) OUTPUT_DIR = path.dirname(path.realpath(__file__)) # Init Bittrex exchange exchange._API = Bittrex({'key': '', 'secret': ''}) for pair in PAIRS: data = exchange.get_ticker_history(pair, TICKER_INTERVAL) filename = path.join(OUTPUT_DIR, '{}-{}.json'.format( pair, TICKER_INTERVAL, )) with open(filename, 'w') as fp: json.dump(data, fp)
from freqtrade.exchange import Bittrex parser = misc.common_args_parser('download utility') parser.add_argument( '-p', '--pair', help='JSON file containing pairs to download', dest='pair', default=None ) args = parser.parse_args(sys.argv[1:]) TICKER_INTERVALS = [1, 5] # ticker interval in minutes (currently implemented: 1 and 5) PAIRS = [] if args.pair: with open(args.pair) as file: PAIRS = json.load(file) PAIRS = list(set(PAIRS)) print('About to download pairs:', PAIRS) # Init Bittrex exchange exchange._API = Bittrex({'key': '', 'secret': ''}) for pair in PAIRS: for tick_interval in TICKER_INTERVALS: print('downloading pair %s, interval %s' % (pair, tick_interval)) data = exchange.get_ticker_history(pair, tick_interval) filename = '{}-{}.json'.format(pair, tick_interval) misc.file_dump_json(filename, data)
def start(args): # Initialize logger logging.basicConfig( level=args.loglevel, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', ) exchange._API = Bittrex({'key': '', 'secret': ''}) logger.info('Using config: %s ...', args.config) config = misc.load_config(args.config) logger.info('Using ticker_interval: %s ...', args.ticker_interval) data = {} pairs = config['exchange']['pair_whitelist'] if args.live: logger.info('Downloading data for all pairs in whitelist ...') for pair in pairs: data[pair] = exchange.get_ticker_history(pair, args.ticker_interval) else: logger.info( 'Using local backtesting data (using whitelist in given config) ...' ) data = optimize.load_data(args.datadir, pairs=pairs, ticker_interval=args.ticker_interval, refresh_pairs=args.refresh_pairs) logger.info('Using stake_currency: %s ...', config['stake_currency']) logger.info('Using stake_amount: %s ...', config['stake_amount']) max_open_trades = 0 if args.realistic_simulation: logger.info('Using max_open_trades: %s ...', config['max_open_trades']) max_open_trades = config['max_open_trades'] # Monkey patch config from freqtrade import main main._CONF = config preprocessed = preprocess(data) # Print timeframe min_date, max_date = get_timeframe(preprocessed) logger.info('Measuring data from %s up to %s ...', min_date.isoformat(), max_date.isoformat()) # Execute backtest and print results results = backtest( stake_amount=config['stake_amount'], processed=preprocessed, max_open_trades=max_open_trades, realistic=args.realistic_simulation, sell_profit_only=config.get('experimental', {}).get('sell_profit_only', False), stoploss=config.get('stoploss'), use_sell_signal=config.get('experimental', {}).get('use_sell_signal', False)) logger.info( '\n==================================== BACKTESTING REPORT ====================================\n%s', # noqa generate_text_table(data, results, config['stake_currency'], args.ticker_interval))
def plot_analyzed_dataframe(args: Namespace) -> None: """ Calls analyze() and plots the returned dataframe :return: None """ pair = args.pair.replace('-', '_') timerange = Arguments.parse_timerange(args.timerange) # Init strategy try: analyze = Analyze({'strategy': args.strategy}) except AttributeError: logger.critical( 'Impossible to load the strategy. Please check the file "user_data/strategies/%s.py"', args.strategy) exit() tick_interval = analyze.strategy.ticker_interval tickers = {} if args.live: logger.info('Downloading pair.') # Init Bittrex to use public API exchange._API = exchange.Bittrex({'key': '', 'secret': ''}) tickers[pair] = exchange.get_ticker_history(pair, tick_interval) else: tickers = optimize.load_data(datadir=args.datadir, pairs=[pair], ticker_interval=tick_interval, refresh_pairs=False, timerange=timerange) dataframes = analyze.tickerdata_to_dataframe(tickers) dataframe = dataframes[pair] dataframe = analyze.populate_buy_trend(dataframe) dataframe = analyze.populate_sell_trend(dataframe) if len(dataframe.index) > 750: logger.warning('Ticker contained more than 750 candles, clipping.') data = dataframe.tail(750) candles = go.Candlestick(x=data.date, open=data.open, high=data.high, low=data.low, close=data.close, name='Price') df_buy = data[data['buy'] == 1] buys = go.Scattergl(x=df_buy.date, y=df_buy.close, mode='markers', name='buy', marker=dict( symbol='triangle-up-dot', size=9, line=dict(width=1), color='green', )) df_sell = data[data['sell'] == 1] sells = go.Scattergl(x=df_sell.date, y=df_sell.close, mode='markers', name='sell', marker=dict( symbol='triangle-down-dot', size=9, line=dict(width=1), color='red', )) bb_lower = go.Scatter( x=data.date, y=data.bb_lowerband, name='BB lower', line={'color': "transparent"}, ) bb_upper = go.Scatter( x=data.date, y=data.bb_upperband, name='BB upper', fill="tonexty", fillcolor="rgba(0,176,246,0.2)", line={'color': "transparent"}, ) macd = go.Scattergl(x=data['date'], y=data['macd'], name='MACD') macdsignal = go.Scattergl(x=data['date'], y=data['macdsignal'], name='MACD signal') volume = go.Bar(x=data['date'], y=data['volume'], name='Volume') fig = tools.make_subplots( rows=3, cols=1, shared_xaxes=True, row_width=[1, 1, 4], vertical_spacing=0.0001, ) fig.append_trace(candles, 1, 1) fig.append_trace(bb_lower, 1, 1) fig.append_trace(bb_upper, 1, 1) fig.append_trace(buys, 1, 1) fig.append_trace(sells, 1, 1) fig.append_trace(volume, 2, 1) fig.append_trace(macd, 3, 1) fig.append_trace(macdsignal, 3, 1) fig['layout'].update(title=args.pair) fig['layout']['yaxis1'].update(title='Price') fig['layout']['yaxis2'].update(title='Volume') fig['layout']['yaxis3'].update(title='MACD') plot(fig, filename='freqtrade-plot.html')