dipping-charts 0.1.0

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 (137) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +216 -0
  3. package/dist/__tests__/FullFeaturedChart.test.d.ts +2 -0
  4. package/dist/__tests__/FullFeaturedChart.test.d.ts.map +1 -0
  5. package/dist/__tests__/indicators-accuracy.test.d.ts +2 -0
  6. package/dist/__tests__/indicators-accuracy.test.d.ts.map +1 -0
  7. package/dist/__tests__/indicators.test.d.ts +2 -0
  8. package/dist/__tests__/indicators.test.d.ts.map +1 -0
  9. package/dist/__tests__/setup.d.ts +1 -0
  10. package/dist/__tests__/setup.d.ts.map +1 -0
  11. package/dist/__tests__/validateCandle.test.d.ts +2 -0
  12. package/dist/__tests__/validateCandle.test.d.ts.map +1 -0
  13. package/dist/chart/index.d.ts +2 -0
  14. package/dist/chart/index.js +5 -0
  15. package/dist/chart/index.js.map +1 -0
  16. package/dist/components/TradingChart.d.ts +24 -0
  17. package/dist/components/TradingChart.d.ts.map +1 -0
  18. package/dist/components/TradingChart.js +100 -0
  19. package/dist/components/TradingChart.js.map +1 -0
  20. package/dist/components/index.d.ts +3 -0
  21. package/dist/components/index.d.ts.map +1 -0
  22. package/dist/dipping-charts.css +1 -0
  23. package/dist/index.d.ts +5 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +28 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/indicators/atr.d.ts +15 -0
  28. package/dist/indicators/atr.d.ts.map +1 -0
  29. package/dist/indicators/atr.js +30 -0
  30. package/dist/indicators/atr.js.map +1 -0
  31. package/dist/indicators/bollingerBands.d.ts +11 -0
  32. package/dist/indicators/bollingerBands.d.ts.map +1 -0
  33. package/dist/indicators/bollingerBands.js +39 -0
  34. package/dist/indicators/bollingerBands.js.map +1 -0
  35. package/dist/indicators/currencyStrength.d.ts +43 -0
  36. package/dist/indicators/currencyStrength.d.ts.map +1 -0
  37. package/dist/indicators/currencyStrength.js +53 -0
  38. package/dist/indicators/currencyStrength.js.map +1 -0
  39. package/dist/indicators/ema.d.ts +11 -0
  40. package/dist/indicators/ema.d.ts.map +1 -0
  41. package/dist/indicators/ema.js +24 -0
  42. package/dist/indicators/ema.js.map +1 -0
  43. package/dist/indicators/index.d.ts +19 -0
  44. package/dist/indicators/index.d.ts.map +1 -0
  45. package/dist/indicators/index.js +23 -0
  46. package/dist/indicators/index.js.map +1 -0
  47. package/dist/indicators/macd.d.ts +11 -0
  48. package/dist/indicators/macd.d.ts.map +1 -0
  49. package/dist/indicators/macd.js +52 -0
  50. package/dist/indicators/macd.js.map +1 -0
  51. package/dist/indicators/rsi.d.ts +11 -0
  52. package/dist/indicators/rsi.d.ts.map +1 -0
  53. package/dist/indicators/rsi.js +29 -0
  54. package/dist/indicators/rsi.js.map +1 -0
  55. package/dist/indicators/sma.d.ts +13 -0
  56. package/dist/indicators/sma.d.ts.map +1 -0
  57. package/dist/indicators/sma.js +22 -0
  58. package/dist/indicators/sma.js.map +1 -0
  59. package/dist/indicators/stochastic.d.ts +15 -0
  60. package/dist/indicators/stochastic.d.ts.map +1 -0
  61. package/dist/indicators/stochastic.js +34 -0
  62. package/dist/indicators/stochastic.js.map +1 -0
  63. package/dist/indicators/types.d.ts +102 -0
  64. package/dist/indicators/types.d.ts.map +1 -0
  65. package/dist/indicators/vwap.d.ts +14 -0
  66. package/dist/indicators/vwap.d.ts.map +1 -0
  67. package/dist/indicators/vwap.js +17 -0
  68. package/dist/indicators/vwap.js.map +1 -0
  69. package/dist/indicators/williamsR.d.ts +17 -0
  70. package/dist/indicators/williamsR.d.ts.map +1 -0
  71. package/dist/indicators/williamsR.js +19 -0
  72. package/dist/indicators/williamsR.js.map +1 -0
  73. package/dist/react/FullFeaturedChart.d.ts +3 -0
  74. package/dist/react/FullFeaturedChart.d.ts.map +1 -0
  75. package/dist/react/FullFeaturedChart.js +640 -0
  76. package/dist/react/FullFeaturedChart.js.map +1 -0
  77. package/dist/react/components/IndicatorSettings.d.ts +20 -0
  78. package/dist/react/components/IndicatorSettings.d.ts.map +1 -0
  79. package/dist/react/components/IndicatorSettings.js +748 -0
  80. package/dist/react/components/IndicatorSettings.js.map +1 -0
  81. package/dist/react/hooks/useChart.d.ts +15 -0
  82. package/dist/react/hooks/useChart.d.ts.map +1 -0
  83. package/dist/react/hooks/useChart.js +155 -0
  84. package/dist/react/hooks/useChart.js.map +1 -0
  85. package/dist/react/hooks/useIndicators.d.ts +10 -0
  86. package/dist/react/hooks/useIndicators.d.ts.map +1 -0
  87. package/dist/react/hooks/useIndicators.js +264 -0
  88. package/dist/react/hooks/useIndicators.js.map +1 -0
  89. package/dist/react/hooks/useLineTools.d.ts +26 -0
  90. package/dist/react/hooks/useLineTools.d.ts.map +1 -0
  91. package/dist/react/hooks/useLineTools.js +189 -0
  92. package/dist/react/hooks/useLineTools.js.map +1 -0
  93. package/dist/react/hooks/useShiftSnap.d.ts +12 -0
  94. package/dist/react/hooks/useShiftSnap.d.ts.map +1 -0
  95. package/dist/react/hooks/useShiftSnap.js +54 -0
  96. package/dist/react/hooks/useShiftSnap.js.map +1 -0
  97. package/dist/react/index.d.ts +14 -0
  98. package/dist/react/index.d.ts.map +1 -0
  99. package/dist/react/index.js +18 -0
  100. package/dist/react/index.js.map +1 -0
  101. package/dist/react/loadLightweightCharts.d.ts +18 -0
  102. package/dist/react/loadLightweightCharts.d.ts.map +1 -0
  103. package/dist/react/loadLightweightCharts.js +32 -0
  104. package/dist/react/loadLightweightCharts.js.map +1 -0
  105. package/dist/react/locale.d.ts +79 -0
  106. package/dist/react/locale.d.ts.map +1 -0
  107. package/dist/react/locale.js +158 -0
  108. package/dist/react/locale.js.map +1 -0
  109. package/dist/react/types.d.ts +130 -0
  110. package/dist/react/types.d.ts.map +1 -0
  111. package/dist/types/index.d.ts +24 -0
  112. package/dist/types/index.d.ts.map +1 -0
  113. package/dist/utils/getToolId.d.ts +9 -0
  114. package/dist/utils/getToolId.d.ts.map +1 -0
  115. package/dist/utils/getToolId.js +12 -0
  116. package/dist/utils/getToolId.js.map +1 -0
  117. package/dist/utils/mockData.d.ts +10 -0
  118. package/dist/utils/mockData.d.ts.map +1 -0
  119. package/dist/utils/mockData.js +61 -0
  120. package/dist/utils/mockData.js.map +1 -0
  121. package/dist/utils/snapCrosshair.d.ts +25 -0
  122. package/dist/utils/snapCrosshair.d.ts.map +1 -0
  123. package/dist/utils/validateCandle.d.ts +30 -0
  124. package/dist/utils/validateCandle.d.ts.map +1 -0
  125. package/dist/utils/validateCandle.js +21 -0
  126. package/dist/utils/validateCandle.js.map +1 -0
  127. package/examples/css/base.css +209 -0
  128. package/examples/css/chart.css +282 -0
  129. package/examples/css/indicators.css +255 -0
  130. package/examples/index.html +163 -0
  131. package/examples/js/chart.js +370 -0
  132. package/examples/js/indicators.js +27 -0
  133. package/examples/js/main.js +6 -0
  134. package/examples/js/ui.js +1641 -0
  135. package/lib/lightweight-charts.standalone.production.js +7 -0
  136. package/package.json +106 -0
  137. package/src/react/FullFeaturedChart.css +1007 -0
