┏━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━┓
┃ Strategy ┃ Trades ┃ Avg Profit % ┃ Tot Profit USDC ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃ Drawdown ┃ ┡━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━┩ │ Empty │ 45 │ 3.05 │ 903.533 │ 90.35 │ 15 days, 1:43:00 │ 44 0 1 97.8 │ 1300.8 USDC 40.60% │ └──────────┴────────┴──────────────┴─────────────────┴──────────────┴──────────────────┴────────────────────────┴─────────────────────┘
This commit is contained in:
38
Empty.json
38
Empty.json
@@ -4,6 +4,15 @@
|
|||||||
"roi": {
|
"roi": {
|
||||||
"0": 10
|
"0": 10
|
||||||
},
|
},
|
||||||
|
"stoploss": {
|
||||||
|
"stoploss": -1.0
|
||||||
|
},
|
||||||
|
"trailing": {
|
||||||
|
"trailing_stop": false,
|
||||||
|
"trailing_stop_positive": null,
|
||||||
|
"trailing_stop_positive_offset": 0.0,
|
||||||
|
"trailing_only_offset_is_reached": false
|
||||||
|
},
|
||||||
"max_open_trades": {
|
"max_open_trades": {
|
||||||
"max_open_trades": 20
|
"max_open_trades": 20
|
||||||
},
|
},
|
||||||
@@ -21,30 +30,15 @@
|
|||||||
"buy_real_num1": 1.0,
|
"buy_real_num1": 1.0,
|
||||||
"buy_real_num2": -0.4
|
"buy_real_num2": -0.4
|
||||||
},
|
},
|
||||||
|
"protection": {
|
||||||
|
"stop_buying_indicator": "stop_buying12_1d",
|
||||||
|
"stoploss_indicator": "stop_buying12_1d",
|
||||||
|
"stoploss_timeperiod": "12"
|
||||||
|
},
|
||||||
"sell": {
|
"sell": {
|
||||||
"sell_crossed_indicator0": "sma24_deriv2",
|
"sell_score_indicator": "sma48_score"
|
||||||
"sell_crossed_indicator1": "sma48_score",
|
|
||||||
"sell_crossed_indicator2": "sma24_score",
|
|
||||||
"sell_indicator0": "sma5_deriv2",
|
|
||||||
"sell_indicator1": "sma60_score",
|
|
||||||
"sell_indicator2": "sma60_score",
|
|
||||||
"sell_operator0": "<",
|
|
||||||
"sell_operator1": ">",
|
|
||||||
"sell_operator2": "CB",
|
|
||||||
"sell_real_num0": 0.7,
|
|
||||||
"sell_real_num1": 0.5,
|
|
||||||
"sell_real_num2": 0.9
|
|
||||||
},
|
|
||||||
"stoploss": {
|
|
||||||
"stoploss": -1
|
|
||||||
},
|
|
||||||
"trailing": {
|
|
||||||
"trailing_stop": false,
|
|
||||||
"trailing_stop_positive": 0.055,
|
|
||||||
"trailing_stop_positive_offset": 0.106,
|
|
||||||
"trailing_only_offset_is_reached": false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ft_stratparam_v": 1,
|
"ft_stratparam_v": 1,
|
||||||
"export_time": "2026-02-19 19:14:20.539321+00:00"
|
"export_time": "2026-02-20 20:19:59.741160+00:00"
|
||||||
}
|
}
|
||||||
466
Empty.py
466
Empty.py
@@ -25,6 +25,8 @@ from random import shuffle
|
|||||||
|
|
||||||
timeperiods = [3, 5, 12, 24, 48, 60]
|
timeperiods = [3, 5, 12, 24, 48, 60]
|
||||||
|
|
||||||
|
score_indicators = list()
|
||||||
|
stoploss_indicators = list()
|
||||||
god_genes_with_timeperiod = list()
|
god_genes_with_timeperiod = list()
|
||||||
for timeperiod in timeperiods:
|
for timeperiod in timeperiods:
|
||||||
# god_genes_with_timeperiod.append(f'max{timeperiod}')
|
# god_genes_with_timeperiod.append(f'max{timeperiod}')
|
||||||
@@ -34,11 +36,20 @@ for timeperiod in timeperiods:
|
|||||||
god_genes_with_timeperiod.append(f"sma{timeperiod}_deriv1")
|
god_genes_with_timeperiod.append(f"sma{timeperiod}_deriv1")
|
||||||
god_genes_with_timeperiod.append(f"sma{timeperiod}_deriv2")
|
god_genes_with_timeperiod.append(f"sma{timeperiod}_deriv2")
|
||||||
god_genes_with_timeperiod.append(f"sma{timeperiod}_score")
|
god_genes_with_timeperiod.append(f"sma{timeperiod}_score")
|
||||||
|
|
||||||
|
# stoploss_indicators.append(f"stop_buying{timeperiod}")
|
||||||
|
stoploss_indicators.append(f"stop_buying{timeperiod}_1d")
|
||||||
|
|
||||||
|
score_indicators.append(f"sma{timeperiod}_score")
|
||||||
|
# score_indicators.append(f"sma{timeperiod}_score_1d")
|
||||||
|
|
||||||
# god_genes_with_timeperiod.append(f"sma{timeperiod}_trend_up")
|
# god_genes_with_timeperiod.append(f"sma{timeperiod}_trend_up")
|
||||||
# god_genes_with_timeperiod.append(f"sma{timeperiod}_trend_down")
|
# god_genes_with_timeperiod.append(f"sma{timeperiod}_trend_down")
|
||||||
# god_genes_with_timeperiod.append(f"sma{timeperiod}_trend_change_up")
|
# god_genes_with_timeperiod.append(f"sma{timeperiod}_trend_change_up")
|
||||||
# god_genes_with_timeperiod.append(f"sma{timeperiod}_trend_change_down")
|
# god_genes_with_timeperiod.append(f"sma{timeperiod}_trend_change_down")
|
||||||
|
|
||||||
|
print(stoploss_indicators)
|
||||||
|
|
||||||
operators = [
|
operators = [
|
||||||
"D", # Disabled gene
|
"D", # Disabled gene
|
||||||
">", # Indicator, bigger than cross indicator
|
">", # Indicator, bigger than cross indicator
|
||||||
@@ -249,14 +260,13 @@ class Empty(IStrategy):
|
|||||||
|
|
||||||
# Stoploss:
|
# Stoploss:
|
||||||
stoploss = -1
|
stoploss = -1
|
||||||
trailing_stop = True
|
trailing_stop = False
|
||||||
trailing_stop_positive = 0.15
|
# trailing_stop_positive = 0.15
|
||||||
trailing_stop_positive_offset = 0.20
|
# trailing_stop_positive_offset = 0.20
|
||||||
trailing_only_offset_is_reached = True
|
# trailing_only_offset_is_reached = False
|
||||||
|
|
||||||
position_adjustment_enable = True
|
position_adjustment_enable = True
|
||||||
use_custom_stoploss = True
|
use_custom_stoploss = False
|
||||||
|
|
||||||
|
|
||||||
#max_open_trades = 3
|
#max_open_trades = 3
|
||||||
|
|
||||||
@@ -295,6 +305,7 @@ class Empty(IStrategy):
|
|||||||
'last_date': 0,
|
'last_date': 0,
|
||||||
'stop': False,
|
'stop': False,
|
||||||
'max_profit': 0,
|
'max_profit': 0,
|
||||||
|
'last_profit': 0,
|
||||||
'total_amount': 0,
|
'total_amount': 0,
|
||||||
'has_gain': 0,
|
'has_gain': 0,
|
||||||
'force_sell': False,
|
'force_sell': False,
|
||||||
@@ -356,22 +367,27 @@ class Empty(IStrategy):
|
|||||||
buy_real_num2 = DecimalParameter(-1, 1, decimals=DECIMALS, default=0, space='buy')
|
buy_real_num2 = DecimalParameter(-1, 1, decimals=DECIMALS, default=0, space='buy')
|
||||||
|
|
||||||
# Sell Hyperoptable Parameters/Spaces.
|
# Sell Hyperoptable Parameters/Spaces.
|
||||||
sell_crossed_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="CDLSHOOTINGSTAR-150", space='sell')
|
# sell_crossed_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="CDLSHOOTINGSTAR-150", space='sell')
|
||||||
sell_crossed_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="MAMA-1-100", space='sell')
|
# sell_crossed_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="MAMA-1-100", space='sell')
|
||||||
sell_crossed_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDLMATHOLD-6", space='sell')
|
# sell_crossed_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDLMATHOLD-6", space='sell')
|
||||||
|
#
|
||||||
|
# sell_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="CDLUPSIDEGAP2CROWS-5", space='sell')
|
||||||
|
# sell_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="CDLHARAMICROSS-150", space='sell')
|
||||||
|
# sell_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDL2CROWS-5", space='sell')
|
||||||
|
#
|
||||||
|
# sell_operator0 = CategoricalParameter(operators, default="<R", space='sell')
|
||||||
|
# sell_operator1 = CategoricalParameter(operators, default="D", space='sell')
|
||||||
|
# sell_operator2 = CategoricalParameter(operators, default="/>R", space='sell')
|
||||||
|
#
|
||||||
|
# sell_real_num0 = DecimalParameter(-1, 1, decimals=DECIMALS, default=0, space='sell')
|
||||||
|
# sell_real_num1 = DecimalParameter(-1, 1, decimals=DECIMALS, default=0, space='sell')
|
||||||
|
# sell_real_num2 = DecimalParameter(-1, 1, decimals=DECIMALS, default=0, space='sell')
|
||||||
|
|
||||||
sell_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="CDLUPSIDEGAP2CROWS-5", space='sell')
|
sell_score_indicator = CategoricalParameter(score_indicators, default="sma24_score", space='sell')
|
||||||
sell_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="CDLHARAMICROSS-150", space='sell')
|
|
||||||
sell_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDL2CROWS-5", space='sell')
|
|
||||||
|
|
||||||
sell_operator0 = CategoricalParameter(operators, default="<R", space='sell')
|
|
||||||
sell_operator1 = CategoricalParameter(operators, default="D", space='sell')
|
|
||||||
sell_operator2 = CategoricalParameter(operators, default="/>R", space='sell')
|
|
||||||
|
|
||||||
sell_real_num0 = DecimalParameter(-1, 1, decimals=DECIMALS, default=0, space='sell')
|
|
||||||
sell_real_num1 = DecimalParameter(-1, 1, decimals=DECIMALS, default=0, space='sell')
|
|
||||||
sell_real_num2 = DecimalParameter(-1, 1, decimals=DECIMALS, default=0, space='sell')
|
|
||||||
|
|
||||||
|
stoploss_indicator = CategoricalParameter(stoploss_indicators, default="stop_buying12_1d", space='protection')
|
||||||
|
stop_buying_indicator = CategoricalParameter(stoploss_indicators, default="stop_buying12_1d", space='protection')
|
||||||
|
stoploss_timeperiod = CategoricalParameter(timeperiods, default="12", space='protection')
|
||||||
|
|
||||||
def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
|
def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
|
||||||
proposed_stake: float, min_stake: float, max_stake: float,
|
proposed_stake: float, min_stake: float, max_stake: float,
|
||||||
@@ -384,7 +400,21 @@ class Empty(IStrategy):
|
|||||||
if (self.pairs[pair]['first_amount'] > 0):
|
if (self.pairs[pair]['first_amount'] > 0):
|
||||||
amount = min(self.wallets.get_available_stake_amount(), self.pairs[pair]['first_amount'])
|
amount = min(self.wallets.get_available_stake_amount(), self.pairs[pair]['first_amount'])
|
||||||
else:
|
else:
|
||||||
amount = self.wallets.get_available_stake_amount() / 4
|
# range_min = last_candle[f"min{self.stoploss_timeperiod.value}_1d"]
|
||||||
|
# range_max = last_candle[f"max{self.stoploss_timeperiod.value}_1d"]
|
||||||
|
#
|
||||||
|
# if range_max == range_min:
|
||||||
|
# return -0.1 # sécurité
|
||||||
|
#
|
||||||
|
# range_pos = (last_candle['close'] - range_min) / (range_max - range_min)
|
||||||
|
|
||||||
|
range_pos = last_candle[f"range_pos"]
|
||||||
|
sl_min = self.wallets.get_available_stake_amount() / 2
|
||||||
|
sl_max = self.wallets.get_available_stake_amount() / 6
|
||||||
|
|
||||||
|
amount = sl_min + (1 - range_pos) * (sl_max - sl_min)
|
||||||
|
# amount = self.wallets.get_available_stake_amount() / 8
|
||||||
|
|
||||||
return min(amount, self.wallets.get_available_stake_amount())
|
return min(amount, self.wallets.get_available_stake_amount())
|
||||||
|
|
||||||
def adjust_trade_position(self, trade: Trade, current_time: datetime,
|
def adjust_trade_position(self, trade: Trade, current_time: datetime,
|
||||||
@@ -442,7 +472,7 @@ class Empty(IStrategy):
|
|||||||
|
|
||||||
drop_from_last_entry = (current_rate - last_entry_price) / last_entry_price
|
drop_from_last_entry = (current_rate - last_entry_price) / last_entry_price
|
||||||
|
|
||||||
if drop_from_last_entry <= -0.05:
|
if drop_from_last_entry <= -0.025 and last_candle['min60'] == last_candle_3['min60']:
|
||||||
# stake_amount = trade.stake_amount
|
# stake_amount = trade.stake_amount
|
||||||
print(f"adjust {current_time} {stake_amount}")
|
print(f"adjust {current_time} {stake_amount}")
|
||||||
print(f"adjust {pair} {current_time} dispo={dispo} amount={stake_amount} rate={current_rate}")
|
print(f"adjust {pair} {current_time} dispo={dispo} amount={stake_amount} rate={current_rate}")
|
||||||
@@ -462,7 +492,19 @@ class Empty(IStrategy):
|
|||||||
last_candle_2 = dataframe.iloc[-2].squeeze()
|
last_candle_2 = dataframe.iloc[-2].squeeze()
|
||||||
last_candle_3 = dataframe.iloc[-3].squeeze()
|
last_candle_3 = dataframe.iloc[-3].squeeze()
|
||||||
|
|
||||||
condition = True #(last_candle[f"{indic_5m}_deriv1"] >= indic_deriv1_5m) and (last_candle[f"{indic_5m}_deriv2"] >= indic_deriv2_5m)
|
condition = True
|
||||||
|
|
||||||
|
if self.pairs[pair]['last_trade'] and self.pairs[pair]['last_trade'].close_date:
|
||||||
|
# base cooldown = n bougies / cooldown proportionnel au profit
|
||||||
|
# bougies de plus par %
|
||||||
|
cooldown_candles = 12 + 6 * (int(self.pairs[pair]['last_profit'] / 0.01)) # réglable
|
||||||
|
|
||||||
|
# temps depuis la fermeture
|
||||||
|
candles_since_close = ((current_time - self.pairs[pair]['last_trade'].close_date).total_seconds() / 3600) # seconds / heure
|
||||||
|
|
||||||
|
condition = (candles_since_close >= cooldown_candles)
|
||||||
|
|
||||||
|
print(f"Cool down {round(self.pairs[pair]['last_profit'], 3)} {cooldown_candles} {candles_since_close}")
|
||||||
|
|
||||||
# self.should_enter_trade(pair, last_candle, current_time)
|
# self.should_enter_trade(pair, last_candle, current_time)
|
||||||
allow_to_buy = (condition and not self.pairs[pair]['stop']) | (entry_tag == 'force_entry')
|
allow_to_buy = (condition and not self.pairs[pair]['stop']) | (entry_tag == 'force_entry')
|
||||||
@@ -482,6 +524,8 @@ class Empty(IStrategy):
|
|||||||
self.pairs[pair]['last_candle'] = last_candle
|
self.pairs[pair]['last_candle'] = last_candle
|
||||||
self.pairs[pair]['count_of_buys'] = 1
|
self.pairs[pair]['count_of_buys'] = 1
|
||||||
self.pairs[pair]['current_profit'] = 0
|
self.pairs[pair]['current_profit'] = 0
|
||||||
|
self.pairs[pair]['last_profit'] = 0
|
||||||
|
self.pairs[pair]['last_trade'] = None
|
||||||
self.pairs[pair]['last_max'] = max(last_candle['close'], self.pairs[pair]['last_max'])
|
self.pairs[pair]['last_max'] = max(last_candle['close'], self.pairs[pair]['last_max'])
|
||||||
self.pairs[pair]['last_min'] = min(last_candle['close'], self.pairs[pair]['last_min'])
|
self.pairs[pair]['last_min'] = min(last_candle['close'], self.pairs[pair]['last_min'])
|
||||||
self.pairs[pair]['last_date'] = current_time
|
self.pairs[pair]['last_date'] = current_time
|
||||||
@@ -492,7 +536,7 @@ class Empty(IStrategy):
|
|||||||
stake_amount = self.adjust_stake_amount(pair, last_candle)
|
stake_amount = self.adjust_stake_amount(pair, last_candle)
|
||||||
self.pairs[pair]['first_amount'] = stake_amount
|
self.pairs[pair]['first_amount'] = stake_amount
|
||||||
self.pairs[pair]['total_amount'] = stake_amount
|
self.pairs[pair]['total_amount'] = stake_amount
|
||||||
print(f"Buy {pair} {current_time} {entry_tag} dispo={dispo} amount={amount} rate={rate} rate={rate}")
|
print(f"Buy {pair} {current_time} {entry_tag} dispo={dispo} amount={stake_amount} rate={rate} rate={rate}")
|
||||||
|
|
||||||
# self.log_trade(
|
# self.log_trade(
|
||||||
# last_candle=last_candle,
|
# last_candle=last_candle,
|
||||||
@@ -519,7 +563,7 @@ class Empty(IStrategy):
|
|||||||
|
|
||||||
profit = trade.calc_profit(rate)
|
profit = trade.calc_profit(rate)
|
||||||
force = self.pairs[pair]['force_sell']
|
force = self.pairs[pair]['force_sell']
|
||||||
allow_to_sell = (last_candle['hapercent'] < 0 and profit > 0) or force or (exit_reason == 'force_exit') or (exit_reason == 'stop_loss')
|
allow_to_sell = (last_candle['hapercent'] < 0 and profit > 0) or force or (exit_reason == 'force_exit') or (exit_reason == 'stop_loss') or (exit_reason == 'trailing_stop_loss')
|
||||||
|
|
||||||
minutes = int(round((current_time - trade.date_last_filled_utc).total_seconds() / 60, 0))
|
minutes = int(round((current_time - trade.date_last_filled_utc).total_seconds() / 60, 0))
|
||||||
|
|
||||||
@@ -530,7 +574,7 @@ class Empty(IStrategy):
|
|||||||
profit = trade.calc_profit(rate)
|
profit = trade.calc_profit(rate)
|
||||||
self.pairs[pair]['previous_profit'] = profit
|
self.pairs[pair]['previous_profit'] = profit
|
||||||
dispo = round(self.wallets.get_available_stake_amount())
|
dispo = round(self.wallets.get_available_stake_amount())
|
||||||
print(f"Sell {pair} {current_time} {exit_reason} dispo={dispo} amount={amount} rate={rate} open_rate={trade.open_rate} profit={profit}")
|
print(f"Sell {pair} {current_time} {exit_reason} dispo={dispo} rate={rate} open_rate={trade.open_rate} profit={profit}")
|
||||||
# self.log_trade(
|
# self.log_trade(
|
||||||
# last_candle=last_candle,
|
# last_candle=last_candle,
|
||||||
# date=current_time,
|
# date=current_time,
|
||||||
@@ -541,6 +585,7 @@ class Empty(IStrategy):
|
|||||||
# dispo=dispo,
|
# dispo=dispo,
|
||||||
# profit=round(profit, 2)
|
# profit=round(profit, 2)
|
||||||
# )
|
# )
|
||||||
|
self.pairs[pair]['first_amount'] = 0
|
||||||
self.pairs[pair]['force_sell'] = False
|
self.pairs[pair]['force_sell'] = False
|
||||||
self.pairs[pair]['has_gain'] = 0
|
self.pairs[pair]['has_gain'] = 0
|
||||||
self.pairs[pair]['current_profit'] = 0
|
self.pairs[pair]['current_profit'] = 0
|
||||||
@@ -551,42 +596,71 @@ class Empty(IStrategy):
|
|||||||
self.pairs[pair]['last_date'] = current_time
|
self.pairs[pair]['last_date'] = current_time
|
||||||
self.pairs[pair]['last_trade'] = self.pairs[pair]['current_trade']
|
self.pairs[pair]['last_trade'] = self.pairs[pair]['current_trade']
|
||||||
self.pairs[pair]['current_trade'] = None
|
self.pairs[pair]['current_trade'] = None
|
||||||
# else:
|
else:
|
||||||
# print(f"STOP triggered for {pair} ({exit_reason}) but condition blocked", "warning")
|
print(f"{current_time} STOP triggered for {pair} ({exit_reason}) but condition blocked", "warning")
|
||||||
return (allow_to_sell) | (exit_reason == 'force_exit') | (exit_reason == 'stop_loss') | force
|
return (allow_to_sell) | (exit_reason == 'force_exit') | (exit_reason == 'stop_loss') | force
|
||||||
|
|
||||||
def custom_exit(self, pair, trade, current_time,
|
def custom_exit(self, pair, trade, current_time, current_rate, current_profit, **kwargs):
|
||||||
current_rate, current_profit, **kwargs):
|
|
||||||
|
|
||||||
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
|
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
|
||||||
last_row = dataframe.iloc[-1]
|
last_candle = dataframe.iloc[-1]
|
||||||
|
before_last_candle = dataframe.iloc[-2]
|
||||||
self.pairs[pair]['current_trade'] = trade
|
self.pairs[pair]['current_trade'] = trade
|
||||||
momentum = last_row["sma24_score"]
|
momentum = last_candle[self.sell_score_indicator.value]
|
||||||
|
|
||||||
# Si momentum fort → on laisse courir
|
# Si momentum fort → on laisse courir
|
||||||
if momentum > 1:
|
if momentum > 1:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Si momentum faiblit → on prend profit plus tôt
|
# Si momentum faiblit → on prend profit plus tôt
|
||||||
if momentum < 0 and current_profit > 0.02:
|
if momentum < 0 and current_profit > 0.02 and last_candle[self.sell_score_indicator.value] < before_last_candle[self.sell_score_indicator.value]\
|
||||||
|
and last_candle['close'] < last_candle['sma5']:
|
||||||
|
self.pairs[pair]['last_profit'] = current_profit
|
||||||
return "momentum_drop"
|
return "momentum_drop"
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def custom_stoploss(self, pair, trade, current_time,
|
# def custom_stoploss(self, pair, trade, current_time, current_rate, current_profit, **kwargs):
|
||||||
current_rate, current_profit, **kwargs):
|
# dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
|
||||||
|
# last_candle = dataframe.iloc[-1]
|
||||||
if current_profit < - 0.1 and self.wallets.get_available_stake_amount():
|
# profit = trade.calc_profit(current_rate)
|
||||||
return -0.1
|
#
|
||||||
|
# # print(f'test stop loss {self.stoploss} {last_candle["stop_buying12_1d"]}')
|
||||||
return -1
|
# if last_candle[self.stoploss_indicator.value] and (trade.nr_of_successful_entries >= 4 or self.wallets.get_available_stake_amount() < 300): # and profit < - 30 :
|
||||||
|
# range_min = last_candle[f"min{self.stoploss_timeperiod.value}_1d"]
|
||||||
|
# range_max = last_candle[f"max{self.stoploss_timeperiod.value}_1d"]
|
||||||
|
#
|
||||||
|
# if range_max == range_min:
|
||||||
|
# print(f'ranges={range_min}')
|
||||||
|
# return -0.1 # sécurité
|
||||||
|
#
|
||||||
|
# range_pos = (current_rate - range_min) / (range_max - range_min)
|
||||||
|
#
|
||||||
|
# if (range_pos > 1):
|
||||||
|
# return -1
|
||||||
|
#
|
||||||
|
# sl_min = -0.02
|
||||||
|
# sl_max = -0.1 #self.stoploss
|
||||||
|
#
|
||||||
|
# dynamic_sl = (sl_min + (1 - range_pos) * (sl_max - sl_min))
|
||||||
|
#
|
||||||
|
# print(f'{current_time} Loss ranges={round(range_min,0)} {round(range_max, 0)} range_pos={round(range_pos, 3)} dynamic_sl={round(dynamic_sl, 3)} '
|
||||||
|
# f'profit={profit} current={current_profit} {self.stoploss_indicator.value} {self.stoploss_timeperiod.value} {last_candle[self.stoploss_indicator.value]}')
|
||||||
|
#
|
||||||
|
# return dynamic_sl
|
||||||
|
#
|
||||||
|
# return -1
|
||||||
|
|
||||||
def informative_pairs(self):
|
def informative_pairs(self):
|
||||||
|
# get access to all pairs available in whitelist.
|
||||||
|
pairs = self.dp.current_whitelist()
|
||||||
|
informative_pairs = [(pair, '1d') for pair in pairs]
|
||||||
|
# informative_pairs += [(pair, '1h') for pair in pairs]
|
||||||
|
|
||||||
return []
|
return informative_pairs
|
||||||
|
|
||||||
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||||
|
pair = metadata['pair']
|
||||||
dataframe = dataframe.copy()
|
dataframe = dataframe.copy()
|
||||||
heikinashi = qtpylib.heikinashi(dataframe)
|
heikinashi = qtpylib.heikinashi(dataframe)
|
||||||
dataframe['haopen'] = heikinashi['open']
|
dataframe['haopen'] = heikinashi['open']
|
||||||
@@ -602,19 +676,64 @@ class Empty(IStrategy):
|
|||||||
dataframe[f"sma{timeperiod}"] = dataframe['mid'].ewm(span=timeperiod, adjust=False).mean()
|
dataframe[f"sma{timeperiod}"] = dataframe['mid'].ewm(span=timeperiod, adjust=False).mean()
|
||||||
self.calculeDerivees(dataframe, f"sma{timeperiod}", timeframe=self.timeframe, ema_period=timeperiod)
|
self.calculeDerivees(dataframe, f"sma{timeperiod}", timeframe=self.timeframe, ema_period=timeperiod)
|
||||||
|
|
||||||
# ######################################################################################################
|
dataframe[f'stop_buying{timeperiod}_deb'] = (dataframe[f'sma{timeperiod}_trend_change_down'] == 1)
|
||||||
dataframe['stop_buying_deb'] = (dataframe['sma60_trend_change_down'] == 1)
|
dataframe[f'stop_buying{timeperiod}_end'] = (dataframe[f'sma{timeperiod}_trend_change_up'] == 1)
|
||||||
dataframe['stop_buying_end'] = (dataframe['sma60_trend_change_up'] == 1)
|
latched = np.zeros(len(dataframe), dtype=bool)
|
||||||
latched = np.zeros(len(dataframe), dtype=bool)
|
|
||||||
|
|
||||||
for i in range(1, len(dataframe)):
|
for i in range(1, len(dataframe)):
|
||||||
if dataframe['stop_buying_deb'].iloc[i]:
|
if dataframe[f'stop_buying{timeperiod}_deb'].iloc[i]:
|
||||||
latched[i] = True
|
latched[i] = True
|
||||||
elif dataframe['stop_buying_end'].iloc[i]:
|
elif dataframe[f'stop_buying{timeperiod}_end'].iloc[i]:
|
||||||
latched[i] = False
|
latched[i] = False
|
||||||
else:
|
else:
|
||||||
latched[i] = latched[i - 1]
|
latched[i] = latched[i - 1]
|
||||||
dataframe['stop_buying'] = latched
|
dataframe[f'stop_buying{timeperiod}'] = latched
|
||||||
|
|
||||||
|
# ######################################################################################################
|
||||||
|
################### INFORMATIVE 1d
|
||||||
|
informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d")
|
||||||
|
# informative = self.populateDataframe(informative, timeframe='1d')
|
||||||
|
# heikinashi = qtpylib.heikinashi(informative)
|
||||||
|
# informative['haopen'] = heikinashi['open']
|
||||||
|
# informative['haclose'] = heikinashi['close']
|
||||||
|
# informative['hapercent'] = (informative['haclose'] - informative['haopen']) / informative['haclose']
|
||||||
|
informative['mid'] = informative['open'] + (informative['close'] - informative['open']) / 2
|
||||||
|
for timeperiod in timeperiods:
|
||||||
|
informative[f'max{timeperiod}'] = talib.MAX(informative['close'], timeperiod=timeperiod)
|
||||||
|
informative[f'min{timeperiod}'] = talib.MIN(informative['close'], timeperiod=timeperiod)
|
||||||
|
# informative[f"range{timeperiod}"] = ((informative["close"] - informative[f'min{timeperiod}']) / (informative[f'max{timeperiod}'] - informative[f'min{timeperiod}']))
|
||||||
|
# informative[f"percent{timeperiod}"] = informative['close'].pct_change(timeperiod)
|
||||||
|
informative[f"sma{timeperiod}"] = informative['mid'].ewm(span=timeperiod, adjust=False).mean()
|
||||||
|
self.calculeDerivees(informative, f"sma{timeperiod}", timeframe=self.timeframe, ema_period=timeperiod)
|
||||||
|
|
||||||
|
informative[f'stop_buying_deb{timeperiod}'] = (informative[f'sma{timeperiod}_trend_change_down'] == 1)
|
||||||
|
informative[f'stop_buying_end{timeperiod}'] = (informative[f'sma{timeperiod}_trend_change_up'] == 1)
|
||||||
|
latched = np.zeros(len(informative), dtype=bool)
|
||||||
|
|
||||||
|
for i in range(1, len(informative)):
|
||||||
|
if informative[f'stop_buying_deb{timeperiod}'].iloc[i]:
|
||||||
|
latched[i] = True
|
||||||
|
elif informative[f'stop_buying_end{timeperiod}'].iloc[i]:
|
||||||
|
latched[i] = False
|
||||||
|
else:
|
||||||
|
latched[i] = latched[i - 1]
|
||||||
|
informative[f'stop_buying{timeperiod}'] = latched
|
||||||
|
|
||||||
|
dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True)
|
||||||
|
# ######################################################################################################
|
||||||
|
|
||||||
|
range_min = dataframe[f"min12_1d"]
|
||||||
|
range_max = dataframe[f"max48"]
|
||||||
|
|
||||||
|
dataframe[f"range_pos"] = (dataframe['close'] - range_min) / (range_max - range_min)
|
||||||
|
|
||||||
|
|
||||||
|
# récupérer le dernier trade fermé
|
||||||
|
trades = Trade.get_trades_proxy(pair=pair,is_open=False)
|
||||||
|
if trades:
|
||||||
|
last_trade = trades[-1]
|
||||||
|
self.pairs[pair]['last_profit'] = last_trade.close_profit # ex: 0.12 = +12%
|
||||||
|
self.pairs[pair]['last_trade'] = last_trade
|
||||||
|
|
||||||
return dataframe
|
return dataframe
|
||||||
|
|
||||||
@@ -631,51 +750,52 @@ class Empty(IStrategy):
|
|||||||
|
|
||||||
conditions = list()
|
conditions = list()
|
||||||
|
|
||||||
# print(dataframe.columns)
|
# # print(dataframe.columns)
|
||||||
|
#
|
||||||
|
# buy_indicator = self.buy_indicator0.value
|
||||||
|
# buy_crossed_indicator = self.buy_crossed_indicator0.value
|
||||||
|
# buy_operator = self.buy_operator0.value
|
||||||
|
# buy_real_num = self.buy_real_num0.value
|
||||||
|
# condition, dataframe = condition_generator(
|
||||||
|
# dataframe,
|
||||||
|
# buy_operator,
|
||||||
|
# buy_indicator,
|
||||||
|
# buy_crossed_indicator,
|
||||||
|
# buy_real_num
|
||||||
|
# )
|
||||||
|
# conditions.append(condition)
|
||||||
|
# # backup
|
||||||
|
# buy_indicator = self.buy_indicator1.value
|
||||||
|
# buy_crossed_indicator = self.buy_crossed_indicator1.value
|
||||||
|
# buy_operator = self.buy_operator1.value
|
||||||
|
# buy_real_num = self.buy_real_num1.value
|
||||||
|
#
|
||||||
|
# condition, dataframe = condition_generator(
|
||||||
|
# dataframe,
|
||||||
|
# buy_operator,
|
||||||
|
# buy_indicator,
|
||||||
|
# buy_crossed_indicator,
|
||||||
|
# buy_real_num
|
||||||
|
# )
|
||||||
|
# conditions.append(condition)
|
||||||
|
#
|
||||||
|
# buy_indicator = self.buy_indicator2.value
|
||||||
|
# buy_crossed_indicator = self.buy_crossed_indicator2.value
|
||||||
|
# buy_operator = self.buy_operator2.value
|
||||||
|
# buy_real_num = self.buy_real_num2.value
|
||||||
|
# condition, dataframe = condition_generator(
|
||||||
|
# dataframe,
|
||||||
|
# buy_operator,
|
||||||
|
# buy_indicator,
|
||||||
|
# buy_crossed_indicator,
|
||||||
|
# buy_real_num
|
||||||
|
# )
|
||||||
|
# conditions.append(condition)
|
||||||
|
conditions.append((dataframe[self.stop_buying_indicator.value] == False))
|
||||||
|
|
||||||
buy_indicator = self.buy_indicator0.value
|
# conditions.append(dataframe['hapercent'] > 0)
|
||||||
buy_crossed_indicator = self.buy_crossed_indicator0.value
|
# # conditions.append(dataframe[f"range_pos"] <= 0.5)
|
||||||
buy_operator = self.buy_operator0.value
|
# conditions.append(dataframe[f"sma5_deriv1"] > 0)
|
||||||
buy_real_num = self.buy_real_num0.value
|
|
||||||
condition, dataframe = condition_generator(
|
|
||||||
dataframe,
|
|
||||||
buy_operator,
|
|
||||||
buy_indicator,
|
|
||||||
buy_crossed_indicator,
|
|
||||||
buy_real_num
|
|
||||||
)
|
|
||||||
conditions.append(condition)
|
|
||||||
# backup
|
|
||||||
buy_indicator = self.buy_indicator1.value
|
|
||||||
buy_crossed_indicator = self.buy_crossed_indicator1.value
|
|
||||||
buy_operator = self.buy_operator1.value
|
|
||||||
buy_real_num = self.buy_real_num1.value
|
|
||||||
|
|
||||||
condition, dataframe = condition_generator(
|
|
||||||
dataframe,
|
|
||||||
buy_operator,
|
|
||||||
buy_indicator,
|
|
||||||
buy_crossed_indicator,
|
|
||||||
buy_real_num
|
|
||||||
)
|
|
||||||
conditions.append(condition)
|
|
||||||
|
|
||||||
buy_indicator = self.buy_indicator2.value
|
|
||||||
buy_crossed_indicator = self.buy_crossed_indicator2.value
|
|
||||||
buy_operator = self.buy_operator2.value
|
|
||||||
buy_real_num = self.buy_real_num2.value
|
|
||||||
condition, dataframe = condition_generator(
|
|
||||||
dataframe,
|
|
||||||
buy_operator,
|
|
||||||
buy_indicator,
|
|
||||||
buy_crossed_indicator,
|
|
||||||
buy_real_num
|
|
||||||
)
|
|
||||||
conditions.append(condition)
|
|
||||||
conditions.append((dataframe['stop_buying'] == False))
|
|
||||||
|
|
||||||
# conditions.append((dataframe['min60'].shift(5) == dataframe['min60']))
|
|
||||||
# conditions.append((dataframe['low'] < dataframe['min60']))
|
|
||||||
|
|
||||||
print(f"BUY indicators tested \n"
|
print(f"BUY indicators tested \n"
|
||||||
f"{self.buy_indicator0.value} {self.buy_crossed_indicator0.value} {self.buy_operator0.value} {self.buy_real_num0.value} \n"
|
f"{self.buy_indicator0.value} {self.buy_crossed_indicator0.value} {self.buy_operator0.value} {self.buy_real_num0.value} \n"
|
||||||
@@ -692,57 +812,57 @@ class Empty(IStrategy):
|
|||||||
return dataframe
|
return dataframe
|
||||||
|
|
||||||
def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||||
conditions = list()
|
# conditions = list()
|
||||||
# TODO: Its not dry code!
|
# # TODO: Its not dry code!
|
||||||
sell_indicator = self.sell_indicator0.value
|
# sell_indicator = self.sell_indicator0.value
|
||||||
sell_crossed_indicator = self.sell_crossed_indicator0.value
|
# sell_crossed_indicator = self.sell_crossed_indicator0.value
|
||||||
sell_operator = self.sell_operator0.value
|
# sell_operator = self.sell_operator0.value
|
||||||
sell_real_num = self.sell_real_num0.value
|
# sell_real_num = self.sell_real_num0.value
|
||||||
condition, dataframe = condition_generator(
|
# condition, dataframe = condition_generator(
|
||||||
dataframe,
|
# dataframe,
|
||||||
sell_operator,
|
# sell_operator,
|
||||||
sell_indicator,
|
# sell_indicator,
|
||||||
sell_crossed_indicator,
|
# sell_crossed_indicator,
|
||||||
sell_real_num
|
# sell_real_num
|
||||||
)
|
# )
|
||||||
conditions.append(condition)
|
# conditions.append(condition)
|
||||||
|
#
|
||||||
sell_indicator = self.sell_indicator1.value
|
# sell_indicator = self.sell_indicator1.value
|
||||||
sell_crossed_indicator = self.sell_crossed_indicator1.value
|
# sell_crossed_indicator = self.sell_crossed_indicator1.value
|
||||||
sell_operator = self.sell_operator1.value
|
# sell_operator = self.sell_operator1.value
|
||||||
sell_real_num = self.sell_real_num1.value
|
# sell_real_num = self.sell_real_num1.value
|
||||||
condition, dataframe = condition_generator(
|
# condition, dataframe = condition_generator(
|
||||||
dataframe,
|
# dataframe,
|
||||||
sell_operator,
|
# sell_operator,
|
||||||
sell_indicator,
|
# sell_indicator,
|
||||||
sell_crossed_indicator,
|
# sell_crossed_indicator,
|
||||||
sell_real_num
|
# sell_real_num
|
||||||
)
|
# )
|
||||||
conditions.append(condition)
|
# conditions.append(condition)
|
||||||
|
#
|
||||||
sell_indicator = self.sell_indicator2.value
|
# sell_indicator = self.sell_indicator2.value
|
||||||
sell_crossed_indicator = self.sell_crossed_indicator2.value
|
# sell_crossed_indicator = self.sell_crossed_indicator2.value
|
||||||
sell_operator = self.sell_operator2.value
|
# sell_operator = self.sell_operator2.value
|
||||||
sell_real_num = self.sell_real_num2.value
|
# sell_real_num = self.sell_real_num2.value
|
||||||
condition, dataframe = condition_generator(
|
# condition, dataframe = condition_generator(
|
||||||
dataframe,
|
# dataframe,
|
||||||
sell_operator,
|
# sell_operator,
|
||||||
sell_indicator,
|
# sell_indicator,
|
||||||
sell_crossed_indicator,
|
# sell_crossed_indicator,
|
||||||
sell_real_num
|
# sell_real_num
|
||||||
)
|
# )
|
||||||
conditions.append(condition)
|
# conditions.append(condition)
|
||||||
|
#
|
||||||
|
#
|
||||||
print(f"SELL indicators tested \n"
|
# print(f"SELL indicators tested \n"
|
||||||
f"{self.sell_indicator0.value} {self.sell_crossed_indicator0.value} {self.sell_operator0.value} {self.sell_real_num0.value} \n"
|
# f"{self.sell_indicator0.value} {self.sell_crossed_indicator0.value} {self.sell_operator0.value} {self.sell_real_num0.value} \n"
|
||||||
f"{self.sell_indicator1.value} {self.sell_crossed_indicator1.value} {self.sell_operator1.value} {self.sell_real_num1.value} \n"
|
# f"{self.sell_indicator1.value} {self.sell_crossed_indicator1.value} {self.sell_operator1.value} {self.sell_real_num1.value} \n"
|
||||||
f"{self.sell_indicator2.value} {self.sell_crossed_indicator2.value} {self.sell_operator2.value} {self.sell_real_num2.value} \n"
|
# f"{self.sell_indicator2.value} {self.sell_crossed_indicator2.value} {self.sell_operator2.value} {self.sell_real_num2.value} \n"
|
||||||
)
|
# )
|
||||||
|
#
|
||||||
|
#
|
||||||
if conditions:
|
# if conditions:
|
||||||
dataframe.loc[reduce(lambda x, y: x & y, conditions), ['exit_long', 'exit_tag']] = (1, 'god')
|
# dataframe.loc[reduce(lambda x, y: x & y, conditions), ['exit_long', 'exit_tag']] = (1, 'god')
|
||||||
return dataframe
|
return dataframe
|
||||||
|
|
||||||
def calculeDerivees(
|
def calculeDerivees(
|
||||||
@@ -816,4 +936,50 @@ class Empty(IStrategy):
|
|||||||
(momentum_short < momentum_long)
|
(momentum_short < momentum_long)
|
||||||
)
|
)
|
||||||
|
|
||||||
return dataframe
|
return dataframe
|
||||||
|
|
||||||
|
@property
|
||||||
|
def protections(self):
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"method": "CooldownPeriod",
|
||||||
|
"stop_duration_candles": 12
|
||||||
|
},
|
||||||
|
# {
|
||||||
|
# "method": "MaxDrawdown",
|
||||||
|
# "lookback_period_candles": 96,
|
||||||
|
# "trade_limit": 4,
|
||||||
|
# "max_allowed_drawdown": 0.1,
|
||||||
|
# "stop_duration_candles": 24
|
||||||
|
# },
|
||||||
|
# {
|
||||||
|
# "method": "StoplossGuard",
|
||||||
|
# "lookback_period_candles": 96,
|
||||||
|
# "trade_limit": 2,
|
||||||
|
# "stop_duration_candles": 48,
|
||||||
|
# "only_per_pair": False
|
||||||
|
# },
|
||||||
|
# {
|
||||||
|
# "method": "LowProfitPairs",
|
||||||
|
# "lookback_period_candles": 6,
|
||||||
|
# "trade_limit": 2,
|
||||||
|
# "stop_duration_candles": 60,
|
||||||
|
# "required_profit": 0.02
|
||||||
|
# },
|
||||||
|
# {
|
||||||
|
# "method": "LowProfitPairs",
|
||||||
|
# "lookback_period_candles": 24,
|
||||||
|
# "trade_limit": 4,
|
||||||
|
# "stop_duration_candles": 2,
|
||||||
|
# "required_profit": 0.01
|
||||||
|
# }
|
||||||
|
]
|
||||||
|
|
||||||
|
def printLog(self, str):
|
||||||
|
if self.config.get('runmode') == 'hyperopt' or self.dp.runmode.value in ('hyperopt'):
|
||||||
|
return;
|
||||||
|
if not self.dp.runmode.value in ('backtest', 'hyperopt', 'lookahead-analysis'):
|
||||||
|
logger.info(str)
|
||||||
|
else:
|
||||||
|
if not self.dp.runmode.value in ('hyperopt'):
|
||||||
|
print(str)
|
||||||
126
Empty.txt
Normal file
126
Empty.txt
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
|
||||||
|
BACKTESTING REPORT
|
||||||
|
┏━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||||
|
┃ Pair ┃ Trades ┃ Avg Profit % ┃ Tot Profit USDC ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃
|
||||||
|
┡━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━┩
|
||||||
|
│ BTC/USDC │ 45 │ 3.05 │ 903.533 │ 90.35 │ 15 days, 1:43:00 │ 44 0 1 97.8 │
|
||||||
|
│ TOTAL │ 45 │ 3.05 │ 903.533 │ 90.35 │ 15 days, 1:43:00 │ 44 0 1 97.8 │
|
||||||
|
└──────────┴────────┴──────────────┴─────────────────┴──────────────┴──────────────────┴────────────────────────┘
|
||||||
|
LEFT OPEN TRADES REPORT
|
||||||
|
┏━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||||
|
┃ Pair ┃ Trades ┃ Avg Profit % ┃ Tot Profit USDC ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃
|
||||||
|
┡━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━┩
|
||||||
|
│ BTC/USDC │ 1 │ -40.58 │ -1300.800 │ -130.08 │ 129 days, 4:00:00 │ 0 0 1 0 │
|
||||||
|
│ TOTAL │ 1 │ -40.58 │ -1300.800 │ -130.08 │ 129 days, 4:00:00 │ 0 0 1 0 │
|
||||||
|
└──────────┴────────┴──────────────┴─────────────────┴──────────────┴───────────────────┴────────────────────────┘
|
||||||
|
ENTER TAG STATS
|
||||||
|
┏━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||||
|
┃ Enter Tag ┃ Entries ┃ Avg Profit % ┃ Tot Profit USDC ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃
|
||||||
|
┡━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━┩
|
||||||
|
│ god │ 45 │ 3.05 │ 903.533 │ 90.35 │ 15 days, 1:43:00 │ 44 0 1 97.8 │
|
||||||
|
│ TOTAL │ 45 │ 3.05 │ 903.533 │ 90.35 │ 15 days, 1:43:00 │ 44 0 1 97.8 │
|
||||||
|
└───────────┴─────────┴──────────────┴─────────────────┴──────────────┴──────────────────┴────────────────────────┘
|
||||||
|
EXIT REASON STATS
|
||||||
|
┏━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||||
|
┃ Exit Reason ┃ Exits ┃ Avg Profit % ┃ Tot Profit USDC ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃
|
||||||
|
┡━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━┩
|
||||||
|
│ momentum_drop │ 44 │ 4.04 │ 2204.333 │ 220.43 │ 12 days, 11:29:00 │ 44 0 0 100 │
|
||||||
|
│ force_exit │ 1 │ -40.58 │ -1300.800 │ -130.08 │ 129 days, 4:00:00 │ 0 0 1 0 │
|
||||||
|
│ TOTAL │ 45 │ 3.05 │ 903.533 │ 90.35 │ 15 days, 1:43:00 │ 44 0 1 97.8 │
|
||||||
|
└───────────────┴───────┴──────────────┴─────────────────┴──────────────┴───────────────────┴────────────────────────┘
|
||||||
|
MIXED TAG STATS
|
||||||
|
┏━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||||
|
┃ Enter Tag ┃ Exit Reason ┃ Trades ┃ Avg Profit % ┃ Tot Profit USDC ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃
|
||||||
|
┡━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━┩
|
||||||
|
│ god │ momentum_drop │ 44 │ 4.04 │ 2204.333 │ 220.43 │ 12 days, 11:29:00 │ 44 0 0 100 │
|
||||||
|
│ god │ force_exit │ 1 │ -40.58 │ -1300.800 │ -130.08 │ 129 days, 4:00:00 │ 0 0 1 0 │
|
||||||
|
│ TOTAL │ │ 45 │ 3.05 │ 903.533 │ 90.35 │ 15 days, 1:43:00 │ 44 0 1 97.8 │
|
||||||
|
└───────────┴───────────────┴────────┴──────────────┴─────────────────┴──────────────┴───────────────────┴────────────────────────┘
|
||||||
|
MONTH BREAKDOWN
|
||||||
|
┏━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||||
|
┃ Month ┃ Trades ┃ Tot Profit USDC ┃ Profit Factor ┃ Win Draw Loss Win% ┃
|
||||||
|
┡━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━┩
|
||||||
|
│ 31/01/2024 │ 3 │ 101.297 │ 0.0 │ 3 0 0 100 │
|
||||||
|
│ 29/02/2024 │ 5 │ 123.805 │ 0.0 │ 5 0 0 100 │
|
||||||
|
│ 31/03/2024 │ 4 │ 119.449 │ 0.0 │ 4 0 0 100 │
|
||||||
|
│ 30/04/2024 │ 2 │ 48.352 │ 0.0 │ 2 0 0 100 │
|
||||||
|
│ 31/05/2024 │ 3 │ 102.779 │ 0.0 │ 3 0 0 100 │
|
||||||
|
│ 30/06/2024 │ 1 │ 40.932 │ 0.0 │ 1 0 0 100 │
|
||||||
|
│ 31/07/2024 │ 4 │ 163.017 │ 0.0 │ 4 0 0 100 │
|
||||||
|
│ 31/08/2024 │ 0 │ 0 │ 0.0 │ 0 0 0 0 │
|
||||||
|
│ 30/09/2024 │ 0 │ 0 │ 0.0 │ 0 0 0 0 │
|
||||||
|
│ 31/10/2024 │ 2 │ 84.227 │ 0.0 │ 2 0 0 100 │
|
||||||
|
│ 30/11/2024 │ 5 │ 301.166 │ 0.0 │ 5 0 0 100 │
|
||||||
|
│ 31/12/2024 │ 1 │ 96.283 │ 0.0 │ 1 0 0 100 │
|
||||||
|
│ 31/01/2025 │ 3 │ 160.102 │ 0.0 │ 3 0 0 100 │
|
||||||
|
│ 28/02/2025 │ 0 │ 0 │ 0.0 │ 0 0 0 0 │
|
||||||
|
│ 31/03/2025 │ 1 │ 62.274 │ 0.0 │ 1 0 0 100 │
|
||||||
|
│ 30/04/2025 │ 1 │ 192.722 │ 0.0 │ 1 0 0 100 │
|
||||||
|
│ 31/05/2025 │ 3 │ 138.491 │ 0.0 │ 3 0 0 100 │
|
||||||
|
│ 30/06/2025 │ 0 │ 0 │ 0.0 │ 0 0 0 0 │
|
||||||
|
│ 31/07/2025 │ 2 │ 210.269 │ 0.0 │ 2 0 0 100 │
|
||||||
|
│ 31/08/2025 │ 2 │ 72.441 │ 0.0 │ 2 0 0 100 │
|
||||||
|
│ 30/09/2025 │ 1 │ 61.018 │ 0.0 │ 1 0 0 100 │
|
||||||
|
│ 31/10/2025 │ 1 │ 125.709 │ 0.0 │ 1 0 0 100 │
|
||||||
|
│ 30/11/2025 │ 0 │ 0 │ 0.0 │ 0 0 0 0 │
|
||||||
|
│ 31/12/2025 │ 0 │ 0 │ 0.0 │ 0 0 0 0 │
|
||||||
|
│ 31/01/2026 │ 0 │ 0 │ 0.0 │ 0 0 0 0 │
|
||||||
|
│ 28/02/2026 │ 1 │ -1300.8 │ 0.0 │ 0 0 1 0 │
|
||||||
|
└────────────┴────────┴─────────────────┴───────────────┴────────────────────────┘
|
||||||
|
SUMMARY METRICS
|
||||||
|
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
||||||
|
┃ Metric ┃ Value ┃
|
||||||
|
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
|
||||||
|
│ Backtesting from │ 2024-01-01 00:00:00 │
|
||||||
|
│ Backtesting to │ 2026-02-10 00:00:00 │
|
||||||
|
│ Trading Mode │ Spot │
|
||||||
|
│ Max open trades │ 1 │
|
||||||
|
│ │ │
|
||||||
|
│ Total/Daily Avg Trades │ 45 / 0.06 │
|
||||||
|
│ Starting balance │ 1000 USDC │
|
||||||
|
│ Final balance │ 1903.533 USDC │
|
||||||
|
│ Absolute profit │ 903.533 USDC │
|
||||||
|
│ Total profit % │ 90.35% │
|
||||||
|
│ CAGR % │ 35.63% │
|
||||||
|
│ Sortino │ -100.00 │
|
||||||
|
│ Sharpe │ 0.11 │
|
||||||
|
│ Calmar │ 5.52 │
|
||||||
|
│ SQN │ 0.66 │
|
||||||
|
│ Profit factor │ 1.69 │
|
||||||
|
│ Expectancy (Ratio) │ 20.08 (0.02) │
|
||||||
|
│ Avg. daily profit │ 1.172 USDC │
|
||||||
|
│ Avg. stake amount │ 1314.959 USDC │
|
||||||
|
│ Total trade volume │ 119488.585 USDC │
|
||||||
|
│ │ │
|
||||||
|
│ Best Pair │ BTC/USDC 90.35% │
|
||||||
|
│ Worst Pair │ BTC/USDC 90.35% │
|
||||||
|
│ Best trade │ BTC/USDC 9.64% │
|
||||||
|
│ Worst trade │ BTC/USDC -40.58% │
|
||||||
|
│ Best day │ 192.722 USDC │
|
||||||
|
│ Worst day │ -1300.8 USDC │
|
||||||
|
│ Days win/draw/lose │ 44 / 726 / 1 │
|
||||||
|
│ Min/Max/Avg. Duration Winners │ 0d 16:00 / 85d 14:00 / 12d 11:29 │
|
||||||
|
│ Min/Max/Avg. Duration Losers │ 129d 04:00 / 129d 04:00 / 129d 04:00 │
|
||||||
|
│ Max Consecutive Wins / Loss │ 44 / 1 │
|
||||||
|
│ Rejected Entry signals │ 0 │
|
||||||
|
│ Entry/Exit Timeouts │ 0 / 0 │
|
||||||
|
│ │ │
|
||||||
|
│ Min balance │ 1029.974 USDC │
|
||||||
|
│ Max balance │ 3204.333 USDC │
|
||||||
|
│ Max % of account underwater │ 40.60% │
|
||||||
|
│ Absolute drawdown │ 1300.8 USDC (40.60%) │
|
||||||
|
│ Drawdown duration │ 129 days 23:00:00 │
|
||||||
|
│ Profit at drawdown start │ 2204.333 USDC │
|
||||||
|
│ Profit at drawdown end │ 903.533 USDC │
|
||||||
|
│ Drawdown start │ 2025-10-03 01:00:00 │
|
||||||
|
│ Drawdown end │ 2026-02-10 00:00:00 │
|
||||||
|
│ Market change │ 64.37% │
|
||||||
|
└───────────────────────────────┴──────────────────────────────────────┘
|
||||||
|
|
||||||
|
Backtested 2024-01-01 00:00:00 -> 2026-02-10 00:00:00 | Max open trades : 1
|
||||||
|
STRATEGY SUMMARY
|
||||||
|
┏━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━┓
|
||||||
|
┃ Strategy ┃ Trades ┃ Avg Profit % ┃ Tot Profit USDC ┃ Tot Profit % ┃ Avg Duration ┃ Win Draw Loss Win% ┃ Drawdown ┃
|
||||||
|
┡━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━┩
|
||||||
|
│ Empty │ 45 │ 3.05 │ 903.533 │ 90.35 │ 15 days, 1:43:00 │ 44 0 1 97.8 │ 1300.8 USDC 40.60% │
|
||||||
|
└──────────┴────────┴──────────────┴─────────────────┴──────────────┴──────────────────┴────────────────────────┴─────────────────────┘
|
||||||
Reference in New Issue
Block a user