添加策略
This commit is contained in:
183
.gitignore
vendored
Normal file
183
.gitignore
vendored
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
pip-wheel-metadata/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
.python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# Data files (keep structure but ignore large data)
|
||||||
|
data/
|
||||||
|
|
||||||
|
# IDE files
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS generated files
|
||||||
|
.DS_Store
|
||||||
|
.DS_Store?
|
||||||
|
._*
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
ehthumbs.db
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
.dockerignore
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
logs/
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
tmp/
|
||||||
|
temp/
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
|
||||||
|
# API keys and secrets
|
||||||
|
.env
|
||||||
|
config.ini
|
||||||
|
secrets.json
|
||||||
|
api_keys.txt
|
||||||
|
|
||||||
|
# Database files
|
||||||
|
*.db
|
||||||
|
*.sqlite
|
||||||
|
*.sqlite3
|
||||||
|
|
||||||
|
# Backup files
|
||||||
|
*.bak
|
||||||
|
*.backup
|
||||||
|
|
||||||
|
|
||||||
|
user_data/data
|
||||||
|
user_data/freqaimodels
|
||||||
|
user_data/hyperopts
|
||||||
|
user_data/logs
|
||||||
|
user_data/backtest_results
|
||||||
14
Dockerfile
Normal file
14
Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
FROM freqtradeorg/freqtrade:stable
|
||||||
|
|
||||||
|
# 设置工作目录
|
||||||
|
WORKDIR /freqtrade
|
||||||
|
|
||||||
|
# 复制用户数据到镜像(策略、配置等),但排除 data 和 backtest_results 目录
|
||||||
|
COPY user_data /freqtrade/user_data
|
||||||
|
RUN rm -rf /freqtrade/user_data/data /freqtrade/user_data/backtest_results
|
||||||
|
|
||||||
|
# 暴露 freqtrade WebUI 端口
|
||||||
|
EXPOSE 8077
|
||||||
|
|
||||||
|
# 设置启动命令,与 compose 保持一致
|
||||||
|
CMD ["trade", "--config", "/freqtrade/user_data/config.json", "--strategy", "SimpleRSIStrategyFixed"]
|
||||||
@@ -10,4 +10,4 @@ services:
|
|||||||
command: >
|
command: >
|
||||||
trade
|
trade
|
||||||
--config ./user_data/config.json
|
--config ./user_data/config.json
|
||||||
--strategy MACDStrategy
|
--strategy SimpleRSIStrategyFixed
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||||
"max_open_trades": 1,
|
"max_open_trades": 3,
|
||||||
"stake_currency": "USDT",
|
"stake_currency": "USDT",
|
||||||
"stake_amount": "unlimited",
|
"stake_amount": "unlimited",
|
||||||
"tradable_balance_ratio": 0.99,
|
"tradable_balance_ratio": 0.99,
|
||||||
@@ -41,7 +41,8 @@
|
|||||||
"ccxt_config": {},
|
"ccxt_config": {},
|
||||||
"ccxt_async_config": {},
|
"ccxt_async_config": {},
|
||||||
"pair_whitelist": [
|
"pair_whitelist": [
|
||||||
"ETH/USDT"
|
"ETH/USDT",
|
||||||
|
"BTC/USDT"
|
||||||
],
|
],
|
||||||
"pair_blacklist": [
|
"pair_blacklist": [
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -42,9 +42,9 @@ class MACDStrategy(IStrategy):
|
|||||||
INTERFACE_VERSION = 3
|
INTERFACE_VERSION = 3
|
||||||
|
|
||||||
minimal_roi = {"0": 100}
|
minimal_roi = {"0": 100}
|
||||||
stoploss = -1
|
stoploss = -0.05
|
||||||
trailing_stop = False
|
trailing_stop = False
|
||||||
timeframe = '15m'
|
timeframe = '4h'
|
||||||
|
|
||||||
use_exit_signal = True
|
use_exit_signal = True
|
||||||
exit_profit_only = False
|
exit_profit_only = False
|
||||||
@@ -72,7 +72,7 @@ class MACDStrategy(IStrategy):
|
|||||||
dataframe["macd"] = macd["macd"]
|
dataframe["macd"] = macd["macd"]
|
||||||
dataframe["macdsignal"] = macd["macdsignal"]
|
dataframe["macdsignal"] = macd["macdsignal"]
|
||||||
dataframe["macdhist"] = macd["macdhist"]
|
dataframe["macdhist"] = macd["macdhist"]
|
||||||
dataframe['cci'] = ta.CCI(dataframe, 26)
|
dataframe['cci'] = ta.CCI(dataframe, 14)
|
||||||
dataframe['TD'] = self.TD(dataframe)
|
dataframe['TD'] = self.TD(dataframe)
|
||||||
return dataframe
|
return dataframe
|
||||||
|
|
||||||
@@ -80,15 +80,14 @@ class MACDStrategy(IStrategy):
|
|||||||
# 入场:1d与4h CCI < -100,且4h CCI上升(当前 > 前一根)
|
# 入场:1d与4h CCI < -100,且4h CCI上升(当前 > 前一根)
|
||||||
dataframe.loc[
|
dataframe.loc[
|
||||||
(
|
(
|
||||||
# (dataframe['macdhist'] < 0) &
|
(dataframe['macdhist'] < 0) &
|
||||||
# (dataframe['macdhist'] > dataframe['macdhist'].shift(1)) &
|
(dataframe['macdhist'] > dataframe['macdhist'].shift(1)) &
|
||||||
# (dataframe['macdsignal'] < 0) &
|
# (dataframe['macdsignal'] < 0) &
|
||||||
# (dataframe['macd'] < dataframe['macdsignal']) &
|
# (dataframe['macd'] < dataframe['macdsignal']) &
|
||||||
# (dataframe['cci'] < -100) &
|
(dataframe['cci'] < -100) &
|
||||||
# (dataframe['cci'].shift(1) < dataframe['cci']) &
|
(dataframe['cci'] > dataframe['cci'].shift(1))
|
||||||
# (dataframe['macdhist'] < 0) &
|
# (dataframe['macdhist'] < 0) &
|
||||||
(dataframe['TD'] == 1) &
|
# (dataframe['volume'] > 0)
|
||||||
(dataframe['volume'] > 0)
|
|
||||||
),
|
),
|
||||||
'enter_long',
|
'enter_long',
|
||||||
] = 1
|
] = 1
|
||||||
@@ -99,15 +98,17 @@ class MACDStrategy(IStrategy):
|
|||||||
# 离场:1d与4h CCI > 100,且4h CCI下降(当前 < 前一根)
|
# 离场:1d与4h CCI > 100,且4h CCI下降(当前 < 前一根)
|
||||||
dataframe.loc[
|
dataframe.loc[
|
||||||
(
|
(
|
||||||
# (dataframe['macdhist'] > 0) &
|
((dataframe['macdhist'] < 0) &
|
||||||
|
(dataframe['macdhist'].shift(1) > 0) )
|
||||||
|
| (qtpylib.crossed_below(dataframe['macd'], dataframe['macdsignal']) )
|
||||||
# (dataframe['macdhist'] < dataframe['macdhist'].shift(1)) &
|
# (dataframe['macdhist'] < dataframe['macdhist'].shift(1)) &
|
||||||
# (dataframe['macdsignal'] > 0) &
|
# (dataframe['macdsignal'] > 0) &
|
||||||
# (dataframe['macd'] > dataframe['macdsignal']) &
|
# (dataframe['macd'] > dataframe['macdsignal']) &
|
||||||
# (dataframe['cci'] > 100 )&
|
# (dataframe['cci'] > 100 )&
|
||||||
# (dataframe['cci'].shift(1) > dataframe['cci']) &
|
# (dataframe['cci'] < dataframe['cci'].shift(1)) &
|
||||||
# (dataframe['macdhist'] > 0) &
|
# (dataframe['macdhist'] > 0) &
|
||||||
(dataframe['TD'] == -1) &
|
# (dataframe['TD'] == -1) &
|
||||||
(dataframe['volume'] > 0)
|
# (dataframe['volume'] > 0)
|
||||||
),
|
),
|
||||||
'exit_long',
|
'exit_long',
|
||||||
] = 1
|
] = 1
|
||||||
|
|||||||
66
user_data/strategies/SimpleRSIStrategyOptimized.py
Normal file
66
user_data/strategies/SimpleRSIStrategyOptimized.py
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import talib.abstract as ta
|
||||||
|
import pandas as pd
|
||||||
|
import numpy as np
|
||||||
|
from freqtrade.strategy import IStrategy
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleRSIStrategyOptimized(IStrategy):
|
||||||
|
INTERFACE_VERSION = 3
|
||||||
|
can_short: bool = False
|
||||||
|
stoploss = -0.05
|
||||||
|
minimal_roi = {"0": 100} # 由止盈逻辑替代
|
||||||
|
timeframe = '1d'
|
||||||
|
|
||||||
|
# === 策略参数(可优化)===
|
||||||
|
ema_short = 10
|
||||||
|
ema_long = 20
|
||||||
|
rsi_period = 6
|
||||||
|
rsi_oversold = 20
|
||||||
|
|
||||||
|
def populate_indicators(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
|
||||||
|
# EMA
|
||||||
|
dataframe['ema_short'] = ta.EMA(dataframe['close'], timeperiod=self.ema_short)
|
||||||
|
dataframe['ema_long'] = ta.EMA(dataframe['close'], timeperiod=self.ema_long)
|
||||||
|
|
||||||
|
# RSI
|
||||||
|
dataframe['rsi'] = ta.RSI(dataframe['close'], timeperiod=self.rsi_period)
|
||||||
|
|
||||||
|
# 辅助:昨日值(shift 1)
|
||||||
|
dataframe['ema_short_prev'] = dataframe['ema_short'].shift(1)
|
||||||
|
dataframe['rsi_prev'] = dataframe['rsi'].shift(1)
|
||||||
|
|
||||||
|
return dataframe
|
||||||
|
|
||||||
|
def populate_entry_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
|
||||||
|
dataframe.loc[
|
||||||
|
(
|
||||||
|
# 趋势条件:EMA 短期 > 长期 且 短期 EMA 上升
|
||||||
|
(dataframe['ema_short'] > dataframe['ema_long']) &
|
||||||
|
(dataframe['ema_short'] > dataframe['ema_short_prev']) &
|
||||||
|
|
||||||
|
# RSI 超卖后反弹
|
||||||
|
(dataframe['rsi_prev'] <= self.rsi_oversold) &
|
||||||
|
(dataframe['rsi'] > dataframe['rsi_prev'])
|
||||||
|
),
|
||||||
|
'enter_long',
|
||||||
|
] = 1
|
||||||
|
|
||||||
|
return dataframe
|
||||||
|
|
||||||
|
def populate_exit_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
|
||||||
|
|
||||||
|
dataframe.loc[
|
||||||
|
(
|
||||||
|
# 趋势破坏:EMA 死叉
|
||||||
|
(dataframe['ema_short'] < dataframe['ema_long']) |
|
||||||
|
# 或价格跌破长期 EMA(支撑失效)
|
||||||
|
(dataframe['close'] < dataframe['ema_long'])
|
||||||
|
),
|
||||||
|
'exit_long',
|
||||||
|
] = 1
|
||||||
|
|
||||||
|
|
||||||
|
return dataframe
|
||||||
@@ -118,8 +118,8 @@ class SimpleRSIStrategyFixed(IStrategy):
|
|||||||
f"方向: {side}, "
|
f"方向: {side}, "
|
||||||
f"仓位: {position}"
|
f"仓位: {position}"
|
||||||
)
|
)
|
||||||
dingtalk.send_text(
|
# dingtalk.send_text(
|
||||||
content=f"订单成交 - 交易对: {trading_pair}, 时间: {fill_time}, 价格: {fill_price}, 方向: {side}, 仓位: {position}")
|
# content=f"订单成交 - 交易对: {trading_pair}, 时间: {fill_time}, 价格: {fill_price}, 方向: {side}, 仓位: {position}")
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
# flake8: noqa: F401
|
# flake8: noqa: F401
|
||||||
# isort: skip_file
|
# isort: skip_file
|
||||||
# --- Do not remove these imports ---
|
# --- Do not remove these imports ---
|
||||||
|
from dingtalk import DingTalkBot
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
@@ -37,12 +38,22 @@ import talib.abstract as ta
|
|||||||
from technical import qtpylib
|
from technical import qtpylib
|
||||||
|
|
||||||
|
|
||||||
|
webhook = "https://oapi.dingtalk.com/robot/send?access_token=21de667159edadd33172c6ec414a2addf9c6359189350ffd36819d2a20e8a0f4" # 填写你的webhook
|
||||||
|
secret = "SEC43a0fa0b29717f98637a119b92a0bd5f7b2b6da671bdd2bd1279ed8323454d5e" # 填写你的加签token(如果有),否则留空
|
||||||
|
|
||||||
|
# CTA 群机器人
|
||||||
|
# webhook = "https://oapi.dingtalk.com/robot/send?access_token=87c7abfcdd69b699c32da4e4f5981cd2ca6b0445474fc6ffb36f2ed0f6262fbb"
|
||||||
|
# secret = "SECf3d6b43f2f8a87ab91feffd052e71ec314fbf57a1842e483fe07af3c0a0e5aa6"
|
||||||
|
dingtalk = DingTalkBot(webhook, secret)
|
||||||
|
|
||||||
|
|
||||||
class CCIMultiTimeframeSpotStrategy(IStrategy):
|
class CCIMultiTimeframeSpotStrategy(IStrategy):
|
||||||
# 策略参数
|
# 策略参数
|
||||||
INTERFACE_VERSION = 3
|
INTERFACE_VERSION = 3
|
||||||
|
|
||||||
minimal_roi = {"0": 100}
|
minimal_roi = {"0": 100}
|
||||||
stoploss = -1
|
stoploss = -1
|
||||||
|
use_custom_stoploss = False
|
||||||
trailing_stop = False
|
trailing_stop = False
|
||||||
timeframe = '4h'
|
timeframe = '4h'
|
||||||
|
|
||||||
@@ -50,6 +61,37 @@ class CCIMultiTimeframeSpotStrategy(IStrategy):
|
|||||||
exit_profit_only = False
|
exit_profit_only = False
|
||||||
ignore_roi_if_entry_signal = False
|
ignore_roi_if_entry_signal = False
|
||||||
|
|
||||||
|
def order_filled(self, pair: str, trade: Trade, order: Order, current_time: datetime, **kwargs) -> None:
|
||||||
|
# 交易对
|
||||||
|
trading_pair = pair
|
||||||
|
|
||||||
|
# 时间
|
||||||
|
fill_time = order.order_filled_date or current_time
|
||||||
|
|
||||||
|
# 价格
|
||||||
|
fill_price = order.average or order.price
|
||||||
|
|
||||||
|
# 买入还是卖出
|
||||||
|
side = order.ft_order_side # 'buy' 或 'sell'
|
||||||
|
|
||||||
|
# 仓位(当前持仓数量)
|
||||||
|
position = trade.amount
|
||||||
|
|
||||||
|
# 或者使用日志
|
||||||
|
logger.info(
|
||||||
|
f"订单成交 - 交易对: {trading_pair}, "
|
||||||
|
f"时间: {fill_time}, "
|
||||||
|
f"价格: {fill_price}, "
|
||||||
|
f"方向: {side}, "
|
||||||
|
f"仓位: {position}"
|
||||||
|
)
|
||||||
|
# if self.config["runmode"].value in ("live", "dry_run"):
|
||||||
|
logger.info(f"11111111{self.config['runmode']}")
|
||||||
|
# dingtalk.send_text(
|
||||||
|
# content=f"订单成交 - 交易对: {trading_pair}, 时间: {fill_time}, 价格: {fill_price}, 方向: {side}, 仓位: {position}")
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
def TD(self, dataframe:DataFrame):
|
def TD(self, dataframe:DataFrame):
|
||||||
close = dataframe['close'].to_list()
|
close = dataframe['close'].to_list()
|
||||||
td = [0,0,0,0]
|
td = [0,0,0,0]
|
||||||
@@ -98,6 +140,7 @@ class CCIMultiTimeframeSpotStrategy(IStrategy):
|
|||||||
|
|
||||||
# dataframe['adx_hist_4h'] = ta.ADX(dataframe['macdhist_4h'])
|
# dataframe['adx_hist_4h'] = ta.ADX(dataframe['macdhist_4h'])
|
||||||
dataframe['TD'] = self.TD(dataframe)
|
dataframe['TD'] = self.TD(dataframe)
|
||||||
|
dataframe["atr"] = ta.ATR(dataframe, timeperiod=14)
|
||||||
|
|
||||||
logger.info(dataframe.tail())
|
logger.info(dataframe.tail())
|
||||||
return dataframe
|
return dataframe
|
||||||
@@ -132,3 +175,14 @@ class CCIMultiTimeframeSpotStrategy(IStrategy):
|
|||||||
|
|
||||||
return dataframe
|
return dataframe
|
||||||
|
|
||||||
|
def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime,
|
||||||
|
current_rate: float, current_profit: float, after_fill: bool,
|
||||||
|
**kwargs) -> float | None:
|
||||||
|
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
|
||||||
|
candle = dataframe.iloc[-1].squeeze()
|
||||||
|
side = 1 if trade.is_short else -1
|
||||||
|
return stoploss_from_absolute(current_rate + (side * candle["atr"] * 3),
|
||||||
|
current_rate=current_rate,
|
||||||
|
is_short=trade.is_short,
|
||||||
|
leverage=trade.leverage)
|
||||||
|
|
||||||
|
|||||||
@@ -36,23 +36,26 @@ import talib.abstract as ta
|
|||||||
from technical import qtpylib
|
from technical import qtpylib
|
||||||
|
|
||||||
|
|
||||||
# This class is a sample. Feel free to customize it.
|
# 基于x.py策略的MA60/MA120趋势跟踪策略
|
||||||
class SampleStrategy(IStrategy):
|
class MA60MA120TrendStrategy(IStrategy):
|
||||||
"""
|
"""
|
||||||
This is a sample strategy to inspire you.
|
基于x.py策略的MA60/MA120趋势跟踪策略
|
||||||
More information in https://www.freqtrade.io/en/latest/strategy-customization/
|
|
||||||
|
策略逻辑:
|
||||||
You can:
|
1. 买入条件:
|
||||||
:return: a Dataframe with all mandatory indicators for the strategies
|
- 价格在MA60上方且距离MA60不超过10%
|
||||||
- Rename the class name (Do not forget to update class_name)
|
- MA60呈上升趋势(过去5天中至少3天上升)
|
||||||
- Add any methods you want to build your strategy
|
- 成交量放大(当日成交量大于过去5日平均)
|
||||||
- Add any lib you need to build your strategy
|
- 前5天中下跌天数不超过2天
|
||||||
|
- 当天收盘价大于5天前的价格
|
||||||
You must keep:
|
- 价格在MA120上方
|
||||||
- the lib in the section "Do not remove these libs"
|
- MA120过去5天不是下降趋势
|
||||||
- the methods: populate_indicators, populate_entry_trend, populate_exit_trend
|
- RSI小于阈值(默认70)
|
||||||
You should keep:
|
|
||||||
- timeframe, minimal_roi, stoploss, trailing_*
|
2. 卖出条件:
|
||||||
|
- 价格跌破MA60且跌幅超过10%
|
||||||
|
|
||||||
|
更多信息: https://www.freqtrade.io/en/latest/strategy-customization/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Strategy interface version - allow new iterations of the strategy interface.
|
# Strategy interface version - allow new iterations of the strategy interface.
|
||||||
@@ -63,26 +66,25 @@ class SampleStrategy(IStrategy):
|
|||||||
can_short: bool = False
|
can_short: bool = False
|
||||||
|
|
||||||
# Minimal ROI designed for the strategy.
|
# Minimal ROI designed for the strategy.
|
||||||
# This attribute will be overridden if the config file contains "minimal_roi".
|
# 基于x.py策略,使用更保守的ROI设置
|
||||||
minimal_roi = {
|
# minimal_roi = {
|
||||||
# "120": 0.0, # exit after 120 minutes at break even
|
# "30": 0.3, # 20% 目标收益
|
||||||
"60": 0.01,
|
# # ""
|
||||||
"30": 0.02,
|
# }
|
||||||
"0": 0.04,
|
|
||||||
}
|
|
||||||
|
|
||||||
# Optimal stoploss designed for the strategy.
|
# Optimal stoploss designed for the strategy.
|
||||||
# This attribute will be overridden if the config file contains "stoploss".
|
# 基于x.py策略的卖出条件:跌破MA60且跌幅超过10%
|
||||||
stoploss = -0.10
|
stoploss = -1
|
||||||
|
|
||||||
# Trailing stoploss
|
# Trailing stoploss
|
||||||
trailing_stop = False
|
# trailing_stop = True
|
||||||
# trailing_only_offset_is_reached = False
|
# trailing_only_offset_is_reached = False
|
||||||
# trailing_stop_positive = 0.01
|
# trailing_stop_positive = 0.3
|
||||||
# trailing_stop_positive_offset = 0.0 # Disabled / not configured
|
# trailing_stop_positive_offset = 0.0 # Disabled / not configured
|
||||||
|
|
||||||
# Optimal timeframe for the strategy.
|
# Optimal timeframe for the strategy.
|
||||||
timeframe = "5m"
|
# 使用日线数据,因为x.py策略基于日线
|
||||||
|
timeframe = "1d"
|
||||||
|
|
||||||
# Run "populate_indicators()" only for new candle.
|
# Run "populate_indicators()" only for new candle.
|
||||||
process_only_new_candles = True
|
process_only_new_candles = True
|
||||||
@@ -92,13 +94,15 @@ class SampleStrategy(IStrategy):
|
|||||||
exit_profit_only = False
|
exit_profit_only = False
|
||||||
ignore_roi_if_entry_signal = False
|
ignore_roi_if_entry_signal = False
|
||||||
|
|
||||||
# Hyperoptable parameters
|
# 移除原有的RSI参数,因为新策略使用固定值
|
||||||
buy_rsi = IntParameter(low=1, high=50, default=30, space="buy", optimize=True, load=True)
|
# 可以添加其他可优化的参数
|
||||||
sell_rsi = IntParameter(low=50, high=100, default=70, space="sell", optimize=True, load=True)
|
ma60_period = IntParameter(low=30, high=90, default=60, space="buy", optimize=True, load=True)
|
||||||
short_rsi = IntParameter(low=51, high=100, default=70, space="sell", optimize=True, load=True)
|
ma120_period = IntParameter(low=60, high=180, default=120, space="buy", optimize=True, load=True)
|
||||||
exit_short_rsi = IntParameter(low=1, high=50, default=30, space="buy", optimize=True, load=True)
|
rsi_threshold = IntParameter(low=50, high=80, default=70, space="buy", optimize=True, load=True)
|
||||||
|
volume_surge_multiplier = DecimalParameter(low=0.5, high=2.0, default=1.0, space="buy", optimize=True, load=True)
|
||||||
|
|
||||||
# Number of candles the strategy requires before producing valid signals
|
# Number of candles the strategy requires before producing valid signals
|
||||||
|
# 需要更多数据来计算MA120和趋势指标
|
||||||
startup_candle_count: int = 200
|
startup_candle_count: int = 200
|
||||||
|
|
||||||
# Optional order type mapping.
|
# Optional order type mapping.
|
||||||
@@ -114,16 +118,21 @@ class SampleStrategy(IStrategy):
|
|||||||
|
|
||||||
plot_config = {
|
plot_config = {
|
||||||
"main_plot": {
|
"main_plot": {
|
||||||
"tema": {},
|
"ma60": {"color": "blue", "width": 2},
|
||||||
"sar": {"color": "white"},
|
"ma120": {"color": "orange", "width": 2},
|
||||||
},
|
},
|
||||||
"subplots": {
|
"subplots": {
|
||||||
"MACD": {
|
|
||||||
"macd": {"color": "blue"},
|
|
||||||
"macdsignal": {"color": "orange"},
|
|
||||||
},
|
|
||||||
"RSI": {
|
"RSI": {
|
||||||
"rsi": {"color": "red"},
|
"rsi": {"color": "red"},
|
||||||
|
"rsi_threshold": {"color": "gray", "type": "line", "width": 1},
|
||||||
|
},
|
||||||
|
"Volume": {
|
||||||
|
"volume": {"color": "lightblue"},
|
||||||
|
"volume_5ma": {"color": "blue"},
|
||||||
|
},
|
||||||
|
"Signals": {
|
||||||
|
"buy_signal": {"color": "green", "type": "scatter", "marker": "^"},
|
||||||
|
"sell_signal": {"color": "red", "type": "scatter", "marker": "v"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -143,284 +152,211 @@ class SampleStrategy(IStrategy):
|
|||||||
|
|
||||||
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||||
"""
|
"""
|
||||||
Adds several different TA indicators to the given DataFrame
|
基于x.py策略的技术指标计算
|
||||||
|
实现MA60/MA120趋势跟踪策略的所有必要指标
|
||||||
Performance Note: For the best performance be frugal on the number of indicators
|
|
||||||
you are using. Let uncomment only the indicator you are using in your strategies
|
|
||||||
or your hyperopt configuration, otherwise you will waste your memory and CPU usage.
|
|
||||||
:param dataframe: Dataframe with data from the exchange
|
:param dataframe: Dataframe with data from the exchange
|
||||||
:param metadata: Additional information, like the currently traded pair
|
:param metadata: Additional information, like the currently traded pair
|
||||||
:return: a Dataframe with all mandatory indicators for the strategies
|
:return: a Dataframe with all mandatory indicators for the strategies
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Momentum Indicators
|
# 基础移动平均线指标
|
||||||
|
# ------------------------------------
|
||||||
|
|
||||||
|
# MA60 - 使用可优化参数
|
||||||
|
dataframe['ma60'] = ta.SMA(dataframe, timeperiod=60)
|
||||||
|
|
||||||
|
# MA120 - 使用可优化参数
|
||||||
|
dataframe['ma120'] = ta.SMA(dataframe, timeperiod=120)
|
||||||
|
|
||||||
|
# RSI指标
|
||||||
|
dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
|
||||||
|
|
||||||
|
# 成交量相关指标
|
||||||
|
# ------------------------------------
|
||||||
|
|
||||||
|
# 成交量5日移动平均
|
||||||
|
dataframe['volume_5ma'] = dataframe['volume'].rolling(window=5).mean()
|
||||||
|
|
||||||
|
# 成交量放大信号:当日成交量大于过去5日平均的倍数
|
||||||
|
dataframe['volume_surge'] = dataframe['volume'] > (dataframe['volume_5ma'] * 1)
|
||||||
|
|
||||||
|
# 价格与MA60关系指标
|
||||||
# ------------------------------------
|
# ------------------------------------
|
||||||
|
|
||||||
# ADX
|
# 价格在MA60上方且距离MA60不超过10%
|
||||||
dataframe["adx"] = ta.ADX(dataframe)
|
dataframe['price_above_ma60'] = (
|
||||||
|
(dataframe['close'] > dataframe['ma60']) &
|
||||||
# # Plus Directional Indicator / Movement
|
((dataframe['ma60'] - dataframe['close']) / dataframe['close'] < 0.1)
|
||||||
# dataframe['plus_dm'] = ta.PLUS_DM(dataframe)
|
|
||||||
# dataframe['plus_di'] = ta.PLUS_DI(dataframe)
|
|
||||||
|
|
||||||
# # Minus Directional Indicator / Movement
|
|
||||||
# dataframe['minus_dm'] = ta.MINUS_DM(dataframe)
|
|
||||||
# dataframe['minus_di'] = ta.MINUS_DI(dataframe)
|
|
||||||
|
|
||||||
# # Aroon, Aroon Oscillator
|
|
||||||
# aroon = ta.AROON(dataframe)
|
|
||||||
# dataframe['aroonup'] = aroon['aroonup']
|
|
||||||
# dataframe['aroondown'] = aroon['aroondown']
|
|
||||||
# dataframe['aroonosc'] = ta.AROONOSC(dataframe)
|
|
||||||
|
|
||||||
# # Awesome Oscillator
|
|
||||||
# dataframe['ao'] = qtpylib.awesome_oscillator(dataframe)
|
|
||||||
|
|
||||||
# # Keltner Channel
|
|
||||||
# keltner = qtpylib.keltner_channel(dataframe)
|
|
||||||
# dataframe["kc_upperband"] = keltner["upper"]
|
|
||||||
# dataframe["kc_lowerband"] = keltner["lower"]
|
|
||||||
# dataframe["kc_middleband"] = keltner["mid"]
|
|
||||||
# dataframe["kc_percent"] = (
|
|
||||||
# (dataframe["close"] - dataframe["kc_lowerband"]) /
|
|
||||||
# (dataframe["kc_upperband"] - dataframe["kc_lowerband"])
|
|
||||||
# )
|
|
||||||
# dataframe["kc_width"] = (
|
|
||||||
# (dataframe["kc_upperband"] - dataframe["kc_lowerband"]) / dataframe["kc_middleband"]
|
|
||||||
# )
|
|
||||||
|
|
||||||
# # Ultimate Oscillator
|
|
||||||
# dataframe['uo'] = ta.ULTOSC(dataframe)
|
|
||||||
|
|
||||||
# # Commodity Channel Index: values [Oversold:-100, Overbought:100]
|
|
||||||
# dataframe['cci'] = ta.CCI(dataframe)
|
|
||||||
|
|
||||||
# RSI
|
|
||||||
dataframe["rsi"] = ta.RSI(dataframe)
|
|
||||||
|
|
||||||
# # Inverse Fisher transform on RSI: values [-1.0, 1.0] (https://goo.gl/2JGGoy)
|
|
||||||
# rsi = 0.1 * (dataframe['rsi'] - 50)
|
|
||||||
# dataframe['fisher_rsi'] = (np.exp(2 * rsi) - 1) / (np.exp(2 * rsi) + 1)
|
|
||||||
|
|
||||||
# # Inverse Fisher transform on RSI normalized: values [0.0, 100.0] (https://goo.gl/2JGGoy)
|
|
||||||
# dataframe['fisher_rsi_norma'] = 50 * (dataframe['fisher_rsi'] + 1)
|
|
||||||
|
|
||||||
# # Stochastic Slow
|
|
||||||
# stoch = ta.STOCH(dataframe)
|
|
||||||
# dataframe['slowd'] = stoch['slowd']
|
|
||||||
# dataframe['slowk'] = stoch['slowk']
|
|
||||||
|
|
||||||
# Stochastic Fast
|
|
||||||
stoch_fast = ta.STOCHF(dataframe)
|
|
||||||
dataframe["fastd"] = stoch_fast["fastd"]
|
|
||||||
dataframe["fastk"] = stoch_fast["fastk"]
|
|
||||||
|
|
||||||
# # Stochastic RSI
|
|
||||||
# Please read https://github.com/freqtrade/freqtrade/issues/2961 before using this.
|
|
||||||
# STOCHRSI is NOT aligned with tradingview, which may result in non-expected results.
|
|
||||||
# stoch_rsi = ta.STOCHRSI(dataframe)
|
|
||||||
# dataframe['fastd_rsi'] = stoch_rsi['fastd']
|
|
||||||
# dataframe['fastk_rsi'] = stoch_rsi['fastk']
|
|
||||||
|
|
||||||
# MACD
|
|
||||||
macd = ta.MACD(dataframe)
|
|
||||||
dataframe["macd"] = macd["macd"]
|
|
||||||
dataframe["macdsignal"] = macd["macdsignal"]
|
|
||||||
dataframe["macdhist"] = macd["macdhist"]
|
|
||||||
|
|
||||||
# MFI
|
|
||||||
dataframe["mfi"] = ta.MFI(dataframe)
|
|
||||||
|
|
||||||
# # ROC
|
|
||||||
# dataframe['roc'] = ta.ROC(dataframe)
|
|
||||||
|
|
||||||
# Overlap Studies
|
|
||||||
# ------------------------------------
|
|
||||||
|
|
||||||
# Bollinger Bands
|
|
||||||
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
|
|
||||||
dataframe["bb_lowerband"] = bollinger["lower"]
|
|
||||||
dataframe["bb_middleband"] = bollinger["mid"]
|
|
||||||
dataframe["bb_upperband"] = bollinger["upper"]
|
|
||||||
dataframe["bb_percent"] = (dataframe["close"] - dataframe["bb_lowerband"]) / (
|
|
||||||
dataframe["bb_upperband"] - dataframe["bb_lowerband"]
|
|
||||||
)
|
)
|
||||||
dataframe["bb_width"] = (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe[
|
|
||||||
"bb_middleband"
|
# 价格在MA120上方
|
||||||
]
|
dataframe['price_above_ma120'] = dataframe['close'] > dataframe['ma120']
|
||||||
|
|
||||||
# Bollinger Bands - Weighted (EMA based instead of SMA)
|
# 前一天在MA60附近(95%-105%)
|
||||||
# weighted_bollinger = qtpylib.weighted_bollinger_bands(
|
dataframe['prev_near_ma'] = (
|
||||||
# qtpylib.typical_price(dataframe), window=20, stds=2
|
(dataframe['close'].shift(1) >= dataframe['ma60'].shift(1) * 0.95) &
|
||||||
# )
|
(dataframe['close'].shift(1) <= dataframe['ma60'].shift(1) * 1.05)
|
||||||
# dataframe["wbb_upperband"] = weighted_bollinger["upper"]
|
)
|
||||||
# dataframe["wbb_lowerband"] = weighted_bollinger["lower"]
|
|
||||||
# dataframe["wbb_middleband"] = weighted_bollinger["mid"]
|
# 前一天在MA60下方
|
||||||
# dataframe["wbb_percent"] = (
|
dataframe['prev_below_ma'] = dataframe['close'].shift(1) < dataframe['ma60'].shift(1)
|
||||||
# (dataframe["close"] - dataframe["wbb_lowerband"]) /
|
|
||||||
# (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"])
|
# 突破信号:前一天在MA60附近或下方,当天在MA60上方
|
||||||
# )
|
dataframe['breakthrough'] = (
|
||||||
# dataframe["wbb_width"] = (
|
(dataframe['prev_below_ma'] | dataframe['prev_near_ma']) &
|
||||||
# (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) /
|
dataframe['price_above_ma60']
|
||||||
# dataframe["wbb_middleband"]
|
)
|
||||||
# )
|
|
||||||
|
# MA60趋势指标
|
||||||
# # EMA - Exponential Moving Average
|
|
||||||
# dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3)
|
|
||||||
# dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5)
|
|
||||||
# dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10)
|
|
||||||
# dataframe['ema21'] = ta.EMA(dataframe, timeperiod=21)
|
|
||||||
# dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)
|
|
||||||
# dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100)
|
|
||||||
|
|
||||||
# # SMA - Simple Moving Average
|
|
||||||
# dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3)
|
|
||||||
# dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5)
|
|
||||||
# dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10)
|
|
||||||
# dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21)
|
|
||||||
# dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50)
|
|
||||||
# dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100)
|
|
||||||
|
|
||||||
# Parabolic SAR
|
|
||||||
dataframe["sar"] = ta.SAR(dataframe)
|
|
||||||
|
|
||||||
# TEMA - Triple Exponential Moving Average
|
|
||||||
dataframe["tema"] = ta.TEMA(dataframe, timeperiod=9)
|
|
||||||
|
|
||||||
# Cycle Indicator
|
|
||||||
# ------------------------------------
|
# ------------------------------------
|
||||||
# Hilbert Transform Indicator - SineWave
|
|
||||||
hilbert = ta.HT_SINE(dataframe)
|
# 计算MA60的上升趋势(过去5天中至少3天上升)
|
||||||
dataframe["htsine"] = hilbert["sine"]
|
trend_days = 5
|
||||||
dataframe["htleadsine"] = hilbert["leadsine"]
|
ma_trend = []
|
||||||
|
for i in range(len(dataframe)):
|
||||||
# Pattern Recognition - Bullish candlestick patterns
|
if i < trend_days - 1:
|
||||||
|
ma_trend.append(False)
|
||||||
|
else:
|
||||||
|
start_idx = i - trend_days + 1
|
||||||
|
ma_values = dataframe['ma60'].iloc[start_idx:i+1].values
|
||||||
|
if pd.isna(ma_values).any():
|
||||||
|
ma_trend.append(False)
|
||||||
|
else:
|
||||||
|
# 检查是否呈上升趋势(至少3天上升)
|
||||||
|
ma_trend.append(sum(ma_values[1:] > ma_values[:-1]) > trend_days - 3)
|
||||||
|
dataframe['ma_uptrend'] = ma_trend
|
||||||
|
|
||||||
|
# MA120趋势指标
|
||||||
# ------------------------------------
|
# ------------------------------------
|
||||||
# # Hammer: values [0, 100]
|
|
||||||
# dataframe['CDLHAMMER'] = ta.CDLHAMMER(dataframe)
|
# 计算MA120的趋势(过去5天)不能为下降
|
||||||
# # Inverted Hammer: values [0, 100]
|
ma120_trend = []
|
||||||
# dataframe['CDLINVERTEDHAMMER'] = ta.CDLINVERTEDHAMMER(dataframe)
|
for i in range(len(dataframe)):
|
||||||
# # Dragonfly Doji: values [0, 100]
|
if i < 4:
|
||||||
# dataframe['CDLDRAGONFLYDOJI'] = ta.CDLDRAGONFLYDOJI(dataframe)
|
ma120_trend.append(True) # 数据不足时不限制
|
||||||
# # Piercing Line: values [0, 100]
|
else:
|
||||||
# dataframe['CDLPIERCING'] = ta.CDLPIERCING(dataframe) # values [0, 100]
|
window = dataframe['ma120'].iloc[i-4:i+1].values
|
||||||
# # Morningstar: values [0, 100]
|
if pd.isna(window).any():
|
||||||
# dataframe['CDLMORNINGSTAR'] = ta.CDLMORNINGSTAR(dataframe) # values [0, 100]
|
ma120_trend.append(True)
|
||||||
# # Three White Soldiers: values [0, 100]
|
else:
|
||||||
# dataframe['CDL3WHITESOLDIERS'] = ta.CDL3WHITESOLDIERS(dataframe) # values [0, 100]
|
# 只要有一天不是上升就不是下降趋势
|
||||||
|
ma120_trend.append((window[1:] >= window[:-1]).all())
|
||||||
# Pattern Recognition - Bearish candlestick patterns
|
dataframe['ma120_not_downtrend'] = ma120_trend
|
||||||
|
|
||||||
|
# 价格动量指标
|
||||||
# ------------------------------------
|
# ------------------------------------
|
||||||
# # Hanging Man: values [0, 100]
|
|
||||||
# dataframe['CDLHANGINGMAN'] = ta.CDLHANGINGMAN(dataframe)
|
# 阳线条件:收盘价 > 开盘价,且阳线幅度至少0.5%
|
||||||
# # Shooting Star: values [0, 100]
|
dataframe['is_bullish'] = (
|
||||||
# dataframe['CDLSHOOTINGSTAR'] = ta.CDLSHOOTINGSTAR(dataframe)
|
(dataframe['close'] > dataframe['open']) &
|
||||||
# # Gravestone Doji: values [0, 100]
|
((dataframe['close'] - dataframe['open']) / dataframe['open'] >= 0.005)
|
||||||
# dataframe['CDLGRAVESTONEDOJI'] = ta.CDLGRAVESTONEDOJI(dataframe)
|
)
|
||||||
# # Dark Cloud Cover: values [0, 100]
|
|
||||||
# dataframe['CDLDARKCLOUDCOVER'] = ta.CDLDARKCLOUDCOVER(dataframe)
|
# 前5天的下跌天数不超过2天
|
||||||
# # Evening Doji Star: values [0, 100]
|
dataframe['is_down_day'] = dataframe['close'] < dataframe['open']
|
||||||
# dataframe['CDLEVENINGDOJISTAR'] = ta.CDLEVENINGDOJISTAR(dataframe)
|
dataframe['down_days_in_5'] = dataframe['is_down_day'].rolling(window=5, min_periods=1).sum()
|
||||||
# # Evening Star: values [0, 100]
|
dataframe['not_too_many_downs'] = dataframe['down_days_in_5'] <= 2
|
||||||
# dataframe['CDLEVENINGSTAR'] = ta.CDLEVENINGSTAR(dataframe)
|
|
||||||
|
# 当天收盘价大于第五天前的价格
|
||||||
# Pattern Recognition - Bullish/Bearish candlestick patterns
|
dataframe['price_higher_than_5days_ago'] = dataframe['close'] > dataframe['close'].shift(5)
|
||||||
|
|
||||||
|
# RSI条件 - 使用可优化参数
|
||||||
|
dataframe['rsi_lt_threshold'] = dataframe['rsi'] < 70
|
||||||
|
|
||||||
|
# 综合买入信号
|
||||||
# ------------------------------------
|
# ------------------------------------
|
||||||
# # Three Line Strike: values [0, -100, 100]
|
|
||||||
# dataframe['CDL3LINESTRIKE'] = ta.CDL3LINESTRIKE(dataframe)
|
dataframe["buy_signal"] = (
|
||||||
# # Spinning Top: values [0, -100, 100]
|
dataframe["price_above_ma60"] &
|
||||||
# dataframe['CDLSPINNINGTOP'] = ta.CDLSPINNINGTOP(dataframe) # values [0, -100, 100]
|
dataframe["ma_uptrend"] &
|
||||||
# # Engulfing: values [0, -100, 100]
|
dataframe['ma60'].notna() &
|
||||||
# dataframe['CDLENGULFING'] = ta.CDLENGULFING(dataframe) # values [0, -100, 100]
|
dataframe["volume_surge"] &
|
||||||
# # Harami: values [0, -100, 100]
|
dataframe["not_too_many_downs"] &
|
||||||
# dataframe['CDLHARAMI'] = ta.CDLHARAMI(dataframe) # values [0, -100, 100]
|
dataframe["price_higher_than_5days_ago"] &
|
||||||
# # Three Outside Up/Down: values [0, -100, 100]
|
dataframe['price_above_ma120'] &
|
||||||
# dataframe['CDL3OUTSIDE'] = ta.CDL3OUTSIDE(dataframe) # values [0, -100, 100]
|
dataframe['ma120_not_downtrend']
|
||||||
# # Three Inside Up/Down: values [0, -100, 100]
|
# &
|
||||||
# dataframe['CDL3INSIDE'] = ta.CDL3INSIDE(dataframe) # values [0, -100, 100]
|
# dataframe['rsi_lt_threshold']
|
||||||
|
)
|
||||||
# # Chart type
|
|
||||||
# # ------------------------------------
|
# 卖出信号:价格跌破MA60且跌幅超过10%
|
||||||
# # Heikin Ashi Strategy
|
dataframe['sell_signal'] = (
|
||||||
# heikinashi = qtpylib.heikinashi(dataframe)
|
(~dataframe['price_above_ma60']) &
|
||||||
# dataframe['ha_open'] = heikinashi['open']
|
((dataframe['ma60'] - dataframe['close']) / dataframe['ma60'] > 0.1) &
|
||||||
# dataframe['ha_close'] = heikinashi['close']
|
dataframe['ma60'].notna()
|
||||||
# dataframe['ha_high'] = heikinashi['high']
|
)
|
||||||
# dataframe['ha_low'] = heikinashi['low']
|
|
||||||
|
|
||||||
# Retrieve best bid and best ask from the orderbook
|
|
||||||
# ------------------------------------
|
|
||||||
"""
|
|
||||||
# first check if dataprovider is available
|
|
||||||
if self.dp:
|
|
||||||
if self.dp.runmode.value in ('live', 'dry_run'):
|
|
||||||
ob = self.dp.orderbook(metadata['pair'], 1)
|
|
||||||
dataframe['best_bid'] = ob['bids'][0][0]
|
|
||||||
dataframe['best_ask'] = ob['asks'][0][0]
|
|
||||||
"""
|
|
||||||
|
|
||||||
return dataframe
|
return dataframe
|
||||||
|
|
||||||
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||||
"""
|
"""
|
||||||
Based on TA indicators, populates the entry signal for the given dataframe
|
基于x.py策略的买入信号逻辑
|
||||||
|
实现MA60/MA120趋势跟踪策略的买入条件
|
||||||
:param dataframe: DataFrame
|
:param dataframe: DataFrame
|
||||||
:param metadata: Additional information, like the currently traded pair
|
:param metadata: Additional information, like the currently traded pair
|
||||||
:return: DataFrame with entry columns populated
|
:return: DataFrame with entry columns populated
|
||||||
"""
|
"""
|
||||||
|
# 多头买入信号 - 基于x.py的buy_signal逻辑
|
||||||
dataframe.loc[
|
dataframe.loc[
|
||||||
(
|
(
|
||||||
# Signal: RSI crosses above 30
|
# 价格在MA60上方且距离MA60不超过10%
|
||||||
(qtpylib.crossed_above(dataframe["rsi"], self.buy_rsi.value))
|
dataframe["price_above_ma60"] &
|
||||||
& (dataframe["tema"] <= dataframe["bb_middleband"]) # Guard: tema below BB middle
|
# MA60呈上升趋势(过去5天中至少3天上升)
|
||||||
& (dataframe["tema"] > dataframe["tema"].shift(1)) # Guard: tema is raising
|
dataframe["ma_uptrend"] &
|
||||||
& (dataframe["volume"] > 0) # Make sure Volume is not 0
|
# MA60数据有效
|
||||||
|
dataframe['ma60'].notna() &
|
||||||
|
# 成交量放大(当日成交量大于过去5日平均)
|
||||||
|
dataframe["volume_surge"] &
|
||||||
|
# 前5天中下跌天数不超过2天
|
||||||
|
dataframe["not_too_many_downs"] &
|
||||||
|
# 当天收盘价大于5天前的价格
|
||||||
|
dataframe["price_higher_than_5days_ago"] &
|
||||||
|
# 价格在MA120上方
|
||||||
|
dataframe['price_above_ma120'] &
|
||||||
|
# MA120过去5天不是下降趋势
|
||||||
|
dataframe['ma120_not_downtrend'] &
|
||||||
|
# RSI小于阈值
|
||||||
|
dataframe['rsi_lt_threshold'] &
|
||||||
|
# 确保成交量不为0
|
||||||
|
(dataframe["volume"] > 0)
|
||||||
),
|
),
|
||||||
"enter_long",
|
"enter_long",
|
||||||
] = 1
|
] = 1
|
||||||
|
|
||||||
dataframe.loc[
|
|
||||||
(
|
|
||||||
# Signal: RSI crosses above 70
|
|
||||||
(qtpylib.crossed_above(dataframe["rsi"], self.short_rsi.value))
|
|
||||||
& (dataframe["tema"] > dataframe["bb_middleband"]) # Guard: tema above BB middle
|
|
||||||
& (dataframe["tema"] < dataframe["tema"].shift(1)) # Guard: tema is falling
|
|
||||||
& (dataframe["volume"] > 0) # Make sure Volume is not 0
|
|
||||||
),
|
|
||||||
"enter_short",
|
|
||||||
] = 1
|
|
||||||
|
|
||||||
return dataframe
|
return dataframe
|
||||||
|
|
||||||
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||||
"""
|
"""
|
||||||
Based on TA indicators, populates the exit signal for the given dataframe
|
基于x.py策略的卖出信号逻辑
|
||||||
|
实现MA60/MA120趋势跟踪策略的卖出条件
|
||||||
:param dataframe: DataFrame
|
:param dataframe: DataFrame
|
||||||
:param metadata: Additional information, like the currently traded pair
|
:param metadata: Additional information, like the currently traded pair
|
||||||
:return: DataFrame with exit columns populated
|
:return: DataFrame with exit columns populated
|
||||||
"""
|
"""
|
||||||
|
# 多头卖出信号 - 基于x.py的sell_signal逻辑
|
||||||
|
# dataframe.loc[
|
||||||
|
# (
|
||||||
|
# # 价格跌破MA60且跌幅超过10%
|
||||||
|
# (~dataframe['price_above_ma60']) &
|
||||||
|
# ((dataframe['ma60'] - dataframe['close']) / dataframe['ma60'] > 0.1) &
|
||||||
|
# dataframe['ma60'].notna() &
|
||||||
|
# # 确保成交量不为0
|
||||||
|
# (dataframe["volume"] > 0)
|
||||||
|
# ),
|
||||||
|
# "exit_long",
|
||||||
|
# ] = 1
|
||||||
|
|
||||||
dataframe.loc[
|
dataframe.loc[
|
||||||
(
|
(
|
||||||
# Signal: RSI crosses above 70
|
# 价格跌破MA60且跌幅超过10%
|
||||||
(qtpylib.crossed_above(dataframe["rsi"], self.sell_rsi.value))
|
(dataframe['close'] < dataframe['ma120']) &
|
||||||
& (dataframe["tema"] > dataframe["bb_middleband"]) # Guard: tema above BB middle
|
dataframe['ma120'].notna() &
|
||||||
& (dataframe["tema"] < dataframe["tema"].shift(1)) # Guard: tema is falling
|
# 确保成交量不为0
|
||||||
& (dataframe["volume"] > 0) # Make sure Volume is not 0
|
(dataframe["volume"] > 0)
|
||||||
),
|
),
|
||||||
"exit_long",
|
"exit_long",
|
||||||
] = 1
|
] = 1
|
||||||
|
|
||||||
dataframe.loc[
|
|
||||||
(
|
|
||||||
# Signal: RSI crosses above 30
|
|
||||||
(qtpylib.crossed_above(dataframe["rsi"], self.exit_short_rsi.value))
|
|
||||||
&
|
|
||||||
# Guard: tema below BB middle
|
|
||||||
(dataframe["tema"] <= dataframe["bb_middleband"])
|
|
||||||
& (dataframe["tema"] > dataframe["tema"].shift(1)) # Guard: tema is raising
|
|
||||||
& (dataframe["volume"] > 0) # Make sure Volume is not 0
|
|
||||||
),
|
|
||||||
"exit_short",
|
|
||||||
] = 1
|
|
||||||
|
|
||||||
return dataframe
|
return dataframe
|
||||||
|
|||||||
Reference in New Issue
Block a user