Skip to content
All library documents

Stock Screening with Shortening 15-Minute MACD Bars, Turnover, and Valuation

Code Freqtrade

Summary

This proposed equity screen combines three conditions: the position-increase ratio is above 5%, the prior day's trading value exceeds 60 million yuan, and the histogram bars in a 15-minute MACD calculation are getting shorter. The text interprets the position ratio as a sign of buying interest, trading value as a liquidity or activity filter, and a shrinking negative MACD histogram as a possible easing of short-term downside pressure. An expanded version adds a price-to-earnings ratio below 20.

The article provides illustrative Python logic for applying these filters, but gives no backtest, sample period, or measured results. It acknowledges that the setup focuses on short-term price behavior and omits longer-term trend and company fundamentals; changing market conditions may also undermine it. Adding valuation and other technical filters is suggested, but their effect is not evaluated. The screening conditions therefore describe a hypothesis rather than demonstrated evidence of future gains.

Key ideas

  • The screen combines a position-increase ratio above 5%, prior-day trading value above 60 million yuan, and shortening 15-minute MACD histogram bars.
  • The proposed extension also requires a price-to-earnings ratio below 20.
  • The MACD condition is presented as a possible sign that short-term selling pressure is easing.
  • The article provides sample implementation logic but no measured strategy performance.
  • It cautions that short-term signals omit fundamentals and may fail when market conditions shift.

Tags

Full text
# MultiMa.py


```py
# MultiMa Strategy V2
# Author: @Mablue (Masoud Azizi)
# github: https://github.com/mablue/

# --- Do not remove these libs ---
from freqtrade.strategy import IntParameter, IStrategy
from pandas import DataFrame

# --------------------------------

# Add your lib to import here
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
from functools import reduce


class MultiMa(IStrategy):
    # 111/2000:     18 trades. 12/4/2 Wins/Draws/Losses. Avg profit   9.72%. Median profit   3.01%. Total profit  733.01234143 USDT (  73.30%). Avg duration 2 days, 18:40:00 min. Objective: 1.67048

    INTERFACE_VERSION: int = 3
    # Buy hyperspace params:
    buy_params = {
        "buy_ma_count": 4,
        "buy_ma_gap": 15,
    }

    # Sell hyperspace params:
    sell_params = {
        "sell_ma_count": 12,
        "sell_ma_gap": 68,
    }

    # ROI table:
    minimal_roi = {
        "0": 0.523,
        "1553": 0.123,
        "2332": 0.076,
        "3169": 0
    }

    # Stoploss:
    stoploss = -0.345

    # Trailing stop:
    trailing_stop = False  # value loaded from strategy
    trailing_stop_positive = None  # value loaded from strategy
    trailing_stop_positive_offset = 0.0  # value loaded from strategy
    trailing_only_offset_is_reached = False  # value loaded from strategy

    # Opimal Timeframe
    timeframe = "4h"

    count_max = 20
    gap_max = 100

    buy_ma_count = IntParameter(1, count_max, default=7, space="buy")
    buy_ma_gap = IntParameter(1, gap_max, default=7, space="buy")

    sell_ma_count = IntParameter(1, count_max, default=7, space="sell")
    sell_ma_gap = IntParameter(1, gap_max, default=94, space="sell")

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        for count in range(self.count_max):
            for gap in range(self.gap_max):
                if count*gap > 1 and count*gap not in dataframe.keys():
                    dataframe[count*gap] = ta.TEMA(
                        dataframe, timeperiod=int(count*gap)
                    )
        print(" ", metadata['pair'], end="\t\r")

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        conditions = []
        # I used range(self.buy_ma_count.value) instade of self.buy_ma_count.range
        # Cuz it returns range(7,8) but we need range(8) for all modes hyperopt, backtest and etc

        for ma_count in range(self.buy_ma_count.value):
            key = ma_count*self.buy_ma_gap.value
            past_key = (ma_count-1)*self.buy_ma_gap.value
            if past_key > 1 and key in dataframe.keys() and past_key in dataframe.keys():
                conditions.append(dataframe[key] < dataframe[past_key])

        if conditions:
            dataframe.loc[reduce(lambda x, y: x & y, conditions), "enter_long"] = 1
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        conditions = []

        for ma_count in range(self.sell_ma_count.value):
            key = ma_count*self.sell_ma_gap.value
            past_key = (ma_count-1)*self.sell_ma_gap.value
            if past_key > 1 and key in dataframe.keys() and past_key in dataframe.keys():
                conditions.append(dataframe[key] > dataframe[past_key])

        if conditions:
            dataframe.loc[reduce(lambda x, y: x | y, conditions), "exit_long"] = 1
        return dataframe

```

Shown in full with attribution under the source's licence. Licence: GPL-3.0

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.