Releases: eq19/maps
v0.3.2
Full Changelog: v0.3.1...v0.3.2
Download action repository 'eq19/maps@v8' (SHA:05385eba14057c52cba8f4b6e357eefb43a6d1af)
Download action repository 'eq19/feed@v6' (SHA:2740ae59b6a76c3799523cd21df4db5e5169024f)
Download action repository 'eq19/lexer@v4' (SHA:5635fea849281dc1dc5ab591881d7171381b1427)
Download action repository 'eq19/eq19@v2' (SHA:e2b2107712ebc0f1d47156488157bf64abeb9566)
v0.3.1
Full Changelog: v0.2.20...v0.3.1
FreqAI
class Fibbo(Istrategy):
def feature_engineering_expand_all(
self, dataframe: DataFrame, period: int, metadata: dict, **kwargs
) -> DataFrame:
"""
*Only functional with FreqAI enabled strategies*
This function will automatically expand the defined features on the config defined
`indicator_periods_candles`, `include_timeframes`, `include_shifted_candles`, and
`include_corr_pairs`. In other words, a single feature defined in this function
will automatically expand to a total of
`indicator_periods_candles` * `include_timeframes` * `include_shifted_candles` *
`include_corr_pairs` numbers of features added to the model.
All features must be prepended with `%` to be recognized by FreqAI internals.
More details on how these config defined parameters accelerate feature engineering
in the documentation at:
https://www.freqtrade.io/en/latest/freqai-parameter-table/#feature-parameters
https://www.freqtrade.io/en/latest/freqai-feature-engineering/#defining-the-features
:param dataframe: strategy dataframe which will receive the features
:param period: period of the indicator - usage example:
:param metadata: metadata of current pair
dataframe["%-ema-period"] = ta.EMA(dataframe, timeperiod=period)
"""
dataframe["%-rsi-period"] = ta.RSI(dataframe, timeperiod=period)
dataframe["%-mfi-period"] = ta.MFI(dataframe, timeperiod=period)
dataframe["%-adx-period"] = ta.ADX(dataframe, timeperiod=period)
dataframe["%-sma-period"] = ta.SMA(dataframe, timeperiod=period)
dataframe["%-ema-period"] = ta.EMA(dataframe, timeperiod=period)
bollinger = qtpylib.bollinger_bands(
qtpylib.typical_price(dataframe), window=period, stds=2.2
)
dataframe["bb_lowerband-period"] = bollinger["lower"]
dataframe["bb_middleband-period"] = bollinger["mid"]
dataframe["bb_upperband-period"] = bollinger["upper"]
dataframe["%-bb_width-period"] = (
dataframe["bb_upperband-period"] - dataframe["bb_lowerband-period"]
) / dataframe["bb_middleband-period"]
dataframe["%-close-bb_lower-period"] = dataframe["close"] / dataframe["bb_lowerband-period"]
dataframe["%-roc-period"] = ta.ROC(dataframe, timeperiod=period)
dataframe["%-relative_volume-period"] = (
dataframe["volume"] / dataframe["volume"].rolling(period).mean()
)
return dataframe
def set_freqai_targets(
self,
dataframe: DataFrame,
metadata: dict,
**kwargs
) -> DataFrame:
"""
FreqAI target definition for:
- Classifier
- ClassifierMultiTarget
- Regressor
- RegressorMultiTarget
"""
model_name = self.model_name.lower()
is_classifier = "classifier" in model_name
is_multi_target = "multitarget" in model_name
label_period = self.freqai_info["feature_parameters"]["label_period_candles"]
if is_classifier:
# ==================================================
# CLASSIFIERS
# ==================================================
if is_multi_target:
# CatboostClassifierMultiTarget
# IMPORTANT:
# - class labels must be UNIQUE across targets
# - target 1 uses {0, 1}
# - target 2 uses {2, 3}
self.freqai.class_names = [0, 1, 2, 3]
# Target 1: direction (0 = down, 1 = up)
dataframe["&s-up_or_down"] = (
dataframe["close"].shift(-label_period) > dataframe["close"]
).astype(int)
# Target 2: volatility (2 = low, 3 = high)
dataframe["&s-volatility"] = (
(
dataframe["close"].rolling(label_period).std()
> dataframe["close"].rolling(label_period).std().median()
).astype(int)
+ 2
)
else:
# CatboostClassifier (single target)
self.freqai.class_names = [0, 1]
dataframe["&s-up_or_down"] = (
dataframe["close"].shift(-label_period) > dataframe["close"]
).astype(int)
else:
# ==================================================
# REGRESSORS
# ==================================================
if is_multi_target:
# CatboostRegressorMultiTarget
dataframe["&-s_close"] = (
dataframe["close"]
.shift(-label_period)
.rolling(label_period)
.mean()
/ dataframe["close"]
- 1
)
dataframe["&-s_range"] = (
dataframe["close"]
.shift(-label_period)
.rolling(label_period)
.max()
-
dataframe["close"]
.shift(-label_period)
.rolling(label_period)
.min()
)
else:
# CatboostRegressor
dataframe["&-s_close"] = (
dataframe["close"]
.shift(-label_period)
.rolling(label_period)
.mean()
/ dataframe["close"]
- 1
)
return dataframe
Fibbo Indicator
class Fibbo(Istrategy):
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
pair = metadata["pair"]
# --- FreqAI (robust for dynamic pairs) ---
if self.freqai is not None and self.freqai_enabled:
try:
# Start FreqAI
dataframe = self.freqai.start(dataframe, metadata, self)
# Process DI_values if available
if 'DI_values' in dataframe.columns:
# Check if we have enough data for meaningful percentile
if len(dataframe) >= self.di_rolling_window:
dataframe['di_percentile'] = (dataframe['DI_values']
.rolling(self.di_rolling_window)
.rank(pct=True))
logger.debug(f"FreqAI DI_percentile calculated for {pair}")
else:
# Not enough data yet, use neutral value
dataframe['di_percentile'] = 0.5
logger.debug(f"FreqAI: Insufficient data for {pair}, using neutral confidence")
# Log DI_values stats for debugging
logger.debug(f"DI_values - min: {dataframe['DI_values'].min():.3f}, "
f"max: {dataframe['DI_values'].max():.3f}, "
f"mean: {dataframe['DI_values'].mean():.3f}")
# Also log do_predict stats
if 'do_predict' in dataframe.columns:
buy_signals = (dataframe['do_predict'] == 1).sum()
sell_signals = (dataframe['do_predict'] == -1).sum()
logger.debug(f"FreqAI signals for {pair}: {buy_signals} buy, {sell_signals} sell")
except KeyError:
# Pair introduced dynamically without FreqAI history/model
logger.debug(f"FreqAI model not ready for {pair} - skipping AI signals")
except Exception as e:
# Extra safety: never let AI crash the strategy
logger.warning(f"FreqAI error for {pair}: {e}")
else:
if self.freqai is None:
logger.debug("FreqAI not initialized for this strategy")
# --- Classical indicators (always run) ---
# SWING high/low for Fibonacci levels
dataframe['swing_high'] = dataframe['high'].rolling(self.buy_swing_period.value).max()
dataframe['swing_low'] = dataframe['low'].rolling(self.buy_swing_period.value).min()
swing_range = dataframe['swing_high'] - dataframe['swing_low']
# LONG (retracement in uptrend)
dataframe['fib_long_0236'] = dataframe['swing_high'] - swing_range * 0.236
dataframe['fib_long_0382'] = dataframe['swing_high'] - swing_range * 0.382
dataframe['fib_long_0618'] = dataframe['swing_high'] - swing_range * 0.618
dataframe['fib_long_0786'] = dataframe['swing_high'] - swing_range * 0.786
# SHORT (retracement in downtrend)
dataframe['fib_short_0236'] = dataframe['swing_low'] + swing_range * 0.236
dataframe['fib_short_0382'] = dataframe['swing_low'] + swing_range * 0.382
dataframe['fib_short_0618'] = dataframe['swing_low'] + swing_range * 0.618
dataframe['fib_short_0786'] = dataframe['swing_low'] + swing_range * 0.786
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Combine your Fibbo strategy with FreqAI predictions.
FreqAI columns are now available in the dataframe.
"""
logger.debug(f"Generating entry signals for {metadata['pair']}")
entry_long_conditions = []
entry_short_conditions = []
...
v0.2.20
Full Changelog: v0.2.18...v0.2.20
calculate_score
NEW_SCORE=$SCORE
OLD_SCORE=$(gh variable get SCORE)
if (( $(echo "$NEW_SCORE > $OLD_SCORE" | bc -l) )); then
cat $STRATEGY
curl -L -s -X PATCH \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GH_TOKEN" \
-H "X-GitHub-Api-Version: 2022-11-28" \
-d "$(jq -n '{name:"PARAMS_JSON", value:$value}' --arg value "$(cat "$STRATEGY")")" \
https://api.github.com/repos/$( [[ "$GITHUB_JOB" == "lexering" ]] && echo "$TARGET_REPOSITORY" || echo "$GITHUB_REPOSITORY" )/actions/variables/PARAMS_JSON
gh variable set FREQAIMODEL --body "${FREQAI_MODEL}" && gh variable set HYPEROPT --body "${HYPEROPT:-$loss}" && gh variable set SCORE --body "${NEW_SCORE}" && gh variable set JOB --body "${GITHUB_JOB}"
if [[ "$GITHUB_JOB" != "lexering" ]]; then
gh workflow run "main.yml" --raw-field "FREQAI_MODEL=$FREQAI_MODEL" --raw-field "REDUCE_EPOCH=$REDUCE_EPOCH"
else
if [[ "$FREQAI_NEXT" != "false" ]]; then gh workflow run "main.yml" --raw-field "FREQAI_MODEL=$FREQAI_NEXT"; fi
fi
elif (( $(echo "$NEW_SCORE < $OLD_SCORE" | bc -l) )); then
if [[ "$GITHUB_JOB" == "lexering" ]]; then
if [[ "$(gh variable get JOB)" != "lexering" ]]; then
gh workflow run "main.yml" --raw-field "FREQAI_MODEL=$FREQAI_MODEL" --raw-field "REDUCE_EPOCH=$REDUCE_EPOCH"
else
if [[ "$FREQAI_NEXT" != "false" ]]; then gh workflow run "main.yml" --raw-field "FREQAI_MODEL=$FREQAI_NEXT"; fi
fi
fi
# Environment SCORE is unchanged in case calculation is failed
elif (( $(echo "$NEW_SCORE == $OLD_SCORE" | bc -l) )); then
if [[ "$GITHUB_JOB" == "lexering" && "$CALCULATION" != "false" ]]; then
gh workflow run "main.yml" --raw-field "FREQAI_MODEL=$FREQAI_NEXT"
fi
fi

v0.2.19
Full Changelog: v0.2.18...v0.2.19
Download action repository 'eq19/maps@v8' (SHA:c6194cc0e448d1e91083b1856e8689fb098b7eae)
Download action repository 'eq19/feed@v6' (SHA:0c2fc49365e51ae87aa1e8c999927773643ec827)
Download action repository 'eq19/lexer@v4' (SHA:1a7f40bbd717a5e78e8259464f34256f02b547db)
Download action repository 'eq19/eq19@v2' (SHA:af444f788505ab2e38d768875795bce2d3859419)
v0.2.18
Full Changelog: v0.2.17...v0.2.18
class EnhancedCatboostRegressor(BaseFreqAIModel):
"""
Enhanced CatBoost Regressor for FreqAI
CatBoost is a gradient boosting algorithm that handles categorical features
automatically and provides excellent performance on financial time series data.
Advantages:
- Automatic categorical feature handling
- Robust to overfitting
- Good performance on small datasets
- Built-in feature importance
"""
model_type = "tree_based"
default_parameters = {
"iterations": 100,
"learning_rate": 0.05,
"depth": 6,
"l2_leaf_reg": 3,
"random_strength": 1,
"bagging_temperature": 1,
"border_count": 254,
"verbose": False,
"task_type": "CPU",
"early_stopping_rounds": 10,
"eval_metric": "RMSE"
}
def __init__(self, **kwargs):
super().__init__(**kwargs)
if not CATBOOST_AVAILABLE:
raise ImportError("CatBoost is required. Install with: pip install catboost")
self.catboost = cb
def fit(self, X: np.ndarray, y: np.ndarray, **kwargs) -> 'EnhancedCatboostRegressor':
"""Train the CatBoost model with enhanced logging"""
self.validate_data(X, y)
X = self.preprocess_features(X)
# Create CatBoost dataset
train_data = self.catboost.Pool(X, y)
# Initialize model
self.model = self.catboost.CatBoostRegressor(**self.parameters)
# Train model
self.model.fit(train_data, **kwargs)
self.is_trained = True
self.feature_names = [f"feature_{i}" for i in range(X.shape[1])]
# Log training completion
self._setup_logging()
self.logger.info(f"CatBoost model trained with {X.shape[0]} samples, {X.shape[1]} features")
return self
def predict(self, X: np.ndarray) -> np.ndarray:
"""Make predictions"""
if not self.is_trained:
raise ValueError("Model must be trained before making predictions")
X = self.preprocess_features(X)
return self.model.predict(X)
def get_feature_importance(self) -> Optional[np.ndarray]:
"""Get CatBoost feature importance"""
if self.is_trained and hasattr(self.model, 'get_feature_importance'):
return self.model.get_feature_importance()
return None
v0.2.17
Full Changelog: v0.2.16...v0.2.17
{
"freqai": {
"enabled": true,
"identifier": "indodax-1h-24h-v1",
"purge_old_models": 1,
"train_period_days": 21,
"backtest_period_days": 7,
"live_retrain_hours": 24,
"feature_parameters": {
"include_timeframes": ["1h"],
"include_corr_pairlist": [
"BTC/IDR",
"ETH/IDR"
],
"label_period_candles": 24,
"include_shifted_candles": 3,
"indicator_periods_candles": [12, 24],
"DI_threshold": 0.75,
"weight_factor": 0.9,
"principal_component_analysis": false,
"use_SVM_to_remove_outliers": true,
"plot_feature_importances": 0
},
"data_split_parameters": {
"test_size": 0,
"random_state": 42
},
"model_training_parameters": {
"n_estimators": 600,
"learning_rate": 0.05,
"max_depth": 5
}
}
}
v0.2.16
Full Changelog: v0.2.15...v0.2.16
----------------------------------------------------------------------------------
ID: 1 👉 Running CalmarHyperOptLoss | Days: 7 | Epochs: 2500
Spaces: all | FreqAImodel: CatboostClassifierMultiTarget
----------------------------------------------------------------------------------
v0.2.15
Full Changelog: v0.2.14...v0.2.15
def set_freqai_targets(
self,
dataframe: DataFrame,
metadata: dict,
**kwargs
) -> DataFrame:
"""
FreqAI target definition for:
- Classifier
- ClassifierMultiTarget
- Regressor
- RegressorMultiTarget
"""
model_name = self.model_name.lower()
is_classifier = "classifier" in model_name
is_multi_target = "multitarget" in model_name
label_period = self.freqai_info["feature_parameters"]["label_period_candles"]
if is_classifier:
# ==================================================
# CLASSIFIERS
# ==================================================
if is_multi_target:
# CatboostClassifierMultiTarget
# IMPORTANT:
# - class labels must be UNIQUE across targets
# - target 1 uses {0, 1}
# - target 2 uses {2, 3}
self.freqai.class_names = [0, 1, 2, 3]
# Target 1: direction (0 = down, 1 = up)
dataframe["&s-up_or_down"] = (
dataframe["close"].shift(-label_period) > dataframe["close"]
).astype(int)
# Target 2: volatility (2 = low, 3 = high)
dataframe["&s-volatility"] = (
(
dataframe["close"].rolling(label_period).std()
> dataframe["close"].rolling(label_period).std().median()
).astype(int)
+ 2
)
else:
# CatboostClassifier (single target)
self.freqai.class_names = [0, 1]
dataframe["&s-up_or_down"] = (
dataframe["close"].shift(-label_period) > dataframe["close"]
).astype(int)
else:
# ==================================================
# REGRESSORS
# ==================================================
if is_multi_target:
# CatboostRegressorMultiTarget
dataframe["&-s_close"] = (
dataframe["close"]
.shift(-label_period) hi
.rolling(label_period)
.mean()
/ dataframe["close"]
- 1
)
dataframe["&-s_range"] = (
dataframe["close"]
.shift(-label_period)
.rolling(label_period)
.max()
-
dataframe["close"]
.shift(-label_period)
.rolling(label_period)
.min()
)
else:
# CatboostRegressor
dataframe["&-s_close"] = (
dataframe["close"]
.shift(-label_period)
.rolling(label_period)
.mean()
/ dataframe["close"]
- 1
)
return dataframe
v0.2.14
Full Changelog: v0.2.13...v0.2.14
def set_freqai_targets(
self,
dataframe: DataFrame,
metadata: dict,
**kwargs
) -> DataFrame:
"""
FreqAI target definition for:
- Classifier
- ClassifierMultiTarget
- Regressor
- RegressorMultiTarget
"""
model_name = self.model_name.lower()
is_classifier = "classifier" in model_name
is_multi_target = "multitarget" in model_name
label_period = self.freqai_info["feature_parameters"]["label_period_candles"]
if is_classifier:
# ==================================================
# CLASSIFIERS
# ==================================================
self.freqai.class_names = [0, 1]
if is_multi_target:
# ClassifierMultiTarget
dataframe["&s-up_or_down"] = (
dataframe["close"].shift(-label_period) > dataframe["close"]
).astype(int)
dataframe["&s-volatility"] = (
dataframe["close"].rolling(label_period).std()
> dataframe["close"].rolling(label_period).std().median()
).astype(int)
else:
# Classifier
dataframe["&s-up_or_down"] = (
dataframe["close"].shift(-label_period) > dataframe["close"]
).astype(int)
else:
# ==================================================
# REGRESSORS
# ==================================================
if is_multi_target:
# RegressorMultiTarget
dataframe["&-s_close"] = (
dataframe["close"]
.shift(-label_period)
.rolling(label_period)
.mean()
/ dataframe["close"]
- 1
)
dataframe["&-s_range"] = (
dataframe["close"]
.shift(-label_period)
.rolling(label_period)
.max()
-
dataframe["close"]
.shift(-label_period)
.rolling(label_period)
.min()
)
else:
# Regressor
dataframe["&-s_close"] = (
dataframe["close"]
.shift(-label_period)
.rolling(label_period)
.mean()
/ dataframe["close"]
- 1
)
return dataframe
def set_freqai_targets(
self,
dataframe: DataFrame,
metadata: dict,
**kwargs
) -> DataFrame:
"""
FreqAI target definition for:
- Classifier
- ClassifierMultiTarget
- Regressor
- RegressorMultiTarget
"""
model_name = self.model_name.lower()
is_classifier = "classifier" in model_name
is_multi_target = "multitarget" in model_name
label_period = self.freqai_info["feature_parameters"]["label_period_candles"]
if is_classifier:
# ==================================================
# CLASSIFIERS
# ==================================================
self.freqai.class_names = [0, 1]
# Classifier
dataframe["&s-up_or_down"] = (
dataframe["close"].shift(-label_period) > dataframe["close"]
).astype(int)
if is_multi_target:
# ClassifierMultiTarget
dataframe["&s-volatility"] = (
dataframe["close"].rolling(label_period).std()
> dataframe["close"].rolling(label_period).std().median()
).astype(int)
else:
# ==================================================
# REGRESSORS
# ==================================================
# Regressor
dataframe["&-s_close"] = (
dataframe["close"]
.shift(-label_period)
.rolling(label_period)
.mean()
/ dataframe["close"]
- 1
)
if is_multi_target:
# RegressorMultiTarget
dataframe["&-s_range"] = (
dataframe["close"]
.shift(-label_period)
.rolling(label_period)
.max()
-
dataframe["close"]
.shift(-label_period)
.rolling(label_period)
.min()
)
return dataframe
v0.2.13
Full Changelog: v0.2.12...v0.2.13
def set_freqai_targets(self, dataframe: DataFrame, metadata: dict, **kwargs) -> DataFrame:
"""
*Only functional with FreqAI enabled strategies*
Required function to set the targets for the model.
All targets must be prepended with `&` to be recognized by the FreqAI internals.
More details about feature engineering available:
https://www.freqtrade.io/en/latest/freqai-feature-engineering
:param dataframe: strategy dataframe which will receive the targets
:param metadata: metadata of current pair
usage example: dataframe["&-target"] = dataframe["close"].shift(-1) / dataframe["close"]
"""
is_classifier = "classifier" in self.model_name.lower()
is_multi_target = "multitarget" in self.model_name.lower()
# Classifiers are typically set up with strings as targets:
if is_classifier:
self.freqai.class_names = ["down", "up"]
dataframe["&s-up_or_down"] = np.where(
dataframe["close"].shift(-50) > dataframe["close"], "up", "down"
)
else:
dataframe["&-s_close"] = (
dataframe["close"]
.shift(-self.freqai_info["feature_parameters"]["label_period_candles"])
.rolling(self.freqai_info["feature_parameters"]["label_period_candles"])
.mean()
/ dataframe["close"]
- 1
)
# If user wishes to use multiple targets, they can add more by
# appending more columns with '&'. User should keep in mind that multi targets
# requires a multioutput prediction model such as
# freqai/prediction_models/CatboostRegressorMultiTarget.py,
# freqtrade trade --freqaimodel CatboostRegressorMultiTarget
if is_multi_target:
dataframe["&-s_range"] = (
dataframe["close"]
.shift(-self.freqai_info["feature_parameters"]["label_period_candles"])
.rolling(self.freqai_info["feature_parameters"]["label_period_candles"])
.max()
-
dataframe["close"]
.shift(-self.freqai_info["feature_parameters"]["label_period_candles"])
.rolling(self.freqai_info["feature_parameters"]["label_period_candles"])
.min()
)
return dataframe







