70 lines
2.1 KiB
Python
70 lines
2.1 KiB
Python
from typing import Dict, List, NamedTuple, Optional
|
|
|
|
import arrow
|
|
from pandas import DataFrame
|
|
|
|
from freqtrade.enums import ExitType
|
|
from freqtrade.exchange import timeframe_to_minutes
|
|
|
|
|
|
tests_start_time = arrow.get(2018, 10, 3)
|
|
tests_timeframe = '1h'
|
|
|
|
|
|
class BTrade(NamedTuple):
|
|
"""
|
|
Minimalistic Trade result used for functional backtesting
|
|
"""
|
|
sell_reason: ExitType
|
|
open_tick: int
|
|
close_tick: int
|
|
enter_tag: Optional[str] = None
|
|
is_short: bool = False
|
|
|
|
|
|
class BTContainer(NamedTuple):
|
|
"""
|
|
Minimal BacktestContainer defining Backtest inputs and results.
|
|
"""
|
|
data: List[List[float]]
|
|
stop_loss: float
|
|
roi: Dict[str, float]
|
|
trades: List[BTrade]
|
|
profit_perc: float
|
|
trailing_stop: bool = False
|
|
trailing_only_offset_is_reached: bool = False
|
|
trailing_stop_positive: Optional[float] = None
|
|
trailing_stop_positive_offset: float = 0.0
|
|
use_sell_signal: bool = False
|
|
use_custom_stoploss: bool = False
|
|
custom_entry_price: Optional[float] = None
|
|
custom_exit_price: Optional[float] = None
|
|
leverage: float = 1.0
|
|
|
|
|
|
def _get_frame_time_from_offset(offset):
|
|
minutes = offset * timeframe_to_minutes(tests_timeframe)
|
|
return tests_start_time.shift(minutes=minutes).datetime
|
|
|
|
|
|
def _build_backtest_dataframe(data):
|
|
columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'enter_long', 'exit_long',
|
|
'enter_short', 'exit_short']
|
|
if len(data[0]) == 8:
|
|
# No short columns
|
|
data = [d + [0, 0] for d in data]
|
|
columns = columns + ['enter_tag'] if len(data[0]) == 11 else columns
|
|
|
|
frame = DataFrame.from_records(data, columns=columns)
|
|
frame['date'] = frame['date'].apply(_get_frame_time_from_offset)
|
|
# Ensure floats are in place
|
|
for column in ['open', 'high', 'low', 'close', 'volume']:
|
|
frame[column] = frame[column].astype('float64')
|
|
|
|
# Ensure all candles make kindof sense
|
|
assert all(frame['low'] <= frame['close'])
|
|
assert all(frame['low'] <= frame['open'])
|
|
assert all(frame['high'] >= frame['close'])
|
|
assert all(frame['high'] >= frame['open'])
|
|
return frame
|