Price movement expectation over the labeling window:
----------------------------------------------------
-trade candles = current candle date - trade candle date / timeframe (in the same time unit)
+trade duration candles = (current candle date - trade candle date) // timeframe (in the same time unit)
- trade candle
- |
-------------------------------- -> trade natr * trade open rate = expected price movement in the future label window candles
-[ trade natr label window ]
+ trade candle
+ |
+ current candle
+ |
+---------------------------------
+[ current natr label window ]
+trade duration candles = 0
+expected price movement in the future label window candles = current natr * current rate
trade candle current candle
| |
---------------------------------- -> current natr * current rate = expected price movement in the future label window candles
+---------------------------------
[ current natr label window ]
- trade candle current candle
- | |
------------------------------------------------------------------- -> current natr * current rate * trade candles // label window = expected price movement in the future (trade candles // label window candles) candles
-[ current natr label window ][ current natr label window ]
-
+trade duration candles > 0
+expected price movement in the future label window candles = current natr * current rate
+expected price movement in the future trade candles window = current natr * current rate * trades duration candles / label window
+ trade 1 candle trade 2 candle current candle
+ | | |
+------------------------------------------------------------------
+[ current natr label window ][ current natr label window ]
+expected price movement in the future trade candles approximation = current natr * current rate * trade duration candles / label window
from functools import reduce
import datetime
from pathlib import Path
+from statistics import fmean
import talib.abstract as ta
from pandas import DataFrame, Series, isna
from technical import qtpylib
@property
def trailing_stoploss_natr_ratio(self) -> float:
- return self.config.get("trailing_stoploss_natr_ratio", 0.05)
+ return self.config.get("trailing_stoploss_natr_ratio", 0.025)
# Trailing stop:
trailing_stop = False
return None
return entry_candle
- def get_trade_candles(self, df: DataFrame, trade: Trade) -> int | None:
+ def get_trade_entry_natr(self, df: DataFrame, trade: Trade) -> float | None:
+ entry_candle = self.get_trade_entry_candle(df, trade)
+ if isna(entry_candle):
+ return None
+ entry_candle = entry_candle.squeeze()
+ return entry_candle["natr_ratio_labeling_window"]
+
+ def get_trade_duration_candles(self, df: DataFrame, trade: Trade) -> int | None:
entry_candle = self.get_trade_entry_candle(df, trade)
if isna(entry_candle):
return None
trade_duration_minutes = (
current_candle_date - entry_candle_date
).total_seconds() / 60.0
- return max(
- int(trade_duration_minutes / timeframe_to_minutes(self.timeframe)), 1
- )
+ return trade_duration_minutes // timeframe_to_minutes(self.timeframe)
- def get_trade_entry_natr(self, df: DataFrame, trade: Trade) -> float | None:
- entry_candle = self.get_trade_entry_candle(df, trade)
- if isna(entry_candle):
+ def is_trade_duration_valid(self, df: DataFrame, trade: Trade) -> bool:
+ trade_duration_candles = self.get_trade_duration_candles(df, trade)
+ if isna(trade_duration_candles):
+ return False
+ if trade_duration_candles == 0:
+ return False
+ return True
+
+ def get_stoploss_distance(
+ self, df: DataFrame, trade: Trade, current_rate: float
+ ) -> float | None:
+ if self.is_trade_duration_valid(df, trade) is False:
return None
- entry_candle = entry_candle.squeeze()
- return entry_candle["natr_ratio_labeling_window"]
+ current_natr = df["natr_ratio_labeling_window"].iloc[-1]
+ if isna(current_natr):
+ return None
+ return current_rate * current_natr * self.trailing_stoploss_natr_ratio
- def get_trade_stoploss_distance(self, df: DataFrame, trade: Trade) -> float | None:
+ def get_take_profit_distance(self, df: DataFrame, trade: Trade) -> float | None:
+ if self.is_trade_duration_valid(df, trade) is False:
+ return None
entry_natr = self.get_trade_entry_natr(df, trade)
if isna(entry_natr):
return None
- return trade.open_rate * entry_natr * self.trailing_stoploss_natr_ratio
-
- def get_current_stoploss_distance(
- self, df: DataFrame, current_rate: float
- ) -> float | None:
current_natr = df["natr_ratio_labeling_window"].iloc[-1]
if isna(current_natr):
return None
- return current_rate * current_natr * self.trailing_stoploss_natr_ratio
-
- def get_stoploss_distance(
- self, df: DataFrame, trade: Trade, current_rate: float
- ) -> float | None:
- label_window_frequency = self.get_trade_candles(df, trade) // (
- self.get_label_period_candles(trade.pair) * 2
+ return (
+ trade.open_rate
+ * max(entry_natr, fmean([entry_natr, current_natr]))
+ * self.trailing_stoploss_natr_ratio
+ * self.reward_risk_ratio
)
- # trade_stoploss_distance = self.get_trade_stoploss_distance(df, trade)
- current_stoploss_distance = self.get_current_stoploss_distance(
- df, current_rate
- ) / self.get_trade_candles(df, trade)
- # if isna(trade_stoploss_distance) or isna(current_stoploss_distance):
- # return None
- # return max(trade_stoploss_distance, current_stoploss_distance)
- if isna(current_stoploss_distance):
- return None
- return current_stoploss_distance
def custom_stoploss(
self,
if isna(stoploss_distance):
return None
if stoploss_distance == 0:
- logger.warning(
- f"Stoploss distance is 0 for trade {trade.id} on pair {pair}. "
- f"Current rate: {current_rate}, trade open rate: {trade.open_rate}, "
- f"trade entry tag: {trade.enter_tag}"
- )
return None
sign = 1 if trade.is_short else -1
return stoploss_from_absolute(
):
return "maxima_detected_long"
- take_profit_distance = (
- self.get_stoploss_distance(df, trade, current_rate) * self.reward_risk_ratio
- )
+ take_profit_distance = self.get_take_profit_distance(df, trade)
if isna(take_profit_distance):
return None
if take_profit_distance == 0:
- logger.warning(
- f"Take profit distance is 0 for trade {trade.id} on pair {pair}. "
- f"Current rate: {current_rate}, trade open rate: {trade.open_rate}, "
- f"trade entry tag: {trade.enter_tag}"
- )
return None
if trade.is_short:
take_profit_price = trade.open_rate - take_profit_distance