alterzick commited on
Commit
20e5e07
·
verified ·
1 Parent(s): ea5157a

Add 3 files

Browse files
Files changed (3) hide show
  1. README.md +7 -5
  2. index.html +788 -19
  3. prompts.txt +0 -0
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Stock Record V2
3
- emoji: 🏃
4
- colorFrom: blue
5
- colorTo: yellow
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: stock-record-v2
3
+ emoji: ⚛️
4
+ colorFrom: red
5
+ colorTo: purple
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,788 @@
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="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Stock Analysis Record Application</title>
7
+ <!-- SheetJS CDN -->
8
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
9
+ <style>
10
+ :root {
11
+ --primary: #007bff;
12
+ --primary-dark: #0056b3;
13
+ --success: #28a745;
14
+ --danger: #dc3545;
15
+ --warning: #ffc107;
16
+ --info: #17a2b8;
17
+ --light: #f8f9fa;
18
+ --dark: #343a40;
19
+ --gray: #6c757d;
20
+ --border: #dee2e6;
21
+ --buy: #d4edda;
22
+ --sell: #f8d7da;
23
+ --hold: #fff3cd;
24
+ --watch: #d1ecf1;
25
+ --avoid: #f5c6cb;
26
+ --profit: #28a745;
27
+ --loss: #dc3545;
28
+ --profit-bg: #d4edda;
29
+ --loss-bg: #f8d7da;
30
+ --planning-bg: #d1ecf1;
31
+ --progress-bg: #fff3cd;
32
+ --closed-bg: #e2e3e5;
33
+ }
34
+ body {
35
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
36
+ margin: 0;
37
+ padding: 0;
38
+ background-color: #f8f9fa;
39
+ color: #212529;
40
+ line-height: 1.5;
41
+ }
42
+ h1 {
43
+ text-align: center;
44
+ padding: 20px;
45
+ margin: 0;
46
+ background-color: var(--primary);
47
+ color: white;
48
+ font-size: 1.5rem;
49
+ }
50
+ .container {
51
+ max-width: 1400px;
52
+ margin: 0 auto;
53
+ padding: 15px;
54
+ }
55
+ .tab {
56
+ display: flex;
57
+ overflow-x: auto;
58
+ background-color: white;
59
+ border-bottom: 1px solid var(--border);
60
+ margin-bottom: 20px;
61
+ -webkit-overflow-scrolling: touch;
62
+ }
63
+ .tab button {
64
+ flex: 1;
65
+ min-width: 150px;
66
+ background-color: transparent;
67
+ border: none;
68
+ outline: none;
69
+ cursor: pointer;
70
+ padding: 14px 16px;
71
+ font-size: 1rem;
72
+ transition: background-color 0.3s;
73
+ }
74
+ .tab button:hover {
75
+ background-color: #e9ecef;
76
+ }
77
+ .tab button.active {
78
+ background-color: var(--primary);
79
+ color: white;
80
+ }
81
+ .tabcontent {
82
+ display: none;
83
+ background-color: white;
84
+ padding: 20px;
85
+ border-radius: 8px;
86
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
87
+ }
88
+
89
+ /* ==== INPUT FORM COMPACT & RAPI ==== */
90
+ #stockForm {
91
+ display: grid;
92
+ grid-template-columns: repeat(2, 1fr);
93
+ gap: 16px;
94
+ align-items: start;
95
+ }
96
+ #stockForm .full-width {
97
+ grid-column: 1 / -1;
98
+ }
99
+ #stockForm .button-group {
100
+ display: flex;
101
+ gap: 12px;
102
+ flex-wrap: wrap;
103
+ margin-top: 10px;
104
+ }
105
+ label {
106
+ font-weight: 600;
107
+ margin-bottom: 6px;
108
+ display: block;
109
+ font-size: 0.95rem;
110
+ }
111
+ input[type="text"], input[type="date"], input[type="number"], select {
112
+ width: 100%;
113
+ padding: 10px;
114
+ border: 1px solid var(--border);
115
+ border-radius: 4px;
116
+ box-sizing: border-box;
117
+ font-size: 1rem;
118
+ }
119
+ textarea {
120
+ width: 100%;
121
+ padding: 10px;
122
+ border: 1px solid var(--border);
123
+ border-radius: 4px;
124
+ box-sizing: border-box;
125
+ font-size: 1rem;
126
+ resize: vertical;
127
+ min-height: 100px;
128
+ }
129
+ button[type="submit"], button[type="button"] {
130
+ background-color: var(--success);
131
+ color: white;
132
+ padding: 12px 20px;
133
+ border: none;
134
+ border-radius: 4px;
135
+ cursor: pointer;
136
+ font-size: 1rem;
137
+ }
138
+ button[type="submit"]:hover, button[type="button"]:hover { background-color: #218838; }
139
+ button.cancel, button.clear { background-color: var(--gray); }
140
+ button.cancel:hover, button.clear:hover { background-color: #5a6268; }
141
+ button.edit-btn { background-color: var(--primary); }
142
+ button.edit-btn:hover { background-color: var(--primary-dark); }
143
+ button.delete-btn { background-color: var(--danger); }
144
+ button.delete-btn:hover { background-color: #c82333; }
145
+ button.export-btn { background-color: var(--warning); color: var(--dark); }
146
+ button.export-btn:hover { background-color: #e0a800; }
147
+
148
+ @media (max-width: 768px) {
149
+ #stockForm { grid-template-columns: 1fr; }
150
+ }
151
+
152
+ /* ==== TABEL RAPI ==== */
153
+ table {
154
+ width: 100%;
155
+ border-collapse: separate;
156
+ border-spacing: 0;
157
+ margin-top: 10px;
158
+ font-size: 0.95rem;
159
+ }
160
+ th {
161
+ background-color: var(--primary);
162
+ color: white;
163
+ text-align: left;
164
+ padding: 12px 10px;
165
+ cursor: pointer;
166
+ user-select: none;
167
+ position: sticky;
168
+ top: 0;
169
+ z-index: 10;
170
+ }
171
+ th:hover { background-color: var(--primary-dark); }
172
+ th.sorted-asc::after { content: " ↑"; }
173
+ th.sorted-desc::after { content: " ↓"; }
174
+ td { padding: 10px 10px; border-bottom: 1px solid var(--border); vertical-align: top; }
175
+ tr:nth-child(even) { background-color: #f2f2f2; }
176
+
177
+ .truncated {
178
+ max-width: 200px;
179
+ white-space: nowrap;
180
+ overflow: hidden;
181
+ text-overflow: ellipsis;
182
+ display: inline-block;
183
+ cursor: pointer;
184
+ }
185
+ .truncated:hover {
186
+ overflow: visible;
187
+ white-space: normal;
188
+ background: #f8f9fa;
189
+ padding: 8px;
190
+ border-radius: 4px;
191
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
192
+ position: relative;
193
+ z-index: 100;
194
+ max-width: 400px;
195
+ word-wrap: break-word;
196
+ }
197
+ .badge {
198
+ padding: 4px 10px;
199
+ border-radius: 12px;
200
+ font-size: 0.85rem;
201
+ font-weight: bold;
202
+ color: white;
203
+ }
204
+ .badge-planning { background: #17a2b8; }
205
+ .badge-progress { background: #ffc107; color: #212529; }
206
+ .badge-closed { background: #6c757d; }
207
+ .pl-profit { background: var(--profit-bg); color: var(--success); font-weight: bold; padding: 4px 8px; border-radius: 4px; }
208
+ .pl-loss { background: var(--loss-bg); color: var(--danger); font-weight: bold; padding: 4px 8px; border-radius: 4px; }
209
+ .pl-zero { padding: 4px 8px; }
210
+ .decision-buy { background: var(--buy); font-weight: bold; padding: 4px 8px; border-radius: 4px; }
211
+ .decision-sell { background: var(--sell); font-weight: bold; padding: 4px 8px; border-radius: 4px; }
212
+ .decision-hold { background: var(--hold); font-weight: bold; padding: 4px 8px; border-radius: 4px; }
213
+ .decision-watch { background: var(--watch); font-weight: bold; padding: 4px 8px; border-radius: 4px; }
214
+ .decision-avoid { background: var(--avoid); font-weight: bold; padding: 4px 8px; border-radius: 4px; }
215
+
216
+ .actions { display: flex; gap: 8px; flex-wrap: wrap; }
217
+ .actions button { flex: 1; min-width: 70px; padding: 8px; font-size: 0.9rem; }
218
+
219
+ .import-export { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 20px; align-items: center; }
220
+ #filterForm, #historicalFilterForm {
221
+ display: grid;
222
+ grid-template-columns: 1fr 1fr 1fr auto auto;
223
+ gap: 10px;
224
+ align-items: end;
225
+ margin-bottom: 20px;
226
+ }
227
+ @media (max-width: 768px) {
228
+ #filterForm, #historicalFilterForm { grid-template-columns: 1fr; }
229
+ table, thead, tbody, th, td, tr { display: block; }
230
+ thead tr { position: absolute; top: -9999px; left: -9999px; }
231
+ tr { border: 1px solid var(--border); border-radius: 8px; margin-bottom: 15px; background: white; box-shadow: 0 2px 5px rgba(0,0,0,0.05); }
232
+ td { border: none; position: relative; padding-left: 50%; }
233
+ td:before { content: attr(data-label); position: absolute; left: 12px; width: 45%; font-weight: 600; white-space: nowrap; }
234
+ .truncated { max-width: none; display: block; }
235
+ }
236
+
237
+ .tenets-section {
238
+ margin-top: 30px;
239
+ padding: 20px;
240
+ background-color: #e9f7ef;
241
+ border-radius: 8px;
242
+ border-left: 5px solid var(--success);
243
+ }
244
+ .last-updated { text-align: right; font-size: 0.9rem; color: var(--gray); margin-bottom: 10px; }
245
+ </style>
246
+ </head>
247
+ <body>
248
+ <h1>Stock Analysis Record Application</h1>
249
+
250
+ <div class="container">
251
+ <div class="tab">
252
+ <button class="tablinks active" onclick="openTab(event, 'Input')">Input Record</button>
253
+ <button class="tablinks" onclick="openTab(event, 'View')">View & Manage (Active)</button>
254
+ <button class="tablinks" onclick="openTab(event, 'Historical')">Historical (Closed)</button>
255
+ </div>
256
+
257
+ <!-- ====================== INPUT RECORD (COMPACT) ====================== -->
258
+ <div id="Input" class="tabcontent" style="display: block;">
259
+ <form id="stockForm">
260
+ <input type="hidden" id="editIndex" value="-1">
261
+
262
+ <div>
263
+ <label for="symbol">Symbol</label>
264
+ <input type="text" list="symbolList" id="symbol" required placeholder="Ketik/pilih symbol" autofocus>
265
+ <datalist id="symbolList"></datalist>
266
+ </div>
267
+
268
+ <div>
269
+ <label for="date">Tanggal Analisis</label>
270
+ <input type="date" id="date" required>
271
+ </div>
272
+
273
+ <div>
274
+ <label for="status">Status</label>
275
+ <select id="status" required>
276
+ <option value="">-- Pilih Status --</option>
277
+ <option value="Planning">Planning</option>
278
+ <option value="On Progress">On Progress</option>
279
+ <option value="Closed">Closed</option>
280
+ </select>
281
+ </div>
282
+
283
+ <div>
284
+ <label for="decision">Decision</label>
285
+ <select id="decision" required>
286
+ <option value="">-- Pilih Decision --</option>
287
+ <option value="Buy">Buy</option>
288
+ <option value="Sell">Sell</option>
289
+ <option value="Hold">Hold</option>
290
+ <option value="Watch">Watch</option>
291
+ <option value="Avoid">Avoid</option>
292
+ </select>
293
+ </div>
294
+
295
+ <div>
296
+ <label for="entryPrice">Entry Price</label>
297
+ <input type="number" step="0.01" id="entryPrice" required>
298
+ </div>
299
+
300
+ <div>
301
+ <label for="exitPrice">Exit/Current Price</label>
302
+ <input type="number" step="0.01" id="exitPrice" placeholder="Wajib jika Closed">
303
+ </div>
304
+
305
+ <div>
306
+ <label for="support">Support</label>
307
+ <input type="number" step="0.01" id="support" required>
308
+ </div>
309
+
310
+ <div>
311
+ <label for="resistance">Resistance</label>
312
+ <input type="number" step="0.01" id="resistance" required>
313
+ </div>
314
+
315
+ <div>
316
+ <label for="mosPrice">MOS Price</label>
317
+ <input type="number" step="0.01" id="mosPrice" required>
318
+ </div>
319
+
320
+ <div>
321
+ <label for="trend">Trend Saat Ini</label>
322
+ <select id="trend" required>
323
+ <option value="">-- Pilih Trend --</option>
324
+ <option value="Major uptrend">Major uptrend</option>
325
+ <option value="Major downtrend">Major downtrend</option>
326
+ <option value="Medium uptrend">Medium uptrend</option>
327
+ <option value="Medium downtrend">Medium downtrend</option>
328
+ <option value="Minor uptrend">Minor uptrend</option>
329
+ <option value="Minor downtrend">Minor downtrend</option>
330
+ <option value="Sideways">Sideways</option>
331
+ </select>
332
+ </div>
333
+
334
+ <div>
335
+ <label for="broker">Broker Diikuti</label>
336
+ <input type="text" list="brokerList" id="broker" placeholder="Ketik/pilih">
337
+ <datalist id="brokerList"></datalist>
338
+ </div>
339
+
340
+ <div>
341
+ <label for="priceVolume">Price/Volume Pattern</label>
342
+ <select id="priceVolume">
343
+ <option value="">-- Pilih Pattern --</option>
344
+ <option value="Price up, Volume up">Naik + Volume naik</option>
345
+ <option value="Price up, Volume down">Naik + Volume turun</option>
346
+ <option value="Price down, Volume down">Turun + Volume turun</option>
347
+ <option value="Price down, Volume up">Turun + Volume naik</option>
348
+ </select>
349
+ </div>
350
+
351
+ <div class="full-width">
352
+ <label for="historicalData">Historical Data Comment</label>
353
+ <textarea id="historicalData" placeholder="Pola harga historis, volume, event penting, dll." required></textarea>
354
+ </div>
355
+
356
+ <div class="full-width">
357
+ <label for="analysis">Analysis Notes</label>
358
+ <textarea id="analysis" placeholder="Analisis lengkap: fundamental, technical, sentiment, risiko, dll." required></textarea>
359
+ </div>
360
+
361
+ <div class="full-width">
362
+ <label for="sentimentReason">News / Sentiment Reason</label>
363
+ <textarea id="sentimentReason" placeholder="Ringkas berita atau sentimen penting"></textarea>
364
+ </div>
365
+
366
+ <div class="full-width button-group">
367
+ <button type="submit" id="submitButton">Save Record</button>
368
+ <button type="button" class="cancel" id="cancelButton" style="display:none;" onclick="cancelEdit()">Cancel Edit</button>
369
+ <button type="button" class="clear" onclick="clearForm()">Clear Form</button>
370
+ </div>
371
+ </form>
372
+
373
+ <div class="tenets-section">
374
+ <h3>Tenets & Notes for Stock Screening and Trading</h3>
375
+ <p>Prinsip-prinsip utama trader profesional sebagai panduan analisis:</p>
376
+ <ul>
377
+ <li><strong>Follow the Trend:</strong> "The trend is your friend."</li>
378
+ <li><strong>Cut Losses Short, Let Profits Run</strong></li>
379
+ <li><strong>Risk Management:</strong> Maksimal 1-2% risiko per trade</li>
380
+ <li><strong>Never Add to Losing Position</strong></li>
381
+ <li><strong>Be Patient and Disciplined</strong></li>
382
+ <li><strong>Value Investing + Momentum</strong></li>
383
+ <li><strong>Diversification</strong></li>
384
+ </ul>
385
+ </div>
386
+ </div>
387
+
388
+ <!-- ====================== VIEW (ACTIVE) ====================== -->
389
+ <div id="View" class="tabcontent">
390
+ <div class="last-updated" id="lastUpdatedView"></div>
391
+
392
+ <form id="filterForm">
393
+ <div><label for="filterSymbol">Filter by Symbol</label><input type="text" id="filterSymbol" placeholder="e.g., BBCA"></div>
394
+ <div><label for="filterDecision">Filter by Decision</label>
395
+ <select id="filterDecision">
396
+ <option value="">-- All Decisions --</option>
397
+ <option value="Buy">Buy</option><option value="Sell">Sell</option><option value="Hold">Hold</option>
398
+ <option value="Watch">Watch</option><option value="Avoid">Avoid</option>
399
+ </select>
400
+ </div>
401
+ <div><label for="filterStatus">Filter by Status</label>
402
+ <select id="filterStatus">
403
+ <option value="">-- All Status (Non-Closed) --</option>
404
+ <option value="Planning">Planning</option><option value="On Progress">On Progress</option>
405
+ </select>
406
+ </div>
407
+ <div><button type="button" onclick="applyFilters('View')">Apply Filter</button></div>
408
+ <div><button type="button" onclick="clearFilters('View')">Clear Filter</button></div>
409
+ </form>
410
+
411
+ <div class="import-export">
412
+ <div><label for="importFile">Import XLSX</label><input type="file" id="importFile" accept=".xlsx"></div>
413
+ <button type="button" onclick="importExcel()">Import</button>
414
+ <button type="button" class="export-btn" onclick="exportToExcel()">Export to XLSX</button>
415
+ </div>
416
+
417
+ <table id="recordsTable">
418
+ <thead>
419
+ <tr>
420
+ <th onclick="sortTable(0,'View')">Symbol</th>
421
+ <th onclick="sortTable(1,'View')">Date</th>
422
+ <th>Status</th>
423
+ <th>Entry</th>
424
+ <th>Current</th>
425
+ <th>P/L (%)</th>
426
+ <th>Historical</th>
427
+ <th>Analysis</th>
428
+ <th>Support</th>
429
+ <th>Resistance</th>
430
+ <th>MOS</th>
431
+ <th onclick="sortTable(11,'View')">Decision</th>
432
+ <th>Trend</th>
433
+ <th>Broker</th>
434
+ <th>Sentiment</th>
435
+ <th>Pattern</th>
436
+ <th>Actions</th>
437
+ </tr>
438
+ </thead>
439
+ <tbody></tbody>
440
+ </table>
441
+ </div>
442
+
443
+ <!-- ====================== HISTORICAL (CLOSED) ====================== -->
444
+ <div id="Historical" class="tabcontent">
445
+ <div class="last-updated" id="lastUpdatedHistorical"></div>
446
+
447
+ <form id="historicalFilterForm">
448
+ <div><label for="historicalFilterSymbol">Filter by Symbol</label><input type="text" id="historicalFilterSymbol" placeholder="e.g., BBCA"></div>
449
+ <div><label for="historicalFilterDecision">Filter by Decision</label>
450
+ <select id="historicalFilterDecision">
451
+ <option value="">-- All Decisions --</option>
452
+ <option value="Buy">Buy</option><option value="Sell">Sell</option><option value="Hold">Hold</option>
453
+ <option value="Watch">Watch</option><option value="Avoid">Avoid</option>
454
+ </select>
455
+ </div>
456
+ <div><label for="historicalFilterStatus">Filter by Status</label>
457
+ <select id="historicalFilterStatus"><option value="Closed">-- Only Closed --</option></select>
458
+ </div>
459
+ <div><button type="button" onclick="applyFilters('Historical')">Apply Filter</button></div>
460
+ <div><button type="button" onclick="clearFilters('Historical')">Clear Filter</button></div>
461
+ </form>
462
+
463
+ <div class="import-export">
464
+ <div><label for="historicalImportFile">Import XLSX</label><input type="file" id="historicalImportFile" accept=".xlsx"></div>
465
+ <button type="button" onclick="importExcel('historical')">Import</button>
466
+ <button type="button" class="export-btn" onclick="exportToExcel('historical')">Export Historical</button>
467
+ </div>
468
+
469
+ <table id="historicalTable">
470
+ <thead>
471
+ <tr>
472
+ <th onclick="sortTable(0,'Historical')">Symbol</th>
473
+ <th onclick="sortTable(1,'Historical')">Date</th>
474
+ <th>Status</th>
475
+ <th>Entry</th>
476
+ <th>Exit</th>
477
+ <th>P/L (%)</th>
478
+ <th>Historical</th>
479
+ <th>Analysis</th>
480
+ <th>Support</th>
481
+ <th>Resistance</th>
482
+ <th>MOS</th>
483
+ <th onclick="sortTable(11,'Historical')">Decision</th>
484
+ <th>Trend</th>
485
+ <th>Broker</th>
486
+ <th>Sentiment</th>
487
+ <th>Pattern</th>
488
+ <th>Actions</th>
489
+ </tr>
490
+ </thead>
491
+ <tbody></tbody>
492
+ </table>
493
+ </div>
494
+ </div>
495
+
496
+ <script>
497
+ let records = JSON.parse(localStorage.getItem('stockRecords')) || [];
498
+ let viewSortColumn = 1, viewSortDirection = -1;
499
+ let historicalSortColumn = 1, historicalSortDirection = -1;
500
+
501
+ const form = document.getElementById('stockForm');
502
+ const tableBodyView = document.querySelector('#recordsTable tbody');
503
+ const tableBodyHistorical = document.querySelector('#historicalTable tbody');
504
+ const symbolDatalist = document.getElementById('symbolList');
505
+ const brokerDatalist = document.getElementById('brokerList');
506
+ const lastUpdatedView = document.getElementById('lastUpdatedView');
507
+ const lastUpdatedHistorical = document.getElementById('lastUpdatedHistorical');
508
+
509
+ function updateLastUpdated(tab) {
510
+ const filtered = getFilteredRecords(tab);
511
+ const el = tab === 'View' ? lastUpdatedView : lastUpdatedHistorical;
512
+ if (filtered.length > 0) {
513
+ const latest = filtered.reduce((a,b) => a.date > b.date ? a : b);
514
+ el.textContent = `Last updated: ${new Date(latest.date).toLocaleDateString('id-ID')}`;
515
+ } else el.textContent = '';
516
+ }
517
+
518
+ function updateSymbolDatalist() {
519
+ const unique = [...new Set(records.map(r => r.symbol.toUpperCase()))].sort();
520
+ symbolDatalist.innerHTML = unique.map(s => `<option value="${s}">`).join('');
521
+ }
522
+
523
+ function updateBrokerDatalist() {
524
+ const unique = [...new Set(records.map(r => (r.broker || '').trim()).filter(b => b))].sort();
525
+ brokerDatalist.innerHTML = unique.map(b => `<option value="${b}">`).join('');
526
+ }
527
+
528
+ function getDecisionClass(d) { return 'decision-' + d.toLowerCase(); }
529
+
530
+ function calculateProfitLoss(r) {
531
+ if (!r.entryPrice || !r.exitPrice) return {value: '-', class: ''};
532
+ const pl = ((r.exitPrice - r.entryPrice) / r.entryPrice * 100).toFixed(2) + '%';
533
+ const cls = parseFloat(pl) > 0 ? 'profit' : (parseFloat(pl) < 0 ? 'loss' : '');
534
+ return {value: pl, class: cls};
535
+ }
536
+
537
+ function displayRecords(tab, data) {
538
+ const tbody = tab === 'View' ? tableBodyView : tableBodyHistorical;
539
+ tbody.innerHTML = '';
540
+ if (data.length === 0) {
541
+ tbody.innerHTML = '<tr><td colspan="17" style="text-align:center;padding:30px;">No records found.</td></tr>';
542
+ return;
543
+ }
544
+ data.forEach(record => {
545
+ const pl = calculateProfitLoss(record);
546
+ const plClass = pl.class === 'profit' ? 'pl-profit' : (pl.class === 'loss' ? 'pl-loss' : 'pl-zero');
547
+ const statusBadge = record.status === 'Planning' ? 'badge-planning' :
548
+ record.status === 'On Progress' ? 'badge-progress' : 'badge-closed';
549
+
550
+ const tr = document.createElement('tr');
551
+ tr.innerHTML = `
552
+ <td data-label="Symbol"><strong>${record.symbol}</strong></td>
553
+ <td data-label="Date">${new Date(record.date).toLocaleDateString('id-ID')}</td>
554
+ <td data-label="Status"><span class="badge ${statusBadge}">${record.status || '-'}</span></td>
555
+ <td data-label="Entry">${record.entryPrice ? record.entryPrice.toFixed(2) : '-'}</td>
556
+ <td data-label="${tab==='Historical'?'Exit':'Current'}">${record.exitPrice ? record.exitPrice.toFixed(2) : '-'}</td>
557
+ <td data-label="P/L (%)" class="${plClass}">${pl.value}</td>
558
+ <td data-label="Historical" class="truncated">${record.historicalData || '-'}</td>
559
+ <td data-label="Analysis" class="truncated">${record.analysis || '-'}</td>
560
+ <td data-label="Support">${record.support}</td>
561
+ <td data-label="Resistance">${record.resistance}</td>
562
+ <td data-label="MOS">${record.mosPrice}</td>
563
+ <td data-label="Decision" class="${getDecisionClass(record.decision)}">${record.decision}</td>
564
+ <td data-label="Trend">${record.trend || '-'}</td>
565
+ <td data-label="Broker">${record.broker || '-'}</td>
566
+ <td data-label="Sentiment" class="truncated">${record.sentimentReason || '-'}</td>
567
+ <td data-label="Pattern">${record.priceVolume || '-'}</td>
568
+ <td data-label="Actions" class="actions">
569
+ <button class="edit-btn" onclick="editRecord(${records.indexOf(record)})">Edit</button>
570
+ <button class="delete-btn" onclick="deleteRecord(${records.indexOf(record)})">Delete</button>
571
+ </td>
572
+ `;
573
+ tbody.appendChild(tr);
574
+ });
575
+ }
576
+
577
+ function getFilteredRecords(tab) {
578
+ return records.filter(r => tab === 'View' ? r.status !== 'Closed' : r.status === 'Closed');
579
+ }
580
+
581
+ function sortTable(colIdx, tab) {
582
+ let colVar = tab === 'View' ? viewSortColumn : historicalSortColumn;
583
+ let dirVar = tab === 'View' ? viewSortDirection : historicalSortDirection;
584
+ if (colVar === colIdx) dirVar *= -1;
585
+ else { colVar = colIdx; dirVar = colIdx === 1 ? -1 : 1; }
586
+ if (tab === 'View') { viewSortColumn = colVar; viewSortDirection = dirVar; }
587
+ else { historicalSortColumn = colVar; historicalSortDirection = dirVar; }
588
+
589
+ const tableId = tab === 'View' ? '#recordsTable' : '#historicalTable';
590
+ document.querySelectorAll(`${tableId} th`).forEach(th => th.classList.remove('sorted-asc','sorted-desc'));
591
+ document.querySelectorAll(`${tableId} th`)[colIdx].classList.add(dirVar > 0 ? 'sorted-asc' : 'sorted-desc');
592
+
593
+ const sorted = [...getFilteredRecords(tab)].sort((a,b) => {
594
+ let A = getRecordValue(a, colIdx), B = getRecordValue(b, colIdx);
595
+ if (colIdx === 1) { A = new Date(A); B = new Date(B); }
596
+ else if ([3,4,8,9,10].includes(colIdx)) { A = parseFloat(A)||0; B = parseFloat(B)||0; }
597
+ else if (colIdx === 5) { A = parseFloat(calculateProfitLoss(a).value)||0; B = parseFloat(calculateProfitLoss(b).value)||0; }
598
+ return (A < B ? -1 : A > B ? 1 : 0) * dirVar;
599
+ });
600
+ displayRecords(tab, sorted);
601
+ }
602
+
603
+ function getRecordValue(r, i) {
604
+ return [r.symbol,r.date,r.status,r.entryPrice,r.exitPrice,
605
+ parseFloat(calculateProfitLoss(r).value)||0,
606
+ r.historicalData,r.analysis,r.support,r.resistance,r.mosPrice,
607
+ r.decision,r.trend,r.broker,r.sentimentReason,r.priceVolume][i];
608
+ }
609
+
610
+ function applyFilters(tab) {
611
+ const sym = document.getElementById(tab==='View'?'filterSymbol':'historicalFilterSymbol').value.toLowerCase().trim();
612
+ const dec = document.getElementById(tab==='View'?'filterDecision':'historicalFilterDecision').value;
613
+ const stat = document.getElementById(tab==='View'?'filterStatus':'historicalFilterStatus').value;
614
+
615
+ let filtered = getFilteredRecords(tab);
616
+ if (sym) filtered = filtered.filter(r => r.symbol.toLowerCase().includes(sym));
617
+ if (dec) filtered = filtered.filter(r => r.decision === dec);
618
+ if (stat) filtered = filtered.filter(r => r.status === stat);
619
+
620
+ displayRecords(tab, filtered);
621
+ }
622
+
623
+ function clearFilters(tab) {
624
+ document.getElementById(tab==='View'?'filterSymbol':'historicalFilterSymbol').value = '';
625
+ document.getElementById(tab==='View'?'filterDecision':'historicalFilterDecision').value = '';
626
+ document.getElementById(tab==='View'?'filterStatus':'historicalFilterStatus').value = tab==='View' ? '' : 'Closed';
627
+ sortTable(1, tab);
628
+ }
629
+
630
+ function openTab(evt, name) {
631
+ document.querySelectorAll('.tabcontent').forEach(t=>t.style.display='none');
632
+ document.querySelectorAll('.tablinks').forEach(t=>t.classList.remove('active'));
633
+ document.getElementById(name).style.display='block';
634
+ evt.currentTarget.classList.add('active');
635
+ if (name==='Input') document.getElementById('symbol').focus();
636
+ else if (name==='View') { updateLastUpdated('View'); sortTable(1,'View'); }
637
+ else if (name==='Historical') { updateLastUpdated('Historical'); sortTable(1,'Historical'); }
638
+ }
639
+
640
+ function editRecord(idx) {
641
+ const r = records[idx];
642
+ document.getElementById('symbol').value = r.symbol;
643
+ document.getElementById('date').value = r.date;
644
+ document.getElementById('status').value = r.status || '';
645
+ document.getElementById('entryPrice').value = r.entryPrice || '';
646
+ document.getElementById('exitPrice').value = r.exitPrice || '';
647
+ document.getElementById('historicalData').value = r.historicalData;
648
+ document.getElementById('analysis').value = r.analysis;
649
+ document.getElementById('support').value = r.support;
650
+ document.getElementById('resistance').value = r.resistance;
651
+ document.getElementById('mosPrice').value = r.mosPrice;
652
+ document.getElementById('decision').value = r.decision;
653
+ document.getElementById('trend').value = r.trend || '';
654
+ document.getElementById('broker').value = r.broker || '';
655
+ document.getElementById('sentimentReason').value = r.sentimentReason || '';
656
+ document.getElementById('priceVolume').value = r.priceVolume || '';
657
+
658
+ document.getElementById('editIndex').value = idx;
659
+ document.getElementById('submitButton').textContent = 'Update Record';
660
+ document.getElementById('cancelButton').style.display = 'inline-block';
661
+ openTab({currentTarget: document.querySelector('[onclick*="Input"]')}, 'Input');
662
+ }
663
+
664
+ function cancelEdit() {
665
+ clearForm();
666
+ document.getElementById('editIndex').value = '-1';
667
+ document.getElementById('submitButton').textContent = 'Save Record';
668
+ document.getElementById('cancelButton').style.display = 'none';
669
+ }
670
+
671
+ function clearForm() {
672
+ form.reset();
673
+ document.getElementById('decision').value = '';
674
+ document.getElementById('trend').value = '';
675
+ document.getElementById('status').value = '';
676
+ document.getElementById('priceVolume').value = '';
677
+ }
678
+
679
+ function deleteRecord(idx) {
680
+ if (confirm('Hapus record ini?')) {
681
+ records.splice(idx,1);
682
+ localStorage.setItem('stockRecords', JSON.stringify(records));
683
+ updateSymbolDatalist();
684
+ updateBrokerDatalist();
685
+ sortTable(1,'View');
686
+ sortTable(1,'Historical');
687
+ updateLastUpdated('View');
688
+ updateLastUpdated('Historical');
689
+ }
690
+ }
691
+
692
+ form.addEventListener('submit', e => {
693
+ e.preventDefault();
694
+ const editIdx = parseInt(document.getElementById('editIndex').value);
695
+ const symbol = document.getElementById('symbol').value.trim().toUpperCase();
696
+ const date = document.getElementById('date').value;
697
+ const status = document.getElementById('status').value;
698
+ const entryPrice = parseFloat(document.getElementById('entryPrice').value);
699
+ const exitPrice = parseFloat(document.getElementById('exitPrice').value) || null;
700
+ const support = parseFloat(document.getElementById('support').value);
701
+ const resistance = parseFloat(document.getElementById('resistance').value);
702
+
703
+ if (records.find((r,i) => r.symbol === symbol && r.date === date && i !== editIdx)) {
704
+ alert('Record dengan Symbol dan Tanggal yang sama sudah ada!');
705
+ return;
706
+ }
707
+ if (support >= resistance && !confirm('Support ≥ Resistance. Tetap simpan?')) return;
708
+ if (status === 'Closed' && !exitPrice) { alert('Exit Price wajib diisi jika Status Closed!'); return; }
709
+
710
+ const newRec = {
711
+ symbol, date, status, entryPrice, exitPrice,
712
+ historicalData: document.getElementById('historicalData').value.trim(),
713
+ analysis: document.getElementById('analysis').value.trim(),
714
+ support, resistance,
715
+ mosPrice: parseFloat(document.getElementById('mosPrice').value),
716
+ decision: document.getElementById('decision').value,
717
+ trend: document.getElementById('trend').value,
718
+ broker: document.getElementById('broker').value.trim(),
719
+ sentimentReason: document.getElementById('sentimentReason').value.trim(),
720
+ priceVolume: document.getElementById('priceVolume').value
721
+ };
722
+
723
+ if (editIdx === -1) records.push(newRec);
724
+ else records[editIdx] = newRec;
725
+
726
+ localStorage.setItem('stockRecords', JSON.stringify(records));
727
+ updateSymbolDatalist();
728
+ updateBrokerDatalist();
729
+ updateLastUpdated('View');
730
+ updateLastUpdated('Historical');
731
+ cancelEdit();
732
+ alert(editIdx === -1 ? 'Record berhasil disimpan!' : 'Record berhasil diperbarui!');
733
+ sortTable(1,'View');
734
+ sortTable(1,'Historical');
735
+ });
736
+
737
+ function exportToExcel(tab = null) {
738
+ const data = tab ? getFilteredRecords(tab) : records;
739
+ if (data.length === 0) { alert('Tidak ada data untuk diekspor.'); return; }
740
+ const ws = XLSX.utils.json_to_sheet(data);
741
+ const wb = XLSX.utils.book_new();
742
+ XLSX.utils.book_append_sheet(wb, ws, "StockRecords");
743
+ XLSX.writeFile(wb, tab === 'historical' ? "Historical_Stock_Records.xlsx" : "Stock_Analysis_Records.xlsx");
744
+ }
745
+
746
+ function importExcel() {
747
+ const file = document.getElementById('importFile').files[0];
748
+ if (!file) { alert('Pilih file XLSX terlebih dahulu.'); return; }
749
+ const reader = new FileReader();
750
+ reader.onload = e => {
751
+ try {
752
+ const wb = XLSX.read(e.target.result, {type:'binary'});
753
+ const sheet = wb.Sheets[wb.SheetNames[0]];
754
+ const imported = XLSX.utils.sheet_to_json(sheet);
755
+ let added = 0;
756
+ imported.forEach(row => {
757
+ if (row.symbol && row.date && row.decision && row.status && typeof row.entryPrice === 'number' &&
758
+ typeof row.support === 'number' && typeof row.resistance === 'number' && typeof row.mosPrice === 'number') {
759
+ row.symbol = row.symbol.toString().trim().toUpperCase();
760
+ row.broker = (row.broker || '').toString().trim();
761
+ row.exitPrice = row.exitPrice || null;
762
+ records.push(row);
763
+ added++;
764
+ }
765
+ });
766
+ if (added > 0) {
767
+ localStorage.setItem('stockRecords', JSON.stringify(records));
768
+ updateSymbolDatalist(); updateBrokerDatalist();
769
+ updateLastUpdated('View'); updateLastUpdated('Historical');
770
+ sortTable(1,'View'); sortTable(1,'Historical');
771
+ alert(`${added} record berhasil diimport.`);
772
+ } else alert('Tidak ada record valid.');
773
+ } catch (err) { alert('Error: ' + err.message); }
774
+ document.getElementById('importFile').value = '';
775
+ };
776
+ reader.readAsBinaryString(file);
777
+ }
778
+
779
+ // Init
780
+ updateSymbolDatalist();
781
+ updateBrokerDatalist();
782
+ sortTable(1,'View');
783
+ sortTable(1,'Historical');
784
+ updateLastUpdated('View');
785
+ updateLastUpdated('Historical');
786
+ </script>
787
+ <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/stock-record-v2" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
788
+ </html>
prompts.txt ADDED
File without changes