@@ -0,0 +1,163 @@
1
+ <!DOCTYPE html>
2
+ <html lang="ko">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>TradingView Chart Demo</title>
7
+ <link rel="stylesheet" href="css/base.css">
8
+ <link rel="stylesheet" href="css/indicators.css">
9
+ <link rel="stylesheet" href="css/chart.css">
10
+ </head>
11
+ <body>
12
+ <div class="container">
13
+ <div class="header">
14
+ <div class="header-left">
15
+ <div class="timeframe-group">
16
+ <button class="btn-timeframe" data-timeframe="1m">1분</button>
17
+ <button class="btn-timeframe active" data-timeframe="5m">5분</button>
18
+ <button class="btn-timeframe" data-timeframe="1h">1시간</button>
19
+ <button class="btn-timeframe" data-timeframe="1d">일</button>
20
+ <button class="btn-timeframe" data-timeframe="1w">주</button>
21
+ <button class="btn-timeframe" data-timeframe="1M">월</button>
22
+ </div>
23
+ </div>
24
+
25
+ <div class="header-right">
26
+ <!-- 보조지표 드롭다운 -->
27
+ <div class="dropdown">
28
+ <button class="btn-text" id="indicatorBtn">
29
+ + 보조지표
30
+ </button>
31
+ <div class="dropdown-menu" id="indicatorMenu">
32
+ <div class="indicator-menu-layout">
33
+ <!-- 왼쪽: 지표 목록 -->
34
+ <div class="indicator-list-side">
35
+ <div class="indicator-category">상단 지표</div>
36
+ <div class="indicator-item" data-indicator="sma">
37
+ <span>이동평균선</span>
38
+ <div class="indicator-checkbox"></div>
39
+ </div>
40
+ <div class="indicator-item" data-indicator="ema">
41
+ <span>지수이동평균</span>
42
+ <div class="indicator-checkbox"></div>
43
+ </div>
44
+ <div class="indicator-item" data-indicator="bbands">
45
+ <span>볼린저 밴드</span>
46
+ <div class="indicator-checkbox"></div>
47
+ </div>
48
+ <div class="indicator-category">하단 지표</div>
49
+ <div class="indicator-item" data-indicator="rsi">
50
+ <span>RSI</span>
51
+ <div class="indicator-checkbox"></div>
52
+ </div>
53
+ <div class="indicator-item" data-indicator="macd">
54
+ <span>MACD</span>
55
+ <div class="indicator-checkbox"></div>
56
+ </div>
57
+ </div>
58
+
59
+ <!-- 오른쪽: 설정 영역 -->
60
+ <div class="indicator-settings-side" id="indicatorSettingsSide">
61
+ <div class="indicator-empty-state">
62
+ 지표를 선택하세요
63
+ </div>
64
+ </div>
65
+ </div>
66
+ </div>
67
+ </div>
68
+
69
+ <!-- 그리기 드롭다운 -->
70
+ <div class="dropdown">
71
+ <button class="btn-text" id="drawingBtn">
72
+ <span class="tossface">✏️</span> 그리기
73
+ </button>
74
+ <div class="dropdown-menu" id="drawingMenu">
75
+ <button class="dropdown-item" id="trendLineToolBtn">
76
+ <span class="item-icon tossface">📏</span>
77
+ <span>추세선</span>
78
+ </button>
79
+ <button class="dropdown-item" id="horizontalLineToolBtn">
80
+ <span class="item-icon tossface">➖</span>
81
+ <span>수평선</span>
82
+ </button>
83
+ <button class="dropdown-item" id="verticalLineToolBtn">
84
+ <span class="item-icon">|</span>
85
+ <span>수직선</span>
86
+ </button>
87
+ <button class="dropdown-item" id="rectangleToolBtn">
88
+ <span class="item-icon tossface">▭</span>
89
+ <span>사각형</span>
90
+ </button>
91
+ <button class="dropdown-item" id="fibRetracementToolBtn">
92
+ <span class="item-icon">Φ</span>
93
+ <span>피보나치</span>
94
+ </button>
95
+ <button class="dropdown-item" id="textToolBtn">
96
+ <span class="item-icon">T</span>
97
+ <span>텍스트</span>
98
+ </button>
99
+ </div>
100
+ </div>
101
+
102
+ <div class="separator"></div>
103
+
104
+ <button class="btn-delete" id="removeAllLineToolsBtn">전체 삭제</button>
105
+ </div>
106
+ </div>
107
+
108
+ <div id="chart"></div>
109
+
110
+ <!-- 텍스트 입력 모달 -->
111
+ <div class="modal-overlay" id="textModal">
112
+ <div class="modal-content">
113
+ <div class="modal-title">텍스트 입력</div>
114
+ <input type="text" class="modal-input" id="textInput" placeholder="텍스트를 입력하세요">
115
+ <div class="modal-buttons">
116
+ <button class="modal-btn modal-btn-cancel" id="textModalCancel">취소</button>
117
+ <button class="modal-btn modal-btn-confirm" id="textModalConfirm">확인</button>
118
+ </div>
119
+ </div>
120
+ </div>
121
+
122
+ <!-- Context Menu -->
123
+ <div id="contextMenu" class="context-menu">
124
+ <!-- 선 두께 조절 -->
125
+ <button class="context-menu-btn" data-action="decrease-width" data-tooltip="선 얇게">−</button>
126
+ <div class="width-display" id="widthDisplay">2</div>
127
+ <button class="context-menu-btn" data-action="increase-width" data-tooltip="선 두껍게">+</button>
128
+
129
+ <div class="context-menu-separator"></div>
130
+
131
+ <!-- 색상 선택기 -->
132
+ <div class="color-picker-dropdown">
133
+ <button class="color-current-btn" id="colorCurrentBtn" data-tooltip="색상 변경">
134
+ <div class="color-current-display" id="colorCurrentDisplay" style="background: #2962FF;"></div>
135
+ </button>
136
+ <div class="color-palette" id="colorPalette">
137
+ <button class="color-option" data-color="#000000" style="background: #000000;"></button>
138
+ <button class="color-option" data-color="#ef4444" style="background: #ef4444;"></button>
139
+ <button class="color-option" data-color="#2962FF" style="background: #2962FF;"></button>
140
+ <button class="color-option" data-color="#22c55e" style="background: #22c55e;"></button>
141
+ <button class="color-option" data-color="#eab308" style="background: #eab308;"></button>
142
+ <button class="color-option" data-color="#a855f7" style="background: #a855f7;"></button>
143
+ <button class="color-option" data-color="#f97316" style="background: #f97316;"></button>
144
+ </div>
145
+ </div>
146
+
147
+ <div class="context-menu-separator"></div>
148
+
149
+ <!-- 텍스트 수정 버튼 -->
150
+ <button class="context-menu-btn" data-action="edit-text" data-tooltip="텍스트 수정">T</button>
151
+
152
+ <div class="context-menu-separator"></div>
153
+
154
+ <!-- 삭제 버튼 -->
155
+ <button class="context-menu-btn danger" data-action="delete" data-tooltip="삭제"><span class="tossface">🗑</span></button>
156
+ </div>
157
+
158
+ </div>
159
+
160
+ <script src="../lib/lightweight-charts.standalone.production.js"></script>
161
+ <script type="module" src="js/main.js"></script>
162
+ </body>
163
+ </html>
@@ -0,0 +1,370 @@
1
+ const { createChart } = LightweightCharts;
2
+
3
+ // 시간봉에 따른 시간 간격 (초)
4
+ const TIME_INTERVALS = {
5
+ '1m': 60,
6
+ '5m': 300,
7
+ '1h': 3600,
8
+ '1d': 86400,
9
+ '1w': 604800,
10
+ '1M': 2592000,
11
+ '1y': 31536000,
12
+ };
13
+
14
+ // 가상 데이터 생성
15
+ function generateMockCandles(timeFrame, count = 100, basePrice = 150) {
16
+ const candles = [];
17
+ const interval = TIME_INTERVALS[timeFrame];
18
+ const now = Math.floor(Date.now() / 1000);
19
+ let currentPrice = basePrice;
20
+
21
+ for (let i = count - 1; i >= 0; i--) {
22
+ const time = now - (i * interval);
23
+ const priceChange = currentPrice * (Math.random() * 0.04 - 0.02);
24
+ currentPrice += priceChange;
25
+
26
+ const open = currentPrice;
27
+ const close = currentPrice + (Math.random() * 4 - 2);
28
+ const high = Math.max(open, close) + Math.random() * 2;
29
+ const low = Math.min(open, close) - Math.random() * 2;
30
+ const volume = Math.floor(Math.random() * 1000000) + 100000;
31
+
32
+ candles.push({
33
+ time,
34
+ open: parseFloat(open.toFixed(2)),
35
+ high: parseFloat(high.toFixed(2)),
36
+ low: parseFloat(low.toFixed(2)),
37
+ close: parseFloat(close.toFixed(2)),
38
+ volume,
39
+ });
40
+
41
+ currentPrice = close;
42
+ }
43
+
44
+ return candles;
45
+ }
46
+
47
+ // 차트 생성
48
+ export const chartContainer = document.getElementById('chart');
49
+ window.chartContainer = chartContainer;
50
+
51
+ export const chart = createChart(chartContainer, {
52
+ width: chartContainer.clientWidth,
53
+ height: 600,
54
+ layout: {
55
+ background: { color: '#ffffff' },
56
+ textColor: '#333',
57
+ },
58
+ grid: {
59
+ vertLines: { color: '#f0f0f0' },
60
+ horzLines: { color: '#f0f0f0' },
61
+ },
62
+ crosshair: {
63
+ mode: 0, // 0 = Normal (자연스러운 움직임), 1 = Magnet (snap)
64
+ },
65
+ rightPriceScale: {
66
+ borderColor: '#cccccc',
67
+ },
68
+ timeScale: {
69
+ borderColor: '#cccccc',
70
+ timeVisible: true,
71
+ secondsVisible: false,
72
+ },
73
+ });
74
+ window.chart = chart;
75
+
76
+ // 캔들스틱 시리즈
77
+ export const candleSeries = chart.addCandlestickSeries({
78
+ upColor: '#ef4444',
79
+ downColor: '#3b82f6',
80
+ borderUpColor: '#ef4444',
81
+ borderDownColor: '#3b82f6',
82
+ wickUpColor: '#ef4444',
83
+ wickDownColor: '#3b82f6',
84
+ });
85
+ window.candleSeries = candleSeries;
86
+
87
+ // 거래량 시리즈
88
+ export const volumeSeries = chart.addHistogramSeries({
89
+ color: '#26a69a',
90
+ priceFormat: { type: 'volume' },
91
+ priceScaleId: '',
92
+ });
93
+ window.volumeSeries = volumeSeries;
94
+
95
+ volumeSeries.priceScale().applyOptions({
96
+ scaleMargins: {
97
+ top: 0.8,
98
+ bottom: 0,
99
+ },
100
+ });
101
+
102
+ // Line Tools 관련 변수
103
+ export let activeLineTool = null;
104
+ window.activeLineTool = null;
105
+
106
+ // 지표 시리즈 관리
107
+ export let currentCandles = [];
108
+ window.currentCandles = [];
109
+
110
+ export let dynamicIndicatorSeries = []; // 동적으로 추가된 지표들
111
+ window.dynamicIndicatorSeries = [];
112
+
113
+ export const indicatorSeries = {
114
+ sma20: null,
115
+ ema12: null,
116
+ rsi14: null,
117
+ macdLine: null,
118
+ macdSignal: null,
119
+ macdHist: null,
120
+ bbUpper: null,
121
+ bbMiddle: null,
122
+ bbLower: null,
123
+ };
124
+ window.indicatorSeries = indicatorSeries;
125
+
126
+ export const indicatorStates = {
127
+ sma20: false,
128
+ ema12: false,
129
+ rsi14: false,
130
+ macd: false,
131
+ bbands: false,
132
+ };
133
+ window.indicatorStates = indicatorStates;
134
+
135
+ // 데이터 로드
136
+ export function loadData(timeFrame) {
137
+ const candles = generateMockCandles(timeFrame, 200);
138
+ currentCandles = candles;
139
+ window.currentCandles = candles;
140
+
141
+ const candleData = candles.map(c => ({
142
+ time: c.time,
143
+ open: c.open,
144
+ high: c.high,
145
+ low: c.low,
146
+ close: c.close,
147
+ }));
148
+
149
+ const volumeData = candles.map(c => ({
150
+ time: c.time,
151
+ value: c.volume,
152
+ color: c.close >= c.open ? '#ef444466' : '#3b82f666',
153
+ }));
154
+
155
+ candleSeries.setData(candleData);
156
+ volumeSeries.setData(volumeData);
157
+ chart.timeScale().fitContent();
158
+
159
+ // 활성화된 지표 다시 로드
160
+ if (indicatorStates.sma20) updateSMA20();
161
+ if (indicatorStates.ema12) updateEMA12();
162
+ if (indicatorStates.rsi14) updateRSI14();
163
+ if (indicatorStates.macd) updateMACD();
164
+ if (indicatorStates.bbands) updateBollingerBands();
165
+
166
+ }
167
+
168
+ // ===== Indicator Toggle Functions =====
169
+
170
+ function updateSMA20() {
171
+ const smaData = calculateSMA(currentCandles, { period: 20 });
172
+ if (indicatorSeries.sma20) {
173
+ indicatorSeries.sma20.setData(smaData);
174
+ }
175
+ }
176
+
177
+ export function toggleSMA20() {
178
+ indicatorStates.sma20 = !indicatorStates.sma20;
179
+ const btn = document.getElementById('sma20Btn');
180
+
181
+ if (indicatorStates.sma20) {
182
+ btn.classList.add('active');
183
+ if (!indicatorSeries.sma20) {
184
+ indicatorSeries.sma20 = chart.addLineSeries({
185
+ color: '#2962FF',
186
+ lineWidth: 2,
187
+ title: 'SMA(20)',
188
+ });
189
+ }
190
+ updateSMA20();
191
+ } else {
192
+ btn.classList.remove('active');
193
+ if (indicatorSeries.sma20) {
194
+ chart.removeSeries(indicatorSeries.sma20);
195
+ indicatorSeries.sma20 = null;
196
+ }
197
+ }
198
+ }
199
+
200
+ function updateEMA12() {
201
+ const emaData = calculateEMA(currentCandles, { period: 12 });
202
+ if (indicatorSeries.ema12) {
203
+ indicatorSeries.ema12.setData(emaData);
204
+ }
205
+ }
206
+
207
+ export function toggleEMA12() {
208
+ indicatorStates.ema12 = !indicatorStates.ema12;
209
+ const btn = document.getElementById('ema12Btn');
210
+
211
+ if (indicatorStates.ema12) {
212
+ btn.classList.add('active');
213
+ if (!indicatorSeries.ema12) {
214
+ indicatorSeries.ema12 = chart.addLineSeries({
215
+ color: '#E91E63',
216
+ lineWidth: 2,
217
+ title: 'EMA(12)',
218
+ });
219
+ }
220
+ updateEMA12();
221
+ } else {
222
+ btn.classList.remove('active');
223
+ if (indicatorSeries.ema12) {
224
+ chart.removeSeries(indicatorSeries.ema12);
225
+ indicatorSeries.ema12 = null;
226
+ }
227
+ }
228
+ }
229
+
230
+ function updateRSI14() {
231
+ const rsiData = calculateRSI(currentCandles, { period: 14 });
232
+ if (indicatorSeries.rsi14) {
233
+ indicatorSeries.rsi14.setData(rsiData);
234
+ }
235
+ }
236
+
237
+ export function toggleRSI14() {
238
+ indicatorStates.rsi14 = !indicatorStates.rsi14;
239
+ const btn = document.getElementById('rsi14Btn');
240
+
241
+ if (indicatorStates.rsi14) {
242
+ btn.classList.add('active');
243
+ if (!indicatorSeries.rsi14) {
244
+ indicatorSeries.rsi14 = chart.addLineSeries({
245
+ color: '#7E57C2',
246
+ lineWidth: 2,
247
+ title: 'RSI(14)',
248
+ priceScaleId: 'rsi',
249
+ });
250
+ indicatorSeries.rsi14.priceScale().applyOptions({
251
+ scaleMargins: {
252
+ top: 0.7,
253
+ bottom: 0,
254
+ },
255
+ });
256
+ }
257
+ updateRSI14();
258
+ } else {
259
+ btn.classList.remove('active');
260
+ if (indicatorSeries.rsi14) {
261
+ chart.removeSeries(indicatorSeries.rsi14);
262
+ indicatorSeries.rsi14 = null;
263
+ }
264
+ }
265
+ }
266
+
267
+ function updateMACD() {
268
+ const macdData = calculateMACD(currentCandles, { fastPeriod: 12, slowPeriod: 26, signalPeriod: 9 });
269
+ if (indicatorSeries.macdLine) {
270
+ indicatorSeries.macdLine.setData(macdData.macd);
271
+ indicatorSeries.macdSignal.setData(macdData.signal);
272
+ indicatorSeries.macdHist.setData(macdData.histogram);
273
+ }
274
+ }
275
+
276
+ export function toggleMACD() {
277
+ indicatorStates.macd = !indicatorStates.macd;
278
+ const btn = document.getElementById('macdBtn');
279
+
280
+ if (indicatorStates.macd) {
281
+ btn.classList.add('active');
282
+ if (!indicatorSeries.macdLine) {
283
+ indicatorSeries.macdHist = chart.addHistogramSeries({
284
+ color: '#26a69a',
285
+ priceFormat: { type: 'price', precision: 5, minMove: 0.00001 },
286
+ priceScaleId: 'macd',
287
+ });
288
+ indicatorSeries.macdLine = chart.addLineSeries({
289
+ color: '#2962FF',
290
+ lineWidth: 2,
291
+ title: 'MACD',
292
+ priceFormat: { type: 'price', precision: 5, minMove: 0.00001 },
293
+ priceScaleId: 'macd',
294
+ });
295
+ indicatorSeries.macdSignal = chart.addLineSeries({
296
+ color: '#FF6D00',
297
+ lineWidth: 2,
298
+ title: 'Signal',
299
+ priceFormat: { type: 'price', precision: 5, minMove: 0.00001 },
300
+ priceScaleId: 'macd',
301
+ });
302
+ indicatorSeries.macdLine.priceScale().applyOptions({
303
+ scaleMargins: {
304
+ top: 0.8,
305
+ bottom: 0,
306
+ },
307
+ });
308
+ }
309
+ updateMACD();
310
+ } else {
311
+ btn.classList.remove('active');
312
+ if (indicatorSeries.macdLine) {
313
+ chart.removeSeries(indicatorSeries.macdLine);
314
+ chart.removeSeries(indicatorSeries.macdSignal);
315
+ chart.removeSeries(indicatorSeries.macdHist);
316
+ indicatorSeries.macdLine = null;
317
+ indicatorSeries.macdSignal = null;
318
+ indicatorSeries.macdHist = null;
319
+ }
320
+ }
321
+ }
322
+
323
+ function updateBollingerBands() {
324
+ const bbData = calculateBollingerBands(currentCandles, { period: 20, stdDev: 2 });
325
+ if (indicatorSeries.bbUpper) {
326
+ indicatorSeries.bbUpper.setData(bbData.upper);
327
+ indicatorSeries.bbMiddle.setData(bbData.middle);
328
+ indicatorSeries.bbLower.setData(bbData.lower);
329
+ }
330
+ }
331
+
332
+ export function toggleBollingerBands() {
333
+ indicatorStates.bbands = !indicatorStates.bbands;
334
+ const btn = document.getElementById('bbandsBtn');
335
+
336
+ if (indicatorStates.bbands) {
337
+ btn.classList.add('active');
338
+ if (!indicatorSeries.bbUpper) {
339
+ indicatorSeries.bbUpper = chart.addLineSeries({
340
+ color: '#F23645',
341
+ lineWidth: 2,
342
+ title: 'BB Upper',
343
+ });
344
+ indicatorSeries.bbMiddle = chart.addLineSeries({
345
+ color: '#2962FF',
346
+ lineWidth: 2,
347
+ title: 'BB Middle',
348
+ });
349
+ indicatorSeries.bbLower = chart.addLineSeries({
350
+ color: '#089981',
351
+ lineWidth: 2,
352
+ title: 'BB Lower',
353
+ });
354
+ }
355
+ updateBollingerBands();
356
+ } else {
357
+ btn.classList.remove('active');
358
+ if (indicatorSeries.bbUpper) {
359
+ chart.removeSeries(indicatorSeries.bbUpper);
360
+ chart.removeSeries(indicatorSeries.bbMiddle);
361
+ chart.removeSeries(indicatorSeries.bbLower);
362
+ indicatorSeries.bbUpper = null;
363
+ indicatorSeries.bbMiddle = null;
364
+ indicatorSeries.bbLower = null;
365
+ }
366
+ }
367
+ }
368
+
369
+ // 초기 로드 (5분봉)
370
+ loadData('5m');
@@ -0,0 +1,27 @@
1
+ // Import indicators from built library
2
+ import {
3
+ calculateSMA,
4
+ calculateEMA,
5
+ calculateRSI,
6
+ calculateMACD,
7
+ calculateBollingerBands,
8
+ calculateCurrencyStrength
9
+ } from '../../dist/indicators/index.js';
10
+
11
+ // Export for use in other modules
12
+ export {
13
+ calculateSMA,
14
+ calculateEMA,
15
+ calculateRSI,
16
+ calculateMACD,
17
+ calculateBollingerBands,
18
+ calculateCurrencyStrength
19
+ };
20
+
21
+ // Make available globally for backward compatibility
22
+ window.calculateSMA = calculateSMA;
23
+ window.calculateEMA = calculateEMA;
24
+ window.calculateRSI = calculateRSI;
25
+ window.calculateMACD = calculateMACD;
26
+ window.calculateBollingerBands = calculateBollingerBands;
27
+ window.calculateCurrencyStrength = calculateCurrencyStrength;
@@ -0,0 +1,6 @@
1
+ // Import all modules
2
+ import './indicators.js';
3
+ import './chart.js';
4
+ import './ui.js';
5
+
6
+ console.log('TradingView Chart Demo loaded successfully');