Add ATR regime backtest filter
This commit is contained in:
parent
0c92789d26
commit
d27b0e301c
43
atr_regime_backtest.py
Normal file
43
atr_regime_backtest.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from os import getenv
|
||||||
|
from pandas import concat
|
||||||
|
|
||||||
|
from backtesting import backtest_iron_condor
|
||||||
|
from backtesting.credit_targeting import create_strategies
|
||||||
|
from backtesting.filter import ATRRegimeFilter
|
||||||
|
from plotting import BacktestChart, plot
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
start_date = datetime(2016, 1, 1)
|
||||||
|
end_date = datetime.now()
|
||||||
|
|
||||||
|
credit_target = float(getenv('CREDIT_TARGET'))
|
||||||
|
entry_times = getenv('ENTRY_TIMES').split(',')
|
||||||
|
|
||||||
|
backtest_results = []
|
||||||
|
|
||||||
|
for entry_time in entry_times:
|
||||||
|
call_spread_strategy, put_spread_strategy = create_strategies(credit_target, entry_time)
|
||||||
|
|
||||||
|
backtest_result = backtest_iron_condor(
|
||||||
|
f'${credit_target:.2f} Iron Condor @ {call_spread_strategy.trade_entry_time}',
|
||||||
|
call_spread_strategy,
|
||||||
|
put_spread_strategy,
|
||||||
|
start_date,
|
||||||
|
end_date,
|
||||||
|
filters = [ATRRegimeFilter()]
|
||||||
|
)
|
||||||
|
|
||||||
|
backtest_results.append(backtest_result)
|
||||||
|
|
||||||
|
combined_backtest_results = concat(backtest_results)
|
||||||
|
summed_results = combined_backtest_results.groupby('Date')['Cumulative Profit'].sum().reset_index()
|
||||||
|
|
||||||
|
plot(BacktestChart(
|
||||||
|
dates = summed_results['Date'],
|
||||||
|
profit = summed_results['Cumulative Profit'],
|
||||||
|
title = f'${credit_target:.2f} Iron Condor (ATR Regime Filter)'
|
||||||
|
))
|
@ -1,3 +1,4 @@
|
|||||||
|
from .atr_regime_filter import ATRRegimeFilter
|
||||||
from .backtest_filter import BacktestFilter
|
from .backtest_filter import BacktestFilter
|
||||||
from .volatility_regime_filter import VolatilityRegimeFilter
|
from .volatility_regime_filter import VolatilityRegimeFilter
|
||||||
from .vvix_regime_filter import VVIXRegimeFilter
|
from .vvix_regime_filter import VVIXRegimeFilter
|
64
backtesting/filter/atr_regime_filter.py
Normal file
64
backtesting/filter/atr_regime_filter.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from os import getenv
|
||||||
|
from pandas import concat, DataFrame, Series
|
||||||
|
from database.ohlc import ohlc
|
||||||
|
|
||||||
|
from .backtest_filter import BacktestFilter
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
class ATRRegimeFilter(BacktestFilter):
|
||||||
|
def __init__(self):
|
||||||
|
self.backtest_filter = self.filter()
|
||||||
|
|
||||||
|
def calculate_atr(self, high, low, close, period = 5):
|
||||||
|
tr = np.maximum(high - low, np.abs(high - close.shift(1)), np.abs(low - close.shift(1)))
|
||||||
|
atr = tr.rolling(window = period).mean()
|
||||||
|
return atr
|
||||||
|
|
||||||
|
def filter(self) -> DataFrame:
|
||||||
|
data_start_date = datetime.strptime(getenv('OPTION_DATA_START_DATE'), '%Y-%m-%d')
|
||||||
|
now = datetime.now()
|
||||||
|
|
||||||
|
spx_data = ohlc(
|
||||||
|
symbol = 'SPX.XO',
|
||||||
|
timeframe = '1d',
|
||||||
|
start_date = data_start_date - timedelta(weeks = 52),
|
||||||
|
end_date = now
|
||||||
|
)
|
||||||
|
spx_data.rename(columns = {'Timestamp': 'Date'}, inplace = True)
|
||||||
|
|
||||||
|
spx_data['ATR'] = self.calculate_atr(spx_data['High'], spx_data['Low'], spx_data['Close'])
|
||||||
|
|
||||||
|
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||||
|
Filtering is based on the previous day's close, so the current date can be included even though the
|
||||||
|
data may not be availble yet.
|
||||||
|
|
||||||
|
This allows for utilizing the filter in live trading to decide whether to trade prior to market open.
|
||||||
|
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||||
|
spx_data = concat([
|
||||||
|
spx_data,
|
||||||
|
DataFrame({
|
||||||
|
'Date': [datetime.combine(now, datetime.min.time())],
|
||||||
|
'Open': [0.0],
|
||||||
|
'High': [0.0],
|
||||||
|
'Low': [0.0],
|
||||||
|
'Close': [0.0],
|
||||||
|
'Volume': [0.0],
|
||||||
|
'ATR': [0.0]
|
||||||
|
})],
|
||||||
|
ignore_index = True
|
||||||
|
)
|
||||||
|
|
||||||
|
percent_rank = lambda x: Series(x).rank(pct = True).iloc[-1]
|
||||||
|
spx_data['ATR_Percent_Rank'] = spx_data['ATR'].shift(1).rolling(window = 252).apply(percent_rank)
|
||||||
|
|
||||||
|
filtered_data = spx_data[spx_data['Date'] >= data_start_date].copy()
|
||||||
|
# filtered_data['Trade Allowed'] = (filtered_data['ATR_Percent_Rank'] > 0.25) & (filtered_data['ATR_Percent_Rank'] < 0.75)
|
||||||
|
filtered_data['Trade Allowed'] = filtered_data['ATR_Percent_Rank'] < 0.75
|
||||||
|
|
||||||
|
filtered_data = filtered_data[['Date', 'Trade Allowed']].reset_index(drop = True)
|
||||||
|
return filtered_data
|
4
test/filter/atr_regime_filter_test.py
Normal file
4
test/filter/atr_regime_filter_test.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
from backtesting.filter import ATRRegimeFilter
|
||||||
|
|
||||||
|
filter = ATRRegimeFilter()
|
||||||
|
print(filter.backtest_filter)
|
Loading…
Reference in New Issue
Block a user