添加裸K形态,前端添加KLC显示
This commit is contained in:
@@ -0,0 +1,233 @@
|
||||
|
||||
# --- Do not remove these libs ---
|
||||
from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter, CategoricalParameter
|
||||
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 PatternTrader --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 PatternTrader --strategy-path ./user_data/Chan/strategies
|
||||
# freqtrade backtesting -c ./user_data/Chan/config/ChanLun_BTC_30.json --strategy PatternTrader --strategy-path ./user_data/Chan/strategies --timerange=20251023-
|
||||
# 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 PatternTrader --strategy-path ./user_data/Chan/strategies -c ./user_data/Chan/config/ChanLun_BTC_30.json -e 200 --timerange=20250201-20250401
|
||||
# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --spaces buy sell roi stoploss --strategy PatternTrader --strategy-path ./user_data/Chan/strategies -c ./user_data/Chan/config/ChanLun_BTC_30.json -e 600 --timerange=20250201-20250401
|
||||
# sudo docker compose run --rm chan_btc backtesting -c ./user_data/Chan/config/ChanLun_BTC_30.json --strategy PatternTrader --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 PatternTrader --strategy-path ./user_data/Chan/strategies
|
||||
|
||||
class PatternTrader(IStrategy):
|
||||
"""
|
||||
极简双均线策略
|
||||
只使用双均线交叉作为唯一信号
|
||||
"""
|
||||
|
||||
INTERFACE_VERSION: int = 3
|
||||
|
||||
# 极简参数
|
||||
fast_ma: IntParameter = IntParameter(5, 15, default=8, space='buy') # 快速均线
|
||||
slow_ma: IntParameter = IntParameter(20, 50, default=30, space='buy') # 慢速均线
|
||||
|
||||
# 添加一个简单的sell空间参数
|
||||
exit_delay: IntParameter = IntParameter(1, 10, default=3, space='sell') # 出场延迟
|
||||
|
||||
# 时间框架
|
||||
time: IntParameter = IntParameter(15, 60, default=30, space='buy')
|
||||
|
||||
# ROI 超参
|
||||
roi_t1: IntParameter = IntParameter(10, 60, default=30, space='roi')
|
||||
roi_t2: IntParameter = IntParameter(60, 240, default=120, space='roi')
|
||||
roi_p1: DecimalParameter = DecimalParameter(0.02, 0.08, default=0.05, decimals=3, space='roi')
|
||||
roi_p2: DecimalParameter = DecimalParameter(0.005, 0.03, default=0.01, decimals=3, space='roi')
|
||||
|
||||
# 合约交易参数
|
||||
can_short = True
|
||||
stoploss = -0.02 # 2% 止损
|
||||
|
||||
# 杠杆设置
|
||||
lev: DecimalParameter = DecimalParameter(1.0, 3.0, default=2.0, decimals=1, space='buy')
|
||||
|
||||
# 运行设置
|
||||
process_only_new_candles = False
|
||||
startup_candle_count: int = 100
|
||||
|
||||
# ROI 外部覆盖
|
||||
_roi_override: Optional[Dict[str, float]] = None
|
||||
|
||||
@property
|
||||
def minimal_roi(self) -> Dict[str, float]:
|
||||
"""
|
||||
基于超参动态生成 ROI 梯度
|
||||
"""
|
||||
if self._roi_override is not None:
|
||||
return self._roi_override
|
||||
t1 = int(self.roi_t1.value)
|
||||
t2 = int(self.roi_t2.value)
|
||||
times = sorted([t1, t2])
|
||||
p1 = float(self.roi_p1.value)
|
||||
p2 = float(self.roi_p2.value)
|
||||
profits = sorted([p1, p2], reverse=True)
|
||||
return {
|
||||
"0": profits[0],
|
||||
str(times[0]): profits[1],
|
||||
str(times[1]): 0.0,
|
||||
}
|
||||
|
||||
@minimal_roi.setter
|
||||
def minimal_roi(self, value: Dict[str, float]) -> None:
|
||||
# 允许框架在解析时覆盖 ROI 设置
|
||||
self._roi_override = value
|
||||
|
||||
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
"""
|
||||
计算技术指标(极简版)
|
||||
只计算双均线
|
||||
"""
|
||||
res = self.get_ticker_indicator() * int(self.time.value)
|
||||
dataframe_3 = resample_to_interval(dataframe, res)
|
||||
|
||||
# 只计算双均线
|
||||
dataframe_3['fast_ma'] = ta.SMA(dataframe_3['close'], timeperiod=int(self.fast_ma.value))
|
||||
dataframe_3['slow_ma'] = ta.SMA(dataframe_3['close'], timeperiod=int(self.slow_ma.value))
|
||||
|
||||
# 计算金叉和死叉
|
||||
dataframe_3['fast_ma_cross_slow_ma'] = (dataframe_3['fast_ma'] > dataframe_3['slow_ma']) & (dataframe_3['fast_ma'].shift(1) <= dataframe_3['slow_ma'].shift(1))
|
||||
dataframe_3['fast_ma_cross_slow_ma_down'] = (dataframe_3['fast_ma'] < dataframe_3['slow_ma']) & (dataframe_3['fast_ma'].shift(1) >= dataframe_3['slow_ma'].shift(1))
|
||||
|
||||
dataframe = resampled_merge(dataframe, dataframe_3)
|
||||
return dataframe
|
||||
|
||||
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
"""
|
||||
基于TA指标,填充进场趋势列(极简版)
|
||||
只使用双均线交叉
|
||||
"""
|
||||
res = self.get_ticker_indicator() * int(self.time.value)
|
||||
def _pick(df: DataFrame, name: str) -> str:
|
||||
col = f"resample_{res}_{name}"
|
||||
if col in df.columns:
|
||||
return col
|
||||
col2 = f"resample_{float(res)}_{name}"
|
||||
if col2 in df.columns:
|
||||
return col2
|
||||
cand = [c for c in df.columns if c.endswith(f"_{name}")]
|
||||
return cand[0] if len(cand) else col
|
||||
|
||||
fast_ma_cross_slow_ma_str = _pick(dataframe, 'fast_ma_cross_slow_ma')
|
||||
fast_ma_cross_slow_ma_down_str = _pick(dataframe, 'fast_ma_cross_slow_ma_down')
|
||||
|
||||
# 检测多头信号:快线上穿慢线
|
||||
dataframe.loc[
|
||||
(
|
||||
(dataframe[fast_ma_cross_slow_ma_str] == True) &
|
||||
(pd.notna(dataframe[fast_ma_cross_slow_ma_str]))
|
||||
),
|
||||
['enter_long', 'enter_tag']] = (1, 'long_signal_simple')
|
||||
|
||||
# 检测空头信号:快线下穿慢线
|
||||
dataframe.loc[
|
||||
(
|
||||
(dataframe[fast_ma_cross_slow_ma_down_str] == True) &
|
||||
(pd.notna(dataframe[fast_ma_cross_slow_ma_down_str]))
|
||||
),
|
||||
['enter_short', 'enter_tag']] = (1, 'short_signal_simple')
|
||||
|
||||
return dataframe
|
||||
|
||||
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
"""
|
||||
基于TA指标,填充出场趋势列(极简版)
|
||||
反向交叉出场
|
||||
"""
|
||||
res = self.get_ticker_indicator() * int(self.time.value)
|
||||
def _pick(df: DataFrame, name: str) -> str:
|
||||
col = f"resample_{res}_{name}"
|
||||
if col in df.columns:
|
||||
return col
|
||||
col2 = f"resample_{float(res)}_{name}"
|
||||
if col2 in df.columns:
|
||||
return col2
|
||||
cand = [c for c in df.columns if c.endswith(f"_{name}")]
|
||||
return cand[0] if len(cand) else col
|
||||
|
||||
fast_ma_cross_slow_ma_str = _pick(dataframe, 'fast_ma_cross_slow_ma')
|
||||
fast_ma_cross_slow_ma_down_str = _pick(dataframe, 'fast_ma_cross_slow_ma_down')
|
||||
|
||||
# 做多出场:出现死叉
|
||||
dataframe.loc[
|
||||
(
|
||||
(dataframe[fast_ma_cross_slow_ma_down_str] == True) &
|
||||
(pd.notna(dataframe[fast_ma_cross_slow_ma_down_str]))
|
||||
),
|
||||
['exit_long', 'exit_tag']] = (1, 'long_exit_simple')
|
||||
|
||||
# 做空出场:出现金叉
|
||||
dataframe.loc[
|
||||
(
|
||||
(dataframe[fast_ma_cross_slow_ma_str] == True) &
|
||||
(pd.notna(dataframe[fast_ma_cross_slow_ma_str]))
|
||||
),
|
||||
['exit_short', 'exit_tag']] = (1, 'short_exit_simple')
|
||||
|
||||
return dataframe
|
||||
|
||||
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 float(self.lev.value)
|
||||
|
||||
def get_ticker_indicator(self):
|
||||
return int(self.timeframe[:-1])
|
||||
|
||||
|
||||
# 简单的测试函数
|
||||
def test_strategy():
|
||||
"""
|
||||
测试策略基本功能
|
||||
"""
|
||||
try:
|
||||
# 创建策略实例
|
||||
strategy = PatternTrader()
|
||||
|
||||
# 检查基本属性
|
||||
print("✅ 策略实例化成功")
|
||||
print(f"策略名称: {strategy.__class__.__name__}")
|
||||
print(f"接口版本: {strategy.INTERFACE_VERSION}")
|
||||
print(f"支持做空: {strategy.can_short}")
|
||||
print(f"默认止损: {strategy.stoploss}")
|
||||
|
||||
# 检查参数
|
||||
print("\n✅ 策略参数检查:")
|
||||
print(f"布林带长度: {strategy.bb_length.value}")
|
||||
print(f"杠杆: {strategy.lev.value}")
|
||||
print(f"仓位比例: {strategy.position_size_pct.value}")
|
||||
|
||||
print("\n🎉 策略测试通过!")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 策略测试失败: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_strategy()
|
||||
Reference in New Issue
Block a user