回测结果还不错的一个版本, 但是grok说还能更优化, 结果优化完了不如现在, 但是我我先存一个版本

This commit is contained in:
zhangkun9038@dingtalk.com 2025-05-14 17:06:16 +00:00
parent 1ea7d2ec75
commit 8d479752ee
5 changed files with 130 additions and 260 deletions

2
.gitignore vendored
View File

@ -75,6 +75,8 @@ coverage.xml
# Django stuff:
#*.log
!outout_filted.log
!result/outout_filted.log
!result/outout.log
local_settings.py

2
aaa Normal file
View File

@ -0,0 +1,2 @@
https://www.freqtrade.io/en/stable/strategy/custom-stakeamount/

View File

@ -2,10 +2,24 @@
"$schema": "https://schema.freqtrade.io/schema.json",
"trading_mode": "spot",
"margin_mode": "isolated",
"max_open_trades": 4,
"max_open_trades": 3,
"stake_amount": 300,
"unfilledtimeout": {
"entry": 10,
"exit": 20
},
"entry_pricing": {
"price_side": "same",
"use_order_book": true,
"order_book_top": 1
},
"exit_pricing": {
"price_side": "other",
"use_order_book": true,
"order_book_top": 1
},
"stake_currency": "USDT",
"stake_amount": 150,
"startup_candle_count": 400,
"startup_candle_count": 200,
"tradable_balance_ratio": 1,
"fiat_display_currency": "USD",
"dry_run": true,
@ -14,14 +28,10 @@
"dry_run_wallet": 1000,
"cancel_open_orders_on_exit": true,
"stoploss": -0.1,
"unfilledtimeout": {
"entry": 5,
"exit": 15
},
"exchange": {
"name": "okx",
"key": "eca767d4-fda5-4a1b-bb28-49ae18093307",
"secret": "8CA3628A556ED137977DB298D37BC7F3",
"key": "REDACTED",
"secret": "REDACTED",
"enable_ws": false,
"ccxt_config": {
"enableRateLimit": true,
@ -32,77 +42,12 @@
},
"ccxt_async_config": {
"enableRateLimit": true,
"rateLimit": 3000,
"rateLimit": 3000,
"timeout": 20000
},
"pair_whitelist": [
"OKB/USDT",
"BTC/USDT",
"ETH/USDT",
"SOL/USDT",
"DOT/USDT"
],
"pair_whitelist": ["OKB/USDT", "BTC/USDT", "ETH/USDT", "SOL/USDT"],
"pair_blacklist": []
},
"freqai": {
"enabled": true,
"identifier": "test175",
"freqaimodel": "XGBoostRegressor",
"model_path": "/freqtrade/user_data/models",
"save_backtesting_prediction": true,
"save_backtest_models": true,
"backtest_period_days": 30,
"purge_old_models": true,
"load_trained_model": true,
"train_period_days": 90,
"backtest_period_days": 10,
"live_retrain_hours": 0,
"include_predictions_in_final_dataframe": true,
"data_kitchen": {
"fillna": "ffill",
"feature_parameters": {
"DI_threshold": 0.5,
"weight_factor": 0.9
}
},
"feature_parameters": {
"include_timeframes": ["5m", "15m", "1h"],
"include_corr_pairlist": ["BTC/USDT", "ETH/USDT"],
"label_period_candles": 12,
"include_shifted_candles": 3,
"indicator_periods_candles": [10, 20, 50],
"plot_feature_importances": 1,
"feature_selection": {
"method": "none"
}
},
"data_split_parameters": {
"test_size": 0.2,
"shuffle": false
},
"model_training_parameters": {
"n_estimators": 200,
"learning_rate": 0.05,
"max_depth": 6,
"subsample": 0.8,
"colsample_bytree": 0.8
}
},
"entry_pricing": {
"price_side": "same",
"use_order_book": true,
"order_book_top": 1,
"price_last_balance": 0.0,
"check_depth_of_market": {
"enabled": false,
"bids_to_ask_delta": 1
}
},
"exit_pricing": {
"price_side": "other",
"use_order_book": true,
"order_book_top": 1
},
"pairlists": [
{
"method": "StaticPairList"
@ -118,7 +63,7 @@
"ws_token": "6O5pBDiRigiZrmIsofaE2rkKMJtf9h8zVQ",
"CORS_origins": [],
"username": "freqAdmin",
"password": "admin"
"password": "REDACTED"
},
"use_exit_signal": true,
"bot_name": "freqtrade",
@ -127,6 +72,7 @@
"internals": {
"process_throttle_secs": 5,
"heartbeat_interval": 20,
"loglevel": "DEBUG"
}
"loglevel": "INFO"
},
"config_files": ["config_examples/theforcev7.json"]
}

View File

