WsDataOrder.php 8.6 KB
<?php

namespace trader\struct;

require_once __DIR__ . '/SymbolInfo.php';

use trader\struct\SymbolInfo;

class WsDataOrder
{
    public string $platform;
    public string $posSide; //大写
    public string $symbol; //大写
    public string $side; //大写
    public float $price; //下单价格
    public float $avgPx; //成交均价
    public float $qty; //币种真实数量
    public float $lot; //合约张数
    public float $pnl; //盈亏
    public int $ts; //下单时间戳 ms
    public int $uts; //更新时间戳 ms
    public string $ordType; //下单类型
    public string $cliOrdId; //客户端订单号
    public string $ordId; //订单号
    public string $status; //订单状态

    public function __construct(string $platform, string $posSide, string $symbol, string $side, float $price, float $avgPx, float $qty, float $lot, float $pnl, int $ts, int $uts, string $ordType, string $cliOrdId, string $ordId, string $status)
    {
        $this->platform = $platform;
        $this->posSide = $posSide;
        $this->symbol = $symbol;
        $this->side = $side;
        $this->price = $price;
        $this->avgPx = $avgPx;
        $this->qty = $qty;
        $this->lot = $lot;
        $this->pnl = $pnl;
        $this->ts = $ts;
        $this->uts = $uts;
        $this->ordType = $ordType;
        $this->cliOrdId = $cliOrdId;
        $this->ordId = $ordId;
        $this->status = $status;
    }

    public function toArray()
    {
        return [
            'platform' => $this->platform,
            'posSide' => $this->posSide,
            'symbol' => $this->symbol,
            'side' => $this->side,
            'price' => $this->price,
            'avgPx' => $this->avgPx,
            'qty' => $this->qty,
            'lot' => $this->lot,
            'pnl' => $this->pnl,
            'ts' => $this->ts,
            'uts' => $this->uts,
            'ordType' => $this->ordType,
            'cliOrdId' => $this->cliOrdId,
            'ordId' => $this->ordId,
            'status' => $this->status,
        ];
    }

