bitunix-automated-crypto-trading 2.6.0__py3-none-any.whl → 2.6.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bitunix_automated_crypto_trading/AsyncThreadRunner.py +81 -0
- bitunix_automated_crypto_trading/BitunixApi.py +278 -0
- bitunix_automated_crypto_trading/BitunixSignal.py +1099 -0
- bitunix_automated_crypto_trading/BitunixWebSocket.py +254 -0
- bitunix_automated_crypto_trading/DataFrameHtmlRenderer.py +74 -0
- bitunix_automated_crypto_trading/NotificationManager.py +23 -0
- bitunix_automated_crypto_trading/ResourceManager.py +35 -0
- bitunix_automated_crypto_trading/ThreadManager.py +69 -0
- bitunix_automated_crypto_trading/TickerManager.py +636 -0
- bitunix_automated_crypto_trading/__init__.py +1 -0
- bitunix_automated_crypto_trading/bitunix.py +593 -0
- bitunix_automated_crypto_trading/clearenv.py +8 -0
- bitunix_automated_crypto_trading/config.py +90 -0
- bitunix_automated_crypto_trading/config.txt +60 -0
- bitunix_automated_crypto_trading/logger.py +85 -0
- bitunix_automated_crypto_trading/sampleenv.txt +5 -0
- bitunix_automated_crypto_trading/static/chart.css +28 -0
- bitunix_automated_crypto_trading/static/chart.js +362 -0
- bitunix_automated_crypto_trading/static/modal.css +68 -0
- bitunix_automated_crypto_trading/static/modal.js +147 -0
- bitunix_automated_crypto_trading/static/script.js +166 -0
- bitunix_automated_crypto_trading/static/styles.css +118 -0
- bitunix_automated_crypto_trading/templates/charts.html +98 -0
- bitunix_automated_crypto_trading/templates/login.html +19 -0
- bitunix_automated_crypto_trading/templates/main.html +551 -0
- bitunix_automated_crypto_trading/templates/modal-chart.html +26 -0
- bitunix_automated_crypto_trading/templates/modal-config.html +34 -0
- bitunix_automated_crypto_trading/templates/modal-logs.html +15 -0
- {bitunix_automated_crypto_trading-2.6.0.dist-info → bitunix_automated_crypto_trading-2.6.1.dist-info}/METADATA +1 -1
- bitunix_automated_crypto_trading-2.6.1.dist-info/RECORD +33 -0
- bitunix_automated_crypto_trading-2.6.1.dist-info/top_level.txt +1 -0
- bitunix_automated_crypto_trading-2.6.0.dist-info/RECORD +0 -5
- bitunix_automated_crypto_trading-2.6.0.dist-info/top_level.txt +0 -1
- {bitunix_automated_crypto_trading-2.6.0.dist-info → bitunix_automated_crypto_trading-2.6.1.dist-info}/WHEEL +0 -0
- {bitunix_automated_crypto_trading-2.6.0.dist-info → bitunix_automated_crypto_trading-2.6.1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,60 @@
|
|
1
|
+
AUTOTRADE=True
|
2
|
+
OPTION_MOVING_AVERAGE=1h
|
3
|
+
MAX_AUTO_TRADES=10
|
4
|
+
PROFIT_AMOUNT=0
|
5
|
+
LOSS_AMOUNT=0
|
6
|
+
LEVERAGE=20
|
7
|
+
THRESHOLD=5
|
8
|
+
MIN_VOLUME=10000000
|
9
|
+
ORDER_AMOUNT_PERCENTAGE=5
|
10
|
+
BARS=100
|
11
|
+
MA_FAST=12
|
12
|
+
MA_MEDIUM=26
|
13
|
+
MA_SLOW=50
|
14
|
+
RSI_FAST=6
|
15
|
+
RSI_SLOW=24
|
16
|
+
BBM_PERIOD=20
|
17
|
+
BBM_STD=2
|
18
|
+
MACD_PERIOD=9
|
19
|
+
MACD_SHORT=12
|
20
|
+
MACD_LONG=26
|
21
|
+
ADX_PERIOD=14
|
22
|
+
OPEN_ON_ANY_SIGNAL=True
|
23
|
+
EMA_CHART=True
|
24
|
+
EMA_STUDY=True
|
25
|
+
EMA_CROSSING=True
|
26
|
+
EMA_CHECK_ON_OPEN=True
|
27
|
+
EMA_CHECK_ON_CLOSE=True
|
28
|
+
MACD_CHART=False
|
29
|
+
MACD_STUDY=True
|
30
|
+
MACD_CROSSING=True
|
31
|
+
MACD_CHECK_ON_OPEN=True
|
32
|
+
MACD_CHECK_ON_CLOSE=False
|
33
|
+
BBM_CHART=False
|
34
|
+
BBM_STUDY=True
|
35
|
+
BBM_CROSSING=True
|
36
|
+
BBM_CHECK_ON_OPEN=True
|
37
|
+
BBM_CHECK_ON_CLOSE=False
|
38
|
+
RSI_CHART=False
|
39
|
+
RSI_STUDY=True
|
40
|
+
RSI_CROSSING=True
|
41
|
+
RSI_CHECK_ON_OPEN=True
|
42
|
+
RSI_CHECK_ON_CLOSE=False
|
43
|
+
ADX_STUDY=True
|
44
|
+
ADX_CHECK_ON_OPEN=True
|
45
|
+
ADX_CHECK_ON_CLOSE=False
|
46
|
+
CANDLE_TREND_STUDY=False
|
47
|
+
CANDLE_TREND_CHECK_ON_OPEN=False
|
48
|
+
CANDLE_TREND_CHECK_ON_CLOSE=False
|
49
|
+
SCREEN_REFRESH_INTERVAL=1
|
50
|
+
SIGNAL_CHECK_INTERVAL=30
|
51
|
+
PORTFOLIO_API_INTERVAL=3
|
52
|
+
PENDING_POSITIONS_API_INTERVAL=3
|
53
|
+
PENDING_ORDERS_API_INTERVAL=3
|
54
|
+
TRADE_HISTORY_API_INTERVAL=3
|
55
|
+
POSITION_HISTORY_API_INTERVAL=3
|
56
|
+
TICKER_DATA_API_INTERVAL=120
|
57
|
+
PUBLIC_WEBSOCKET_RESTART_INTERVAL=10800
|
58
|
+
USE_PUBLIC_WEBSOCKET=False
|
59
|
+
VERBOSE_LOGGING=False
|
60
|
+
BENCHMARK=False
|
@@ -0,0 +1,85 @@
|
|
1
|
+
import logging
|
2
|
+
import os
|
3
|
+
from logging.handlers import RotatingFileHandler
|
4
|
+
from datetime import datetime, timezone
|
5
|
+
from zoneinfo import ZoneInfo
|
6
|
+
from colorama import init, Fore, Style
|
7
|
+
|
8
|
+
# Initialize colorama for Windows support
|
9
|
+
init()
|
10
|
+
|
11
|
+
class Colors:
|
12
|
+
RESET = Style.RESET_ALL
|
13
|
+
RED = Fore.RED
|
14
|
+
GREEN = Fore.GREEN
|
15
|
+
YELLOW = Fore.YELLOW
|
16
|
+
BLUE = Fore.BLUE
|
17
|
+
PURPLE = Fore.MAGENTA
|
18
|
+
CYAN = Fore.CYAN
|
19
|
+
LBLUE = Fore.LIGHTBLUE_EX
|
20
|
+
|
21
|
+
|
22
|
+
class ColoredFormatter(logging.Formatter):
|
23
|
+
COLORS = {
|
24
|
+
'DEBUG': Colors.BLUE,
|
25
|
+
'INFO': Colors.GREEN,
|
26
|
+
'WARNING': Colors.YELLOW,
|
27
|
+
'ERROR': Colors.RED,
|
28
|
+
'CRITICAL': Colors.PURPLE,
|
29
|
+
}
|
30
|
+
|
31
|
+
def format(self, record):
|
32
|
+
# Add color to the level name
|
33
|
+
color = self.COLORS.get(record.levelname, Colors.RESET)
|
34
|
+
record.msg = f"{color}{record.msg}{Colors.RESET}"
|
35
|
+
return super().format(record)
|
36
|
+
|
37
|
+
class CSTFormatter(logging.Formatter):
|
38
|
+
def formatTime(self, record, datefmt=None):
|
39
|
+
# Convert UTC time to CST
|
40
|
+
utc_time = datetime.fromtimestamp(record.created, tz=timezone.utc)
|
41
|
+
cst_time = utc_time.astimezone(ZoneInfo("US/Central"))
|
42
|
+
if datefmt:
|
43
|
+
return cst_time.strftime(datefmt)
|
44
|
+
else:
|
45
|
+
return cst_time.isoformat()
|
46
|
+
|
47
|
+
class Logger:
|
48
|
+
def __init__(self, logger_name, log_file='app.log', level=logging.DEBUG, max_bytes=5 * 1024 * 1024, backup_count=3):
|
49
|
+
"""
|
50
|
+
Initialize the logger.
|
51
|
+
|
52
|
+
:param logger_name: Name of the logger.
|
53
|
+
:param log_file: Log file path.
|
54
|
+
:param level: Logging level (default: DEBUG).
|
55
|
+
:param max_bytes: Max size of the log file before rotation (default: 5MB).
|
56
|
+
:param backup_count: Number of backup files to keep (default: 3).
|
57
|
+
"""
|
58
|
+
# Create the logger
|
59
|
+
self.logger = logging.getLogger(logger_name)
|
60
|
+
self.logger.setLevel(level)
|
61
|
+
colors=Colors()
|
62
|
+
|
63
|
+
# Check if handlers are already attached
|
64
|
+
if not self.logger.handlers:
|
65
|
+
# Create a file handler with rotation
|
66
|
+
file_handler = RotatingFileHandler(log_file, maxBytes=max_bytes, backupCount=backup_count)
|
67
|
+
file_formatter = CSTFormatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
|
68
|
+
file_handler.setFormatter(file_formatter)
|
69
|
+
|
70
|
+
# Create a console handler
|
71
|
+
console_handler = logging.StreamHandler()
|
72
|
+
console_formatter = CSTFormatter(f'{colors.RESET}%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
|
73
|
+
console_handler.setFormatter(console_formatter)
|
74
|
+
|
75
|
+
# Add handlers to the logger
|
76
|
+
self.logger.addHandler(file_handler)
|
77
|
+
self.logger.addHandler(console_handler)
|
78
|
+
|
79
|
+
def get_logger(self):
|
80
|
+
"""
|
81
|
+
Return the configured logger.
|
82
|
+
|
83
|
+
:return: Configured logger instance.
|
84
|
+
"""
|
85
|
+
return self.logger
|
@@ -0,0 +1,28 @@
|
|
1
|
+
.chart-body {
|
2
|
+
flex: 1;
|
3
|
+
display: flex;
|
4
|
+
flex-direction: column;
|
5
|
+
overflow-y: auto; /* Enable vertical scrolling */
|
6
|
+
height: calc(100vh - 100px); /* Ensure it accounts for header height */
|
7
|
+
box-sizing: border-box; /* Prevent height calculation issues due to padding */
|
8
|
+
}
|
9
|
+
|
10
|
+
.chart-grid {
|
11
|
+
display: grid;
|
12
|
+
grid-template-columns: repeat(2, 1fr);
|
13
|
+
grid-template-rows: repeat(3, 1fr);
|
14
|
+
gap: 10px;
|
15
|
+
height: auto; /* Let the grid adapt naturally */
|
16
|
+
overflow-y: auto; /* Add vertical scrolling for grid */
|
17
|
+
|
18
|
+
}
|
19
|
+
.chart-container {
|
20
|
+
display: flex;
|
21
|
+
align-items: center;
|
22
|
+
justify-content: center;
|
23
|
+
}
|
24
|
+
canvas {
|
25
|
+
width: 100% !important;
|
26
|
+
height: 100% !important;
|
27
|
+
}
|
28
|
+
|
@@ -0,0 +1,362 @@
|
|
1
|
+
const charts = {};
|
2
|
+
|
3
|
+
const createOrUpdateChart = (
|
4
|
+
chartId, data, buysell, ema_study, ema_display, macd_study, macd_display, bbm_study, bbm_display, rsi_study, rsi_display, timeUnit
|
5
|
+
) => {
|
6
|
+
const datasets = [];
|
7
|
+
const scales = {}; // Dynamic y-axis configuration
|
8
|
+
|
9
|
+
const studyColors = {
|
10
|
+
ema_slow: 'rgba(0, 123, 255, 0.7)', // Blue for EMA Slow
|
11
|
+
ema_medium: 'rgba(255, 123, 0, 0.7)', // Orange for EMA Medium
|
12
|
+
ema_fast: 'rgba(0, 255, 123, 0.7)', // Green for EMA Fast
|
13
|
+
macd_line: 'rgba(255, 0, 0, 0.7)', // Red for MACD Line
|
14
|
+
macd_signal: 'rgba(123, 0, 255, 0.7)', // Purple for MACD Signal
|
15
|
+
macd_histogram: 'rgba(123, 123, 123, 0.7)', // Gray for Histogram
|
16
|
+
bbl: 'rgba(0, 0, 255, 0.7)', // Dark Blue for BBL
|
17
|
+
bbm: 'rgba(26, 26, 17, 0.7)', // Dark Gray for BBM
|
18
|
+
bbu: 'rgba(0, 123, 0, 0.7)', // Green for BBU
|
19
|
+
rsi_slow: 'rgba(112, 91, 99, 0.7)', // Purple for RSI Slow
|
20
|
+
rsi_fast: 'rgba(156, 23, 23, 0.7)', // Red for RSI Fast
|
21
|
+
};
|
22
|
+
|
23
|
+
const candlestickData = data.map(d => ({
|
24
|
+
x: parseInt(d.time),
|
25
|
+
o: parseFloat(d.open),
|
26
|
+
h: parseFloat(d.high),
|
27
|
+
l: parseFloat(d.low),
|
28
|
+
c: parseFloat(d.close)
|
29
|
+
}));
|
30
|
+
|
31
|
+
const mapOptionalData = (data, key, yKey = key.replace('_', '')) => {
|
32
|
+
return data.filter(d => typeof d[key] !== 'undefined' && parseFloat(d[key]) !== 0).map(d => ({
|
33
|
+
x: parseInt(d.time),
|
34
|
+
y: parseFloat(d[key])
|
35
|
+
}));
|
36
|
+
};
|
37
|
+
|
38
|
+
const slowMA = mapOptionalData(data, 'ma_slow');
|
39
|
+
const mediumMA = mapOptionalData(data, 'ma_medium');
|
40
|
+
const fastMA = mapOptionalData(data, 'ma_fast');
|
41
|
+
const macdLine = mapOptionalData(data, 'MACD_Line');
|
42
|
+
const signalLine = mapOptionalData(data, 'MACD_Signal');
|
43
|
+
const macdHistogram = mapOptionalData(data, 'MACD_Histogram');
|
44
|
+
const BBL = mapOptionalData(data, 'BBL');
|
45
|
+
const BBM = mapOptionalData(data, 'BBM');
|
46
|
+
const BBU = mapOptionalData(data, 'BBU');
|
47
|
+
const rsi_slow = mapOptionalData(data, 'rsi_slow');
|
48
|
+
const rsi_fast = mapOptionalData(data, 'rsi_fast');
|
49
|
+
|
50
|
+
// Find the min and max time from candlestickData
|
51
|
+
const times = candlestickData.map(d => d.x);
|
52
|
+
const minTime = Math.min(...times);
|
53
|
+
const maxTime = Math.max(...times);
|
54
|
+
|
55
|
+
const buyselldata = buysell.filter(d => {
|
56
|
+
const time = parseInt(d.ctime);
|
57
|
+
return time >= minTime && time <= maxTime;
|
58
|
+
}).map(d => ({
|
59
|
+
x: parseInt(d.ctime),
|
60
|
+
y: parseFloat(d.price),
|
61
|
+
side: d.side,
|
62
|
+
backgroundColor: d.side === 'BUY' ? 'rgba(0, 255, 0, 0.9)' : 'rgba(255, 0, 0, 0.9)', // Color based on buy/sell
|
63
|
+
borderColor: d.side === 'BUY' ? 'rgba(0, 255, 0, 1)' : 'rgba(255, 0, 0, 1)',
|
64
|
+
borderWidth: 2,
|
65
|
+
pointRadius: 5, // Increased for better visibility
|
66
|
+
pointStyle: 'triangle' // Changed for better visibility
|
67
|
+
}));
|
68
|
+
|
69
|
+
const timeSettings = {
|
70
|
+
second: { unit: 'second', stepSize: 1, displayFormats: { second: 'HH:mm:ss' } },
|
71
|
+
minute: { unit: 'minute', stepSize: 1, displayFormats: { minute: 'HH:mm' } },
|
72
|
+
hour: { unit: 'hour', stepSize: 1, displayFormats: { hour: 'HH:mm' } },
|
73
|
+
day: { unit: 'day', stepSize: 1, displayFormats: { day: 'MMM dd' } }
|
74
|
+
};
|
75
|
+
|
76
|
+
const timeConfig = timeSettings[timeUnit] || timeSettings['second'];
|
77
|
+
|
78
|
+
|
79
|
+
// Candlestick Data
|
80
|
+
scales['y-axis'] = {
|
81
|
+
type: 'linear',
|
82
|
+
position: 'left',
|
83
|
+
title: { display: true, text: 'CandleStick' }
|
84
|
+
};
|
85
|
+
|
86
|
+
datasets.push({
|
87
|
+
type: 'candlestick',
|
88
|
+
label: 'Candlestick Data',
|
89
|
+
data: candlestickData,
|
90
|
+
borderColor: 'rgba(0, 0, 0, 1)',
|
91
|
+
backgroundColor: 'rgba(75, 192, 192, 0.2)',
|
92
|
+
yAxisID: 'y-axis' // Unique y-axis ID
|
93
|
+
});
|
94
|
+
|
95
|
+
// EMA Study
|
96
|
+
if (ema_study) {
|
97
|
+
datasets.push({
|
98
|
+
label: 'EMA Slow',
|
99
|
+
data: slowMA,
|
100
|
+
backgroundColor: studyColors.ema_slow,
|
101
|
+
borderColor: studyColors.ema_slow,
|
102
|
+
borderWidth: 1,
|
103
|
+
fill: false,
|
104
|
+
type: 'line',
|
105
|
+
pointRadius: 0,
|
106
|
+
yAxisID: 'y-axis',
|
107
|
+
hidden: true
|
108
|
+
});
|
109
|
+
|
110
|
+
datasets.push({
|
111
|
+
label: 'EMA Medium',
|
112
|
+
data: mediumMA,
|
113
|
+
backgroundColor: studyColors.ema_medium,
|
114
|
+
borderColor: studyColors.ema_medium,
|
115
|
+
borderWidth: 1,
|
116
|
+
fill: false,
|
117
|
+
type: 'line',
|
118
|
+
pointRadius: 0,
|
119
|
+
yAxisID: 'y-axis',
|
120
|
+
hidden: !ema_display // Hide if not displayed
|
121
|
+
});
|
122
|
+
|
123
|
+
datasets.push({
|
124
|
+
label: 'EMA Fast',
|
125
|
+
data: fastMA,
|
126
|
+
backgroundColor: studyColors.ema_fast,
|
127
|
+
borderColor: studyColors.ema_fast,
|
128
|
+
borderWidth: 1,
|
129
|
+
fill: false,
|
130
|
+
type: 'line',
|
131
|
+
pointRadius: 0,
|
132
|
+
yAxisID: 'y-axis',
|
133
|
+
hidden: !ema_display // Hide if not displayed
|
134
|
+
});
|
135
|
+
}
|
136
|
+
|
137
|
+
// BBM Study
|
138
|
+
if (bbm_study) {
|
139
|
+
datasets.push({
|
140
|
+
label: 'BBU',
|
141
|
+
data: BBU,
|
142
|
+
backgroundColor: studyColors.bbu,
|
143
|
+
borderColor: studyColors.bbu,
|
144
|
+
borderWidth: 1,
|
145
|
+
fill: false,
|
146
|
+
type: 'line',
|
147
|
+
pointRadius: 0,
|
148
|
+
yAxisID: 'y-axis',
|
149
|
+
hidden: !bbm_display // Hide if not displayed
|
150
|
+
});
|
151
|
+
|
152
|
+
datasets.push({
|
153
|
+
label: 'BBM',
|
154
|
+
data: BBM,
|
155
|
+
backgroundColor: studyColors.bbm,
|
156
|
+
borderColor: studyColors.bbm,
|
157
|
+
borderWidth: 1,
|
158
|
+
fill: false,
|
159
|
+
type: 'line',
|
160
|
+
pointRadius: 0,
|
161
|
+
yAxisID: 'y-axis',
|
162
|
+
hidden: !bbm_display // Hide if not displayed
|
163
|
+
});
|
164
|
+
|
165
|
+
datasets.push({
|
166
|
+
label: 'BBL',
|
167
|
+
data: BBL,
|
168
|
+
backgroundColor: studyColors.bbl,
|
169
|
+
borderColor: studyColors.bbl,
|
170
|
+
borderWidth: 1,
|
171
|
+
fill: false,
|
172
|
+
type: 'line',
|
173
|
+
pointRadius: 0,
|
174
|
+
yAxisID: 'y-axis',
|
175
|
+
hidden: !bbm_display // Hide if not displayed
|
176
|
+
});
|
177
|
+
}
|
178
|
+
|
179
|
+
// MACD Study
|
180
|
+
if (macd_study) {
|
181
|
+
|
182
|
+
scales['y-axis-macd'] = {
|
183
|
+
type: 'linear',
|
184
|
+
position: 'left',
|
185
|
+
title: { display: true, text: 'MACD' }
|
186
|
+
};
|
187
|
+
|
188
|
+
datasets.push({
|
189
|
+
label: 'MACD Line',
|
190
|
+
data: macdLine,
|
191
|
+
backgroundColor: studyColors.macd_line,
|
192
|
+
borderColor: studyColors.macd_line,
|
193
|
+
borderWidth: 1,
|
194
|
+
fill: false,
|
195
|
+
type: 'line',
|
196
|
+
pointRadius: 0,
|
197
|
+
yAxisID: 'y-axis-macd',
|
198
|
+
hidden: !macd_display // Hide if not displayed
|
199
|
+
});
|
200
|
+
|
201
|
+
datasets.push({
|
202
|
+
label: 'MACD Signal',
|
203
|
+
data: signalLine,
|
204
|
+
backgroundColor: studyColors.macd_signal,
|
205
|
+
borderColor: studyColors.macd_signal,
|
206
|
+
borderWidth: 1,
|
207
|
+
fill: false,
|
208
|
+
type: 'line',
|
209
|
+
pointRadius: 0,
|
210
|
+
yAxisID: 'y-axis-macd',
|
211
|
+
hidden: !macd_display // Hide if not displayed
|
212
|
+
});
|
213
|
+
|
214
|
+
scales['y-axis-macd-histogram'] = {
|
215
|
+
type: 'linear',
|
216
|
+
position: 'left',
|
217
|
+
title: { display: true, text: 'MACD Histogram' }
|
218
|
+
};
|
219
|
+
datasets.push({
|
220
|
+
label: 'MACD Histogram',
|
221
|
+
data: macdHistogram,
|
222
|
+
backgroundColor: studyColors.macd_histogram,
|
223
|
+
borderColor: studyColors.macd_histogram,
|
224
|
+
borderWidth: 0,
|
225
|
+
type: 'bar',
|
226
|
+
yAxisID: 'y-axis-macd-histogram',
|
227
|
+
hidden: true
|
228
|
+
});
|
229
|
+
}
|
230
|
+
|
231
|
+
// RSI Study
|
232
|
+
if (rsi_study) {
|
233
|
+
|
234
|
+
scales['y-axis-rsi'] = {
|
235
|
+
type: 'linear',
|
236
|
+
position: 'left',
|
237
|
+
title: { display: true, text: 'RSI' }
|
238
|
+
};
|
239
|
+
|
240
|
+
datasets.push({
|
241
|
+
label: 'RSI SLOW',
|
242
|
+
data: rsi_slow,
|
243
|
+
backgroundColor: studyColors.rsi_slow,
|
244
|
+
borderColor: studyColors.rsi_slow,
|
245
|
+
borderWidth: 1,
|
246
|
+
fill: false,
|
247
|
+
type: 'line',
|
248
|
+
pointRadius: 0,
|
249
|
+
yAxisID: 'y-axis-rsi',
|
250
|
+
hidden: !rsi_display // Hide if not displayed
|
251
|
+
});
|
252
|
+
|
253
|
+
datasets.push({
|
254
|
+
label: 'RSI FAST',
|
255
|
+
data: rsi_fast,
|
256
|
+
backgroundColor: studyColors.rsi_fast,
|
257
|
+
borderColor: studyColors.rsi_fast,
|
258
|
+
borderWidth: 1,
|
259
|
+
fill: false,
|
260
|
+
type: 'line',
|
261
|
+
pointRadius: 0,
|
262
|
+
yAxisID: 'y-axis-rsi',
|
263
|
+
hidden: !rsi_display // Hide if not displayed
|
264
|
+
});
|
265
|
+
}
|
266
|
+
|
267
|
+
// Buy/Sell data points
|
268
|
+
datasets.push({
|
269
|
+
label: 'BuySell',
|
270
|
+
data: buyselldata,
|
271
|
+
type: 'scatter',
|
272
|
+
backgroundColor: buyselldata.map(d => d.backgroundColor),
|
273
|
+
borderColor: buyselldata.map(d => d.borderColor),
|
274
|
+
borderWidth: buyselldata.map(d => d.borderWidth),
|
275
|
+
pointRadius: buyselldata.map(d => d.pointRadius),
|
276
|
+
pointStyle: buyselldata.map(d => d.pointStyle),
|
277
|
+
yAxisID: 'y-axis' // Unique y-axis ID
|
278
|
+
});
|
279
|
+
|
280
|
+
|
281
|
+
// Define chart configuration
|
282
|
+
const chartConfig = {
|
283
|
+
type: 'candlestick',
|
284
|
+
data: { datasets },
|
285
|
+
options: {
|
286
|
+
scales: {
|
287
|
+
x: {
|
288
|
+
type: 'time',
|
289
|
+
time: timeConfig
|
290
|
+
},
|
291
|
+
...scales // Dynamically include y-axis configurations
|
292
|
+
},
|
293
|
+
plugins: {
|
294
|
+
responsive: true,
|
295
|
+
tooltip: {
|
296
|
+
callbacks: {
|
297
|
+
label: function (context) {
|
298
|
+
|
299
|
+
// Handle cases where context.parsed might be undefined
|
300
|
+
if (!context.parsed) {
|
301
|
+
return `${context.dataset.label}: Data unavailable`;
|
302
|
+
}
|
303
|
+
|
304
|
+
const x = context.parsed.x !== undefined ? context.parsed.x : 'N/A';
|
305
|
+
const y = context.parsed.y !== undefined ? context.parsed.y : 'N/A';
|
306
|
+
|
307
|
+
// Check if the dataset is buysell
|
308
|
+
if (context.dataset.label === 'BuySell') {
|
309
|
+
const side = context.raw.side !== undefined ? context.raw.side : 'N/A'; // Side
|
310
|
+
const price = context.raw.y !== undefined ? context.raw.y : 'N/A'; // Price
|
311
|
+
const time = context.raw.x !== undefined ? context.raw.x : 'N/A'; // Time
|
312
|
+
return `${context.dataset.label}: (Side: ${side}, Price: ${price}, Time: ${time})`;
|
313
|
+
}
|
314
|
+
// Check if the dataset is candlestick
|
315
|
+
else if (context.dataset.type === 'candlestick') {
|
316
|
+
const o = context.raw.o !== undefined ? context.raw.o : 'N/A'; // Open
|
317
|
+
const h = context.raw.h !== undefined ? context.raw.h : 'N/A'; // High
|
318
|
+
const l = context.raw.l !== undefined ? context.raw.l : 'N/A'; // Low
|
319
|
+
const c = context.raw.c !== undefined ? context.raw.c : 'N/A'; // Close
|
320
|
+
return `${context.dataset.label}: (Open: ${o}, High: ${h}, Low: ${l}, Close: ${c})`;
|
321
|
+
}
|
322
|
+
else {
|
323
|
+
// Default for other datasets
|
324
|
+
return `${context.dataset.label}: (${x}, ${y})`;
|
325
|
+
}
|
326
|
+
}
|
327
|
+
}
|
328
|
+
}
|
329
|
+
|
330
|
+
}
|
331
|
+
}
|
332
|
+
};
|
333
|
+
|
334
|
+
// Render or update the chart
|
335
|
+
if (charts[chartId]) {
|
336
|
+
// Assign only the 'data' of the specified dataset (indexed by x)
|
337
|
+
chartConfig.data.datasets.forEach((dataset, index) => {
|
338
|
+
if (charts[chartId].data.datasets[index]) {
|
339
|
+
charts[chartId].data.datasets[index].data = dataset.data; // Update only the data property
|
340
|
+
}
|
341
|
+
});
|
342
|
+
charts[chartId].options = chartConfig.options;
|
343
|
+
charts[chartId].update();
|
344
|
+
}
|
345
|
+
else {
|
346
|
+
if (charts[chartId]) {
|
347
|
+
charts[chartId].destroy(); // Destroy the existing chart instance
|
348
|
+
delete charts[chartId]
|
349
|
+
}
|
350
|
+
|
351
|
+
const chartContainer = document.querySelector(`.chart-container#${chartId}`);
|
352
|
+
chartContainer.innerHTML=""
|
353
|
+
const newCanvas = document.createElement('canvas');
|
354
|
+
newCanvas.id = chartId;
|
355
|
+
chartContainer.appendChild(newCanvas);
|
356
|
+
|
357
|
+
const ctx = newCanvas.getContext('2d');
|
358
|
+
charts[chartId] = new Chart(ctx, chartConfig);
|
359
|
+
}
|
360
|
+
};
|
361
|
+
|
362
|
+
|
@@ -0,0 +1,68 @@
|
|
1
|
+
.scrollable-view {
|
2
|
+
height: 80%;
|
3
|
+
overflow-y: auto; /* Enable vertical scrolling */
|
4
|
+
margin-bottom: 20px;
|
5
|
+
}
|
6
|
+
|
7
|
+
.modal-overlay {
|
8
|
+
display: none; /* Hidden initially */
|
9
|
+
position: fixed;
|
10
|
+
top: 0;
|
11
|
+
left: 0;
|
12
|
+
width: 100%;
|
13
|
+
height: 100%;
|
14
|
+
background: rgba(0, 0, 0, 0.5);
|
15
|
+
z-index: 999;
|
16
|
+
}
|
17
|
+
|
18
|
+
.modal-overlay.modal-open { /* Only show overlay when modal is active */
|
19
|
+
display: block;
|
20
|
+
}
|
21
|
+
|
22
|
+
.modal-open {
|
23
|
+
display: block;
|
24
|
+
}
|
25
|
+
|
26
|
+
.modal-container { /* Added container for positioning */
|
27
|
+
width: 100%;
|
28
|
+
height: 100vw; /* Set height equal to the screen width */
|
29
|
+
max-height: 100vw; /* Ensure height does not exceed the width */
|
30
|
+
position: fixed;
|
31
|
+
z-index: 1000;
|
32
|
+
top: 0;
|
33
|
+
left: 0;
|
34
|
+
width: 100%;
|
35
|
+
height: 100%;
|
36
|
+
display: flex;
|
37
|
+
justify-content: center;
|
38
|
+
align-items: center;
|
39
|
+
pointer-events: none; /* Allow clicks to pass through when hidden */
|
40
|
+
}
|
41
|
+
|
42
|
+
.modal {
|
43
|
+
width: 100%;
|
44
|
+
height: 100vw; /* Set height equal to the screen width */
|
45
|
+
max-height: 100vw; /* Ensure height does not exceed the width */
|
46
|
+
background-color: white;
|
47
|
+
padding: 20px;
|
48
|
+
border: 1px solid #ccc;
|
49
|
+
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2);
|
50
|
+
resize: none; /* Disable default resizing */
|
51
|
+
pointer-events: auto; /* Make modal interactable */
|
52
|
+
}
|
53
|
+
|
54
|
+
.modal .resize-handle {
|
55
|
+
position: absolute;
|
56
|
+
width: 10px;
|
57
|
+
height: 10px;
|
58
|
+
background-color: gray;
|
59
|
+
cursor: nwse-resize; /* Change cursor to indicate resizing */
|
60
|
+
}
|
61
|
+
|
62
|
+
.resize-top-right { top: 0; right: 0; cursor: nesw-resize; }
|
63
|
+
.resize-bottom-right { bottom: 0; right: 0; cursor: nwse-resize; }
|
64
|
+
.resize-bottom-left { bottom: 0; left: 0; cursor: nesw-resize; }
|
65
|
+
.resize-top-left { top: 0; left: 0; cursor: nwse-resize; }
|
66
|
+
|
67
|
+
|
68
|
+
|