Timeframe
1h
Direction
Long Only
Stoploss
-7.5%
Trailing Stop
Yes
ROI
0m: 23.1%, 222m: 16.5%, 805m: 7.5%, 2074m: 0.0%
Interface Version
N/A
Startup Candles
180
Indicators
2
freqtrade/freqtrade-strategies
freqtrade/freqtrade-strategies
this is an example class, implementing a PSAR based trailing stop loss you are supposed to take the `custom_stoploss()` and `populate_indicators()` parts and adapt it to your own strategy
freqtrade/freqtrade-strategies
freqtrade/freqtrade-strategies
This strategy uses custom_stoploss() to enforce a fixed risk/reward ratio by first calculating a dynamic initial stoploss via ATR - last negative peak
# --- Do not remove these libs ---
from functools import reduce
from freqtrade.strategy import BooleanParameter, CategoricalParameter, DecimalParameter, IntParameter, IStrategy, \
stoploss_from_open
from pandas import DataFrame
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
from freqtrade.optimize.space import Categorical, Dimension, Integer, SKDecimal, Real
from typing import Optional, Any, Callable, Dict, List
# --------------------------------
import pandas as pd
import numpy as np
import technical.indicators as ftt
pd.options.mode.chained_assignment = None # default='warn'
# Obelisk_TradePro_Ichi v2.1 - 2021-04-02
#
# by Obelisk
# https://twitter.com/brookmiles
#
# Originally based on "Crazy Results Best Ichimoku Cloud Trading Strategy Proven 100 Trades" by Trade Pro
# https://www.youtube.com/watch?v=8gWIykJgMNY
#
# Contributions:
#
# JimmyNixx
# - SSL Channel confirmation
# - ROCR & RMI confirmations
#
#
# Backtested with pairlist generated from:
# "pairlists": [
# {
# "method": "VolumePairList",
# "number_assets": 50,
# "sort_key": "quoteVolume",
# "refresh_period": 1800
# },
# {"method": "AgeFilter", "min_days_listed": 10},
# {"method": "PrecisionFilter"},
# {"method": "PriceFilter",
# "low_price_ratio": 0.001,
# "max_price": 20,
# },
# {"method": "SpreadFilter", "max_spread_ratio": 0.002},
# {
# "method": "RangeStabilityFilter",
# "lookback_days": 3,
# "min_rate_of_change": 0.1,
# "refresh_period": 1440
# },
# ],
class Obelisk_TradePro_Ichi_v2_1(IStrategy):
# Optimal timeframe for the strategy
timeframe = '1h'
can_short = False
class HyperOpt:
# def roi_space():
# return [
# Integer(0, 1, name='roi_t'),
# SKDecimal(0.010, 0.050, decimals=3, name='roi_p'),
# ]
#
# def generate_roi_table(params: Dict) -> Dict[int, float]:
# roi_table = {}
# roi_table[0] = params['roi_p']
# return roi_table
def trailing_space() -> List[Dimension]:
# All parameters here are mandatory, you can only modify their type or the range.
return [
# Fixed to true, if optimizing trailing_stop we assume to use trailing stop at all times.
Categorical([True, False], name='trailing_stop'),
SKDecimal(0.001, 0.01, decimals=3, name='trailing_stop_positive'),
# 'trailing_stop_positive_offset' should be greater than 'trailing_stop_positive',
# so this intermediate parameter is used as the value of the difference between
# them. The value of the 'trailing_stop_positive_offset' is constructed in the
# generate_trailing_params() method.
# This is similar to the hyperspace dimensions used for constructing the ROI tables.
SKDecimal(0.001, 0.05, decimals=3, name='trailing_stop_positive_offset_p1'),
Categorical([True, False], name='trailing_only_offset_is_reached'),
]
def stoploss_space():
return [SKDecimal(-0.1, -0.05, decimals=3, name='stoploss')]
# WARNING: ichimoku is a long indicator, if you remove or use a
# shorter startup_candle_count your results will be unstable/invalid
# for up to a week from the start of your backtest or dry/live run
# (180 candles = 7.5 days)
startup_candle_count = 180
# NOTE: this strat only uses candle information, so processing between
# new candles is a waste of resources as nothing will change
process_only_new_candles = True
minimal_roi = {
"0": 0.231,
"222": 0.165,
"805": 0.075,
"2074": 0
}
# Stoploss:
stoploss = -0.075
# Trailing stop:
trailing_stop = True
trailing_stop_positive = 0.001
trailing_stop_positive_offset = 0.015
trailing_only_offset_is_reached = True
# plot_config = {
# # Main plot indicators (Moving averages, ...)
# 'main_plot': {
# 'senkou_a': {
# 'color': 'green',
# 'fill_to': 'senkou_b',
# 'fill_label': 'Ichimoku Cloud',
# 'fill_color': 'rgba(0,0,0,0.2)',
# },
# # plot senkou_b, too. Not only the area to it.
# 'senkou_b': {
# 'color': 'red',
# },
# 'tenkan_sen': {'color': 'orange'},
# 'kijun_sen': {'color': 'blue'},
#
# 'chikou_span': {'color': 'lightgreen'},
#
# # 'ssl_up': { 'color': 'green' },
# # 'ssl_down': { 'color': 'red' },
# },
# 'subplots': {
# "Signals": {
# 'go_long': {'color': 'blue'},
# 'future_green': {'color': 'green'},
# 'chikou_high': {'color': 'lightgreen'},
# 'ssl_high': {'color': 'orange'},
# },
# }
# }
# buy_conversion_line_period = IntParameter(15, 25, default=20, space="buy")
# buy_base_line_periods = IntParameter(45, 75, default=60, space="buy")
# buy_laggin_span = IntParameter(100, 140, default=120, space="buy")
# buy_displacement = IntParameter(20, 40, default=30, space="buy")
buy_1 = IntParameter(15, 25, default=16, space="buy")
buy_2 = IntParameter(50, 70, default=67, space="buy")
buy_3 = IntParameter(90, 150, default=142, space="buy")
buy_4 = IntParameter(20, 40, default=30, space="buy", optimize=False)
buy_rocr = IntParameter(15, 35, default=19, space="buy")
buy_rmi_fast = IntParameter(5, 20, default=5, space="buy")
buy_ssl = IntParameter(5, 15, default=6, space="buy")
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
for va1 in self.buy_1.range:
for va2 in self.buy_2.range:
for va3 in self.buy_3.range:
ichimoku = ftt.ichimoku(dataframe, conversion_line_period=va1, base_line_periods=va2,
laggin_span=va3,
displacement=30)
dataframe[f'chikou_span{va1}{va2}{va3}{30}'] = ichimoku['chikou_span']
# cross indicators
dataframe[f'tenkan_sen{va1}{va2}{va3}{30}'] = ichimoku['tenkan_sen']
dataframe[f'kijun_sen{va1}{va2}{va3}{30}'] = ichimoku['kijun_sen']
# cloud, green a > b, red a < b
dataframe[f'senkou_a{va1}{va2}{va3}{30}'] = ichimoku['senkou_span_a']
dataframe[f'senkou_b{va1}{va2}{va3}{30}'] = ichimoku['senkou_span_b']
dataframe[f'leading_senkou_span_a{va1}{va2}{va3}{30}'] = ichimoku['leading_senkou_span_a']
dataframe[f'leading_senkou_span_b{va1}{va2}{va3}{30}'] = ichimoku['leading_senkou_span_b']
dataframe[f'cloud_green{va1}{va2}{va3}{30}'] = ichimoku['cloud_green'] * 1
dataframe[f'cloud_red{va1}{va2}{va3}{30}'] = ichimoku['cloud_red'] * -1
dataframe[f'future_green{va1}{va2}{va3}{30}'] = (dataframe[
f'leading_senkou_span_a{va1}{va2}{va3}{30}'] >
dataframe[
f'leading_senkou_span_b{va1}{va2}{va3}{30}']).astype(
'int') * 2
dataframe[f'chikou_high{va1}{va2}{va3}{30}'] = (
(dataframe[
f'chikou_span{va1}{va2}{va3}{30}'] >
dataframe[
f'senkou_a{va1}{va2}{va3}{30}']) &
(dataframe[
f'chikou_span{va1}{va2}{va3}{30}'] >
dataframe[
f'senkou_b{va1}{va2}{va3}{30}'])
).shift(30).fillna(0).astype('int')
# DANGER ZONE START
# NOTE: Not actually the future, present data that is normally shifted forward for display as the cloud
# The chikou_span is shifted into the past, so we need to be careful not to read the
# current value. But if we shift it forward again by displacement it should be safe to use.
# We're effectively "looking back" at where it normally appears on the chart.
# DANGER ZONE END
for val in self.buy_ssl.range:
dataframe[f'ssl_down{val}'] = self.SSLChannels(dataframe, val)['SslDown']
dataframe[f'ssl_up{val}'] = self.SSLChannels(dataframe, val)['SslUp']
dataframe[f'ssl_high{val}'] = (dataframe[f'ssl_up{val}'] > dataframe[f'ssl_down{val}']).astype('int') * 3
for val in self.buy_rocr.range:
dataframe[f'rocr{val}'] = ta.ROCR(dataframe, timeperiod=val)
for val in self.buy_rmi_fast.range:
dataframe[f'rmi-fast{val}'] = ftt.RMI(dataframe, length=val, mom=3)
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
conditions = [(dataframe['volume'] > 0)]
dataframe['go_long'] = (
(dataframe[f'tenkan_sen{self.buy_1.value}{self.buy_2.value}{self.buy_3.value}{30}'] >
dataframe[f'kijun_sen{self.buy_1.value}{self.buy_2.value}{self.buy_3.value}{30}']) &
(dataframe['close'] > dataframe[f'senkou_a{self.buy_1.value}{self.buy_2.value}{self.buy_3.value}{30}']) &
(dataframe['close'] > dataframe[f'senkou_b{self.buy_1.value}{self.buy_2.value}{self.buy_3.value}{30}']) &
(dataframe[f'future_green{self.buy_1.value}{self.buy_2.value}{self.buy_3.value}{30}'] > 0) &
(dataframe[f'chikou_high{self.buy_1.value}{self.buy_2.value}{self.buy_3.value}{30}'] > 0) &
(dataframe[f'ssl_high{self.buy_ssl.value}'] > 0) &
(dataframe[f'rocr{self.buy_rocr.value}'] > dataframe[f'rocr{self.buy_rocr.value}'].shift()) &
(dataframe[f'rmi-fast{self.buy_rmi_fast.value}'] > dataframe[f'rmi-fast{self.buy_rmi_fast.value}'].shift(2))
).astype('int') * 4
conditions.append(qtpylib.crossed_above(dataframe['go_long'], 0))
conditions = reduce(lambda x, y: x & y, conditions)
dataframe.loc[conditions, 'enter_long'] = 1
# print(dataframe.tail(50))
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[
(dataframe[f'ssl_high{self.buy_ssl.value}'] == 0)
&
(
(dataframe[f'tenkan_sen{self.buy_1.value}{self.buy_2.value}{self.buy_3.value}{30}'] < dataframe[f'kijun_sen{self.buy_1.value}{self.buy_2.value}{self.buy_3.value}{30}'])
|
(dataframe['close'] < dataframe[f'kijun_sen{self.buy_1.value}{self.buy_2.value}{self.buy_3.value}{30}'])
)
,
'exit_long'] = 1
return dataframe
def SSLChannels(self, dataframe: DataFrame, length=7):
df = dataframe.copy()
df['ATR'] = ta.ATR(df, timeperiod=14)
df['smaHigh'] = df['high'].rolling(length).mean() + df['ATR']
df['smaLow'] = df['low'].rolling(length).mean() - df['ATR']
df['hlv'] = np.where(df['close'] > df['smaHigh'], 1, np.where(df['close'] < df['smaLow'], -1, np.NAN))
df['hlv'] = df['hlv'].ffill()
df['sslDown'] = np.where(df['hlv'] < 0, df['smaHigh'], df['smaLow'])
df['sslUp'] = np.where(df['hlv'] < 0, df['smaLow'], df['smaHigh'])
return DataFrame(index=df.index, data={
'SslDown': df['sslDown'],
'SslUp': df['sslUp']
})