diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index e3fb9b946..5a0770953 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -67,30 +67,29 @@ def generate_text_table( return tabulate(tabular_data, headers=headers, floatfmt=floatfmt) -def get_sell_trade_entry(pair, row, buy_subset, ticker, trade_count_lock, args): +def get_sell_trade_entry(pair, row, partial_ticker, trade_count_lock, args): stake_amount = args['stake_amount'] max_open_trades = args.get('max_open_trades', 0) trade = Trade(open_rate=row.close, - open_date=row.Index, + 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[ticker.index > row.Index][['close', 'sell', 'buy']] - for row2 in sell_subset.itertuples(index=True): + for row2 in partial_ticker: if max_open_trades > 0: # Increase trade_count_lock for every iteration - trade_count_lock[row2.Index] = trade_count_lock.get(row2.Index, 0) + 1 + trade_count_lock[row2.date] = trade_count_lock.get(row2.date, 0) + 1 buy_signal = row2.buy - if(should_sell(trade, row2.close, row2.Index, buy_signal, row2.sell)): + if should_sell(trade, row2.close, row2.date, buy_signal, row2.sell): return row2, (pair, trade.calc_profit_percent(rate=row2.close), trade.calc_profit(rate=row2.close), - (row2.Index - row.Index).seconds // 60 - ), row2.Index + (row2.date - row.date).seconds // 60 + ), row2.date return None @@ -107,6 +106,7 @@ def backtest(args) -> DataFrame: stoploss: use stoploss :return: DataFrame """ + headers = ['date', 'buy', 'open', 'close', 'sell'] processed = args['processed'] max_open_trades = args.get('max_open_trades', 0) realistic = args.get('realistic', True) @@ -116,29 +116,29 @@ def backtest(args) -> DataFrame: 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)) - if 'date' in ticker: - ticker.set_index('date', inplace=True) - # for each buy point + pair_data['buy'], pair_data['sell'] = 0, 0 # cleanup from previous run + + ticker_data = populate_sell_trend(populate_buy_trend(pair_data))[headers] + ticker = [x for x in ticker_data.itertuples()] + lock_pair_until = None - headers = ['buy', 'open', 'close', 'sell'] - buy_subset = ticker[(ticker.buy == 1) & (ticker.sell == 0)][headers] - for row in buy_subset.itertuples(index=True): + for index, row in enumerate(ticker): + if row.buy == 0 or row.sell == 1: + continue # skip rows where no buy signal or that would immediately sell off + if realistic: - if lock_pair_until is not None and row.Index <= lock_pair_until: + if lock_pair_until is not None and row.date <= 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.Index, 0) < max_open_trades: + if not trade_count_lock.get(row.date, 0) < max_open_trades: continue if max_open_trades > 0: # Increase lock - trade_count_lock[row.Index] = trade_count_lock.get(row.Index, 0) + 1 + trade_count_lock[row.date] = trade_count_lock.get(row.date, 0) + 1 - ret = get_sell_trade_entry(pair, row, buy_subset, ticker, - trade_count_lock, args) + ret = get_sell_trade_entry(pair, row, ticker[index+1:], trade_count_lock, args) if ret: row2, trade_entry, next_date = ret lock_pair_until = next_date @@ -148,9 +148,9 @@ def backtest(args) -> DataFrame: # record a tuple of pair, current_profit_percent, # entry-date, duration records.append((pair, trade_entry[1], - row.Index.strftime('%s'), - row2.Index.strftime('%s'), - row.Index, trade_entry[3])) + row.date.strftime('%s'), + row2.date.strftime('%s'), + row.date, trade_entry[3])) # For now export inside backtest(), maybe change so that backtest() # returns a tuple like: (dataframe, records, logs, etc) if record and record.find('trades') >= 0: