Skip to main content

Arsitektur Teknis LAPAS (Laboratorium Pesugihan Analisa Saham)

Studi Kasus Teknis • Version v1.0 •

Asal-Usul: dari Obrolan Santai Soal AI ke Laboratorium Analisis Saham

LAPAS bermula dari pertanyaan sederhana: apakah AI bisa membantu analisis saham? Eksplorasi awal menimbang beberapa pendekatan — analisis teknikal berbasis machine learning, sentimen berita, hingga ekstraksi rasio fundamental otomatis — dan berhenti pada satu kesimpulan yang jadi tulang punggung seluruh proyek: AI di sini membantu membaca informasi lebih cepat, bukan meramal harga dengan pasti. Hasil AI adalah salah satu input, bukan keputusan final.

Tiga tahap ambisi dipetakan sejak awal: (1) alat analisis pribadi untuk satu-dua emiten tanpa antarmuka rapi, (2) coverage tool yang mencakup banyak emiten dengan dashboard konsisten, dan (3) equity research platform sungguhan dengan model valuasi dan distribusi lewat API. LAPAS berhenti di tahap kedua yang diperluas secara sadar — ambisinya adalah laboratorium pribadi, bukan platform publik yang memberi rekomendasi ke banyak orang dan bersinggungan dengan pengawasan OJK.


Stack

KomponenPilihan
FrameworkLaravel 12 (PHP 8.3), Livewire 4, Flux UI, Tailwind CSS
DatabaseMySQL 8
Sumber dataYahoo Finance (tidak resmi) — harga EOD, fundamental via cookie+crumb, dividen/split via parameter events pada endpoint chart yang sama
AI opsionalClaude, ChatGPT, atau Gemini — API key milik masing-masing pengguna
LingkunganWindows/Uniform Server lokal, rencana pindah ke shared hosting
Pengujian305 tes PHPUnit, RefreshDatabase + SQLite in-memory, Http::fake() untuk sumber data eksternal

Isolasi Data Multi-Pengguna: Global Scope, Bukan Policy Satu-per-Satu

Skema awal LAPAS murni tunggal-pengguna: registrasi publik dimatikan, dan tidak ada satu pun kolom user_id di tabel manapun. Mengubah ini tanpa kehilangan data nyata yang sudah ada adalah tantangan struktural terbesar dalam pengembangannya.

Solusinya sebuah trait Eloquent, MilikPengguna, dipasang di empat tabel data pribadi (jurnal, watchlist, simulasi, preset screener). Trait ini otomatis menyaring setiap kueri lewat global scope agar hanya menampilkan milik pengguna yang sedang login, dan otomatis mengisi kepemilikan saat data baru dibuat:

trait MilikPengguna
{
    protected static function bootMilikPengguna(): void
    {
        static::addGlobalScope('milikPengguna', function (Builder $query) {
            if (Auth::check()) {
                $query->where($query->getModel()->getTable().'.user_id', Auth::id());
            }
        });
        static::creating(function ($model) {
            if (! $model->user_id && Auth::check()) {
                $model->user_id = Auth::id();
            }
        });
    }
}

Manfaat Tak Terduga: IDOR Tertutup Otomatis

Karena route model binding Laravel ikut memakai scope Eloquent yang sama, celah keamanan IDOR (menebak ID data milik orang lain lewat URL) tertutup otomatis di seluruh aplikasi — tanpa menulis kebijakan otorisasi manual di tiap halaman satu per satu.


Cron yang “Menjadi” Setiap Pengguna

Untuk perintah terjadwal yang memproses banyak pengguna sekaligus (notifikasi, backtest mingguan), dipakai Auth::onceUsingId(): proses “menjadi” tiap pengguna secara bergantian dalam satu putaran, sehingga kode yang sama persis dipakai baik dari halaman web maupun command line tanpa duplikasi logika.


Notifikasi Dua Lapis: Infrastruktur Bersama, Preferensi Pribadi

Bot Telegram dan perangkat Fonnte (WhatsApp) secara alami dimiliki bersama — satu bot, satu nomor. Tapi tujuan pengiriman dan preferensi peristiwa harus jadi milik pribadi begitu ada lebih dari satu pengguna. Notifikasi dipisah menjadi dua lapisan: token bot/perangkat dikelola admin (infrastruktur bersama), sementara tujuan, sakelar, dan preferensi peristiwa dikelola tiap pengguna sendiri.

Satu masalah turunan muncul saat menyambungkan Telegram: karena satu bot dipakai bersama, mengambil “Chat ID” dari pesan /start paling terakhir jadi ambigu ketika dua orang menghubungi bot hampir bersamaan. Solusinya, tiap pengguna diberi kode verifikasi 6 digit yang harus dikirim ke bot, dan sistem hanya mencocokkan pesan yang berisi kode tersebut — bukan sekadar pesan yang paling baru.


Pagar Konten AI: Matriks Integrasi Tanpa Verdict

Permintaan agar AI menyimpulkan sebuah saham “layak/tidak layak” dibeli bertentangan langsung dengan prinsip dasar LAPAS: bukan pemberi sinyal beli/jual. Solusinya adalah “matriks integrasi” yang menyandingkan sinyal empat dimensi (teknikal, fundamental, likuiditas, sentimen berita), masing-masing berlabel positif/negatif/netral/campuran beserta alasannya — tanpa pernah menyimpulkan tindakan.

Yang membedakan pendekatan ini dari sekadar instruksi di dalam prompt: pagar dipasang di level kode, bukan hanya di level permintaan ke AI. Kolom sinyal hanya boleh diisi dari empat kata baku lewat sebuah sanitizer (matriksBersih()), sehingga walau model AI mencoba menjawab “beli sekarang”, jawaban itu otomatis diperhalus menjadi “netral” oleh kode — kata beli/jual secara struktural tidak mungkin lolos lewat kolom ini.


Backup Database: Kredensial yang Tidak Pernah Muncul di Argumen Proses

Backup penuh (mysqldump) berjalan otomatis tiap malam untuk data yang tidak bisa dibangun ulang, terutama jurnal keputusan. Dua detail keamanan dan keandalan yang sengaja diperhatikan:

  • Kredensial database dilewatkan lewat berkas --defaults-extra-file sementara, bukan argumen proses — supaya tidak terlihat di daftar proses sistem operasi (ps/Task Manager).
  • Nama berkas backup diberi akhiran acak (bin2hex(random_bytes(2))), bukan hanya presisi detik — ditemukan lewat pengujian bahwa dua backup yang dibuat dalam detik yang sama bisa saling menimpa tanpa akhiran ini.

Bug Nyata yang Ditemukan Saat Audit

Audit menyeluruh setelah retrofit multi-pengguna menemukan beberapa bug produksi nyata, bukan sekadar hipotetis:

  • Notifikasi dan AI harian mogok diam-diam: gerbang pemeriksaan (->when()) di penjadwal masih memanggil fungsi lama dengan argumen yang sudah tidak sesuai setelah model kepemilikan data berubah — satu menyebabkan crash, satu lagi diam-diam selalu bernilai salah sehingga AI harian tidak pernah berjalan otomatis.
  • PHP max_execution_time (30 detik) vs panggilan AI (bisa 90 detik lebih): menyebabkan Internal Server Error mentah di halaman Ringkasan AI — diperbaiki dengan menaikkan batas waktu secara eksplisit sebelum tiap panggilan ke API AI.
  • Migrasi MySQL gagal meski lolos di pengujian: kunci asing (foreign key) di MySQL ternyata butuh indeksnya sendiri sebelum indeks unik lama boleh dihapus — celah yang tidak terlihat di SQLite (basis data pengujian).

Pola yang konsisten: perubahan arsitektur besar berpotensi meninggalkan jejak tersembunyi di bagian kode yang tidak langsung disentuh, dan satu-satunya cara menemukannya secara andal adalah verifikasi nyata setelah setiap perubahan — bukan berasumsi bahwa perubahan lokal tidak berdampak luas.


LAPAS saat ini berjalan di server Windows pribadi (Uniform Server) dan belum pernah diuji penuh di lingkungan shared hosting sungguhan. Panduan pemasangan sudah disiapkan, termasuk catatan bahwa set_time_limit() — yang dipakai untuk mengatasi bug timeout AI di atas — kemungkinan dibatasi oleh sebagian penyedia shared hosting, sehingga perlu diverifikasi ulang begitu pemindahan benar-benar dilakukan.


Referensi Terkait

Pelayanan

Butuh website atau aplikasi?

Sashindo Project membangun company profile, aplikasi web, LMS, dan otomasi Google Apps Script sesuai kebutuhan bisnis Anda.

Lihat layanan