Skip to content
All strategies

Strategies

Freqtrade 'Low_BB' by berlinguyinca (Thorsten), faithful port: long-only 1m buy when close <= 0.98 x lower Bollinger(20,2), exits ONLY via ROI table {0:90%,1:5%,10:4%,15:50%} and fixed -1.5% stoploss, run per pair on a 6-alt Binance Spot basket

Outcome: Backtesting

LowBbFreqtradeSpotBasket

Freqtrade 'Low_BB' by berlinguyinca (Thorsten), faithful port: long-only 1m buy when close <= 0.98 x lower Bollinger(20,2), exits ONLY via ROI table {0:90%,1:5%,10:4%,15:50%} and fixed -1.5% stoploss, run per pair on a…

Hypothesis

Faithful line-for-line translation of the GPL-3.0 Freqtrade strategy 'Low_BB' by berlinguyinca (author Thorsten), https://github.com/freqtrade/freqtrade-strategies/blob/f3340ce11f5bdf62f598522e64d1f5638eaa13f5/user_data/strategies/berlinguyinca/Low_BB.py. The author gets the credit. Any performance the original claims is unverified; our backtest with real fees decides. The source is a real, tradeable strategy, not a utility. INDICATORS: populate_indicators first computes Bollinger(20, 2) on the typical price and……Show moreShow less

Faithful line-for-line translation of the GPL-3.0 Freqtrade strategy 'Low_BB' by berlinguyinca (author Thorsten), https://github.com/freqtrade/freqtrade-strategies/blob/f3340ce11f5bdf62f598522e64d1f5638eaa13f5/user_data/strategies/berlinguyinca/Low_BB.py. The author gets the credit. Any performance the original claims is unverified; our backtest with real fees decides. The source is a real, tradeable strategy, not a utility. INDICATORS: populate_indicators first computes Bollinger(20, 2) on the typical price and MACD(12,26,9). Both are then OVERWRITTEN or never used: the 'required for graphing' block recomputes bb_lowerband/bb_middleband/bb_upperband from Bollinger(window=20, stds=2) on CLOSE, and MACD is never referenced by entry or exit. The developer must therefore use the CLOSE-based band, defined as qtpylib does it: mid = rolling mean(close, 20), std = pandas rolling std(close, 20) with ddof=1 (sample std), lower = mid - 2*std. MACD and the typical-price band are dead code and must not be implemented as signals. ENTRY: enter_long = 1 when close <= 0.98 * bb_lowerband on a closed 1m bar; there are no other conditions. Freqtrade fills at the NEXT candle's open, so the port submits a market buy on that signal bar's close (equivalent within one bar). EXIT SIGNAL: populate_exit_trend runs dataframe.loc[(), 'exit_long'] = 1. Verified on pandas 2.3.3, that statement creates the exit_long column but leaves it all-NaN, so the strategy has NO signal exit. That matches the docstring's 'sell if (trailing) stop loss is hit'. trailing_stop is not set, so the stop is a FIXED stop, not a trailing one. Positions close only through: (1) minimal_roi = {"0": 0.9, "1": 0.05, "10": 0.04, "15": 0.5}, keyed by minutes since entry. The rule in force is the one with the largest key <= the trade's age: ages 0 to <1 min take +90%, 1 to <10 min take +5%, 10 to <15 min take +4%, and 15+ min take +50%, which in practice is unbounded. (2) stoploss = -0.015, a fixed -1.5% from the entry price that is always active. No time stop, no other exit. VENUE/INSTRUMENTS: the rules are long-only with no leverage, so they belong on BINANCE_SPOT (CASH), which is also under its >=15% venue quota at 10.7%. Freqtrade runs a strategy over a pairlist, one trade per pair, so the faithful platform equivalent runs the identical rules independently on 6 liquid, volatile Binance Spot alts. Each has 1m history from 2018-2020 in the catalog (checked): DOGE, SOL, AVAX, ADA, XRP, LINK. BTC is deliberately excluded. A 1m close 2% below its lower band is rare enough on BTC to give almost no trades, and BTC is the over-mined series. SIZING: this replaces Freqtrade's stake_amount/max_open_trades. Each pair gets a fixed 1/6 of account equity per trade (≈16.7% notional, the max exposure per position), at most one open position per pair, leverage 1.0. The -1.5% stop therefore risks ≈0.25% of equity per trade. FEES: Binance Spot 0.10% taker each side, 0.20% round trip.

