以「上次成交價為中心」的網格策略 - 跌超 x% -> 每次加倉 +0.1 NAV - 漲超 y% -> 每次減倉 -0.1 NAV (或加空/回補) - 倉位上限 ±0.9 NAV - 使用 position adjustment 逐步加減碼
Timeframe
1h
Direction
Long Only
Stoploss
-100.0%
Trailing Stop
No
ROI
0m: 100.0%
Interface Version
3
Startup Candles
20
Indicators
0
freqtrade/freqtrade-strategies
freqtrade/freqtrade-strategies
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
# user_data/strategies/grid_strategy.py
from freqtrade.strategy import IStrategy, DecimalParameter
from pandas import DataFrame
class GridStrategy(IStrategy):
"""
以「上次成交價為中心」的網格策略
- 跌超 x% -> 每次加倉 +0.1 NAV
- 漲超 y% -> 每次減倉 -0.1 NAV (或加空/回補)
- 倉位上限 ±0.9 NAV
- 使用 position adjustment 逐步加減碼
"""
INTERFACE_VERSION = 3
can_short: bool = True
position_adjustment_enable = True
timeframe = "1h"
process_only_new_candles = True
startup_candle_count = 20
minimal_roi = {"0": 1}
stoploss = -1
trailing_stop = False
x = DecimalParameter(0.001, 0.02, default=0.005, decimals=4,
space="buy", optimize=True, load=True)
y = DecimalParameter(0.001, 0.02, default=0.005, decimals=4,
space="sell", optimize=True, load=True)
step: float = 0.10 # 每次調整 = 10% 的基準名義額
max_pos: float = 0.90
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 單一變數:初始為 None,等 populate_indicators() 時再設
self.last_trade_price: float | None = None
def populate_indicators(self, df: DataFrame, metadata: dict) -> DataFrame:
# ✅ 初始化:只在第一次還沒設定時,把第一根 K 的 close 當作 last_trade_price
if self.last_trade_price is None and len(df) > 0:
self.last_trade_price = float(df["close"].iloc[0])
df["prev_close"] = df["close"].shift(1)
df["chg"] = (df["close"] - df["prev_close"]) / df["prev_close"]
return df
def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
df["enter_long"] = 0
df["enter_short"] = 0
# 跌超 x% → 開多(第一筆由這裡觸發)
df.loc[(df["chg"] <= -self.x.value) & (df["volume"] > 0), "enter_long"] = 1
# 漲超 y% → 開空
df.loc[(df["chg"] >= self.y.value) & (df["volume"] > 0), "enter_short"] = 1
return df
def populate_exit_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
df["exit_long"] = 0
df["exit_short"] = 0
return df
def adjust_trade_position(self, trade, current_time, current_rate, current_profit, **kwargs):
# 1) 確保每筆交易開始時,有中心
if self.last_trade_price is None:
self.last_trade_price = current_rate # 或 trade.open_rate
last_trade_price = self.last_trade_price
change_pct = (current_rate - last_trade_price) / last_trade_price
# 名義額基準(建議用名義額,不用保證金)
entry_price = trade.open_rate or current_rate
base_notional = max(abs(trade.amount) * entry_price, 1e-9)
current_amount = abs(getattr(trade, "amount", 0.0))
current_notional = current_amount * current_rate
pos_ratio = current_notional / base_notional
one_step_notional = self.step * base_notional
one_step_qty = one_step_notional / current_rate
eps = 1e-9
close_tolerance = 1.05
band = 0.003 # 放寬一點 0.3%
delta_qty = 0.0
if not trade.is_short:
if change_pct <= -self.x.value:
if pos_ratio > self.max_pos - self.step + eps:
delta_qty = -current_amount # 平倉
self.last_trade_price = None # 2) 平倉後清空
return float(delta_qty)
delta_qty = +abs(one_step_qty)
self.last_trade_price = current_rate # 3) 加倉/減倉後更新中心
elif change_pct >= self.y.value and current_amount > 0:
if current_notional <= one_step_notional * close_tolerance:
delta_qty = -current_amount # 平倉
self.last_trade_price = None
return float(delta_qty)
delta_qty = -abs(one_step_qty)
self.last_trade_price = current_rate
# 回中心就平(先拿掉 need profit)
if abs(change_pct) < band and current_amount > 0:
delta_qty = -current_amount
self.last_trade_price = None
return float(delta_qty)
else:
if change_pct >= self.y.value:
if pos_ratio > self.max_pos - self.step + eps:
delta_qty = +current_amount # 平空
self.last_trade_price = None
return float(delta_qty)
delta_qty = -abs(one_step_qty)
self.last_trade_price = current_rate
elif change_pct <= -self.x.value and current_amount > 0:
if current_notional <= one_step_notional * close_tolerance:
delta_qty = +current_amount # 平空
self.last_trade_price = None
return float(delta_qty)
delta_qty = +abs(one_step_qty)
self.last_trade_price = current_rate
if abs(change_pct) < band and current_amount > 0:
delta_qty = +current_amount
self.last_trade_price = None
return float(delta_qty)
return float(delta_qty)
def on_trade_close(self, trade, order, **kwargs):
# 保險:成交全平時再清一次,讓下一筆能重新設中心
self.last_trade_price = None