lonestar108 commited on
Commit
aa8bc9a
·
verified ·
1 Parent(s): e2d8d12

gimme a nie indicator panel that allows me to script a custom indicator in javascript

Browse files
Files changed (3) hide show
  1. components/custom-indicator.js +217 -0
  2. index.html +6 -0
  3. script.js +18 -0
components/custom-indicator.js ADDED
@@ -0,0 +1,217 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class CustomIndicator extends HTMLElement {
2
+ connectedCallback() {
3
+ this.attachShadow({ mode: 'open' });
4
+ this.shadowRoot.innerHTML = `
5
+ <style>
6
+ :host {
7
+ display: block;
8
+ background: #1e293b;
9
+ border-radius: 0.5rem;
10
+ padding: 1rem;
11
+ margin-top: 1rem;
12
+ border: 1px solid #334155;
13
+ }
14
+ .header {
15
+ display: flex;
16
+ justify-content: space-between;
17
+ align-items: center;
18
+ margin-bottom: 1rem;
19
+ padding-bottom: 0.5rem;
20
+ border-bottom: 1px solid #334155;
21
+ }
22
+ .title {
23
+ font-weight: bold;
24
+ font-size: 1.125rem;
25
+ }
26
+ .controls {
27
+ display: flex;
28
+ gap: 0.5rem;
29
+ }
30
+ .editor-container {
31
+ position: relative;
32
+ height: 300px;
33
+ margin-bottom: 1rem;
34
+ }
35
+ #editor {
36
+ width: 100%;
37
+ height: 100%;
38
+ border-radius: 0.375rem;
39
+ background: #0f172a;
40
+ color: #f8fafc;
41
+ font-family: 'Courier New', monospace;
42
+ font-size: 0.875rem;
43
+ padding: 0.5rem;
44
+ border: 1px solid #334155;
45
+ resize: none;
46
+ }
47
+ .btn {
48
+ padding: 0.5rem 1rem;
49
+ border-radius: 0.375rem;
50
+ font-weight: 500;
51
+ cursor: pointer;
52
+ border: none;
53
+ display: flex;
54
+ align-items: center;
55
+ gap: 0.5rem;
56
+ }
57
+ .btn-run {
58
+ background: #3b82f6;
59
+ color: white;
60
+ }
61
+ .btn-reset {
62
+ background: #334155;
63
+ color: white;
64
+ }
65
+ .output {
66
+ margin-top: 1rem;
67
+ padding: 0.75rem;
68
+ background: #0f172a;
69
+ border-radius: 0.375rem;
70
+ border: 1px solid #334155;
71
+ font-family: monospace;
72
+ font-size: 0.875rem;
73
+ white-space: pre-wrap;
74
+ min-height: 100px;
75
+ max-height: 200px;
76
+ overflow-y: auto;
77
+ }
78
+ .error {
79
+ color: #ef4444;
80
+ }
81
+ .success {
82
+ color: #10b981;
83
+ }
84
+ </style>
85
+ <div class="header">
86
+ <div class="title">Custom Indicator</div>
87
+ <div class="controls">
88
+ <button class="btn btn-reset" id="reset-btn">
89
+ <i data-feather="trash-2"></i> Reset
90
+ </button>
91
+ <button class="btn btn-run" id="run-btn">
92
+ <i data-feather="play"></i> Run
93
+ </button>
94
+ </div>
95
+ </div>
96
+ <div class="editor-container">
97
+ <textarea id="editor">// Custom Indicator Script
98
+ // Access price data with: data.close, data.open, data.high, data.low
99
+ // Must return an object with format: { value: number, color?: string, text?: string }
100
+
101
+ function calculateIndicator(data) {
102
+ // Example: Simple Moving Average (SMA)
103
+ if (data.length < 14) return null;
104
+
105
+ const closePrices = data.map(d => d.close);
106
+ const sum = closePrices.slice(-14).reduce((a, b) => a + b, 0);
107
+ const sma = sum / 14;
108
+
109
+ return {
110
+ value: sma,
111
+ color: sma > data[data.length-1].close ? '#ef4444' : '#10b981',
112
+ text: \`SMA(14): \${sma.toFixed(2)}\`
113
+ };
114
+ }
115
+
116
+ return calculateIndicator(data);</textarea>
117
+ </div>
118
+ <div class="output" id="output"></div>
119
+ `;
120
+
121
+ if (window.feather) {
122
+ window.feather.replace({ class: 'feather-inline' });
123
+ }
124
+
125
+ this.shadowRoot.getElementById('run-btn').addEventListener('click', () => this.runScript());
126
+ this.shadowRoot.getElementById('reset-btn').addEventListener('click', () => this.resetScript());
127
+
128
+ // Sample data for testing
129
+ this.sampleData = Array.from({ length: 30 }, (_, i) => ({
130
+ close: 25000 + Math.sin(i/3) * 1000 + Math.random() * 500,
131
+ open: 25000 + Math.sin(i/3) * 1000 + Math.random() * 500,
132
+ high: 25000 + Math.sin(i/3) * 1000 + Math.random() * 600,
133
+ low: 25000 + Math.sin(i/3) * 1000 - Math.random() * 400,
134
+ volume: 1000 + Math.random() * 5000
135
+ }));
136
+ }
137
+
138
+ runScript() {
139
+ const editor = this.shadowRoot.getElementById('editor');
140
+ const output = this.shadowRoot.getElementById('output');
141
+
142
+ try {
143
+ output.innerHTML = '';
144
+ output.classList.remove('error');
145
+
146
+ // Create a function from the editor content
147
+ const indicatorFn = new Function('data', `
148
+ ${editor.value}
149
+ `);
150
+
151
+ // Execute with sample data
152
+ const result = indicatorFn(this.sampleData);
153
+
154
+ if (result === null || result === undefined) {
155
+ output.innerHTML = 'Indicator returned no data (null)';
156
+ } else if (typeof result === 'object' && 'value' in result) {
157
+ output.classList.add('success');
158
+ output.innerHTML = `Indicator value: ${result.value}\n\n`;
159
+
160
+ if (result.text) {
161
+ output.innerHTML += `Display text: ${result.text}\n\n`;
162
+ }
163
+
164
+ if (result.color) {
165
+ output.innerHTML += `Color: <span style="color:${result.color}">${result.color}</span>`;
166
+ }
167
+
168
+ // Emit event with the indicator function
169
+ this.dispatchEvent(new CustomEvent('indicator-created', {
170
+ detail: {
171
+ fn: indicatorFn,
172
+ config: result
173
+ }
174
+ }));
175
+ } else {
176
+ throw new Error('Indicator must return an object with at least a "value" property');
177
+ }
178
+ } catch (err) {
179
+ output.classList.add('error');
180
+ output.innerHTML = err.message;
181
+ console.error(err);
182
+ }
183
+ }
184
+
185
+ resetScript() {
186
+ const editor = this.shadowRoot.getElementById('editor');
187
+ const output = this.shadowRoot.getElementById('output');
188
+
189
+ editor.value = `// Custom Indicator Script
190
+ // Access price data with: data.close, data.open, data.high, data.low
191
+ // Must return an object with format: { value: number, color?: string, text?: string }
192
+
193
+ function calculateIndicator(data) {
194
+ // Example: Simple Moving Average (SMA)
195
+ if (data.length < 14) return null;
196
+
197
+ const closePrices = data.map(d => d.close);
198
+ const sum = closePrices.slice(-14).reduce((a, b) => a + b, 0);
199
+ const sma = sum / 14;
200
+
201
+ return {
202
+ value: sma,
203
+ color: sma > data[data.length-1].close ? '#ef4444' : '#10b981',
204
+ text: \`SMA(14): \${sma.toFixed(2)}\`
205
+ };
206
+ }
207
+
208
+ return calculateIndicator(data);`;
209
+
210
+ output.innerHTML = '';
211
+ output.classList.remove('error', 'success');
212
+
213
+ this.dispatchEvent(new CustomEvent('indicator-removed'));
214
+ }
215
+ }
216
+
217
+ customElements.define('custom-indicator', CustomIndicator);
index.html CHANGED
@@ -187,6 +187,11 @@
187
  <market-scanner></market-scanner>