Faithful port, so parameters are the source's: BB(20,2) on close, 0.98 multiplier, ROI {0:0.9,1:0.05,10:0.04,15:0.5}, stoploss -0.015. Venue is BINANCE_SPOT because the strategy is long-only and unleveraged. Spot is below its 15% quota, and the spot fee (0.20% RT) is the correct cost for this design. OKX_SPOT and BYBIT_SPOT would add venue diversity, but their 1m catalogs hold only about a week of history (checked), so they are not usable. BTC is excluded (over-mined and near-zero firing). A 6-alt basket mirrors Freqtrade's per-pair pairlist and lifts the trade count above the ~10/yr/pair firing rate. The plan has no tunables because a faithful port should be tested as written.

Implementation

Faithful port of Freqtrade Low_BB by berlinguyinca (Thorsten). The same rules run independently on each of 6 Binance Spot alts (DOGE, SOL, AVAX, ADA, XRP, LINK). Entry: a market buy on a closed 1m bar when close <= 0.98 x the lower Bollinger band (20, 2, sample std) on close. Exits come only from a venue bracket: a fixed -1.5% stop, plus a resting limit take-profit that is re-priced to the minimal_roi level for the trade's age {0:90%,1:5%,10:4%,15:50%}. Each pair gets 1/6 of equity, leverage 1.0, long-only.

Iteration 3 fixes the Layer 1.5 failure 'optimization_plan fixes leverage at 1.0'. The plan's fixed parameter `leverage` (float 1.0) was missing from parameters. It is now present and is read into self.leverage, mirroring config.leverage. It multiplies the per-pair notional, which changes nothing at 1.0. All other fixed plan parameters are present with matching types (bb_window is int 20, the rest are floats). Trading logic, signal, sizing and the iteration-2 performance changes are unchanged, so the projected trade count is unchanged.

Verification Results

If the full backtest times out, profile the remaining per-bar cost before touching rules, starting with the cache.positions_open/orders_open queries on every extra-leg bar. Do not drop pairs or change the timeframe; both would break faithfulness.

Compute budget (L176). The run loads 6 instruments at 1m bars over about 2,616 days, roughly 3.77M bars per instrument and about 22.6M bars in total. Per-bar work is O(1): the running-sum Bollinger band is re-summed every 500 bars, ROI re-pricing happens only when the ROI level changes, and nothing is refit or ranked per bar. The sandbox still took 290s for 30 days. The projected baseline is about 25,000s, close to the 36,000s optimization budget. That cost scales with span and is not a defect that grows with history. It is still a timeout risk for the full backtest and walk-forward.

Optionally re-anchor the stop to avg_px_open on the first bar after the fill, the same way the take-profit is re-priced.

The bracket's stop and initial take-profit are anchored to the signal bar's close, not the actual fill price. Later ROI re-pricing does use avg_px_open. The stop can sit a few basis points away from Freqtrade's entry*(1-0.015). The effect is small at 1m.

The sandbox covered 30 days and closed 16 positions. Average return was 0.177% of notional, below the 0.25% spot viability floor, with PF 1.14 and win rate 31%. Modeled impact took 43% of gross because every entry is a market buy into a 1m flush. Once a trade is 15 minutes old the ROI target is 50%, which never fills, so nearly every trade older than 15 minutes ends at the -1.5% stop. Average hold was about 3 hours. The edge therefore depends on the share of rebounds that reach +4-5% within 15 minutes. In the full backtest, check per-trade return net of impact against the 0.20% round trip, and the share of exits at the stop versus ROI.

Iteration History

sandbox_timeout: Verification failed (Layer 3 — sandbox backtest): smoke test exceeded the 300s wall-clock limit on a ~20000-bar window (30 days of DOGEUSDT.BINANCE_SPOT-1-MINUTE-LAST-EXTERNAL). This almost always means per-bar work that scales with history — e.g. rescanning the full funding/supplementary series, or rebuilding a list and calling min()/sorted() inside calculate_signal()/on_bar() on every bar. Precompute sorted timestamp arrays ONCE in __init__ and use bisect, or cache lookups keyed by timestamp, so per-bar cost is O(log n) not O(n).

Verification failed (Layer 1.5 — hypothesis/config consistency) [class=hypothesis_mismatch]: - optimization_plan fixes leverage at 1.0

Analysis

Low_BB

Backtest and paper results are hypothetical. Trading involves risk of loss.