def backtest(backtest_conf, backdata, mocker): trades = [] exchange._API = Bittrex({'key': '', 'secret': ''}) mocked_history = mocker.patch('freqtrade.analyze.get_ticker_history') mocker.patch.dict('freqtrade.main._CONF', backtest_conf) mocker.patch('arrow.utcnow', return_value=arrow.get('2017-08-20T14:50:00')) for pair, pair_data in backdata.items(): mocked_history.return_value = pair_data ticker = analyze_ticker(pair)[['close', 'date', 'buy']].copy() # for each buy point for row in ticker[ticker.buy == 1].itertuples(index=True): trade = Trade(open_rate=row.close, open_date=row.date, amount=1, fee=exchange.get_fee() * 2) # calculate win/lose forwards from buy point for row2 in ticker[row.Index:].itertuples(index=True): if should_sell(trade, row2.close, row2.date): current_profit = trade.calc_profit(row2.close) trades.append( (pair, current_profit, row2.Index - row.Index)) break labels = ['currency', 'profit', 'duration'] results = DataFrame.from_records(trades, columns=labels) return results
def _get_sell_trade_entry( self, pair: str, buy_row: DataFrame, partial_ticker: List, trade_count_lock: Dict, args: Dict) -> Optional[Tuple]: stake_amount = args['stake_amount'] max_open_trades = args.get('max_open_trades', 0) trade = Trade( open_rate=buy_row.close + self.slippage, #implement slippage 0.01 for buy_row open_date=buy_row.date, stake_amount=stake_amount, amount=stake_amount / buy_row.open, fee=exchange.get_fee() ) # calculate win/lose forwards from buy point for sell_row in partial_ticker: if max_open_trades > 0: # Increase trade_count_lock for every iteration trade_count_lock[sell_row.date] = trade_count_lock.get(sell_row.date, 0) + 1 buy_signal = sell_row.buy #implement slippage 0.01 for sell_row if self.analyze.should_sell(trade, sell_row.close - self.slippage, sell_row.date, buy_signal, sell_row.sell): return \ sell_row, \ ( pair, trade.calc_profit_percent(rate=sell_row.close), trade.calc_profit(rate=sell_row.close), (sell_row.date - buy_row.date).seconds // 60 ), \ sell_row.date return None
def backtest(backtest_conf, processed, mocker): trades = [] exchange._API = Bittrex({'key': '', 'secret': ''}) mocker.patch.dict('freqtrade.main._CONF', backtest_conf) for pair, pair_data in processed.items(): pair_data['buy'] = 0 pair_data['sell'] = 0 ticker = populate_sell_trend(populate_buy_trend(pair_data)) # for each buy point for row in ticker[ticker.buy == 1].itertuples(index=True): trade = Trade(open_rate=row.close, open_date=row.date, amount=backtest_conf['stake_amount'], fee=exchange.get_fee() * 2) # calculate win/lose forwards from buy point for row2 in ticker[row.Index:].itertuples(index=True): if min_roi_reached(trade, row2.close, row2.date) or row2.sell == 1: current_profit = trade.calc_profit(row2.close) trades.append( (pair, current_profit, row2.Index - row.Index)) break labels = ['currency', 'profit', 'duration'] return DataFrame.from_records(trades, columns=labels)
def create_trade(stake_amount: float) -> Optional[Trade]: """ Checks the implemented trading indicator(s) for a randomly picked pair, if one pair triggers the buy_signal a new trade record gets created :param stake_amount: amount of btc to spend """ logger.info( 'Checking buy signals to create a new trade with stake_amount: %f ...', stake_amount) whitelist = copy.deepcopy(_CONF['exchange']['pair_whitelist']) # Check if stake_amount is fulfilled if exchange.get_balance(_CONF['stake_currency']) < stake_amount: raise ValueError('stake amount is not fulfilled (currency={})'.format( _CONF['stake_currency'])) # Remove currently opened and latest pairs from whitelist for trade in Trade.query.filter(Trade.is_open.is_(True)).all(): if trade.pair in whitelist: whitelist.remove(trade.pair) logger.debug('Ignoring %s in pair whitelist', trade.pair) if not whitelist: raise ValueError('No pair in whitelist') # Pick pair based on StochRSI buy signals for _pair in whitelist: if get_buy_signal(_pair): pair = _pair break else: return None # Calculate amount and subtract fee fee = exchange.get_fee() buy_limit = get_target_bid(exchange.get_ticker(pair)) amount = (1 - fee) * stake_amount / buy_limit order_id = exchange.buy(pair, buy_limit, amount) # Create trade entity and return message = '*{}:* Buying [{}]({}) with limit `{:.8f}`'.format( exchange.get_name().upper(), pair.replace('_', '/'), exchange.get_pair_detail_url(pair), buy_limit) logger.info(message) telegram.send_msg(message) # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL return Trade(pair=pair, stake_amount=stake_amount, amount=amount, fee=fee * 2, open_rate=buy_limit, open_date=datetime.utcnow(), exchange=exchange.get_name().upper(), open_order_id=order_id)
def test_exchange_misc(mocker): api_mock = MagicMock() mocker.patch('freqtrade.exchange._API', api_mock) exchange.get_markets() assert api_mock.get_markets.call_count == 1 exchange.get_market_summaries() assert api_mock.get_market_summaries.call_count == 1 api_mock.name = 123 assert exchange.get_name() == 123 api_mock.fee = 456 assert exchange.get_fee() == 456 exchange.get_wallet_health() assert api_mock.get_wallet_health.call_count == 1
def create_trade(self) -> bool: """ Checks the implemented trading indicator(s) for a randomly picked pair, if one pair triggers the buy_signal a new trade record gets created :param stake_amount: amount of btc to spend :param interval: Ticker interval used for Analyze :return: True if a trade object has been created and persisted, False otherwise """ stake_amount = self.config['stake_amount'] interval = self.analyze.get_ticker_interval() logger.info( 'Checking buy signals to create a new trade with stake_amount: %f ...', stake_amount) whitelist = copy.deepcopy(self.config['exchange']['pair_whitelist']) # Check if stake_amount is fulfilled if exchange.get_balance(self.config['stake_currency']) < stake_amount: raise DependencyException( 'stake amount is not fulfilled (currency={})'.format( self.config['stake_currency'])) # Remove currently opened and latest pairs from whitelist for trade in Trade.query.filter(Trade.is_open.is_(True)).all(): if trade.pair in whitelist: whitelist.remove(trade.pair) logger.debug('Ignoring %s in pair whitelist', trade.pair) if not whitelist: raise DependencyException('No currency pairs in whitelist') # Pick pair based on StochRSI buy signals for _pair in whitelist: (buy, sell) = self.analyze.get_signal(_pair, interval) if buy and not sell: pair = _pair break else: return False # Calculate amount buy_limit = self.get_target_bid(exchange.get_ticker(pair)) amount = stake_amount / buy_limit order_id = exchange.buy(pair, buy_limit, amount) stake_amount_fiat = self.fiat_converter.convert_amount( stake_amount, self.config['stake_currency'], self.config['fiat_display_currency']) # Create trade entity and return self.rpc.send_msg( '*{}:* Buying [{}]({}) with limit `{:.8f} ({:.6f} {}, {:.3f} {})` ' .format(exchange.get_name().upper(), pair.replace('_', '/'), exchange.get_pair_detail_url(pair), buy_limit, stake_amount, self.config['stake_currency'], stake_amount_fiat, self.config['fiat_display_currency'])) # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL trade = Trade(pair=pair, stake_amount=stake_amount, amount=amount, fee=exchange.get_fee(), open_rate=buy_limit, open_date=datetime.utcnow(), exchange=exchange.get_name().upper(), open_order_id=order_id) Trade.session.add(trade) Trade.session.flush() return True
def backtest(stake_amount: float, processed: Dict[str, DataFrame], max_open_trades: int = 0, realistic: bool = True, sell_profit_only: bool = False, stoploss: int = -1.00, use_sell_signal: bool = False) -> DataFrame: """ Implements backtesting functionality :param stake_amount: btc amount to use for each trade :param processed: a processed dictionary with format {pair, data} :param max_open_trades: maximum number of concurrent trades (default: 0, disabled) :param realistic: do we try to simulate realistic trades? (default: True) :return: DataFrame """ trades = [] trade_count_lock: dict = {} exchange._API = Bittrex({'key': '', 'secret': ''}) for pair, pair_data in processed.items(): pair_data['buy'], pair_data['sell'] = 0, 0 ticker = populate_sell_trend(populate_buy_trend(pair_data)) # for each buy point lock_pair_until = None buy_subset = ticker[ticker.buy == 1][['buy', 'open', 'close', 'date', 'sell']] for row in buy_subset.itertuples(index=True): if realistic: if lock_pair_until is not None and row.Index <= lock_pair_until: continue if max_open_trades > 0: # Check if max_open_trades has already been reached for the given date if not trade_count_lock.get(row.date, 0) < max_open_trades: continue if max_open_trades > 0: # Increase lock trade_count_lock[row.date] = trade_count_lock.get(row.date, 0) + 1 trade = Trade( open_rate=row.close, open_date=row.date, stake_amount=stake_amount, amount=stake_amount / row.open, fee=exchange.get_fee() ) # calculate win/lose forwards from buy point sell_subset = ticker[row.Index + 1:][['close', 'date', 'sell']] for row2 in sell_subset.itertuples(index=True): if max_open_trades > 0: # Increase trade_count_lock for every iteration trade_count_lock[row2.date] = trade_count_lock.get(row2.date, 0) + 1 current_profit_percent = trade.calc_profit_percent(rate=row2.close) if (sell_profit_only and current_profit_percent < 0): continue if min_roi_reached(trade, row2.close, row2.date) or \ (row2.sell == 1 and use_sell_signal) or \ current_profit_percent <= stoploss: current_profit_btc = trade.calc_profit(rate=row2.close) lock_pair_until = row2.Index trades.append( ( pair, current_profit_percent, current_profit_btc, row2.Index - row.Index, current_profit_btc > 0, current_profit_btc < 0 ) ) break labels = ['currency', 'profit_percent', 'profit_BTC', 'duration', 'profit', 'loss'] return DataFrame.from_records(trades, columns=labels)
def test_get_fee(default_conf, mocker): mocker.patch('freqtrade.exchange.validate_pairs', side_effect=lambda s: True) init(default_conf) assert get_fee() == 0.0025
def backtest(stake_amount: float, processed: Dict[str, DataFrame], max_open_trades: int = 0, realistic: bool = True, sell_profit_only: bool = False, stoploss: int = -1.00, use_sell_signal: bool = False) -> DataFrame: """ Implements backtesting functionality :param stake_amount: btc amount to use for each trade :param processed: a processed dictionary with format {pair, data} :param max_open_trades: maximum number of concurrent trades (default: 0, disabled) :param realistic: do we try to simulate realistic trades? (default: True) :return: DataFrame """ trades = [] trade_count_lock: dict = {} exchange._API = Bittrex({'key': '', 'secret': ''}) for pair, pair_data in processed.items(): pair_data['buy'], pair_data['sell'] = 0, 0 ticker = populate_sell_trend(populate_buy_trend(pair_data)) # for each buy point lock_pair_until = None buy_subset = ticker[ticker.buy == 1][[ 'buy', 'open', 'close', 'date', 'sell' ]] for row in buy_subset.itertuples(index=True): if realistic: if lock_pair_until is not None and row.Index <= lock_pair_until: continue if max_open_trades > 0: # Check if max_open_trades has already been reached for the given date if not trade_count_lock.get(row.date, 0) < max_open_trades: continue if max_open_trades > 0: # Increase lock trade_count_lock[row.date] = trade_count_lock.get(row.date, 0) + 1 trade = Trade(open_rate=row.close, open_date=row.date, stake_amount=stake_amount, amount=stake_amount / row.open, fee=exchange.get_fee()) # calculate win/lose forwards from buy point sell_subset = ticker[row.Index + 1:][['close', 'date', 'sell']] for row2 in sell_subset.itertuples(index=True): if max_open_trades > 0: # Increase trade_count_lock for every iteration trade_count_lock[row2.date] = trade_count_lock.get( row2.date, 0) + 1 current_profit_percent = trade.calc_profit_percent( rate=row2.close) if (sell_profit_only and current_profit_percent < 0): continue if min_roi_reached(trade, row2.close, row2.date) or \ (row2.sell == 1 and use_sell_signal) or \ current_profit_percent <= stoploss: current_profit_btc = trade.calc_profit(rate=row2.close) lock_pair_until = row2.Index trades.append( (pair, current_profit_percent, current_profit_btc, row2.Index - row.Index, current_profit_btc > 0, current_profit_btc < 0)) break labels = [ 'currency', 'profit_percent', 'profit_BTC', 'duration', 'profit', 'loss' ] return DataFrame.from_records(trades, columns=labels)
def create_trade(stake_amount: float) -> bool: """ Checks the implemented trading indicator(s) for a randomly picked pair, if one pair triggers the buy_signal a new trade record gets created :param stake_amount: amount of btc to spend :return: True if a trade object has been created and persisted, False otherwise """ logger.info( 'Checking buy signals to create a new trade with stake_amount: %f ...', stake_amount ) whitelist = copy.deepcopy(_CONF['exchange']['pair_whitelist']) # Check if stake_amount is fulfilled if exchange.get_balance(_CONF['stake_currency']) < stake_amount: raise DependencyException( 'stake amount is not fulfilled (currency={})'.format(_CONF['stake_currency']) ) # Remove currently opened and latest pairs from whitelist for trade in Trade.query.filter(Trade.is_open.is_(True)).all(): if trade.pair in whitelist: whitelist.remove(trade.pair) logger.debug('Ignoring %s in pair whitelist', trade.pair) if not whitelist: raise DependencyException('No pair in whitelist') # Pick pair based on StochRSI buy signals for _pair in whitelist: if get_signal(_pair, SignalType.BUY): pair = _pair break else: return False # Calculate amount buy_limit = get_target_bid(exchange.get_ticker(pair)) amount = stake_amount / buy_limit order_id = exchange.buy(pair, buy_limit, amount) fiat_converter = CryptoToFiatConverter() stake_amount_fiat = fiat_converter.convert_amount( stake_amount, _CONF['stake_currency'], _CONF['fiat_display_currency'] ) # Create trade entity and return rpc.send_msg('*{}:* Buying [{}]({}) with limit `{:.8f} ({:.6f} {}, {:.3f} {})` '.format( exchange.get_name().upper(), pair.replace('_', '/'), exchange.get_pair_detail_url(pair), buy_limit, stake_amount, _CONF['stake_currency'], stake_amount_fiat, _CONF['fiat_display_currency'] )) # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL trade = Trade( pair=pair, stake_amount=stake_amount, amount=amount, fee=exchange.get_fee(), open_rate=buy_limit, open_date=datetime.utcnow(), exchange=exchange.get_name().upper(), open_order_id=order_id ) Trade.session.add(trade) Trade.session.flush() return True
def create_trade(stake_amount: float) -> Optional[Trade]: """ Checks the implemented trading indicator(s) for a randomly picked pair, if one pair triggers the buy_signal a new trade record gets created :param stake_amount: amount of btc to spend """ logger.info('Creating new trade with stake_amount: %f ...', stake_amount) whitelist = copy.deepcopy(_CONF['exchange']['pair_whitelist']) # Check if stake_amount is fulfilled if exchange.get_balance(_CONF['stake_currency']) < stake_amount: raise ValueError('stake amount is not fulfilled (currency={})'.format( _CONF['stake_currency'])) # Remove currently opened and latest pairs from whitelist for trade in Trade.query.filter(Trade.is_open.is_(True)).all(): if trade.pair in whitelist: whitelist.remove(trade.pair) logger.debug('Ignoring %s in pair whitelist', trade.pair) # if not whitelist: # raise ValueError('No pair in whitelist') # Pick pair based on StochRSI buy signals if analyzer.name == 'danml': for _pair in whitelist: if get_buy_signal(_pair, exchange.get_name(), analyzer): pair = _pair break else: return None elif analyzer.name == 'cryptoml': update = False if datetime.utcnow().minute % 5 == 0: update = True pair = analyzer.get_buy_signal(whitelist, update=update, threshold=0.01, repeats=3) if pair is None: return None # Calculate amount and subtract fee fee = exchange.get_fee() buy_limit = get_target_bid(exchange.get_ticker(pair)) amount = (1 - fee) * stake_amount / buy_limit health = exchange.get_wallet_health() if exchange.get_name() == 'HitBTC': token = pair else: token = get_quote_token(pair) token_healthy = False for status in health: if status['Currency'] == token: token_healthy = status['IsActive'] if token_healthy: order_id = exchange.buy(pair, buy_limit, amount) # Create trade entity and return message = '*{}:* Buying [{}]({}) with limit `{:.8f}`'.format( exchange.get_name().upper(), pair.replace('_', '/'), exchange.get_pair_detail_url(pair), buy_limit) logger.info(message) telegram.send_msg(message) # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL return Trade( pair=pair, stake_amount=stake_amount, amount=amount, fee=fee * 2., open_rate=buy_limit, open_date=datetime.utcnow(), exchange=exchange.get_name().upper(), open_order_id=order_id, # open_order_type='buy' )
# 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())