diff --git a/ChanKLC.py b/ChanKLC.py index 1a1e848..73c9bad 100644 --- a/ChanKLC.py +++ b/ChanKLC.py @@ -74,6 +74,7 @@ class ChanKLC(): self.bb_out = True if self.low <= klu.bblow30 and klu.bblow30 > 0: self.klc_fx_type = Chan_KLC_FX.BOTTOM4 + #self.bb_out = True def cal_indicators(self): for index in range(1, len(self.klus)): self.volume += self.klus[index].volume diff --git a/config/ChanLun_BTC_30.json b/config/ChanLun_BTC_30.json index 5ade8e0..d403952 100644 --- a/config/ChanLun_BTC_30.json +++ b/config/ChanLun_BTC_30.json @@ -58,7 +58,7 @@ } ], "telegram": { - "enabled": false, + "enabled": true, "token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y", "chat_id": "580807463" }, diff --git a/strategies/BB9033.py b/strategies/BB9033.py index 93ae3df..87e922f 100644 --- a/strategies/BB9033.py +++ b/strategies/BB9033.py @@ -5,8 +5,12 @@ 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 @@ -28,38 +32,37 @@ logger = logging.getLogger(__name__) class BB9033(IStrategy): """ - 布林带ATR反转策略 - 基于ATR动态调整布林带轨道,实现反转交易 + 布林带反转策略(与Pine Script保持一致) + 基于EMA和标准差计算布林带,实现反转交易 交易逻辑: - 做多:价格跌破下轨后反转 - 做空:价格突破上轨后反转 - - 止盈:价格触及对侧轨道 + - 做多止盈:价格减去0.5倍ATR突破上轨 + - 做空止盈:价格跌破下轨 - 止损:基于ATR动态设置 """ INTERFACE_VERSION: int = 3 - # 策略参数 - bb_length = 90 # 布林带长度 - atr_multiplier = 4.2 # ATR乘数(轨道) - atr_stop_multiplier = 1.8 # ATR乘数(止损) - atr_length = 14 # ATR计算周期 + # 策略参数(与Pine Script保持一致) + bb_length = 41 # 布林带长度 + atr_multiplier = 2.3 # 布林带倍数 + atr_stop_multiplier = 3 # 止损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 = { - "0": 0.5 } - + 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 - timeframe = '3m' - time = 30 + time = 5 # Trailing stop loss trailing_stop = False lev = 1.0 @@ -67,26 +70,27 @@ class BB9033(IStrategy): process_only_new_candles = False # 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] = {} def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ - 计算技术指标 + 计算技术指标(与Pine Script保持一致) """ dataframe_3 = resample_to_interval(dataframe, self.get_ticker_indicator() * self.time) # 计算ATR(用于止损计算) dataframe_3['atr'] = ta.ATR(dataframe_3, timeperiod=self.atr_length) - # 计算布林带(使用标准方法:移动平均线 ± 标准差倍数) - 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) - dataframe_3['bb_upper'] = bb_upper - dataframe_3['bb_lower'] = bb_lower - for i in range(1700, 1800): - print(dataframe_3.iloc[i]) + # 计算布林带(使用EMA作为基础,与Pine Script保持一致) + bb_basis = ta.EMA(dataframe_3['close'], timeperiod=self.bb_length) + bb_dev = self.atr_multiplier * ta.STDDEV(dataframe_3['close'], timeperiod=self.bb_length) + dataframe_3['bb_upper'] = bb_basis + bb_dev + dataframe_3['bb_middle'] = bb_basis + dataframe_3['bb_lower'] = bb_basis - bb_dev + # 计算突破条件(与Pine Script保持一致) # 确保所有用于计算的数据都不是NaN valid_data = ( @@ -98,16 +102,20 @@ class BB9033(IStrategy): dataframe_3['bb_lower'].shift(1).notna() ) + # 做空条件:价格突破上轨 dataframe_3['break_above_upper'] = ( (dataframe_3['close'] > dataframe_3['bb_upper']) & (dataframe_3['close'].shift(1) <= dataframe_3['bb_upper'].shift(1)) & valid_data ) + + # 做多条件:价格跌破下轨 dataframe_3['break_below_lower'] = ( (dataframe_3['close'] < dataframe_3['bb_lower']) & (dataframe_3['close'].shift(1) >= dataframe_3['bb_lower'].shift(1)) & valid_data ) + dataframe = resampled_merge(dataframe, dataframe_3) return dataframe @@ -118,23 +126,25 @@ class BB9033(IStrategy): 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) - # 做多条件:价格跌破下轨 + # 检测多头信号:价格跌破下轨 dataframe.loc[ ( - (dataframe[break_below_lower] == True) & # 价格跌破下轨,明确检查True值 - (dataframe[break_below_lower].notna()) # 确保不是NaN + (dataframe[break_below_lower] == True) & + (pd.notna(dataframe[break_below_lower])) ), - 'enter_long'] = 1 + ['enter_long', 'enter_tag']] = (1, 'long_signal_chan') - # 做空条件:价格突破上轨 + # 检测空头信号:价格突破上轨 dataframe.loc[ ( - (dataframe[break_above_upper] == True) & # 价格突破上轨,明确检查True值 - (dataframe[break_above_upper].notna()) # 确保不是NaN + (dataframe[break_above_upper] == True) & + (pd.notna(dataframe[break_above_upper])) ), - 'enter_short'] = 1 + ['enter_short', 'enter_tag']] = (1, 'short_signal_chan') return 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_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['close'] > dataframe[bb_upper_str]) & # 当前价格突破上轨 - (dataframe[bb_upper_str].notna()) & # 确保布林带上轨不是NaN - (dataframe['close'].notna()) # 确保收盘价不是NaN + (dataframe[close_str] > dataframe[bb_upper_str]) & # close-atr_value*0.5 > bb_upper + (dataframe[bb_upper_str].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['close'] < dataframe[bb_lower_str]) & # 当前价格跌破下轨 - (dataframe[bb_lower_str].notna()) & # 确保布林带下轨不是NaN - (dataframe['close'].notna()) # 确保收盘价不是NaN + (dataframe[low_str] < dataframe[bb_lower_str]) & # low < bb_lower + (dataframe[bb_lower_str].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 @@ -180,8 +196,9 @@ class BB9033(IStrategy): if len(dataframe) < 2: return self.stoploss - # 使用上一个K线的收盘价 - last_close = dataframe.iloc[-2]['close'] # 上一个完整K线的收盘价 + # 使用上一个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] @@ -266,7 +283,7 @@ class BB9033(IStrategy): 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}") + 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: """ diff --git a/strategies/BB90331.py b/strategies/BB90331.py new file mode 100644 index 0000000..c947f88 --- /dev/null +++ b/strategies/BB90331.py @@ -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]) \ No newline at end of file diff --git a/strategies/bollinger_atr_strategy.pine b/strategies/bollinger_atr_strategy.pine index 6f6decb..6ce2391 100644 --- a/strategies/bollinger_atr_strategy.pine +++ b/strategies/bollinger_atr_strategy.pine @@ -1,32 +1,30 @@ -//@version=6 -strategy("布林带ATR反转策略", shorttitle="BBB_ATR", overlay=true, default_qty_type=strategy.percent_of_equity, default_qty_value=100, calc_on_every_tick=true) +//@version=5 +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) -atr_multiplier = input.float(3.0, "ATR乘数(轨道)", 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(9, "ATR计算周期", minval=5, maxval=50) +bb_length = input.int(41, "布林带长度", minval=10, maxval=200) +bb_mult = input.float(2.3, "布林带倍数", minval=1.0, maxval=10.0, step=0.1) +atr_length = input.int(11, "ATR计算周期", minval=5, maxval=90) +atr_mult = input.float(3, "止损ATR倍数", minval=0.5, maxval=10.0, step=0.1) // 显示设置 show_bands = 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) -// 计算上下轨 -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_lower : na, "下轨", color=color.green, linewidth=2) - // 交易条件 // 做空条件:价格突破上轨 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] // 做空止盈条件:价格跌破下轨 -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 @@ -51,12 +49,12 @@ if strategy.position_size == 0 if long_condition strategy.entry("做多", strategy.long) long_entry_price := close - long_stop_loss := close - (atr_stop_multiplier * atr_value) + long_stop_loss := close - (atr_mult * atr_value) if short_condition strategy.entry("做空", strategy.short) 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 @@ -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(short_stop_loss) ? short_stop_loss : na, "空头止损", color=color.red, style=plot.style_linebr, linewidth=1) - // 信息表格 if barstate.islast 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, 0, 1, "布林带长度", 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, 1, 2, str.tostring(atr_multiplier), text_color=color.black) + table.cell(info_table, 0, 2, "布林带倍数", 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, 1, 3, str.tostring(atr_stop_multiplier), text_color=color.black) + table.cell(info_table, 0, 3, "ATR止损倍数", 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, 1, 4, str.tostring(math.round(atr_value, 4)), text_color=color.black) diff --git a/web/app.py b/web/app.py index c8b4e92..af9889b 100644 --- a/web/app.py +++ b/web/app.py @@ -274,10 +274,10 @@ def add_indicators(df): df['bb_upper'] = bb['upperband'].fillna(0) df['bb_middle'] = bb['middleband'].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['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['bblow302'] = bb302['lowerband'].fillna(0) # 计算次周期布林带 (14周期,2标准差)