NFI策略回测指导

Creado el: 14 de noviembre de 2024

Respondido usando O1 Preview por Chat01

Pregunta

您是Freqtrade加密货币交易系统专家,现在请你:
1、详细分析NostalgiaForInfinity策略的运行逻辑和功能
2、我用的是WIN10系统,已安装了Docker、VSCode,现在请你详细指导我将NostalgiaForInfinity策略能过Docder安装,通过创建VSCode的开发容器来运行回测该策略
3、请写出回测该策略的详细步骤,谢谢

Pensamiento

分析策略

首先,我在研究 NostalgiaForInfinity 策略,详细理解其逻辑和功能。然后,我引导在 WIN10 系统上使用 Docker 安装策略,并在 VSCode 中跑回测。

分析隐网技术

我正在详细探讨隐网技术的工作逻辑和功能,计划通过Docker和VSCode在Windows 10系统上实现部署,并逐步进行策略回测设置。

解析NFI战略

NostalgiaForInfinity(NFI)是一种复杂的交易策略,结合多个指标和条件生成买卖信号。接下来,我将详细分析其运营逻辑和功能。

了解NFI策略

NostalgiaForInfinity,简称NFI,是一种复杂的交易策略,专为Freqtrade设计,旨在加密货币交易中实现高性能。

整合技术指标

NFI策略结合RSI、MACD、Bollinger Bands和EMA等多个技术指标,从而生成买入和卖出信号。这些指标的综合使用,有助于更准确地预测市场动向。

探测市场动态

  1. 判断超买/超卖、趋势确认和波动性水平。
  2. 配置风险管理功能:止损、止盈和跟踪止损。
  3. 定制策略参数,适应不同风险容忍度和交易风格。
  4. 买入逻辑:寻找牛市信号,如超卖RSI、移动平均线交叉和止损进场。
  5. 卖出逻辑:寻找熊市信号或利润实现机会,如超买RSI、移动平均线交叉和止盈水平。
  6. 实施保护措施:自动止损、调整止损级别和等待期。

正在设置容器

在VSCode中安装Remote - Containers和Docker扩展,打开克隆的Freqtrade文件夹,为Docker容器配置做准备。

设置开发环境
在VSCode中按F1,输入Remote-Containers: Reopen in Container,选择它来构建并打开.devcontainer文件夹定义的开发容器。

创建配置文件

我开始创建一个新的配置文件,并导航到 freqtrade 目录。然后, 我使用命令行命令生成了 user_data/config.json 文件。

编辑配置文件

我打开了 user_data/config.json 文件,设置了各种参数,如最大打开交易量、资金币种、资金额度、显示货币和时间框架等。

下载历史数据

使用命令行命令下载了 Binance 上所有 USDT 货币对的 5 分钟时间框架的历史数据。

运行回测

执行了回测命令,生成了详细的回测报告。

分析性能

我使用freqtrade backtesting --export trades命令导出交易数据以进行进一步分析,同时生成相关图表。

提升指标

打开 nostalgiaforinfinity.py 文件,调整参数以定制策略。继续进行回测,使用命令 freqtrade backtesting 并指定时间范围。考察超参数优化结果,进一步细化参数。endant

Respuesta

1. 详细分析 NostalgiaForInfinity 策略的运行逻辑和功能

概述

NostalgiaForInfinity(以下简称 NFI)是一个为 Freqtrade 量身打造的复杂且高性能的加密货币交易策略。该策略以其出色的回测和实时交易表现而闻名,广泛应用于加密货币自动化交易中。NFI 策略综合运用了多种技术指标和交易技巧,旨在在不同的市场条件下获取最佳收益。

核心组件

  • 技术指标:NFI 策略使用了多种技术指标,包括但不限于:

    • 相对强弱指数(RSI)
    • 移动平均线(MA)和指数移动平均线(EMA)
    • 布林带(Bollinger Bands)
    • 移动平均线收敛散度(MACD)
    • 随机指标(Stochastic)
  • 信号生成:通过对这些指标的综合分析,策略生成买入和卖出信号。

    • 买入信号:当市场出现超卖、趋势反转或突破等情况时,策略会生成买入信号。
    • 卖出信号:当市场出现超买、趋势疲软或达到预设盈利目标时,策略会生成卖出信号。
  • 风险管理

    • 止损(Stop-Loss):设置最大可承受的损失范围,防止亏损扩大。
    • 止盈(Take-Profit):预设盈利目标,锁定收益。
    • 追踪止损(Trailing Stop):在价格朝有利方向移动时,动态调整止损位,保护已获得的利润。

运行逻辑

  • 市场分析:策略首先对市场进行多层次分析,包括趋势、波动性和动量。
  • 条件筛选:基于技术指标设定的条件,筛选出符合交易标准的币种。
  • 信号确认:在生成交易信号前,策略会通过多个指标的交叉验证来确认信号的可靠性。
  • 仓位管理:策略根据账户余额和风险偏好,确定每笔交易的仓位大小。
  • 订单执行:一旦满足交易条件,策略会自动在交易所下单。

功能特点

  • 多市场适应性:NFI 策略设计用于在不同的市场条件下运行,包括震荡市和趋势市。
  • 高度可定制化:用户可以根据自身需求调整策略参数,如指标周期、阈值和风险控制参数。
  • 自动化程度高:策略全程自动运行,包括数据获取、信号生成、订单执行和风险管理。
  • 数据驱动:通过历史数据的回测和优化,不断提升策略的表现。

2. 通过 Docker 和 VSCode 开发容器安装 NostalgiaForInfinity 策略并运行回测的详细指导

前提条件

  • Windows 10 系统
  • 已安装 Docker Desktop
  • 已安装 Visual Studio Code(VSCode)
  • 已安装 Git

步骤如下:

A. 安装必要的 VSCode 扩展

  1. 打开 VSCode

  2. 安装 Remote - Containers 扩展

    • 点击左侧的扩展图标(四个方块组成的图标),或者按 Ctrl+Shift+X
    • 在搜索栏输入 Remote - Containers,选择由 Microsoft 发布的扩展并点击安装。
  3. 安装 Docker 扩展(可选,但推荐):

    • 同样在扩展市场中搜索 Docker,安装由 Microsoft 发布的扩展。

B. 克隆 Freqtrade 项目

  1. 打开终端

    • 可以在 VSCode 中打开终端,或者使用 Windows 命令提示符/PowerShell。
  2. 克隆仓库

    bash
    git clone https://github.com/freqtrade/freqtrade.git
  3. 进入项目目录

    bash
    cd freqtrade

C. 设置 VSCode 开发容器

  1. 在 VSCode 中打开项目文件夹

    • 点击 文件 > 打开文件夹,选择刚才克隆的 freqtrade 文件夹。
  2. 使用开发容器重新打开项目

    • F1 调出命令面板,输入 Remote-Containers: Reopen in Container,选择该选项。
    • VSCode 会自动根据项目中的 .devcontainer 配置构建并启动开发容器。

D. 安装 NostalgiaForInfinity 策略

  1. 下载策略文件

  2. 将策略文件复制到策略目录

    • 在 VSCode 中,找到并打开 user_data/strategies 文件夹。
    • nostalgiaforinfinity.py 文件复制到该文件夹中。
  3. 安装策略依赖

    • 在 VSCode 的终端中,执行以下命令安装策略所需的 Python 包:

      bash
      pip install -r requirements.txt
    • 如果策略有额外的依赖(如特定的技术指标库),需要单独安装,例如:

      bash
      pip install pandas_ta ta-lib

E. 配置 Freqtrade

  1. 创建配置文件

    • 在终端中,运行以下命令生成默认配置文件:

      bash
      freqtrade new-config --config user_data/config.json
  2. 编辑配置文件

    • 打开 user_data/config.json,根据需要修改配置,例如:

      json
      { "max_open_trades": 10, "stake_currency": "USDT", "stake_amount": "unlimited", "dry_run": true, "timeframe": "5m", "strategy": "NostalgiaForInfinity", "exchange": { "name": "binance", "key": "", "secret": "", "ccxt_config": {}, "ccxt_async_config": {}, "pair_whitelist": ["BTC/USDT", "ETH/USDT"], "pair_blacklist": ["USD/USDT"] } }

F. 下载历史数据

  1. 运行数据下载命令

    bash
    freqtrade download-data --exchange binance --basecurrencies USDT --timeframes 5m
    • 根据策略使用的时间框架(如 5m、15m)下载对应的数据。

G. 运行回测

  1. 执行回测命令

    bash
    freqtrade backtesting --config user_data/config.json --strategy NostalgiaForInfinity
  2. 查看回测结果

    • 回测完成后,终端会显示交易绩效指标。
    • 详细的回测报告保存在 backtest_results 文件夹中。

3. 回测 NostalgiaForInfinity 策略的详细步骤

步骤 1:确保环境准备就绪

  • Docker Desktop 已运行。
  • VSCode 已打开,并且在开发容器中。
  • NostalgiaForInfinity 策略文件已放置在 user_data/strategies 目录中。
  • 必要的依赖已安装。

步骤 2:配置策略和参数

  • 打开 nostalgiaforinfinity.py,根据需要调整策略参数,如指标周期、阈值等。
  • 确保 config.json 中的设置与策略要求一致。

步骤 3:下载历史数据

  • 根据策略的时间框架下载数据,例如:

    bash
    freqtrade download-data --exchange binance --basecurrencies USDT --timeframes 5m 15m 1h

步骤 4:运行回测

  • 在终端中执行回测命令,指定时间范围:

    bash
    freqtrade backtesting --config user_data/config.json --strategy NostalgiaForInfinity --timerange=20210101-20211231
    • --timerange 参数指定回测的日期范围(格式:YYYYMMDD-YYYYMMDD)。

步骤 5:分析回测结果

  • 回测完成后,查看终端输出的绩效指标,如净利润、胜率、夏普比率等。
  • backtest_results 文件夹中,查看详细的回测报告和交易记录。

步骤 6:生成交易图表(可选)

  • 运行以下命令生成交易图表,便于可视化分析:

    bash
    freqtrade plot-dataframe --strategy NostalgiaForInfinity --timerange=20210101-20211231
    • 图表保存在 user_data/plot 文件夹中。

步骤 7:优化策略(可选)

  • 使用 Hyperopt 功能优化策略参数:

    bash
    freqtrade hyperopt --strategy NostalgiaForInfinity --hyperopt-loss SharpeHyperOptLoss --timeframe 5m --timerange=20210101-20211231
  • 根据优化结果,调整策略参数并重新回测。

步骤 8:重复测试

  • 通过调整策略参数和配置,重复回测流程,寻求最佳的策略表现。

提示

  • 保持干跑模式(dry_run)开启:在测试阶段,不要进行真实交易。
  • 注意风险管理:确保策略的风险控制参数符合您的风险偏好。
  • 记录测试结果:每次回测后,记录结果以便比较和分析。

希望以上指导能够帮助您成功安装和回测 NostalgiaForInfinity 策略。如有任何疑问,请随时提问。祝您交易顺利!

Pregunta

for live trailing_stop = False and use_custom_stoploss = True

for backtest trailing_stop = True and use_custom_stoploss = False

--- Do not remove these libs ---

--- Do not remove these libs ---

from logging import FATAL
from freqtrade.strategy.interface import IStrategy
from typing import Dict, List
from functools import reduce
from pandas import DataFrame

--------------------------------

import talib.abstract as ta
import numpy as np
import freqtrade.vendor.qtpylib.indicators as qtpylib
import datetime
from technical.util import resample_to_interval, resampled_merge
from datetime import datetime, timedelta
from freqtrade.persistence import Trade
from freqtrade.strategy import stoploss_from_open, merge_informative_pair, DecimalParameter, IntParameter, CategoricalParameter
import technical.indicators as ftt

@Rallipanos

@pluxury

Buy hyperspace params:

buy_params = {
"base_nb_candles_buy": 8,
"ewo_high": 2.403,
"ewo_high_2": -5.585,
"ewo_low": -14.378,
"lookback_candles": 3,
"low_offset": 0.984,
"low_offset_2": 0.942,
"profit_threshold": 1.008,
"rsi_buy": 72
}

Sell hyperspace params:

sell_params = {
"base_nb_candles_sell": 16,
"high_offset": 1.084,
"high_offset_2": 1.401,
"pHSL": -0.15,
"pPF_1": 0.016,
"pPF_2": 0.024,
"pSL_1": 0.014,
"pSL_2": 0.022
}

def EWO(dataframe, ema_length=5, ema2_length=35):
df = dataframe.copy()
ema1 = ta.EMA(df, timeperiod=ema_length)
ema2 = ta.EMA(df, timeperiod=ema2_length)
emadif = (ema1 - ema2) / df['low'] * 100
return emadif

class NASOSv4(IStrategy):
INTERFACE_VERSION = 2

text
# ROI table: minimal_roi = { # "0": 0.283, # "40": 0.086, # "99": 0.036, "0": 10 } # Stoploss: stoploss = -0.15 # SMAOffset base_nb_candles_buy = IntParameter( 2, 20, default=buy_params['base_nb_candles_buy'], space='buy', optimize=True) base_nb_candles_sell = IntParameter( 2, 25, default=sell_params['base_nb_candles_sell'], space='sell', optimize=True) low_offset = DecimalParameter( 0.9, 0.99, default=buy_params['low_offset'], space='buy', optimize=False) low_offset_2 = DecimalParameter( 0.9, 0.99, default=buy_params['low_offset_2'], space='buy', optimize=False) high_offset = DecimalParameter( 0.95, 1.1, default=sell_params['high_offset'], space='sell', optimize=True) high_offset_2 = DecimalParameter( 0.99, 1.5, default=sell_params['high_offset_2'], space='sell', optimize=True) # Protection fast_ewo = 50 slow_ewo = 200 lookback_candles = IntParameter( 1, 24, default=buy_params['lookback_candles'], space='buy', optimize=True) profit_threshold = DecimalParameter(1.0, 1.03, default=buy_params['profit_threshold'], space='buy', optimize=True) ewo_low = DecimalParameter(-20.0, -8.0, default=buy_params['ewo_low'], space='buy', optimize=False) ewo_high = DecimalParameter( 2.0, 12.0, default=buy_params['ewo_high'], space='buy', optimize=False) ewo_high_2 = DecimalParameter( -6.0, 12.0, default=buy_params['ewo_high_2'], space='buy', optimize=False) rsi_buy = IntParameter(50, 100, default=buy_params['rsi_buy'], space='buy', optimize=False) # trailing stoploss hyperopt parameters # hard stoploss profit pHSL = DecimalParameter(-0.200, -0.040, default=-0.15, decimals=3, space='sell', optimize=False, load=True) # profit threshold 1, trigger point, SL_1 is used pPF_1 = DecimalParameter(0.008, 0.020, default=0.016, decimals=3, space='sell', optimize=False, load=True) pSL_1 = DecimalParameter(0.008, 0.020, default=0.014, decimals=3, space='sell', optimize=False, load=True) # profit threshold 2, SL_2 is used pPF_2 = DecimalParameter(0.040, 0.100, default=0.024, decimals=3, space='sell', optimize=False, load=True) pSL_2 = DecimalParameter(0.020, 0.070, default=0.022, decimals=3, space='sell', optimize=False, load=True) # Trailing stop: trailing_stop = True trailing_stop_positive = 0.001 trailing_stop_positive_offset = 0.016 trailing_only_offset_is_reached = True # Sell signal use_sell_signal = True sell_profit_only = False sell_profit_offset = 0.01 ignore_roi_if_buy_signal = False # Optional order time in force. order_time_in_force = { 'buy': 'gtc', 'sell': 'ioc' } # Optimal timeframe for the strategy timeframe = '5m' inf_1h = '1h' process_only_new_candles = True startup_candle_count = 200 use_custom_stoploss = False plot_config = { 'main_plot': { 'ma_buy': {'color': 'orange'}, 'ma_sell': {'color': 'orange'}, }, } slippage_protection = { 'retries': 3, 'max_slippage': -0.02 } # Custom Trailing Stoploss by Perkmeister def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float: # # hard stoploss profit HSL = self.pHSL.value PF_1 = self.pPF_1.value SL_1 = self.pSL_1.value PF_2 = self.pPF_2.value SL_2 = self.pSL_2.value # For profits between PF_1 and PF_2 the stoploss (sl_profit) used is linearly interpolated # between the values of SL_1 and SL_2. For all profits above PL_2 the sl_profit value # rises linearly with current profit, for profits below PF_1 the hard stoploss profit is used. if (current_profit > PF_2): sl_profit = SL_2 + (current_profit - PF_2) elif (current_profit > PF_1): sl_profit = SL_1 + ((current_profit - PF_1)*(SL_2 - SL_1)/(PF_2 - PF_1)) else: sl_profit = HSL # if current_profit < 0.001 and current_time - timedelta(minutes=600) > trade.open_date_utc: # return -0.005 return stoploss_from_open(sl_profit, current_profit) def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float, rate: float, time_in_force: str, sell_reason: str, current_time: datetime, **kwargs) -> bool: dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) last_candle = dataframe.iloc[-1] if (last_candle is not None): if (sell_reason in ['sell_signal']): if (last_candle['hma_50']*1.149 > last_candle['ema_100']) and (last_candle['close'] < last_candle['ema_100']*0.951): # *1.2 return False # slippage try: state = self.slippage_protection['__pair_retries'] except KeyError: state = self.slippage_protection['__pair_retries'] = {} candle = dataframe.iloc[-1].squeeze() slippage = (rate / candle['close']) - 1 if slippage < self.slippage_protection['max_slippage']: pair_retries = state.get(pair, 0) if pair_retries < self.slippage_protection['retries']: state[pair] = pair_retries + 1 return False state[pair] = 0 return True def informative_pairs(self): pairs = self.dp.current_whitelist() informative_pairs = [(pair, '1h') for pair in pairs] return informative_pairs def informative_1h_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: assert self.dp, "DataProvider is required for multiple timeframes." # Get the informative pair informative_1h = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.inf_1h) # EMA # informative_1h['ema_50'] = ta.EMA(informative_1h, timeperiod=50) # informative_1h['ema_200'] = ta.EMA(informative_1h, timeperiod=200) # # RSI # informative_1h['rsi'] = ta.RSI(informative_1h, timeperiod=14) # bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) # informative_1h['bb_lowerband'] = bollinger['lower'] # informative_1h['bb_middleband'] = bollinger['mid'] # informative_1h['bb_upperband'] = bollinger['upper'] return informative_1h def normal_tf_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # Calculate all ma_buy values for val in self.base_nb_candles_buy.range: dataframe[f'ma_buy_{val}'] = ta.EMA(dataframe, timeperiod=val) # Calculate all ma_sell values for val in self.base_nb_candles_sell.range: dataframe[f'ma_sell_{val}'] = ta.EMA(dataframe, timeperiod=val) dataframe['hma_50'] = qtpylib.hull_moving_average(dataframe['close'], window=50) dataframe['ema_100'] = ta.EMA(dataframe, timeperiod=100) dataframe['sma_9'] = ta.SMA(dataframe, timeperiod=9) # Elliot dataframe['EWO'] = EWO(dataframe, self.fast_ewo, self.slow_ewo) # RSI dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) dataframe['rsi_fast'] = ta.RSI(dataframe, timeperiod=4) dataframe['rsi_slow'] = ta.RSI(dataframe, timeperiod=20) return dataframe def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: informative_1h = self.informative_1h_indicators(dataframe, metadata) dataframe = merge_informative_pair( dataframe, informative_1h, self.timeframe, self.inf_1h, ffill=True) # The indicators for the normal (5m) timeframe dataframe = self.normal_tf_indicators(dataframe, metadata) return dataframe def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dont_buy_conditions = [] dont_buy_conditions.append( ( # don't buy if there isn't 3% profit to be made (dataframe['close_1h'].rolling(self.lookback_candles.value).max() < (dataframe['close'] * self.profit_threshold.value)) ) ) dataframe.loc[ ( (dataframe['rsi_fast'] < 35) & (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) & (dataframe['EWO'] > self.ewo_high.value) & (dataframe['rsi'] < self.rsi_buy.value) & (dataframe['volume'] > 0) & (dataframe['close'] < ( dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) ), ['buy', 'buy_tag']] = (1, 'ewo1') dataframe.loc[ ( (dataframe['rsi_fast'] < 35) & (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset_2.value)) & (dataframe['EWO'] > self.ewo_high_2.value) & (dataframe['rsi'] < self.rsi_buy.value) & (dataframe['volume'] > 0) & (dataframe['close'] < (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) & (dataframe['rsi'] < 25) ), ['buy', 'buy_tag']] = (1, 'ewo2') dataframe.loc[ ( (dataframe['rsi_fast'] < 35) & (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) & (dataframe['EWO'] < self.ewo_low.value) & (dataframe['volume'] > 0) & (dataframe['close'] < ( dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) ), ['buy', 'buy_tag']] = (1, 'ewolow') if dont_buy_conditions: for condition in dont_buy_conditions: dataframe.loc[condition, 'buy'] = 0 return dataframe def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = [] conditions.append( ((dataframe['close'] > dataframe['sma_9']) & (dataframe['close'] > (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset_2.value)) & (dataframe['rsi'] > 50) & (dataframe['volume'] > 0) & (dataframe['rsi_fast'] > dataframe['rsi_slow']) ) | ( (dataframe['close'] < dataframe['hma_50']) & (dataframe['close'] > (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) & (dataframe['volume'] > 0) & (dataframe['rsi_fast'] > dataframe['rsi_slow']) ) ) if conditions: dataframe.loc[ reduce(lambda x, y: x | y, conditions), 'sell' ]=1 return dataframe

1、请用你的专业知识用中文详细注释以上代码
2、请用你的专业知识详细分析以上代码的交易逻辑、指标生成计算方法等
3、请以你的专业知识指导我用freqtrade回测以上策略,谢谢

Pensamiento

注释代码、解析逻辑、指导回测

我正在为用户提供帮助,包括将代码注释为中文、分析交易逻辑和指标生成方法,以及指导如何用freqtrade回测策略。

确保及时沟通

在进行代码讲解和策略分析时,为每个部分提供详细的中文注释。确保操作的每一步都清晰明了。

Respuesta

1. 用中文详细注释以上代码

python
# for live trailing_stop = False and use_custom_stoploss = True # for backtest trailing_stop = True and use_custom_stoploss = False # --- 不要删除这些库 --- from logging import FATAL from freqtrade.strategy.interface import IStrategy from typing import Dict, List from functools import reduce from pandas import DataFrame # ----------------------- import talib.abstract as ta import numpy as np import freqtrade.vendor.qtpylib.indicators as qtpylib import datetime from technical.util import resample_to_interval, resampled_merge from datetime import datetime, timedelta from freqtrade.persistence import Trade from freqtrade.strategy import stoploss_from_open, merge_informative_pair, DecimalParameter, IntParameter, CategoricalParameter import technical.indicators as ftt # 策略作者 # @Rallipanos # @pluxury # 买入参数(经过超参数优化) buy_params = { "base_nb_candles_buy": 8, # 买入时使用的基础K线数量 "ewo_high": 2.403, # EWO指标高值 "ewo_high_2": -5.585, # EWO指标高值(第二种情况) "ewo_low": -14.378, # EWO指标低值 "lookback_candles": 3, # 回溯的K线数量 "low_offset": 0.984, # 低价偏移量 "low_offset_2": 0.942, # 低价偏移量(第二种情况) "profit_threshold": 1.008, # 盈利阈值 "rsi_buy": 72 # RSI买入阈值 } # 卖出参数(经过超参数优化) sell_params = { "base_nb_candles_sell": 16, # 卖出时使用的基础K线数量 "high_offset": 1.084, # 高价偏移量 "high_offset_2": 1.401, # 高价偏移量(第二种情况) "pHSL": -0.15, # 硬止损值 "pPF_1": 0.016, # 盈利阈值1 "pPF_2": 0.024, # 盈利阈值2 "pSL_1": 0.014, # 止盈值1 "pSL_2": 0.022 # 止盈值2 } # 定义EWO指标函数 def EWO(dataframe, ema_length=5, ema2_length=35): df = dataframe.copy() ema1 = ta.EMA(df, timeperiod=ema_length) # 计算短期EMA ema2 = ta.EMA(df, timeperiod=ema2_length) # 计算长期EMA emadif = (ema1 - ema2) / df['low'] * 100 # 计算EWO值 return emadif # 定义策略类 class NASOSv4(IStrategy): INTERFACE_VERSION = 2 # 接口版本 # 最小回报率 minimal_roi = { "0": 10 # 设置一个高值,实际上在代码中未使用ROI } # 止损 stoploss = -0.15 # 最大止损为15% # 定义参数,供优化使用 base_nb_candles_buy = IntParameter( 2, 20, default=buy_params['base_nb_candles_buy'], space='buy', optimize=True) base_nb_candles_sell = IntParameter( 2, 25, default=sell_params['base_nb_candles_sell'], space='sell', optimize=True) low_offset = DecimalParameter( 0.9, 0.99, default=buy_params['low_offset'], space='buy', optimize=False) low_offset_2 = DecimalParameter( 0.9, 0.99, default=buy_params['low_offset_2'], space='buy', optimize=False) high_offset = DecimalParameter( 0.95, 1.1, default=sell_params['high_offset'], space='sell', optimize=True) high_offset_2 = DecimalParameter( 0.99, 1.5, default=sell_params['high_offset_2'], space='sell', optimize=True) # 保护参数 fast_ewo = 50 # 快速EWO周期 slow_ewo = 200 # 慢速EWO周期 lookback_candles = IntParameter( 1, 24, default=buy_params['lookback_candles'], space='buy', optimize=True) profit_threshold = DecimalParameter(1.0, 1.03, default=buy_params['profit_threshold'], space='buy', optimize=True) ewo_low = DecimalParameter(-20.0, -8.0, default=buy_params['ewo_low'], space='buy', optimize=False) ewo_high = DecimalParameter( 2.0, 12.0, default=buy_params['ewo_high'], space='buy', optimize=False) ewo_high_2 = DecimalParameter( -6.0, 12.0, default=buy_params['ewo_high_2'], space='buy', optimize=False) rsi_buy = IntParameter(50, 100, default=buy_params['rsi_buy'], space='buy', optimize=False) # 自定义追踪止损参数 pHSL = DecimalParameter(-0.200, -0.040, default=-0.15, decimals=3, space='sell', optimize=False, load=True) pPF_1 = DecimalParameter(0.008, 0.020, default=0.016, decimals=3, space='sell', optimize=False, load=True) pSL_1 = DecimalParameter(0.008, 0.020, default=0.014, decimals=3, space='sell', optimize=False, load=True) pPF_2 = DecimalParameter(0.040, 0.100, default=0.024, decimals=3, space='sell', optimize=False, load=True) pSL_2 = DecimalParameter(0.020, 0.070, default=0.022, decimals=3, space='sell', optimize=False, load=True) # 追踪止损 trailing_stop = True trailing_stop_positive = 0.001 trailing_stop_positive_offset = 0.016 trailing_only_offset_is_reached = True # 卖出信号 use_sell_signal = True sell_profit_only = False sell_profit_offset = 0.01 ignore_roi_if_buy_signal = False # 订单有效时间 order_time_in_force = { 'buy': 'gtc', # Good till cancelled 'sell': 'ioc' # Immediate or cancel } # 策略的时间框架 timeframe = '5m' # 5分钟K线 inf_1h = '1h' # 1小时K线(用于获取信息) process_only_new_candles = True # 仅处理新蜡烛 startup_candle_count = 200 # 启动时需要的最小蜡烛数 use_custom_stoploss = False # 不使用自定义止损(用于回测) # 绘图配置 plot_config = { 'main_plot': { 'ma_buy': {'color': 'orange'}, 'ma_sell': {'color': 'orange'}, }, } # 滑点保护配置 slippage_protection = { 'retries': 3, # 重试次数 'max_slippage': -0.02 # 最大允许滑点 } # 自定义止损函数(由Perkmeister提供) def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float: # 获取止损参数 HSL = self.pHSL.value # 硬止损 PF_1 = self.pPF_1.value # 盈利阈值1 SL_1 = self.pSL_1.value # 止盈值1 PF_2 = self.pPF_2.value # 盈利阈值2 SL_2 = self.pSL_2.value # 止盈值2 # 根据当前利润计算动态止损 if (current_profit > PF_2): sl_profit = SL_2 + (current_profit - PF_2) elif (current_profit > PF_1): sl_profit = SL_1 + ((current_profit - PF_1)*(SL_2 - SL_1)/(PF_2 - PF_1)) else: sl_profit = HSL # 使用硬止损 # 返回从开仓价计算的止损价格 return stoploss_from_open(sl_profit, current_profit) # 确认交易退出 def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float, rate: float, time_in_force: str, sell_reason: str, current_time: datetime, **kwargs) -> bool: # 获取当前数据 dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) last_candle = dataframe.iloc[-1] if (last_candle is not None): if (sell_reason in ['sell_signal']): # 如果满足特定条件,不卖出 if (last_candle['hma_50']*1.149 > last_candle['ema_100']) and (last_candle['close'] < last_candle['ema_100']*0.951): return False # 滑点保护逻辑 try: state = self.slippage_protection['__pair_retries'] except KeyError: state = self.slippage_protection['__pair_retries'] = {} candle = dataframe.iloc[-1].squeeze() slippage = (rate / candle['close']) - 1 if slippage < self.slippage_protection['max_slippage']: pair_retries = state.get(pair, 0) if pair_retries < self.slippage_protection['retries']: state[pair] = pair_retries + 1 return False # 不退出交易 state[pair] = 0 # 重置重试计数 return True # 允许退出交易 # 定义需要的附加数据对(信息性数据) def informative_pairs(self): pairs = self.dp.current_whitelist() informative_pairs = [(pair, '1h') for pair in pairs] # 1小时数据 return informative_pairs # 计算1小时时间框架的指标 def informative_1h_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: assert self.dp, "需要DataProvider来获取多时间框架数据。" informative_1h = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.inf_1h) return informative_1h # 计算正常时间框架的指标 def normal_tf_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # 计算所有买入移动平均线 for val in self.base_nb_candles_buy.range: dataframe[f'ma_buy_{val}'] = ta.EMA(dataframe, timeperiod=val) # 计算所有卖出移动平均线 for val in self.base_nb_candles_sell.range: dataframe[f'ma_sell_{val}'] = ta.EMA(dataframe, timeperiod=val) dataframe['hma_50'] = qtpylib.hull_moving_average(dataframe['close'], window=50) dataframe['ema_100'] = ta.EMA(dataframe, timeperiod=100) dataframe['sma_9'] = ta.SMA(dataframe, timeperiod=9) # 计算EWO指标 dataframe['EWO'] = EWO(dataframe, self.fast_ewo, self.slow_ewo) # 计算RSI指标 dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) dataframe['rsi_fast'] = ta.RSI(dataframe, timeperiod=4) dataframe['rsi_slow'] = ta.RSI(dataframe, timeperiod=20) return dataframe # 填充指标数据 def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # 获取并合并1小时的指标数据 informative_1h = self.informative_1h_indicators(dataframe, metadata) dataframe = merge_informative_pair( dataframe, informative_1h, self.timeframe, self.inf_1h, ffill=True) # 计算正常时间框架的指标 dataframe = self.normal_tf_indicators(dataframe, metadata) return dataframe # 定义买入逻辑 def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dont_buy_conditions = [] # 不买入条件:如果在回溯的K线中,1小时的最高价低于当前价的盈利阈值 dont_buy_conditions.append( ( (dataframe['close_1h'].rolling(self.lookback_candles.value).max() < (dataframe['close'] * self.profit_threshold.value)) ) ) # 买入条件1 dataframe.loc[ ( (dataframe['rsi_fast'] < 35) & (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) & (dataframe['EWO'] > self.ewo_high.value) & (dataframe['rsi'] < self.rsi_buy.value) & (dataframe['volume'] > 0) & (dataframe['close'] < ( dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) ), ['buy', 'buy_tag']] = (1, 'ewo1') # 买入条件2 dataframe.loc[ ( (dataframe['rsi_fast'] < 35) & (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset_2.value)) & (dataframe['EWO'] > self.ewo_high_2.value) & (dataframe['rsi'] < self.rsi_buy.value) & (dataframe['volume'] > 0) & (dataframe['close'] < (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) & (dataframe['rsi'] < 25) ), ['buy', 'buy_tag']] = (1, 'ewo2') # 买入条件3 dataframe.loc[ ( (dataframe['rsi_fast'] < 35) & (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) & (dataframe['EWO'] < self.ewo_low.value) & (dataframe['volume'] > 0) & (dataframe['close'] < ( dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) ), ['buy', 'buy_tag']] = (1, 'ewolow') # 应用不买入条件 if dont_buy_conditions: for condition in dont_buy_conditions: dataframe.loc[condition, 'buy'] = 0 # 设置买入信号为0 return dataframe # 定义卖出逻辑 def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = [] # 卖出条件 conditions.append( ( (dataframe['close'] > dataframe['sma_9']) & (dataframe['close'] > (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset_2.value)) & (dataframe['rsi'] > 50) & (dataframe['volume'] > 0) & (dataframe['rsi_fast'] > dataframe['rsi_slow']) ) | ( (dataframe['close'] < dataframe['hma_50']) & (dataframe['close'] > (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) & (dataframe['volume'] > 0) & (dataframe['rsi_fast'] > dataframe['rsi_slow']) ) ) # 应用卖出条件 if conditions: dataframe.loc[ reduce(lambda x, y: x | y, conditions), 'sell' ] = 1 return dataframe

2. 代码的交易逻辑和指标分析

总体概述

该策略名为 NASOSv4,基于 Freqtrade 的策略框架开发,使用了多种技术指标,如移动平均线、EWO指标、RSI等,旨在捕捉市场的短期波动,进行高频交易。策略主要在5分钟K线上运行,同时参考了1小时K线的数据。

指标计算方法

  1. Elliott Wave Oscillator (EWO)

    • 计算方法:EWO = ((EMA(快速) - EMA(慢速)) / 最低价) * 100
    • 用途:用于衡量价格的动量和趋势方向。
  2. 移动平均线

    • EMA(指数移动平均线):对近期价格赋予更高权重,响应速度更快。
    • SMA(简单移动平均线):对所有价格赋予相同权重。
  3. Hull Moving Average (HMA)

    • 计算方法:一种对噪音敏感度低且滞后较小的移动平均线,适用于捕捉趋势变化。
  4. 相对强弱指数 (RSI)

    • 计算方法:衡量价格在一定周期内的涨跌幅度,反映超买或超卖状态。
    • rsi_fast:4周期的RSI,反映短期动量。
    • rsi_slow:20周期的RSI,反映长期动量。

交易逻辑

买入逻辑

策略定义了三种主要的买入条件,均涉及到以下指标和条件:

  • RSI快速线低于35:表示市场可能处于超卖状态。
  • 收盘价低于特定的移动平均线的低偏移:表示价格处于相对低位。
  • EWO指标满足特定条件:用于判断市场的趋势和动量。
  • 成交量大于0:确保有足够的市场流动性。
  • 收盘价低于卖出移动平均线的高偏移:进一步确认价格的低位。

具体的买入条件如下:

  1. 条件1(标签:ewo1)

    • EWO > ewo_high:EWO高于设定的高值,表示强劲的上升动量。
    • RSI < rsi_buy:RSI低于设定值,防止在过高的RSI下买入。
  2. 条件2(标签:ewo2)

    • EWO > ewo_high_2:EWO高于第二个设定值,允许在更低的EWO下买入。
    • RSI < 25:RSI非常低,表示强烈的超卖状态。
  3. 条件3(标签:ewolow)

    • EWO < ewo_low:EWO低于设定的低值,表示下跌趋势可能结束。

此外,还有一个 不买入条件

  • 如果在回溯的 lookback_candles 个1小时K线中,最高价低于当前价乘以 profit_threshold,则不买入。这是为了确保有足够的利润空间。

卖出逻辑

策略定义了两种主要的卖出条件,涉及以下指标和条件:

  • 收盘价高于SMA9:价格位于短期均线上方,可能处于高位。
  • 收盘价高于卖出移动平均线的高偏移:价格相对于均线过高,可能即将回调。
  • RSI > 50:RSI高于50,价格可能处于强势区域。
  • RSI快速线高于RSI慢速线:短期动量强于长期动量。
  • 成交量大于0:确保有足够的市场流动性。

具体的卖出条件如下:

  1. 条件1

    • 收盘价 > SMA9
    • 收盘价 > 卖出移动平均线的高偏移
    • RSI > 50
    • RSI快速线 > RSI慢速线
  2. 条件2

    • 收盘价 < HMA50
    • 收盘价 > 卖出移动平均线的高偏移
    • RSI快速线 > RSI慢速线

止损和保护

  • 硬止损(stoploss):设定在-15%。
  • 自定义止损(custom_stoploss):根据当前利润动态调整止损位,当利润超过一定阈值时,逐步提高止损位,以保护已获得的利润。
  • 滑点保护(slippage_protection):在卖出时,如果滑点超过设定值,则尝试重新卖出,最多重试3次。

其它功能

  • 多时间框架分析:使用1小时K线的数据作为信息性指标,与5分钟K线的数据合并。
  • 参数优化:使用 IntParameterDecimalParameter 定义了多个可优化的参数,方便进行策略优化。
  • 交易确认:在卖出时,通过 confirm_trade_exit 函数,根据特定条件决定是否真正卖出。

3. 使用 Freqtrade 回测该策略的指导

步骤1:准备环境

确保您已经按照之前的步骤,使用 Docker 和 VSCode 设置好了 Freqtrade 的开发环境,并将策略文件放置在 user_data/strategies 目录下。

步骤2:安装策略依赖

该策略可能需要额外的 Python 库,例如 TA-Lib。请在容器内安装:

bash
pip install ta-lib pip install pandas_ta

步骤3:更新配置文件

编辑 user_data/config.json,确保以下参数正确设置:

json
{ "max_open_trades": 10, "stake_currency": "USDT", "stake_amount": "unlimited", "dry_run": true, "timeframe": "5m", "strategy": "NASOSv4", "exchange": { "name": "binance", "key": "", "secret": "", "ccxt_config": {}, "ccxt_async_config": {}, "pair_whitelist": ["BTC/USDT", "ETH/USDT", ...], "pair_blacklist": ["USD/USDT"] } }
  • timeframe:设置为 '5m',与策略一致。
  • strategy:设置为 'NASOSv4'
  • pair_whitelist:添加您想要回测的交易对,例如 ["BTC/USDT", "ETH/USDT"]

步骤4:下载历史数据

由于策略使用了多时间框架,需要下载 5 分钟和 1 小时的历史数据。

bash
freqtrade download-data --exchange binance --basecurrencies USDT --timeframes 5m 1h

步骤5:检查策略代码

确保策略代码中的 use_custom_stoplosstrailing_stop 设置正确:

  • 回测时:

    python
    use_custom_stoploss = False trailing_stop = True
  • 运行实时交易时:

    python
    use_custom_stoploss = True trailing_stop = False

步骤6:运行回测

在终端中运行以下命令,开始回测:

bash
freqtrade backtesting --config user_data/config.json --strategy NASOSv4 --timerange=20210101-20211231
  • --timerange:指定回测的时间范围,可以根据需要调整。

步骤7:查看回测结果

回测完成后,终端会显示回测的摘要,包括:

  • 净利润
  • 交易次数
  • 胜率
  • 收益曲线等

详细的回测报告保存在 backtest_results 目录下,包括交易日志和图表。

步骤8:分析结果

  • 查看交易日志:了解每笔交易的买入和卖出点位,利润情况。
  • 查看绘制的图表:可视化策略的交易点,观察策略的有效性。
  • 调整参数:根据回测结果,可能需要调整策略的参数,重新运行回测。

步骤9:参数优化(可选)

如果希望进一步优化策略,可以使用 Hyperopt 进行参数优化。

bash
freqtrade hyperopt --config user_data/config.json --strategy NASOSv4 --hyperopt-loss SharpeHyperOptLoss --timeframe 5m --timerange=20210101-20211231
  • --hyperopt-loss:选择优化的目标函数,例如夏普比率。

注意事项

  • 确保数据完整性:回测前,确保所需时间范围内的数据已经下载完整。
  • 参数一致性:策略中的参数和配置文件中的参数应保持一致,特别是时间框架和交易对。
  • 风险管理:回测结果可能与实际交易存在差异,务必做好风险管理。

希望以上指导能够帮助您顺利回测该策略。如有任何疑问,请随时提问!

Pregunta

1、请问以上策略可不可以用于合约交易?如果可以请运用你的freqtrade专业知识,生成一份用于合约可进行多空交易的策略,用中文详细注释,并详细讲解交易逻辑
2、请用你的freqtrade知识,根据策略生成一份完整的配置文件

Pensamiento

Adapting strategies

Now I'm looking to modify a strategy for Freqtrade to handle futures trading. It's interesting to see that Freqtrade's margin mode can support both long and short positions.

Expanding trading strategy

I’m figuring out how to tweak a trading strategy to handle both long and short positions in freqtrade, aiming for symmetrical logic and a new derivatives version.

Respuesta

1. 将以上策略扩展为可用于合约交易的多空策略,并用中文详细注释和讲解交易逻辑

代码:NASOSv4_Futures

以下是基于您的 NASOSv4 策略扩展,用于合约交易(允许做多和做空)。我们对策略进行了修改,以支持多空交易,并添加了相应的中国注释和详细解释。

