diff --git a/.DS_Store b/.DS_Store index 87cba7f..90f040d 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/ChanKLU.py b/ChanKLU.py index 96ef47b..9ebbe0f 100644 --- a/ChanKLU.py +++ b/ChanKLU.py @@ -111,10 +111,10 @@ class ChanKLU: def check_bb2633(self, threadhold=300): #print(self.time, self.high, self.bb2633upper, self.low, self.bb2633lower) if abs(self.high - self.bb2633upper) < threadhold: - print(self.time, self.high, self.bb2633upper) + #print(self.time, self.high, self.bb2633upper) return 1 if abs(self.low - self.bb2633lower) < threadhold: - print(self.time, self.low, self.bb2633lower) + #print(self.time, self.low, self.bb2633lower) return -1 return 0 def check_fx_dir(self, pre, next): @@ -202,13 +202,15 @@ class ChanKLU: if 0 < self.low - self.ema52 < 100: self.near0_return = 0 elif self.close > self.ema52 and self.low < self.ema52 and self.open > self.ema52: - self.near0_return = 7 + self.near0_return = 0 elif self.close < self.ema52 and self.open > self.ema52 and self.high > self.ema52 and self.low < self.ema52: self.near0_return = 0 elif self.close < self.ema52 and self.open < self.ema52 and self.high > self.ema52 and self.low < self.ema52: self.near0_return = 0 elif self.close > self.ema52 and self.open > self.ema52 and self.high > self.ema52 and self.low < self.ema52: self.near0_return = 0 + elif self.close > self.ema52 and self.high > self.ema52 and self.low < self.ema52: + self.near0_return = 0 else: if self.macd > self.signal: if 0 < self.ema52 - self.high < 100: @@ -216,10 +218,12 @@ class ChanKLU: elif self.close < self.ema52 and self.high > self.ema52 and self.open < self.ema52: self.near0_return = 0 elif self.close < self.ema52 and self.open > self.ema52 and self.high > self.ema52 and self.low < self.ema52: - self.near0_return = 81 + self.near0_return = 0 elif self.close > self.ema52 and self.open < self.ema52 and self.high > self.ema52 and self.low < self.ema52: self.near0_return = 0 - elif self.close > self.ema52 and self.high > self.ema52 and self.open > self.ema52 and self.low < self.ema52: + elif self.close > self.ema52 and self.high > self.ema52 and self.open >= self.ema52 and self.low < self.ema52: + self.near0_return = 0 + elif self.close > self.ema52 and self.high > self.ema52 and self.low < self.ema52: self.near0_return = 0 # CROSS0 仅以 Signal 穿越零轴判定 if self.pre.signal >= 0 and self.signal < 0: diff --git a/ChanLun.py b/ChanLun.py index 493e6f5..1e12ba9 100644 --- a/ChanLun.py +++ b/ChanLun.py @@ -127,6 +127,8 @@ class ChanLun(): # TF_DF methods ------------------------------------------ + def get_ema_state(self, dataframe): + return self.tf_df.get_ema_state(dataframe) def get_klu_state(self, dataframe): return self.tf_df.get_klu_state(dataframe) def check_fx(self, klc): diff --git a/TF_DF.py b/TF_DF.py index 8f21198..e2907a3 100644 --- a/TF_DF.py +++ b/TF_DF.py @@ -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_PRICE_TREND, Chan_KLU_PATTERN +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_PRICE_TREND, Chan_KLU_PATTERN, Chan_K_DIR from ChanKLU import ChanKLU from ChanKLC import ChanKLC from ChanBI import ChanBI @@ -161,6 +161,23 @@ class TF_DF(): else: klu_state_list.append("00") return klu_state_list + def get_ema_state(self, dataframe): + klu_list = self.get_klu_list(dataframe) + klc_list = self.get_klc_list(klu_list) + bi_list = self.cal_bi_list(klc_list) + klu_state_list = [] + for klu in klu_list: + if klu.near0_return == 1: + klu_state_list.append("1") + elif klu.near0_return == 9: + klu_state_list.append("-1") + elif klu.candle_dir == Chan_K_DIR.BULL: + klu_state_list.append("2") + elif klu.candle_dir == Chan_K_DIR.BEAR: + klu_state_list.append("-2") + else: + klu_state_list.append("0") + return klu_state_list def check_fx(self, klc): if klc.pre and klc.next: if klc.high > klc.pre.high and klc.high > klc.next.high and klc.low > klc.pre.low and klc.low > klc.next.low: diff --git a/config/EMA26_EMA52_Cross.json b/config/EMA26_EMA52_Cross.json new file mode 100644 index 0000000..0fd8acd --- /dev/null +++ b/config/EMA26_EMA52_Cross.json @@ -0,0 +1,83 @@ +{ + "$schema": "https://schema.freqtrade.io/schema.json", + "max_open_trades": 1, + "stake_currency": "USDT", + "stake_amount": "unlimited", + "tradable_balance_ratio": 0.99, + "fiat_display_currency": "USD", + "dry_run": true, + "db_url": "sqlite:///tradesv3.ema26_ema52_cross.sqlite", + "dry_run_wallet": 1000, + "cancel_open_orders_on_exit": true, + "trading_mode": "futures", + "margin_mode": "isolated", + "can_short" : true, + "timeframe" : "1m", + "process_only_new_candles" : false, + "unfilledtimeout": { + "entry": 1, + "exit": 1, + "exit_timeout_count": 5, + "unit": "minutes" + }, + "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": "same", + "use_order_book": true, + "order_book_top": 1 + }, + "exchange": { + "name": "binance", + "key": "hvoXanRExQvcN4tyGFvEnsSF4gqxXp6ZJnBu5lnhvlVuHaDbj2PhLBQGCLkkyeI8", + "secret": "3UKA2oyDj7OoXrausmnaLwLlNfXmlNf2imBdmQqqKHArcJfk6X9xjaUF19wzu82l", + "ccxt_config": {}, + "ccxt_async_config": {}, + "pair_whitelist": [ + "SOL/USDT:USDT" + ], + "pair_blacklist": [ + "BNB/.*" + ] + }, + "pairlists": [ + { + "method": "StaticPairList", + "number_assets": 1, + "sort_key": "quoteVolume", + "min_value": 0, + "refresh_period": 1800 + } + ], + "telegram": { + "enabled": true, + "token": "7677670958:AAFL_jgZvNUTPR3R3vWieREX_tDVi9w2C1Y", + "chat_id": "580807463" + }, + "api_server": { + "enabled": true, + "listen_ip_address": "0.0.0.0", + "listen_port": 8820, + "verbosity": "error", + "enable_openapi": false, + "jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d", + "ws_token": "dtKKDnafBrX4icq_ZCw7acJTahTK4h_yvg", + "CORS_origins": [], + "username": "freqtrader", + "password": "FreqTrade007" + }, + "bot_name": "freqtrade", + "initial_state": "running", + "force_entry_enable": false, + "internals": { + "process_throttle_secs": 2 + } +} \ No newline at end of file diff --git a/strategies/ChanLun_EMA_Align.py b/strategies/ChanLun_EMA_Align.py index d61b4ab..5e2cca1 100644 --- a/strategies/ChanLun_EMA_Align.py +++ b/strategies/ChanLun_EMA_Align.py @@ -102,32 +102,28 @@ class ChanLun_EMA_Align(IStrategy): time15 = 15 time30 = 30 time60 = 60 + chan = ChanLun() def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe = self.add_indicators(dataframe) - dataframe_5m = resample_to_interval(dataframe, self.get_ticker_indicator() * self.time5) - dataframe_5m = self.add_indicators(dataframe_5m) + dataframe_30m = resample_to_interval(dataframe, self.get_ticker_indicator() * self.time30) + dataframe_30m = self.add_indicators(dataframe_30m) + dataframe_30m['ema_state'] = self.chan.get_ema_state(dataframe_30m) + dataframe_30m['state'] = self.chan.get_klu_state(dataframe_30m) #print(dataframe_5m.iloc[-1]) - dataframe = resampled_merge(dataframe, dataframe_5m) + dataframe = resampled_merge(dataframe, dataframe_30m) return dataframe def add_indicators(self, dataframe): dataframe['ema24'] = ta.EMA(dataframe, timeperiod=24) - dataframe['dir24'] = dataframe['close'] - dataframe['ema24'] dataframe['ema52'] = ta.EMA(dataframe, timeperiod=52) - dataframe['dir52'] = dataframe['close'] - dataframe['ema52'] dataframe['ema104'] = ta.EMA(dataframe, timeperiod=104) - dataframe['dir104'] = dataframe['close'] - dataframe['ema104'] dataframe['ema156'] = ta.EMA(dataframe, timeperiod=156) - dataframe['dir156'] = dataframe['close'] - dataframe['ema156'] - dataframe['dir52_156'] = dataframe['ema52'] - dataframe['ema156'] - dataframe['dir52_104'] = dataframe['ema52'] - dataframe['ema104'] + dataframe['ema52_price'] = dataframe['close'] - dataframe['ema52'] + dataframe['ema156_price'] = dataframe['close'] - dataframe['ema156'] dataframe_macd = ta.MACD(dataframe, fast=12, slow=26, signal=9) dataframe['macdsignal'] = dataframe_macd['macdsignal'] dataframe['macd'] = dataframe_macd['macd'] dataframe['macdhist'] = dataframe_macd['macdhist'] - dataframe['ema_align'] = ( - ((dataframe['ema24'] > dataframe['ema52']) & (dataframe['ema52'] > dataframe['ema104'])) | - ((dataframe['ema24'] < dataframe['ema52']) & (dataframe['ema52'] < dataframe['ema104'])) - ) + dataframe['ema_dir'] = dataframe['ema52'] - dataframe['ema156'] return dataframe def custom_entry_price(self, pair: str, trade: Trade | None, current_time: datetime, proposed_rate: float, entry_tag: str | None, side: str, **kwargs) -> float: @@ -165,32 +161,29 @@ class ChanLun_EMA_Align(IStrategy): return None def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: - resample_5m_align = 'resample_{}_ema_align'.format(self.get_ticker_indicator() * self.time5) - # 使用高周期的 dir52_156 方向作为多空判定依据 - resample_5m_dir = 'resample_{}_dir52_104'.format(self.get_ticker_indicator() * self.time5) - resample_5m_signal = 'resample_{}_macdsignal'.format(self.get_ticker_indicator() * self.time5) + resample_30m_dir = 'resample_{}_ema_dir'.format(self.get_ticker_indicator() * self.time30) + resample_30m_price = 'resample_{}_ema52_price'.format(self.get_ticker_indicator() * self.time30) + resample_30m_signal = 'resample_{}_macdsignal'.format(self.get_ticker_indicator() * self.time30) + resample_30m_state = 'resample_{}_state'.format(self.get_ticker_indicator() * self.time30) + resample_30m_ema_state = 'resample_{}_ema_state'.format(self.get_ticker_indicator() * self.time30) dataframe.loc[ - (dataframe[resample_5m_align]) & - (dataframe[resample_5m_dir] > 0) & - (dataframe[resample_5m_signal] > 0), + (dataframe[resample_30m_dir].shift(self.time30*2) > 0) & + (dataframe[resample_30m_ema_state].shift(self.time30) == "2") & + (dataframe[resample_30m_ema_state].shift(self.time30*2) == "1"), ['enter_long', 'enter_tag']] = (1, 'long_signal_chan') dataframe.loc[ - (dataframe[resample_5m_align]) & - (dataframe[resample_5m_dir] < 0) & - (dataframe[resample_5m_signal] < 0), + (dataframe[resample_30m_dir].shift(self.time30*2) < 0) & + (dataframe[resample_30m_ema_state].shift(self.time30) == "-2") & + (dataframe[resample_30m_ema_state].shift(self.time30*2) == "-1"), ['enter_short', 'enter_tag']] = (1, 'short_signal_chan') return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + state = 'resample_{}_state'.format(self.get_ticker_indicator() * self.time30) dataframe.loc[ - (dataframe['dir156'] < 0) & - (dataframe['dir52_156'] < 0) & - (dataframe['macdhist'] < 0), + (dataframe[state].shift(self.time30) == "10"), ['exit_long', 'exit_tag']] = (1, 'long_exit_signal_chan') dataframe.loc[ - (dataframe['macd'] > 0) & - (dataframe['dir156'] > 0) & - (dataframe['dir52_156'] > 0) & - (dataframe['macdhist'] > 0), + (dataframe[state].shift(self.time30) == "-10"), ['exit_short', 'exit_tag']] = (1, 'short_exit_signal_chan') return dataframe def leverage(self, pair: str, current_time: datetime, current_rate: float, diff --git a/strategies/EMA26_EMA52_Cross.py b/strategies/EMA26_EMA52_Cross.py new file mode 100644 index 0000000..19ec1b7 --- /dev/null +++ b/strategies/EMA26_EMA52_Cross.py @@ -0,0 +1,176 @@ +# --- Do not remove these libs --- +from statistics import median +from freqtrade.strategy import IStrategy, stoploss_from_absolute +import sys +import os +# 添加父目录到系统路径 +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +from ChanLun import ChanLun +from ChanEnum import Chan_FX_TYPE, Chan_KLC_FX, Chan_BI_DIR, Chan_KLC_FX +# -------------------------------- +from technical.util import resample_to_interval, resampled_merge +import talib.abstract as ta +from pandas import DataFrame +import pandas as pd +from datetime import datetime, timedelta +from freqtrade.persistence import Trade, Order +from typing import Optional +import logging +logger = logging.getLogger(__name__) + + +### Now you can use logger.info('asfd') to log +# freqtrade plot-dataframe --strategy ChanLun_BTC --datadir user_data/data/binance -c ./user_data/ChanLun_SOL_30.json --timerange=20250309- + +# freqtrade trade -c ./user_data/Chan/config/EMA26_EMA52_Cross.json --strategy EMA26_EMA52_Cross --strategy-path ./user_data/Chan/strategies +# freqtrade backtesting -c ./user_data/Chan/config/EMA26_EMA52_Cross.json --strategy EMA26_EMA52_Cross --strategy-path ./user_data/Chan/strategies --timerange=20260101- +# freqtrade download-data -c ./user_data/Chan/config/EMA26_EMA52_Cross.json -t 1m 1m 1h 1d 1w 1M --pairs SOL/USDT:USDT --timerange=20240101- +# freqtrade download-data -c ./user_data/Chan/config/EMA26_EMA52_Cross.json -t 1m 1h 1d 1M --pairs SOL/USDT:USDT --timerange=20170101- +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --spaces roi --strategy EMA26_EMA52_Cross --strategy-path ./user_data/Chan/strategies -c ./user_data/Chan/config/EMA26_EMA52_Cross.json -e 200 --timerange=20250201-20250901 +# freqtrade edge -c ./user_data/Chan/config/EMA26_EMA52_Cross.json --strategy EMA26_EMA52_Cross --strategy-path ./user_data/Chan/strategies --timerange 20250721-20250901 +# freqtrade plot-dataframe -c ./user_data/Chan/config/EMA26_EMA52_Cross.json --strategy EMA26_EMA52_Cross --strategy-path ./user_data/Chan/strategies --timerange 20250721-20250901 + +# sudo docker compose run --rm chanlun_btc backtesting -c ./user_data/Chan/config/EMA26_EMA52_Cross.json --strategy EMA26_EMA52_Cross --strategy-path ./user_data/Chan/strategies --timerange=20250721- +# sudo docker compose run --rm chanlun_btc download-data -c ./user_data/Chan/config/EMA26_EMA52_Cross.json --pairs BTC/USDT:USDT -t 1m --timerange 20240101- +# sudo docker compose run --rm chanlun_btc trade -c ./user_data/Chan/config/EMA26_EMA52_Cross.json --strategy EMA26_EMA52_Cross --strategy-path ./user_data/Chan/strategies + +class EMA26_EMA52_Cross(IStrategy): + INTERFACE_VERSION: int = 3 + # Minimal ROI designed for the strategy. + # This attribute will be overridden if the config file contains "minimal_roi" + # 30m and 1h + + minimal_roi = { + "0": 0.15, + "500": 0.2, + "1200": 0.1, + "1800": 0.08, + "2400": 0 + } + # 5m and 15m + minimal_roi_1 = { + "0": 0.1, + "60": 0.05, + "120": 0.02, + "240": 0 + } + # 15m and 30m + minimal_roi_1 = { + "0": 0.1, + "240": 0.05, + "480": 0.03, + "600": 0 + } + minimal_roi_1 = { + "0": 1.50, + "120": 0.05, + "240": 0.025, + "360": 0 + } + + can_short = True + lev = 1.0 + stoploss = -0.3 # 设置为很大的负值,让custom_stoploss来控制 + use_custom_stoploss = False # 启用自定义止损 + + trailing_stop = False + trailing_stop_positive = 0.03 + trailing_stop_positive_offset = 0.06 + trailing_only_offset_is_reached = False + time5 = 5 + time15 = 15 + time30 = 30 + time60 = 60 + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe_15m = resample_to_interval(dataframe, self.get_ticker_indicator() * self.time15) + dataframe_30m = resample_to_interval(dataframe, self.get_ticker_indicator() * self.time30) + dataframe_60m = resample_to_interval(dataframe, self.get_ticker_indicator() * self.time60) + dataframe_15m = self.add_indicators(dataframe_15m) + dataframe_30m = self.add_indicators(dataframe_30m) + dataframe_60m = self.add_indicators(dataframe_60m) + dataframe = resampled_merge(dataframe, dataframe_15m) + dataframe = resampled_merge(dataframe, dataframe_30m) + dataframe = resampled_merge(dataframe, dataframe_60m) + return dataframe + def add_indicators(self, dataframe): + dataframe['ema26'] = ta.EMA(dataframe, timeperiod=26) + dataframe['ema52'] = ta.EMA(dataframe, timeperiod=52) + # 上穿:本根 26 > 52,上一根 26 ≤ 52 + dataframe['ema26_cross_up_52'] = ( + (dataframe['ema26'] > dataframe['ema52']) & + (dataframe['ema26'].shift(1) <= dataframe['ema52'].shift(1)) + ) + # 下穿:本根 26 < 52,上一根 26 ≥ 52 + dataframe['ema26_cross_down_52'] = ( + (dataframe['ema26'] < dataframe['ema52']) & + (dataframe['ema26'].shift(1) >= dataframe['ema52'].shift(1)) + ) + dataframe_macd = ta.MACD(dataframe, fast=12, slow=26, signal=9) + dataframe['macdsignal'] = dataframe_macd['macdsignal'] + dataframe['macd'] = dataframe_macd['macd'] + dataframe['macdhist'] = dataframe_macd['macdhist'] + + return dataframe + def custom_entry_price(self, pair: str, trade: Trade | None, current_time: datetime, proposed_rate: float, + entry_tag: str | None, side: str, **kwargs) -> float: + new_entryprice = proposed_rate + if trade: + if trade.is_short: + new_entryprice = proposed_rate - 10 + else: + new_entryprice = proposed_rate + 10 + return new_entryprice + + def custom_exit_price(self, pair: str, trade: Trade, + current_time: datetime, proposed_rate: float, + current_profit: float, exit_tag: str | None, **kwargs) -> float: + new_exitprice = proposed_rate + if trade: + if trade.is_short: + new_exitprice = proposed_rate + 10 + else: + new_exitprice = proposed_rate - 10 + return new_exitprice + + def adjust_trade_position(self, trade: Trade, current_time: datetime, + current_rate: float, current_profit: float, + min_stake: Optional[float], max_stake: float, + current_entry_rate: float, current_exit_rate: float, + current_entry_profit: float, current_exit_profit: float, + **kwargs) -> Optional[float]: + # 关闭分批止盈,始终不调整仓位 + return None + + def custom_exit(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, + current_profit: float, **kwargs): + # 不做分批止盈/最终止盈处理,退出由策略信号/ROI/止损决定 + return None + + def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + time = self.time30 + cross_up = 'resample_{}_ema26_cross_up_52'.format(self.get_ticker_indicator() * time) + cross_down = 'resample_{}_ema26_cross_down_52'.format(self.get_ticker_indicator() * time) + dataframe.loc[ + (dataframe[cross_up].shift(time) == True), + ['enter_long', 'enter_tag']] = (1, 'long_signal') + dataframe.loc[ + (dataframe[cross_down].shift(time) == True), + ['enter_short', 'enter_tag']] = (1, 'short_signal') + return dataframe + def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + time = self.time30 + cross_up = 'resample_{}_ema26_cross_up_52'.format(self.get_ticker_indicator() * time) + cross_down = 'resample_{}_ema26_cross_down_52'.format(self.get_ticker_indicator() * time) + dataframe.loc[ + (dataframe[cross_down].shift(time) == True), + ['exit_long', 'exit_tag']] = (1, 'long_signal') + dataframe.loc[ + (dataframe[cross_up].shift(time) == True), + ['exit_short', 'exit_tag']] = (1, 'short_signal') + 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 self.lev + def get_ticker_indicator(self): + return int(self.timeframe[:-1]) \ No newline at end of file diff --git a/web/templates/index.html b/web/templates/index.html index d3f5bc0..73d0c37 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -2946,7 +2946,7 @@ mainMarkers.push({ time: ts, position: posCd, color: '#ff9800', shape: 'arrowDown', text: 'CD', size: 0.6 }); } if (item.near0_return && Number(item.near0_return) > 0) { - mainMarkers.push({ time: ts, position: 'belowBar', color: '#8bc34a', shape: 'circle', text: `N${Number(item.near0_return)}`, size: 0.6 }); + mainMarkers.push({ time: ts, position: 'belowBar', color: '#8bc34a', shape: 'circle', text: `${Number(item.near0_return)}`, size: 0.6 }); } }); } @@ -2968,7 +2968,7 @@ elementMarkers.push({ time: ts, position: posCd, color: '#4caf50', shape: 'arrowDown', text: 'CD', size: 0.6 }); } if (item.near0_return && Number(item.near0_return) > 0) { - elementMarkers.push({ time: ts, position: 'belowBar', color: '#009688', shape: 'circle', text: `N${Number(item.near0_return)}`, size: 0.6 }); + elementMarkers.push({ time: ts, position: 'belowBar', color: '#009688', shape: 'circle', text: `${Number(item.near0_return)}`, size: 0.6 }); } }); } @@ -3031,7 +3031,7 @@ mainMarkersAll.push({ time: ts, position: posCd, color: '#ff9800', shape: 'arrowDown', text: 'CD', size: 0.6 }); } if (item.near0_return && Number(item.near0_return) > 0) { - mainMarkersAll.push({ time: ts, position: 'belowBar', color: '#8bc34a', shape: 'circle', text: `N${Number(item.near0_return)}`, size: 0.6 }); + mainMarkersAll.push({ time: ts, position: 'belowBar', color: '#8bc34a', shape: 'circle', text: `${Number(item.near0_return)}`, size: 0.6 }); } }); } @@ -3051,7 +3051,7 @@ elementMarkersAll.push({ time: ts, position: posCd, color: '#4caf50', shape: 'arrowDown', text: 'CD', size: 0.6 }); } if (item.near0_return && Number(item.near0_return) > 0) { - elementMarkersAll.push({ time: ts, position: 'belowBar', color: '#009688', shape: 'circle', text: `N${Number(item.near0_return)}`, size: 0.6 }); + elementMarkersAll.push({ time: ts, position: 'belowBar', color: '#009688', shape: 'circle', text: `${Number(item.near0_return)}`, size: 0.6 }); } }); }