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.
Files changed (35) hide show
  1. bitunix_automated_crypto_trading/AsyncThreadRunner.py +81 -0
  2. bitunix_automated_crypto_trading/BitunixApi.py +278 -0
  3. bitunix_automated_crypto_trading/BitunixSignal.py +1099 -0
  4. bitunix_automated_crypto_trading/BitunixWebSocket.py +254 -0
  5. bitunix_automated_crypto_trading/DataFrameHtmlRenderer.py +74 -0
  6. bitunix_automated_crypto_trading/NotificationManager.py +23 -0
  7. bitunix_automated_crypto_trading/ResourceManager.py +35 -0
  8. bitunix_automated_crypto_trading/ThreadManager.py +69 -0
  9. bitunix_automated_crypto_trading/TickerManager.py +636 -0
  10. bitunix_automated_crypto_trading/__init__.py +1 -0
  11. bitunix_automated_crypto_trading/bitunix.py +593 -0
  12. bitunix_automated_crypto_trading/clearenv.py +8 -0
  13. bitunix_automated_crypto_trading/config.py +90 -0
  14. bitunix_automated_crypto_trading/config.txt +60 -0
  15. bitunix_automated_crypto_trading/logger.py +85 -0
  16. bitunix_automated_crypto_trading/sampleenv.txt +5 -0
  17. bitunix_automated_crypto_trading/static/chart.css +28 -0
  18. bitunix_automated_crypto_trading/static/chart.js +362 -0
  19. bitunix_automated_crypto_trading/static/modal.css +68 -0
  20. bitunix_automated_crypto_trading/static/modal.js +147 -0
  21. bitunix_automated_crypto_trading/static/script.js +166 -0
  22. bitunix_automated_crypto_trading/static/styles.css +118 -0
  23. bitunix_automated_crypto_trading/templates/charts.html +98 -0
  24. bitunix_automated_crypto_trading/templates/login.html +19 -0
  25. bitunix_automated_crypto_trading/templates/main.html +551 -0
  26. bitunix_automated_crypto_trading/templates/modal-chart.html +26 -0
  27. bitunix_automated_crypto_trading/templates/modal-config.html +34 -0
  28. bitunix_automated_crypto_trading/templates/modal-logs.html +15 -0
  29. {bitunix_automated_crypto_trading-2.6.0.dist-info → bitunix_automated_crypto_trading-2.6.1.dist-info}/METADATA +1 -1
  30. bitunix_automated_crypto_trading-2.6.1.dist-info/RECORD +33 -0
  31. bitunix_automated_crypto_trading-2.6.1.dist-info/top_level.txt +1 -0
  32. bitunix_automated_crypto_trading-2.6.0.dist-info/RECORD +0 -5
  33. bitunix_automated_crypto_trading-2.6.0.dist-info/top_level.txt +0 -1
  34. {bitunix_automated_crypto_trading-2.6.0.dist-info → bitunix_automated_crypto_trading-2.6.1.dist-info}/WHEEL +0 -0
  35. {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,5 @@
1
+ API_KEY=your_api_key
2
+ SECRET_KEY=your_secret_key
3
+ SECRET=your_token_secret
4
+ PASSWORD=whatever_is_here_is_your_webpage_password
5
+ HOST=0.0.0.0
@@ -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
+