    public static function TransferOkxOrder(array $data, array $symbolInfos, callable $toSymbolSt): WsDataOrder|null
    {
        $symbol = call_user_func($toSymbolSt, $data['instId']);
        /** @var SymbolInfo $symbolInfo */
        $symbolInfo = $symbolInfos[$symbol] ?? null;
        if ($symbolInfo == null) {
            return null;
        }
        $platform = 'okx';
        $posSide = strtoupper($data['posSide']);
        $side = strtoupper($data['side']);
        $price = (float)$data['px'];
        $avgPx = (float)$data['avgPx'];
        $lot = (float)$data['sz'];
        $qty = round($lot * $symbolInfo->ctVal, $symbolInfo->qtyPrec);
        $pnl = (float)$data['pnl'];
        $ts = (int)$data['cTime'];
        $uts = (int)$data['uTime'];
        $ordType = strtoupper($data['ordType']);
        $cliOrdId = $data['clOrdId'];
        $ordId = $data['ordId'];
        $status = self::getOkxStatus($data['state']);
        if ($data['cancelSource'] != '') {
            $status = 'CANCELED';
        }
        return new WsDataOrder($platform, $posSide, $symbol, $side, $price, $avgPx, $qty, $lot, $pnl, $ts, $uts, $ordType, $cliOrdId, $ordId, $status);
    }
    private static function getOkxStatus(string $state)
    {
        if ($state == 'live') {
            return 'LIVE';
        }
        if ($state == 'partially_filled') {
            return 'PARTIALLY_FILLED';
        }
        if ($state == 'filled') {
            return 'FILLED';
        }
        if ($state == 'canceled') {
            return 'CANCELED';
        }
        return 'UNKNOWN';
    }
    public static function TransferBinanceOrder($data, $symbolInfos, callable $toSymbolSt): WsDataOrder|null
    {
        $order = $data['o'];
        $symbol = call_user_func($toSymbolSt, $order['s']);
        /** @var SymbolInfo $symbolInfo */
        $symbolInfo = $symbolInfos[$symbol] ?? null;
        if ($symbolInfo == null) {
            return null;
        }
        $platform = 'binance';
        $posSide = strtoupper($order['S']);
        $side = strtoupper($order['ps']);
        $price = (float)$order['p'];
        $avgPx = (float)$order['ap'];
        $lot = (float)$order['q'];
        $qty = $lot;
        $pnl = 0;
        $ts = (int)$order['T'];
        $uts = (int)$order['T'];
        $ordType = strtoupper($order['o']);
        $cliOrdId = $order['c'];
        $ordId = $order['i'];
        $status = self::getBinanceStatus($order['X']);
        return new WsDataOrder($platform, $posSide, $symbol, $side, $price, $avgPx, $qty, $lot, $pnl, $ts, $uts, $ordType, $cliOrdId, $ordId, $status);
    }
    private static function getBinanceStatus(string $state)
    {
        if ($state == 'NEW') {
            return 'LIVE';
        }
        if ($state == 'PARTIALLY_FILLED') {
            return 'PARTIALLY_FILLED';
        }
        if ($state == 'FILLED') {
            return 'FILLED';
        }
        if ($state == 'CANCELED') {
            return 'CANCELED';
        }
        if ($state == 'EXPIRED' || $state == "EXPIRED_IN_MATCH") {
            return 'CANCELED';
        }
        return 'UNKNOWN';
    }
    public static function TransferBybitOrder($data, $symbolInfos, callable $toSymbolSt): WsDataOrder|null
    {
        $symbol = call_user_func($toSymbolSt, $data['symbol']);
        /** @var SymbolInfo $symbolInfo */
        $symbolInfo = $symbolInfos[$symbol] ?? null;
        if ($symbolInfo == null) {
            return null;
        }
        $platform = 'bybit';
        $posSide = "BOTH";
        if ($data['positionIdx'] == 1) {
            $posSide = "LONG";
        }
        if ($data['positionIdx'] == 2) {
            $posSide = "SHORT";
        }
        $side = strtoupper($data['side']);
        $price = (float)$data['price'];
        $avgPx = (float)$data['avgPrice'];
        $lot = (float)$data['qty'];
        $qty = $lot;
        $pnl = (float)$data['closedPnl']; //去除手续费资金费的盈亏
        $ts = (int)$data['updatedTime'];
        $uts = (int)$data['updatedTime'];
        $ordType = strtoupper($data['orderType']);
        $timeInForce = $data['timeInForce'];
        if ($timeInForce == 'IOC' && $ordType == "LIMIT") {
            $ordType = "IOC";
        }
        $cliOrdId = $data['orderLinkId'];
        $ordId = $data['orderId'];
        $status = self::getBybitStatus($data['orderStatus']);
        return new WsDataOrder($platform, $posSide, $symbol, $side, $price, $avgPx, $qty, $lot, $pnl, $ts, $uts, $ordType, $cliOrdId, $ordId, $status);
    }
    private static function getBybitStatus(string $state)
    {
        if ($state == 'New') {
            return 'LIVE';
        }
        if ($state == 'PartiallyFilled') {
            return 'PARTIALLY_FILLED';
        }
        if ($state == 'Filled') {
            return 'FILLED';
        }
        if ($state == 'Cancelled' || $state == "Rejected") {
            return 'CANCELED';
        }
        return 'UNKNOWN';
    }
    public static function TransferBitgetOrder(array $data, array $symbolInfos, callable $toSymbolSt): WsDataOrder|null
    {
        $symbol = call_user_func($toSymbolSt, $data['instId']);
        /** @var SymbolInfo $symbolInfo */
        $symbolInfo = $symbolInfos[$symbol] ?? null;
        if ($symbolInfo === null) {
            return null;
        }
        $posSide = strtoupper($data['posSide']);
        if ($posSide == 'NET') {
            $posSide = 'BOTH';
        }
        $avgPx = 0;
        if (isset($data['priceAvg'])) {
            $avgPx = (float)$data['priceAvg'];
        }
        $ordType = strtoupper($data['orderType']);
        $timeInForce = strtoupper($data['force']);
        if ($ordType == 'LIMIT' && $timeInForce == 'IOC') {
            $ordType = 'IOC';
        }
        $pnl = 0;
        if (isset($data['pnl'])) {
            $pnl = (float)$data['pnl'];
        }
        return new WsDataOrder(
            'bitget',
            $posSide,
            $symbol,
            strtoupper($data['side']),
            (float)$data['price'],
            $avgPx,
            (float)$data['size'],
            (float)$data['size'],
            $pnl,
            (int)($data['cTime']),
            (int)($data['uTime']),
            $ordType,
            $data['clientOid'],
            $data['orderId'],
            self::getBitgetStatus($data['status'])
        );
    }

    private static function getBitgetStatus($status)
    {
        switch ($status) {
            case 'live':
                return 'LIVE';
            case 'partially_filled':
                return 'PARTIALLY_FILLED';
            case 'filled':
                return 'FILLED';
            case 'cancelled':
                return 'CANCELED';
            default:
                return 'UNKNOWN';
        }
    }
}