DATA/UPBIT/daemon/target/day_time_code_8.php
<?php
// day_time_code_8.php (개인자산 수익률 조건부 비율 매도)

// 1. [지갑 & DB 공유] 데몬 스코프 상속
global $upbit, $db_upbit;

include_once('./db_trading.php');

// 2. [실행 여부 체크]
if (($row['x2_run'] ?? '') != '1') return;

// 3. [기본 옵션 가져오기]
$coin_symbol    = trim($row['x2_coin']);
$profit_ratio   = (float)preg_replace('/[^0-9.]/', '', $row['x2_profit_ratio_8']);
$sell_ratio     = (float)preg_replace('/[^0-9.]/', '', $row['x2_sell_ratio']);
$currency       = trim(str_replace('KRW-', '', $coin_symbol));

if (!$coin_symbol || $profit_ratio <= 0 || $sell_ratio <= 0) return;

// 4. [쿨다운 가드] 65초 이내 재실행 금지
static $last_fire = [];
$fire_key = ($row['wr_id'] ?? '0') . '_' . basename(__FILE__);
$now_ts   = time();
if (isset($last_fire[$fire_key]) && ($now_ts - $last_fire[$fire_key]) < 65) return;

// 5. [시세 조회]
try {
    $ticker_stmt = $db_upbit->prepare(
        "SELECT trade_price, collected_at
         FROM daemon_upbit_Ticker
         WHERE market = ?
         ORDER BY collected_at DESC LIMIT 1"
    );
    $ticker_stmt->execute([$coin_symbol]);
    $ticker = $ticker_stmt->fetch(PDO::FETCH_ASSOC);
} catch (Throwable $e) {
    echo "[" . date('Y-m-d H:i:s') . "] ($coin_symbol) 시세 조회 실패: " . $e->getMessage() . "\n";
    return;
}

if (!$ticker) {
    echo "[" . date('Y-m-d H:i:s') . "] ($coin_symbol) 시세 데이터 없음. 패스.\n";
    return;
}

$ts = strtotime($ticker['collected_at']);
if ($ts === false) {
    echo "[" . date('Y-m-d H:i:s') . "] ($coin_symbol) collected_at 파싱 실패. 패스.\n";
    return;
}
if ((time() - $ts) > 60) {
    echo "[" . date('Y-m-d H:i:s') . "] ($coin_symbol) 시세 오래됨. 패스.\n";
    return;
}

// 6. [자산 조회]
try {
    $asset_stmt = $db_upbit->prepare(
        "SELECT balance, profit_rate, profit_amount, collected_at
         FROM daemon_upbit_Ticker_user
         WHERE currency = ?
         ORDER BY collected_at DESC LIMIT 1"
    );
    $asset_stmt->execute([$currency]);
    $asset = $asset_stmt->fetch(PDO::FETCH_ASSOC);
} catch (Throwable $e) {
    echo "[" . date('Y-m-d H:i:s') . "] ($coin_symbol) 자산 조회 실패: " . $e->getMessage() . "\n";
    return;
}

if (!$asset) {
    echo "[" . date('Y-m-d H:i:s') . "] ($coin_symbol) 자산 데이터 없음. 패스.\n";
    return;
}

$trade_price   = (float)$ticker['trade_price'];
$balance       = (float)$asset['balance'];
$profit_rate   = (float)$asset['profit_rate'];
$profit_amount = (float)$asset['profit_amount'];
$balance_amount = $balance * $trade_price;
$log_time      = date('Y-m-d H:i:s');

// 7. [최소 보유금액 체크]
if ($balance_amount <= 5500) {
    echo "[$log_time] ($coin_symbol) 보유수량 부족(" . number_format($balance_amount) . "원) - 매도 패스\n";
    return;
}

// 8. [핵심 조건] 수익률이 지정 비율 이상일 때만 타격
if ($profit_rate < $profit_ratio) {
    echo "[$log_time] ($coin_symbol) 수익률(" . $profit_rate . "%) < 지정비율(" . $profit_ratio . "%) - 매도 패스\n";
    return;
}

// 9. [매도 수량 계산] 보유수량의 x%
$sell_volume = $balance * ($sell_ratio / 100);

if ($sell_volume <= 0) {
    echo "[$log_time] ($coin_symbol) 매도수량 계산 오류. 패스.\n";
    return;
}

if ($sell_volume > $balance) {
    echo "[$log_time] ($coin_symbol) 매도수량(" . $sell_volume . ") > 보유수량(" . $balance . ") - 매도 패스\n";
    return;
}

// 10. [매도 실행]
try {
    $result = $upbit->order([
        'market'   => $coin_symbol,
        'side'     => 'ask',
        'ord_type' => 'market',
        'volume'   => (string)$sell_volume,
    ]);

    if (isset($result['uuid']) || isset($result['id'])) {
        $last_fire[$fire_key] = $now_ts;
        echo "[$log_time] (비율매도) $coin_symbol : " . $sell_volume . " 발사 성공! "
           . "(수익률: " . $profit_rate . "% / 손익: " . number_format($profit_amount) . "원 / 매도비율: " . $sell_ratio . "%)\n";

        record_trading($db_upbit, [
            'cron_id'       => $row['wr_id'],
            'market'        => $coin_symbol,
            'side'          => 'ask',
            'ord_type'      => 'market',
            'req_price' => (int)($sell_volume * $trade_price),
            'req_volume'    => $sell_volume,
            'trigger_type'  => 'interval_profit_rate_ratio',
            'trigger_value' => $profit_rate,
            'response'      => $result,
            'result'        => 'success',
        ]);
    } else {
        $msg = isset($result['error']['message']) ? $result['error']['message'] : '알 수 없는 오류';
        echo "[$log_time] (비율매도) $coin_symbol 실패... 이유: $msg\n";

        record_trading($db_upbit, [
            'cron_id'       => $row['wr_id'],
            'market'        => $coin_symbol,
            'side'          => 'ask',
            'ord_type'      => 'market',
            'req_price'     => (int)($sell_volume * $trade_price),  // 추가
            'req_volume'    => $sell_volume,
            'trigger_type'  => 'interval_profit_rate_ratio',
            'trigger_value' => $profit_rate,
            'response'      => $result,
            'result'        => 'fail',
            'message'       => $msg,
        ]);
    }
} catch (Throwable $e) {
    echo "[$log_time] ($coin_symbol) 매도 예외 발생: " . $e->getMessage() . "\n";
}
?>