添加裸K形态,前端添加KLC显示
This commit is contained in:
+74
@@ -51,6 +51,80 @@ class Chan_KLU_TYPE(Enum):
|
||||
SmallBear = auto()
|
||||
Cross = auto()
|
||||
|
||||
class Chan_KLU_PATTERN(Enum):
|
||||
# 单根K线形态
|
||||
HAMMER = auto() # 锤子线
|
||||
INVERTED_HAMMER = auto() # 倒锤子线
|
||||
SHOOTING_STAR = auto() # 射击之星
|
||||
HANGING_MAN = auto() # 上吊线
|
||||
DOJI = auto() # 十字星
|
||||
LONG_LEGGED_DOJI = auto() # 长腿十字星
|
||||
GRAVESTONE_DOJI = auto() # 墓碑十字星
|
||||
DRAGONFLY_DOJI = auto() # 蜻蜓十字星
|
||||
MARUBOZU = auto() # 光头光脚
|
||||
SPINNING_TOP = auto() # 纺锤线
|
||||
|
||||
# 双根K线形态
|
||||
BULLISH_ENGULFING = auto() # 看涨吞没
|
||||
BEARISH_ENGULFING = auto() # 看跌吞没
|
||||
PIERCING_LINE = auto() # 刺透形态
|
||||
DARK_CLOUD_COVER = auto() # 乌云盖顶
|
||||
TWEEZER_TOP = auto() # 镊子顶
|
||||
TWEEZER_BOTTOM = auto() # 镊子底
|
||||
HARAMI = auto() # 孕线
|
||||
BULLISH_HARAMI = auto() # 看涨孕线
|
||||
BEARISH_HARAMI = auto() # 看跌孕线
|
||||
|
||||
# 三根K线形态
|
||||
MORNING_STAR = auto() # 早晨之星
|
||||
EVENING_STAR = auto() # 黄昏之星
|
||||
THREE_WHITE_SOLDIERS = auto() # 红三兵
|
||||
THREE_BLACK_CROWS = auto() # 三只乌鸦
|
||||
THREE_INNER_UP = auto() # 上升三法
|
||||
THREE_INNER_DOWN = auto() # 下降三法
|
||||
ABANDONED_BABY = auto() # 弃婴形态
|
||||
|
||||
# 多根K线形态
|
||||
DOUBLE_TOP = auto() # 双顶
|
||||
DOUBLE_BOTTOM = auto() # 双底
|
||||
TRIPLE_TOP = auto() # 三顶
|
||||
TRIPLE_BOTTOM = auto() # 三底
|
||||
HEAD_AND_SHOULDERS = auto() # 头肩顶
|
||||
INVERSE_HEAD_SHOULDERS = auto() # 头肩底
|
||||
ROUNDING_BOTTOM = auto() # 圆弧底
|
||||
ROUNDING_TOP = auto() # 圆弧顶
|
||||
|
||||
# 缺口形态
|
||||
BREAKAWAY_GAP = auto() # 突破缺口
|
||||
RUNAWAY_GAP = auto() # 持续缺口
|
||||
EXHAUSTION_GAP = auto() # 衰竭缺口
|
||||
|
||||
# 特殊形态
|
||||
ISLAND_REVERSAL = auto() # 岛形反转
|
||||
KEY_REVERSAL = auto() # 关键反转
|
||||
INSIDE_BAR = auto() # 内包线
|
||||
OUTSIDE_BAR = auto() # 外包线
|
||||
|
||||
# 趋势形态
|
||||
HIGHER_HIGH = auto() # 更高高点
|
||||
HIGHER_LOW = auto() # 更高低点
|
||||
LOWER_HIGH = auto() # 更低高点
|
||||
LOWER_LOW = auto() # 更低低点
|
||||
|
||||
# 支撑阻力形态
|
||||
SUPPORT_BOUNCE = auto() # 支撑反弹
|
||||
RESISTANCE_REJECTION = auto() # 阻力拒绝
|
||||
BREAKOUT = auto() # 突破
|
||||
BREAKDOWN = auto() # 跌破
|
||||
|
||||
# 成交量相关形态
|
||||
VOLUME_SPIKE = auto() # 成交量激增
|
||||
VOLUME_DECLINE = auto() # 成交量萎缩
|
||||
|
||||
# 未知/无形态
|
||||
UNKNOWN = auto() # 未知形态
|
||||
|
||||
|
||||
class Chan_FX_TYPE(Enum):
|
||||
BOTTOM = auto()
|
||||
TOP = auto()
|
||||
|
||||
+5
-1
@@ -1,4 +1,4 @@
|
||||
from ChanEnum import Chan_FX_TYPE, Chan_KLU_TYPE, Chan_K_DIR, Chan_MACD_STATE, Chan_MACDHIST_STATE, Chan_PRICE_TREND
|
||||
from ChanEnum import Chan_FX_TYPE, Chan_KLU_TYPE, Chan_K_DIR, Chan_MACD_STATE, Chan_MACDHIST_STATE, Chan_PRICE_TREND, Chan_KLU_PATTERN
|
||||
class ChanKLU:
|
||||
def __init__(self, time, open, high, low, close, volume):
|
||||
# _time, _close, _open, _high, _low, _extra_info={}
|
||||
@@ -21,6 +21,7 @@ class ChanKLU:
|
||||
self.bb52lower = 0
|
||||
# === 新增:K线类型 ===
|
||||
self.kline_type = None # K线类型:大阳线、大阴线、小阳线、小阴线
|
||||
self.pattern = Chan_KLU_PATTERN.UNKNOWN
|
||||
|
||||
# === 新增:实时分型相关属性 ===
|
||||
self.pre = None # 前一根K线
|
||||
@@ -67,10 +68,13 @@ class ChanKLU:
|
||||
#print(self.open, self.close, self.high, self.low, self.candle_dir, self.strength)
|
||||
def set_macd_state(self, state):
|
||||
self.macd_state = state
|
||||
def set_pattern(self, pattern):
|
||||
self.pattern = pattern
|
||||
def cal_exception(self):
|
||||
if self.upper_shadow_ratio > 5 or self.lower_shadow_ratio > 5:
|
||||
self.exception = True
|
||||
#print(self.time, self.upper_shadow_ratio, self.lower_shadow_ratio, self.body, self.lower_shadow, self.upper_shadow, self.high, self.low, self.close, self.open)
|
||||
self.exception = False
|
||||
def set_trend(self, trend):
|
||||
self.trend = trend
|
||||
def set_next(self, next):
|
||||
|
||||
+68
-6
@@ -1,6 +1,6 @@
|
||||
from datetime import timedelta
|
||||
from pandas import DataFrame
|
||||
from ChanEnum import Chan_FX_TYPE, Chan_KLINE_DIR, Chan_BI_DIR, Chan_SEG_DIR, Chan_ZS_DIR, Chan_BSP_DIR, Chan_BSP_TYPE, Chan_KLC_FX, Chan_MACD_STATE, Chan_PRICE_TREND
|
||||
from ChanEnum import Chan_FX_TYPE, Chan_KLINE_DIR, Chan_BI_DIR, Chan_SEG_DIR, Chan_ZS_DIR, Chan_BSP_DIR, Chan_BSP_TYPE, Chan_KLC_FX, Chan_MACD_STATE, Chan_PRICE_TREND, Chan_KLU_PATTERN
|
||||
from ChanKLU import ChanKLU
|
||||
from ChanKLC import ChanKLC
|
||||
from ChanBI import ChanBI
|
||||
@@ -576,7 +576,7 @@ class ChanLun():
|
||||
if near_resistance_touch:
|
||||
# 若动量不强,则更偏空
|
||||
score -= 1 if (hist is None or pre_hist is None or hist <= pre_hist) else 0
|
||||
# 3.4) 多次对 EMA52 的“拒绝”配合 MACD 逆向:易形成压/支并反向
|
||||
# 3.4) 多次对 EMA52 的"拒绝"配合 MACD 逆向:易形成压/支并反向
|
||||
# 统计近窗口内的上/下拒绝次数:
|
||||
# - 上拒绝:价格位于 EMA52 下方,最高触及/越过 EMA52 但收盘仍在下方
|
||||
# - 下拒绝:价格位于 EMA52 上方,最低触及/跌破 EMA52 但收盘仍在上方
|
||||
@@ -701,7 +701,7 @@ class ChanLun():
|
||||
for i in range(1, len(hist_seq)):
|
||||
if abs(hist_seq[i]) < abs(hist_seq[i-1]):
|
||||
weaken_steps += 1
|
||||
# 近窗口对 EMA52 的“未能站上/跌破”统计(放宽窗口与条件)
|
||||
# 近窗口对 EMA52 的"未能站上/跌破"统计(放宽窗口与条件)
|
||||
window_ema = prev_klcs[-4:] if len(prev_klcs) > 0 else []
|
||||
no_up_break = False
|
||||
no_down_break = False
|
||||
@@ -1343,8 +1343,70 @@ class ChanLun():
|
||||
return klc_list
|
||||
|
||||
def get_klu_list(self, dataframe):
|
||||
return self.get_kl_data(dataframe)
|
||||
klu_list = self.get_kl_data(dataframe)
|
||||
return self.cal_klu_pattern(klu_list)
|
||||
def cal_klu_pattern(self, klu_list):
|
||||
"""
|
||||
计算裸K的pattern - 只识别反转形态
|
||||
"""
|
||||
if not klu_list or len(klu_list) < 3:
|
||||
return klu_list
|
||||
|
||||
def get_decimal(self, value):
|
||||
return Decimal("{:.2f}".format(value))
|
||||
for i, klu in enumerate(klu_list):
|
||||
# 单根K线反转模式识别
|
||||
self._detect_single_reversal_pattern(klu)
|
||||
|
||||
|
||||
# 验证形态是否成立
|
||||
if klu.pattern != Chan_KLU_PATTERN.UNKNOWN:
|
||||
print(klu.time, klu.pattern)
|
||||
return klu_list
|
||||
|
||||
def _detect_single_reversal_pattern(self, klu):
|
||||
"""检测单根K线反转模式"""
|
||||
body = abs(klu.close - klu.open)
|
||||
upper_shadow = klu.high - max(klu.close, klu.open)
|
||||
lower_shadow = min(klu.close, klu.open) - klu.low
|
||||
total_range = klu.high - klu.low
|
||||
|
||||
# 避免除零
|
||||
if total_range == 0:
|
||||
return
|
||||
|
||||
body_ratio = body / total_range
|
||||
upper_ratio = upper_shadow / total_range
|
||||
lower_ratio = lower_shadow / total_range
|
||||
|
||||
# 锤子线/上吊线 - 反转信号
|
||||
if lower_ratio / body_ratio >= 2:
|
||||
# 锤子线:底部反转,需要前一根是下跌趋势
|
||||
if klu.close > klu.open and klu.pre and klu.pre.close < klu.pre.open:
|
||||
klu.set_pattern(Chan_KLU_PATTERN.HAMMER) # 底部反转
|
||||
# 上吊线:顶部反转,需要前一根是上涨趋势
|
||||
elif klu.close < klu.open and klu.pre and klu.pre.close > klu.pre.open:
|
||||
klu.set_pattern(Chan_KLU_PATTERN.HANGING_MAN) # 顶部反转
|
||||
|
||||
# 倒锤子线/射击之星 - 反转信号
|
||||
elif upper_ratio / body_ratio >= 2:
|
||||
# 倒锤子线:底部反转,需要前一根是下跌趋势
|
||||
if klu.close > klu.open and klu.pre and klu.pre.close < klu.pre.open:
|
||||
klu.set_pattern(Chan_KLU_PATTERN.INVERTED_HAMMER) # 底部反转
|
||||
# 射击之星:顶部反转,需要前一根是上涨趋势
|
||||
elif klu.close < klu.open and klu.pre and klu.pre.close > klu.pre.open:
|
||||
klu.set_pattern(Chan_KLU_PATTERN.SHOOTING_STAR) # 顶部反转
|
||||
|
||||
# 十字星 - 反转信号
|
||||
elif body_ratio <= 0.1:
|
||||
if upper_ratio > 0.4 and lower_ratio > 0.4:
|
||||
klu.set_pattern(Chan_KLU_PATTERN.LONG_LEGGED_DOJI) # 强烈反转信号
|
||||
elif upper_ratio > 0.4 and lower_ratio <= 0.1:
|
||||
# 墓碑十字星:顶部反转,需要前一根是上涨趋势
|
||||
if klu.pre and klu.pre.close > klu.pre.open:
|
||||
klu.set_pattern(Chan_KLU_PATTERN.GRAVESTONE_DOJI) # 顶部反转
|
||||
elif lower_ratio > 0.4 and upper_ratio <= 0.1:
|
||||
# 蜻蜓十字星:底部反转,需要前一根是下跌趋势
|
||||
if klu.pre and klu.pre.close < klu.pre.open:
|
||||
klu.set_pattern(Chan_KLU_PATTERN.DRAGONFLY_DOJI) # 底部反转
|
||||
else:
|
||||
klu.set_pattern(Chan_KLU_PATTERN.DOJI) # 一般反转信号
|
||||
|
||||
+16
-11
@@ -112,10 +112,7 @@ class ChanSEG():
|
||||
last_zs = zs
|
||||
else:
|
||||
if bi.index > last_zs.bi_list[-1].index and bi.dir == Chan_BI_DIR.DOWN and bi.is_sure:
|
||||
if bi.low < last_zs.zg:
|
||||
last_zs.add_bi(bi.pre)
|
||||
last_zs.add_bi(bi)
|
||||
else:
|
||||
if bi.low > last_zs.zg or bi.high < last_zs.zd:
|
||||
last_zs.set_end_bi(last_zs.bi_list[-1], bi)
|
||||
if bi.next and bi.next.next and bi.next.next.is_sure and bi.next.next.index <= self.bi_list[-1].index and bi.check_bi_zs_overlap() and bi.dir == Chan_BI_DIR.DOWN:
|
||||
zg = min(bi.high, bi.next.high, bi.next.next.high)
|
||||
@@ -131,6 +128,12 @@ class ChanSEG():
|
||||
zs.add_bi(bi.next.next)
|
||||
zs_list.append(zs)
|
||||
last_zs = zs
|
||||
else:
|
||||
last_zs.add_bi(bi.pre)
|
||||
last_zs.add_bi(bi)
|
||||
if index == len(self.bi_list) - 1 and last_zs and not last_zs.is_sure:
|
||||
#print(bi.start_time, "BI", last_zs.is_sure)
|
||||
last_zs.set_end_bi(last_zs.bi_list[-1], last_zs.bi_list[-1])
|
||||
else:
|
||||
for index in range(1, len(self.bi_list)):
|
||||
bi = self.bi_list[index]
|
||||
@@ -151,10 +154,7 @@ class ChanSEG():
|
||||
last_zs = zs
|
||||
else:
|
||||
if bi.index > last_zs.bi_list[-1].index and bi.dir == Chan_BI_DIR.UP and bi.is_sure:
|
||||
if bi.high > last_zs.zd:
|
||||
last_zs.add_bi(bi.pre)
|
||||
last_zs.add_bi(bi)
|
||||
else:
|
||||
if bi.low > last_zs.zg or bi.high < last_zs.zd:
|
||||
last_zs.set_end_bi(last_zs.bi_list[-1], bi)
|
||||
if bi.next and bi.next.next and bi.next.next.is_sure and bi.next.next.index <= self.bi_list[-1].index and bi.check_bi_zs_overlap() and bi.dir == Chan_BI_DIR.UP:
|
||||
zg = min(bi.high, bi.next.high, bi.next.next.high)
|
||||
@@ -170,8 +170,13 @@ class ChanSEG():
|
||||
zs.add_bi(bi.next.next)
|
||||
zs_list.append(zs)
|
||||
last_zs = zs
|
||||
if index == len(self.bi_list) - 1 and last_zs and not last_zs.is_sure:
|
||||
last_zs.set_end_bi(last_zs.bi_list[-1], last_zs.bi_list[-1])
|
||||
else:
|
||||
last_zs.add_bi(bi.pre)
|
||||
last_zs.add_bi(bi)
|
||||
if index == len(self.bi_list) - 1 and last_zs and not last_zs.is_sure:
|
||||
#print(bi.start_time, "BI", last_zs.is_sure)
|
||||
last_zs.set_end_bi(last_zs.bi_list[-1], last_zs.bi_list[-1])
|
||||
|
||||
#print(self.start_time, len(zs_list))
|
||||
print(self.bi_list[-1].end_time, "end_bi")
|
||||
#print(self.bi_list[-1].end_time, "end_bi")
|
||||
return zs_list
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"strategy_name": "PatternTrader",
|
||||
"params": {
|
||||
"trailing": {
|
||||
"trailing_stop": false,
|
||||
"trailing_stop_positive": null,
|
||||
"trailing_stop_positive_offset": 0.0,
|
||||
"trailing_only_offset_is_reached": false
|
||||
},
|
||||
"max_open_trades": {
|
||||
"max_open_trades": 1
|
||||
},
|
||||
"buy": {
|
||||
"fast_ma": 9,
|
||||
"lev": 2.9,
|
||||
"slow_ma": 31,
|
||||
"time": 29
|
||||
},
|
||||
"sell": {
|
||||
"exit_delay": 6
|
||||
},
|
||||
"protection": {},
|
||||
"roi": {
|
||||
"0": 0.062,
|
||||
"6": 0.019,
|
||||
"16": 0.014,
|
||||
"40": 0
|
||||
},
|
||||
"stoploss": {
|
||||
"stoploss": -0.316
|
||||
}
|
||||
},
|
||||
"ft_stratparam_v": 1,
|
||||
"export_time": "2025-10-28 07:39:54.768566+00:00"
|
||||
}
|
||||
@@ -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()
|
||||
+26
@@ -1167,6 +1167,18 @@ def analyze():
|
||||
# 添加主周期分析结果到返回数据
|
||||
result.update({
|
||||
'kline_data': clean_dataframe_for_json(df).to_dict('records'),
|
||||
'klc_list': [{
|
||||
'date': klc.end_time if isinstance(klc.end_time, str) else klc.end_time.astimezone(client_tz).isoformat(),
|
||||
'open': float(klc.open),
|
||||
'high': float(klc.high),
|
||||
'low': float(klc.low),
|
||||
'close': float(klc.close),
|
||||
'volume': float(klc.volume) if hasattr(klc, 'volume') else 0,
|
||||
'direction': str(klc.dir).replace('Chan_KLINE_DIR.', ''),
|
||||
'fx_type': str(klc.fx).replace('Chan_FX_TYPE.', ''),
|
||||
'klc_fx_type': str(klc.klc_fx_type).replace('Chan_KLC_FX.', ''),
|
||||
'trend': str(klc.trend).replace('Chan_PRICE_TREND.', '')
|
||||
} for klc in analysis_result['klc_list'] if hasattr(klc, 'end_time') and klc.end_time],
|
||||
'bi_list': [{
|
||||
'start_time': bi.start_klc.end_time if isinstance(bi.start_klc.end_time, str) else bi.start_klc.end_time.astimezone(client_tz).isoformat(),
|
||||
'end_time': (bi.end_klc.end_time if isinstance(bi.end_klc.end_time, str) else bi.end_klc.end_time.astimezone(client_tz).isoformat()) if bi.end_klc else None,
|
||||
@@ -1354,6 +1366,20 @@ def analyze():
|
||||
# 添加小周期K线数据
|
||||
result['element_kline_data'] = clean_dataframe_for_json(element_df).to_dict('records')
|
||||
|
||||
# 添加小周期KLC列表
|
||||
result['element_klc_list'] = [{
|
||||
'date': klc.end_time if isinstance(klc.end_time, str) else klc.end_time.astimezone(client_tz).isoformat(),
|
||||
'open': float(klc.open),
|
||||
'high': float(klc.high),
|
||||
'low': float(klc.low),
|
||||
'close': float(klc.close),
|
||||
'volume': float(klc.volume) if hasattr(klc, 'volume') else 0,
|
||||
'direction': str(klc.dir).replace('Chan_KLINE_DIR.', ''),
|
||||
'fx_type': str(klc.fx).replace('Chan_FX_TYPE.', ''),
|
||||
'klc_fx_type': str(klc.klc_fx_type).replace('Chan_KLC_FX.', ''),
|
||||
'trend': str(klc.trend).replace('Chan_PRICE_TREND.', '')
|
||||
} for klc in element_analysis['klc_list'] if hasattr(klc, 'end_time') and klc.end_time]
|
||||
|
||||
result['element_seg_list'] = [{
|
||||
'start_time': seg.start_bi.start_klc.end_time if isinstance(seg.start_bi.start_klc.end_time, str) else seg.start_bi.start_klc.end_time.astimezone(client_tz).isoformat(),
|
||||
'end_time': (seg.end_bi.end_klc.end_time if isinstance(seg.end_bi.end_klc.end_time, str) else seg.end_bi.end_klc.end_time.astimezone(client_tz).isoformat()) if seg.end_bi else None,
|
||||
|
||||
+124
-88
@@ -648,7 +648,6 @@
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.bb-config-content {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
@@ -817,6 +816,7 @@
|
||||
<option value="baseline">基线</option>
|
||||
<option value="renko">砖型</option>
|
||||
<option value="heikin">平均K</option>
|
||||
<option value="klc">KLC</option>
|
||||
</select>
|
||||
<!-- 为兼容现有逻辑,保留隐藏的原始K线开关,与下拉同步 -->
|
||||
<input class="form-check-input" type="checkbox" id="showOriginalKline" checked style="display:none;">
|
||||
@@ -2548,6 +2548,19 @@
|
||||
const baseData = candles.map(c => ({ time: c.time, value: c.close }));
|
||||
series.setData(baseData);
|
||||
tvWidget.series.baselineSeries = series;
|
||||
} else if (klineType === 'klc') {
|
||||
// KLC显示模式 - 使用蜡烛线显示KLC数据
|
||||
const series = mainChart.addCandlestickSeries({
|
||||
upColor: '#dc3545',
|
||||
downColor: '#28a745',
|
||||
borderVisible: false,
|
||||
wickUpColor: '#dc3545',
|
||||
wickDownColor: '#28a745',
|
||||
});
|
||||
// 使用KLC数据创建蜡烛图
|
||||
const klcCandles = buildKLCFromAnalysis(currentData);
|
||||
series.setData(klcCandles);
|
||||
tvWidget.series.klcSeries = series;
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -2717,7 +2730,6 @@
|
||||
tvWidget.series.signalLineSeries = signalLineSeries;
|
||||
tvWidget.series.histogramSeries = histogramSeries;
|
||||
}
|
||||
|
||||
// 添加ChanMACD图表
|
||||
console.log('ChanMACD图表创建条件检查:', {
|
||||
showMacd: showMacd,
|
||||
@@ -2733,7 +2745,6 @@
|
||||
if (typeof window.showUOnElement === 'undefined') {
|
||||
window.showUOnElement = $('#toggleUOnElement').is(':checked');
|
||||
}
|
||||
|
||||
if (showMacd && chanMacdChart && ((useElementPeriod && currentData.element_macd) || currentData.macd) && (useElementPeriod ? currentData.element_kline_data : currentData.kline_data)) {
|
||||
console.log('✅ 开始创建 ChanMACD 系列');
|
||||
// 创建ChanMACD线系列
|
||||
@@ -4338,7 +4349,6 @@
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 主周期未完成中枢
|
||||
if ($('#showMainZs').is(':checked') && currentData.uncompleted_zs_list && currentData.uncompleted_zs_list.length > 0) {
|
||||
console.log(`绘制主周期未完成中枢数据,共${currentData.uncompleted_zs_list.length}条`);
|
||||
@@ -5305,7 +5315,6 @@
|
||||
// 收集所有主周期分型标记
|
||||
const allMainFxMarkers = [];
|
||||
const mainFxMarkers = []; // 用于tooltip支持
|
||||
|
||||
// 处理主周期KLC分型
|
||||
if ($('#showKlcFxType').is(':checked') && currentData.klc_fx_info && currentData.klc_fx_info.length > 0) {
|
||||
console.log(`绘制主周期K线合并分型标签,共${currentData.klc_fx_info.length}条`);
|
||||
@@ -5671,6 +5680,7 @@
|
||||
else if (klineType === 'line') targetSeries = tvWidget.series.lineSeries;
|
||||
else if (klineType === 'area') targetSeries = tvWidget.series.areaSeries;
|
||||
else if (klineType === 'baseline') targetSeries = tvWidget.series.baselineSeries;
|
||||
else if (klineType === 'klc') targetSeries = tvWidget.series.klcSeries;
|
||||
if (targetSeries) {
|
||||
targetSeries.setMarkers(combinedMarkers);
|
||||
} else {
|
||||
@@ -5765,6 +5775,7 @@
|
||||
else if (klineType2 === 'line') targetSeries2 = tvWidget.series.lineSeries;
|
||||
else if (klineType2 === 'area') targetSeries2 = tvWidget.series.areaSeries;
|
||||
else if (klineType2 === 'baseline') targetSeries2 = tvWidget.series.baselineSeries;
|
||||
else if (klineType2 === 'klc') targetSeries2 = tvWidget.series.klcSeries;
|
||||
if (targetSeries2) {
|
||||
targetSeries2.setMarkers(onlyMainAndU);
|
||||
} else {
|
||||
@@ -5782,6 +5793,7 @@
|
||||
else if (klineType3 === 'line') targetSeries3 = tvWidget.series.lineSeries;
|
||||
else if (klineType3 === 'area') targetSeries3 = tvWidget.series.areaSeries;
|
||||
else if (klineType3 === 'baseline') targetSeries3 = tvWidget.series.baselineSeries;
|
||||
else if (klineType3 === 'klc') targetSeries3 = tvWidget.series.klcSeries;
|
||||
if (targetSeries3) {
|
||||
targetSeries3.setMarkers([]);
|
||||
}
|
||||
@@ -6024,6 +6036,9 @@
|
||||
} else if (klineType === 'baseline' && tvWidget.series.baselineSeries) {
|
||||
const baseData = candles.map(c => ({ time: c.time, value: c.close }));
|
||||
tvWidget.series.baselineSeries.setData(baseData);
|
||||
} else if (klineType === 'klc' && tvWidget.series.klcSeries) {
|
||||
const klcCandles = buildKLCFromAnalysis(currentData);
|
||||
tvWidget.series.klcSeries.setData(klcCandles);
|
||||
}
|
||||
|
||||
// 更新均线数据
|
||||
@@ -7010,7 +7025,6 @@
|
||||
// 更新数据源信息
|
||||
setupDataSourceInfo(data);
|
||||
}
|
||||
|
||||
// 设置数据源信息显示
|
||||
function setupDataSourceInfo(data) {
|
||||
// 获取用户当前的周期选择
|
||||
@@ -7103,7 +7117,6 @@
|
||||
$('#end_time').val(formatDatetimeLocal(now));
|
||||
$('#start_time').val(formatDatetimeLocal(oneDayAgo));
|
||||
}
|
||||
|
||||
// 格式化日期为datetime-local输入框格式
|
||||
function formatDatetimeLocal(date) {
|
||||
const year = date.getFullYear();
|
||||
@@ -7753,7 +7766,6 @@
|
||||
console.error('加载A股股票列表失败');
|
||||
});
|
||||
}
|
||||
|
||||
// 检测交易对类型并返回相应的配置
|
||||
function getSymbolConfig(symbol) {
|
||||
const isAStock = symbol && symbol.length === 6 && /^\d+$/.test(symbol);
|
||||
@@ -7811,7 +7823,6 @@
|
||||
|
||||
return chartOptions;
|
||||
}
|
||||
|
||||
// 过滤非交易时间的数据(仅用于显示优化)
|
||||
function filterTradingHours(data, symbolConfig) {
|
||||
if (symbolConfig.type !== 'a_stock') {
|
||||
@@ -8402,7 +8413,6 @@
|
||||
function updateMAPanel() {
|
||||
updateIndicatorPanel();
|
||||
}
|
||||
|
||||
// 切换均线可见性
|
||||
function toggleMAVisibility(maId) {
|
||||
console.log('👁️ 切换均线可见性,ID:', maId, 'Type:', typeof maId);
|
||||
@@ -8605,7 +8615,6 @@
|
||||
|
||||
return candles;
|
||||
}
|
||||
|
||||
// 从蜡烛数据生成 Heikin-Ashi(平均K)
|
||||
function buildHeikinFromCandles(candles) {
|
||||
if (!Array.isArray(candles) || candles.length === 0) return [];
|
||||
@@ -8628,6 +8637,44 @@
|
||||
return result;
|
||||
}
|
||||
|
||||
// 从分析数据生成KLC蜡烛数据
|
||||
function buildKLCFromAnalysis(data) {
|
||||
if (!data) return [];
|
||||
|
||||
// 检查是否使用小周期数据
|
||||
const useElementPeriod = $('#elementPeriodKline').is(':checked') &&
|
||||
data.element_klc_list &&
|
||||
Array.isArray(data.element_klc_list);
|
||||
|
||||
const klcList = useElementPeriod ? data.element_klc_list : data.klc_list;
|
||||
|
||||
if (!klcList) return [];
|
||||
|
||||
const klcCandles = [];
|
||||
|
||||
// 遍历KLC列表,转换为蜡烛数据格式
|
||||
klcList.forEach(klc => {
|
||||
if (!klc || !klc.date) return;
|
||||
|
||||
// 使用KLC的date字段,转换为时间戳格式
|
||||
const date = new Date(klc.date);
|
||||
const timestamp = date.getTime() / 1000;
|
||||
|
||||
// 创建KLC蜡烛数据
|
||||
const candle = {
|
||||
time: timestamp,
|
||||
open: klc.open || 0,
|
||||
high: klc.high || 0,
|
||||
low: klc.low || 0,
|
||||
close: klc.close || 0
|
||||
};
|
||||
|
||||
klcCandles.push(candle);
|
||||
});
|
||||
|
||||
return klcCandles;
|
||||
}
|
||||
|
||||
// 计算默认砖大小(优先使用ATR的最新非零值,否则按收盘价的0.5%)
|
||||
function computeDefaultBrickSize(candles) {
|
||||
try {
|
||||
@@ -8881,10 +8928,10 @@
|
||||
|
||||
console.log('🔄 按钮已切换为更新模式');
|
||||
|
||||
$('#bbConfigModal').css('display', 'flex');
|
||||
$('#bbConfigModal').css('display', 'flex');
|
||||
|
||||
// 更新预览
|
||||
setTimeout(updateBBLinePreview, 50);
|
||||
// 更新预览
|
||||
setTimeout(updateBBLinePreview, 50);
|
||||
}
|
||||
// 更新布林带
|
||||
function updateBollingerBand(bbId) {
|
||||
@@ -9008,78 +9055,78 @@
|
||||
console.warn('addBollingerBandsToChart 未就绪');
|
||||
}
|
||||
|
||||
// 更新均线预览
|
||||
// 更新均线预览
|
||||
function updateLinePreview() {
|
||||
// 检查是否在均线配置窗口
|
||||
if ($('#maConfigModal').is(':visible')) {
|
||||
const color = $('#maColor').val();
|
||||
const width = $('#maLineWidth').val();
|
||||
const style = $('#maLineStyle').val();
|
||||
// 检查是否在均线配置窗口
|
||||
if ($('#maConfigModal').is(':visible')) {
|
||||
const color = $('#maColor').val();
|
||||
const width = $('#maLineWidth').val();
|
||||
const style = $('#maLineStyle').val();
|
||||
|
||||
const line = $('#previewLine');
|
||||
line.attr('stroke', color);
|
||||
line.attr('stroke-width', width);
|
||||
const line = $('#previewLine');
|
||||
line.attr('stroke', color);
|
||||
line.attr('stroke-width', width);
|
||||
|
||||
// 设置线条样式
|
||||
switch(parseInt(style)) {
|
||||
case 0: // 实线
|
||||
line.attr('stroke-dasharray', 'none');
|
||||
break;
|
||||
case 1: // 点线
|
||||
line.attr('stroke-dasharray', '2,3');
|
||||
break;
|
||||
case 2: // 虚线
|
||||
line.attr('stroke-dasharray', '5,5');
|
||||
break;
|
||||
case 3: // 大虚线
|
||||
line.attr('stroke-dasharray', '10,5');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 设置线条样式
|
||||
switch(parseInt(style)) {
|
||||
case 0: // 实线
|
||||
line.attr('stroke-dasharray', 'none');
|
||||
break;
|
||||
case 1: // 点线
|
||||
line.attr('stroke-dasharray', '2,3');
|
||||
break;
|
||||
case 2: // 虚线
|
||||
line.attr('stroke-dasharray', '5,5');
|
||||
break;
|
||||
case 3: // 大虚线
|
||||
line.attr('stroke-dasharray', '10,5');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 更新布林带预览
|
||||
function updateBBLinePreview() {
|
||||
const upperColor = $('#bbUpperColor').val();
|
||||
const middleColor = $('#bbMiddleColor').val();
|
||||
const lowerColor = $('#bbLowerColor').val();
|
||||
const width = $('#bbLineWidth').val();
|
||||
const style = $('#bbLineStyle').val();
|
||||
|
||||
// 更新布林带预览
|
||||
function updateBBLinePreview() {
|
||||
const upperColor = $('#bbUpperColor').val();
|
||||
const middleColor = $('#bbMiddleColor').val();
|
||||
const lowerColor = $('#bbLowerColor').val();
|
||||
const width = $('#bbLineWidth').val();
|
||||
const style = $('#bbLineStyle').val();
|
||||
const upperLine = $('#bbPreviewUpper');
|
||||
const middleLine = $('#bbPreviewMiddle');
|
||||
const lowerLine = $('#bbPreviewLower');
|
||||
|
||||
const upperLine = $('#bbPreviewUpper');
|
||||
const middleLine = $('#bbPreviewMiddle');
|
||||
const lowerLine = $('#bbPreviewLower');
|
||||
// 设置各条线的颜色
|
||||
upperLine.attr('stroke', upperColor);
|
||||
middleLine.attr('stroke', middleColor);
|
||||
lowerLine.attr('stroke', lowerColor);
|
||||
|
||||
// 设置各条线的颜色
|
||||
upperLine.attr('stroke', upperColor);
|
||||
middleLine.attr('stroke', middleColor);
|
||||
lowerLine.attr('stroke', lowerColor);
|
||||
// 设置线条宽度和样式
|
||||
[upperLine, middleLine, lowerLine].forEach(line => {
|
||||
line.attr('stroke-width', width);
|
||||
|
||||
// 设置线条宽度和样式
|
||||
[upperLine, middleLine, lowerLine].forEach(line => {
|
||||
line.attr('stroke-width', width);
|
||||
|
||||
// 设置线条样式
|
||||
switch(parseInt(style)) {
|
||||
case 0: // 实线
|
||||
line.attr('stroke-dasharray', 'none');
|
||||
break;
|
||||
case 1: // 点线
|
||||
line.attr('stroke-dasharray', '2,3');
|
||||
break;
|
||||
case 2: // 虚线
|
||||
line.attr('stroke-dasharray', '5,5');
|
||||
break;
|
||||
case 3: // 大虚线
|
||||
line.attr('stroke-dasharray', '10,5');
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
// 设置线条样式
|
||||
switch(parseInt(style)) {
|
||||
case 0: // 实线
|
||||
line.attr('stroke-dasharray', 'none');
|
||||
break;
|
||||
case 1: // 点线
|
||||
line.attr('stroke-dasharray', '2,3');
|
||||
break;
|
||||
case 2: // 虚线
|
||||
line.attr('stroke-dasharray', '5,5');
|
||||
break;
|
||||
case 3: // 大虚线
|
||||
line.attr('stroke-dasharray', '10,5');
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 监听配置变化以更新预览
|
||||
$(document).on('change', '#maColor, #maLineWidth, #maLineStyle', updateLinePreview);
|
||||
$(document).on('change', '#bbUpperColor, #bbMiddleColor, #bbLowerColor, #bbLineWidth, #bbLineStyle', updateBBLinePreview);
|
||||
$(document).on('change', '#bbUpperColor, #bbMiddleColor, #bbLowerColor, #bbLineWidth, #bbLineStyle', updateBBLinePreview);
|
||||
|
||||
// 点击弹窗外部关闭
|
||||
$(document).on('click', '#bbConfigModal', function(e) {
|
||||
if (e.target === this) {
|
||||
@@ -9128,8 +9175,6 @@
|
||||
const now = new Date();
|
||||
const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
|
||||
|
||||
|
||||
|
||||
// 添加页面滚动事件监听器,清除十字线延长线
|
||||
$(window).on('scroll', function() {
|
||||
try {
|
||||
@@ -9146,18 +9191,10 @@
|
||||
console.debug('清除滚动中的十字线时出错:', e);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
// ====== ChanMACD图表相关函数 ======
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 清除ChanMACD标注
|
||||
function clearChanMacdMarkers() {
|
||||
// 清除所有系列的标记
|
||||
@@ -9219,7 +9256,7 @@
|
||||
color: seg.seg_dir === 'ABOVE' ? '#e91e63' : '#4caf50',
|
||||
shape: 'square',
|
||||
text: `S${index}E`,
|
||||
size: 0.5
|
||||
size: 0.5
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -9369,7 +9406,6 @@
|
||||
addStateMarkers(stateMarkers);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加状态标记
|
||||
function addStateMarkers(stateMarkers) {
|
||||
const stateMarkersList = [];
|
||||
|
||||
Reference in New Issue
Block a user