Timeframe
1h
Direction
Long Only
Stoploss
-3.0%
Trailing Stop
Yes
ROI
0m: 4.0%, 60m: 2.0%, 120m: 1.0%
Interface Version
3
Startup Candles
N/A
Indicators
2
freqtrade/freqtrade-strategies
Strategy 003 author@: Gerald Lonlas github@: https://github.com/freqtrade/freqtrade-strategies
"""Example freqtrade strategy using QuantGistProtection.
This strategy trades the EMA crossover on the 1h timeframe and uses
``QuantGistProtection`` to automatically pause trading 10 minutes before
and 5 minutes after any high-impact macro event (NFP, CPI, FOMC, etc.).
Setup
-----
1. Install the plugin::
pip install quantgist-freqtrade
2. Add the protection to your ``config.json`` (see PROTECTION_CONFIG_EXAMPLE
below) or configure it directly in the ``protections`` class attribute
(see this file).
3. Set your API key::
export QUANTGIST_API_KEY=qg_live_...
4. Run freqtrade normally::
freqtrade trade --strategy MacroAwareEMACross --config config.json
config.json excerpt
-------------------
.. code-block:: json
{
"protections": [
{
"method": "QuantGistProtection",
"api_key": "qg_live_...",
"pause_minutes_before": 10,
"pause_minutes_after": 5,
"impact": "high"
}
],
"pairlists": [
{
"method": "StaticPairList"
}
],
"exchange": {
"name": "binance",
"pair_whitelist": ["BTC/USDT", "ETH/USDT", "EUR/USD"]
}
}
"""
from __future__ import annotations
import os
from datetime import datetime
from functools import reduce
from typing import Optional
# freqtrade imports — only available when running inside freqtrade
try:
import pandas as pd
import talib.abstract as ta
from freqtrade.strategy import IStrategy, merge_informative_pair
from freqtrade.strategy.interface import IStrategy as _IStratBase
_FT_AVAILABLE = True
except ImportError:
_FT_AVAILABLE = False
IStrategy = object # type: ignore[assignment, misc]
from qg_freqtrade import QuantGistProtection
# ---------------------------------------------------------------------------
# Strategy
# ---------------------------------------------------------------------------
class MacroAwareEMACross(IStrategy): # type: ignore[misc]
"""EMA crossover strategy with QuantGist macro-event protection.
Entry: EMA(9) crosses above EMA(21) on 1h candles.
Exit: EMA(9) crosses below EMA(21), or ROI/stoploss triggers.
Guard: ``QuantGistProtection`` blocks all entries around high-impact events.
"""
# -----------------------------------------------------------------------
# Strategy metadata
# -----------------------------------------------------------------------
INTERFACE_VERSION = 3
timeframe = "1h"
can_short = False
# ROI table
minimal_roi = {
"0": 0.04, # 4 % at any time (take-profit)
"60": 0.02, # 2 % after 60 min
"120": 0.01, # 1 % after 2 h
}
stoploss = -0.03 # 3 % stoploss
trailing_stop = True
trailing_stop_positive = 0.01
trailing_stop_positive_offset = 0.02
# -----------------------------------------------------------------------
# Protection — configure via env var or hardcode for development
# -----------------------------------------------------------------------
@property
def protections(self) -> list[dict]: # type: ignore[override]
return [
{
"method": "QuantGistProtection",
# API key from env var (recommended) or inline for quick testing
"api_key": os.environ.get("QUANTGIST_API_KEY", "qg_live_YOUR_KEY_HERE"),
"pause_minutes_before": 10,
"pause_minutes_after": 5,
"impact": "high",
# Cache event list for 5 minutes to avoid repeated API calls
"cache_ttl_seconds": 300,
}
]
# -----------------------------------------------------------------------
# Indicators
# -----------------------------------------------------------------------
def populate_indicators(self, dataframe: "pd.DataFrame", metadata: dict) -> "pd.DataFrame": # type: ignore[name-defined]
dataframe["ema9"] = ta.EMA(dataframe, timeperiod=9)
dataframe["ema21"] = ta.EMA(dataframe, timeperiod=21)
dataframe["ema50"] = ta.EMA(dataframe, timeperiod=50)
dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
return dataframe
# -----------------------------------------------------------------------
# Entry / exit conditions
# -----------------------------------------------------------------------
def populate_entry_trend(self, dataframe: "pd.DataFrame", metadata: dict) -> "pd.DataFrame": # type: ignore[name-defined]
dataframe.loc[
(
# EMA 9 crosses above EMA 21
(dataframe["ema9"] > dataframe["ema21"])
& (dataframe["ema9"].shift(1) <= dataframe["ema21"].shift(1))
# Price above EMA 50 (uptrend filter)
& (dataframe["close"] > dataframe["ema50"])
# RSI not overbought
& (dataframe["rsi"] < 70)
# Ensure volume is non-zero
& (dataframe["volume"] > 0)
),
"enter_long",
] = 1
return dataframe
def populate_exit_trend(self, dataframe: "pd.DataFrame", metadata: dict) -> "pd.DataFrame": # type: ignore[name-defined]
dataframe.loc[
(
# EMA 9 crosses below EMA 21
(dataframe["ema9"] < dataframe["ema21"])
& (dataframe["ema9"].shift(1) >= dataframe["ema21"].shift(1))
),
"exit_long",
] = 1
return dataframe
# ---------------------------------------------------------------------------
# PROTECTION_CONFIG_EXAMPLE — copy-paste ready config.json snippet
# ---------------------------------------------------------------------------
PROTECTION_CONFIG_EXAMPLE = """
Add the following to your freqtrade config.json:
{
"protections": [
{
"method": "QuantGistProtection",
"api_key": "qg_live_YOUR_KEY_HERE",
"pause_minutes_before": 10,
"pause_minutes_after": 5,
"impact": "high",
"cache_ttl_seconds": 300
}
]
}
Alternatively, set QUANTGIST_API_KEY as an environment variable and omit
the "api_key" field — the plugin reads it from the environment.
"""
if __name__ == "__main__":
print("This file is a freqtrade strategy — run it with the freqtrade CLI.")
print(PROTECTION_CONFIG_EXAMPLE)