zhangkun9038@dingtalk.com 0da072dbd3 fix issue
2025-11-25 21:19:01 +08:00

100 lines
3.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# user_data/strategies/SmartBBGrid.py
from freqtrade.strategy import IStrategy
from pandas import DataFrame
import talib.abstract as ta
class SmartBBGrid(IStrategy):
INTERFACE_VERSION = 3
timeframe = '4h'
can_short = False
minimal_roi = {"0": 100} # 不自动ROI
stoploss = -0.99
startup_candle_count = 200
use_exit_signal = True
exit_profit_only = False
ignore_roi_if_entry_signal = True
# ---------- 指标 ----------
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# 布林带20期 2.3倍)
bb = ta.BBANDS(dataframe, timeperiod=20, nbdevup=2.3, nbdevdn=2.3)
dataframe['bb_lower'] = bb['lowerband']
dataframe['bb_upper'] = bb['upperband']
dataframe['bb_mid'] = bb['middleband']
dataframe['bb_width'] = (dataframe['bb_upper'] - dataframe['bb_lower']) / dataframe['bb_mid']
# ADX
dataframe['adx'] = ta.ADX(dataframe, timeperiod=14)
return dataframe
# ---------- 买入 ----------
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe['enter_long'] = False
# 震荡或强牛市都允许在下轨补仓
oscillating = (dataframe['adx'] < 24) & (dataframe['bb_width'] > 0.04)
strong_bull = dataframe['adx'] > 30
dataframe.loc[
((oscillating | strong_bull) &
(dataframe['close'] <= dataframe['bb_lower'] * 1.006) &
(dataframe['volume'] > 0)),
'enter_long'] = True
return dataframe
# ---------- 卖出 ----------
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe['exit_long'] = False
# 只有震荡市才卖,牛市死拿
dataframe.loc[
(dataframe['adx'] < 24) & (
(dataframe['close'] >= dataframe['bb_upper'] * 0.994) | # 碰到上轨卖
(dataframe['close'] >= dataframe['bb_mid'] * 1.13) # 赚13%全清
),
'exit_long'] = True
return dataframe
# ---------- 每币25-30个订单 ----------
def custom_stake_amount(self, pair: str, current_time, current_rate,
proposed_stake, min_stake, max_stake,
entry_tag, **kwargs) -> float:
# 目标:每个币对保持 25-30 个订单
TARGET_ORDERS_PER_PAIR = 27 # 中位数
# 1. 拿到当前钱包总余额
total_balance = self.wallets.get_total('stake')
# 2. 定义每个币对的资金占比
weights = {
'BTC/USDT': 0.30,
'ETH/USDT': 0.30,
'SOL/USDT': 0.40,
}
# 3. 该币对应该占的总金额
target_for_this_pair = total_balance * weights.get(pair, 0.33)
# 4. 该币对已经占了多少(通过计算钱包中该币对的余额)
# 使用 wallets.get_free() 获取可用余额get_used() 获取锁定余额
used_for_this_pair = self.wallets.get_used(pair)
# 5. 还剩多少可以投入
available = target_for_this_pair - used_for_this_pair
if available <= 0:
return 0
# 6. 简化逻辑:返回固定金额,让市场自然形成网格
# 如果想要25-30个订单直接计算平均单笔
# 每个币对预算 / 目标订单数
per_order_stake = target_for_this_pair / TARGET_ORDERS_PER_PAIR
# 不超过可用资金,也不超过 proposed_stake
stake = min(per_order_stake, proposed_stake, available)
stake = max(stake, min_stake * 2) # 防止太小
return stake