dexbot 1.6.5 → 1.6.7
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.
- package/CHANGELOG.md +76 -4
- package/README.md +108 -36
- package/analysis/README.md +11 -16
- package/analysis/account_resolver.ts +1 -11
- package/analysis/bot_usage/kibana_bot_queries.ts +1 -1
- package/analysis/chain_pool.ts +1 -1
- package/analysis/fills_source.ts +0 -6
- package/analysis/grid_correction_check.ts +20 -26
- package/analysis/resolve_source.ts +1 -1
- package/analysis/trade_profitability.ts +2 -5
- package/analysis/trend_detection/README.md +1 -3
- package/analysis/trend_detection/package.json +1 -1
- package/claw/package.json +1 -1
- package/claw/runtimes/openclaw-plugin/openclaw.plugin.json +1 -1
- package/claw/runtimes/openclaw-plugin/package.json +1 -1
- package/claw/tests/test_claw_mcp_transport.ts +2 -2
- package/dist/analysis/account_resolver.d.ts +1 -4
- package/dist/analysis/account_resolver.d.ts.map +1 -1
- package/dist/analysis/account_resolver.js +1 -1
- package/dist/analysis/account_resolver.js.map +1 -1
- package/dist/analysis/bot_usage/kibana_bot_queries.d.ts +1 -2
- package/dist/analysis/bot_usage/kibana_bot_queries.d.ts.map +1 -1
- package/dist/analysis/bot_usage/kibana_bot_queries.js +1 -1
- package/dist/analysis/bot_usage/kibana_bot_queries.js.map +1 -1
- package/dist/analysis/chain_pool.d.ts +1 -3
- package/dist/analysis/chain_pool.d.ts.map +1 -1
- package/dist/analysis/chain_pool.js +1 -1
- package/dist/analysis/chain_pool.js.map +1 -1
- package/dist/analysis/fills_source.d.ts +1 -51
- package/dist/analysis/fills_source.d.ts.map +1 -1
- package/dist/analysis/fills_source.js +1 -1
- package/dist/analysis/fills_source.js.map +1 -1
- package/dist/analysis/grid_correction_check.d.ts +2 -3
- package/dist/analysis/grid_correction_check.d.ts.map +1 -1
- package/dist/analysis/grid_correction_check.js +18 -26
- package/dist/analysis/grid_correction_check.js.map +1 -1
- package/dist/analysis/resolve_source.d.ts +1 -1
- package/dist/analysis/resolve_source.d.ts.map +1 -1
- package/dist/analysis/trade_profitability.d.ts.map +1 -1
- package/dist/analysis/trade_profitability.js +2 -7
- package/dist/analysis/trade_profitability.js.map +1 -1
- package/dist/dexbot.d.ts.map +1 -1
- package/dist/dexbot.js +82 -83
- package/dist/dexbot.js.map +1 -1
- package/dist/market_adapter/core/kibana_candles.d.ts +1 -15
- package/dist/market_adapter/core/kibana_candles.d.ts.map +1 -1
- package/dist/market_adapter/core/kibana_candles.js +1 -1
- package/dist/market_adapter/core/kibana_candles.js.map +1 -1
- package/dist/market_adapter/inputs/fetch_lp_data.d.ts +1 -35
- package/dist/market_adapter/inputs/fetch_lp_data.d.ts.map +1 -1
- package/dist/market_adapter/inputs/fetch_lp_data.js +10 -54
- package/dist/market_adapter/inputs/fetch_lp_data.js.map +1 -1
- package/dist/market_adapter/inputs/kibana_feed_source.d.ts +1 -8
- package/dist/market_adapter/inputs/kibana_feed_source.d.ts.map +1 -1
- package/dist/market_adapter/inputs/kibana_feed_source.js +1 -1
- package/dist/market_adapter/inputs/kibana_feed_source.js.map +1 -1
- package/dist/market_adapter/inputs/window_cache.d.ts +2 -63
- package/dist/market_adapter/inputs/window_cache.d.ts.map +1 -1
- package/dist/market_adapter/inputs/window_cache.js +22 -102
- package/dist/market_adapter/inputs/window_cache.js.map +1 -1
- package/dist/modules/account_bots.d.ts +16 -2
- package/dist/modules/account_bots.d.ts.map +1 -1
- package/dist/modules/account_bots.js +108 -44
- package/dist/modules/account_bots.js.map +1 -1
- package/dist/modules/account_orders.d.ts +20 -2
- package/dist/modules/account_orders.d.ts.map +1 -1
- package/dist/modules/account_orders.js +63 -3
- package/dist/modules/account_orders.js.map +1 -1
- package/dist/modules/bitshares-native/chain_client.d.ts +10 -0
- package/dist/modules/bitshares-native/chain_client.d.ts.map +1 -1
- package/dist/modules/bitshares-native/chain_client.js +197 -28
- package/dist/modules/bitshares-native/chain_client.js.map +1 -1
- package/dist/modules/bitshares-native/crypto/ecc.browser.d.ts.map +1 -1
- package/dist/modules/bitshares-native/crypto/ecc.browser.js +0 -31
- package/dist/modules/bitshares-native/crypto/ecc.browser.js.map +1 -1
- package/dist/modules/bitshares-native/subscriptions.d.ts.map +1 -1
- package/dist/modules/bitshares-native/subscriptions.js +312 -10
- package/dist/modules/bitshares-native/subscriptions.js.map +1 -1
- package/dist/modules/bitshares-native/transport.d.ts +1 -0
- package/dist/modules/bitshares-native/transport.d.ts.map +1 -1
- package/dist/modules/bitshares-native/transport.js +97 -3
- package/dist/modules/bitshares-native/transport.js.map +1 -1
- package/dist/modules/bitshares_client.d.ts +20 -0
- package/dist/modules/bitshares_client.d.ts.map +1 -1
- package/dist/modules/bitshares_client.js +29 -0
- package/dist/modules/bitshares_client.js.map +1 -1
- package/dist/modules/chain_keys.d.ts +37 -22
- package/dist/modules/chain_keys.d.ts.map +1 -1
- package/dist/modules/chain_keys.js +145 -44
- package/dist/modules/chain_keys.js.map +1 -1
- package/dist/modules/cli_start_onboarding.d.ts +10 -0
- package/dist/modules/cli_start_onboarding.d.ts.map +1 -0
- package/dist/modules/cli_start_onboarding.js +15 -0
- package/dist/modules/cli_start_onboarding.js.map +1 -0
- package/dist/modules/cli_start_output.d.ts +6 -0
- package/dist/modules/cli_start_output.d.ts.map +1 -0
- package/dist/modules/cli_start_output.js +8 -0
- package/dist/modules/cli_start_output.js.map +1 -0
- package/dist/modules/constants.d.ts +13 -0
- package/dist/modules/constants.d.ts.map +1 -1
- package/dist/modules/constants.js +103 -1
- package/dist/modules/constants.js.map +1 -1
- package/dist/modules/credit_runtime.d.ts +0 -2
- package/dist/modules/credit_runtime.d.ts.map +1 -1
- package/dist/modules/credit_runtime.js +1 -10
- package/dist/modules/credit_runtime.js.map +1 -1
- package/dist/modules/dexbot_class.d.ts +13 -10
- package/dist/modules/dexbot_class.d.ts.map +1 -1
- package/dist/modules/dexbot_class.js +0 -9
- package/dist/modules/dexbot_class.js.map +1 -1
- package/dist/modules/dexbot_cow_runtime.d.ts +13 -0
- package/dist/modules/dexbot_cow_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_cow_runtime.js +62 -10
- package/dist/modules/dexbot_cow_runtime.js.map +1 -1
- package/dist/modules/dexbot_fill_runtime.d.ts +6 -1
- package/dist/modules/dexbot_fill_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_fill_runtime.js +22 -3
- package/dist/modules/dexbot_fill_runtime.js.map +1 -1
- package/dist/modules/dexbot_maintenance_runtime.d.ts +24 -1
- package/dist/modules/dexbot_maintenance_runtime.d.ts.map +1 -1
- package/dist/modules/dexbot_maintenance_runtime.js +45 -1
- package/dist/modules/dexbot_maintenance_runtime.js.map +1 -1
- package/dist/modules/dexbot_state_recovery.d.ts.map +1 -1
- package/dist/modules/dexbot_state_recovery.js +13 -1
- package/dist/modules/dexbot_state_recovery.js.map +1 -1
- package/dist/modules/order/accounting.d.ts +4 -18
- package/dist/modules/order/accounting.d.ts.map +1 -1
- package/dist/modules/order/accounting.js +4 -34
- package/dist/modules/order/accounting.js.map +1 -1
- package/dist/modules/order/export.d.ts +9 -12
- package/dist/modules/order/export.d.ts.map +1 -1
- package/dist/modules/order/export.js +9 -12
- package/dist/modules/order/export.js.map +1 -1
- package/dist/modules/order/grid.d.ts +35 -40
- package/dist/modules/order/grid.d.ts.map +1 -1
- package/dist/modules/order/grid.js +76 -44
- package/dist/modules/order/grid.js.map +1 -1
- package/dist/modules/order/grid_reconcile.d.ts.map +1 -1
- package/dist/modules/order/grid_reconcile.js +111 -20
- package/dist/modules/order/grid_reconcile.js.map +1 -1
- package/dist/modules/order/grid_reconcile_internal.d.ts +5 -3
- package/dist/modules/order/grid_reconcile_internal.d.ts.map +1 -1
- package/dist/modules/order/grid_reconcile_internal.js +29 -3
- package/dist/modules/order/grid_reconcile_internal.js.map +1 -1
- package/dist/modules/order/logger.d.ts +12 -2
- package/dist/modules/order/logger.d.ts.map +1 -1
- package/dist/modules/order/logger.js +29 -7
- package/dist/modules/order/logger.js.map +1 -1
- package/dist/modules/order/manager.d.ts +49 -8
- package/dist/modules/order/manager.d.ts.map +1 -1
- package/dist/modules/order/manager.js +72 -34
- package/dist/modules/order/manager.js.map +1 -1
- package/dist/modules/order/utils/order.d.ts +22 -7
- package/dist/modules/order/utils/order.d.ts.map +1 -1
- package/dist/modules/order/utils/order.js +200 -33
- package/dist/modules/order/utils/order.js.map +1 -1
- package/dist/modules/order/utils/system.d.ts +97 -0
- package/dist/modules/order/utils/system.d.ts.map +1 -1
- package/dist/modules/order/utils/system.js +274 -2
- package/dist/modules/order/utils/system.js.map +1 -1
- package/dist/modules/order/utils/withPoolRef.d.ts +12 -0
- package/dist/modules/order/utils/withPoolRef.d.ts.map +1 -1
- package/dist/modules/order/utils/withPoolRef.js +17 -0
- package/dist/modules/order/utils/withPoolRef.js.map +1 -1
- package/dist/modules/order/working_grid.d.ts +8 -13
- package/dist/modules/order/working_grid.d.ts.map +1 -1
- package/dist/modules/order/working_grid.js +8 -29
- package/dist/modules/order/working_grid.js.map +1 -1
- package/dist/modules/paths.d.ts +7 -1
- package/dist/modules/paths.d.ts.map +1 -1
- package/dist/modules/paths.js +10 -3
- package/dist/modules/paths.js.map +1 -1
- package/dist/modules/utils/text_width.d.ts +19 -0
- package/dist/modules/utils/text_width.d.ts.map +1 -0
- package/dist/modules/utils/text_width.js +97 -0
- package/dist/modules/utils/text_width.js.map +1 -0
- package/dist/pm2.d.ts.map +1 -1
- package/dist/pm2.js +95 -18
- package/dist/pm2.js.map +1 -1
- package/dist/scripts/sync-version.js +0 -22
- package/dist/scripts/sync-version.js.map +1 -1
- package/dist/scripts/update.js +62 -13
- package/dist/scripts/update.js.map +1 -1
- package/dist/scripts/update_dist_freshness.d.ts +12 -0
- package/dist/scripts/update_dist_freshness.d.ts.map +1 -1
- package/dist/scripts/update_dist_freshness.js +30 -1
- package/dist/scripts/update_dist_freshness.js.map +1 -1
- package/dist/unlock.d.ts +14 -2
- package/dist/unlock.d.ts.map +1 -1
- package/dist/unlock.js +73 -4
- package/dist/unlock.js.map +1 -1
- package/docs/BITSHARES_ONBOARDING.md +9 -6
- package/docs/COW_INVARIANTS.md +9 -0
- package/docs/CREDENTIAL_SECURITY.md +26 -0
- package/docs/DEXBOT_COMPARISON.md +11 -11
- package/docs/EVOLUTION.md +9 -7
- package/docs/FUND_MOVEMENT_AND_ACCOUNTING.md +1 -1
- package/docs/GRID_PRICE_INVARIANT.md +573 -0
- package/docs/GRID_RECALCULATION.md +1 -1
- package/docs/GRID_RECONCILE.md +60 -39
- package/docs/LOGGING.md +17 -2
- package/docs/ORDER_ENGINE_POST_1.0_RETROSPECTIVE.md +555 -0
- package/docs/README.md +3 -3
- package/docs/WORKFLOW.md +12 -6
- package/docs/architecture.md +40 -38
- package/docs/developer_guide.md +4 -8
- package/market_adapter/README.md +16 -12
- package/modules/README.md +1 -1
- package/package.json +8 -4
- package/scripts/README.md +0 -9
- package/scripts/reset-settings.sh +1 -1
- package/analysis/ama_fitting/package.json +0 -12
- package/analysis/analyze_derivatives.ts +0 -320
- package/analysis/derivative_chart_generator.ts +0 -874
- package/analysis/trend_detection/SIGNAL_DOCUMENTATION.md +0 -403
- package/analysis/trend_detection/derivative_analyzer.ts +0 -1059
- package/dist/analysis/analyze_derivatives.d.ts +0 -68
- package/dist/analysis/analyze_derivatives.d.ts.map +0 -1
- package/dist/analysis/analyze_derivatives.js +0 -287
- package/dist/analysis/analyze_derivatives.js.map +0 -1
- package/dist/analysis/derivative_chart_generator.d.ts +0 -12
- package/dist/analysis/derivative_chart_generator.d.ts.map +0 -1
- package/dist/analysis/derivative_chart_generator.js +0 -891
- package/dist/analysis/derivative_chart_generator.js.map +0 -1
- package/dist/analysis/trend_detection/derivative_analyzer.d.ts +0 -248
- package/dist/analysis/trend_detection/derivative_analyzer.d.ts.map +0 -1
- package/dist/analysis/trend_detection/derivative_analyzer.js +0 -910
- package/dist/analysis/trend_detection/derivative_analyzer.js.map +0 -1
|
@@ -1,891 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { pathToFileURL } from 'node:url';
|
|
6
|
-
import { escapeHtml, serializeJsonForScript, toEpochSeconds, UPLOT_SHARED_SCRIPT, uplotInlineTags } from './chart_utils.js';
|
|
7
|
-
import { getStorage } from '../modules/storage/index.js';
|
|
8
|
-
const { ensureDir, readJSON } = getStorage();
|
|
9
|
-
import { fixedTo } from '../modules/order/utils/math.js';
|
|
10
|
-
import { bindHoverStateFn, zoomResetScript } from './chart_ui.js';
|
|
11
|
-
import { PATHS } from '../modules/paths.js';
|
|
12
|
-
function parseArgs(argv = process.argv.slice(2)) {
|
|
13
|
-
const cfg = {
|
|
14
|
-
inputFile: null,
|
|
15
|
-
outputFile: path.join(PATHS.ANALYSIS.CHARTS_DIR, 'derivative_chart.html'),
|
|
16
|
-
title: 'Derivative Trend Analysis',
|
|
17
|
-
quiet: false,
|
|
18
|
-
};
|
|
19
|
-
for (let i = 0; i < argv.length; i++) {
|
|
20
|
-
const arg = argv[i];
|
|
21
|
-
if (arg === '--input')
|
|
22
|
-
cfg.inputFile = argv[++i] ?? null;
|
|
23
|
-
else if (arg === '--output')
|
|
24
|
-
cfg.outputFile = argv[++i] ?? null;
|
|
25
|
-
else if (arg === '--title')
|
|
26
|
-
cfg.title = argv[++i] ?? null;
|
|
27
|
-
else if (arg === '--quiet')
|
|
28
|
-
cfg.quiet = true;
|
|
29
|
-
else if (arg === '--help' || arg === '-h') {
|
|
30
|
-
showHelp();
|
|
31
|
-
process.exit(0);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
if (!cfg.inputFile) {
|
|
35
|
-
console.error('Error: --input required');
|
|
36
|
-
showHelp();
|
|
37
|
-
process.exit(1);
|
|
38
|
-
}
|
|
39
|
-
return cfg;
|
|
40
|
-
}
|
|
41
|
-
function showHelp() {
|
|
42
|
-
console.log(`
|
|
43
|
-
Derivative Chart Generator (uPlot)
|
|
44
|
-
Usage:
|
|
45
|
-
node dist/analysis/derivative_chart_generator.js --input <file.json> [options]
|
|
46
|
-
Options:
|
|
47
|
-
--output FILE Output HTML (default: <analysis charts dir>/derivative_chart.html)
|
|
48
|
-
--title TEXT Chart title
|
|
49
|
-
--quiet Suppress output
|
|
50
|
-
`);
|
|
51
|
-
}
|
|
52
|
-
function trendToNum(trend) {
|
|
53
|
-
if (trend === 'UP')
|
|
54
|
-
return 1;
|
|
55
|
-
if (trend === 'DOWN')
|
|
56
|
-
return -1;
|
|
57
|
-
return 0;
|
|
58
|
-
}
|
|
59
|
-
function countTrend(arr) {
|
|
60
|
-
return arr.reduce((acc, v) => {
|
|
61
|
-
acc.total += 1;
|
|
62
|
-
if (v > 0)
|
|
63
|
-
acc.up += 1;
|
|
64
|
-
else if (v < 0)
|
|
65
|
-
acc.down += 1;
|
|
66
|
-
else
|
|
67
|
-
acc.neutral += 1;
|
|
68
|
-
return acc;
|
|
69
|
-
}, { total: 0, up: 0, down: 0, neutral: 0 });
|
|
70
|
-
}
|
|
71
|
-
function countStates(arr, positive, negative) {
|
|
72
|
-
return arr.reduce((acc, v) => {
|
|
73
|
-
acc.total += 1;
|
|
74
|
-
if (v === positive)
|
|
75
|
-
acc.positive += 1;
|
|
76
|
-
else if (v === negative)
|
|
77
|
-
acc.negative += 1;
|
|
78
|
-
else
|
|
79
|
-
acc.neutral += 1;
|
|
80
|
-
return acc;
|
|
81
|
-
}, { total: 0, positive: 0, negative: 0, neutral: 0 });
|
|
82
|
-
}
|
|
83
|
-
function generateHTML(data, title) {
|
|
84
|
-
const results = data.allResults || [];
|
|
85
|
-
if (results.length === 0)
|
|
86
|
-
throw new Error('No analysis results in input');
|
|
87
|
-
const source = data.config?.source || 'Unknown';
|
|
88
|
-
const smaPeriod = data.config?.slowSmaPeriod || 'N/A';
|
|
89
|
-
const fastSmaPeriod = data.config?.fastSmaPeriod || null;
|
|
90
|
-
const hasFastSma = fastSmaPeriod !== null && results.some((r) => r.fastSmaValue !== null && r.fastSmaValue !== undefined);
|
|
91
|
-
const macdFast = data.config?.macdFastPeriod ?? 12;
|
|
92
|
-
const macdSlow = data.config?.macdSlowPeriod ?? 26;
|
|
93
|
-
const macdSig = data.config?.macdSignalPeriod ?? 9;
|
|
94
|
-
const rsiPeriod = data.config?.rsiPeriod ?? 14;
|
|
95
|
-
const rsiOB = data.config?.rsiExtreme ?? 90;
|
|
96
|
-
const rsiOS = 100 - rsiOB;
|
|
97
|
-
const dates = results.map((r, idx) => toEpochSeconds(r.timestamp || Date.now(), idx));
|
|
98
|
-
const prices = results.map((r) => r.price);
|
|
99
|
-
const smaValues = results.map((r) => r.slowSma);
|
|
100
|
-
const fastSmaValues = results.map((r) => r.fastSmaValue);
|
|
101
|
-
const smaNum = results.map((r) => trendToNum(r.smaRawTrend));
|
|
102
|
-
const smaUp = smaNum.map((v) => (v > 0 ? 1 : 0));
|
|
103
|
-
const smaDown = smaNum.map((v) => (v < 0 ? -1 : 0));
|
|
104
|
-
const smaConf = results.map((r) => r.smaConfidence ?? 0);
|
|
105
|
-
const fastSmaNum = results.map((r) => trendToNum(r.fastSmaRawTrend));
|
|
106
|
-
const fastSmaUp = fastSmaNum.map((v) => (v > 0 ? 1 : 0));
|
|
107
|
-
const fastSmaDown = fastSmaNum.map((v) => (v < 0 ? -1 : 0));
|
|
108
|
-
const fastSmaConf = results.map((r) => r.fastSmaConfidence || 0);
|
|
109
|
-
const macdHistogram = results.map((r) => r.macdHistogram ?? null);
|
|
110
|
-
const macdLine = results.map((r) => r.macdLine ?? null);
|
|
111
|
-
const macdSignal = results.map((r) => r.macdSignal ?? null);
|
|
112
|
-
const macdHistUp = macdHistogram.map((v) => (v !== null && v > 0 ? v : null));
|
|
113
|
-
const macdHistDown = macdHistogram.map((v) => (v !== null && v < 0 ? v : null));
|
|
114
|
-
const rsiValues = results.map((r) => r.rsi ?? null);
|
|
115
|
-
const interpState = results.map((r) => r.interpretation || 'NEUTRAL');
|
|
116
|
-
const interpBars = results.map((r) => r.interpretationBars ?? 0);
|
|
117
|
-
const interpValues = interpState.map((s) => (s === 'BULL' ? 0.75
|
|
118
|
-
: s === 'BULL_WEAK' ? 0.35
|
|
119
|
-
: s === 'OVERBOUGHT' ? 0.95
|
|
120
|
-
: s === 'BEAR' ? -0.75
|
|
121
|
-
: s === 'BEAR_WEAK' ? -0.35
|
|
122
|
-
: s === 'OVERSOLD' ? -0.95
|
|
123
|
-
: 0));
|
|
124
|
-
const interpBull = interpState.map((s) => (s === 'BULL' ? 1 : 0));
|
|
125
|
-
const interpBullWeak = interpState.map((s) => (s === 'BULL_WEAK' ? 0.5 : 0));
|
|
126
|
-
const interpOB = interpState.map((s) => (s === 'OVERBOUGHT' ? 0.75 : 0));
|
|
127
|
-
const interpBear = interpState.map((s) => (s === 'BEAR' ? -1 : 0));
|
|
128
|
-
const interpBearWeak = interpState.map((s) => (s === 'BEAR_WEAK' ? -0.5 : 0));
|
|
129
|
-
const interpOS = interpState.map((s) => (s === 'OVERSOLD' ? -0.75 : 0));
|
|
130
|
-
const interpBullBlock = interpState.map((s) => (s === 'BULL' ? 1 : 0));
|
|
131
|
-
const interpBullWeakBlock = interpState.map((s) => (s === 'BULL_WEAK' ? 1 : 0));
|
|
132
|
-
const interpOBBlock = interpState.map((s) => (s === 'OVERBOUGHT' ? 1 : 0));
|
|
133
|
-
const interpBearBlock = interpState.map((s) => (s === 'BEAR' ? -1 : 0));
|
|
134
|
-
const interpBearWeakBlock = interpState.map((s) => (s === 'BEAR_WEAK' ? -1 : 0));
|
|
135
|
-
const interpOSBlock = interpState.map((s) => (s === 'OVERSOLD' ? -1 : 0));
|
|
136
|
-
const entryBias = results.map((r) => r.entryBias || 'NONE');
|
|
137
|
-
const entryLabelMap = {
|
|
138
|
-
NONE: 'No fresh entry',
|
|
139
|
-
EARLY_LONG: 'Early long entry',
|
|
140
|
-
CONFIRM_LONG: 'Confirmed long entry',
|
|
141
|
-
LATE_LONG: 'Late long entry',
|
|
142
|
-
EARLY_SHORT: 'Early short entry',
|
|
143
|
-
CONFIRM_SHORT: 'Confirmed short entry',
|
|
144
|
-
LATE_SHORT: 'Late short entry',
|
|
145
|
-
};
|
|
146
|
-
const entryBiasLabel = entryBias.map((v) => entryLabelMap[v] || v);
|
|
147
|
-
const signalPhase = results.map((_r, i) => {
|
|
148
|
-
if (entryBias[i] !== 'NONE')
|
|
149
|
-
return entryLabelMap[entryBias[i]] || entryBias[i];
|
|
150
|
-
switch (interpState[i]) {
|
|
151
|
-
case 'BULL':
|
|
152
|
-
return 'Bull trend active';
|
|
153
|
-
case 'BULL_WEAK':
|
|
154
|
-
return 'Bull setup active';
|
|
155
|
-
case 'BEAR':
|
|
156
|
-
return 'Bear trend active';
|
|
157
|
-
case 'BEAR_WEAK':
|
|
158
|
-
return 'Bear setup active';
|
|
159
|
-
case 'OVERBOUGHT':
|
|
160
|
-
return 'Overbought exit pressure';
|
|
161
|
-
case 'OVERSOLD':
|
|
162
|
-
return 'Oversold exit pressure';
|
|
163
|
-
default:
|
|
164
|
-
return 'No active setup';
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
const bullWeakEntryMarkers = results.map((r) => (r.isBullWeakEntry ? 0.38 : null));
|
|
168
|
-
const bullConfirmationMarkers = results.map((r) => (r.isBullConfirmation ? 0.88 : null));
|
|
169
|
-
const lateBullMarkers = results.map((r) => (r.isLateBullWithoutWeak ? 0.88 : null));
|
|
170
|
-
const bearWeakEntryMarkers = results.map((r) => (r.isBearWeakEntry ? -0.38 : null));
|
|
171
|
-
const bearConfirmationMarkers = results.map((r) => (r.isBearConfirmation ? -0.88 : null));
|
|
172
|
-
const lateBearMarkers = results.map((r) => (r.isLateBearWithoutWeak ? -0.88 : null));
|
|
173
|
-
const priceSeries = prices.filter((v) => Number.isFinite(v));
|
|
174
|
-
const priceStart = priceSeries.length ? priceSeries[0] : null;
|
|
175
|
-
const priceEnd = priceSeries.length ? priceSeries[priceSeries.length - 1] : null;
|
|
176
|
-
const priceHigh = priceSeries.length ? Math.max(...priceSeries) : null;
|
|
177
|
-
const priceLow = priceSeries.length ? Math.min(...priceSeries) : null;
|
|
178
|
-
const priceChange = priceStart !== null && priceEnd !== null ? priceEnd - priceStart : null;
|
|
179
|
-
const priceChangePct = priceStart !== null && priceEnd !== null && priceStart !== 0
|
|
180
|
-
? ((priceEnd - priceStart) / priceStart) * 100
|
|
181
|
-
: null;
|
|
182
|
-
const smaTotals = countTrend(smaNum);
|
|
183
|
-
const fastSmaTotals = countTrend(fastSmaNum);
|
|
184
|
-
const macdTotals = countTrend(results.map((r) => (r.macdTrend === 'BULL' ? 1 : r.macdTrend === 'BEAR' ? -1 : 0)));
|
|
185
|
-
const rsiTotals = countStates(results.map((r) => r.rsiZone || 'NEUTRAL'), 'OVERBOUGHT', 'OVERSOLD');
|
|
186
|
-
const signalTotals = (() => {
|
|
187
|
-
let up = 0, down = 0, prev = null;
|
|
188
|
-
for (const state of interpState) {
|
|
189
|
-
const isBull = state === 'BULL' || state === 'BULL_WEAK';
|
|
190
|
-
const isBear = state === 'BEAR' || state === 'BEAR_WEAK';
|
|
191
|
-
if (isBull) {
|
|
192
|
-
if (state !== prev)
|
|
193
|
-
up += 1;
|
|
194
|
-
}
|
|
195
|
-
else if (isBear) {
|
|
196
|
-
if (state !== prev)
|
|
197
|
-
down += 1;
|
|
198
|
-
}
|
|
199
|
-
prev = state;
|
|
200
|
-
}
|
|
201
|
-
return { up, down };
|
|
202
|
-
})();
|
|
203
|
-
const candleCount = results.length;
|
|
204
|
-
const fmtPrice = (v) => (v === null ? 'n/a' : fixedTo(v, 6));
|
|
205
|
-
const fmtSignedPrice = (v) => (v === null ? 'n/a' : `${v >= 0 ? '+' : ''}${Math.round(Number(v))}`);
|
|
206
|
-
const fmtPct = (v) => (v === null ? 'n/a' : `${v >= 0 ? '+' : ''}${Math.round(Number(v))}%`);
|
|
207
|
-
const fmtShare = (count) => (candleCount > 0 ? `${Math.round((count / candleCount) * 100)}%` : 'n/a');
|
|
208
|
-
const headerParts = [];
|
|
209
|
-
if (smaPeriod !== 'N/A')
|
|
210
|
-
headerParts.push(`SMA(${smaPeriod})`);
|
|
211
|
-
if (hasFastSma)
|
|
212
|
-
headerParts.push(`fastSMA(${fastSmaPeriod})`);
|
|
213
|
-
headerParts.push(`MACD(${macdFast},${macdSlow},${macdSig})`);
|
|
214
|
-
headerParts.push(`RSI(${rsiPeriod})`);
|
|
215
|
-
const headerSub = headerParts.join(' · ');
|
|
216
|
-
const statsPanel = `
|
|
217
|
-
<div><span class="label">Candles</span> <span class="val">${results.length}</span></div>
|
|
218
|
-
<div style="margin-top:6px"><span class="label">── Price summary ──</span></div>
|
|
219
|
-
<div><span class="label">Start</span> <span class="val">${fmtPrice(priceStart)}</span></div>
|
|
220
|
-
<div><span class="label">End</span> <span class="val">${fmtPrice(priceEnd)}</span></div>
|
|
221
|
-
<div style="margin-top:4px"></div>
|
|
222
|
-
<div><span class="label">High</span> <span class="val">${fmtPrice(priceHigh)}</span></div>
|
|
223
|
-
<div><span class="label">Low</span> <span class="val">${fmtPrice(priceLow)}</span></div>
|
|
224
|
-
<div style="margin-top:4px"></div>
|
|
225
|
-
<div><span class="label">Abs change</span> <span class="val">${fmtSignedPrice(priceChange)}</span></div>
|
|
226
|
-
<div><span class="label">Rel change</span> <span class="val">${fmtPct(priceChangePct)}</span></div>
|
|
227
|
-
<div style="margin-top:6px"><span class="label">── Totals ──</span></div>
|
|
228
|
-
<div><span class="label">SMA(${smaPeriod})</span> <span class="val"><span class="pos">▲</span>${fmtShare(smaTotals.up)} <span class="neg">▼</span>${fmtShare(smaTotals.down)} <span class="muted">●</span>${fmtShare(smaTotals.neutral)}</span></div>
|
|
229
|
-
${hasFastSma ? `<div><span class="label">fastSMA(${fastSmaPeriod})</span> <span class="val"><span class="pos">▲</span>${fmtShare(fastSmaTotals.up)} <span class="neg">▼</span>${fmtShare(fastSmaTotals.down)} <span class="muted">●</span>${fmtShare(fastSmaTotals.neutral)}</span></div>` : ''}
|
|
230
|
-
<div style="margin-top:4px"></div>
|
|
231
|
-
<div><span class="label">MACD(${macdFast},${macdSlow},${macdSig})</span> <span class="val"><span class="pos">▲</span>${fmtShare(macdTotals.up)} <span class="neg">▼</span>${fmtShare(macdTotals.down)} <span class="muted">●</span>${fmtShare(macdTotals.neutral)}</span></div>
|
|
232
|
-
<div><span class="label">RSI(${rsiPeriod})</span> <span class="val"><span class="pos">▲</span>${fmtShare(rsiTotals.positive)} <span class="neg">▼</span>${fmtShare(rsiTotals.negative)} <span class="muted">●</span>${fmtShare(rsiTotals.neutral)}</span></div>
|
|
233
|
-
<div style="margin-top:4px"></div>
|
|
234
|
-
<div><span class="label">Signals</span> <span class="val"><span class="pos">▲</span>${signalTotals.up} <span class="neg">▼</span>${signalTotals.down}</span></div>
|
|
235
|
-
`;
|
|
236
|
-
const chartId = `deriv-uplot-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
237
|
-
return `<!DOCTYPE html>
|
|
238
|
-
<html lang="en">
|
|
239
|
-
<head>
|
|
240
|
-
<meta charset="UTF-8">
|
|
241
|
-
<meta name="darkreader-lock">
|
|
242
|
-
<meta name="color-scheme" content="dark">
|
|
243
|
-
<title>${escapeHtml(title)}</title>
|
|
244
|
-
${uplotInlineTags()}
|
|
245
|
-
<style>
|
|
246
|
-
:root { color-scheme: dark; }
|
|
247
|
-
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
248
|
-
body {
|
|
249
|
-
background: #0e1117;
|
|
250
|
-
color: #e0e0e0;
|
|
251
|
-
font-family: 'Segoe UI', sans-serif;
|
|
252
|
-
overflow: hidden;
|
|
253
|
-
}
|
|
254
|
-
#header {
|
|
255
|
-
position: fixed;
|
|
256
|
-
top: 0;
|
|
257
|
-
left: 0;
|
|
258
|
-
right: 0;
|
|
259
|
-
z-index: 100;
|
|
260
|
-
background: rgba(14,17,23,0.92);
|
|
261
|
-
backdrop-filter: blur(4px);
|
|
262
|
-
border-bottom: 1px solid #2a2e3e;
|
|
263
|
-
padding: 8px 20px;
|
|
264
|
-
display: flex;
|
|
265
|
-
align-items: center;
|
|
266
|
-
gap: 18px;
|
|
267
|
-
}
|
|
268
|
-
#header h1 { font-size: 16px; font-weight: 600; color: #fff; white-space: nowrap; }
|
|
269
|
-
#header .sub { font-size: 11px; color: #888; white-space: nowrap; }
|
|
270
|
-
#stats {
|
|
271
|
-
position: fixed;
|
|
272
|
-
top: 160px;
|
|
273
|
-
right: 10px;
|
|
274
|
-
z-index: 100;
|
|
275
|
-
background: rgba(20,24,32,0.92);
|
|
276
|
-
backdrop-filter: blur(4px);
|
|
277
|
-
border: 1px solid #2a2e3e;
|
|
278
|
-
border-radius: 6px;
|
|
279
|
-
padding: 10px 14px;
|
|
280
|
-
font-size: 12px;
|
|
281
|
-
line-height: 1.85;
|
|
282
|
-
min-width: 230px;
|
|
283
|
-
}
|
|
284
|
-
#stats .label { color: #888; }
|
|
285
|
-
#stats .val { color: #e0e0e0; font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
286
|
-
#stats .pos { color: #26a69a; }
|
|
287
|
-
#stats .neg { color: #ef5350; }
|
|
288
|
-
#stats .muted { color: #9ca3b8; }
|
|
289
|
-
#charts {
|
|
290
|
-
position: fixed;
|
|
291
|
-
top: 44px;
|
|
292
|
-
left: 0;
|
|
293
|
-
right: 276px;
|
|
294
|
-
bottom: 0;
|
|
295
|
-
padding-top: 0;
|
|
296
|
-
display: flex;
|
|
297
|
-
flex-direction: column;
|
|
298
|
-
gap: 0;
|
|
299
|
-
border-right: 1px solid #2a2e3e;
|
|
300
|
-
}
|
|
301
|
-
.chart {
|
|
302
|
-
width: 100%;
|
|
303
|
-
min-height: 0;
|
|
304
|
-
position: relative;
|
|
305
|
-
padding-top: 18px;
|
|
306
|
-
}
|
|
307
|
-
.chart::before {
|
|
308
|
-
content: attr(data-panel-title);
|
|
309
|
-
position: absolute;
|
|
310
|
-
top: 4px;
|
|
311
|
-
left: 10px;
|
|
312
|
-
z-index: 2;
|
|
313
|
-
pointer-events: none;
|
|
314
|
-
font-size: 10px;
|
|
315
|
-
letter-spacing: 0.08em;
|
|
316
|
-
text-transform: uppercase;
|
|
317
|
-
color: #8ea3be;
|
|
318
|
-
background: rgba(14,17,23,0.84);
|
|
319
|
-
border: 1px solid #263042;
|
|
320
|
-
border-radius: 999px;
|
|
321
|
-
padding: 2px 8px;
|
|
322
|
-
}
|
|
323
|
-
#price-chart { flex: 2.35; }
|
|
324
|
-
#deriv-chart { flex: 0.78; }
|
|
325
|
-
#interp-chart { flex: 1.22; }
|
|
326
|
-
#macd-chart { flex: 1.05; }
|
|
327
|
-
#rsi-chart { flex: 1.28; min-height: 0; }
|
|
328
|
-
.uplot { background: #0e1117; }
|
|
329
|
-
.u-cursor-y { display: none !important; border-top: 1px dashed rgba(255,255,255,0.35) !important; background: transparent !important; }
|
|
330
|
-
.uplot.is-hovered .u-cursor-y { display: block !important; }
|
|
331
|
-
.u-cursor-x { border-left: 1px dashed rgba(255,255,255,0.35) !important; background: transparent !important; }
|
|
332
|
-
.u-legend { display: none !important; }
|
|
333
|
-
#global-tooltip {
|
|
334
|
-
position: fixed;
|
|
335
|
-
z-index: 200;
|
|
336
|
-
pointer-events: none;
|
|
337
|
-
background: rgba(14,17,23,0.96);
|
|
338
|
-
border: 1px solid #2a2e3e;
|
|
339
|
-
border-radius: 6px;
|
|
340
|
-
padding: 8px 12px;
|
|
341
|
-
font-size: 11px;
|
|
342
|
-
line-height: 1.6;
|
|
343
|
-
color: #e0e0e0;
|
|
344
|
-
box-shadow: 0 4px 16px rgba(0,0,0,0.4);
|
|
345
|
-
display: none;
|
|
346
|
-
max-width: 260px;
|
|
347
|
-
white-space: nowrap;
|
|
348
|
-
}
|
|
349
|
-
#global-tooltip .tt-row { display: flex; justify-content: space-between; gap: 16px; }
|
|
350
|
-
#global-tooltip .tt-label { color: #888; }
|
|
351
|
-
#global-tooltip .tt-val { font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
352
|
-
#global-tooltip .tt-sep { border-top: 1px solid #263042; margin: 4px 0; }
|
|
353
|
-
#global-tooltip .tt-head { color: #fff; font-weight: 700; margin-bottom: 4px; }
|
|
354
|
-
</style>
|
|
355
|
-
</head>
|
|
356
|
-
<body>
|
|
357
|
-
<div id="global-tooltip"></div>
|
|
358
|
-
<div id="header">
|
|
359
|
-
<h1>📊 ${escapeHtml(title)}</h1>
|
|
360
|
-
<span class="sub">Source: ${escapeHtml(source)}</span>
|
|
361
|
-
<span class="sub">${headerSub}</span>
|
|
362
|
-
<button id="reset-zoom-btn" onclick="resetZoom()" title="Reset x-axis zoom on all panels" style="background:#1e2330;color:#ccc;border:1px solid #2a2e3e;border-radius:4px;padding:3px 10px;font-size:11px;cursor:pointer;white-space:nowrap;">↺ Reset Zoom</button>
|
|
363
|
-
<span class="sub" style="margin-left:auto">Generated: ${new Date().toLocaleString()}</span>
|
|
364
|
-
</div>
|
|
365
|
-
<div id="stats">${statsPanel}</div>
|
|
366
|
-
<div id="charts">
|
|
367
|
-
<div id="price-chart" class="chart" data-panel-title="Price / AMA"></div>
|
|
368
|
-
<div id="deriv-chart" class="chart" data-panel-title="Trend Blocks"></div>
|
|
369
|
-
<div id="interp-chart" class="chart" data-panel-title="Signal State"></div>
|
|
370
|
-
<div id="macd-chart" class="chart" data-panel-title="MACD"></div>
|
|
371
|
-
<div id="rsi-chart" class="chart" data-panel-title="RSI"></div>
|
|
372
|
-
</div>
|
|
373
|
-
<script type="application/json" id="deriv-payload">${serializeJsonForScript({
|
|
374
|
-
dates,
|
|
375
|
-
prices,
|
|
376
|
-
smaValues,
|
|
377
|
-
fastSmaValues: hasFastSma ? fastSmaValues : null,
|
|
378
|
-
macdHistogram,
|
|
379
|
-
macdLine,
|
|
380
|
-
macdSignal,
|
|
381
|
-
macdHistUp,
|
|
382
|
-
macdHistDown,
|
|
383
|
-
rsiValues,
|
|
384
|
-
interpBull,
|
|
385
|
-
interpBullWeak,
|
|
386
|
-
interpOB,
|
|
387
|
-
interpBear,
|
|
388
|
-
interpBearWeak,
|
|
389
|
-
interpOS,
|
|
390
|
-
interpBullBlock,
|
|
391
|
-
interpBullWeakBlock,
|
|
392
|
-
interpOBBlock,
|
|
393
|
-
interpBearBlock,
|
|
394
|
-
interpBearWeakBlock,
|
|
395
|
-
interpOSBlock,
|
|
396
|
-
interpValues,
|
|
397
|
-
interpState,
|
|
398
|
-
interpBars,
|
|
399
|
-
entryBias,
|
|
400
|
-
entryBiasLabel,
|
|
401
|
-
signalPhase,
|
|
402
|
-
bullWeakEntryMarkers,
|
|
403
|
-
bullConfirmationMarkers,
|
|
404
|
-
lateBullMarkers,
|
|
405
|
-
bearWeakEntryMarkers,
|
|
406
|
-
bearConfirmationMarkers,
|
|
407
|
-
lateBearMarkers,
|
|
408
|
-
smaUp,
|
|
409
|
-
smaDown,
|
|
410
|
-
smaConf,
|
|
411
|
-
smaNum,
|
|
412
|
-
fastSmaUp: hasFastSma ? fastSmaUp : null,
|
|
413
|
-
fastSmaDown: hasFastSma ? fastSmaDown : null,
|
|
414
|
-
fastSmaConf: hasFastSma ? fastSmaConf : null,
|
|
415
|
-
fastSmaNum: hasFastSma ? fastSmaNum : null,
|
|
416
|
-
rsiOB,
|
|
417
|
-
rsiOS,
|
|
418
|
-
})}</script>
|
|
419
|
-
<script>
|
|
420
|
-
const payload = JSON.parse(document.getElementById('deriv-payload').textContent);
|
|
421
|
-
const dates = payload.dates;
|
|
422
|
-
const prices = payload.prices;
|
|
423
|
-
const smaValues = payload.smaValues;
|
|
424
|
-
const fastSmaValues = payload.fastSmaValues || [];
|
|
425
|
-
const macdHistogram = payload.macdHistogram;
|
|
426
|
-
const macdLine = payload.macdLine;
|
|
427
|
-
const macdSignal = payload.macdSignal;
|
|
428
|
-
const macdHistUp = payload.macdHistUp;
|
|
429
|
-
const macdHistDown = payload.macdHistDown;
|
|
430
|
-
const rsiValues = payload.rsiValues;
|
|
431
|
-
const interpBull = payload.interpBull;
|
|
432
|
-
const interpBullWeak = payload.interpBullWeak;
|
|
433
|
-
const interpOB = payload.interpOB;
|
|
434
|
-
const interpBear = payload.interpBear;
|
|
435
|
-
const interpBearWeak = payload.interpBearWeak;
|
|
436
|
-
const interpOS = payload.interpOS;
|
|
437
|
-
const interpBullBlock = payload.interpBullBlock;
|
|
438
|
-
const interpBullWeakBlock = payload.interpBullWeakBlock;
|
|
439
|
-
const interpOBBlock = payload.interpOBBlock;
|
|
440
|
-
const interpBearBlock = payload.interpBearBlock;
|
|
441
|
-
const interpBearWeakBlock = payload.interpBearWeakBlock;
|
|
442
|
-
const interpOSBlock = payload.interpOSBlock;
|
|
443
|
-
const interpValues = payload.interpValues;
|
|
444
|
-
const interpState = payload.interpState;
|
|
445
|
-
const interpBars = payload.interpBars;
|
|
446
|
-
const entryBias = payload.entryBias;
|
|
447
|
-
const entryBiasLabel = payload.entryBiasLabel;
|
|
448
|
-
const signalPhase = payload.signalPhase;
|
|
449
|
-
const bullWeakEntryMarkers = payload.bullWeakEntryMarkers;
|
|
450
|
-
const bullConfirmationMarkers = payload.bullConfirmationMarkers;
|
|
451
|
-
const lateBullMarkers = payload.lateBullMarkers;
|
|
452
|
-
const bearWeakEntryMarkers = payload.bearWeakEntryMarkers;
|
|
453
|
-
const bearConfirmationMarkers = payload.bearConfirmationMarkers;
|
|
454
|
-
const lateBearMarkers = payload.lateBearMarkers;
|
|
455
|
-
const smaUp = payload.smaUp;
|
|
456
|
-
const smaDown = payload.smaDown;
|
|
457
|
-
const smaConf = payload.smaConf;
|
|
458
|
-
const smaNum = payload.smaNum;
|
|
459
|
-
const hasFastSma = Array.isArray(payload.fastSmaValues) && payload.fastSmaValues.length > 0;
|
|
460
|
-
const fastSmaUp = payload.fastSmaUp || [];
|
|
461
|
-
const fastSmaDown = payload.fastSmaDown || [];
|
|
462
|
-
const fastSmaConf = payload.fastSmaConf || [];
|
|
463
|
-
const fastSmaNum = payload.fastSmaNum || [];
|
|
464
|
-
const rsiOB = payload.rsiOB;
|
|
465
|
-
const rsiOS = payload.rsiOS;
|
|
466
|
-
const chartGroupId = ${JSON.stringify(chartId)};
|
|
467
|
-
const THEME = {
|
|
468
|
-
text: '#ccc',
|
|
469
|
-
muted: '#888',
|
|
470
|
-
background: '#0e1117',
|
|
471
|
-
grid: '#1e2330',
|
|
472
|
-
axis: '#ffffff',
|
|
473
|
-
};
|
|
474
|
-
const SIGNAL_GREEN = 'rgba(38,166,154,0.45)';
|
|
475
|
-
const SIGNAL_GREEN_WEAK = 'rgba(38,166,154,0.22)';
|
|
476
|
-
const SIGNAL_GREEN_LIGHT = 'rgba(38,166,154,0.18)';
|
|
477
|
-
const SIGNAL_RED = 'rgba(239,83,80,0.45)';
|
|
478
|
-
const SIGNAL_RED_WEAK = 'rgba(239,83,80,0.22)';
|
|
479
|
-
const SIGNAL_RED_LIGHT = 'rgba(239,83,80,0.18)';
|
|
480
|
-
function initChart(domId, opts, data) {
|
|
481
|
-
if (typeof uPlot === 'undefined') throw new Error('uPlot library did not load');
|
|
482
|
-
const el = document.getElementById(domId);
|
|
483
|
-
const rect = el.getBoundingClientRect();
|
|
484
|
-
const chart = new uPlot({
|
|
485
|
-
...opts,
|
|
486
|
-
width: Math.max(320, Math.floor(rect.width)),
|
|
487
|
-
height: Math.max(110, Math.floor(rect.height)),
|
|
488
|
-
}, data, el);
|
|
489
|
-
chart.root.style.background = THEME.background;
|
|
490
|
-
return chart;
|
|
491
|
-
}
|
|
492
|
-
function fmtShortDate(sec) {
|
|
493
|
-
const d = new Date(sec * 1000);
|
|
494
|
-
return d.toLocaleDateString(undefined, { month: '2-digit', day: '2-digit' });
|
|
495
|
-
}
|
|
496
|
-
function fixedTo(value, decimals) {
|
|
497
|
-
return Number(value).toFixed(decimals);
|
|
498
|
-
}
|
|
499
|
-
function padRange(min, max, lower = 0.04, upper = 0.04) {
|
|
500
|
-
if (!Number.isFinite(min) || !Number.isFinite(max)) return null;
|
|
501
|
-
if (min === max) {
|
|
502
|
-
const delta = Math.abs(min) * 0.03 || 1;
|
|
503
|
-
return [min - delta, max + delta];
|
|
504
|
-
}
|
|
505
|
-
const span = max - min;
|
|
506
|
-
return [min - span * lower, max + span * upper];
|
|
507
|
-
}
|
|
508
|
-
function makeAxis(show, type) {
|
|
509
|
-
return {
|
|
510
|
-
show,
|
|
511
|
-
scale: type,
|
|
512
|
-
stroke: THEME.axis,
|
|
513
|
-
grid: { show: false },
|
|
514
|
-
ticks: { show: true, stroke: THEME.axis, width: 1 },
|
|
515
|
-
size: show ? 34 : 0,
|
|
516
|
-
space: 50,
|
|
517
|
-
font: '10px Segoe UI, sans-serif',
|
|
518
|
-
label: '',
|
|
519
|
-
values: show ? (u, splits) => splits.map((v) => fmtShortDate(v)) : () => [],
|
|
520
|
-
};
|
|
521
|
-
}
|
|
522
|
-
function makeYAxis(show, formatter, size = 58, label = '') {
|
|
523
|
-
return {
|
|
524
|
-
show,
|
|
525
|
-
scale: 'y',
|
|
526
|
-
stroke: THEME.axis,
|
|
527
|
-
grid: { show: true, stroke: THEME.grid },
|
|
528
|
-
ticks: { show: true, stroke: THEME.axis, width: 1 },
|
|
529
|
-
size,
|
|
530
|
-
space: 60,
|
|
531
|
-
font: '10px Segoe UI, sans-serif',
|
|
532
|
-
values: formatter,
|
|
533
|
-
label,
|
|
534
|
-
labelSize: label ? 14 : 0,
|
|
535
|
-
labelGap: 4,
|
|
536
|
-
labelFont: '10px Segoe UI, sans-serif',
|
|
537
|
-
};
|
|
538
|
-
}
|
|
539
|
-
function makeCursor() {
|
|
540
|
-
return {
|
|
541
|
-
show: true,
|
|
542
|
-
x: true,
|
|
543
|
-
y: true,
|
|
544
|
-
points: { show: false },
|
|
545
|
-
drag: { x: false, y: false, setScale: false },
|
|
546
|
-
sync: { key: chartGroupId, setSeries: false, scales: ['x', null] },
|
|
547
|
-
focus: { prox: -1 },
|
|
548
|
-
};
|
|
549
|
-
}
|
|
550
|
-
function makePlotBase(showX, yFormatter, yScale) {
|
|
551
|
-
return {
|
|
552
|
-
width: 0,
|
|
553
|
-
height: 0,
|
|
554
|
-
title: null,
|
|
555
|
-
legend: { show: false },
|
|
556
|
-
cursor: makeCursor(),
|
|
557
|
-
select: { show: false },
|
|
558
|
-
scales: {
|
|
559
|
-
x: { time: true },
|
|
560
|
-
y: yScale,
|
|
561
|
-
},
|
|
562
|
-
axes: [
|
|
563
|
-
makeAxis(showX, 'x'),
|
|
564
|
-
makeYAxis(true, yFormatter),
|
|
565
|
-
],
|
|
566
|
-
series: [
|
|
567
|
-
{ label: 'Time' },
|
|
568
|
-
],
|
|
569
|
-
padding: [10, 16, 0, 0],
|
|
570
|
-
hooks: {
|
|
571
|
-
ready: [
|
|
572
|
-
(u) => {
|
|
573
|
-
u.root.style.background = THEME.background;
|
|
574
|
-
},
|
|
575
|
-
],
|
|
576
|
-
},
|
|
577
|
-
};
|
|
578
|
-
}
|
|
579
|
-
function lineSeries(name, color, width, extra = {}) {
|
|
580
|
-
return {
|
|
581
|
-
label: name,
|
|
582
|
-
stroke: color,
|
|
583
|
-
width,
|
|
584
|
-
points: { show: false },
|
|
585
|
-
fill: null,
|
|
586
|
-
...extra,
|
|
587
|
-
};
|
|
588
|
-
}
|
|
589
|
-
function barSeries(name, color, fill) {
|
|
590
|
-
return {
|
|
591
|
-
label: name,
|
|
592
|
-
stroke: color,
|
|
593
|
-
fill,
|
|
594
|
-
width: 1,
|
|
595
|
-
points: { show: false },
|
|
596
|
-
paths: uPlot.paths.bars({ align: 0, gap: 1, size: [0.72, 14, 1] }),
|
|
597
|
-
};
|
|
598
|
-
}
|
|
599
|
-
function blockSeries(name, color, fill) {
|
|
600
|
-
return {
|
|
601
|
-
label: name,
|
|
602
|
-
stroke: color,
|
|
603
|
-
fill,
|
|
604
|
-
width: 1,
|
|
605
|
-
points: { show: false },
|
|
606
|
-
paths: uPlot.paths.bars({ align: 0, gap: 0, size: [0.96, 18, 1] }),
|
|
607
|
-
};
|
|
608
|
-
}
|
|
609
|
-
function markerSeries(name, color, size = 7) {
|
|
610
|
-
return {
|
|
611
|
-
label: name,
|
|
612
|
-
stroke: 'transparent',
|
|
613
|
-
width: 0,
|
|
614
|
-
points: { show: true, size, space: 0, fill: color, stroke: color },
|
|
615
|
-
};
|
|
616
|
-
}
|
|
617
|
-
function tooltipPlugin() {
|
|
618
|
-
const tooltip = document.getElementById('global-tooltip');
|
|
619
|
-
let lastIdx = -1;
|
|
620
|
-
function updateTooltip(u) {
|
|
621
|
-
const idx = u.cursor.idx;
|
|
622
|
-
if (idx == null || idx === lastIdx) return;
|
|
623
|
-
lastIdx = idx;
|
|
624
|
-
const d = new Date(dates[idx] * 1000);
|
|
625
|
-
const dateStr = d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' })
|
|
626
|
-
+ ' ' + d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' });
|
|
627
|
-
const priceVal = prices[idx];
|
|
628
|
-
const smaVal = smaValues[idx];
|
|
629
|
-
const fsVal = hasFastSma ? fastSmaValues[idx] : null;
|
|
630
|
-
const macdL = macdLine[idx];
|
|
631
|
-
const macdS = macdSignal[idx];
|
|
632
|
-
const macdH = macdHistogram[idx];
|
|
633
|
-
const rsiVal = rsiValues[idx];
|
|
634
|
-
const state = interpState[idx];
|
|
635
|
-
const entry = entryBiasLabel[idx];
|
|
636
|
-
const bars = interpBars[idx];
|
|
637
|
-
const rows = [
|
|
638
|
-
'<div class="tt-head">' + dateStr + '</div>',
|
|
639
|
-
];
|
|
640
|
-
if (Number.isFinite(priceVal)) {
|
|
641
|
-
rows.push('<div class="tt-row"><span class="tt-label">Price</span><span class="tt-val">' + priceVal.toFixed(6) + '</span></div>');
|
|
642
|
-
}
|
|
643
|
-
if (Number.isFinite(smaVal)) {
|
|
644
|
-
rows.push('<div class="tt-row"><span class="tt-label">SMA(' + ${JSON.stringify(smaPeriod)} + ')</span><span class="tt-val">' + smaVal.toFixed(6) + '</span></div>');
|
|
645
|
-
}
|
|
646
|
-
if (hasFastSma && Number.isFinite(fsVal)) {
|
|
647
|
-
rows.push('<div class="tt-row"><span class="tt-label">fastSMA</span><span class="tt-val">' + fsVal.toFixed(6) + '</span></div>');
|
|
648
|
-
}
|
|
649
|
-
rows.push('<div class="tt-sep"></div>');
|
|
650
|
-
if (entry && entry !== 'No fresh entry') {
|
|
651
|
-
const entryColor = entry.includes('Long') ? '#26a69a' : entry.includes('Short') ? '#ef5350' : '#ccc';
|
|
652
|
-
rows.push('<div class="tt-row"><span class="tt-label">Entry</span><span class="tt-val" style="color:' + entryColor + '">' + entry + '</span></div>');
|
|
653
|
-
}
|
|
654
|
-
if (state && state !== 'NEUTRAL') {
|
|
655
|
-
const stateColor = state.startsWith('BULL') || state === 'OVERBOUGHT' ? '#26a69a' : state.startsWith('BEAR') || state === 'OVERSOLD' ? '#ef5350' : '#ccc';
|
|
656
|
-
rows.push('<div class="tt-row"><span class="tt-label">State</span><span class="tt-val" style="color:' + stateColor + '">' + state + (bars > 0 ? ' (' + bars + ' bars)' : '') + '</span></div>');
|
|
657
|
-
}
|
|
658
|
-
rows.push('<div class="tt-sep"></div>');
|
|
659
|
-
if (Number.isFinite(macdL) && Number.isFinite(macdS)) {
|
|
660
|
-
rows.push('<div class="tt-row"><span class="tt-label">MACD</span><span class="tt-val">' + macdL.toFixed(4) + '</span></div>');
|
|
661
|
-
rows.push('<div class="tt-row"><span class="tt-label">Signal</span><span class="tt-val">' + macdS.toFixed(4) + '</span></div>');
|
|
662
|
-
rows.push('<div class="tt-row"><span class="tt-label">Hist</span><span class="tt-val">' + (macdH >= 0 ? '+' : '') + macdH.toFixed(4) + '</span></div>');
|
|
663
|
-
}
|
|
664
|
-
if (Number.isFinite(rsiVal)) {
|
|
665
|
-
rows.push('<div class="tt-row"><span class="tt-label">RSI</span><span class="tt-val">' + rsiVal.toFixed(1) + '</span></div>');
|
|
666
|
-
}
|
|
667
|
-
tooltip.innerHTML = rows.join('');
|
|
668
|
-
}
|
|
669
|
-
return {
|
|
670
|
-
hooks: {
|
|
671
|
-
init: [(u) => {
|
|
672
|
-
u.over.addEventListener('mouseenter', () => { tooltip.style.display = 'block'; });
|
|
673
|
-
u.over.addEventListener('mouseleave', () => { tooltip.style.display = 'none'; lastIdx = -1; });
|
|
674
|
-
}],
|
|
675
|
-
setCursor: [(u) => {
|
|
676
|
-
updateTooltip(u);
|
|
677
|
-
const { left, top } = u.cursor;
|
|
678
|
-
const rect = u.root.getBoundingClientRect();
|
|
679
|
-
const ttWidth = tooltip.offsetWidth || 220;
|
|
680
|
-
const ttHeight = tooltip.offsetHeight || 180;
|
|
681
|
-
let x = rect.left + left + 16;
|
|
682
|
-
let y = rect.top + top + 16;
|
|
683
|
-
if (x + ttWidth > window.innerWidth - 12) x = rect.left + left - ttWidth - 16;
|
|
684
|
-
if (y + ttHeight > window.innerHeight - 12) y = rect.top + top - ttHeight - 16;
|
|
685
|
-
tooltip.style.left = x + 'px';
|
|
686
|
-
tooltip.style.top = y + 'px';
|
|
687
|
-
}],
|
|
688
|
-
},
|
|
689
|
-
};
|
|
690
|
-
}
|
|
691
|
-
function applyChartHeights() {
|
|
692
|
-
const available = Math.max(window.innerHeight - 44, 300);
|
|
693
|
-
const specs = [
|
|
694
|
-
['price-chart', 2.35],
|
|
695
|
-
['deriv-chart', 0.78],
|
|
696
|
-
['interp-chart', 1.22],
|
|
697
|
-
['macd-chart', 1.05],
|
|
698
|
-
['rsi-chart', 1.28],
|
|
699
|
-
];
|
|
700
|
-
const minHeight = 60;
|
|
701
|
-
const extraAvailable = Math.max(0, available - (minHeight * specs.length));
|
|
702
|
-
const totalWeight = specs.reduce((sum, [, weight]) => sum + weight, 0);
|
|
703
|
-
let assignedExtra = 0;
|
|
704
|
-
specs.forEach(([id, weight], idx) => {
|
|
705
|
-
const extra = idx === specs.length - 1
|
|
706
|
-
? extraAvailable - assignedExtra
|
|
707
|
-
: Math.floor((extraAvailable * weight) / totalWeight);
|
|
708
|
-
assignedExtra += extra;
|
|
709
|
-
document.getElementById(id).style.height = (minHeight + extra) + 'px';
|
|
710
|
-
});
|
|
711
|
-
}
|
|
712
|
-
let priceChart;
|
|
713
|
-
let derivChart;
|
|
714
|
-
let interpChart;
|
|
715
|
-
let macdChart;
|
|
716
|
-
let rsiChart;
|
|
717
|
-
let charts = [];
|
|
718
|
-
let pendingRange = null;
|
|
719
|
-
let pendingRangeRaf = 0;
|
|
720
|
-
const [xMin, xMax] = [dates[0], dates[dates.length - 1]];
|
|
721
|
-
try {
|
|
722
|
-
priceChart = initChart('price-chart', {
|
|
723
|
-
...makePlotBase(false, (u, vals) => vals.map((v) => fixedTo(v, 6)), {
|
|
724
|
-
auto: true,
|
|
725
|
-
distr: 3,
|
|
726
|
-
log: 10,
|
|
727
|
-
range: (u, min, max) => {
|
|
728
|
-
if (!Number.isFinite(min) || !Number.isFinite(max) || min <= 0 || max <= 0) {
|
|
729
|
-
return [min, max];
|
|
730
|
-
}
|
|
731
|
-
return [min * 0.98, max * 1.03];
|
|
732
|
-
},
|
|
733
|
-
}),
|
|
734
|
-
axes: [
|
|
735
|
-
makeAxis(false, 'x'),
|
|
736
|
-
makeYAxis(true, (u, vals) => vals.map((v) => fixedTo(v, 6)), 62, 'Price'),
|
|
737
|
-
],
|
|
738
|
-
plugins: [tooltipPlugin()],
|
|
739
|
-
series: [
|
|
740
|
-
{ label: 'Time' },
|
|
741
|
-
lineSeries('Price', '#67a8ff', 2.35, { fill: 'rgba(103,168,255,0.08)' }),
|
|
742
|
-
lineSeries('SMA(${smaPeriod})', '#f2b94d', 2),
|
|
743
|
-
${hasFastSma ? `lineSeries('fastSMA(${fastSmaPeriod})', '#ff9d4d', 1.9),` : ''}
|
|
744
|
-
],
|
|
745
|
-
}, [dates, prices, smaValues${hasFastSma ? ', fastSmaValues' : ''}]);
|
|
746
|
-
derivChart = initChart('deriv-chart', {
|
|
747
|
-
...makePlotBase(false, (u, vals) => vals.map((v) => (v === 1 ? 'UP' : v === -1 ? 'DOWN' : '—')), {
|
|
748
|
-
auto: true,
|
|
749
|
-
range: (u, min, max) => [-1.15, 1.15],
|
|
750
|
-
}),
|
|
751
|
-
axes: [
|
|
752
|
-
makeAxis(false, 'x'),
|
|
753
|
-
makeYAxis(true, (u, vals) => vals.map((v) => (v === 1 ? 'UP' : v === -1 ? 'DOWN' : '—')), 56, 'Trend'),
|
|
754
|
-
],
|
|
755
|
-
series: [
|
|
756
|
-
{ label: 'Time' },
|
|
757
|
-
blockSeries('SMA Up', 'rgba(83,214,184,0.88)', 'rgba(83,214,184,0.88)'),
|
|
758
|
-
blockSeries('SMA Down', 'rgba(255,114,105,0.88)', 'rgba(255,114,105,0.88)'),
|
|
759
|
-
${hasFastSma ? `blockSeries('fastSMA Up', 'rgba(251,146,60,0.35)', 'rgba(251,146,60,0.35)'),
|
|
760
|
-
blockSeries('fastSMA Down', 'rgba(251,146,60,0.35)', 'rgba(251,146,60,0.35)'),` : ''}
|
|
761
|
-
lineSeries('Zero', 'rgba(255,255,255,0.20)', 1),
|
|
762
|
-
],
|
|
763
|
-
}, [dates, smaUp, smaDown, ${hasFastSma ? 'fastSmaUp, fastSmaDown, ' : ''}dates.map(() => 0)]);
|
|
764
|
-
interpChart = initChart('interp-chart', {
|
|
765
|
-
...makePlotBase(false, (u, vals) => vals.map((v) => {
|
|
766
|
-
if (v === 1) return 'BULL';
|
|
767
|
-
if (v === 0.75) return 'OS';
|
|
768
|
-
if (v === 0.5) return 'WEAK';
|
|
769
|
-
if (v === 0.35) return 'WEAK';
|
|
770
|
-
if (v === -0.35) return 'WEAK';
|
|
771
|
-
if (v === -0.5) return 'WEAK';
|
|
772
|
-
if (v === -0.75) return 'OB';
|
|
773
|
-
if (v === -1) return 'BEAR';
|
|
774
|
-
return '—';
|
|
775
|
-
}), {
|
|
776
|
-
auto: true,
|
|
777
|
-
range: (u, min, max) => [-1.15, 1.15],
|
|
778
|
-
}),
|
|
779
|
-
axes: [
|
|
780
|
-
makeAxis(false, 'x'),
|
|
781
|
-
makeYAxis(true, (u, vals) => vals.map((v) => {
|
|
782
|
-
if (v === 1) return 'BULL';
|
|
783
|
-
if (v === 0.75) return 'OS';
|
|
784
|
-
if (v === 0.5 || v === -0.5) return 'WEAK';
|
|
785
|
-
if (v === -0.75) return 'OB';
|
|
786
|
-
if (v === -1) return 'BEAR';
|
|
787
|
-
return '—';
|
|
788
|
-
}), 56, 'Signal'),
|
|
789
|
-
],
|
|
790
|
-
series: [
|
|
791
|
-
{ label: 'Time' },
|
|
792
|
-
blockSeries('Bull', SIGNAL_GREEN, SIGNAL_GREEN),
|
|
793
|
-
blockSeries('Bull Weak', SIGNAL_GREEN_WEAK, SIGNAL_GREEN_WEAK),
|
|
794
|
-
blockSeries('Overbought', SIGNAL_GREEN_LIGHT, SIGNAL_GREEN_LIGHT),
|
|
795
|
-
blockSeries('Bear', SIGNAL_RED, SIGNAL_RED),
|
|
796
|
-
blockSeries('Bear Weak', SIGNAL_RED_WEAK, SIGNAL_RED_WEAK),
|
|
797
|
-
blockSeries('Oversold', SIGNAL_RED_LIGHT, SIGNAL_RED_LIGHT),
|
|
798
|
-
lineSeries('Interp', 'rgba(255,255,255,0.35)', 1, { dash: [4, 4] }),
|
|
799
|
-
markerSeries('Early Long', '#67e8f9', 7),
|
|
800
|
-
markerSeries('Confirm Long', '#34d399', 8),
|
|
801
|
-
markerSeries('Late Long', '#f59e0b', 8),
|
|
802
|
-
markerSeries('Early Short', '#fca5a5', 7),
|
|
803
|
-
markerSeries('Confirm Short', '#ef4444', 8),
|
|
804
|
-
markerSeries('Late Short', '#fb7185', 8),
|
|
805
|
-
],
|
|
806
|
-
}, [dates, interpBullBlock, interpBullWeakBlock, interpOBBlock, interpBearBlock, interpBearWeakBlock, interpOSBlock, interpValues, bullWeakEntryMarkers, bullConfirmationMarkers, lateBullMarkers, bearWeakEntryMarkers, bearConfirmationMarkers, lateBearMarkers]);
|
|
807
|
-
macdChart = initChart('macd-chart', {
|
|
808
|
-
...makePlotBase(false, (u, vals) => vals.map((v) => fixedTo(v, 4)), {
|
|
809
|
-
auto: true,
|
|
810
|
-
range: (u, min, max) => {
|
|
811
|
-
const base = padRange(min, max, 0.12, 0.12);
|
|
812
|
-
return base || [min, max];
|
|
813
|
-
},
|
|
814
|
-
}),
|
|
815
|
-
axes: [
|
|
816
|
-
makeAxis(false, 'x'),
|
|
817
|
-
makeYAxis(true, (u, vals) => vals.map((v) => fixedTo(v, 4)), 62, 'MACD %'),
|
|
818
|
-
],
|
|
819
|
-
series: [
|
|
820
|
-
{ label: 'Time' },
|
|
821
|
-
barSeries('Hist+', 'rgba(83,214,184,0.40)', 'rgba(83,214,184,0.40)'),
|
|
822
|
-
barSeries('Hist-', 'rgba(255,114,105,0.40)', 'rgba(255,114,105,0.40)'),
|
|
823
|
-
lineSeries('MACD', '#67a8ff', 1.5),
|
|
824
|
-
lineSeries('Signal', '#ff9d4d', 1.5),
|
|
825
|
-
lineSeries('Zero', 'rgba(255,255,255,0.22)', 1),
|
|
826
|
-
],
|
|
827
|
-
}, [dates, macdHistUp, macdHistDown, macdLine, macdSignal, dates.map(() => 0)]);
|
|
828
|
-
rsiChart = initChart('rsi-chart', {
|
|
829
|
-
...makePlotBase(true, (u, vals) => vals.map((v) => fixedTo(v, 1)), {
|
|
830
|
-
range: (u, min, max) => [0, 100],
|
|
831
|
-
}),
|
|
832
|
-
axes: [
|
|
833
|
-
makeAxis(true, 'x'),
|
|
834
|
-
makeYAxis(true, (u, vals) => vals.map((v) => fixedTo(v, 1)), 56, 'RSI'),
|
|
835
|
-
],
|
|
836
|
-
series: [
|
|
837
|
-
{ label: 'Time' },
|
|
838
|
-
lineSeries('OB', 'rgba(255,114,105,0.35)', 1),
|
|
839
|
-
lineSeries('OS', 'rgba(83,214,184,0.35)', 1),
|
|
840
|
-
lineSeries('Mid', 'rgba(255,255,255,0.18)', 1),
|
|
841
|
-
lineSeries('RSI(${rsiPeriod})', '#c4a5ff', 1.55),
|
|
842
|
-
],
|
|
843
|
-
}, [dates, dates.map(() => rsiOB), dates.map(() => rsiOS), dates.map(() => 50), rsiValues]);
|
|
844
|
-
} catch (err) {
|
|
845
|
-
console.error(err);
|
|
846
|
-
document.getElementById('charts').innerHTML = '<div style="padding:20px;color:#ef5350;white-space:pre-wrap;">' + String(err && err.message ? err.message : err) + '</div>';
|
|
847
|
-
throw err;
|
|
848
|
-
}
|
|
849
|
-
charts = [priceChart, derivChart, interpChart, macdChart, rsiChart];
|
|
850
|
-
${UPLOT_SHARED_SCRIPT}
|
|
851
|
-
${bindHoverStateFn()}
|
|
852
|
-
function resetZoom() {
|
|
853
|
-
syncXRange(xMin, xMax);
|
|
854
|
-
}
|
|
855
|
-
function sizeCharts() {
|
|
856
|
-
applyChartHeights();
|
|
857
|
-
for (const chart of charts) {
|
|
858
|
-
const rect = chart.root.parentElement.getBoundingClientRect();
|
|
859
|
-
chart.setSize({ width: Math.max(320, Math.floor(rect.width)), height: Math.max(100, Math.floor(rect.height)) });
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
window.addEventListener('resize', sizeCharts);
|
|
863
|
-
${zoomResetScript()}
|
|
864
|
-
charts.forEach((chart) => {
|
|
865
|
-
bindWheelZoom(chart);
|
|
866
|
-
bindPan(chart);
|
|
867
|
-
bindHoverState(chart);
|
|
868
|
-
});
|
|
869
|
-
const raf = window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : (fn) => setTimeout(fn, 0);
|
|
870
|
-
raf(() => sizeCharts());
|
|
871
|
-
</script>
|
|
872
|
-
</body>
|
|
873
|
-
</html>`;
|
|
874
|
-
}
|
|
875
|
-
async function main() {
|
|
876
|
-
const cfg = parseArgs();
|
|
877
|
-
const data = readJSON(cfg.inputFile);
|
|
878
|
-
const html = generateHTML(data, cfg.title);
|
|
879
|
-
ensureDir(path.dirname(cfg.outputFile));
|
|
880
|
-
fs.writeFileSync(cfg.outputFile, html, 'utf8');
|
|
881
|
-
if (!cfg.quiet)
|
|
882
|
-
console.log(`Chart written to ${cfg.outputFile}`);
|
|
883
|
-
}
|
|
884
|
-
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
885
|
-
main().catch((err) => {
|
|
886
|
-
console.error(err);
|
|
887
|
-
process.exit(1);
|
|
888
|
-
});
|
|
889
|
-
}
|
|
890
|
-
export { generateHTML, parseArgs, showHelp, trendToNum };
|
|
891
|
-
//# sourceMappingURL=derivative_chart_generator.js.map
|