python
# --- 扩展用于合约交易的NASOSv4策略 --- from logging import FATAL from freqtrade.strategy.interface import IStrategy from typing import Dict, List from functools import reduce from pandas import DataFrame import talib.abstract as ta import numpy as np import freqtrade.vendor.qtpylib.indicators as qtpylib import datetime from technical.util import resample_to_interval, resampled_merge from datetime import datetime, timedelta from freqtrade.persistence import Trade from freqtrade.strategy import stoploss_from_open, merge_informative_pair, DecimalParameter, IntParameter, CategoricalParameter import technical.indicators as ftt # 策略作者 # @Rallipanos # @pluxury # 买入参数(经过超参数优化) buy_params = { "base_nb_candles_buy": 8, # 买入时使用的基础K线数量 "ewo_high": 2.403, # EWO指标高值 "ewo_high_2": -5.585, # EWO指标高值(第二种情况) "ewo_low": -14.378, # EWO指标低值 "lookback_candles": 3, # 回溯的K线数量 "low_offset": 0.984, # 低价偏移量 "low_offset_2": 0.942, # 低价偏移量(第二种情况) "profit_threshold": 1.008, # 盈利阈值 "rsi_buy": 72 # RSI买入阈值 } # 卖出参数(经过超参数优化) sell_params = { "base_nb_candles_sell": 16, # 卖出时使用的基础K线数量 "high_offset": 1.084, # 高价偏移量 "high_offset_2": 1.401, # 高价偏移量(第二种情况) "pHSL": -0.15, # 硬止损值 "pPF_1": 0.016, # 盈利阈值1 "pPF_2": 0.024, # 盈利阈值2 "pSL_1": 0.014, # 止盈值1 "pSL_2": 0.022 # 止盈值2 } # 定义EWO指标函数 def EWO(dataframe, ema_length=5, ema2_length=35): df = dataframe.copy() ema1 = ta.EMA(df, timeperiod=ema_length) # 计算短期EMA ema2 = ta.EMA(df, timeperiod=ema2_length) # 计算长期EMA emadif = (ema1 - ema2) / df['low'] * 100 # 计算EWO值 return emadif # 定义策略类 class NASOSv4_Futures(IStrategy): INTERFACE_VERSION = 2 # 接口版本 # 多空策略 can_short: bool = True # 允许做空 # 最小回报率(ROI) # 对于合约交易,最小回报率设得很高,仅用于占位 minimal_roi = { "0": 10 } # 止损 stoploss = -0.15 # 最大止损为15% # 定义参数,供优化使用 base_nb_candles_buy = IntParameter( 2, 20, default=buy_params['base_nb_candles_buy'], space='buy', optimize=True) base_nb_candles_sell = IntParameter( 2, 25, default=sell_params['base_nb_candles_sell'], space='sell', optimize=True) low_offset = DecimalParameter( 0.9, 0.99, default=buy_params['low_offset'], space='buy', optimize=False) low_offset_2 = DecimalParameter( 0.9, 0.99, default=buy_params['low_offset_2'], space='buy', optimize=False) high_offset = DecimalParameter( 0.95, 1.1, default=sell_params['high_offset'], space='sell', optimize=True) high_offset_2 = DecimalParameter( 0.99, 1.5, default=sell_params['high_offset_2'], space='sell', optimize=True) # 保护参数(EWO) fast_ewo = 50 # 快速EWO周期 slow_ewo = 200 # 慢速EWO周期 # 回溯K线数量 lookback_candles = IntParameter( 1, 24, default=buy_params['lookback_candles'], space='buy', optimize=True) # 利润阈值 profit_threshold = DecimalParameter(1.0, 1.03, default=buy_params['profit_threshold'], space='buy', optimize=True) # EWO指标阈值 ewo_low = DecimalParameter(-20.0, -8.0, default=buy_params['ewo_low'], space='buy', optimize=False) ewo_high = DecimalParameter( 2.0, 12.0, default=buy_params['ewo_high'], space='buy', optimize=False) ewo_high_2 = DecimalParameter( -6.0, 12.0, default=buy_params['ewo_high_2'], space='buy', optimize=False) # RSI买入阈值 rsi_buy = IntParameter(50, 100, default=buy_params['rsi_buy'], space='buy', optimize=False) # 自定义追踪止损参数 pHSL = DecimalParameter(-0.200, -0.040, default=-0.15, decimals=3, space='sell', optimize=False, load=True) pPF_1 = DecimalParameter(0.008, 0.020, default=0.016, decimals=3, space='sell', optimize=False, load=True) pSL_1 = DecimalParameter(0.008, 0.020, default=0.014, decimals=3, space='sell', optimize=False, load=True) pPF_2 = DecimalParameter(0.040, 0.100, default=0.024, decimals=3, space='sell', optimize=False, load=True) pSL_2 = DecimalParameter(0.020, 0.070, default=0.022, decimals=3, space='sell', optimize=False, load=True) # 追踪止损配置 trailing_stop = True trailing_stop_positive = 0.001 trailing_stop_positive_offset = 0.016 trailing_only_offset_is_reached = True # 卖出信号 use_sell_signal = True sell_profit_only = False sell_profit_offset = 0.01 ignore_roi_if_buy_signal = False # 订单有效时间 order_time_in_force = { 'buy': 'gtc', # Good till cancelled 'sell': 'ioc' # Immediate or cancel } # 策略的时间框架 timeframe = '5m' # 5分钟K线 inf_1h = '1h' # 1小时K线(用于获取信息) process_only_new_candles = True # 仅处理新蜡烛 startup_candle_count = 200 # 启动时需要的最小蜡烛数 use_custom_stoploss = False # 不使用自定义止损(用于回测) # 绘图配置 plot_config = { 'main_plot': { 'ma_buy': {'color': 'orange'}, 'ma_sell': {'color': 'orange'}, }, } # 滑点保护配置 slippage_protection = { 'retries': 3, # 重试次数 'max_slippage': -0.02 # 最大允许滑点 } # 自定义止损函数(由Perkmeister提供) def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float: # 获取止损参数 HSL = self.pHSL.value # 硬止损 PF_1 = self.pPF_1.value # 盈利阈值1 SL_1 = self.pSL_1.value # 止盈值1 PF_2 = self.pPF_2.value # 盈利阈值2 SL_2 = self.pSL_2.value # 止盈值2 # 根据当前利润计算动态止损 if (current_profit > PF_2): sl_profit = SL_2 + (current_profit - PF_2) elif (current_profit > PF_1): sl_profit = SL_1 + ((current_profit - PF_1)*(SL_2 - SL_1)/(PF_2 - PF_1)) else: sl_profit = HSL # 使用硬止损 # 返回从开仓价计算的止损价格 return stoploss_from_open(sl_profit, current_profit) # 确认交易退出 def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float, rate: float, time_in_force: str, sell_reason: str, current_time: datetime, **kwargs) -> bool: # 获取当前数据 dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) last_candle = dataframe.iloc[-1] if (last_candle is not None): if (sell_reason in ['sell_signal']): # 如果满足特定条件,不卖出 if (last_candle['hma_50']*1.149 > last_candle['ema_100']) and (last_candle['close'] < last_candle['ema_100']*0.951): return False # 滑点保护逻辑 try: state = self.slippage_protection['__pair_retries'] except KeyError: state = self.slippage_protection['__pair_retries'] = {} candle = dataframe.iloc[-1].squeeze() slippage = (rate / candle['close']) - 1 if slippage < self.slippage_protection['max_slippage']: pair_retries = state.get(pair, 0) if pair_retries < self.slippage_protection['retries']: state[pair] = pair_retries + 1 return False # 不退出交易 state[pair] = 0 # 重置重试计数 return True # 允许退出交易 # 定义需要的附加数据对(信息性数据) def informative_pairs(self): pairs = self.dp.current_whitelist() informative_pairs = [(pair, '1h') for pair in pairs] # 1小时数据 return informative_pairs # 计算1小时时间框架的指标 def informative_1h_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: assert self.dp, "需要DataProvider来获取多时间框架数据。" informative_1h = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.inf_1h) return informative_1h # 计算正常时间框架的指标 def normal_tf_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # 计算所有买入移动平均线 for val in self.base_nb_candles_buy.range: dataframe[f'ma_buy_{val}'] = ta.EMA(dataframe, timeperiod=val) # 计算所有卖出移动平均线 for val in self.base_nb_candles_sell.range: dataframe[f'ma_sell_{val}'] = ta.EMA(dataframe, timeperiod=val) # 其他指标 dataframe['hma_50'] = qtpylib.hull_moving_average(dataframe['close'], window=50) dataframe['ema_100'] = ta.EMA(dataframe, timeperiod=100) dataframe['sma_9'] = ta.SMA(dataframe, timeperiod=9) # 计算EWO指标 dataframe['EWO'] = EWO(dataframe, self.fast_ewo, self.slow_ewo) # 计算RSI指标 dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) dataframe['rsi_fast'] = ta.RSI(dataframe, timeperiod=4) dataframe['rsi_slow'] = ta.RSI(dataframe, timeperiod=20) return dataframe # 填充指标数据 def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # 获取并合并1小时的指标数据 informative_1h = self.informative_1h_indicators(dataframe, metadata) dataframe = merge_informative_pair( dataframe, informative_1h, self.timeframe, self.inf_1h, ffill=True) # 计算正常时间框架的指标 dataframe = self.normal_tf_indicators(dataframe, metadata) return dataframe # 定义开仓(买入/做空)逻辑 def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # 初始化 dataframe.loc[:, 'enter_long'] = 0 dataframe.loc[:, 'enter_short'] = 0 # 不做多的条件:如果在回溯的K线中,1小时的最高价低于当前价的盈利阈值 dont_long_conditions = ( (dataframe['close_1h'].rolling(self.lookback_candles.value).max() < (dataframe['close'] * self.profit_threshold.value)) ) # 买入(做多)条件1 long_condition_1 = ( (dataframe['rsi_fast'] < 35) & (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) & (dataframe['EWO'] > self.ewo_high.value) & (dataframe['rsi'] < self.rsi_buy.value) & (dataframe['volume'] > 0) & (dataframe['close'] < ( dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) & ~dont_long_conditions ) # 买入(做多)条件2 long_condition_2 = ( (dataframe['rsi_fast'] < 35) & (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset_2.value)) & (dataframe['EWO'] > self.ewo_high_2.value) & (dataframe['rsi'] < self.rsi_buy.value) & (dataframe['volume'] > 0) & (dataframe['close'] < (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) & (dataframe['rsi'] < 25) & ~dont_long_conditions ) # 买入(做多)条件3 long_condition_3 = ( (dataframe['rsi_fast'] < 35) & (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) & (dataframe['EWO'] < self.ewo_low.value) & (dataframe['volume'] > 0) & (dataframe['close'] < ( dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) & ~dont_long_conditions ) # 做空条件:当价格接近高位时 short_condition = ( (dataframe['rsi_fast'] > 70) & (dataframe['close'] > dataframe['hma_50']) & (dataframe['EWO'] < 0) & (dataframe['volume'] > 0) & (dataframe['close'] > (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) ) # 为做多信号赋值 dataframe.loc[long_condition_1, 'enter_long'] = 1 dataframe.loc[long_condition_2, 'enter_long'] = 1 dataframe.loc[long_condition_3, 'enter_long'] = 1 # 为做空信号赋值 dataframe.loc[short_condition, 'enter_short'] = 1 return dataframe # 定义平仓(卖出/做多平仓)逻辑 def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # 初始化 dataframe.loc[:, 'exit_long'] = 0 dataframe.loc[:, 'exit_short'] = 0 # 做多仓位平仓条件 long_exit_condition = ( ((dataframe['close'] > dataframe['sma_9']) & (dataframe['close'] > (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset_2.value)) & (dataframe['rsi'] > 50) & (dataframe['volume'] > 0) & (dataframe['rsi_fast'] > dataframe['rsi_slow'])) | ((dataframe['close'] < dataframe['hma_50']) & (dataframe['close'] > (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) & (dataframe['volume'] > 0) & (dataframe['rsi_fast'] > dataframe['rsi_slow'])) ) # 做空仓位平仓条件 short_exit_condition = ( ((dataframe['close'] < dataframe['sma_9']) & (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset_2.value)) & (dataframe['rsi'] < 50) & (dataframe['volume'] > 0) & (dataframe['rsi_fast'] < dataframe['rsi_slow'])) | ((dataframe['close'] > dataframe['hma_50']) & (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) & (dataframe['volume'] > 0) & (dataframe['rsi_fast'] < dataframe['rsi_slow'])) ) # 为平多信号赋值 dataframe.loc[long_exit_condition, 'exit_long'] = 1 # 为平空信号赋值 dataframe.loc[short_exit_condition, 'exit_short'] = 1 return dataframe

中文详细说明:

总体概述
该策略 NASOSv4_Futures 基于原始的 NASOSv4 策略扩展,使其支持合约交易中的多空策略。在此策略中,我们使用5分钟K线作为主要时间框架,并参考1小时K线作为附加信息。策略会自动根据市场情况决定何时做多或做空,并通过止损和追踪止损机制来管理风险。