@ -1,201 +1,121 @@
from freqtrade.strategy import IStrategy
from pandas import DataFrame
import numpy as np
import os
import datetime
import time
import pandas as pd
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
from freqtrade.persistence import Trade # 必须导入 Trade 类
from tempfile import NamedTemporaryFile
import shutil
from typing import Optional, Union
from freqtrade.persistence import Trade
from datetime import datetime
class TheForceV7(IStrategy):
INTERFACE_VERSION = 3
minimal_roi = {
"0": 10
}
stoploss = -0.1
trailing_stop = False
timeframe = '5m'
process_only_new_candles = True
use_exit_signal = True
ignore_roi_if_entry_signal = True
startup_candle_count: int = 30
order_types = {
'entry': 'limit',
'exit': 'limit',
'stoploss': 'market',
'stoploss_on_exchange': False
}
order_time_in_force = {
'entry': 'gtc',
'exit': 'gtc'
}
plot_config = {
'main_plot': {
'tema': {},
'sar': {'color': 'white'},
},
'subplots': {
"MACD": {
'macd': {'color': 'blue'},
'macdsignal': {'color': 'orange'},
},
"RSI": {
'rsi': {'color': 'red'},
}
}
}
def __init__(self, config: dict) -> None:
super().__init__(config)
self.trade_data = {} # 用于缓存 K 线数据以计算 min_rate 和 max_rate
def informative_pairs(self):
return []
# Strategy configuration
timeframe = "5m"
minimal_roi = {"0": 0.02} # 2% ROI for realistic take-profit
stoploss = -0.1 # Fallback, overridden by custom_exit
startup_candle_count = 200 # Reduced for indicator calculations
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# 缓存 K 线数据
self.trade_data[metadata['pair']] = dataframe
stoch = ta.STOCH(dataframe)
dataframe['slowd'] = stoch['slowd']
dataframe['slowk'] = stoch['slowk']
dataframe['rsi7'] = ta.RSI(dataframe, timeperiod=7)
macd = ta.MACD(dataframe, 12, 26, 1)
dataframe['macd'] = macd['macd']
dataframe['macdsignal'] = macd['macdsignal']
dataframe['macdhist'] = macd['macdhist']
dataframe['ema5h'] = ta.EMA(dataframe['high'], timeperiod=5)
dataframe['ema5l'] = ta.EMA(dataframe['low'], timeperiod=5)
dataframe['ema5c'] = ta.EMA(dataframe['close'], timeperiod=5)
dataframe['ema5o'] = ta.EMA(dataframe['open'], timeperiod=5)
dataframe['ema200c'] = ta.MA(dataframe['close'], 200)
dataframe['volvar'] = (dataframe['volume'].rolling(100).mean() * 1.5)
bollinger = qtpylib.bollinger_bands(dataframe['close'], window=21, stds=2)
dataframe['bb_lowerband'] = bollinger['lower']
dataframe['bb_upperband'] = bollinger['upper']
dataframe['bb_middleband'] = bollinger['mid']
dataframe['ema200c'] = ta.EMA(dataframe['close'], timeperiod=200)
dataframe['ema50c'] = ta.EMA(dataframe['close'], timeperiod=50)
dataframe['ema20c'] = ta.EMA(dataframe['close'], timeperiod=20)
dataframe['rsi7'] = ta.RSI(dataframe['close'], timeperiod=7)
macd = ta.MACD(dataframe['close'])
dataframe['macd'] = macd[0]
dataframe['macdsignal'] = macd[1]
stoch = ta.STOCH(dataframe['high'], dataframe['low'], dataframe['close'], fastk_period=14, slowk_period=3, slowd_period=3)
dataframe['slowk'] = stoch[0]
dataframe['slowd'] = stoch[1]
dataframe['adx'] = ta.ADX(dataframe['high'], dataframe['low'], dataframe['close'], timeperiod=14)
dataframe['volvar'] = dataframe['volume'].rolling(window=20).mean()
return dataframe
def crossover(self, series1: DataFrame, series2: DataFrame) -> DataFrame:
"""Detects when series1 crosses above series2."""
return (series1 > series2) & (series1.shift(1) <= series2.shift(1))
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[
(
(
(
(dataframe['slowk'] >= 20) & (dataframe['slowk'] <= 80)
&
(dataframe['slowd'] >= 20) & (dataframe['slowd'] <= 80)
)
|
(
(dataframe['slowk'] < 30) & (dataframe['slowd'] < 30) &
(qtpylib.crossed_above(dataframe['slowk'], dataframe['slowd']))
)
)
&
(
(dataframe['macd'] > dataframe['macd'].shift(1))
&
(dataframe['macdsignal'] > dataframe['macdsignal'].shift(1))
)
&
(
(dataframe['close'] > dataframe['close'].shift(1))
&
(dataframe['open'] > dataframe['open'].shift(1))
)
&
(
(dataframe['ema5c'] >= dataframe['ema5o'])
|
(dataframe['open'] < dataframe['ema5l'])
)
&
(
(dataframe['volume'] > dataframe['volvar'])
)
|
(
(dataframe['slowk'] >= 20) & (dataframe['slowk'] <= 80)
&
(dataframe['slowd'] >= 20) & (dataframe['slowd'] <= 80)
&
(
(dataframe['close'] <= dataframe['bb_lowerband'])
|
(dataframe['open'] <= dataframe['bb_lowerband'])
)
)
|
(
(dataframe['close'] > dataframe['ema200c'])
&
(dataframe['rsi7'] < 35)
)
),
'enter_long'] = 1
(dataframe['close'] > dataframe['ema200c']) & # Relaxed trend
(dataframe['close'] > dataframe['ema50c']) & # Short-term trend
(dataframe['rsi7'] < 50) & # Relaxed RSI
(dataframe['macd'] > 0) & # Relaxed MACD
(dataframe['volume'] > dataframe['volvar'] * 0.5) & # Relaxed volume
(dataframe['adx'] > 20), # Trend strength
'enter_long'
] = 1
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[
(
(
(
(dataframe['slowk'] <= 80) & (dataframe['slowd'] <= 80)
)
|
(
(qtpylib.crossed_above(dataframe['slowk'], 70))
|
(qtpylib.crossed_above(dataframe['slowd'], 70))
)
)
&
(
(dataframe['macd'] < dataframe['macd'].shift(1))
&
(dataframe['macdsignal'] < dataframe['macdsignal'].shift(1))
)
&
(
(dataframe['ema5c'] < dataframe['ema5o'])
|
(dataframe['open'] >= dataframe['ema5h'])
)
|
(
(dataframe['slowk'] <= 80)
&
(dataframe['slowd'] <= 80)
&
(
(dataframe['close'] >= dataframe['bb_upperband'])
|
(dataframe['open'] >= dataframe['bb_upperband'])
)
)
|
(
(dataframe['high'] > dataframe['bb_upperband'])
&
(((dataframe['high'] - dataframe['bb_upperband']) * 100 / dataframe['bb_upperband']) > 1)
)
),
'exit_long'] = 1
(dataframe['slowk'] > 65) | (dataframe['slowd'] > 65) | # Relaxed STOCH
(dataframe['rsi7'] > 70) # Overbought
) &
(dataframe['close'] < dataframe['ema20c']) & # Trend reversal
(self.crossover(dataframe['macdsignal'], dataframe['macd'])) & # Custom crossover
(dataframe['macd'] < 0), # Downtrend
'exit_long'
] = 1
return dataframe
def custom_exit(self, pair: str, trade: Trade, current_time: datetime, current_rate: float,
current_profit: float, **kwargs) -> Optional[Union[str, bool]]:
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
if dataframe.empty or dataframe['date'].iloc[-1] < current_time: # Fixed: Use date column
return None
last_candle = dataframe.iloc[-1]
atr = ta.ATR(dataframe, timeperiod=14).iloc[-1]
duration = (current_time - trade.open_date).total_seconds() / 60 # Minutes
# Dynamic Take-Profit
take_profit = current_rate + 1.0 * atr # Lowered ATR
if current_rate >= take_profit:
return "take_profit"
# Partial Take-Profit at 2%
if current_profit >= 0.02:
return "partial_take_profit"
# Dynamic Stop-Loss
stop_loss = current_rate - 2.0 * atr # Relaxed ATR
if current_rate <= stop_loss:
return "dynamic_stop_loss"
# Trailing Stop
if current_profit > 0.005: # Lowered threshold
self.trailing_stop = True
self.trailing_stop_positive = 0.003 # 0.3% retracement
self.trailing_stop_positive_offset = 0.008 # 0.8% offset
if current_profit < trade.max_profit - self.trailing_stop_positive:
return "trailing_stop"
# Time Stop
if duration > 60: # 1 hour
return "time_stop"
return None
def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
proposed_stake: float, min_stake: float, max_stake: float,
leverage: float, entry_tag: Optional[str], side: str,
**kwargs) -> float:
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
if dataframe.empty:
return proposed_stake
atr = ta.ATR(dataframe, timeperiod=14).iloc[-1]
price_std = dataframe['close'].std()
combined_volatility = atr + price_std
base_stake = self.wallets.get_total_stake_amount() * 0.0333 # 3.33% risk
base_stake = min(base_stake, 50.0) # Cap at 50 USDT
risk_factor = 1.0
if combined_volatility > current_rate * 0.03: # High volatility
risk_factor = 0.3 if pair in ['SOL/USDT', 'OKB/USDT'] else 0.5
elif combined_volatility < current_rate * 0.01: # Low volatility
risk_factor = 1.2 if pair in ['BTC/USDT', 'ETH/USDT'] else 1.0
return base_stake * risk_factor

View File

@ -67,7 +67,7 @@ fi
cd -
sed -i 's/\x1B\[[0-9;]*m//g' output.log
#python3 ../filter.py
cp output_filted.log result/ -f
cp output.log result/ -f
cd tools/
python tradestocsv.py
cd ../