Timeframe
1m
Direction
Long Only
Stoploss
-99.0%
Trailing Stop
No
ROI
0m: 10.0%
Interface Version
N/A
Startup Candles
50
Indicators
0
freqtrade/freqtrade-strategies
freqtrade/freqtrade-strategies
this strategy is based around the idea of generating a lot of potentatils buys and make tiny profits on each trade
freqtrade/freqtrade-strategies
this strategy is based around the idea of generating a lot of potentatils buys and make tiny profits on each trade
from __future__ import annotations
import json
from datetime import datetime, timezone
from pathlib import Path
from typing import Optional
import pandas as pd
from freqtrade.persistence import Trade
from freqtrade.strategy import IStrategy
class AIStrategy(IStrategy):
timeframe = "1m"
process_only_new_candles = True
startup_candle_count = 50
use_exit_signal = True
exit_profit_only = False
ignore_roi_if_entry_signal = False
# ROI is managed mostly via custom_exit + AI targets.
minimal_roi = {"0": 0.10}
stoploss = -0.99
can_short = False
base_dir = Path(__file__).resolve().parents[1]
ai_strategy_file = base_dir / "ai_strategy.json"
signals_file = base_dir / "signals.json"
def _load_json(self, path: Path, default):
if not path.exists():
return default
try:
with path.open("r", encoding="utf-8") as f:
return json.load(f)
except Exception:
return default
def _load_ai_cfg(self) -> dict:
default = {
"pairs": ["DOGE/USD"],
"timeframe": "1m",
"target_profit": 0.01,
"stop_loss": 0.01,
"confidence_threshold": 0.7,
"max_daily_loss": 0.01,
"max_daily_profit": 0.05,
}
return self._load_json(self.ai_strategy_file, default)
def _load_signals(self) -> dict:
return self._load_json(self.signals_file, {"signals": []})
def informative_pairs(self):
cfg = self._load_ai_cfg()
pairs = [p for p in cfg.get("pairs", []) if isinstance(p, str)]
return [(p, self.timeframe) for p in pairs]
def _signal_for_pair(self, pair: str) -> Optional[dict]:
pair_dash = pair.replace("/", "-")
data = self._load_signals()
for sig in data.get("signals", []):
if sig.get("coin") == pair_dash:
return sig
return None
def _ai_pair_allowed(self, pair: str) -> bool:
cfg = self._load_ai_cfg()
return pair in cfg.get("pairs", [])
def _daily_pnl_ratio(self) -> float:
"""
Sum realized close_profit (ratio) for current UTC day.
Example: two closed trades at +1% and -0.5% => +0.5% day ratio (0.005).
"""
today = datetime.now(timezone.utc).date()
closed = Trade.get_trades_proxy(is_open=False)
profits: list[float] = []
for trade in closed:
if trade.close_date_utc and trade.close_date_utc.date() == today:
profits.append(float(trade.close_profit or 0.0))
return float(sum(profits))
def _risk_limits_reached(self) -> bool:
cfg = self._load_ai_cfg()
pnl = self._daily_pnl_ratio()
max_loss = -abs(float(cfg.get("max_daily_loss", 0.01)))
max_profit = abs(float(cfg.get("max_daily_profit", 0.05)))
return pnl <= max_loss or pnl >= max_profit
def _threshold(self) -> float:
cfg = self._load_ai_cfg()
return float(cfg.get("confidence_threshold", 0.7))
def _target_profit_ratio(self, signal: Optional[dict]) -> float:
cfg = self._load_ai_cfg()
if signal and signal.get("take_profit_ratio") is not None:
try:
return abs(float(signal["take_profit_ratio"]))
except Exception:
pass
return abs(float(cfg.get("target_profit", 0.01)))
def _stop_loss_ratio(self, signal: Optional[dict]) -> float:
cfg = self._load_ai_cfg()
if signal and signal.get("stop_loss_ratio") is not None:
try:
return abs(float(signal["stop_loss_ratio"]))
except Exception:
pass
return abs(float(cfg.get("stop_loss", 0.01)))
def populate_indicators(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
return dataframe
def populate_entry_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
dataframe["enter_long"] = 0
pair = metadata["pair"]
if not self._ai_pair_allowed(pair):
return dataframe
if self._risk_limits_reached():
return dataframe
sig = self._signal_for_pair(pair)
threshold = self._threshold()
if sig and sig.get("action") == "BUY" and float(sig.get("confidence", 0.0)) >= threshold:
dataframe.loc[dataframe.index[-1], "enter_long"] = 1
return dataframe
def populate_exit_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
dataframe["exit_long"] = 0
pair = metadata["pair"]
sig = self._signal_for_pair(pair)
if sig and sig.get("action") == "SELL":
dataframe.loc[dataframe.index[-1], "exit_long"] = 1
return dataframe
def custom_stoploss(
self,
pair: str,
trade: Trade,
current_time: datetime,
current_rate: float,
current_profit: float,
after_fill: bool = False,
**kwargs,
) -> float:
sig = self._signal_for_pair(pair)
stop_ratio = self._stop_loss_ratio(sig)
# Freqtrade expects negative ratio.
return -abs(stop_ratio)
def custom_exit(
self,
pair: str,
trade: Trade,
current_time: datetime,
current_rate: float,
current_profit: float,
**kwargs,
):
sig = self._signal_for_pair(pair)
if self._risk_limits_reached():
return "daily_limit_reached"
if sig and sig.get("action") == "SELL":
return "ai_sell_signal"
target = self._target_profit_ratio(sig)
if current_profit >= target:
return "ai_take_profit"
return None
def custom_stake_amount(
self,
pair: str,
current_time: datetime,
current_rate: float,
proposed_stake: float,
min_stake: Optional[float],
max_stake: float,
leverage: float,
entry_tag: Optional[str],
side: str,
**kwargs,
) -> float:
if not self._ai_pair_allowed(pair):
return 0.0
if self._risk_limits_reached():
return 0.0
sig = self._signal_for_pair(pair)
if not sig:
return 0.0
if sig.get("action") != "BUY":
return 0.0
if float(sig.get("confidence", 0.0)) < self._threshold():
return 0.0
stake = min(proposed_stake, max_stake)
if min_stake is not None:
stake = max(stake, min_stake)
return stake