Timeframe
1h
Direction
Long Only
Stoploss
-10.0%
Trailing Stop
No
ROI
0m: 5.0%, 30m: 3.0%, 60m: 2.0%, 120m: 0.0%
Interface Version
3
Startup Candles
N/A
Indicators
1
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
Strategy 003 author@: Gerald Lonlas github@: https://github.com/freqtrade/freqtrade-strategies
from freqtrade.strategy import (BooleanParameter, CategoricalParameter,
DecimalParameter, IStrategy, IntParameter,
RealParameter, merge_informative_pair, informative)
import pandas as pd
import numpy as np
import talib.abstract as ta
from pandas import DataFrame
import logging
class MSO(IStrategy):
# Strategy interface version
INTERFACE_VERSION = 3
# Minimal ROI
minimal_roi = {
"0": 0.05,
"30": 0.03,
"60": 0.02,
"120": 0
}
# Stop loss
stoploss = -0.1
# Trailing stop
trailing_stop = False
# Timeframe
timeframe = '1h'
# Hyperopt parameters
ms_weight_k1 = DecimalParameter(0.0, 5.0, default=1.0, space='buy')
ms_weight_k2 = DecimalParameter(0.0, 5.0, default=3.0, space='buy')
ms_weight_k3 = DecimalParameter(0.0, 5.0, default=5.0, space='buy')
norm_smooth = IntParameter(1, 10, default=3, space='buy')
cyc_smooth = IntParameter(1, 14, default=9, space='buy')
buy_threshold = DecimalParameter(50.0, 85.0, default=60.0, space='buy')
sell_threshold = DecimalParameter(15.0, 50.0, default=40.0, space='sell')
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# Initialize swing structures
st_high = {'last_price': 0, 'mid_price': 0, 'prev_price': 0, 'is_crossed': False}
st_low = {'last_price': 0, 'mid_price': 0, 'prev_price': 0, 'is_crossed': False}
it_high = {'last_price': 0, 'mid_price': 0, 'prev_price': 0, 'is_crossed': False}
it_low = {'last_price': 0, 'mid_price': 0, 'prev_price': 0, 'is_crossed': False}
lt_high = {'last_price': 0, 'mid_price': 0, 'prev_price': 0, 'is_crossed': False}
lt_low = {'last_price': 0, 'mid_price': 0, 'prev_price': 0, 'is_crossed': False}
# Short-term market structure
def short_market_structure(df, high, low, close):
bull = np.zeros(len(df))
bear = np.zeros(len(df))
osc = np.zeros(len(df))
max_vals = np.zeros(len(df))
min_vals = np.zeros(len(df))
for i in range(2, len(df)):
# High swing detection
if high[i-2] < high[i-1] and high[i-1] >= high[i]:
st_high['prev_price'] = st_high['mid_price']
st_high['mid_price'] = st_high['last_price']
st_high['last_price'] = high[i-1]
st_high['is_crossed'] = False
if close[i] > st_high['last_price'] and not st_high['is_crossed']:
st_high['is_crossed'] = True
bull[i] = 1
# Low swing detection
if low[i-2] > low[i-1] and low[i-1] <= low[i]:
st_low['prev_price'] = st_low['mid_price']
st_low['mid_price'] = st_low['last_price']
st_low['last_price'] = low[i-1]
st_low['is_crossed'] = False
if close[i] < st_low['last_price'] and not st_low['is_crossed']:
st_low['is_crossed'] = True
bear[i] = 1
# Normalize oscillator
os = 1 if bull[i] else -1 if bear[i] else osc[i-1] if i > 0 else 0
max_vals[i] = close[i] if os > (osc[i-1] if i > 0 else 0) else max(close[i], max_vals[i-1] if i > 0 else close[i])
min_vals[i] = close[i] if os < (osc[i-1] if i > 0 else 0) else min(close[i], min_vals[i-1] if i > 0 else close[i])
if max_vals[i] != min_vals[i] and i >= self.norm_smooth.value:
series = np.array([(close[j] - min_vals[j]) / (max_vals[j] - min_vals[j]) * 100 for j in range(i - self.norm_smooth.value + 1, i + 1)])
sma_result = ta.SMA(series, timeperiod=self.norm_smooth.value)
osc[i] = sma_result[-1] if sma_result is not None and not np.isnan(sma_result[-1]) else osc[i-1] if i > 0 else 50.0
else:
osc[i] = osc[i-1] if i > 0 else 50.0
return osc
# General market structure
def market_structure(df, h_swing_high, h_swing_low, l_swing_high, l_swing_low, close):
bull = np.zeros(len(df))
bear = np.zeros(len(df))
osc = np.zeros(len(df))
max_vals = np.zeros(len(df))
min_vals = np.zeros(len(df))
for i in range(2, len(df)):
# High swing detection
c_swing_high = l_swing_high['prev_price'] < l_swing_high['mid_price'] and l_swing_high['mid_price'] >= l_swing_high['last_price']
if c_swing_high:
h_swing_high['prev_price'] = h_swing_high['mid_price']
h_swing_high['mid_price'] = h_swing_high['last_price']
h_swing_high['last_price'] = l_swing_high['mid_price']
h_swing_high['is_crossed'] = False
if close[i] > h_swing_high['last_price'] and not h_swing_high['is_crossed']:
h_swing_high['is_crossed'] = True
bull[i] = 1
# Low swing detection
c_swing_low = l_swing_low['prev_price'] > l_swing_low['mid_price'] and l_swing_low['mid_price'] <= l_swing_low['last_price']
if c_swing_low:
h_swing_low['prev_price'] = h_swing_low['mid_price']
h_swing_low['mid_price'] = h_swing_low['last_price']
h_swing_low['last_price'] = l_swing_low['mid_price']
h_swing_low['is_crossed'] = False
if close[i] < h_swing_low['last_price'] and not h_swing_low['is_crossed']:
h_swing_low['is_crossed'] = True
bear[i] = 1
# Normalize oscillator
os = 1 if bull[i] else -1 if bear[i] else osc[i-1] if i > 0 else 0
max_vals[i] = close[i] if os > (osc[i-1] if i > 0 else 0) else max(close[i], max_vals[i-1] if i > 0 else close[i])
min_vals[i] = close[i] if os < (osc[i-1] if i > 0 else 0) else min(close[i], min_vals[i-1] if i > 0 else close[i])
if max_vals[i] != min_vals[i] and i >= self.norm_smooth.value:
series = np.array([(close[j] - min_vals[j]) / (max_vals[j] - min_vals[j]) * 100 for j in range(i - self.norm_smooth.value + 1, i + 1)])
sma_result = ta.SMA(series, timeperiod=self.norm_smooth.value)
osc[i] = sma_result[-1] if sma_result is not None and not np.isnan(sma_result[-1]) else osc[i-1] if i > 0 else 50.0
else:
osc[i] = osc[i-1] if i > 0 else 50.0
return osc
# Calculate oscillators
dataframe['st_osc'] = short_market_structure(dataframe, dataframe['high'], dataframe['low'], dataframe['close'])
dataframe['it_osc'] = market_structure(dataframe, it_high, it_low, st_high, st_low, dataframe['close'])
dataframe['lt_osc'] = market_structure(dataframe, lt_high, lt_low, it_high, it_low, dataframe['close'])
# Market Structure Oscillator
weights_sum = self.ms_weight_k1.value + self.ms_weight_k2.value + self.ms_weight_k3.value
weights_count = (1 if not np.isnan(dataframe['st_osc']).any() else 0) + \
(1 if not np.isnan(dataframe['it_osc']).any() else 0) + \
(1 if not np.isnan(dataframe['lt_osc']).any() else 0)
dataframe['ms_osc'] = (self.ms_weight_k1.value * dataframe['st_osc'].fillna(0) +
self.ms_weight_k2.value * dataframe['it_osc'].fillna(0) +
self.ms_weight_k3.value * dataframe['lt_osc'].fillna(0)) / (weights_sum if weights_sum > 0 else 1)
dataframe['ms_osc_smo'] = dataframe['ms_osc'].rolling(self.cyc_smooth.value).mean()
# Cycle Oscillator
dataframe['cycle_osc'] = (dataframe['ms_osc'] - dataframe['ms_osc_smo']) + 50
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[
(
(dataframe['ms_osc'] > self.buy_threshold.value) &
(dataframe['cycle_osc'] > 50)
),
'enter_long'] = 1
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[
(
(dataframe['ms_osc'] < self.sell_threshold.value) &
(dataframe['cycle_osc'] < 50)
),
'exit_long'] = 1
return dataframe