self.unset_unsupported()
@staticmethod
- def is_short_allowed(trading_mode: str) -> bool:
+ def _is_short_allowed(trading_mode: str) -> bool:
if trading_mode in {"margin", "futures"}:
return True
elif trading_mode == "spot":
position: Positions,
force_action: Optional[ForceActions] = None,
) -> NDArray[np.bool_]:
- is_short_allowed = ReforceXY.is_short_allowed(trading_mode)
+ is_short_allowed = ReforceXY._is_short_allowed(trading_mode)
position = ReforceXY._normalize_position(position)
action_masks = np.zeros(len(Actions), dtype=np.bool_)
del fb[0 : len(fb) - frame_stacking]
if len(fb) < frame_stacking:
pad_count = frame_stacking - len(fb)
- pad_frame = fb[0] if fb else np_observation
+ pad_frame = np.zeros_like(np_observation, dtype=np.float32)
fb_padded = [pad_frame] * pad_count + fb
else:
fb_padded = fb
stacked_observations = np.concatenate(fb_padded, axis=1)
- observations = stacked_observations.reshape(1, -1)
+ observations = stacked_observations.reshape(
+ 1, stacked_observations.shape[0], stacked_observations.shape[1]
+ )
else:
- observations = np_observation.reshape(1, -1)
+ observations = np_observation.reshape(
+ 1, np_observation.shape[0], np_observation.shape[1]
+ )
if self.action_masking and self.inference_masking:
action_masks_param["action_masks"] = ReforceXY.get_action_masks(
),
}
)
+ self._candle_duration_secs = int(
+ timeframe_to_minutes(self.config.get("timeframe")) * 60
+ )
+ self.last_candle_start_secs: dict[str, Optional[int]] = {
+ pair: None for pair in self.pairs
+ }
process_throttle_secs = self.config.get("internals", {}).get(
"process_throttle_secs", 5
)
- self._throttle_modulo = max(
- 1,
- int(
- round(
- (timeframe_to_minutes(self.config.get("timeframe")) * 60)
- / process_throttle_secs
- )
- ),
- )
self._max_history_size = int(12 * 60 * 60 / process_throttle_secs)
self._pnl_momentum_window_size = int(30 * 60 / process_throttle_secs)
self._exit_thresholds_calibration: dict[str, float] = {
current_time: datetime.datetime,
callback: Callable[[], None],
) -> None:
- if hash(pair + str(current_time)) % self._throttle_modulo == 0:
+ timestamp = int(current_time.timestamp())
+ candle_start_secs = timestamp - (timestamp % self._candle_duration_secs)
+ if candle_start_secs != self.last_candle_start_secs.get(pair):
+ self.last_candle_start_secs[pair] = candle_start_secs
try:
callback()
except Exception as e:
side: str,
order: Literal["entry", "exit"],
rate: float,
- min_natr_ratio_percent: float = 0.005,
+ min_natr_ratio_percent: float = 0.0075,
max_natr_ratio_percent: float = 0.025,
lookback_period: int = 1,
decay_ratio: float = 0.5,
side: str,
**kwargs,
) -> bool:
+ if side not in {"long", "short"}:
+ return False
+ if side == "short" and not self.can_short:
+ logger.info(f"User denied short entry for {pair}: shorting not allowed")
+ return False
if Trade.get_open_trade_count() >= self.config.get("max_open_trades"):
return False
max_open_trades_per_side = self.max_open_trades_per_side
set -eu
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
-FREQTRADE_CONFIG="${SCRIPT_DIR}/user_data/config.json"
-LOCAL_DOCKER_IMAGE="reforcexy-freqtrade"
-REMOTE_DOCKER_IMAGE="freqtradeorg/freqtrade:stable_freqairl"
+FREQTRADE_CONFIG="${FREQTRADE_CONFIG:-$SCRIPT_DIR}/user_data/config.json}"
+LOCAL_DOCKER_IMAGE="${LOCAL_DOCKER_IMAGE:-reforcexy-freqtrade}"
+REMOTE_DOCKER_IMAGE="${REMOTE_DOCKER_IMAGE:-freqtradeorg/freqtrade:stable_freqairl}"
################################
fi
if [ ! -f "$FREQTRADE_CONFIG" ]; then
- echo_timestamped "Error: ${FREQTRADE_CONFIG} file not found from current directory"
+ echo_timestamped "Error: ${FREQTRADE_CONFIG} file not found"
exit 1
fi
echo_timestamped "Info: $message"
send_telegram_message "$message"
cd -- "$SCRIPT_DIR" || exit 1
+ if ! command docker compose pull --quiet >/dev/null 2>&1; then
+ echo_timestamped "Warning: docker compose pull failed"
+ fi
if ! command docker compose --progress quiet down; then
echo_timestamped "Error: docker compose down failed"
exit 1