PHP WebShell

Текущая директория: /var/www/bitcardoApp/cron

Просмотр файла: update_coin_rates.php

<?php
/**
 * update_coin_rates.php
 * - Fetches live USD prices from CoinGecko for rows where auto_update=1
 * - Applies percentage margin:
 *     sell_rate = live * (1 + margin_percent/100)
 *     buy_rate  = live * (1 - margin_percent/100)
 * - Writes sell_rate, buy_rate, updated_at into coin_rates
 *
*/

@ini_set('display_errors', '0');

require_once __DIR__ . '/../config/db_config.php';  // must define $conn (mysqli)

// ---------- Config ----------
$LOG = '/var/log/coin_rates_updater.log';  // ensure cron user can write this
// Map your DB coin symbols -> CoinGecko IDs
$COINGECKO_MAP = [
  'BTC'  => 'bitcoin',
  'ETH'  => 'ethereum',
  'SOL'  => 'solana',
  'USDT' => 'tether',
  'USDC' => 'usd-coin',
  // 'TRX' => 'tron',
  // add more as needed
];

// ---------- Helpers ----------
function log_msg(string $lvl, string $msg, array $ctx = []): void {
  global $LOG;
  $line = ['ts'=>gmdate('c'), 'lvl'=>$lvl, 'msg'=>$msg, 'ctx'=>$ctx];
  @file_put_contents($LOG, json_encode($line, JSON_UNESCAPED_SLASHES) . PHP_EOL, FILE_APPEND);
}

function round_price(float $v): float {
  // 8 dp is plenty for USD quotes stored in DECIMAL columns
  return round($v, 8);
}

function clamp_pct(float $pct, float $min = 0.0, float $max = 90.0): float {
  // prevent accidental 1000% etc.
  return max($min, min($max, $pct));
}

// ---------- 1) Load target coins (auto_update=1) ----------
$sql = "SELECT UPPER(coin) AS coin, COALESCE(margin_percent,0) AS margin_percent
          FROM coin_rates
         WHERE auto_update = 1";
$rs = $conn->query($sql);
if (!$rs) {
  log_msg('error', 'failed to read coin_rates', ['err' => $conn->error]);
  exit(1);
}

$rows = [];
$idsToFetch = [];     // set of coingecko slugs
$slugToCoin = [];     // slug -> coin symbol
while ($r = $rs->fetch_assoc()) {
  $coin = strtoupper($r['coin'] ?? '');
  if ($coin === '') continue;

  $rows[$coin] = [
    'coin'   => $coin,
    'margin' => (float)$r['margin_percent'], // PERCENT (e.g., 0.5 means 0.5%)
  ];

  if (!isset($COINGECKO_MAP[$coin])) {
    log_msg('warn', 'no CoinGecko mapping; skipping', ['coin' => $coin]);
    continue;
  }
  $slug = $COINGECKO_MAP[$coin];
  $idsToFetch[$slug] = true;
  $slugToCoin[$slug] = $coin;
}

if (empty($idsToFetch)) {
  log_msg('info', 'no coins to update (none with auto_update=1 or no mappings)');
  exit(0);
}

// ---------- 2) Fetch market prices from CoinGecko ----------
$idsParam = implode(',', array_keys($idsToFetch));
$url = "https://api.coingecko.com/api/v3/simple/price?ids={$idsParam}&vs_currencies=usd";

$resp = @file_get_contents($url);
if ($resp === false) {
  log_msg('error', 'failed to fetch CoinGecko', ['url' => $url]);
  exit(1);
}
$data = json_decode($resp, true);
if (!is_array($data)) {
  log_msg('error', 'invalid CoinGecko JSON', ['snippet' => substr($resp, 0, 200)]);
  exit(1);
}

// ---------- 3) Prepare update statement ----------
$upd = $conn->prepare("UPDATE coin_rates SET sell_rate=?, buy_rate=?, updated_at=NOW() WHERE coin=?");
if (!$upd) {
  log_msg('error', 'prepare failed', ['err' => $conn->error]);
  exit(1);
}

// ---------- 4) Apply percentage margin and write ----------
$updated = 0;

foreach ($slugToCoin as $slug => $coin) {
  if (!isset($data[$slug]['usd'])) {
    log_msg('warn', 'missing usd price from CoinGecko', ['coin' => $coin, 'slug' => $slug]);
    continue;
  }

  $live = (float)$data[$slug]['usd'];
  if ($live <= 0) {
    log_msg('warn', 'non-positive live price; skip', ['coin' => $coin, 'live' => $live]);
    continue;
  }

  // margin_percent is percentage of live price
  $mPct = clamp_pct((float)$rows[$coin]['margin']); // e.g., 0.50 = 0.5%
  $m    = $mPct / 100.0;

  // Your rules (percentage-based):
  //   sell_rate = live * (1 + m)
  //   buy_rate  = live * (1 - m)
  $sell = $live * (1.0 + $m);
  $buy  = $live * (1.0 - $m);

  // Avoid non-positive buy after extreme margins
  if ($buy <= 0) {
    $buy = 0.00000001;
  }

  $sell = round_price($sell);
  $buy  = round_price($buy);

  $upd->bind_param('dds', $sell, $buy, $coin);
  if (!$upd->execute()) {
    log_msg('error', 'update failed', ['coin' => $coin, 'err' => $upd->error]);
    continue;
  }
  $updated++;
  log_msg('info', 'rate updated', [
    'coin' => $coin,
    'live' => $live,
    'margin_percent' => $mPct,
    'sell_rate' => $sell,
    'buy_rate'  => $buy
  ]);
}

$upd->close();
log_msg('info', 'done', ['updated' => $updated]);

Выполнить команду


Для локальной разработки. Не используйте в интернете!