# DSE Marginable Securities Category Scraper — Guide

Guide for **MarginableSecuritiesCategoryController** (DSE marginable securities scrape + database storage).

**Source URL:** `https://dsebd.org/marginable-securities.php`  
**Database Table:** `marginable_securities_category`  
**Stack:** Laravel `^11.31`, PHP `^8.2`

---

## 1. What this feature does

1. Scrapes HTML from DSE marginable securities page (`https://dsebd.org/marginable-securities.php`).
2. Parses HTML table rows (`serial`, `trading_code`, `company_name`, `category`) and header (`as_of`).
3. Stores records directly into the **`marginable_securities_category`** database table with `activity_flag = 'A'`.
4. Operates **without a hot cache layer** (`Cache::get`/`Cache::put`).
5. Checks if today's data (`fetched_at` in Asia/Dhaka) is already updated in the database:
   - **If updated today:** Returns active records directly from database.
   - **If not yet updated today:** Performs fresh scrape, soft-retires old records (`activity_flag = 'I'`), inserts new records (`activity_flag = 'A'`), and returns the stored data.

---

## 2. API Endpoint

| Property | Value |
|---|---|
| **Method** | `GET` |
| **URL** | `/api/paper-trading/marginable-securities-category` |
| **Authentication** | Open / No Auth |
| **Route Name** | `paper-trading-marginable-securities-category` |

### Sample Curl Request

```bash
curl --location --request GET 'http://localhost:8006/api/paper-trading/marginable-securities-category'
```

---

## 3. Database Schema: `marginable_securities_category`

| Column | Type | Nullable | Default | Description |
|---|---|---|---|---|
| `id` | `BIGINT UNSIGNED` PK AI | NO | — | Primary Key |
| `serial` | `INT UNSIGNED` | YES | `NULL` | DSE row serial number |
| `trading_code` | `VARCHAR(64)` | NO | — | DSE security trade code (e.g. `ACIFORMULA`) |
| `company_name` | `VARCHAR(255)` | NO | — | Company full name |
| `category` | `VARCHAR(12)` | NO | — | Category (e.g. `A`, `B`) |
| `as_of` | `VARCHAR(64)` | YES | `NULL` | Date text from page header |
| `source_url` | `VARCHAR(255)` | YES | `NULL` | Scrape source URL |
| `fetched_at` | `DATETIME` | NO | — | Scrape timestamp in Asia/Dhaka |
| `activity_flag` | `CHAR(1)` | NO | `'A'` | `'A'` = Active, `'I'` = Inactive |
| `created_at` | `TIMESTAMP` | YES | `NULL` | Migration timestamp |
| `updated_at` | `TIMESTAMP` | YES | `NULL` | Migration timestamp |