1. 新增做空逻辑

  • 策略中添加了 can_short = True,允许策略打开空头头寸。
  • populate_entry_trend 函数中添加了 enter_short 信号,定义了满足特定条件时做空的逻辑。

2. 指标计算
策略使用了多个技术指标来进行市场分析:

  • EWO(Elliott Wave Oscillator):衡量市场动量的指标。
  • EMA(指数移动平均线):计算多种EMA用于买入和卖出判断。
  • HMA(Hull Moving Average):Hull移动平均线,减少延迟,提高对趋势变化的敏感度。
  • RSI(Relative Strength Index):相对强弱指数,用于判断市场的超买和超卖状态。

3. 买入(做多)逻辑
策略在以下情况下做多(enter_long):

  • 长条件1rsi_fast < 35EWO > ewo_high 表明市场可能处于超卖状态,等待反弹。
  • 长条件2:进一步确认价格非常低 (rsi < 25) 且 EWO > ewo_high_2,市场处于极端超卖状态。
  • 长条件3EWO < ewo_low 表明市场动量可能正在转向。

同时,还应用了其他条件以确保买入时价格处于相对于移动平均线的低位,并且在过去的数根1小时K线中有足够的利润空间。

4. 卖出(做多平仓)逻辑
策略在以下情况下平多(exit_long):

  • 当价格高于短期移动平均线(如 SMA9)且相对于移动平均线过高(ma_sell),同时 RSI > 50 表明价格可能进入高位区域,动量增强。
  • 当价格回落至 HMA50 以下,但仍高于卖出移动平均线的高偏移,同时 RSI_fast > RSI_slow 表明市场可能处于高位但开始回落。

5. 做空逻辑
我们为策略添加了做空逻辑。当市场显示反向迹象时,策略将做空(enter_short):

  • rsi_fast > 70EWO < 0,表明市场可能处于超买状态,价格有可能下跌。
  • 价格位于 HMA50 上方,并高于 ma_sell 移动平均线的高偏移,这些条件表明价格可能过高,将要回落。

6. 做空平仓逻辑
策略在以下情况下平空(exit_short):

  • 当价格跌至短期移动平均线(SMA9)以下,并相对于移动平均线过低(ma_buy),同时 RSI < 50 表明价格可能触底反弹。
  • 当价格回升至 HMA50 以上,但仍低于买入移动平均线的低偏移,同时 RSI_fast < RSI_slow 表明市场有可能从低位反弹。

7. 止损和追踪止损
策略使用 stoplosscustom_stoploss 来管理风险:

  • stoploss 设置为 -0.15,即如果亏损达到15%,将止损。
  • custom_stoploss 根据当前利润动态调整止损位置,保护已获得的利润。

8. 滑点保护
confirm_trade_exit 函数包含滑点保护逻辑。如果卖出出现滑点超过设定阈值,策略将尝试重新卖出,最多重试3次。

9. 多时间框架
策略使用1小时K线的最高价信息来确定是否有足够的利润空间进行交易,并使用 merge_informative_pair 将1小时数据合并到5分钟数据中。

总结
NASOSv4_Futures 策略通过多项技术指标和多时间框架分析市场情况,根据市场状况灵活地决定做多或做空位置。通过止损、追踪止损和滑点保护等机制,该策略旨在最大化利润并最小化风险。

2. 生成一份用于合约交易的完整配置文件

以下是一个适用于合约交易(Futures)的 config.json 示例文件。该文件配置了交易所和策略等相关设置。

json
{ "max_open_trades": 10, "stake_currency": "USDT", "stake_amount": "unlimited", "dry_run": true, "margin_mode": "isolated", "margin_leverage": 10, "timeframe": "5m", "strategy": "NASOSv4_Futures", "exit_profit_only": false, "trailing_stop": true, "trailing_stop_positive": 0.001, "trailing_stop_positive_offset": 0.016, "trailing_only_offset_is_reached": true, "ignore_roi_if_buy_signal": false, "exchange": { "name": "binance_futures", "key": "YOUR_BINANCE_API_KEY", "secret": "YOUR_BINANCE_API_SECRET", "password": "", "uid": "", "ccxt_config": { "enableRateLimit": true }, "ccxt_async_config": {}, "pair_whitelist": [ "BTC/USDT", "ETH/USDT", "BNB/USDT" ], "pair_blacklist": [ "USD/USDT" ] }, "pairlists": [ { "method": "StaticPairList", "pairs": [ "BTC/USDT", "ETH/USDT", "BNB/USDT" ] } ], "telegram": { "enabled": false, "token": "", "chat_id": "" }, "bot_name": "freqtrade-futures", "initial_safety_order_trigger": -0.05, "max_safety_orders": 0, "max_so_vol_scale": 1, "max_so_price_scale": 1, "unfilledtimeout": { "buy": 15, "sell": 15, "unit": "minutes" }, "bid_strategy": { "price_side": "bid", "ask_last_balance": 0.0, "use_order_book": false, "order_book_top": 1 }, "ask_strategy": { "price_side": "ask", "use_order_book": false, "order_book_top": 1 }, "api_server": { "enabled": false, "listen_ip_address": "127.0.0.1", "listen_port": 8080, "verbosity": "error", "whitelist": [], "jwt_secret_key": "random_key_here", "CORS_origins": [], "username": "freqtrade", "password": "freqtrade" }, "bot_credentials": { "username": "freqtrade", "password": "freqtrade" }, "log_level": "info", "experimental": { "roi_enhancement": false } }

配置文件说明:

  • max_open_trades:最多允许同时打开的交易数量,设置为10。
  • stake_currency:基准货币为 USDT
  • stake_amount:每笔交易的下注金额,这里设置为 unlimited,即根据可用余额进行交易。
  • dry_run:设为 true 表示模拟交易模式,不会进行真实交易。
  • margin_mode:合约交易模式设置为 isolated 孤立模式。
  • margin_leverage:杠杆倍数,设置为10倍杠杆。请根据您的风险偏好进行调整。
  • timeframe:主交易时间框架为5分钟K线。
  • strategy:使用我们定义的 NASOSv4_Futures 策略。
  • trailing_stop 和相关参数:启用追踪止损功能,并设置追踪止损参数。
  • exchange:配置交易所为 binance_futures
    • name:交易所名称 binance_futures
    • keysecret:您的Binance API密钥和私钥。请填写您的真实值,不要泄露给任何人。
    • pair_whitelist:指定要交易的交易对列表,这里包含 BTC/USDTETH/USDTBNB/USDT
  • pairlists:定义交易对列表的获取方式,这里使用静态交易对列表。
  • bot_name:机器人名称为 freqtrade-futures
  • logginglog_level 设置为 info,表示输出信息级别的日志。

使用注意事项

  1. API密钥:请在配置文件中填写您的真实 keysecret,以便连接到Binance Futures进行合约交易。
  2. 杠杆和风险:合约交易存在高风险,请根据自身风险承受能力设置合适的杠杆倍数。
  3. dry_run模式:开始时建议保持 dry_runtrue,进行模拟交易。当您准备好进行真实交易时,可以将其设置为 false
  4. 交易对选择:根据您的偏好和市场状况选择合适的交易对。在此例子中,我们使用了 BTC/USDTETH/USDTBNB/USDT
  5. 资金管理和风险控制:务必在开始实际交易前仔细评估风险,并确保您的资金管理策略到位。

总结

在本解答中,我们详细介绍了如何将 NASOSv4 策略扩展为可用于合约交易的多空策略,包括在代码中添加详细的中文注释和对交易逻辑的深入讲解。此外,我们还根据策略生成了一份完整的配置文件,以帮助您在Binance Futures上进行多空合约交易。

请根据您具体的交易所API密钥、风险偏好和交易对进行适当调整后再使用该策略。合约交易具有高风险,请谨慎操作。

Compartir este Q&A