Update VolatilitySystem to better adjust positions
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
# flake8: noqa: F401
|
||||
# isort: skip_file
|
||||
# --- Do not remove these libs ---
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
import numpy as np # noqa
|
||||
import pandas as pd # noqa
|
||||
from pandas import DataFrame
|
||||
|
||||
import talib.abstract as ta
|
||||
from freqtrade.persistence import Trade
|
||||
from freqtrade.strategy import (CategoricalParameter, DecimalParameter,
|
||||
IntParameter, IStrategy)
|
||||
from freqtrade.exchange import date_minus_candles
|
||||
import freqtrade.vendor.qtpylib.indicators as qtpylib
|
||||
|
||||
from technical.util import resample_to_interval, resampled_merge
|
||||
|
||||
|
||||
class VolatilitySystem(IStrategy):
|
||||
"""
|
||||
Volatility System strategy.
|
||||
Based on https://www.tradingview.com/script/3hhs0XbR/
|
||||
|
||||
"""
|
||||
can_short: bool = True
|
||||
|
||||
minimal_roi = {
|
||||
"0": 100
|
||||
}
|
||||
|
||||
stoploss = -0.10
|
||||
|
||||
# Optimal ticker interval for the strategy
|
||||
timeframe = '1h'
|
||||
|
||||
plot_config = {
|
||||
# Main plot indicators (Moving averages, ...)
|
||||
'main_plot': {
|
||||
},
|
||||
'subplots': {
|
||||
"Volatility system": {
|
||||
"atr": {"color": "white"},
|
||||
"abs_close_change": {"color": "red"},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
"""
|
||||
Adds several indicators to the given DataFrame
|
||||
|
||||
Performance Note: For the best performance be frugal on the number of indicators
|
||||
you are using. Let easyprofiler do the work for you in finding out which indicators
|
||||
are worth adding.
|
||||
"""
|
||||
resample_int = 60 * 3
|
||||
resampled = resample_to_interval(dataframe, resample_int)
|
||||
# Average True Range (ATR)
|
||||
resampled['atr'] = ta.ATR(resampled, timeperiod=14) * 2.0
|
||||
# Absolute close change
|
||||
resampled['close_change'] = resampled['close'].diff()
|
||||
resampled['abs_close_change'] = resampled['close_change'].abs()
|
||||
|
||||
dataframe = resampled_merge(dataframe, resampled, fill_na=True)
|
||||
dataframe['atr'] = dataframe[f'resample_{resample_int}_atr']
|
||||
dataframe['close_change'] = dataframe[f'resample_{resample_int}_close_change']
|
||||
dataframe['abs_close_change'] = dataframe[f'resample_{resample_int}_abs_close_change']
|
||||
|
||||
# Average True Range (ATR)
|
||||
# dataframe['atr'] = ta.ATR(dataframe, timeperiod=14) * 2.0
|
||||
# Absolute close change
|
||||
# dataframe['close_change'] = dataframe['close'].diff()
|
||||
# dataframe['abs_close_change'] = dataframe['close_change'].abs()
|
||||
|
||||
return dataframe
|
||||
|
||||
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
"""
|
||||
Based on TA indicators, populates the buy and sell signals for the given dataframe
|
||||
:param dataframe: DataFrame
|
||||
:return: DataFrame with buy and sell columns
|
||||
"""
|
||||
# Use qtpylib.crossed_above to get only one signal, otherwise the signal is active
|
||||
# for the whole "long" timeframe.
|
||||
dataframe.loc[
|
||||
# qtpylib.crossed_above(dataframe['close_change'] * 1, dataframe['atr']),
|
||||
(dataframe['close_change'] * 1 > dataframe['atr'].shift(1)),
|
||||
'enter_long'] = 1
|
||||
dataframe.loc[
|
||||
# qtpylib.crossed_above(dataframe['close_change'] * -1, dataframe['atr']),
|
||||
(dataframe['close_change'] * -1 > dataframe['atr'].shift(1)),
|
||||
'enter_short'] = 1
|
||||
|
||||
return dataframe
|
||||
|
||||
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
"""
|
||||
use sell/buy signals as long/short indicators
|
||||
"""
|
||||
dataframe.loc[
|
||||
dataframe['enter_long'] == 1,
|
||||
'exit_short'] = 1
|
||||
dataframe.loc[
|
||||
dataframe['enter_short'] == 1,
|
||||
'exit_long'] = 1
|
||||
return dataframe
|
||||
|
||||
def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
|
||||
proposed_stake: float, min_stake: Optional[float], max_stake: float,
|
||||
leverage: float, entry_tag: Optional[str], side: str,
|
||||
**kwargs) -> float:
|
||||
# 50% stake amount on initial entry
|
||||
return proposed_stake / 2
|
||||
|
||||
position_adjustment_enable = True
|
||||
|
||||
def adjust_trade_position(self, trade: Trade, current_time: datetime,
|
||||
current_rate: float, current_profit: float,
|
||||
min_stake: Optional[float], max_stake: float,
|
||||
current_entry_rate: float, current_exit_rate: float,
|
||||
current_entry_profit: float, current_exit_profit: float,
|
||||
**kwargs) -> Optional[float]:
|
||||
dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe)
|
||||
if len(dataframe) > 2:
|
||||
last_candle = dataframe.iloc[-1].squeeze()
|
||||
previous_candle = dataframe.iloc[-2].squeeze()
|
||||
signal_name = 'enter_long' if not trade.is_short else 'enter_short'
|
||||
prior_date = date_minus_candles(self.timeframe, 1, current_time)
|
||||
# Only enlarge position on new signal.
|
||||
if (
|
||||
last_candle[signal_name] == 1
|
||||
and previous_candle[signal_name] != 1
|
||||
and trade.nr_of_successful_entries < 2
|
||||
and trade.orders[-1].order_date_utc < prior_date
|
||||
):
|
||||
return trade.stake_amount
|
||||
return None
|
||||
Reference in New Issue
Block a user