Frictrade
This commit is contained in:
57
Frictrade.py
57
Frictrade.py
@@ -41,7 +41,7 @@ RESET = "\033[0m"
|
|||||||
|
|
||||||
|
|
||||||
class Frictrade(IStrategy):
|
class Frictrade(IStrategy):
|
||||||
startup_candle_count = 60 * 24
|
startup_candle_count = 180
|
||||||
|
|
||||||
# ROI table:
|
# ROI table:
|
||||||
minimal_roi = {
|
minimal_roi = {
|
||||||
@@ -103,6 +103,19 @@ class Frictrade(IStrategy):
|
|||||||
trades = list()
|
trades = list()
|
||||||
max_profit_pairs = {}
|
max_profit_pairs = {}
|
||||||
|
|
||||||
|
btc_ath_history = [
|
||||||
|
{"date": "2011-06-09", "price_usd": 26.15, "note": "pic 2011 (early breakout)"},
|
||||||
|
{"date": "2013-11-29", "price_usd": 1132.00, "note": "bull run fin 2013"},
|
||||||
|
{"date": "2017-12-17", "price_usd": 19783.00, "note": "ATH décembre 2017 (crypto bubble)"},
|
||||||
|
{"date": "2020-12-31", "price_usd": 29001.72, "note": "fin 2020, nouveau record après accumulation)"},
|
||||||
|
{"date": "2021-11-10", "price_usd": 68742.00, "note": "record novembre 2021 (institutional demand)"},
|
||||||
|
{"date": "2024-03-05", "price_usd": 69000.00,
|
||||||
|
"note": "nouveau pic début 2024 (source presse, valeur indicative)"},
|
||||||
|
{"date": "2025-07-11", "price_usd": 118755.00, "note": "pic juillet 2025 (valeur rapportée par la presse)"},
|
||||||
|
{"date": "2025-10-06", "price_usd": 126198.07,
|
||||||
|
"note": "pic oct. 2025 (source agrégée, à vérifier selon l'exchange)"}
|
||||||
|
]
|
||||||
|
|
||||||
def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str,
|
def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str,
|
||||||
current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool:
|
current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool:
|
||||||
|
|
||||||
@@ -292,7 +305,7 @@ class Frictrade(IStrategy):
|
|||||||
if self.columns_logged % 10 == 0:
|
if self.columns_logged % 10 == 0:
|
||||||
self.printLog(
|
self.printLog(
|
||||||
f"| {'Date':<16} | {'Action':<10} |{'Pair':<5}| {'Trade Type':<18} |{'Rate':>8} | {'Dispo':>6} | {'Profit':>8} "
|
f"| {'Date':<16} | {'Action':<10} |{'Pair':<5}| {'Trade Type':<18} |{'Rate':>8} | {'Dispo':>6} | {'Profit':>8} "
|
||||||
f"| {'Pct':>6} | {'max_touch':>11} | {'last_lost':>12} | {'last_max':>7}| {'last_max':>7}|{'Buys':>5}| {'Stake':>5} |"
|
f"| {'Pct':>6} | {'max_touch':>11} | {'last_lost':>12} | {'last_max':>7}| {'last_min':>7}|{'Buys':>5}| {'Stake':>5} |"
|
||||||
f"{'rsi':>6}" #|Distmax|s201d|s5_1d|s5_2d|s51h|s52h|smt1h|smt2h|tdc1d|tdc1h"
|
f"{'rsi':>6}" #|Distmax|s201d|s5_1d|s5_2d|s51h|s52h|smt1h|smt2h|tdc1d|tdc1h"
|
||||||
)
|
)
|
||||||
self.printLineLog()
|
self.printLineLog()
|
||||||
@@ -539,13 +552,18 @@ class Frictrade(IStrategy):
|
|||||||
# return adjusted_stake_amount
|
# return adjusted_stake_amount
|
||||||
|
|
||||||
def adjust_stake_amount(self, pair: str, last_candle: DataFrame):
|
def adjust_stake_amount(self, pair: str, last_candle: DataFrame):
|
||||||
|
|
||||||
|
ath = max(self.pairs[pair]['last_max'], self.get_last_ath_before_candle(last_candle))
|
||||||
|
|
||||||
|
ath_dist = 100 * (ath - last_candle["mid"]) / ath
|
||||||
|
|
||||||
# Calcule max/min 180
|
# Calcule max/min 180
|
||||||
low180 = last_candle["min180"]
|
low180 = last_candle["min180"]
|
||||||
high180 = last_candle["max180"]
|
high180 = last_candle["max180"]
|
||||||
|
|
||||||
mult = 1 - ((last_candle["mid"] - low180) / (high180 - low180))
|
mult = 1 - ((last_candle["mid"] - low180) / (high180 - low180))
|
||||||
|
|
||||||
print(f"low={low180} mid={last_candle['mid']} high={high180} mult={mult}")
|
print(f"low={low180} mid={last_candle['mid']} high={high180} mult={mult} ath={ath} ath_dist={round(ath_dist, 2)}" )
|
||||||
# base_size = montant de base que tu veux utiliser (ex: stake_amount ou autre)
|
# base_size = montant de base que tu veux utiliser (ex: stake_amount ou autre)
|
||||||
base_size = 2 * self.config.get('stake_amount') # exemple fraction du portefeuille; adapte selon ton code
|
base_size = 2 * self.config.get('stake_amount') # exemple fraction du portefeuille; adapte selon ton code
|
||||||
# new stake proportionnel à mult
|
# new stake proportionnel à mult
|
||||||
@@ -612,7 +630,7 @@ class Frictrade(IStrategy):
|
|||||||
# stake_amount = last_amount * current_rate * 0.5
|
# stake_amount = last_amount * current_rate * 0.5
|
||||||
# return stake_amount
|
# return stake_amount
|
||||||
|
|
||||||
condition = last_candle['hapercent'] > 0 and last_candle['sma60_deriv1'] > 0
|
condition = last_candle['hapercent'] > 0 and last_candle['sma24_deriv1'] > 0
|
||||||
limit_buy = 40
|
limit_buy = 40
|
||||||
if decline >= dca_threshold and condition:
|
if decline >= dca_threshold and condition:
|
||||||
try:
|
try:
|
||||||
@@ -760,11 +778,11 @@ class Frictrade(IStrategy):
|
|||||||
if zone == 0:
|
if zone == 0:
|
||||||
current_trailing_stop_positive = self.trailing_stop_positive
|
current_trailing_stop_positive = self.trailing_stop_positive
|
||||||
current_trailing_stop_positive_offset = self.trailing_stop_positive_offset * 2
|
current_trailing_stop_positive_offset = self.trailing_stop_positive_offset * 2
|
||||||
|
if minutes > 1440:
|
||||||
|
current_trailing_only_offset_is_reached = False
|
||||||
|
current_trailing_stop_positive_offset = self.trailing_stop_positive_offset
|
||||||
# if zone == 1:
|
# if zone == 1:
|
||||||
|
|
||||||
|
|
||||||
if last_candle['sma24_deriv1'] > 0: # and last_candle['sma5_deriv1'] > -0.15:
|
|
||||||
return None
|
|
||||||
# ----- 5) Calcul du trailing stop dynamique -----
|
# ----- 5) Calcul du trailing stop dynamique -----
|
||||||
# Exemple : offset=0.321 => stop à +24.8%
|
# Exemple : offset=0.321 => stop à +24.8%
|
||||||
|
|
||||||
@@ -773,7 +791,7 @@ class Frictrade(IStrategy):
|
|||||||
if max_profit:
|
if max_profit:
|
||||||
baisse = (max_profit - profit) / max_profit
|
baisse = (max_profit - profit) / max_profit
|
||||||
|
|
||||||
if minutes % 15 == 0:
|
if minutes % 12 == 0:
|
||||||
self.log_trade(
|
self.log_trade(
|
||||||
last_candle=last_candle,
|
last_candle=last_candle,
|
||||||
date=current_time,
|
date=current_time,
|
||||||
@@ -781,12 +799,15 @@ class Frictrade(IStrategy):
|
|||||||
dispo=dispo,
|
dispo=dispo,
|
||||||
pair=pair,
|
pair=pair,
|
||||||
rate=last_candle['close'],
|
rate=last_candle['close'],
|
||||||
trade_type=f"{round(profit, 2)} {round(max_profit, 2)} {round(trailing_stop,2)}",
|
trade_type=f"{round(profit, 2)} {round(max_profit, 2)} {round(trailing_stop,2)} {minutes}",
|
||||||
profit=round(profit, 2),
|
profit=round(profit, 2),
|
||||||
buys=count_of_buys,
|
buys=count_of_buys,
|
||||||
stake=0
|
stake=0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if last_candle['sma24_deriv1'] > 0 and minutes < 180: # and last_candle['sma5_deriv1'] > -0.15:
|
||||||
|
return None
|
||||||
|
|
||||||
# ----- 4) OFFSET : faut-il attendre de dépasser trailing_stop_positive_offset ? -----
|
# ----- 4) OFFSET : faut-il attendre de dépasser trailing_stop_positive_offset ? -----
|
||||||
if current_trailing_only_offset_is_reached:
|
if current_trailing_only_offset_is_reached:
|
||||||
# Max profit pas atteint ET perte < 2 * current_trailing_stop_positive
|
# Max profit pas atteint ET perte < 2 * current_trailing_stop_positive
|
||||||
@@ -797,7 +818,7 @@ class Frictrade(IStrategy):
|
|||||||
|
|
||||||
# ----- 6) Condition de vente -----
|
# ----- 6) Condition de vente -----
|
||||||
if profit > 0 and profit <= trailing_stop and last_candle['mid'] < last_candle['sma5']:
|
if profit > 0 and profit <= trailing_stop and last_candle['mid'] < last_candle['sma5']:
|
||||||
return f"stop_{count_of_buys}"
|
return f"stop_{count_of_buys}_{self.pairs[pair]['has_gain']}"
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def informative_pairs(self):
|
def informative_pairs(self):
|
||||||
@@ -892,3 +913,19 @@ class Frictrade(IStrategy):
|
|||||||
|
|
||||||
return dataframe
|
return dataframe
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
def to_utc_ts(self, x):
|
||||||
|
return pd.to_datetime(x, utc=True)
|
||||||
|
|
||||||
|
# suppose self.btc_ath_history exists (liste de dict)
|
||||||
|
def get_last_ath_before_candle(self, last_candle):
|
||||||
|
candle_date = self.to_utc_ts(last_candle['date']) # ou to_utc_ts(last_candle.name)
|
||||||
|
best = None
|
||||||
|
for a in self.btc_ath_history: #getattr(self, "btc_ath_history", []):
|
||||||
|
ath_date = self.to_utc_ts(a["date"])
|
||||||
|
if ath_date <= candle_date:
|
||||||
|
if best is None or ath_date > best[0]:
|
||||||
|
best = (ath_date, a["price_usd"])
|
||||||
|
return best[1] if best is not None else None
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user