CmBroker.php 7.8 KB
<?php

namespace trader;

require_once __DIR__ . '/struct/ApiInfo.php';
require_once __DIR__ . '/exchange/okx/ExBroker.php';
require_once __DIR__ . '/exchange/binance/ExBroker.php';
require_once __DIR__ . '/struct/Kline.php';
require_once __DIR__ . '/struct/Order.php';
require_once __DIR__ . '/struct/WsData.php';
require_once __DIR__ . '/struct/WsDataTrade.php';
require_once __DIR__ . '/struct/WsDataPos.php';
require_once __DIR__ . '/struct/SymbolInfo.php';
require_once __DIR__ . '/struct/WsDataOrder.php';
require_once __DIR__ . '/struct/WsDataAccount.php';
require_once __DIR__ . '/struct/Pos.php';
require_once __DIR__ . '/../../jytools/func.php';
require_once __DIR__ . '/../../jytools/Websocket.php';


use trader\struct\ApiInfo;
use trader\okx\ExBroker as OkxBroker;
use trader\binance\ExBroker as BinanceBroker;
use trader\struct\Kline;
use trader\struct\Order;
use trader\struct\WsData;
use trader\struct\WsDataTrade;
use trader\struct\WsDataPos;
use trader\struct\SymbolInfo;
use trader\struct\WsDataOrder;
use trader\struct\WsDataAccount;
use trader\struct\Pos;
use \Exception;
use function jytools\timeFormat;

class CmBroker
{
    private $plat;
    private ?OkxBroker $exBroker;
    /** @var SymbolInfo[] $symbolInfos */
    public $symbolInfos = [];
    /** @var Pos[] $positions */
    private $positions = [];
    private $name = '';


    public function __construct($plat, ApiInfo $apiInfo)
    {
        $this->plat = $plat;
        $exBroker = null;
        if ($plat == 'okx') {
            $exBroker = new OkxBroker($apiInfo);
        }
        if ($plat == 'binance') {
            $exBroker = new BinanceBroker($apiInfo);
        }
        $this->exBroker = $exBroker;
    }
    public function setName($name)
    {
        $this->name = $name;
    }
    public function init()
    {
        $this->initSymbolInfos();
    }
    public function accListen(callable $onData)
    {
        $this->exBroker->accListen(function ($data) use ($onData) {
            // output("ws 有效数据", $data);
            if ($this->plat == 'binance') {
                $this->msg("binance 无处理ws数据实现");
            }
            if ($this->plat == 'okx') {
                if (isset($data['arg']) && $data['arg']['channel'] == 'orders') {
                    foreach ($data['data'] as $key => $value) {
                        $wsDataTrade = WsDataTrade::TransferOkxOrder($value, $this->symbolInfos, function ($symbol) {
                            return $this->getSymbolSt($symbol);
                        });
                        if ($wsDataTrade != null) {
                            $wsData = new WsData($this->plat, 'trade', $trade = $wsDataTrade);
                            $onData($wsData);
                        }
                        $wsDataOrd = WsDataOrder::TransferOkxOrder($value, $this->symbolInfos, function ($symbol) {
                            return $this->getSymbolSt($symbol);
                        });
                        if ($wsDataOrd != null) {
                            $wsData = new WsData($this->plat, 'order', $trade = null, $pos = null, $order = $wsDataOrd);
                            $onData($wsData);
                        }
                    }
                    return;
                }
                if (isset($data['arg']) && $data['arg']['channel'] == 'positions') {
                    $positions = [];
                    foreach ($data['data'] as $key => $value) {
                        $wsDataPos = WsDataPos::TransferOkxPos($value, $this->symbolInfos, function ($symbol) {
                            return $this->getSymbolSt($symbol);
                        });
                        if ($wsDataPos) {
                            $pos = Pos::transferWsDataPos($wsDataPos);
                            $positions[$wsDataPos->symbol . "_" . $wsDataPos->posSide] = $pos;
                            $wsData = new WsData($this->plat, 'pos', $trade = null, $pos = $wsDataPos);
                            $onData($wsData);
                        }
                    }
                    $this->positions = $positions;
                    return;
                }
                if (isset($data['arg']) && $data['arg']['channel'] == 'account') {
                    $wsDataAccount = WsDataAccount::TransferOkxAccount($data['data']);
                    $wsData = new WsData($this->plat, 'account', $trade = null, $pos = null, $order = null, $account = $wsDataAccount);
                    $onData($wsData);
                    return;
                }
                $this->msg("okx 无处理ws数据实现", $data);
            }
        });
    }
    public function klineListen($symbol, $peroid, $onData)
    {
        $symbol = $this->getSymbolOri($symbol, $this->plat);
        $this->exBroker->klineListen($symbol, $peroid, function ($data) use ($onData) {
            $kline = Kline::transferOkx($data);
            $onData($kline);
        });
    }
    //转换为标准交易对
    public function getSymbolSt($symbol)
    {
        $symbol = str_replace('-USDT-SWAP', 'USDT',  $symbol);
        if (preg_match('/^[A-Z0-9]+USDT$/', $symbol)) {
            return $symbol;
        } else {
            throw new Exception('转换标准交易对错误' + $symbol + ' to ', $symbol);
        }
    }

    //转换为原始交易对
    public function getSymbolOri($symbol, $platTarget)
    {
        $symbolSt = $this->getSymbolSt($symbol);
        if ($platTarget == 'binance') {
            return $symbolSt;
        }
        if ($platTarget == 'okx') {
            return str_replace('USDT', '-USDT-SWAP', $symbolSt);
        }
        throw new Exception('平台错误' + $platTarget);
    }

    public function placeOrder(Order $order)
    {
        if ($this->plat == 'okx') {
            $orderOri = $order->toOkxOrder($this->symbolInfos, function ($symbol) {
                return $this->getSymbolOri($symbol, $this->plat);
            });
            $this->msg("下单", $orderOri);
            if ($orderOri['sz'] == 0) {
                $this->msg("下单数量为0,不下单", $orderOri);
                return ["code" => 1, "msg" => "下单数量为0,不下单"];
            }
            $res = $this->exBroker->placeOrder($orderOri);
            $this->msg("下单结果", $res);
            return $res;
        }
    }

    public function msg()
    {
        $args = func_get_args();
        $outStr = '[' . timeFormat('ms') . ']:' . $this->name . ": ";
        foreach ($args as $key => $value) {
            $value = is_array($value) ? json_encode($value) : $value;
            if (is_bool($value)) {
                $value = $value ? 'bool:true' : 'bool:false';
            }
            $outStr .= count($args) - $key > 1 ? $value . '  ' : $value;
        }
        echo $outStr . PHP_EOL;
    }
    public function getPosQty($symbol, $posSide)
    {
        $key = $symbol . "_" . $posSide;
        if (!isset($this->positions[$key])) {
            return 0;
        }
        /** @var Pos $pos */
        $pos = $this->positions[$key];
        return abs($pos->qty);
    }
    private function initSymbolInfos()
    {
        if ($this->plat == 'okx') {
            //获取所有USDT SWAP 交易对
            $res = $this->exBroker->getSymbolInfos();
            $infos = [];
            foreach ($res as $key => $value) {
                if ($value["settleCcy"] == "USDT") {
                    $info = SymbolInfo::transferOkx($value, function ($symbol) {
                        return $this->getSymbolSt($symbol);
                    });
                    $infos[$info->symbol] = $info;
                }
            }
            $this->symbolInfos = $infos;
        }
        //定时10m刷新
        swoole_timer_after(1000 * 60 * 10, function () {
            $this->initSymbolInfos();
        });
    }
    public function stopListen()
    {
        $this->exBroker->stopListen();
    }
}