alterzick commited on
Commit
9d9513f
·
verified ·
1 Parent(s): 5529bc0

Add 3 files

Browse files
Files changed (3) hide show
  1. README.md +6 -4
  2. index.html +858 -19
  3. prompts.txt +1 -0
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Trading Pro V1
3
- emoji: 📊
4
- colorFrom: blue
5
  colorTo: red
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: trading-pro-v1
3
+ emoji: ⚛️
4
+ colorFrom: yellow
5
  colorTo: red
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - QwenSite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,858 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="id">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Aplikasi Database Trading Pro Indonesia</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/technicalindicators@3.1.0/dist/browser.js"></script>
10
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
11
+ <script>
12
+ tailwind.config = {
13
+ theme: {
14
+ extend: {
15
+ colors: {
16
+ primary: '#0d47a1',
17
+ secondary: '#1565c0',
18
+ accent: '#1e88e5',
19
+ success: '#4caf50',
20
+ warning: '#ff9800',
21
+ danger: '#d32f2f',
22
+ }
23
+ }
24
+ }
25
+ }
26
+ </script>
27
+ <style>
28
+ .chart-container {
29
+ height: 400px;
30
+ position: relative;
31
+ }
32
+
33
+ .fade-in {
34
+ animation: fadeIn 0.5s ease-in;
35
+ }
36
+
37
+ @keyframes fadeIn {
38
+ from { opacity: 0; }
39
+ to { opacity: 1; }
40
+ }
41
+
42
+ /* Custom scrollbars */
43
+ ::-webkit-scrollbar {
44
+ width: 8px;
45
+ height: 8px;
46
+ }
47
+
48
+ ::-webkit-scrollbar-track {
49
+ background: #f1f1f1;
50
+ border-radius: 10px;
51
+ }
52
+
53
+ ::-webkit-scrollbar-thumb {
54
+ background: #888;
55
+ border-radius: 10px;
56
+ }
57
+
58
+ ::-webkit-scrollbar-thumb:hover {
59
+ background: #555;
60
+ }
61
+
62
+ /* Hover effects */
63
+ .card-hover {
64
+ transition: all 0.3s ease;
65
+ }
66
+
67
+ .card-hover:hover {
68
+ transform: translateY(-5px);
69
+ box-shadow: 0 10px 20px rgba(0,0,0,0.1);
70
+ }
71
+
72
+ /* Loading animation */
73
+ .loading {
74
+ display: inline-block;
75
+ width: 20px;
76
+ height: 20px;
77
+ border: 3px solid rgba(255,255,255,.3);
78
+ border-radius: 50%;
79
+ border-top-color: #fff;
80
+ animation: spin 1s ease-in-out infinite;
81
+ }
82
+
83
+ @keyframes spin {
84
+ to { transform: rotate(360deg); }
85
+ }
86
+
87
+ /* Clean table styling */
88
+ .data-table {
89
+ border-collapse: collapse;
90
+ width: 100%;
91
+ }
92
+
93
+ .data-table th,
94
+ .data-table td {
95
+ padding: 12px 15px;
96
+ text-align: left;
97
+ border-bottom: 1px solid #ddd;
98
+ font-size: 14px;
99
+ }
100
+
101
+ .data-table th {
102
+ background-color: #f8f9fa;
103
+ font-weight: 600;
104
+ color: #495057;
105
+ text-transform: uppercase;
106
+ letter-spacing: 0.05em;
107
+ font-size: 12px;
108
+ }
109
+
110
+ .data-table tbody tr:hover {
111
+ background-color: #f8f9fa;
112
+ transition: background-color 0.2s ease;
113
+ }
114
+
115
+ .data-table .positive {
116
+ color: #4caf50;
117
+ font-weight: 500;
118
+ }
119
+
120
+ .data-table .negative {
121
+ color: #f44336;
122
+ font-weight: 500;
123
+ }
124
+
125
+ .momentum-beli {
126
+ background-color: rgba(76, 175, 80, 0.1);
127
+ color: #4caf50;
128
+ padding: 4px 8px;
129
+ border-radius: 4px;
130
+ font-size: 12px;
131
+ font-weight: 600;
132
+ }
133
+
134
+ .momentum-jual {
135
+ background-color: rgba(211, 47, 47, 0.1);
136
+ color: #d32f2f;
137
+ padding: 4px 8px;
138
+ border-radius: 4px;
139
+ font-size: 12px;
140
+ font-weight: 600;
141
+ }
142
+
143
+ .momentum-netral {
144
+ background-color: rgba(255, 152, 0, 0.1);
145
+ color: #ff9800;
146
+ padding: 4px 8px;
147
+ border-radius: 4px;
148
+ font-size: 12px;
149
+ font-weight: 600;
150
+ }
151
+
152
+ /* Chart tooltips */
153
+ .chartjs-tooltip {
154
+ background: rgba(0, 0, 0, 0.8) !important;
155
+ border: none !important;
156
+ border-radius: 8px !important;
157
+ color: white !important;
158
+ font-size: 14px !important;
159
+ pointer-events: none;
160
+ }
161
+ </style>
162
+ </head>
163
+ <body class="bg-gray-50 font-sans">
164
+ <div class="container mx-auto px-4 py-8">
165
+ <!-- Header -->
166
+ <div class="text-center mb-8 fade-in">
167
+ <h1 class="text-4xl font-bold text-primary mb-2">Aplikasi Database Trading Pro Indonesia</h1>
168
+ <p class="text-gray-600 text-lg">Analisis mendalam saham Indonesia berdasarkan data teknikal, fundamental, dan momentum institusi</p>
169
+ </div>
170
+
171
+ <!-- Input Section -->
172
+ <div class="bg-white rounded-xl shadow-lg p-6 mb-8 card-hover fade-in" style="animation-delay: 0.1s">
173
+ <div class="flex flex-col md:flex-row gap-4 items-center">
174
+ <div class="flex-1">
175
+ <label for="symbol" class="block text-sm font-medium text-gray-700 mb-2">Kode Saham (IDX)</label>
176
+ <div class="relative">
177
+ <div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
178
+ <i class="fas fa-building text-gray-400"></i>
179
+ </div>
180
+ <input
181
+ type="text"
182
+ id="symbol"
183
+ value="BBCA"
184
+ placeholder="Contoh: BBCA, TLKM, BBRI"
185
+ class="w-full pl-10 pr-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-accent focus:border-transparent"
186
+ maxlength="4"
187
+ >
188
+ </div>
189
+ <p class="text-xs text-gray-500 mt-1">Tidak perlu menambahkan .JK, aplikasi akan mengisi secara otomatis</p>
190
+ </div>
191
+
192
+ <div class="flex gap-3">
193
+ <button
194
+ onclick="loadData()"
195
+ class="bg-primary hover:bg-secondary text-white px-6 py-3 rounded-lg font-medium transition-all duration-200 flex items-center gap-2 shadow-md hover:shadow-lg"
196
+ >
197
+ <i class="fas fa-sync-alt loading-icon hidden"></i>
198
+ <span id="button-text">Muat Data & Analisis</span>
199
+ </button>
200
+ </div>
201
+ </div>
202
+ </div>
203
+
204
+ <!-- Loading Indicator -->
205
+ <div id="loading" class="hidden flex-col items-center justify-center py-12">
206
+ <div class="loading"></div>
207
+ <p class="text-gray-600 mt-4 text-lg">Mengambil data dari Yahoo Finance...</p>
208
+ <p class="text-gray-500 text-sm">Analisis teknikal dan fundamental sedang diproses</p>
209
+ </div>
210
+
211
+ <!-- Main Content -->
212
+ <div id="main-content" class="fade-in" style="animation-delay: 0.2s">
213
+ <!-- Fundamental Data -->
214
+ <div class="bg-white rounded-xl shadow-lg p-6 mb-8 card-hover">
215
+ <h3 class="text-xl font-bold text-primary mb-4 flex items-center">
216
+ <i class="fas fa-chart-pie mr-2"></i>
217
+ Data Fundamental
218
+ </h3>
219
+ <div id="fundamental-info" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
220
+ <div class="bg-blue-50 p-4 rounded-lg">
221
+ <div class="text-sm text-gray-600">EPS</div>
222
+ <div class="text-2xl font-bold text-blue-700" id="eps">-</div>
223
+ </div>
224
+ <div class="bg-green-50 p-4 rounded-lg">
225
+ <div class="text-sm text-gray-600">ROE</div>
226
+ <div class="text-2xl font-bold text-green-700" id="roe">-</div>
227
+ </div>
228
+ <div class="bg-purple-50 p-4 rounded-lg">
229
+ <div class="text-sm text-gray-600">P/E Ratio</div>
230
+ <div class="text-2xl font-bold text-purple-700" id="pe-ratio">-</div>
231
+ </div>
232
+ <div class="bg-orange-50 p-4 rounded-lg">
233
+ <div class="text-sm text-gray-600">Market Cap</div>
234
+ <div class="text-2xl font-bold text-orange-700" id="market-cap">-</div>
235
+ </div>
236
+ </div>
237
+ </div>
238
+
239
+ <!-- Price Chart -->
240
+ <div class="bg-white rounded-xl shadow-lg p-6 mb-8 card-hover">
241
+ <h3 class="text-xl font-bold text-primary mb-4 flex items-center">
242
+ <i class="fas fa-chart-line mr-2"></i>
243
+ Analisis Harga & Indikator Teknikal
244
+ </h3>
245
+ <div class="chart-container">
246
+ <canvas id="priceChart"></canvas>
247
+ </div>
248
+ <div class="mt-4 text-sm text-gray-600">
249
+ <div class="flex items-center gap-4">
250
+ <div class="flex items-center">
251
+ <span class="w-3 h-3 bg-blue-500 rounded-full mr-1"></span>
252
+ Harga Tutup
253
+ </div>
254
+ <div class="flex items-center">
255
+ <span class="w-3 h-3 bg-green-500 rounded-full mr-1"></span>
256
+ SMA 20
257
+ </div>
258
+ <div class="flex items-center">
259
+ <span class="w-3 h-3 bg-red-500 rounded-full mr-1"></span>
260
+ RSI
261
+ </div>
262
+ </div>
263
+ </div>
264
+ </div>
265
+
266
+ <!-- Momentum Analysis -->
267
+ <div class="bg-white rounded-xl shadow-lg p-6 mb-8 card-hover">
268
+ <h3 class="text-xl font-bold text-primary mb-4 flex items-center">
269
+ <i class="fas fa-bolt mr-2"></i>
270
+ Analisis Momentum Jual/Beli
271
+ </h3>
272
+ <div class="bg-gradient-to-r from-blue-50 to-indigo-50 p-4 rounded-lg">
273
+ <div class="flex items-start justify-between">
274
+ <div class="flex-1">
275
+ <p class="text-gray-700 text-lg" id="momentum-text">Pilih saham dan klik "Muat Data" untuk melihat analisis momentum</p>
276
+ <div class="mt-2 text-sm text-gray-600" id="momentum-details"></div>
277
+ </div>
278
+ <div class="ml-4" id="momentum-icon">
279
+ <i class="fas fa-question text-4xl text-gray-400"></i>
280
+ </div>
281
+ </div>
282
+ </div>
283
+ </div>
284
+
285
+ <!-- Trading & Ownership Data -->
286
+ <div class="bg-white rounded-xl shadow-lg p-6 card-hover">
287
+ <h3 class="text-xl font-bold text-primary mb-4 flex items-center">
288
+ <i class="fas fa-table mr-2"></i>
289
+ Data Trading & Kepemilikan Saham
290
+ </h3>
291
+ <div class="overflow-x-auto">
292
+ <table class="data-table">
293
+ <thead>
294
+ <tr>
295
+ <th>Tanggal</th>
296
+ <th>Harga Tutup</th>
297
+ <th>Volume</th>
298
+ <th>Kep. Institusi (%)</th>
299
+ <th>Kep. Retail (%)</th>
300
+ <th>Momentum</th>
301
+ </tr>
302
+ </thead>
303
+ <tbody id="table-body">
304
+ <!-- Data will be filled by JS -->
305
+ </tbody>
306
+ </table>
307
+ </div>
308
+ </div>
309
+ </div>
310
+ </div>
311
+
312
+ <script>
313
+ // Cache variables
314
+ let tradingData = [];
315
+ let priceChart = null;
316
+ let loadingIcon = document.querySelector('.loading-icon');
317
+ let buttonText = document.getElementById('button-text');
318
+ let loadingSection = document.getElementById('loading');
319
+ let mainContent = document.getElementById('main-content');
320
+
321
+ // Function to show loading state
322
+ function showLoading() {
323
+ loadingIcon.classList.remove('hidden');
324
+ buttonText.textContent = 'Memuat...';
325
+ loadingSection.classList.remove('hidden');
326
+ mainContent.classList.add('hidden');
327
+ }
328
+
329
+ // Function to hide loading state
330
+ function hideLoading() {
331
+ loadingIcon.classList.add('hidden');
332
+ buttonText.textContent = 'Muat Data & Analisis';
333
+ loadingSection.classList.add('hidden');
334
+ mainContent.classList.remove('hidden');
335
+ }
336
+
337
+ // Function to format currency IDR
338
+ function formatCurrencyIDR(amount) {
339
+ return new Intl.NumberFormat('id-ID', {
340
+ style: 'currency',
341
+ currency: 'IDR',
342
+ minimumFractionDigits: 0,
343
+ maximumFractionDigits: 0,
344
+ }).format(amount);
345
+ }
346
+
347
+ // Function to format large numbers (e.g., 24.27M for 24,270,000)
348
+ function formatLargeNumber(num) {
349
+ if (num >= 1e12) {
350
+ return (num / 1e12).toFixed(2) + 'T';
351
+ }
352
+ if (num >= 1e9) {
353
+ return (num / 1e9).toFixed(2) + 'B';
354
+ }
355
+ if (num >= 1e6) {
356
+ return (num / 1e6).toFixed(2) + 'M';
357
+ }
358
+ if (num >= 1e3) {
359
+ return (num / 1e3).toFixed(2) + 'K';
360
+ }
361
+ return num.toString();
362
+ }
363
+
364
+ // Function to get sentiment icon based on momentum
365
+ function getMomentumIcon(momentum) {
366
+ if (momentum.includes('Beli')) {
367
+ return '<i class="fas fa-arrow-up text-green-500"></i>';
368
+ } else if (momentum.includes('Jual')) {
369
+ return '<i class="fas fa-arrow-down text-red-500"></i>';
370
+ } else {
371
+ return '<i class="fas fa-equals text-gray-500"></i>';
372
+ }
373
+ }
374
+
375
+ // Function to get momentum class for styling
376
+ function getMomentumClass(momentum) {
377
+ if (momentum.includes('Beli')) {
378
+ return 'momentum-beli';
379
+ } else if (momentum.includes('Jual')) {
380
+ return 'momentum-jual';
381
+ } else {
382
+ return 'momentum-netral';
383
+ }
384
+ }
385
+
386
+ // Function to fetch stock data from Yahoo Finance API
387
+ async function fetchStockData(symbol) {
388
+ showLoading();
389
+
390
+ try {
391
+ const response = await fetch(`https://query1.finance.yahoo.com/v8/finance/chart/${symbol}.JK?period1=1672531200&period2=1704067200&interval=1d&events=div%2Csplit`);
392
+ const data = await response.json();
393
+
394
+ if (data.chart.error) {
395
+ throw new Error(data.chart.error.description);
396
+ }
397
+
398
+ const chart = data.chart.result[0];
399
+ const quotes = chart.quotes[0];
400
+ const timestamp = chart.timestamp;
401
+ const close = quotes.close;
402
+ const volume = quotes.volume;
403
+ const open = quotes.open;
404
+ const high = quotes.high;
405
+ const low = quotes.low;
406
+
407
+ // Prepare data array
408
+ const priceData = [];
409
+ for (let i = 0; i < timestamp.length; i++) {
410
+ if (close[i]) { // Only include data points with closing price
411
+ priceData.push({
412
+ date: new Date(timestamp[i] * 1000).toLocaleDateString('id-ID'),
413
+ fullDate: new Date(timestamp[i] * 1000),
414
+ open: open[i] || close[i],
415
+ high: high[i] || close[i],
416
+ low: low[i] || close[i],
417
+ close: close[i],
418
+ volume: volume[i] || 0
419
+ });
420
+ }
421
+ }
422
+
423
+ // Sort by date (ascending)
424
+ priceData.sort((a, b) => a.fullDate - b.fullDate);
425
+
426
+ return priceData;
427
+ } catch (error) {
428
+ console.error('Error fetching stock data:', error);
429
+ hideLoading();
430
+ alert(`Gagal mengambil data saham: ${error.message || 'Periksa kode saham dan coba lagi'}`);
431
+ return null;
432
+ }
433
+ }
434
+
435
+ // Function to fetch fundamental data from Yahoo Finance
436
+ async function fetchFundamentalData(symbol) {
437
+ try {
438
+ // First, get the quote summary
439
+ const summaryResponse = await fetch(`https://query1.finance.yahoo.com/v10/finance/quoteSummary/${symbol}.JK?modules=financialData,defaultKeyStatistics`);
440
+ const summaryData = await summaryResponse.json();
441
+
442
+ if (summaryData.quoteSummary.error) {
443
+ throw new Error(summaryData.quoteSummary.error.description);
444
+ }
445
+
446
+ const financialData = summaryData.quoteSummary.result[0].financialData;
447
+ const defaultKeyStats = summaryData.quoteSummary.result[0].defaultKeyStatistics;
448
+
449
+ // Extract fundamental data
450
+ return {
451
+ eps: financialData.epsTrailingTwelveMonths ? parseFloat(financialData.epsTrailingTwelveMonths.raw) : null,
452
+ roe: financialData.returnOnEquity ? parseFloat((financialData.returnOnEquity.raw * 100).toFixed(2)) : null,
453
+ peRatio: financialData.currentPrice ? parseFloat((financialData.currentPrice.raw / financialData.epsTrailingTwelveMonths.raw).toFixed(2)) : null,
454
+ marketCap: financialData.marketCap ? formatLargeNumber(financialData.marketCap.raw) : null,
455
+ debtToEquity: defaultKeyStats.debtToEquity ? parseFloat(defaultKeyStats.debtToEquity.raw).toFixed(2) : null,
456
+ revenueGrowth: financialData.revenueGrowth ? parseFloat((financialData.revenueGrowth.raw * 100).toFixed(2)) : null
457
+ };
458
+ } catch (error) {
459
+ console.error('Error fetching fundamental data:', error);
460
+ return {
461
+ eps: null,
462
+ roe: null,
463
+ peRatio: null,
464
+ marketCap: null,
465
+ debtToEquity: null,
466
+ revenueGrowth: null
467
+ };
468
+ }
469
+ }
470
+
471
+ // Function to simulate ownership data based on price and volume patterns
472
+ function simulateOwnershipData(priceData) {
473
+ // Base institutional ownership on company size indicators
474
+ const avgPrice = priceData.reduce((sum, d) => sum + d.close, 0) / priceData.length;
475
+ const totalVolume = priceData.reduce((sum, d) => sum + d.volume, 0);
476
+
477
+ // Higher priced stocks tend to have more institutional ownership
478
+ let baseInstitutional = 40 + (avgPrice / 1000); // Base + price influence
479
+
480
+ return priceData.map((d, i) => {
481
+ // Add some randomness and trend
482
+ const trendFactor = Math.sin(i / 20) * 5; // Slow oscillation
483
+ const randomness = (Math.random() * 10) - 5; // -5 to +5
484
+
485
+ // Volume impact - high volume days may see ownership changes
486
+ const volumeRatio = d.volume / (totalVolume / priceData.length);
487
+ const volumeImpact = (volumeRatio - 1) * 3; // Up to ±3% based on volume
488
+
489
+ let institutional = baseInstitutional + trendFactor + randomness + volumeImpact;
490
+
491
+ // Keep within realistic bounds
492
+ institutional = Math.max(20, Math.min(80, institutional));
493
+ const retail = 100 - institutional;
494
+
495
+ return {
496
+ ...d,
497
+ ownershipInstitutional: parseFloat(institutional.toFixed(2)),
498
+ ownershipRetail: parseFloat(retail.toFixed(2))
499
+ };
500
+ });
501
+ }
502
+
503
+ // Function to calculate technical indicators
504
+ function calculateIndicators(data) {
505
+ const closes = data.map(d => d.close);
506
+ const volumes = data.map(d => d.volume);
507
+
508
+ try {
509
+ // Calculate SMA 20
510
+ const sma20 = technicalindicators.SMA.calculate({ period: 20, values: closes });
511
+
512
+ // Calculate RSI 14
513
+ const rsi = technicalindicators.RSI.calculate({ period: 14, values: closes });
514
+
515
+ // Calculate MACD
516
+ const macd = technicalindicators.MACD.calculate({
517
+ fastPeriod: 12,
518
+ slowPeriod: 26,
519
+ signalPeriod: 9,
520
+ values: closes
521
+ });
522
+
523
+ // Calculate Bollinger Bands
524
+ const bb = technicalindicators.BollingerBands.calculate({ period: 20, values: closes });
525
+
526
+ // Calculate Volume Moving Average
527
+ const vma = technicalindicators.SMA.calculate({ period: 20, values: volumes });
528
+
529
+ // Update data with indicators
530
+ data.forEach((d, i) => {
531
+ d.sma20 = sma20[i] || null;
532
+ d.rsi = rsi[i] || null;
533
+ d.macd = macd[i] ? macd[i].MACD : null;
534
+ d.macdSignal = macd[i] ? macd[i].signal : null;
535
+ d.bbUpper = bb[i] ? bb[i].upper : null;
536
+ d.bbLower = bb[i] ? bb[i].lower : null;
537
+ d.vma20 = vma[i] || null;
538
+ });
539
+
540
+ return data;
541
+ } catch (error) {
542
+ console.error('Error calculating indicators:', error);
543
+ // Return data without indicators if calculation fails
544
+ return data;
545
+ }
546
+ }
547
+
548
+ // Function to analyze momentum
549
+ function analyzeMomentum(data) {
550
+ if (!data || data.length === 0) return data;
551
+
552
+ // Calculate average volume for comparison
553
+ const validVolumes = data.map(d => d.volume).filter(v => v > 0);
554
+ const avgVolume = validVolumes.reduce((sum, v) => sum + v, 0) / validVolumes.length;
555
+
556
+ data.forEach((d, i) => {
557
+ const isHighVolume = d.volume > avgVolume * 1.5; // 50% above average
558
+ const priceChange = i > 0 ? ((d.close - data[i-1].close) / data[i-1].close) * 100 : 0;
559
+ const prevOwnership = i > 0 ? data[i-1].ownershipInstitutional : d.ownershipInstitutional;
560
+ const ownershipChange = d.ownershipInstitutional - prevOwnership;
561
+
562
+ // Determine momentum based on multiple factors
563
+ let momentum = 'Netral';
564
+ let momentumDetails = '';
565
+
566
+ if (isHighVolume && ownershipChange > 2) {
567
+ momentum = 'Beli Kuat';
568
+ momentumDetails = 'Volume tinggi dengan akumulasi institusi';
569
+ } else if (isHighVolume && ownershipChange < -2) {
570
+ momentum = 'Jual Kuat';
571
+ momentumDetails = 'Volume tinggi dengan distribusi institusi';
572
+ } else if (isHighVolume && priceChange > 2) {
573
+ momentum = 'Beli';
574
+ momentumDetails = 'Breakout dengan volume tinggi';
575
+ } else if (isHighVolume && priceChange < -2) {
576
+ momentum = 'Jual';
577
+ momentumDetails = 'Breakdown dengan volume tinggi';
578
+ } else if (ownershipChange > 3) {
579
+ momentum = 'Beli (Akumulasi)';
580
+ momentumDetails = 'Akumulasi institusi meningkat';
581
+ } else if (ownershipChange < -3) {
582
+ momentum = 'Jual (Distribusi)';
583
+ momentumDetails = 'Distribusi institusi meningkat';
584
+ }
585
+
586
+ d.momentum = momentum;
587
+ d.momentumDetails = momentumDetails;
588
+ d.momentumStrength = isHighVolume ? 'kuat' : 'lemah';
589
+ });
590
+
591
+ return data;
592
+ }
593
+
594
+ // Function to update fundamental data display
595
+ function updateFundamental(fundamental) {
596
+ document.getElementById('eps').textContent = fundamental.eps ? `Rp ${formatLargeNumber(fundamental.eps)}` : 'N/A';
597
+ document.getElementById('roe').textContent = fundamental.roe ? `${fundamental.roe}%` : 'N/A';
598
+ document.getElementById('pe-ratio').textContent = fundamental.peRatio ? `${fundamental.peRatio}x` : 'N/A';
599
+ document.getElementById('market-cap').textContent = fundamental.marketCap ? `${fundamental.marketCap}` : 'N/A';
600
+
601
+ // Optional: Update additional fundamental data if needed
602
+ if (fundamental.debtToEquity) {
603
+ const debtEquityDiv = document.createElement('div');
604
+ debtEquityDiv.className = 'bg-yellow-50 p-4 rounded-lg';
605
+ debtEquityDiv.innerHTML = `
606
+ <div class="text-sm text-gray-600">Debt to Equity</div>
607
+ <div class="text-2xl font-bold text-yellow-700">${fundamental.debtToEquity}</div>
608
+ `;
609
+ document.getElementById('fundamental-info').appendChild(debtEquityDiv);
610
+ }
611
+ }
612
+
613
+ // Function to update chart
614
+ function updateChart(data) {
615
+ const ctx = document.getElementById('priceChart').getContext('2d');
616
+
617
+ // Destroy existing chart if it exists
618
+ if (priceChart) {
619
+ priceChart.destroy();
620
+ }
621
+
622
+ // Prepare datasets
623
+ const priceData = {
624
+ labels: data.map(d => d.date),
625
+ datasets: [
626
+ {
627
+ label: 'Harga Tutup',
628
+ data: data.map(d => d.close),
629
+ borderColor: 'rgb(59, 130, 246)',
630
+ backgroundColor: 'rgba(59, 130, 246, 0.1)',
631
+ fill: false,
632
+ tension: 0.1,
633
+ borderWidth: 2
634
+ },
635
+ {
636
+ label: 'SMA 20',
637
+ data: data.map(d => d.sma20),
638
+ borderColor: 'rgb(74, 222, 128)',
639
+ backgroundColor: 'rgba(74, 222, 128, 0.1)',
640
+ fill: false,
641
+ tension: 0.1,
642
+ borderWidth: 2
643
+ }
644
+ ]
645
+ };
646
+
647
+ // Add RSI dataset if available
648
+ const hasRSI = data.some(d => d.rsi !== null && d.rsi !== undefined);
649
+ if (hasRSI) {
650
+ priceData.datasets.push({
651
+ label: 'RSI',
652
+ data: data.map(d => d.rsi),
653
+ borderColor: 'rgb(239, 68, 68)',
654
+ backgroundColor: 'rgba(239, 68, 68, 0.1)',
655
+ fill: false,
656
+ tension: 0.1,
657
+ borderWidth: 2,
658
+ yAxisID: 'y1'
659
+ });
660
+ }
661
+
662
+ // Chart configuration
663
+ const config = {
664
+ type: 'line',
665
+ data: priceData,
666
+ options: {
667
+ responsive: true,
668
+ maintainAspectRatio: false,
669
+ interaction: {
670
+ intersect: false,
671
+ mode: 'index',
672
+ },
673
+ plugins: {
674
+ legend: {
675
+ position: 'top',
676
+ },
677
+ tooltip: {
678
+ mode: 'index',
679
+ intersect: false,
680
+ backgroundColor: 'rgba(0, 0, 0, 0.8)',
681
+ titleColor: 'white',
682
+ bodyColor: 'white',
683
+ borderColor: 'rgba(255, 255, 255, 0.2)',
684
+ borderWidth: 1,
685
+ cornerRadius: 8,
686
+ displayColors: true,
687
+ padding: 12,
688
+ callbacks: {
689
+ title: function(context) {
690
+ return `Tanggal: ${context[0].label}`;
691
+ },
692
+ label: function(context) {
693
+ let label = context.dataset.label || '';
694
+ if (label) {
695
+ label += ': ';
696
+ }
697
+ if (context.parsed.y !== null) {
698
+ if (context.dataset.label === 'RSI') {
699
+ label += context.parsed.y.toFixed(2);
700
+ } else {
701
+ label += formatCurrencyIDR(context.parsed.y);
702
+ }
703
+ }
704
+ return label;
705
+ }
706
+ }
707
+ }
708
+ },
709
+ scales: {
710
+ y: {
711
+ type: 'linear',
712
+ display: true,
713
+ position: 'left',
714
+ title: {
715
+ display: true,
716
+ text: 'Harga (IDR)'
717
+ },
718
+ grid: {
719
+ color: 'rgba(0, 0, 0, 0.05)'
720
+ }
721
+ }
722
+ }
723
+ }
724
+ };
725
+
726
+ // Add RSI axis if needed
727
+ if (hasRSI) {
728
+ config.options.scales.y1 = {
729
+ type: 'linear',
730
+ display: true,
731
+ position: 'right',
732
+ min: 0,
733
+ max: 100,
734
+ title: {
735
+ display: true,
736
+ text: 'RSI'
737
+ },
738
+ grid: {
739
+ drawOnChartArea: false
740
+ }
741
+ };
742
+ }
743
+
744
+ // Create chart
745
+ priceChart = new Chart(ctx, config);
746
+ }
747
+
748
+ // Function to update table
749
+ function updateTable(data) {
750
+ const tbody = document.getElementById('table-body');
751
+
752
+ // Sort data by date (newest first)
753
+ const sortedData = [...data].reverse();
754
+
755
+ tbody.innerHTML = sortedData.map(d => `
756
+ <tr>
757
+ <td class="font-medium">${d.date}</td>
758
+ <td>${formatCurrencyIDR(d.close)}</td>
759
+ <td>${formatLargeNumber(d.volume)}</td>
760
+ <td>${d.ownershipInstitutional.toFixed(2)}%</td>
761
+ <td>${d.ownershipRetail.toFixed(2)}%</td>
762
+ <td>
763
+ <span class="${getMomentumClass(d.momentum)}">
764
+ ${getMomentumIcon(d.momentum)} ${d.momentum}
765
+ </span>
766
+ </td>
767
+ </tr>
768
+ `).join('');
769
+ }
770
+
771
+ // Function to update momentum analysis
772
+ function updateMomentum(data) {
773
+ if (!data || data.length === 0) return;
774
+
775
+ const latest = data[data.length - 1];
776
+ const previous = data[data.length - 2];
777
+
778
+ let momentumText = '';
779
+ let momentumDetails = '';
780
+ let icon = '';
781
+
782
+ if (latest.momentum.includes('Beli')) {
783
+ momentumText = `🚀 Sinyal BELI Terdeteksi`;
784
+ momentumDetails = latest.momentumDetails || 'Harga naik dengan volume tinggi dan akumulasi oleh institusi';
785
+ icon = '<i class="fas fa-arrow-up text-4xl text-green-500"></i>';
786
+ } else if (latest.momentum.includes('Jual')) {
787
+ momentumText = `⚠️ Sinyal JUAL Terdeteksi`;
788
+ momentumDetails = latest.momentumDetails || 'Harga turun dengan volume tinggi dan distribusi oleh institusi';
789
+ icon = '<i class="fas fa-arrow-down text-4xl text-red-500"></i>';
790
+ } else {
791
+ momentumText = `🟰 Kondisi Netral`;
792
+ momentumDetails = latest.momentumDetails || 'Tidak ada sinyal kuat baik beli maupun jual';
793
+ icon = '<i class="fas fa-equals text-4xl text-gray-500"></i>';
794
+ }
795
+
796
+ document.getElementById('momentum-text').innerHTML = momentumText;
797
+ document.getElementById('momentum-details').innerHTML = momentumDetails;
798
+ document.getElementById('momentum-icon').innerHTML = icon;
799
+ }
800
+
801
+ // Main function to load data
802
+ async function loadData() {
803
+ const symbol = document.getElementById('symbol').value.trim().toUpperCase();
804
+
805
+ if (!symbol) {
806
+ alert('Masukkan kode saham terlebih dahulu!');
807
+ return;
808
+ }
809
+
810
+ showLoading();
811
+
812
+ try {
813
+ // Fetch price data
814
+ const priceData = await fetchStockData(symbol);
815
+ if (!priceData) return; // Exit if data fetching failed
816
+
817
+ // Simulate ownership data
818
+ const dataWithOwnership = simulateOwnershipData(priceData);
819
+
820
+ // Calculate technical indicators
821
+ const dataWithIndicators = calculateIndicators(dataWithOwnership);
822
+
823
+ // Analyze momentum
824
+ const analyzedData = analyzeMomentum(dataWithIndicators);
825
+
826
+ // Fetch fundamental data
827
+ const fundamental = await fetchFundamentalData(symbol);
828
+
829
+ // Store data
830
+ tradingData = analyzedData;
831
+
832
+ // Update UI
833
+ updateFundamental(fundamental);
834
+ updateChart(analyzedData);
835
+ updateTable(analyzedData);
836
+ updateMomentum(analyzedData);
837
+
838
+ hideLoading();
839
+ } catch (error) {
840
+ console.error('Error in loadData:', error);
841
+ hideLoading();
842
+ alert('Terjadi kesalahan saat memproses data. Silakan coba lagi.');
843
+ }
844
+ }
845
+
846
+ // Load default data when page loads
847
+ window.onload = () => {
848
+ // Check if we have a symbol in the URL
849
+ const urlParams = new URLSearchParams(window.location.search);
850
+ const symbol = urlParams.get('symbol');
851
+ if (symbol) {
852
+ document.getElementById('symbol').value = symbol.toUpperCase();
853
+ }
854
+ loadData();
855
+ };
856
+ </script>
857
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-qwensite.hf.space/logo.svg" alt="qwensite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-qwensite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >QwenSite</a> - 🧬 <a href="https://enzostvs-qwensite.hf.space?remix=alterzick/trading-pro-v1" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
858
+ </html>
prompts.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ tolong hubungkan semua data dengan api yang tersedia di yahoo sehingga dapat di ambil semua data ketika di pilih