add more strategies

This commit is contained in:
jackyu66git
2025-07-21 20:31:25 +08:00
parent d75c202975
commit 6907ce7d8c
6 changed files with 379 additions and 69 deletions
+1
View File
@@ -74,6 +74,7 @@ class ChanKLC():
self.bb_out = True self.bb_out = True
if self.low <= klu.bblow30 and klu.bblow30 > 0: if self.low <= klu.bblow30 and klu.bblow30 > 0:
self.klc_fx_type = Chan_KLC_FX.BOTTOM4 self.klc_fx_type = Chan_KLC_FX.BOTTOM4
#self.bb_out = True
def cal_indicators(self): def cal_indicators(self):
for index in range(1, len(self.klus)): for index in range(1, len(self.klus)):
self.volume += self.klus[index].volume self.volume += self.klus[index].volume
+1 -1
View File
@@ -58,7 +58,7 @@
} }
], ],
"telegram": { "telegram": {
"enabled": false, "enabled": true,
"token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y", "token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y",
"chat_id": "580807463" "chat_id": "580807463"
}, },
+58 -41
View File
@@ -5,8 +5,12 @@ from typing import Dict, List
from functools import reduce from functools import reduce
from pandas import DataFrame from pandas import DataFrame
import numpy as np import numpy as np
import pandas as pd
# -------------------------------- # --------------------------------
# 设置pandas选项以避免FutureWarning
pd.set_option('future.no_silent_downcasting', True)
import talib.abstract as ta import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib import freqtrade.vendor.qtpylib.indicators as qtpylib
from technical.util import resample_to_interval, resampled_merge from technical.util import resample_to_interval, resampled_merge
@@ -28,38 +32,37 @@ logger = logging.getLogger(__name__)
class BB9033(IStrategy): class BB9033(IStrategy):
""" """
布林带ATR反转策略 布林带反转策略(与Pine Script保持一致)
基于ATR动态调整布林带轨道,实现反转交易 基于EMA和标准差计算布林带,实现反转交易
交易逻辑: 交易逻辑:
- 做多:价格跌破下轨后反转 - 做多:价格跌破下轨后反转
- 做空:价格突破上轨后反转 - 做空:价格突破上轨后反转
- 止盈:价格触及对侧轨道 - 做多止盈:价格减去0.5倍ATR突破上轨
- 做空止盈:价格跌破下轨
- 止损:基于ATR动态设置 - 止损:基于ATR动态设置
""" """
INTERFACE_VERSION: int = 3 INTERFACE_VERSION: int = 3
# 策略参数 # 策略参数(与Pine Script保持一致)
bb_length = 90 # 布林带长度 bb_length = 41 # 布林带长度
atr_multiplier = 4.2 # ATR乘数(轨道) atr_multiplier = 2.3 # 布林带倍数
atr_stop_multiplier = 1.8 # ATR乘数(止损) atr_stop_multiplier = 3 # 止损ATR倍数
atr_length = 14 # ATR计算周期 atr_length = 11 # ATR计算周期
# Minimal ROI designed for the strategy. # Minimal ROI designed for the strategy.
# This attribute will be overridden if the config file contains "minimal_roi" # This attribute will be overridden if the config file contains "minimal_roi"
minimal_roi = { minimal_roi = {
"0": 0.5
} }
can_short = True
# Optimal stoploss designed for the strategy # Optimal stoploss designed for the strategy
# This attribute will be overridden if the config file contains "stoploss" # This attribute will be overridden if the config file contains "stoploss"
stoploss = -0.3 stoploss = -0.3
use_custom_stoploss = True use_custom_stoploss = True
# Optimal timeframe for the strategy # Optimal timeframe for the strategy
timeframe = '3m' time = 5
time = 30
# Trailing stop loss # Trailing stop loss
trailing_stop = False trailing_stop = False
lev = 1.0 lev = 1.0
@@ -67,26 +70,27 @@ class BB9033(IStrategy):
process_only_new_candles = False process_only_new_candles = False
# Number of candles the strategy requires before producing valid signals # Number of candles the strategy requires before producing valid signals
startup_candle_count: int = max(bb_length, atr_length) + 10 startup_candle_count: int = max(bb_length*time, atr_length*time) + 10
# 存储每个交易的止损价格 # 存储每个交易的止损价格
trade_stop_prices: Dict[str, float] = {} trade_stop_prices: Dict[str, float] = {}
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
""" """
计算技术指标 计算技术指标(与Pine Script保持一致)
""" """
dataframe_3 = resample_to_interval(dataframe, self.get_ticker_indicator() * self.time) dataframe_3 = resample_to_interval(dataframe, self.get_ticker_indicator() * self.time)
# 计算ATR(用于止损计算) # 计算ATR(用于止损计算)
dataframe_3['atr'] = ta.ATR(dataframe_3, timeperiod=self.atr_length) dataframe_3['atr'] = ta.ATR(dataframe_3, timeperiod=self.atr_length)
# 计算布林带(使用标准方法:移动平均线 ± 标准差倍数 # 计算布林带(使用EMA作为基础,与Pine Script保持一致
bb_upper, bb_middle, bb_lower = ta.BBANDS(dataframe_3['close'], timeperiod=self.bb_length, nbdevup=self.atr_multiplier, nbdevdn=self.atr_multiplier, matype=0) bb_basis = ta.EMA(dataframe_3['close'], timeperiod=self.bb_length)
dataframe_3['bb_upper'] = bb_upper bb_dev = self.atr_multiplier * ta.STDDEV(dataframe_3['close'], timeperiod=self.bb_length)
dataframe_3['bb_lower'] = bb_lower dataframe_3['bb_upper'] = bb_basis + bb_dev
for i in range(1700, 1800): dataframe_3['bb_middle'] = bb_basis
print(dataframe_3.iloc[i]) dataframe_3['bb_lower'] = bb_basis - bb_dev
# 计算突破条件(与Pine Script保持一致) # 计算突破条件(与Pine Script保持一致)
# 确保所有用于计算的数据都不是NaN # 确保所有用于计算的数据都不是NaN
valid_data = ( valid_data = (
@@ -98,16 +102,20 @@ class BB9033(IStrategy):
dataframe_3['bb_lower'].shift(1).notna() dataframe_3['bb_lower'].shift(1).notna()
) )
# 做空条件:价格突破上轨
dataframe_3['break_above_upper'] = ( dataframe_3['break_above_upper'] = (
(dataframe_3['close'] > dataframe_3['bb_upper']) & (dataframe_3['close'] > dataframe_3['bb_upper']) &
(dataframe_3['close'].shift(1) <= dataframe_3['bb_upper'].shift(1)) & (dataframe_3['close'].shift(1) <= dataframe_3['bb_upper'].shift(1)) &
valid_data valid_data
) )
# 做多条件:价格跌破下轨
dataframe_3['break_below_lower'] = ( dataframe_3['break_below_lower'] = (
(dataframe_3['close'] < dataframe_3['bb_lower']) & (dataframe_3['close'] < dataframe_3['bb_lower']) &
(dataframe_3['close'].shift(1) >= dataframe_3['bb_lower'].shift(1)) & (dataframe_3['close'].shift(1) >= dataframe_3['bb_lower'].shift(1)) &
valid_data valid_data
) )
dataframe = resampled_merge(dataframe, dataframe_3) dataframe = resampled_merge(dataframe, dataframe_3)
return dataframe return dataframe
@@ -118,23 +126,25 @@ class BB9033(IStrategy):
break_below_lower = 'resample_{}_break_below_lower'.format(self.get_ticker_indicator()*self.time) break_below_lower = 'resample_{}_break_below_lower'.format(self.get_ticker_indicator()*self.time)
break_above_upper = 'resample_{}_break_above_upper'.format(self.get_ticker_indicator()*self.time) break_above_upper = 'resample_{}_break_above_upper'.format(self.get_ticker_indicator()*self.time)
# 做多条件:价格跌破下轨 # 检测多头信号:价格跌破下轨
dataframe.loc[ dataframe.loc[
( (
(dataframe[break_below_lower] == True) & # 价格跌破下轨,明确检查True值 (dataframe[break_below_lower] == True) &
(dataframe[break_below_lower].notna()) # 确保不是NaN (pd.notna(dataframe[break_below_lower]))
), ),
'enter_long'] = 1 ['enter_long', 'enter_tag']] = (1, 'long_signal_chan')
# 做空条件:价格突破上轨 # 检测空头信号:价格突破上轨
dataframe.loc[ dataframe.loc[
( (
(dataframe[break_above_upper] == True) & # 价格突破上轨,明确检查True值 (dataframe[break_above_upper] == True) &
(dataframe[break_above_upper].notna()) # 确保不是NaN (pd.notna(dataframe[break_above_upper]))
), ),
'enter_short'] = 1 ['enter_short', 'enter_tag']] = (1, 'short_signal_chan')
return dataframe return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
""" """
@@ -142,24 +152,30 @@ class BB9033(IStrategy):
""" """
bb_upper_str = 'resample_{}_bb_upper'.format(self.get_ticker_indicator()*self.time) bb_upper_str = 'resample_{}_bb_upper'.format(self.get_ticker_indicator()*self.time)
bb_lower_str = 'resample_{}_bb_lower'.format(self.get_ticker_indicator()*self.time) bb_lower_str = 'resample_{}_bb_lower'.format(self.get_ticker_indicator()*self.time)
atr_str = 'resample_{}_atr'.format(self.get_ticker_indicator()*self.time)
close_str = 'resample_{}_close'.format(self.get_ticker_indicator()*self.time)
low_str = 'resample_{}_low'.format(self.get_ticker_indicator()*self.time)
# 多头止盈:价格突破上轨(与Pine Script一致) # 做多止盈条件:价格突破上轨(与Pine Script保持一致)
dataframe.loc[ dataframe.loc[
( (
(dataframe['close'] > dataframe[bb_upper_str]) & # 当前价格突破上轨 (dataframe[close_str] > dataframe[bb_upper_str]) & # close-atr_value*0.5 > bb_upper
(dataframe[bb_upper_str].notna()) & # 确保布林带上轨不是NaN (dataframe[bb_upper_str].notna()) & # 确保布林带上轨不是NaN
(dataframe['close'].notna()) # 确保收盘价不是NaN (dataframe[close_str].notna()) & # 确保收盘价不是NaN
(dataframe[atr_str].notna()) & # 确保ATR不是NaN
(len(self.trade_stop_prices) > 0)
), ),
'exit_long'] = 1 ['exit_long', 'exit_tag']] = (1, 'long_close_signal_chan')
# 空头止盈:价格跌破下轨(与Pine Script一致) # 做空止盈条件:价格跌破下轨(与Pine Script保持一致)
dataframe.loc[ dataframe.loc[
( (
(dataframe['close'] < dataframe[bb_lower_str]) & # 当前价格跌破下轨 (dataframe[low_str] < dataframe[bb_lower_str]) & # low < bb_lower
(dataframe[bb_lower_str].notna()) & # 确保布林带下轨不是NaN (dataframe[bb_lower_str].notna()) & # 确保布林带下轨不是NaN
(dataframe['close'].notna()) # 确保收盘价不是NaN (dataframe[low_str].notna()) & # 确保最低价不是NaN
(len(self.trade_stop_prices) > 0)
), ),
'exit_short'] = 1 ['exit_short', 'exit_tag']] = (1, 'short_close_signal_chan')
return dataframe return dataframe
@@ -180,8 +196,9 @@ class BB9033(IStrategy):
if len(dataframe) < 2: if len(dataframe) < 2:
return self.stoploss return self.stoploss
# 使用上一个K线的收盘价 # 使用上一个K线的收盘价(重采样后的数据)
last_close = dataframe.iloc[-2]['close'] # 上一个完整K线的收盘价 close_str = 'resample_{}_close'.format(self.get_ticker_indicator()*self.time)
last_close = dataframe.iloc[-2][close_str] # 上一个完整K线的收盘价
stop_price = self.trade_stop_prices[trade_id] stop_price = self.trade_stop_prices[trade_id]
@@ -266,7 +283,7 @@ class BB9033(IStrategy):
self.trade_stop_prices[str(trade.id)] = stop_price self.trade_stop_prices[str(trade.id)] = stop_price
#logger.info(f"交易 {trade.id} 开仓,记录止损价格: {stop_price}, ATR: {atr_value}, 开仓价: {trade.open_rate}") #logger.info(f"交易 {trade.id} 开仓,记录止损价格: {stop_price}, ATR: {atr_value}, 开仓价: {trade.open_rate}")
#logger.info(f"{current_time} {pair} {trade.open_rate} {stop_price} {atr_value}") logger.info(f"{current_time} {pair} {trade.open_rate} {stop_price} {atr_value}")
def trade_exit(self, pair: str, trade: Trade, order: Order, current_time: datetime, def trade_exit(self, pair: str, trade: Trade, order: Order, current_time: datetime,
**kwargs) -> None: **kwargs) -> None:
""" """
+295
View File
@@ -0,0 +1,295 @@
# --- Do not remove these libs ---
from freqtrade.strategy import IStrategy
from typing import Dict, List
from functools import reduce
from pandas import DataFrame
import numpy as np
import pandas as pd
# --------------------------------
# 设置pandas选项以避免FutureWarning
pd.set_option('future.no_silent_downcasting', True)
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
from technical.util import resample_to_interval, resampled_merge
from freqtrade.persistence import Trade, Order
from datetime import datetime, timedelta
from typing import Optional
import logging
logger = logging.getLogger(__name__)
# freqtrade plot-dataframe --strategy BB90331 --datadir user_data/data/binance -c ./user_data/ChanLun_SOL_30.json --timerange=20250309-
# freqtrade trade -c ./user_data/Chan/config/ChanLun_BTC_30.json --strategy BB90331 --strategy-path ./user_data/Chan/strategies
# freqtrade backtesting -c ./user_data/Chan/config/ChanLun_BTC_30.json --strategy BB90331 --strategy-path ./user_data/Chan/strategies --timerange=20250623-
# freqtrade download-data -c ./user_data/Chan/config/ChanLun_BTC_30.json -t 1m --pairs BTC/USDT:USDT --timerange=20250501-
# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --spaces roi stoploss --strategy BB90331 --strategy-path ./user_data/Chan/strategies -c ./user_data/Chan/config/ChanLun_BTC_30.json -e 200 --timerange=20250201-20250401
# sudo docker compose run --rm chan_btc backtesting -c ./user_data/Chan/config/ChanLun_BTC_30.json --strategy BB90331 --strategy-path ./user_data/Chan/strategies --timerange=20250101-
# sudo docker compose run --rm chan_btc download-data -c ./user_data/Chan/config/ChanLun_BTC_30.json --pairs BTC/USDT:USDT -t 1m --timerange 20240101-
# sudo docker compose run --rm chan_btc trade -c ./user_data/Chan/config/ChanLun_BTC_30.json --strategy BB90331 --strategy-path ./user_data/Chan/strategies
class BB90331(IStrategy):
"""
布林带ATR反转策略
基于ATR动态调整布林带轨道,实现反转交易
交易逻辑:
- 做多:价格跌破下轨后反转
- 做空:价格突破上轨后反转
- 止盈:价格触及对侧轨道
- 止损:基于ATR动态设置
"""
INTERFACE_VERSION: int = 3
# 策略参数
bb_length = 90 # 布林带长度
atr_multiplier = 3.0 # ATR乘数(轨道)
atr_stop_multiplier = 1 # ATR乘数(止损)
atr_length = 11 # ATR计算周期
# Minimal ROI designed for the strategy.
# This attribute will be overridden if the config file contains "minimal_roi"
minimal_roi = {
}
can_short = True
# Optimal stoploss designed for the strategy
# This attribute will be overridden if the config file contains "stoploss"
stoploss = -0.3
use_custom_stoploss = True
# Optimal timeframe for the strategy
time =1
# Trailing stop loss
trailing_stop = False
lev = 1.0
# Run "populate_indicators" only for new candle
process_only_new_candles = False
# Number of candles the strategy requires before producing valid signals
startup_candle_count: int = max(bb_length*time, atr_length*time) + 10
# 存储每个交易的止损价格
trade_stop_prices: Dict[str, float] = {}
# 信号确认机制相关变量 - 已删除,不再使用确认机制
# first_signal_time: Optional = None
# first_signal_type: Optional[str] = None # 'long' 或 'short'
# signal_confirm_hours = 4 # 4小时内需要确认信号
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
计算技术指标
"""
# 计算ATR(用于止损计算)
dataframe['atr'] = ta.ATR(dataframe, timeperiod=self.atr_length)
# 计算布林带(使用标准方法:移动平均线 ± 标准差倍数)
bb_upper, bb_middle, bb_lower = ta.BBANDS(dataframe['close'], timeperiod=self.bb_length, nbdevup=self.atr_multiplier, nbdevdn=self.atr_multiplier, matype=0)
dataframe['bb_upper'] = bb_upper
dataframe['bb_middle'] = bb_middle
dataframe['bb_lower'] = bb_lower
#for i in range(1700, 1800):
#print(dataframe_3.iloc[i])
# 计算突破条件(与Pine Script保持一致)
# 确保所有用于计算的数据都不是NaN
valid_data = (
dataframe['close'].notna() &
dataframe['bb_upper'].notna() &
dataframe['bb_lower'].notna() &
dataframe['close'].shift(1).notna() &
dataframe['bb_upper'].shift(1).notna() &
dataframe['bb_lower'].shift(1).notna()
)
dataframe['break_above_upper'] = (
(dataframe['close'] > dataframe['bb_upper']) &
(dataframe['close'].shift(1) <= dataframe['bb_upper'].shift(1)) &
valid_data
)
dataframe['break_below_lower'] = (
(dataframe['close'] < dataframe['bb_lower']) &
(dataframe['close'].shift(1) >= dataframe['bb_lower'].shift(1)) &
valid_data
)
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Based on TA indicators, populates the entry trend columns
直接开仓策略:检测到信号立即开仓,不需要确认机制
"""
break_below_lower = 'break_below_lower'
break_above_upper = 'break_above_upper'
# 多头信号:价格跌破下轨后直接开仓
dataframe.loc[
(
(dataframe[break_below_lower] == True) &
(dataframe[break_below_lower].notna())
),
['enter_long', 'enter_tag']
] = (1, 'long_signal_chan_direct')
# 空头信号:价格突破上轨后直接开仓
dataframe.loc[
(
(dataframe[break_above_upper] == True) &
(dataframe[break_above_upper].notna())
),
['enter_short', 'enter_tag']
] = (1, 'short_signal_chan_direct')
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Based on TA indicators, populates the exit trend columns
"""
bb_middle_str = 'bb_middle'
# 多头止盈:价格回到中轨时平仓
dataframe.loc[
(
(dataframe['close'] >= dataframe[bb_middle_str]) & # 当前价格回到中轨
(dataframe[bb_middle_str].notna()) & # 确保布林带中轨不是NaN
(dataframe['close'].notna()) & # 确保收盘价不是NaN
(len(self.trade_stop_prices) > 0)
),
['exit_long', 'exit_tag']] = (1, 'long_close_signal_chan')
# 空头止盈:价格回到中轨时平仓
dataframe.loc[
(
(dataframe['close'] <= dataframe[bb_middle_str]) & # 当前价格回到中轨
(dataframe[bb_middle_str].notna()) & # 确保布林带中轨不是NaN
(dataframe['close'].notna()) & # 确保收盘价不是NaN
(len(self.trade_stop_prices) > 0)
),
['exit_short', 'exit_tag']] = (1, 'short_close_signal_chan')
return dataframe
def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime,
current_rate: float, current_profit: float,
after_fill: bool, **kwargs) -> float:
"""
自定义止损逻辑:使用开单时记录的ATR止损价格
"""
# 检查是否有存储的止损价格
trade_id = str(trade.id)
if trade_id not in self.trade_stop_prices:
return self.stoploss
# 获取最新的K线数据
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
if len(dataframe) < 2:
return self.stoploss
# 使用上一个K线的收盘价
last_close = dataframe.iloc[-2]['close'] # 上一个完整K线的收盘价
stop_price = self.trade_stop_prices[trade_id]
if trade.is_short:
# 空头止损:上一个K线收盘价超过止损价格时触发止损
if last_close >= stop_price:
stop_loss_pct = -abs((last_close - stop_price) / last_close)
else:
stop_loss_pct = 1.0 # 不触发止损
else:
# 多头止损:上一个K线收盘价低于止损价格时触发止损
if last_close <= stop_price:
stop_loss_pct = -abs((stop_price - last_close) / last_close)
else:
stop_loss_pct = 1.0 # 不触发止损
# 确保止损不会比默认止损更宽松
return max(stop_loss_pct, self.stoploss)
def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
time_in_force: str, current_time: datetime, entry_tag: str,
side: str, **kwargs) -> bool:
"""
确认交易进场
"""
# 获取最新数据进行最终确认
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
if len(dataframe) == 0:
return False
latest_candle = dataframe.iloc[-1]
# 使用重采样后的字段名
bb_upper_str = 'bb_upper'
bb_lower_str = 'bb_lower'
atr_str = 'atr'
# 确保技术指标有效
if (np.isnan(latest_candle[bb_upper_str]) or
np.isnan(latest_candle[bb_lower_str]) or
np.isnan(latest_candle[atr_str])):
return False
return True
def order_filled(self, pair: str, trade: Trade, order: Order, current_time: datetime,
**kwargs) -> None:
"""
当订单填充时的回调函数
在开仓时记录基于开单时ATR的止损价格
"""
# 处理开仓订单(包括做多和做空)
if (order.ft_order_side == 'buy' or order.ft_order_side == 'sell') and trade.is_open:
# 获取开仓时的数据
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
if len(dataframe) == 0:
return
# 获取开仓时的ATR值
atr_str = 'atr'
# 找到最接近开仓时间的K线
open_candle = dataframe.iloc[-1] # 使用最新的K线作为开仓时的数据
atr_value = open_candle[atr_str]
if not np.isnan(atr_value) and atr_value > 0:
# 计算止损价格并存储
atr_stop_distance = self.atr_stop_multiplier * atr_value
if trade.is_short:
# 空头止损:入场价 + ATR止损距离
stop_price = trade.open_rate + atr_stop_distance
else:
# 多头止损:入场价 - ATR止损距离
stop_price = trade.open_rate - atr_stop_distance
# 使用trade_id作为key存储止损价格
self.trade_stop_prices[str(trade.id)] = stop_price
#logger.info(f"交易 {trade.id} 开仓,记录止损价格: {stop_price}, ATR: {atr_value}, 开仓价: {trade.open_rate}")
logger.info(f"{current_time} {pair} {trade.open_rate} {stop_price} {atr_value}")
def trade_exit(self, pair: str, trade: Trade, order: Order, current_time: datetime,
**kwargs) -> None:
"""
当交易退出时的回调函数
清理存储的止损价格记录
"""
trade_id = str(trade.id)
if trade_id in self.trade_stop_prices:
del self.trade_stop_prices[trade_id]
logger.info(f"交易 {trade.id} 已关闭,清理止损价格记录")
def leverage(self, pair: str, current_time: datetime, current_rate: float,
proposed_leverage: float, max_leverage: float, entry_tag: Optional[str], side: str,
**kwargs) -> float:
return self.lev
def get_ticker_indicator(self):
return int(self.timeframe[:-1])
+22 -25
View File
@@ -1,32 +1,30 @@
//@version=6 //@version=5
strategy("布林带ATR反转策略", shorttitle="BBB_ATR", overlay=true, default_qty_type=strategy.percent_of_equity, default_qty_value=100, calc_on_every_tick=true) strategy("布林带反转策略", shorttitle="BB_REV", overlay=true, default_qty_type=strategy.percent_of_equity, default_qty_value=100, calc_on_every_tick=true)
// 输入参数 // 输入参数
bb_length = input.int(90, "布林带长度", minval=10, maxval=200) bb_length = input.int(41, "布林带长度", minval=10, maxval=200)
atr_multiplier = input.float(3.0, "ATR乘数(轨道)", minval=1.0, maxval=10.0, step=0.1) bb_mult = input.float(2.3, "布林带倍数", minval=1.0, maxval=10.0, step=0.1)
atr_stop_multiplier = input.float(3.0, "ATR乘数(止损)", minval=0.5, maxval=5.0, step=0.1) atr_length = input.int(11, "ATR计算周期", minval=5, maxval=90)
atr_length = input.int(9, "ATR计算周期", minval=5, maxval=50) atr_mult = input.float(3, "止损ATR倍数", minval=0.5, maxval=10.0, step=0.1)
// 显示设置 // 显示设置
show_bands = input.bool(true, "显示布林带") show_bands = input.bool(true, "显示布林带")
show_signals = input.bool(true, "显示信号") show_signals = input.bool(true, "显示信号")
// 计算移动平均线(中线) // 计算布林带(保持标准差计算)
bb_middle = ta.sma(close, bb_length) bb_basis = ta.ema(close, bb_length)
bb_dev = bb_mult * ta.stdev(close, bb_length)
bb_upper = bb_basis + bb_dev
bb_lower = bb_basis - bb_dev
// 计算ATR // 计算ATR(仅用于止损)
atr_value = ta.atr(atr_length) atr_value = ta.atr(atr_length)
// 计算上下轨
bb_upper = bb_middle + (atr_multiplier * atr_value)
bb_lower = bb_middle - (atr_multiplier * atr_value)
// 显示布林带 // 显示布林带
plot(show_bands ? bb_middle : na, "中线", color=color.blue, linewidth=2) plot(show_bands ? bb_basis : na, "中线", color=color.blue, linewidth=2)
plot(show_bands ? bb_upper : na, "上轨", color=color.red, linewidth=2) plot(show_bands ? bb_upper : na, "上轨", color=color.red, linewidth=2)
plot(show_bands ? bb_lower : na, "下轨", color=color.green, linewidth=2) plot(show_bands ? bb_lower : na, "下轨", color=color.green, linewidth=2)
// 交易条件 // 交易条件
// 做空条件:价格突破上轨 // 做空条件:价格突破上轨
short_condition = close > bb_upper and close[1] <= bb_upper[1] short_condition = close > bb_upper and close[1] <= bb_upper[1]
@@ -35,10 +33,10 @@ short_condition = close > bb_upper and close[1] <= bb_upper[1]
long_condition = close < bb_lower and close[1] >= bb_lower[1] long_condition = close < bb_lower and close[1] >= bb_lower[1]
// 做空止盈条件:价格跌破下轨 // 做空止盈条件:价格跌破下轨
short_take_profit = close < bb_lower short_take_profit = low < bb_lower
// 做多止盈条件:价格突破上轨 // 做多止盈条件:价格突破上轨
long_take_profit = close > bb_upper long_take_profit = close-atr_value*0.5 > bb_upper
// 记录入场价格和止损位 // 记录入场价格和止损位
var float long_entry_price = na var float long_entry_price = na
@@ -51,12 +49,12 @@ if strategy.position_size == 0
if long_condition if long_condition
strategy.entry("做多", strategy.long) strategy.entry("做多", strategy.long)
long_entry_price := close long_entry_price := close
long_stop_loss := close - (atr_stop_multiplier * atr_value) long_stop_loss := close - (atr_mult * atr_value)
if short_condition if short_condition
strategy.entry("做空", strategy.short) strategy.entry("做空", strategy.short)
short_entry_price := close short_entry_price := close
short_stop_loss := close + (atr_stop_multiplier * atr_value) short_stop_loss := close + (atr_mult * atr_value)
// 多头仓位管理 // 多头仓位管理
if strategy.position_size > 0 if strategy.position_size > 0
@@ -99,21 +97,20 @@ if show_signals
plot(strategy.position_size > 0 and not na(long_stop_loss) ? long_stop_loss : na, "多头止损", color=color.red, style=plot.style_linebr, linewidth=1) plot(strategy.position_size > 0 and not na(long_stop_loss) ? long_stop_loss : na, "多头止损", color=color.red, style=plot.style_linebr, linewidth=1)
plot(strategy.position_size < 0 and not na(short_stop_loss) ? short_stop_loss : na, "空头止损", color=color.red, style=plot.style_linebr, linewidth=1) plot(strategy.position_size < 0 and not na(short_stop_loss) ? short_stop_loss : na, "空头止损", color=color.red, style=plot.style_linebr, linewidth=1)
// 信息表格 // 信息表格
if barstate.islast if barstate.islast
var table info_table = table.new(position.top_right, 2, 10, bgcolor=color.white, border_width=1) var table info_table = table.new(position.top_right, 2, 10, bgcolor=color.white, border_width=1)
table.cell(info_table, 0, 0, "布林带ATR反转策略", text_color=color.black, bgcolor=color.gray) table.cell(info_table, 0, 0, "布林带反转策略", text_color=color.black, bgcolor=color.gray)
table.cell(info_table, 1, 0, "", text_color=color.black, bgcolor=color.gray) table.cell(info_table, 1, 0, "", text_color=color.black, bgcolor=color.gray)
table.cell(info_table, 0, 1, "布林带长度", text_color=color.black) table.cell(info_table, 0, 1, "布林带长度", text_color=color.black)
table.cell(info_table, 1, 1, str.tostring(bb_length), text_color=color.black) table.cell(info_table, 1, 1, str.tostring(bb_length), text_color=color.black)
table.cell(info_table, 0, 2, "ATR轨道乘数", text_color=color.black) table.cell(info_table, 0, 2, "布林带倍数", text_color=color.black)
table.cell(info_table, 1, 2, str.tostring(atr_multiplier), text_color=color.black) table.cell(info_table, 1, 2, str.tostring(bb_mult), text_color=color.black)
table.cell(info_table, 0, 3, "ATR止损数", text_color=color.black) table.cell(info_table, 0, 3, "ATR止损数", text_color=color.black)
table.cell(info_table, 1, 3, str.tostring(atr_stop_multiplier), text_color=color.black) table.cell(info_table, 1, 3, str.tostring(atr_mult), text_color=color.black)
table.cell(info_table, 0, 4, "当前ATR", text_color=color.black) table.cell(info_table, 0, 4, "当前ATR", text_color=color.black)
table.cell(info_table, 1, 4, str.tostring(math.round(atr_value, 4)), text_color=color.black) table.cell(info_table, 1, 4, str.tostring(math.round(atr_value, 4)), text_color=color.black)
+2 -2
View File
@@ -274,10 +274,10 @@ def add_indicators(df):
df['bb_upper'] = bb['upperband'].fillna(0) df['bb_upper'] = bb['upperband'].fillna(0)
df['bb_middle'] = bb['middleband'].fillna(0) df['bb_middle'] = bb['middleband'].fillna(0)
df['bb_lower'] = bb['lowerband'].fillna(0) df['bb_lower'] = bb['lowerband'].fillna(0)
bb30 = ta.BBANDS(df, timeperiod=90, nbdevup=3.0, nbdevdn=3.0, matype=0) bb30 = ta.BBANDS(df, timeperiod=41, nbdevup=2.3, nbdevdn=2.3, matype=0)
df['bbup30'] = bb30['upperband'].fillna(0) df['bbup30'] = bb30['upperband'].fillna(0)
df['bblow30'] = bb30['lowerband'].fillna(0) df['bblow30'] = bb30['lowerband'].fillna(0)
bb302 = ta.BBANDS(df, timeperiod=90, nbdevup=2.0, nbdevdn=2.0, matype=0) bb302 = ta.BBANDS(df, timeperiod=41, nbdevup=2.0, nbdevdn=2.0, matype=0)
df['bbup302'] = bb302['upperband'].fillna(0) df['bbup302'] = bb302['upperband'].fillna(0)
df['bblow302'] = bb302['lowerband'].fillna(0) df['bblow302'] = bb302['lowerband'].fillna(0)
# 计算次周期布林带 (14周期,2标准差) # 计算次周期布林带 (14周期,2标准差)