188
  </div>
189
 
 
 
 
 
 
190
  <!-- Open Positions -->
191
  <div class="mt-4 bg-gray-800 rounded-lg shadow-xl overflow-hidden">
192
  <div class="p-4 border-b border-gray-700">
@@ -216,6 +221,7 @@
216
  <script src="components/footer.js"></script>
217
  <script src="components/tradingbot-panel.js"></script>
218
  <script src="components/market-scanner.js"></script>
 
219
  <script src="script.js"></script>
220
  <script>
221
  feather.replace();
 
187
  <market-scanner></market-scanner>
188
  </div>
189
 
190
+ <!-- Custom Indicator -->
191
+ <div class="mt-4 bg-gray-800 rounded-lg shadow-xl overflow-hidden">
192
+ <custom-indicator></custom-indicator>
193
+ </div>
194
+
195
  <!-- Open Positions -->
196
  <div class="mt-4 bg-gray-800 rounded-lg shadow-xl overflow-hidden">
197
  <div class="p-4 border-b border-gray-700">
 
221
  <script src="components/footer.js"></script>
222
  <script src="components/tradingbot-panel.js"></script>
223
  <script src="components/market-scanner.js"></script>
224
+ <script src="components/custom-indicator.js"></script>
225
  <script src="script.js"></script>
226
  <script>
227
  feather.replace();
script.js CHANGED
@@ -153,6 +153,24 @@ function startLivePrices() {
153
  }
154
 
155
  document.addEventListener('DOMContentLoaded', startLivePrices);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
 
157
  // Utility functions
158
  function formatPrice(price) {
 
153
  }
154
 
155
  document.addEventListener('DOMContentLoaded', startLivePrices);
156
+ // Custom indicator event handling
157
+ document.addEventListener('indicator-created', (e) => {
158
+ const { fn, config } = e.detail;
159
+ console.log('Custom indicator created:', config);
160
+ // Here you would typically add the indicator to your chart
161
+ // For TradingView widget you would use:
162
+ // widget.chart().createStudy('Custom Study', false, false, [/* inputs */], (study) => {
163
+ // study.setCallback({
164
+ // onData: (data) => {
165
+ // const result = fn(data);
166
+ // if (result) {
167
+ // data.setValue(result.value);
168
+ // if (result.color) data.setLineColor(result.color);
169
+ // }
170
+ // }
171
+ // });
172
+ // });
173
+ });
174
 
175
  // Utility functions
176
  function formatPrice(price) {