Timeframe
5m
Direction
Long & Short
Stoploss
-10.0%
Trailing Stop
No
ROI
0m: 10000.0%
Interface Version
3
Startup Candles
N/A
Indicators
0
freqtrade/freqtrade-strategies
author@: lenik
import logging
from datetime import datetime
from typing import Dict, List, Optional, Tuple
import talib.abstract as ta
from freqtrade.strategy import IStrategy, merge_informative_pair
from freqtrade.persistence import Trade
from pandas import DataFrame
import numpy as np
logger = logging.getLogger(__name__)
class SharkMeatOTE(IStrategy):
INTERFACE_VERSION = 3
# --- [ TIMEFRAMES ] ---
timeframe = '5m'
inf_1h = '1h'
startup_candle_count: int = 200
process_only_new_candles = True
# --- [ RISK MANAGEMENT ] ---
can_short = True
stoploss = -0.10
minimal_roi = {"0": 100.0}
use_custom_stoploss = True
# --- [ GLOBAL MASTER FILTER ] ---
manual_btc_high = 71700.0
manual_btc_low = 69680.0
# --- [ STRATEGY PARAMETERS ] ---
ote_zone_low = 0.58
ote_zone_high = 0.79
# State tracking
last_log_time: Dict[str, datetime] = {}
def informative_pairs(self) -> List[Tuple[str, str]]:
return [("BTC/USDT:USDT", self.inf_1h)]
def leverage(self, *args, **kwargs) -> float:
return 5.0
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# Standard Triggers
dataframe['engulfing'] = ta.CDLENGULFING(dataframe)
dataframe['fvg_bullish'] = (dataframe['low'] > dataframe['high'].shift(2)) & \
(dataframe['close'].shift(1) > dataframe['open'].shift(1))
dataframe['fvg_bearish'] = (dataframe['high'] < dataframe['low'].shift(2)) & \
(dataframe['close'].shift(1) < dataframe['open'].shift(1))
# Range Analysis (20-candle structure)
dataframe['range_high'] = dataframe['high'].rolling(window=20).max().shift(1)
dataframe['range_low'] = dataframe['low'].rolling(window=20).min().shift(1)
dataframe['range_width'] = dataframe['range_high'] - dataframe['range_low']
# Wyckoff "W" (Spring) Logic
# Type 1: Deep Shakeout (Dipped > 2% of range width below floor)
dataframe['spring_type1'] = (dataframe['low'] < (dataframe['range_low'] - (dataframe['range_width'] * 0.02))) & \
(dataframe['close'] > dataframe['range_low'])
# Type 2: Secondary Test (Reclaim + Higher Low pullback)
dataframe['spring_type2'] = (dataframe['low'] > dataframe['low'].shift(1)) & \
(dataframe['low'].shift(1) < dataframe['range_low']) & \
(dataframe['close'] > dataframe['range_low'])
# Type 3: Minor Spring (Shallow peek < 1% range width)
dataframe['spring_type3'] = (dataframe['low'] < dataframe['range_low']) & \
(dataframe['low'] > (dataframe['range_low'] - (dataframe['range_width'] * 0.01))) & \
(dataframe['close'] > dataframe['range_low'])
# Wyckoff "M" (Upthrust) Logic
# Type 1: Violent Upthrust
dataframe['upthrust_type1'] = (dataframe['high'] > (dataframe['range_high'] + (dataframe['range_width'] * 0.02))) & \
(dataframe['close'] < dataframe['range_high'])
# Type 2: Secondary Test (Lower High after sweep)
dataframe['upthrust_type2'] = (dataframe['high'] < dataframe['high'].shift(1)) & \
(dataframe['high'].shift(1) > dataframe['range_high']) & \
(dataframe['close'] < dataframe['range_high'])
# Type 3: Minor Upthrust
dataframe['upthrust_type3'] = (dataframe['high'] > dataframe['range_high']) & \
(dataframe['high'] < (dataframe['range_high'] + (dataframe['range_width'] * 0.01))) & \
(dataframe['close'] < dataframe['range_high'])
# BTC Master Filter
btc_inf = self.dp.get_pair_dataframe(pair="BTC/USDT:USDT", timeframe=self.inf_1h)
if btc_inf is not None and not btc_inf.empty:
btc_inf['btc_range'] = self.manual_btc_high - self.manual_btc_low
btc_inf['btc_in_long_zone'] = (btc_inf['close'] >= (self.manual_btc_high - (btc_inf['btc_range'] * self.ote_zone_high))) & \
(btc_inf['close'] <= (self.manual_btc_high - (btc_inf['btc_range'] * self.ote_zone_low)))
btc_inf['btc_in_short_zone'] = (btc_inf['close'] <= (self.manual_btc_low + (btc_inf['btc_range'] * self.ote_zone_high))) & \
(btc_inf['close'] >= (self.manual_btc_low + (btc_inf['btc_range'] * self.ote_zone_low)))
dataframe = merge_informative_pair(dataframe, btc_inf, self.timeframe, self.inf_1h, ffill=True)
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
btc_long = dataframe[f'btc_in_long_zone_{self.inf_1h}']
btc_short = dataframe[f'btc_in_short_zone_{self.inf_1h}']
# --- [ LONG TAGGING & ENTRIES ] ---
dataframe.loc[btc_long & dataframe['spring_type1'], 'enter_tag'] = 'W_Type1_Shakeout'
dataframe.loc[btc_long & dataframe['spring_type2'], 'enter_tag'] = 'W_Type2_Test'
dataframe.loc[btc_long & dataframe['spring_type3'], 'enter_tag'] = 'W_Type3_Minor'
dataframe.loc[btc_long & (dataframe['fvg_bullish']), 'enter_tag'] = 'FVG_Long'
dataframe.loc[btc_long & (dataframe['engulfing'] == 100), 'enter_tag'] = 'Engulfing_Long'
dataframe.loc[btc_long & (
dataframe['spring_type1'] | dataframe['spring_type2'] | dataframe['spring_type3'] |
dataframe['fvg_bullish'] | (dataframe['engulfing'] == 100)
), 'enter_long'] = 1
# --- [ SHORT TAGGING & ENTRIES ] ---
dataframe.loc[btc_short & dataframe['upthrust_type1'], 'enter_tag'] = 'M_Type1_Shakeout'
dataframe.loc[btc_short & dataframe['upthrust_type2'], 'enter_tag'] = 'M_Type2_Test'
dataframe.loc[btc_short & dataframe['upthrust_type3'], 'enter_tag'] = 'M_Type3_Minor'
dataframe.loc[btc_short & (dataframe['fvg_bearish']), 'enter_tag'] = 'FVG_Short'
dataframe.loc[btc_short & (dataframe['engulfing'] == -100), 'enter_tag'] = 'Engulfing_Short'
dataframe.loc[btc_short & (
dataframe['upthrust_type1'] | dataframe['upthrust_type2'] | dataframe['upthrust_type3'] |
dataframe['fvg_bearish'] | (dataframe['engulfing'] == -100)
), 'enter_short'] = 1
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[:, 'exit_long'] = 0
dataframe.loc[:, 'exit_short'] = 0
return dataframe
def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime,
current_rate: float, current_profit: float, **kwargs) -> float:
if current_profit >= 0.09:
trade.set_custom_data('exit_tag', 'Trailing_Sniper')
return -0.01
if current_profit >= 0.06:
trade.set_custom_data('exit_tag', 'Profit_Lock_2.8')
return (current_rate - (trade.open_rate * 1.028)) / current_rate
trade.set_custom_data('exit_tag', 'Initial_Stop')
return self.stoploss