Stochastic Oscillator + Commodity Channel Index (CCI) momentum strategy.
Timeframe
1h
Direction
Long Only
Stoploss
-9900.0%
Trailing Stop
No
ROI
N/A
Interface Version
3
Startup Candles
N/A
Indicators
3
freqtrade/freqtrade-strategies
this is an example class, implementing a PSAR based trailing stop loss you are supposed to take the `custom_stoploss()` and `populate_indicators()` parts and adapt it to your own strategy
freqtrade/freqtrade-strategies
freqtrade/freqtrade-strategies
Strategy 003 author@: Gerald Lonlas github@: https://github.com/freqtrade/freqtrade-strategies
from datetime import datetime
import talib.abstract as ta
from freqtrade.strategy import (
IntParameter,
IStrategy,
)
from pandas import DataFrame
from technical import qtpylib
class StochasticCciStrategy(IStrategy):
"""
Stochastic Oscillator + Commodity Channel Index (CCI) momentum strategy.
Combines the Stochastic Oscillator (Lane, 1984) with the CCI (Lambert, 1980)
for dual momentum confirmation. Enters when both indicators signal oversold
conditions in an uptrend; exits on overbought or trend reversal.
"""
INTERFACE_VERSION = 3
timeframe = "1h"
can_short: bool = False
minimal_roi = {}
stoploss = -99
trailing_stop = False
process_only_new_candles = True
use_exit_signal = True
exit_profit_only = False
ignore_roi_if_entry_signal = False
startup_candle_count: int = 30
# Strategy parameters
stoch_k_period = IntParameter(10, 20, default=14, space="buy")
stoch_d_period = IntParameter(3, 5, default=3, space="buy")
stoch_slowing = IntParameter(3, 5, default=3, space="buy")
cci_period = IntParameter(14, 30, default=20, space="buy")
sma_period = IntParameter(150, 250, default=200, space="buy")
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
stoch = ta.STOCH(
dataframe,
fastk_period=self.stoch_k_period.value,
slowk_period=self.stoch_d_period.value,
slowd_period=self.stoch_slowing.value,
)
dataframe["slowk"] = stoch["slowk"]
dataframe["slowd"] = stoch["slowd"]
dataframe["cci"] = ta.CCI(dataframe, timeperiod=self.cci_period.value)
dataframe["sma200"] = ta.SMA(dataframe, timeperiod=self.sma_period.value)
dataframe["mean-volume"] = dataframe["volume"].rolling(20).mean()
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[
(
# Stochastic %K crosses above %D in oversold zone
(qtpylib.crossed_above(dataframe["slowk"], dataframe["slowd"]))
& (dataframe["slowk"] < 30)
# CCI confirms oversold bounce
& (dataframe["cci"] > -100)
& (dataframe["cci"].shift(1) <= -100)
# Price above long-term SMA (uptrend filter)
& (dataframe["close"] > dataframe["sma200"])
& (dataframe["mean-volume"] > 0.75)
),
"enter_long",
] = 1
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[
(
# Stochastic overbought
(
(dataframe["slowk"] > 80)
& (qtpylib.crossed_below(dataframe["slowk"], dataframe["slowd"]))
)
# OR CCI overbought reversal
| (
(dataframe["cci"] < 100)
& (dataframe["cci"].shift(1) >= 100)
)
),
"exit_long",
] = 1
return dataframe
def confirm_trade_entry(
self, pair: str, order_type: str, amount: float, rate: float,
time_in_force: str, current_time: datetime, entry_tag: str | None,
side: str, **kwargs,
) -> bool:
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
last_close = dataframe.iloc[-1]["close"]
max_deviation = 0.01
deviation = abs(rate - last_close) / last_close
if deviation > max_deviation:
return False
return True