ExBroker.php 5.3 KB
<?php

namespace trader\okx;

require_once __DIR__ . '/../../struct/ApiInfo.php';
require_once __DIR__ . '/Api.php';
require_once __DIR__ . '/../../../jytools/func.php';
require_once __DIR__ . '/../../../jytools/Websocket.php';

use trader\struct\ApiInfo;
use trader\okx\Api as OkxApi;
use jytools\Websocket;
use function jytools\output;


class ExBroker
{
    // static private $host = 'wss://ws.okx.com:8443';
    // static private $host = 'ws://okws.keetu.com';
    static private $host = 'ws://okws.a.indigo888.com';
    static private $pathPrivate = '/ws/v5/private';
    static private $pathPublic = '/ws/v5/public';
    static private $pathBusiness = '/ws/v5/business';
    private ApiInfo $apiInfo;
    private OkxApi $api;
    private ?Websocket $wsAcc;
    private ?Websocket $wsKline;

    public function __construct($apiInfo)
    {
        $this->apiInfo = $apiInfo;
        $this->api = new OkxApi($apiInfo);
    }

    public function accListen(callable $onWsData)
    {
        if (isset($this->wsAcc)) {
            $this->wsAcc->close();
        }
        $this->wsAcc = new Websocket(self::$host . self::$pathPrivate);
        $this->wsAcc->connect(
            $onOpen = function () {
                $this->wsLogin();
            },
            $onMessage = function ($data) use ($onWsData) {
                $this->onWsDataPre($data, $onWsData);
            },
            $onClose = function () {
                // 关闭链接
            }
        );
    }
    public function klineListen($symbol, $period, callable $onData)
    {
        if (isset($this->wsKline)) {
            $this->wsKline->close();
        }
        $this->wsKline = new Websocket(self::$host . self::$pathBusiness);
        $this->wsKline->connect(
            $onOpen = function () use ($symbol, $period) {
                $subData = [];
                $subData['op'] = 'subscribe';
                $subData['args'] = [
                    [
                        'channel' => $this->getKlineChannels()[$period],
                        'instId' => $symbol,
                    ],
                ];
                output("订阅", $subData);
                $this->wsKline->push(json_encode($subData));
            },
            $onMessage = function ($data) use ($onData) {
                $data = json_decode($data, true);
                if (!isset($data['data'])) {
                    return;
                }
                $data = $data['data'][0];
                $onData($data);
            },
            $onClose = function () {
                // 关闭链接
            }
        );
    }
    private function crateWsSign($timeStamp)
    {
        $method = 'GET';
        $path = '/users/self/verify';
        $str = $timeStamp . $method . $path;
        $sign = hash_hmac('sha256', $str, $this->apiInfo->secret, true);
        $sign = base64_encode($sign);
        return $sign;
    }
    private function  wsLogin()
    {
        $ts = time();
        $subData = [];
        $subData['op'] = 'login';
        $subData['args'] = [
            [
                'apiKey' => $this->apiInfo->key,
                'passphrase' => $this->apiInfo->passphrase,
                'timestamp' => $ts,
                'sign' => $this->crateWsSign($ts),
            ]
        ];
        $this->wsAcc->push(json_encode($subData));
    }
    // ws 消息预处理
    private function onWsDataPre($data, callable $onWsData)
    {
        $data = json_decode($data, true);
        if (isset($data['event'])) {
            if ($data['event'] == 'login' && $data['code'] == '0') {
                output('ws登录成功');
                $this->wsSubscribe();
                return;
            }
            if ($data['event'] == 'subscribe' || $data['event'] == 'unsubscribe' || $data['event'] == 'channel-conn-count') {
                // output("ws 过滤数据", $data);
                return;
            }
            output("ok ws 未处理数据", $data);
        }
        call_user_func($onWsData, $data);
    }
    // 订阅
    private function wsSubscribe()
    {
        $subData = [];
        $subData['op'] = 'subscribe';
        $subData['args'] = [
            [
                'channel' => 'orders',
                'instType' => 'SWAP',
            ],
            [
                'channel' => 'positions',
                'instType' => 'SWAP',
            ],
            [
                'channel' => 'account',
                'ccy' => 'USDT',
            ]
        ];
        $this->wsAcc->push(json_encode($subData));
    }
    public function placeOrder($param)
    {
        return $this->api->placeOrder($param);
    }
    public function getKlineChannels()
    {
        return [
            '1s' => 'candle1s',
            '1m' => 'candle1m',
            '5m' => 'candle5m',
            '15m' => 'candle15m',
            '30m' => 'candle30m',
            '1h' => 'candle1H',
            '2h' => 'candle2H',
            '4h' => 'candle4H',
            '6h' => 'candle6H',
            '12h' => 'candle12H',
            '1d' => 'candle1D',
        ];
    }
    public function getSymbolInfos()
    {
        $res = $this->api->instruments(["instType" => "SWAP"]);
        if ($res['code'] != '0') {
            return [];
        }
        return $res['data'];
    }
    public function stopListen()
    {
        $this->wsAcc->close();
        $this->wsKline->close();
    }
}