CORE TERMINAL
올드보이 & 마리아 백업

바이비트 API 실시간 플렛폼 수집 데몬 Ver.2.3

DATE: 2026-03-03 10:26
분류 정보
핵심 항목
테마2.0
내용
* 바이비트 API 실시간 플렛폼 수집 데몬 Ver.2.3

1. DB '인사' 노가다 (Connection Overhead)
* 기어다니는 이유: `while(true)` 루프 안에서 매번 `get_gnu_connection()`이나 `new PDO(...)`를 하고 있다면?
* 기술적 팩트: DB에 접속해서 "나 왔어!" 하고 악수(Handshake)하는 데만 0.05~0.1초가 걸립니다. 이걸 7마리 사수가 매초 반복하면 서버 부하가 기하급수적으로 늘어나요.
* 해결: 접속은 루프 밖에서 딱 한 번만 하고, 안에서는 쿼리만 쏴야 합니다.

2. '줄 서서 기다리는' API (Sequential cURL)
기어다니는 이유: 1번 주소(Ticker) 부르고 결과 올 때까지 대기 $\rightarrow$ 2번 주소(Orderbook) 부르고 대기... 이런 식으로 **순차적(Sequential)**으로 일하면?
기술적 팩트: 하나라도 서버가 늦게 응답하면 전체 루프가 거기서 멈춥니다.
해결: curl_multi_init() 같은 비동기(Parallel) 방식을 써서 여러 주소를 동시에 찔러야 합니다.




$pdo_gnu를 루프 밖에서 한 번만 연결, 루프 안에서는 끊겼을 때만 재연결.
php// 루프 밖
$pdo_gnu = get_gnu_connection();

// 루프 안 종목 갱신 시
if (time() - $last_market_refresh >= 1 || empty($symbols)) {
    // 끊겼으면 재연결만
    if (!$pdo_gnu) {
        $pdo_gnu = get_gnu_connection();
    } else {
        try { $pdo_gnu->query(\\\\\\\"SELECT 1\\\\\\\"); }
        catch (Throwable $e) { $pdo_gnu = get_gnu_connection(); }
    }
    // 이후 쿼리 실행
}
$pdo도 동일하게 루프 밖에서 이미 연결하고 있는데, $pdo_gnu만 빠져 있던 구조
추가 내용
#!/usr/bin/php
<?php
/**
 * ============================================================
 * 바이비트 무기한 선물(linear) 종목 정보 수집 CLI 데몬
 * - curl_multi 병렬 호출 방식 (전체 종목 동시 호출)
 * - 그누보드(g5_write_daemon_kind_bybit) x2_run=1 종목만 수집
 * - 저장 테이블: daemon_bybit_Ticker
 * ============================================================
 */

error_reporting(E_ALL);
ini_set(\\\\\\\'display_errors\\\\\\\', 1);
date_default_timezone_set(\\\\\\\'Asia/Seoul\\\\\\\');

$DAEMON_ID = pathinfo(__FILE__, PATHINFO_FILENAME);

if (php_sapi_name() !== \\\\\\\'cli\\\\\\\') {
    echo \\\\\\\"CLI 전용 데몬입니다.\\\\\\\\n\\\\\\\";
    exit;
}

function get_db_connection() {
    try {
        $db_upbit = null;
        @include \\\\\\\'/home/www/DB/db_upbit.php\\\\\\\';
        if (!($db_upbit instanceof PDO)) return null;
        $pdo = $db_upbit;
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        return $pdo;
    } catch (Throwable $e) {
        return null;
    }
}

function get_gnu_connection() {
    try {
        $db_gnu  = null;
        $pdo_gnu = null;
        $pdo     = null;
        @include \\\\\\\'/home/www/DB/db_gnu.php\\\\\\\';
        if ($db_gnu instanceof PDO) {
            $db_gnu->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            return $db_gnu;
        }
        if ($pdo_gnu instanceof PDO) {
            $pdo_gnu->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            return $pdo_gnu;
        }
        if ($pdo instanceof PDO) {
            $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            return $pdo;
        }
        return null;
    } catch (Throwable $e) {
        return null;
    }
}

// ============================================================
// curl_multi 병렬 호출 - URL 배열 한번에 날리고 결과 반환
// ============================================================
function http_multi_get(array $urls): array {
    $mh      = curl_multi_init();
    $handles = [];

    foreach ($urls as $key => $url) {
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT        => 8,
            CURLOPT_USERAGENT      => \\\\\\\'bybit-ghost\\\\\\\',
            CURLOPT_SSL_VERIFYPEER => false,
        ]);
        curl_multi_add_handle($mh, $ch);
        $handles[$key] = $ch;
    }

    $running = null;
    do {
        curl_multi_exec($mh, $running);
        curl_multi_select($mh);
    } while ($running > 0);

    $results = [];
    foreach ($handles as $key => $ch) {
        $raw = curl_multi_getcontent($ch);
        $results[$key] = $raw ? json_decode($raw, true) : null;
        curl_multi_remove_handle($mh, $ch);
        curl_close($ch);
    }
    curl_multi_close($mh);

    return $results;
}

// ============================================================
// 전체 종목 한번에 병렬 호출 후 데이터 파싱
// ============================================================
function collectAll(array $symbols): array {
    $API_INSTRUMENTS   = \\\\\\\'https://api.bybit.com/v5/market/instruments-info?category=linear&symbol=\\\\\\\';
    $API_TICKERS       = \\\\\\\'https://api.bybit.com/v5/market/tickers?category=linear&symbol=\\\\\\\';
    $API_KLINE         = \\\\\\\'https://api.bybit.com/v5/market/kline?category=linear&interval=1&limit=1&symbol=\\\\\\\';
    $API_RECENT_TRADE  = \\\\\\\'https://api.bybit.com/v5/market/recent-trade?category=linear&limit=1&symbol=\\\\\\\';
    $API_RISK_LIMIT    = \\\\\\\'https://api.bybit.com/v5/market/risk-limit?category=linear&symbol=\\\\\\\';
    $API_ACCOUNT_RATIO = \\\\\\\'https://api.bybit.com/v5/market/account-ratio?category=linear&period=1d&limit=1&symbol=\\\\\\\';
    $API_MARK_KLINE    = \\\\\\\'https://api.bybit.com/v5/market/mark-price-kline?category=linear&interval=1&limit=1&symbol=\\\\\\\';
    $API_INDEX_KLINE   = \\\\\\\'https://api.bybit.com/v5/market/index-price-kline?category=linear&interval=1&limit=1&symbol=\\\\\\\';
    $API_PREMIUM_KLINE = \\\\\\\'https://api.bybit.com/v5/market/premium-index-price-kline?category=linear&interval=1&limit=1&symbol=\\\\\\\';

    // 전체 종목 × 9개 URL 한번에 구성
    $urls = [];
    foreach ($symbols as $sym) {
        $s = urlencode($sym);
        $urls[\\\\\\\"{$sym}__info\\\\\\\"]    = $API_INSTRUMENTS   . $s;
        $urls[\\\\\\\"{$sym}__ticker\\\\\\\"]  = $API_TICKERS       . $s;
        $urls[\\\\\\\"{$sym}__kline\\\\\\\"]   = $API_KLINE         . $s;
        $urls[\\\\\\\"{$sym}__trade\\\\\\\"]   = $API_RECENT_TRADE  . $s;
        $urls[\\\\\\\"{$sym}__risk\\\\\\\"]    = $API_RISK_LIMIT    . $s;
        $urls[\\\\\\\"{$sym}__ratio\\\\\\\"]   = $API_ACCOUNT_RATIO . $s;
        $urls[\\\\\\\"{$sym}__mark\\\\\\\"]    = $API_MARK_KLINE    . $s;
        $urls[\\\\\\\"{$sym}__index\\\\\\\"]   = $API_INDEX_KLINE   . $s;
        $urls[\\\\\\\"{$sym}__premium\\\\\\\"] = $API_PREMIUM_KLINE . $s;
    }

    // 한방에 병렬 호출
    $raw = http_multi_get($urls);

    // 종목별 파싱
    $result = [];
    foreach ($symbols as $sym) {

        // instruments-info
        $res  = $raw[\\\\\\\"{$sym}__info\\\\\\\"] ?? null;
        $item = ($res && ($res[\\\\\\\'retCode\\\\\\\'] ?? -1) === 0) ? ($res[\\\\\\\'result\\\\\\\'][\\\\\\\'list\\\\\\\'][0] ?? []) : [];
        $lf = $item[\\\\\\\'leverageFilter\\\\\\\'] ?? [];
        $pf = $item[\\\\\\\'priceFilter\\\\\\\']    ?? [];
        $ls = $item[\\\\\\\'lotSizeFilter\\\\\\\']  ?? [];

        // tickers
        $res = $raw[\\\\\\\"{$sym}__ticker\\\\\\\"] ?? null;
        $tk  = ($res && ($res[\\\\\\\'retCode\\\\\\\'] ?? -1) === 0) ? ($res[\\\\\\\'result\\\\\\\'][\\\\\\\'list\\\\\\\'][0] ?? null) : null;

        // kline
        $res = $raw[\\\\\\\"{$sym}__kline\\\\\\\"] ?? null;
        $k   = ($res && ($res[\\\\\\\'retCode\\\\\\\'] ?? -1) === 0) ? ($res[\\\\\\\'result\\\\\\\'][\\\\\\\'list\\\\\\\'][0] ?? null) : null;

        // recent-trade
        $res = $raw[\\\\\\\"{$sym}__trade\\\\\\\"] ?? null;
        $t   = ($res && ($res[\\\\\\\'retCode\\\\\\\'] ?? -1) === 0) ? ($res[\\\\\\\'result\\\\\\\'][\\\\\\\'list\\\\\\\'][0] ?? null) : null;

        // risk-limit
        $res = $raw[\\\\\\\"{$sym}__risk\\\\\\\"] ?? null;
        $r   = ($res && ($res[\\\\\\\'retCode\\\\\\\'] ?? -1) === 0) ? ($res[\\\\\\\'result\\\\\\\'][\\\\\\\'list\\\\\\\'][0] ?? null) : null;

        // account-ratio
        $res = $raw[\\\\\\\"{$sym}__ratio\\\\\\\"] ?? null;
        $a   = ($res && ($res[\\\\\\\'retCode\\\\\\\'] ?? -1) === 0) ? ($res[\\\\\\\'result\\\\\\\'][\\\\\\\'list\\\\\\\'][0] ?? null) : null;

        // mark-price-kline
        $res = $raw[\\\\\\\"{$sym}__mark\\\\\\\"] ?? null;
        $mk  = ($res && ($res[\\\\\\\'retCode\\\\\\\'] ?? -1) === 0) ? ($res[\\\\\\\'result\\\\\\\'][\\\\\\\'list\\\\\\\'][0] ?? null) : null;

        // index-price-kline
        $res = $raw[\\\\\\\"{$sym}__index\\\\\\\"] ?? null;
        $ik  = ($res && ($res[\\\\\\\'retCode\\\\\\\'] ?? -1) === 0) ? ($res[\\\\\\\'result\\\\\\\'][\\\\\\\'list\\\\\\\'][0] ?? null) : null;

        // premium-index-price-kline
        $res = $raw[\\\\\\\"{$sym}__premium\\\\\\\"] ?? null;
        $pk  = ($res && ($res[\\\\\\\'retCode\\\\\\\'] ?? -1) === 0) ? ($res[\\\\\\\'result\\\\\\\'][\\\\\\\'list\\\\\\\'][0] ?? null) : null;

        $result[$sym] = [
            \\\\\\\'symbol\\\\\\\'               => $sym,
            \\\\\\\'status\\\\\\\'               => $item[\\\\\\\'status\\\\\\\']                          ?? \\\\\\\'\\\\\\\',
            \\\\\\\'baseCoin\\\\\\\'             => $item[\\\\\\\'baseCoin\\\\\\\']                        ?? \\\\\\\'\\\\\\\',
            \\\\\\\'quoteCoin\\\\\\\'            => $item[\\\\\\\'quoteCoin\\\\\\\']                       ?? \\\\\\\'\\\\\\\',
            \\\\\\\'settleCoin\\\\\\\'           => $item[\\\\\\\'settleCoin\\\\\\\']                      ?? \\\\\\\'\\\\\\\',
            \\\\\\\'launchTime\\\\\\\'           => $item[\\\\\\\'launchTime\\\\\\\']                      ?? \\\\\\\'\\\\\\\',
            \\\\\\\'deliveryTime\\\\\\\'         => $item[\\\\\\\'deliveryTime\\\\\\\']                    ?? \\\\\\\'\\\\\\\',
            \\\\\\\'priceScale\\\\\\\'           => $item[\\\\\\\'priceScale\\\\\\\']                      ?? \\\\\\\'\\\\\\\',
            \\\\\\\'minLeverage\\\\\\\'          => $lf[\\\\\\\'minLeverage\\\\\\\']                       ?? \\\\\\\'\\\\\\\',
            \\\\\\\'maxLeverage\\\\\\\'          => $lf[\\\\\\\'maxLeverage\\\\\\\']                       ?? \\\\\\\'\\\\\\\',
            \\\\\\\'leverageStep\\\\\\\'         => $lf[\\\\\\\'leverageStep\\\\\\\']                      ?? \\\\\\\'\\\\\\\',
            \\\\\\\'minPrice\\\\\\\'             => $pf[\\\\\\\'minPrice\\\\\\\']                          ?? \\\\\\\'\\\\\\\',
            \\\\\\\'maxPrice\\\\\\\'             => $pf[\\\\\\\'maxPrice\\\\\\\']                          ?? \\\\\\\'\\\\\\\',
            \\\\\\\'tickSize\\\\\\\'             => $pf[\\\\\\\'tickSize\\\\\\\']                          ?? \\\\\\\'\\\\\\\',
            \\\\\\\'minOrderQty\\\\\\\'          => $ls[\\\\\\\'minOrderQty\\\\\\\']                       ?? \\\\\\\'\\\\\\\',
            \\\\\\\'maxOrderQty\\\\\\\'          => $ls[\\\\\\\'maxOrderQty\\\\\\\']                       ?? \\\\\\\'\\\\\\\',
            \\\\\\\'qtyStep\\\\\\\'              => $ls[\\\\\\\'qtyStep\\\\\\\']                           ?? \\\\\\\'\\\\\\\',
            \\\\\\\'fundingInterval\\\\\\\'      => $item[\\\\\\\'fundingInterval\\\\\\\']                 ?? \\\\\\\'\\\\\\\',
            \\\\\\\'copyTrading\\\\\\\'          => $item[\\\\\\\'copyTrading\\\\\\\']                     ?? \\\\\\\'\\\\\\\',
            \\\\\\\'unifiedMarginTrade\\\\\\\'   => ($item[\\\\\\\'unifiedMarginTrade\\\\\\\'] ? 1 : 0),
            \\\\\\\'deliveryFeeRate\\\\\\\'      => $item[\\\\\\\'deliveryFeeRate\\\\\\\']                 ?? \\\\\\\'\\\\\\\',
            \\\\\\\'postOnlyMaxOrderQty\\\\\\\'  => $item[\\\\\\\'lotSizeFilter\\\\\\\'][\\\\\\\'postOnlyMaxOrderQty\\\\\\\'] ?? \\\\\\\'\\\\\\\',
            \\\\\\\'upperLimitPrice\\\\\\\'      => $item[\\\\\\\'upperFundingRate\\\\\\\']                ?? \\\\\\\'\\\\\\\',
            \\\\\\\'lowerLimitPrice\\\\\\\'      => $item[\\\\\\\'lowerFundingRate\\\\\\\']                ?? \\\\\\\'\\\\\\\',
            \\\\\\\'time\\\\\\\'                 => (int)(microtime(true) * 1000),
            \\\\\\\'lastPrice\\\\\\\'            => $tk[\\\\\\\'lastPrice\\\\\\\']                         ?? \\\\\\\'\\\\\\\',
            \\\\\\\'prevPrice24h\\\\\\\'         => $tk[\\\\\\\'prevPrice24h\\\\\\\']                      ?? \\\\\\\'\\\\\\\',
            \\\\\\\'price24hPcnt\\\\\\\'         => $tk[\\\\\\\'price24hPcnt\\\\\\\']                      ?? \\\\\\\'\\\\\\\',
            \\\\\\\'highPrice24h\\\\\\\'         => $tk[\\\\\\\'highPrice24h\\\\\\\']                      ?? \\\\\\\'\\\\\\\',
            \\\\\\\'lowPrice24h\\\\\\\'          => $tk[\\\\\\\'lowPrice24h\\\\\\\']                       ?? \\\\\\\'\\\\\\\',
            \\\\\\\'openInterest\\\\\\\'         => $tk[\\\\\\\'openInterest\\\\\\\']                      ?? \\\\\\\'\\\\\\\',
            \\\\\\\'markPrice\\\\\\\'            => $tk[\\\\\\\'markPrice\\\\\\\']                         ?? \\\\\\\'\\\\\\\',
            \\\\\\\'indexPrice\\\\\\\'           => $tk[\\\\\\\'indexPrice\\\\\\\']                        ?? \\\\\\\'\\\\\\\',
            \\\\\\\'fundingRate\\\\\\\'          => $tk[\\\\\\\'fundingRate\\\\\\\']                       ?? \\\\\\\'\\\\\\\',
            \\\\\\\'prevPrice1h\\\\\\\'          => $tk[\\\\\\\'prevPrice1h\\\\\\\']                       ?? \\\\\\\'\\\\\\\',
            \\\\\\\'openInterestValue\\\\\\\'    => $tk[\\\\\\\'openInterestValue\\\\\\\']                 ?? \\\\\\\'\\\\\\\',
            \\\\\\\'turnover24h\\\\\\\'          => $tk[\\\\\\\'turnover24h\\\\\\\']                       ?? \\\\\\\'\\\\\\\',
            \\\\\\\'volume24h\\\\\\\'            => $tk[\\\\\\\'volume24h\\\\\\\']                         ?? \\\\\\\'\\\\\\\',
            \\\\\\\'nextFundingTime\\\\\\\'      => $tk[\\\\\\\'nextFundingTime\\\\\\\']                   ?? \\\\\\\'\\\\\\\',
            \\\\\\\'predictedDeliveryPrice\\\\\\\' => $tk[\\\\\\\'predictedDeliveryPrice\\\\\\\']          ?? \\\\\\\'\\\\\\\',
            \\\\\\\'basisRate\\\\\\\'            => $tk[\\\\\\\'basisRate\\\\\\\']                         ?? \\\\\\\'\\\\\\\',
            \\\\\\\'ask1Size\\\\\\\'             => $tk[\\\\\\\'ask1Size\\\\\\\']                          ?? \\\\\\\'\\\\\\\',
            \\\\\\\'bid1Price\\\\\\\'            => $tk[\\\\\\\'bid1Price\\\\\\\']                         ?? \\\\\\\'\\\\\\\',
            \\\\\\\'ask1Price\\\\\\\'            => $tk[\\\\\\\'ask1Price\\\\\\\']                         ?? \\\\\\\'\\\\\\\',
            \\\\\\\'bid1Size\\\\\\\'             => $tk[\\\\\\\'bid1Size\\\\\\\']                          ?? \\\\\\\'\\\\\\\',
            \\\\\\\'start\\\\\\\'                => $k[0]                                    ?? \\\\\\\'\\\\\\\',
            \\\\\\\'open\\\\\\\'                 => $k[1]                                    ?? \\\\\\\'\\\\\\\',
            \\\\\\\'high\\\\\\\'                 => $k[2]                                    ?? \\\\\\\'\\\\\\\',
            \\\\\\\'low\\\\\\\'                  => $k[3]                                    ?? \\\\\\\'\\\\\\\',
            \\\\\\\'close\\\\\\\'                => $k[4]                                    ?? \\\\\\\'\\\\\\\',
            \\\\\\\'volume\\\\\\\'               => $k[5]                                    ?? \\\\\\\'\\\\\\\',
            \\\\\\\'turnover\\\\\\\'             => $k[6]                                    ?? \\\\\\\'\\\\\\\',
            \\\\\\\'execId\\\\\\\'               => $t[\\\\\\\'execId\\\\\\\']                             ?? \\\\\\\'\\\\\\\',
            \\\\\\\'price\\\\\\\'                => $t[\\\\\\\'price\\\\\\\']                              ?? \\\\\\\'\\\\\\\',
            \\\\\\\'size\\\\\\\'                 => $t[\\\\\\\'size\\\\\\\']                               ?? \\\\\\\'\\\\\\\',
            \\\\\\\'side\\\\\\\'                 => $t[\\\\\\\'side\\\\\\\']                               ?? \\\\\\\'\\\\\\\',
            \\\\\\\'isBlockTrade\\\\\\\'         => ($t[\\\\\\\'isBlockTrade\\\\\\\'] ? 1 : 0),
            \\\\\\\'isAdlTrade\\\\\\\'           => ($t[\\\\\\\'isBlockTrade\\\\\\\'] ? 1 : 0),
            \\\\\\\'mPnL\\\\\\\'                 => $t[\\\\\\\'mPnL\\\\\\\']                               ?? \\\\\\\'\\\\\\\',
            \\\\\\\'riskId\\\\\\\'               => $r[\\\\\\\'id\\\\\\\']                                 ?? \\\\\\\'\\\\\\\',
            \\\\\\\'isLowestRisk\\\\\\\'         => $r[\\\\\\\'isLowestRisk\\\\\\\']                       ?? 0,
            \\\\\\\'maintenanceMargin\\\\\\\'    => $r[\\\\\\\'maintenanceMargin\\\\\\\']                  ?? \\\\\\\'\\\\\\\',
            \\\\\\\'initialMargin\\\\\\\'        => $r[\\\\\\\'initialMargin\\\\\\\']                      ?? \\\\\\\'\\\\\\\',
            \\\\\\\'limit\\\\\\\'                => $r[\\\\\\\'riskLimitValue\\\\\\\']                     ?? \\\\\\\'\\\\\\\',
            \\\\\\\'buyRatio\\\\\\\'             => $a[\\\\\\\'buyRatio\\\\\\\']                           ?? \\\\\\\'\\\\\\\',
            \\\\\\\'sellRatio\\\\\\\'            => $a[\\\\\\\'sellRatio\\\\\\\']                          ?? \\\\\\\'\\\\\\\',
            \\\\\\\'period\\\\\\\'               => 1,
            \\\\\\\'m_close\\\\\\\'              => $mk[4]                                   ?? \\\\\\\'\\\\\\\',
            \\\\\\\'i_close\\\\\\\'              => $ik[4]                                   ?? \\\\\\\'\\\\\\\',
            \\\\\\\'p_close\\\\\\\'              => $pk[4]                                   ?? \\\\\\\'\\\\\\\',
        ];
    }

    return $result;
}

echo \\\\\\\"==================================================\\\\\\\\n\\\\\\\";
echo \\\\\\\"[{$DAEMON_ID}] 바이비트 선물 데몬이 시작되었습니다.\\\\\\\\n\\\\\\\";
echo \\\\\\\"PID : \\\\\\\" . getmypid() . \\\\\\\"\\\\\\\\n\\\\\\\";
echo \\\\\\\"상태는 daemon_record 테이블에서 확인할 수 있습니다.\\\\\\\\n\\\\\\\";
echo \\\\\\\"종료 시에는 d_kill_flag=1 또는 프로세스 kill 사용.\\\\\\\\n\\\\\\\";
echo \\\\\\\"==================================================\\\\\\\\n\\\\\\\";

$pdo     = get_db_connection();
$pdo_gnu = get_gnu_connection();
$server_ip = \\\\\\\'CLI_DAEMON\\\\\\\';

$cycle_count         = 0;
$last_market_refresh = 0;
$symbols             = [];

$stmt_hb   = null;
$stmt_kill = null;
$stmt_stop = null;
$stmt_ins  = null;
$stmt_best = null;

while (true) {
    $cycle_count++;

    try {
        $reconnected = false;
        if (!$pdo) {
            $pdo = get_db_connection();
            $reconnected = true;
        } else {
            try { $pdo->query(\\\\\\\"SELECT 1\\\\\\\"); }
            catch (Throwable $e) {
                $pdo = get_db_connection();
                $reconnected = true;
            }
        }
        if ($reconnected) {
            $stmt_hb = $stmt_kill = $stmt_stop = $stmt_ins = $stmt_best = null;
        }

        if ($pdo) {
            if (!$stmt_hb) {
                $stmt_hb = $pdo->prepare(\\\\\\\"
                    INSERT INTO daemon_record (
                        d_id, d_category, d_pid, d_status,
                        d_heartbeat, d_ip, d_start_time, d_memo, d_kill_flag
                    )
                    VALUES (
                        :id, \\\\\\\'BYBIT\\\\\\\', :pid, \\\\\\\'RUNNING\\\\\\\',
                        NOW(), :ip, NOW(), \\\\\\\'BYBIT LINEAR FUTURES GHOST\\\\\\\', 0
                    )
                    ON DUPLICATE KEY UPDATE
                        d_pid       = VALUES(d_pid),
                        d_status    = \\\\\\\'RUNNING\\\\\\\',
                        d_heartbeat = NOW(),
                        d_ip        = VALUES(d_ip),
                        d_memo      = VALUES(d_memo)
                \\\\\\\");
            }
            if (!$stmt_kill) {
                $stmt_kill = $pdo->prepare(\\\\\\\"SELECT d_kill_flag FROM daemon_record WHERE d_id = :id LIMIT 1\\\\\\\");
            }
            if (!$stmt_stop) {
                $stmt_stop = $pdo->prepare(\\\\\\\"
                    UPDATE daemon_record
                    SET d_status=\\\\\\\'STOPPED\\\\\\\', d_heartbeat=NOW(), d_pid=0
                    WHERE d_id=:id
                \\\\\\\");
            }

            $stmt_hb->execute([\\\\\\\':id\\\\\\\' => $DAEMON_ID, \\\\\\\':pid\\\\\\\' => getmypid(), \\\\\\\':ip\\\\\\\' => $server_ip]);

            $stmt_kill->execute([\\\\\\\':id\\\\\\\' => $DAEMON_ID]);
            $kill_flag = (int)($stmt_kill->fetchColumn() ?: 0);
            if ($kill_flag === 1) {
                $stmt_stop->execute([\\\\\\\':id\\\\\\\' => $DAEMON_ID]);
                echo \\\\\\\"\\\\\\\\n-> [STOP] d_kill_flag=1 감지. 정상 종료합니다.\\\\\\\\n\\\\\\\";
                exit(0);
            }

            // 종목 갱신 (1초마다)
            if (time() - $last_market_refresh >= 1 || empty($symbols)) {
                $tmp = [];
                // pdo_gnu 끊겼을 때만 재연결
                if (!$pdo_gnu) {
                    $pdo_gnu   = get_gnu_connection();
                    $stmt_best = null;
                } else {
                    try { $pdo_gnu->query(\\\\\\\"SELECT 1\\\\\\\"); }
                    catch (Throwable $e) {
                        $pdo_gnu   = get_gnu_connection();
                        $stmt_best = null;
                    }
                }
                if ($pdo_gnu instanceof PDO) {
                    if (!$stmt_best) {
                        $stmt_best = $pdo_gnu->prepare(\\\\\\\"
                            SELECT wr_subject FROM g5_write_daemon_kind_bybit
                            WHERE (x2_run = 1 OR x2_run = \\\\\\\'1\\\\\\\')
                        \\\\\\\");
                    }
                    $stmt_best->execute();
                    $best_rows = $stmt_best->fetchAll(PDO::FETCH_COLUMN);
                    if (is_array($best_rows)) {
                        foreach ($best_rows as $sym) {
                            $sym = strtoupper(trim((string)$sym));
                            if ($sym === \\\\\\\'\\\\\\\') continue;
                            if (strpos($sym, \\\\\\\'USDT\\\\\\\') === false && strpos($sym, \\\\\\\'-\\\\\\\') === false) {
                                $sym .= \\\\\\\'USDT\\\\\\\';
                            }
                            if (!in_array($sym, $tmp, true)) $tmp[] = $sym;
                        }
                    }
                }
                if ($tmp) {
                    $symbols = $tmp;
                    $last_market_refresh = time();
                    echo \\\\\\\"-> [INFO] 보드 종목 갱신 완료 (\\\\\\\" . count($symbols) . \\\\\\\"개)\\\\\\\\n\\\\\\\";
                } else {
                    $symbols = [];
                    echo \\\\\\\"-> [WARN] 보드 실행 종목(x2_run=1) 없음/DB연결실패. 10초 대기...\\\\\\\\n\\\\\\\";
                    flush();
                    sleep(10);
                    continue;
                }
            }

            if (!$symbols) {
                echo \\\\\\\"-> [WARN] 수집 종목 없음. 10초 대기...\\\\\\\\n\\\\\\\";
                flush();
                sleep(10);
                continue;
            }

            if (!$stmt_ins) {
                $stmt_ins = $pdo->prepare(\\\\\\\"
                    INSERT INTO daemon_bybit_Ticker (
                        symbol, status, baseCoin, quoteCoin, settleCoin,
                        launchTime, deliveryTime, priceScale,
                        minLeverage, maxLeverage, leverageStep,
                        minPrice, maxPrice, tickSize,
                        minOrderQty, maxOrderQty, qtyStep,
                        fundingInterval, copyTrading, unifiedMarginTrade,
                        deliveryFeeRate, postOnlyMaxOrderQty, upperLimitPrice, lowerLimitPrice,
                        time, updated_at,
                        lastPrice, prevPrice24h, price24hPcnt,
                        highPrice24h, lowPrice24h, openInterest,
                        markPrice, indexPrice, fundingRate,
                        prevPrice1h, openInterestValue, turnover24h, volume24h,
                        nextFundingTime, predictedDeliveryPrice, basisRate,
                        ask1Size, bid1Price, ask1Price, bid1Size,
                        `start`, `open`, `high`, `low`, `close`, volume, turnover,
                        execId, price, size, side, isBlockTrade, isAdlTrade, mPnL,
                        riskId, isLowestRisk, maintenanceMargin, initialMargin, `limit`,
                        buyRatio, sellRatio, period,
                        m_close, i_close, p_close
                    )
                    VALUES (
                        :symbol, :status, :baseCoin, :quoteCoin, :settleCoin,
                        :launchTime, :deliveryTime, :priceScale,
                        :minLeverage, :maxLeverage, :leverageStep,
                        :minPrice, :maxPrice, :tickSize,
                        :minOrderQty, :maxOrderQty, :qtyStep,
                        :fundingInterval, :copyTrading, :unifiedMarginTrade,
                        :deliveryFeeRate, :postOnlyMaxOrderQty, :upperLimitPrice, :lowerLimitPrice,
                        :time, NOW(),
                        :lastPrice, :prevPrice24h, :price24hPcnt,
                        :highPrice24h, :lowPrice24h, :openInterest,
                        :markPrice, :indexPrice, :fundingRate,
                        :prevPrice1h, :openInterestValue, :turnover24h, :volume24h,
                        :nextFundingTime, :predictedDeliveryPrice, :basisRate,
                        :ask1Size, :bid1Price, :ask1Price, :bid1Size,
                        :start, :open, :high, :low, :close, :volume, :turnover,
                        :execId, :price, :size, :side, :isBlockTrade, :isAdlTrade, :mPnL,
                        :riskId, :isLowestRisk, :maintenanceMargin, :initialMargin, :limit,
                        :buyRatio, :sellRatio, :period,
                        :m_close, :i_close, :p_close
                    )
                    ON DUPLICATE KEY UPDATE
                        status               = VALUES(status),
                        baseCoin             = VALUES(baseCoin),
                        quoteCoin            = VALUES(quoteCoin),
                        settleCoin           = VALUES(settleCoin),
                        launchTime           = VALUES(launchTime),
                        deliveryTime         = VALUES(deliveryTime),
                        priceScale           = VALUES(priceScale),
                        minLeverage          = VALUES(minLeverage),
                        maxLeverage          = VALUES(maxLeverage),
                        leverageStep         = VALUES(leverageStep),
                        minPrice             = VALUES(minPrice),
                        maxPrice             = VALUES(maxPrice),
                        tickSize             = VALUES(tickSize),
                        minOrderQty          = VALUES(minOrderQty),
                        maxOrderQty          = VALUES(maxOrderQty),
                        qtyStep              = VALUES(qtyStep),
                        fundingInterval      = VALUES(fundingInterval),
                        copyTrading          = VALUES(copyTrading),
                        unifiedMarginTrade   = VALUES(unifiedMarginTrade),
                        deliveryFeeRate      = VALUES(deliveryFeeRate),
                        postOnlyMaxOrderQty  = VALUES(postOnlyMaxOrderQty),
                        upperLimitPrice      = VALUES(upperLimitPrice),
                        lowerLimitPrice      = VALUES(lowerLimitPrice),
                        time                 = VALUES(time),
                        updated_at           = NOW(),
                        lastPrice            = VALUES(lastPrice),
                        prevPrice24h         = VALUES(prevPrice24h),
                        price24hPcnt         = VALUES(price24hPcnt),
                        highPrice24h         = VALUES(highPrice24h),
                        lowPrice24h          = VALUES(lowPrice24h),
                        openInterest         = VALUES(openInterest),
                        markPrice            = VALUES(markPrice),
                        indexPrice           = VALUES(indexPrice),
                        fundingRate              = VALUES(fundingRate),
                        prevPrice1h              = VALUES(prevPrice1h),
                        openInterestValue        = VALUES(openInterestValue),
                        turnover24h              = VALUES(turnover24h),
                        volume24h                = VALUES(volume24h),
                        nextFundingTime          = VALUES(nextFundingTime),
                        predictedDeliveryPrice   = VALUES(predictedDeliveryPrice),
                        basisRate                = VALUES(basisRate),
                        ask1Size                 = VALUES(ask1Size),
                        bid1Price                = VALUES(bid1Price),
                        ask1Price                = VALUES(ask1Price),
                        bid1Size                 = VALUES(bid1Size),
                        `start`              = VALUES(`start`),
                        `open`               = VALUES(`open`),
                        `high`               = VALUES(`high`),
                        `low`                = VALUES(`low`),
                        `close`              = VALUES(`close`),
                        volume               = VALUES(volume),
                        turnover             = VALUES(turnover),
                        execId               = VALUES(execId),
                        price                = VALUES(price),
                        size                 = VALUES(size),
                        side                 = VALUES(side),
                        isBlockTrade         = VALUES(isBlockTrade),
                        isAdlTrade           = VALUES(isAdlTrade),
                        mPnL                 = VALUES(mPnL),
                        riskId               = VALUES(riskId),
                        isLowestRisk         = VALUES(isLowestRisk),
                        maintenanceMargin    = VALUES(maintenanceMargin),
                        initialMargin        = VALUES(initialMargin),
                        `limit`              = VALUES(`limit`),
                        buyRatio             = VALUES(buyRatio),
                        sellRatio            = VALUES(sellRatio),
                        period               = VALUES(period),
                        m_close              = VALUES(m_close),
                        i_close              = VALUES(i_close),
                        p_close              = VALUES(p_close)
                \\\\\\\");

                if ($stmt_ins === false || !($stmt_ins instanceof PDOStatement)) {
                    $err = $pdo->errorInfo();
                    echo \\\\\\\"\\\\\\\\n-> [ERROR] stmt_ins prepare 실패: \\\\\\\" . ($err[2] ?? \\\\\\\'unknown\\\\\\\') . \\\\\\\"\\\\\\\\n\\\\\\\";
                    $stmt_ins = null;
                    flush(); sleep(5); continue;
                }
            }

            $at = date(\\\\\\\'Y-m-d H:i:s\\\\\\\');

            // 전체 종목 한번에 병렬 호출
            $all_data = collectAll($symbols);

            foreach ($all_data as $symbol => $d) {
                if (empty($d)) {
                    echo \\\\\\\"\\\\\\\\n-> [SKIP] {$symbol} API 실패\\\\\\\\n\\\\\\\";
                    continue;
                }

                $stmt_ins->execute([
                    \\\\\\\':symbol\\\\\\\'              => $d[\\\\\\\'symbol\\\\\\\'],
                    \\\\\\\':status\\\\\\\'              => $d[\\\\\\\'status\\\\\\\'],
                    \\\\\\\':baseCoin\\\\\\\'            => $d[\\\\\\\'baseCoin\\\\\\\'],
                    \\\\\\\':quoteCoin\\\\\\\'           => $d[\\\\\\\'quoteCoin\\\\\\\'],
                    \\\\\\\':settleCoin\\\\\\\'          => $d[\\\\\\\'settleCoin\\\\\\\'],
                    \\\\\\\':launchTime\\\\\\\'          => $d[\\\\\\\'launchTime\\\\\\\'],
                    \\\\\\\':deliveryTime\\\\\\\'        => $d[\\\\\\\'deliveryTime\\\\\\\'],
                    \\\\\\\':priceScale\\\\\\\'          => $d[\\\\\\\'priceScale\\\\\\\'],
                    \\\\\\\':minLeverage\\\\\\\'         => $d[\\\\\\\'minLeverage\\\\\\\'],
                    \\\\\\\':maxLeverage\\\\\\\'         => $d[\\\\\\\'maxLeverage\\\\\\\'],
                    \\\\\\\':leverageStep\\\\\\\'        => $d[\\\\\\\'leverageStep\\\\\\\'],
                    \\\\\\\':minPrice\\\\\\\'            => $d[\\\\\\\'minPrice\\\\\\\'],
                    \\\\\\\':maxPrice\\\\\\\'            => $d[\\\\\\\'maxPrice\\\\\\\'],
                    \\\\\\\':tickSize\\\\\\\'            => $d[\\\\\\\'tickSize\\\\\\\'],
                    \\\\\\\':minOrderQty\\\\\\\'         => $d[\\\\\\\'minOrderQty\\\\\\\'],
                    \\\\\\\':maxOrderQty\\\\\\\'         => $d[\\\\\\\'maxOrderQty\\\\\\\'],
                    \\\\\\\':qtyStep\\\\\\\'             => $d[\\\\\\\'qtyStep\\\\\\\'],
                    \\\\\\\':fundingInterval\\\\\\\'     => $d[\\\\\\\'fundingInterval\\\\\\\'],
                    \\\\\\\':copyTrading\\\\\\\'         => $d[\\\\\\\'copyTrading\\\\\\\'],
                    \\\\\\\':unifiedMarginTrade\\\\\\\'  => $d[\\\\\\\'unifiedMarginTrade\\\\\\\'],
                    \\\\\\\':deliveryFeeRate\\\\\\\'     => $d[\\\\\\\'deliveryFeeRate\\\\\\\'],
                    \\\\\\\':postOnlyMaxOrderQty\\\\\\\' => $d[\\\\\\\'postOnlyMaxOrderQty\\\\\\\'],
                    \\\\\\\':upperLimitPrice\\\\\\\'     => $d[\\\\\\\'upperLimitPrice\\\\\\\'],
                    \\\\\\\':lowerLimitPrice\\\\\\\'     => $d[\\\\\\\'lowerLimitPrice\\\\\\\'],
                    \\\\\\\':time\\\\\\\'                => $d[\\\\\\\'time\\\\\\\'],
                    \\\\\\\':lastPrice\\\\\\\'           => $d[\\\\\\\'lastPrice\\\\\\\'],
                    \\\\\\\':prevPrice24h\\\\\\\'        => $d[\\\\\\\'prevPrice24h\\\\\\\'],
                    \\\\\\\':price24hPcnt\\\\\\\'        => $d[\\\\\\\'price24hPcnt\\\\\\\'],
                    \\\\\\\':highPrice24h\\\\\\\'        => $d[\\\\\\\'highPrice24h\\\\\\\'],
                    \\\\\\\':lowPrice24h\\\\\\\'         => $d[\\\\\\\'lowPrice24h\\\\\\\'],
                    \\\\\\\':openInterest\\\\\\\'        => $d[\\\\\\\'openInterest\\\\\\\'],
                    \\\\\\\':markPrice\\\\\\\'           => $d[\\\\\\\'markPrice\\\\\\\'],
                    \\\\\\\':indexPrice\\\\\\\'          => $d[\\\\\\\'indexPrice\\\\\\\'],
                    \\\\\\\':fundingRate\\\\\\\'             => $d[\\\\\\\'fundingRate\\\\\\\'],
                    \\\\\\\':prevPrice1h\\\\\\\'             => $d[\\\\\\\'prevPrice1h\\\\\\\'],
                    \\\\\\\':openInterestValue\\\\\\\'       => $d[\\\\\\\'openInterestValue\\\\\\\'],
                    \\\\\\\':turnover24h\\\\\\\'             => $d[\\\\\\\'turnover24h\\\\\\\'],
                    \\\\\\\':volume24h\\\\\\\'               => $d[\\\\\\\'volume24h\\\\\\\'],
                    \\\\\\\':nextFundingTime\\\\\\\'         => $d[\\\\\\\'nextFundingTime\\\\\\\'],
                    \\\\\\\':predictedDeliveryPrice\\\\\\\'  => $d[\\\\\\\'predictedDeliveryPrice\\\\\\\'],
                    \\\\\\\':basisRate\\\\\\\'               => $d[\\\\\\\'basisRate\\\\\\\'],
                    \\\\\\\':ask1Size\\\\\\\'                => $d[\\\\\\\'ask1Size\\\\\\\'],
                    \\\\\\\':bid1Price\\\\\\\'               => $d[\\\\\\\'bid1Price\\\\\\\'],
                    \\\\\\\':ask1Price\\\\\\\'               => $d[\\\\\\\'ask1Price\\\\\\\'],
                    \\\\\\\':bid1Size\\\\\\\'                => $d[\\\\\\\'bid1Size\\\\\\\'],
                    \\\\\\\':start\\\\\\\'               => $d[\\\\\\\'start\\\\\\\'],
                    \\\\\\\':open\\\\\\\'                => $d[\\\\\\\'open\\\\\\\'],
                    \\\\\\\':high\\\\\\\'                => $d[\\\\\\\'high\\\\\\\'],
                    \\\\\\\':low\\\\\\\'                 => $d[\\\\\\\'low\\\\\\\'],
                    \\\\\\\':close\\\\\\\'               => $d[\\\\\\\'close\\\\\\\'],
                    \\\\\\\':volume\\\\\\\'              => $d[\\\\\\\'volume\\\\\\\'],
                    \\\\\\\':turnover\\\\\\\'            => $d[\\\\\\\'turnover\\\\\\\'],
                    \\\\\\\':execId\\\\\\\'              => $d[\\\\\\\'execId\\\\\\\'],
                    \\\\\\\':price\\\\\\\'               => $d[\\\\\\\'price\\\\\\\'],
                    \\\\\\\':size\\\\\\\'                => $d[\\\\\\\'size\\\\\\\'],
                    \\\\\\\':side\\\\\\\'                => $d[\\\\\\\'side\\\\\\\'],
                    \\\\\\\':isBlockTrade\\\\\\\'        => $d[\\\\\\\'isBlockTrade\\\\\\\'],
                    \\\\\\\':isAdlTrade\\\\\\\'          => $d[\\\\\\\'isAdlTrade\\\\\\\'],
                    \\\\\\\':mPnL\\\\\\\'                => $d[\\\\\\\'mPnL\\\\\\\'],
                    \\\\\\\':riskId\\\\\\\'              => $d[\\\\\\\'riskId\\\\\\\'],
                    \\\\\\\':isLowestRisk\\\\\\\'        => $d[\\\\\\\'isLowestRisk\\\\\\\'],
                    \\\\\\\':maintenanceMargin\\\\\\\'   => $d[\\\\\\\'maintenanceMargin\\\\\\\'],
                    \\\\\\\':initialMargin\\\\\\\'       => $d[\\\\\\\'initialMargin\\\\\\\'],
                    \\\\\\\':limit\\\\\\\'               => $d[\\\\\\\'limit\\\\\\\'],
                    \\\\\\\':buyRatio\\\\\\\'            => $d[\\\\\\\'buyRatio\\\\\\\'],
                    \\\\\\\':sellRatio\\\\\\\'           => $d[\\\\\\\'sellRatio\\\\\\\'],
                    \\\\\\\':period\\\\\\\'              => $d[\\\\\\\'period\\\\\\\'],
                    \\\\\\\':m_close\\\\\\\'             => $d[\\\\\\\'m_close\\\\\\\'],
                    \\\\\\\':i_close\\\\\\\'             => $d[\\\\\\\'i_close\\\\\\\'],
                    \\\\\\\':p_close\\\\\\\'             => $d[\\\\\\\'p_close\\\\\\\'],
                ]);
            }

            echo \\\\\\\".\\\\\\\";
            if ($cycle_count % 10 === 0) {
                echo \\\\\\\" <Running... Time: {$at} / 종목수: \\\\\\\" . count($symbols) . \\\\\\\">\\\\\\\\n\\\\\\\";
            }
            flush();

        } else {
            echo \\\\\\\"-> [WARN] DB 연결 실패. 5초 대기...\\\\\\\\n\\\\\\\";
            flush();
            sleep(5);
        }

        if ($cycle_count % 50 === 0 && function_exists(\\\\\\\'gc_collect_cycles\\\\\\\')) {
            gc_collect_cycles();
        }

    } catch (Throwable $e) {
        $log = date(\\\\\\\'[Y-m-d H:i:s] \\\\\\\') . \\\\\\\"ERROR: \\\\\\\" . $e->getMessage();
        echo \\\\\\\"\\\\\\\\n-> $log\\\\\\\\n\\\\\\\";
        flush();
        sleep(3);
    }

    sleep(30);
}
최근 "데몬" 데이터
# 바이비트 # API # 실시간 # 플렛폼 # 수집 # 데몬 # Ver.2