ExBroker.php 6.3 KB
<?php

namespace trader\exchange\bitget;

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

use trader\struct\ApiInfo;
use trader\exchange\bitget\Api as BgApi;
use Jiaoyin\Websocket;
use function Jiaoyin\output;
use function Jiaoyin\getMicrotime;

class ExBroker
{
    private $host = 'wss://ws.bitget.com';
    private $pathPri = '/v2/ws/private'; //账户信息path
    private $pathPub = '/v2/ws/public'; //行情信息path
    private ?ApiInfo $apiInfo;
    private BgApi $api;
    private ?Websocket $wsAcc;
    private ?Websocket $wsKline;
    private $timerAccPing = 0;
    private $timerKlinePing = 0;

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

    public function setWsHost($host)
    {
        $this->host = $host;
    }

    public function setRestHost($host)
    {
        $this->api->setHost($host);
    }

    public function accListen(callable $onData)
    {
        if (isset($this->wsAcc)) {
            $this->wsAcc->close();
        }
        $this->wsAcc = new Websocket($this->host . $this->pathPri);
        $this->wsAcc->connect(
            function () {
                $this->wsLogin();
                $this->wsAccPing();
            },
            function ($data) use ($onData) {
                $this->onWsDataPre($data, $onData);
            },
            function () {
                swoole_timer_clear($this->timerAccPing);
            }
        );
    }

    public function klineListen($symbol, $interval, callable $onData)
    {
        if (isset($this->wsKline)) {
            $this->wsKline->close();
        }
        $this->wsKline = new Websocket($this->host . $this->pathPub);
        $this->wsKline->connect(
            function () use ($symbol, $interval) {
                $this->wsKline->push(json_encode([
                    'op' => 'subscribe',
                    'args' => [
                        [
                            'instType' => 'USDT-FUTURES',
                            'channel' => $interval,
                            'instId' => $symbol
                        ]
                    ]
                ]));
                $this->wsKlinePing();
            },
            function ($data) use ($onData) {
                $data = json_decode($data, true);
                if (isset($data['data'])) {
                    $onData($data);
                }
            },
            function () {
                swoole_timer_clear($this->timerKlinePing);
            }
        );
    }

    private function wsKlinePing()
    {
        $this->timerKlinePing = swoole_timer_tick(1000 * 20, function () {
            $this->wsKline->push('ping');
        });
    }

    private function wsAccPing()
    {
        $this->timerAccPing = swoole_timer_tick(1000 * 20, function () {
            $this->wsAcc->push('ping');
        });
    }

    private function createWsSign($timestamp)
    {
        $message = $timestamp . 'GET' . '/user/verify';
        return base64_encode(hash_hmac('sha256', $message, $this->apiInfo->secret, true));
    }

    private function wsLogin()
    {
        $timestamp = (string)(getMicrotime());
        $this->wsAcc->push(json_encode([
            'op' => 'login',
            'args' => [
                [
                    'apiKey' => $this->apiInfo->key,
                    'passphrase' => $this->apiInfo->passphrase,
                    'timestamp' => $timestamp,
                    'sign' => $this->createWsSign($timestamp)
                ]
            ]
        ]));
    }

    private function onWsDataPre($data, callable $onWsData)
    {
        if ($data == 'pong') {
            return;
        }
        $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') {
                return;
            }
            output('bitget ws 未处理数据', $data);
        }
        call_user_func($onWsData, $data);
    }

    private function wsSubscribe()
    {
        $this->wsAcc->push(json_encode([
            'op' => 'subscribe',
            'args' => [
                [
                    "instType" => "USDT-FUTURES",
                    "channel" => "positions",
                    "instId" => "default",
                ],
                [
                    "instType" => "USDT-FUTURES",
                    "channel" => "account",
                    "coin" => "default"
                ],
                [
                    "instType" => "USDT-FUTURES",
                    "channel" => "orders",
                    "instId" => "default"
                ],
                [
                    "instType" => "USDT-FUTURES",
                    "channel" => "fill",
                    "instId" => "default"
                ],
            ]
        ]));
    }

    public function placeOrder(array $params)
    {
        return $this->api->placeOrder($params);
    }

    public function setLever($symbol, $lever)
    {
        $res = $this->api->setLeverage([
            'symbol' => $symbol,
            'leverage' => $lever,
            'productType' => 'USDT-FUTURES',
            'marginCoin' => 'USDT'
        ]);
        if (!isset($res['code']) || $res['code'] != '00000') {
            output('bitget设置杠杆失败', $res);
        } else {
            output('bitget设置杠杆成功', $res);
        }
        return $res;
    }

    public function getAllPos()
    {
        return $this->api->getPositions(['productType' => 'SPOT']);
    }
    public function getSymbolInfos()
    {
        $res = $this->api->instruments([
            'productType' => 'USDT-FUTURES'
        ]);
        if (!isset($res['data'])) {
            output('bitget获取所有交易对信息失败');
            return [];
        }
        return $res['data'];
    }
    public function stopListen()
    {
        if (isset($this->wsAcc)) {
            $this->wsAcc->close();
        }
        if (isset($this->wsKline)) {
            $this->wsKline->close();
        }
    }
}