正在显示
9 个修改的文件
包含
556 行增加
和
27 行删除
examples/bgBroker.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +require_once __DIR__ . '/../trader/CmBroker.php'; | ||
| 4 | +require_once __DIR__ . '/../trader/struct/ApiInfo.php'; | ||
| 5 | +require_once __DIR__ . '/../jytools/BinanceFutures.php'; | ||
| 6 | +require_once __DIR__ . '/../jytools/func.php'; | ||
| 7 | + | ||
| 8 | +use trader\struct\ApiInfo; | ||
| 9 | +use trader\struct\WsData; | ||
| 10 | +use trader\CmBroker; | ||
| 11 | +use function jytools\output; | ||
| 12 | + | ||
| 13 | +$key = "bg_21bd365eaecaaa165a6167211785e2c0"; | ||
| 14 | +$secret = "d850a0b1e344157d91b0d2585805e3c8d92f0f61d867a11aaf2569f3a8587991"; | ||
| 15 | +$passphrase = "Z1345zbnm"; | ||
| 16 | +$apiInfo = new ApiInfo($key, $secret, $passphrase); | ||
| 17 | +$wsHost = "ws://bgws.keetu.com"; | ||
| 18 | +$restHost = "http://bgapi.keetu.com"; | ||
| 19 | +$broker = new CmBroker(CmBroker::PLAT_BITGET, $apiInfo, $wsHost, $restHost); | ||
| 20 | + | ||
| 21 | +output("开始监听"); | ||
| 22 | +// $broker->accListen(function (WsData $data) { | ||
| 23 | +// if ($data->dataType == "order") { | ||
| 24 | +// var_dump($data); | ||
| 25 | +// } | ||
| 26 | +// }); | ||
| 27 | +$broker->setLever("BTCUSDT", 20); | ||
| 28 | +// $broker->klineListen("BTCUSDT", "1m", function ($data) { | ||
| 29 | +// output($data->toArray()); | ||
| 30 | +// }); |
| @@ -6,6 +6,7 @@ require_once __DIR__ . '/struct/ApiInfo.php'; | @@ -6,6 +6,7 @@ require_once __DIR__ . '/struct/ApiInfo.php'; | ||
| 6 | require_once __DIR__ . '/exchange/okx/ExBroker.php'; | 6 | require_once __DIR__ . '/exchange/okx/ExBroker.php'; |
| 7 | require_once __DIR__ . '/exchange/binance/ExBroker.php'; | 7 | require_once __DIR__ . '/exchange/binance/ExBroker.php'; |
| 8 | require_once __DIR__ . '/exchange/bybit/ExBroker.php'; | 8 | require_once __DIR__ . '/exchange/bybit/ExBroker.php'; |
| 9 | +require_once __DIR__ . '/exchange/bitget/ExBroker.php'; | ||
| 9 | require_once __DIR__ . '/struct/Kline.php'; | 10 | require_once __DIR__ . '/struct/Kline.php'; |
| 10 | require_once __DIR__ . '/struct/Order.php'; | 11 | require_once __DIR__ . '/struct/Order.php'; |
| 11 | require_once __DIR__ . '/struct/WsData.php'; | 12 | require_once __DIR__ . '/struct/WsData.php'; |
| @@ -23,6 +24,7 @@ use trader\struct\ApiInfo; | @@ -23,6 +24,7 @@ use trader\struct\ApiInfo; | ||
| 23 | use trader\exchange\okx\ExBroker as OkxBroker; | 24 | use trader\exchange\okx\ExBroker as OkxBroker; |
| 24 | use trader\exchange\binance\ExBroker as BinanceBroker; | 25 | use trader\exchange\binance\ExBroker as BinanceBroker; |
| 25 | use trader\exchange\bybit\ExBroker as BybitBroker; | 26 | use trader\exchange\bybit\ExBroker as BybitBroker; |
| 27 | +use trader\exchange\bitget\ExBroker as BitgetBroker; | ||
| 26 | use trader\struct\Kline; | 28 | use trader\struct\Kline; |
| 27 | use trader\struct\Order; | 29 | use trader\struct\Order; |
| 28 | use trader\struct\WsData; | 30 | use trader\struct\WsData; |
| @@ -52,7 +54,7 @@ class CmBroker | @@ -52,7 +54,7 @@ class CmBroker | ||
| 52 | * @see PLAT_BITGET | 54 | * @see PLAT_BITGET |
| 53 | */ | 55 | */ |
| 54 | public $plat; | 56 | public $plat; |
| 55 | - private OkxBroker|BinanceBroker|BybitBroker $exBroker; | 57 | + private OkxBroker|BinanceBroker|BybitBroker|BitgetBroker $exBroker; |
| 56 | /** @var SymbolInfo[] $symbolInfos */ | 58 | /** @var SymbolInfo[] $symbolInfos */ |
| 57 | public $symbolInfos = []; | 59 | public $symbolInfos = []; |
| 58 | /** @var Pos[] $positions */ | 60 | /** @var Pos[] $positions */ |
| @@ -75,14 +77,16 @@ class CmBroker | @@ -75,14 +77,16 @@ class CmBroker | ||
| 75 | if ($plat == self::PLAT_BYBIT) { | 77 | if ($plat == self::PLAT_BYBIT) { |
| 76 | $exBroker = new BybitBroker($apiInfo); | 78 | $exBroker = new BybitBroker($apiInfo); |
| 77 | } | 79 | } |
| 80 | + if ($plat == self::PLAT_BITGET) { | ||
| 81 | + $exBroker = new BitgetBroker($apiInfo); | ||
| 82 | + } | ||
| 78 | $this->exBroker = $exBroker; | 83 | $this->exBroker = $exBroker; |
| 79 | - if ($wsHost) { | 84 | + if ($wsHost && $wsHost != "") { |
| 80 | $this->exBroker->setWsHost($wsHost); | 85 | $this->exBroker->setWsHost($wsHost); |
| 81 | } | 86 | } |
| 82 | - if ($restHost) { | 87 | + if ($restHost && $restHost != "") { |
| 83 | $this->exBroker->setRestHost($restHost); | 88 | $this->exBroker->setRestHost($restHost); |
| 84 | } | 89 | } |
| 85 | - var_dump("初始化。。。。"); | ||
| 86 | $this->init(); | 90 | $this->init(); |
| 87 | } | 91 | } |
| 88 | public function setName($name) | 92 | public function setName($name) |
| @@ -110,6 +114,9 @@ class CmBroker | @@ -110,6 +114,9 @@ class CmBroker | ||
| 110 | if ($this->plat == self::PLAT_BYBIT) { | 114 | if ($this->plat == self::PLAT_BYBIT) { |
| 111 | $this->bybitAccDataHandle($data, $onData); | 115 | $this->bybitAccDataHandle($data, $onData); |
| 112 | } | 116 | } |
| 117 | + if ($this->plat == self::PLAT_BITGET) { | ||
| 118 | + $this->bitgetAccDataHandle($data, $onData); | ||
| 119 | + } | ||
| 113 | }); | 120 | }); |
| 114 | } | 121 | } |
| 115 | //处理币安相关账户数据监听 | 122 | //处理币安相关账户数据监听 |
| @@ -248,6 +255,52 @@ class CmBroker | @@ -248,6 +255,52 @@ class CmBroker | ||
| 248 | return; | 255 | return; |
| 249 | } | 256 | } |
| 250 | } | 257 | } |
| 258 | + //处理bitget账户相关数据监听 | ||
| 259 | + private function bitgetAccDataHandle($data, $onData) | ||
| 260 | + { | ||
| 261 | + // 处理订单和成交数据 | ||
| 262 | + if (isset($data['arg']) && $data['arg']['channel'] == 'orders') { | ||
| 263 | + foreach ($data['data'] as $value) { | ||
| 264 | + // 处理成交数据 | ||
| 265 | + $wsDataTrade = WsDataTrade::TransferBitgetOrder($value, $this->symbolInfos, function ($symbol) { | ||
| 266 | + return $this->getSymbolSt($symbol); | ||
| 267 | + }); | ||
| 268 | + if ($wsDataTrade) { | ||
| 269 | + $wsData = new WsData($this->plat, 'trade', $wsDataTrade); | ||
| 270 | + $onData($wsData); | ||
| 271 | + } | ||
| 272 | + | ||
| 273 | + // 处理订单数据 | ||
| 274 | + $wsDataOrder = WsDataOrder::TransferBitgetOrder($value, $this->symbolInfos, function ($symbol) { | ||
| 275 | + return $this->getSymbolSt($symbol); | ||
| 276 | + }); | ||
| 277 | + if ($wsDataOrder) { | ||
| 278 | + $wsData = new WsData($this->plat, 'order', null, null, $wsDataOrder); | ||
| 279 | + $onData($wsData); | ||
| 280 | + } | ||
| 281 | + } | ||
| 282 | + return; | ||
| 283 | + } | ||
| 284 | + | ||
| 285 | + // 处理账户和持仓数据 | ||
| 286 | + if (isset($data['arg']) && $data['arg']['channel'] == 'account') { | ||
| 287 | + foreach ($data['data'] as $value) { | ||
| 288 | + if (isset($value['positions'])) { | ||
| 289 | + foreach ($value['positions'] as $pos) { | ||
| 290 | + $wsDataPos = WsDataPos::TransferBitgetPos($pos, $this->symbolInfos, function ($symbol) { | ||
| 291 | + return $this->getSymbolSt($symbol); | ||
| 292 | + }); | ||
| 293 | + if ($wsDataPos) { | ||
| 294 | + $this->positions[$wsDataPos->symbol . "_" . $wsDataPos->posSide] = $wsDataPos; | ||
| 295 | + $wsData = new WsData($this->plat, 'pos', null, $wsDataPos); | ||
| 296 | + $onData($wsData); | ||
| 297 | + } | ||
| 298 | + } | ||
| 299 | + } | ||
| 300 | + } | ||
| 301 | + return; | ||
| 302 | + } | ||
| 303 | + } | ||
| 251 | public function klineListen($symbol, $peroid, $onData) | 304 | public function klineListen($symbol, $peroid, $onData) |
| 252 | { | 305 | { |
| 253 | if ($this->plat == self::PLAT_BINANCE && $peroid == '1s') { | 306 | if ($this->plat == self::PLAT_BINANCE && $peroid == '1s') { |
| @@ -266,6 +319,9 @@ class CmBroker | @@ -266,6 +319,9 @@ class CmBroker | ||
| 266 | if ($this->plat == self::PLAT_BYBIT) { | 319 | if ($this->plat == self::PLAT_BYBIT) { |
| 267 | $kline = Kline::transferBybit($data); | 320 | $kline = Kline::transferBybit($data); |
| 268 | } | 321 | } |
| 322 | + if ($this->plat == self::PLAT_BITGET) { | ||
| 323 | + $kline = Kline::transferBitget($data); | ||
| 324 | + } | ||
| 269 | $onData($kline); | 325 | $onData($kline); |
| 270 | }); | 326 | }); |
| 271 | } | 327 | } |
| @@ -455,6 +511,19 @@ class CmBroker | @@ -455,6 +511,19 @@ class CmBroker | ||
| 455 | } | 511 | } |
| 456 | $this->symbolInfos = $infos; | 512 | $this->symbolInfos = $infos; |
| 457 | } | 513 | } |
| 514 | + if ($this->plat == self::PLAT_BITGET) { | ||
| 515 | + $res = $this->exBroker->getSymbolInfos(); | ||
| 516 | + $infos = []; | ||
| 517 | + foreach ($res as $value) { | ||
| 518 | + if ($value['quoteCoin'] == 'USDT' && $value['symbolStatus'] == 'normal') { | ||
| 519 | + $info = SymbolInfo::transferBitget($value, function ($symbol) { | ||
| 520 | + return $this->getSymbolSt($symbol); | ||
| 521 | + }); | ||
| 522 | + $infos[$info->symbol] = $info; | ||
| 523 | + } | ||
| 524 | + } | ||
| 525 | + $this->symbolInfos = $infos; | ||
| 526 | + } | ||
| 458 | if (count($this->symbolInfos) == 0) { | 527 | if (count($this->symbolInfos) == 0) { |
| 459 | $this->initSymbolInfos($count + 1); | 528 | $this->initSymbolInfos($count + 1); |
| 460 | return; | 529 | return; |
| @@ -4,6 +4,7 @@ namespace trader\exchange\bitget; | @@ -4,6 +4,7 @@ namespace trader\exchange\bitget; | ||
| 4 | 4 | ||
| 5 | require_once __DIR__ . '/../../struct/ApiInfo.php'; | 5 | require_once __DIR__ . '/../../struct/ApiInfo.php'; |
| 6 | require_once __DIR__ . '/../../../jytools/func.php'; | 6 | require_once __DIR__ . '/../../../jytools/func.php'; |
| 7 | +require_once __DIR__ . '/../../../jytools/Curl.php'; | ||
| 7 | 8 | ||
| 8 | use trader\struct\ApiInfo; | 9 | use trader\struct\ApiInfo; |
| 9 | use jytools\Curl; | 10 | use jytools\Curl; |
| @@ -11,14 +12,105 @@ use function jytools\getMicrotime; | @@ -11,14 +12,105 @@ use function jytools\getMicrotime; | ||
| 11 | 12 | ||
| 12 | class Api | 13 | class Api |
| 13 | { | 14 | { |
| 14 | - private $host = ''; | 15 | + private $host = 'https://api.bitget.com'; |
| 15 | private ApiInfo $apiInfo; | 16 | private ApiInfo $apiInfo; |
| 17 | + | ||
| 16 | public function __construct(ApiInfo $apiInfo) | 18 | public function __construct(ApiInfo $apiInfo) |
| 17 | { | 19 | { |
| 18 | $this->apiInfo = $apiInfo; | 20 | $this->apiInfo = $apiInfo; |
| 19 | } | 21 | } |
| 22 | + | ||
| 20 | public function setHost($host) | 23 | public function setHost($host) |
| 21 | { | 24 | { |
| 22 | $this->host = $host; | 25 | $this->host = $host; |
| 23 | } | 26 | } |
| 27 | + | ||
| 28 | + //----------- public interface ------------ | ||
| 29 | + // 获取交易对信息 | ||
| 30 | + public function instruments($param) | ||
| 31 | + { | ||
| 32 | + $path = '/api/v2/mix/market/contracts'; | ||
| 33 | + $method = 'GET'; | ||
| 34 | + return $this->request($path, $method, $param); | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + // 获取K线数据 | ||
| 38 | + public function klines($param) | ||
| 39 | + { | ||
| 40 | + $path = '/api/v2/spot/market/candles'; | ||
| 41 | + $method = 'GET'; | ||
| 42 | + return $this->request($path, $method, $param); | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + //----------- private interface ------------ | ||
| 46 | + // 下单 | ||
| 47 | + public function placeOrder($param) | ||
| 48 | + { | ||
| 49 | + $path = '/api/v2/spot/trade/place-order'; | ||
| 50 | + $method = 'POST'; | ||
| 51 | + return $this->request($path, $method, $param, true); | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + // 查询持仓 | ||
| 55 | + public function getPositions($param) | ||
| 56 | + { | ||
| 57 | + $path = '/api/v2/mix/position/positions'; | ||
| 58 | + $method = 'GET'; | ||
| 59 | + return $this->request($path, $method, $param, true); | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + // 设置杠杆 | ||
| 63 | + public function setLeverage($param) | ||
| 64 | + { | ||
| 65 | + $path = '/api/v2/mix/account/set-leverage'; | ||
| 66 | + $method = 'POST'; | ||
| 67 | + return $this->request($path, $method, $param, true); | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + // 查询账户余额 | ||
| 71 | + public function accountBalance($param) | ||
| 72 | + { | ||
| 73 | + $path = '/api/v2/spot/account/assets'; | ||
| 74 | + $method = 'GET'; | ||
| 75 | + return $this->request($path, $method, $param, true); | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + //------------------------------------- | ||
| 79 | + private function createSign($timestamp, $method, $requestPath, $body = '') | ||
| 80 | + { | ||
| 81 | + $message = $timestamp . $method . $requestPath . $body; | ||
| 82 | + return base64_encode(hash_hmac('sha256', $message, $this->apiInfo->secret, true)); | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + private function request($path, $method, $param, $auth = false) | ||
| 86 | + { | ||
| 87 | + $header = []; | ||
| 88 | + $body = ''; | ||
| 89 | + $fullPath = $path; | ||
| 90 | + | ||
| 91 | + if ($method == 'GET' && !empty($param)) { | ||
| 92 | + $fullPath = $path . '?' . http_build_query($param); | ||
| 93 | + } else { | ||
| 94 | + $header[] = 'Content-Type: application/json'; | ||
| 95 | + $body = json_encode($param); | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + if ($auth) { | ||
| 99 | + $timestamp = (string)getMicrotime(); | ||
| 100 | + $header[] = 'ACCESS-KEY: ' . $this->apiInfo->key; | ||
| 101 | + $header[] = 'ACCESS-TIMESTAMP: ' . $timestamp; | ||
| 102 | + $header[] = 'ACCESS-PASSPHRASE: ' . $this->apiInfo->passphrase; | ||
| 103 | + $header[] = 'ACCESS-SIGN: ' . $this->createSign($timestamp, $method, $fullPath, $body); | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + $url = $this->host . $path; | ||
| 107 | + $result = ""; | ||
| 108 | + if ($method == 'POST') { | ||
| 109 | + $result = Curl::httpPost($url, $param, $header); | ||
| 110 | + } else { | ||
| 111 | + $result = Curl::httpGet($url, $param, $header); | ||
| 112 | + } | ||
| 113 | + | ||
| 114 | + return json_decode($result, true); | ||
| 115 | + } | ||
| 24 | } | 116 | } |
trader/exchange/bitget/ExBroker.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +namespace trader\exchange\bitget; | ||
| 4 | + | ||
| 5 | +require_once __DIR__ . '/../../struct/ApiInfo.php'; | ||
| 6 | +require_once __DIR__ . '/Api.php'; | ||
| 7 | +require_once __DIR__ . '/../../../jytools/Websocket.php'; | ||
| 8 | +require_once __DIR__ . '/../../../jytools/func.php'; | ||
| 9 | + | ||
| 10 | +use trader\struct\ApiInfo; | ||
| 11 | +use trader\exchange\bitget\Api as BgApi; | ||
| 12 | +use jytools\Websocket; | ||
| 13 | +use function jytools\output; | ||
| 14 | +use function jytools\getMicrotime; | ||
| 15 | + | ||
| 16 | +class ExBroker | ||
| 17 | +{ | ||
| 18 | + private $host = 'wss://ws.bitget.com'; | ||
| 19 | + private $pathPri = '/v2/ws/private'; //账户信息path | ||
| 20 | + private $pathPub = '/v2/ws/public'; //行情信息path | ||
| 21 | + private ApiInfo $apiInfo; | ||
| 22 | + private BgApi $api; | ||
| 23 | + private ?Websocket $wsAcc; | ||
| 24 | + private ?Websocket $wsKline; | ||
| 25 | + private $timerAccPing = 0; | ||
| 26 | + private $timerKlinePing = 0; | ||
| 27 | + | ||
| 28 | + public function __construct(ApiInfo $apiInfo) | ||
| 29 | + { | ||
| 30 | + $this->apiInfo = $apiInfo; | ||
| 31 | + $this->api = new BgApi($apiInfo); | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + public function setWsHost($host) | ||
| 35 | + { | ||
| 36 | + $this->host = $host; | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + public function setRestHost($host) | ||
| 40 | + { | ||
| 41 | + $this->api->setHost($host); | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + public function accListen(callable $onData) | ||
| 45 | + { | ||
| 46 | + if (isset($this->wsAcc)) { | ||
| 47 | + $this->wsAcc->close(); | ||
| 48 | + } | ||
| 49 | + $this->wsAcc = new Websocket($this->host . $this->pathPri); | ||
| 50 | + $this->wsAcc->connect( | ||
| 51 | + function () { | ||
| 52 | + $this->wsLogin(); | ||
| 53 | + $this->wsAccPing(); | ||
| 54 | + }, | ||
| 55 | + function ($data) use ($onData) { | ||
| 56 | + $this->onWsDataPre($data, $onData); | ||
| 57 | + }, | ||
| 58 | + function () { | ||
| 59 | + swoole_timer_clear($this->timerAccPing); | ||
| 60 | + } | ||
| 61 | + ); | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + public function klineListen($symbol, $interval, callable $onData) | ||
| 65 | + { | ||
| 66 | + if (isset($this->wsKline)) { | ||
| 67 | + $this->wsKline->close(); | ||
| 68 | + } | ||
| 69 | + $this->wsKline = new Websocket($this->host . $this->pathPub); | ||
| 70 | + $this->wsKline->connect( | ||
| 71 | + function () use ($symbol, $interval) { | ||
| 72 | + $this->wsKline->push(json_encode([ | ||
| 73 | + 'op' => 'subscribe', | ||
| 74 | + 'args' => [ | ||
| 75 | + [ | ||
| 76 | + 'instType' => 'SPOT', | ||
| 77 | + 'channel' => 'candle' . $interval, | ||
| 78 | + 'instId' => $symbol | ||
| 79 | + ] | ||
| 80 | + ] | ||
| 81 | + ])); | ||
| 82 | + $this->wsKlinePing(); | ||
| 83 | + }, | ||
| 84 | + function ($data) use ($onData) { | ||
| 85 | + $data = json_decode($data, true); | ||
| 86 | + if (isset($data['data'])) { | ||
| 87 | + $onData($data); | ||
| 88 | + } | ||
| 89 | + }, | ||
| 90 | + function () { | ||
| 91 | + swoole_timer_clear($this->timerKlinePing); | ||
| 92 | + } | ||
| 93 | + ); | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + private function wsKlinePing() | ||
| 97 | + { | ||
| 98 | + $this->timerKlinePing = swoole_timer_tick(1000 * 20, function () { | ||
| 99 | + $this->wsKline->push('ping'); | ||
| 100 | + }); | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + private function wsAccPing() | ||
| 104 | + { | ||
| 105 | + $this->timerAccPing = swoole_timer_tick(1000 * 20, function () { | ||
| 106 | + $this->wsAcc->push('ping'); | ||
| 107 | + }); | ||
| 108 | + } | ||
| 109 | + | ||
| 110 | + private function createWsSign($timestamp) | ||
| 111 | + { | ||
| 112 | + $message = $timestamp . 'GET' . '/user/verify'; | ||
| 113 | + return base64_encode(hash_hmac('sha256', $message, $this->apiInfo->secret, true)); | ||
| 114 | + } | ||
| 115 | + | ||
| 116 | + private function wsLogin() | ||
| 117 | + { | ||
| 118 | + $timestamp = (string)(getMicrotime() / 1000); | ||
| 119 | + $this->wsAcc->push(json_encode([ | ||
| 120 | + 'op' => 'login', | ||
| 121 | + 'args' => [ | ||
| 122 | + [ | ||
| 123 | + 'apiKey' => $this->apiInfo->key, | ||
| 124 | + 'passphrase' => $this->apiInfo->passphrase, | ||
| 125 | + 'timestamp' => $timestamp, | ||
| 126 | + 'sign' => $this->createWsSign($timestamp) | ||
| 127 | + ] | ||
| 128 | + ] | ||
| 129 | + ])); | ||
| 130 | + } | ||
| 131 | + | ||
| 132 | + private function onWsDataPre($data, callable $onWsData) | ||
| 133 | + { | ||
| 134 | + if ($data == 'pong') { | ||
| 135 | + return; | ||
| 136 | + } | ||
| 137 | + $data = json_decode($data, true); | ||
| 138 | + if (isset($data['event'])) { | ||
| 139 | + if ($data['event'] == 'login' && $data['code'] == '0') { | ||
| 140 | + output('ws登录成功'); | ||
| 141 | + $this->wsSubscribe(); | ||
| 142 | + return; | ||
| 143 | + } | ||
| 144 | + if ($data['event'] == 'subscribe') { | ||
| 145 | + return; | ||
| 146 | + } | ||
| 147 | + output('bitget ws 未处理数据', $data); | ||
| 148 | + } | ||
| 149 | + call_user_func($onWsData, $data); | ||
| 150 | + } | ||
| 151 | + | ||
| 152 | + private function wsSubscribe() | ||
| 153 | + { | ||
| 154 | + $this->wsAcc->push(json_encode([ | ||
| 155 | + 'op' => 'subscribe', | ||
| 156 | + 'args' => [ | ||
| 157 | + [ | ||
| 158 | + 'instType' => 'SPOT', | ||
| 159 | + 'channel' => 'account', | ||
| 160 | + 'instId' => 'default' | ||
| 161 | + ], | ||
| 162 | + [ | ||
| 163 | + 'instType' => 'SPOT', | ||
| 164 | + 'channel' => 'orders', | ||
| 165 | + 'instId' => 'default' | ||
| 166 | + ] | ||
| 167 | + ] | ||
| 168 | + ])); | ||
| 169 | + } | ||
| 170 | + | ||
| 171 | + public function placeOrder(array $params) | ||
| 172 | + { | ||
| 173 | + return $this->api->placeOrder($params); | ||
| 174 | + } | ||
| 175 | + | ||
| 176 | + public function setLever($symbol, $lever) | ||
| 177 | + { | ||
| 178 | + $res = $this->api->setLeverage([ | ||
| 179 | + 'symbol' => $symbol, | ||
| 180 | + 'leverage' => $lever, | ||
| 181 | + 'productType' => 'USDT-FUTURES', | ||
| 182 | + 'marginCoin' => 'USDT' | ||
| 183 | + ]); | ||
| 184 | + if (!isset($res['code']) || $res['code'] != '00000') { | ||
| 185 | + output('bitget设置杠杆失败', $res); | ||
| 186 | + } else { | ||
| 187 | + output('bitget设置杠杆成功', $res); | ||
| 188 | + } | ||
| 189 | + return $res; | ||
| 190 | + } | ||
| 191 | + | ||
| 192 | + public function getAllPos() | ||
| 193 | + { | ||
| 194 | + return $this->api->getPositions(['productType' => 'SPOT']); | ||
| 195 | + } | ||
| 196 | + public function getSymbolInfos() | ||
| 197 | + { | ||
| 198 | + $res = $this->api->instruments([ | ||
| 199 | + 'productType' => 'USDT-FUTURES' | ||
| 200 | + ]); | ||
| 201 | + if (!isset($res['data'])) { | ||
| 202 | + output('bitget获取所有交易对信息失败'); | ||
| 203 | + return []; | ||
| 204 | + } | ||
| 205 | + return $res['data']; | ||
| 206 | + } | ||
| 207 | +} |
| @@ -85,4 +85,18 @@ class Kline | @@ -85,4 +85,18 @@ class Kline | ||
| 85 | $isFinal = $kline['confirm']; | 85 | $isFinal = $kline['confirm']; |
| 86 | return new Kline($time, $open, $high, $low, $close, $vol, $volQuote, $uts, $isFinal); | 86 | return new Kline($time, $open, $high, $low, $close, $vol, $volQuote, $uts, $isFinal); |
| 87 | } | 87 | } |
| 88 | + public static function transferBitget($data) | ||
| 89 | + { | ||
| 90 | + $kline = $data['data'][0]; | ||
| 91 | + $time = (int)($kline['ts']); | ||
| 92 | + $open = (float)$kline['open']; | ||
| 93 | + $high = (float)$kline['high']; | ||
| 94 | + $low = (float)$kline['low']; | ||
| 95 | + $close = (float)$kline['close']; | ||
| 96 | + $vol = (float)$kline['vol']; | ||
| 97 | + $volQuote = (float)$kline['volCcy']; | ||
| 98 | + $uts = $time; | ||
| 99 | + $isFinal = $kline['confirm'] ?? true; | ||
| 100 | + return new Kline($time, $open, $high, $low, $close, $vol, $volQuote, $uts, $isFinal); | ||
| 101 | + } | ||
| 88 | } | 102 | } |
| @@ -94,6 +94,30 @@ class SymbolInfo | @@ -94,6 +94,30 @@ class SymbolInfo | ||
| 94 | $info = new SymbolInfo($symbolOri, $symbolSt, $ctVal, $pricePrec, $qtyPrec, $lotPrec, $minLot, $minQty, $mul); | 94 | $info = new SymbolInfo($symbolOri, $symbolSt, $ctVal, $pricePrec, $qtyPrec, $lotPrec, $minLot, $minQty, $mul); |
| 95 | return $info; | 95 | return $info; |
| 96 | } | 96 | } |
| 97 | + public static function transferBitget($data, callable $getSymbolSt): SymbolInfo | ||
| 98 | + { | ||
| 99 | + $symbolOri = $data['symbol']; | ||
| 100 | + $symbolSt = $getSymbolSt($symbolOri); //转换为标准交易对 | ||
| 101 | + $ctVal = 1; // Bitget合约面值为1 | ||
| 102 | + $pricePrec = (int)$data['pricePlace']; | ||
| 103 | + $qtyPrec = (int)$data['volumePlace']; | ||
| 104 | + $lotPrec = $qtyPrec; | ||
| 105 | + $minQty = $data['minTradeNum']; | ||
| 106 | + $minLot = $minQty * $ctVal; | ||
| 107 | + $mul = self::extractNumber($symbolOri); | ||
| 108 | + | ||
| 109 | + return new SymbolInfo( | ||
| 110 | + $symbolOri, | ||
| 111 | + $symbolSt, | ||
| 112 | + $ctVal, | ||
| 113 | + $pricePrec, | ||
| 114 | + $qtyPrec, | ||
| 115 | + $lotPrec, | ||
| 116 | + $minLot, | ||
| 117 | + $minQty, | ||
| 118 | + $mul | ||
| 119 | + ); | ||
| 120 | + } | ||
| 97 | public static function extractNumber($str) | 121 | public static function extractNumber($str) |
| 98 | { | 122 | { |
| 99 | // 示例 | 123 | // 示例 |
| @@ -85,13 +85,13 @@ class WsDataOrder | @@ -85,13 +85,13 @@ class WsDataOrder | ||
| 85 | $ordType = strtoupper($data['ordType']); | 85 | $ordType = strtoupper($data['ordType']); |
| 86 | $cliOrdId = $data['clOrdId']; | 86 | $cliOrdId = $data['clOrdId']; |
| 87 | $ordId = $data['ordId']; | 87 | $ordId = $data['ordId']; |
| 88 | - $status = self::getOkStatus($data['state']); | 88 | + $status = self::getOkxStatus($data['state']); |
| 89 | if ($data['cancelSource'] != '') { | 89 | if ($data['cancelSource'] != '') { |
| 90 | $status = 'CANCELED'; | 90 | $status = 'CANCELED'; |
| 91 | } | 91 | } |
| 92 | return new WsDataOrder($platform, $posSide, $symbol, $side, $price, $avgPx, $qty, $lot, $pnl, $ts, $uts, $ordType, $cliOrdId, $ordId, $status); | 92 | return new WsDataOrder($platform, $posSide, $symbol, $side, $price, $avgPx, $qty, $lot, $pnl, $ts, $uts, $ordType, $cliOrdId, $ordId, $status); |
| 93 | } | 93 | } |
| 94 | - private static function getOkStatus(string $state) | 94 | + private static function getOkxStatus(string $state) |
| 95 | { | 95 | { |
| 96 | if ($state == 'live') { | 96 | if ($state == 'live') { |
| 97 | return 'LIVE'; | 97 | return 'LIVE'; |
| @@ -129,9 +129,28 @@ class WsDataOrder | @@ -129,9 +129,28 @@ class WsDataOrder | ||
| 129 | $ordType = strtoupper($order['o']); | 129 | $ordType = strtoupper($order['o']); |
| 130 | $cliOrdId = $order['c']; | 130 | $cliOrdId = $order['c']; |
| 131 | $ordId = $order['i']; | 131 | $ordId = $order['i']; |
| 132 | - $status = self::getBnStatus($order['X']); | 132 | + $status = self::getBinanceStatus($order['X']); |
| 133 | return new WsDataOrder($platform, $posSide, $symbol, $side, $price, $avgPx, $qty, $lot, $pnl, $ts, $uts, $ordType, $cliOrdId, $ordId, $status); | 133 | return new WsDataOrder($platform, $posSide, $symbol, $side, $price, $avgPx, $qty, $lot, $pnl, $ts, $uts, $ordType, $cliOrdId, $ordId, $status); |
| 134 | } | 134 | } |
| 135 | + private static function getBinanceStatus(string $state) | ||
| 136 | + { | ||
| 137 | + if ($state == 'NEW') { | ||
| 138 | + return 'LIVE'; | ||
| 139 | + } | ||
| 140 | + if ($state == 'PARTIALLY_FILLED') { | ||
| 141 | + return 'PARTIALLY_FILLED'; | ||
| 142 | + } | ||
| 143 | + if ($state == 'FILLED') { | ||
| 144 | + return 'FILLED'; | ||
| 145 | + } | ||
| 146 | + if ($state == 'CANCELED') { | ||
| 147 | + return 'CANCELED'; | ||
| 148 | + } | ||
| 149 | + if ($state == 'EXPIRED' || $state == "EXPIRED_IN_MATCH") { | ||
| 150 | + return 'CANCELED'; | ||
| 151 | + } | ||
| 152 | + return 'UNKNOWN'; | ||
| 153 | + } | ||
| 135 | public static function TransferBybitOrder($data, $symbolInfos, callable $toSymbolSt): WsDataOrder|null | 154 | public static function TransferBybitOrder($data, $symbolInfos, callable $toSymbolSt): WsDataOrder|null |
| 136 | { | 155 | { |
| 137 | $symbol = call_user_func($toSymbolSt, $data['symbol']); | 156 | $symbol = call_user_func($toSymbolSt, $data['symbol']); |
| @@ -166,25 +185,6 @@ class WsDataOrder | @@ -166,25 +185,6 @@ class WsDataOrder | ||
| 166 | $status = self::getBybitStatus($data['orderStatus']); | 185 | $status = self::getBybitStatus($data['orderStatus']); |
| 167 | return new WsDataOrder($platform, $posSide, $symbol, $side, $price, $avgPx, $qty, $lot, $pnl, $ts, $uts, $ordType, $cliOrdId, $ordId, $status); | 186 | return new WsDataOrder($platform, $posSide, $symbol, $side, $price, $avgPx, $qty, $lot, $pnl, $ts, $uts, $ordType, $cliOrdId, $ordId, $status); |
| 168 | } | 187 | } |
| 169 | - private static function getBnStatus(string $state) | ||
| 170 | - { | ||
| 171 | - if ($state == 'NEW') { | ||
| 172 | - return 'LIVE'; | ||
| 173 | - } | ||
| 174 | - if ($state == 'PARTIALLY_FILLED') { | ||
| 175 | - return 'PARTIALLY_FILLED'; | ||
| 176 | - } | ||
| 177 | - if ($state == 'FILLED') { | ||
| 178 | - return 'FILLED'; | ||
| 179 | - } | ||
| 180 | - if ($state == 'CANCELED') { | ||
| 181 | - return 'CANCELED'; | ||
| 182 | - } | ||
| 183 | - if ($state == 'EXPIRED' || $state == "EXPIRED_IN_MATCH") { | ||
| 184 | - return 'CANCELED'; | ||
| 185 | - } | ||
| 186 | - return 'UNKNOWN'; | ||
| 187 | - } | ||
| 188 | private static function getBybitStatus(string $state) | 188 | private static function getBybitStatus(string $state) |
| 189 | { | 189 | { |
| 190 | if ($state == 'New') { | 190 | if ($state == 'New') { |
| @@ -201,4 +201,47 @@ class WsDataOrder | @@ -201,4 +201,47 @@ class WsDataOrder | ||
| 201 | } | 201 | } |
| 202 | return 'UNKNOWN'; | 202 | return 'UNKNOWN'; |
| 203 | } | 203 | } |
| 204 | + public static function TransferBitgetOrder(array $data, array $symbolInfos, callable $toSymbolSt): WsDataOrder|null | ||
| 205 | + { | ||
| 206 | + $symbol = call_user_func($toSymbolSt, $data['symbol']); | ||
| 207 | + /** @var SymbolInfo $symbolInfo */ | ||
| 208 | + $symbolInfo = $symbolInfos[$symbol] ?? null; | ||
| 209 | + if ($symbolInfo === null) { | ||
| 210 | + return null; | ||
| 211 | + } | ||
| 212 | + | ||
| 213 | + return new WsDataOrder( | ||
| 214 | + 'bitget', | ||
| 215 | + 'BOTH', | ||
| 216 | + $symbol, | ||
| 217 | + strtoupper($data['side']), | ||
| 218 | + (float)$data['price'], | ||
| 219 | + (float)$data['fillPrice'], | ||
| 220 | + (float)$data['size'], | ||
| 221 | + (float)$data['size'], | ||
| 222 | + 0, | ||
| 223 | + (int)($data['cTime']), | ||
| 224 | + (int)($data['uTime']), | ||
| 225 | + strtoupper($data['orderType']), | ||
| 226 | + $data['clientOid'], | ||
| 227 | + $data['orderId'], | ||
| 228 | + self::getBitgetStatus($data['status']) | ||
| 229 | + ); | ||
| 230 | + } | ||
| 231 | + | ||
| 232 | + private static function getBitgetStatus($status) | ||
| 233 | + { | ||
| 234 | + switch ($status) { | ||
| 235 | + case 'new': | ||
| 236 | + return 'LIVE'; | ||
| 237 | + case 'partial-filled': | ||
| 238 | + return 'PARTIALLY_FILLED'; | ||
| 239 | + case 'filled': | ||
| 240 | + return 'FILLED'; | ||
| 241 | + case 'cancelled': | ||
| 242 | + return 'CANCELED'; | ||
| 243 | + default: | ||
| 244 | + return 'UNKNOWN'; | ||
| 245 | + } | ||
| 246 | + } | ||
| 204 | } | 247 | } |
| @@ -92,4 +92,23 @@ class WsDataPos | @@ -92,4 +92,23 @@ class WsDataPos | ||
| 92 | $pnl = (float)$data['unrealisedPnl']; | 92 | $pnl = (float)$data['unrealisedPnl']; |
| 93 | return new WsDataPos($symbol, $posSide, $qty, $lot, $avgPrice, false, $pnl); | 93 | return new WsDataPos($symbol, $posSide, $qty, $lot, $avgPrice, false, $pnl); |
| 94 | } | 94 | } |
| 95 | + public static function TransferBitgetPos(array $data, array $symbolInfos, callable $toSymbolSt): WsDataPos|null | ||
| 96 | + { | ||
| 97 | + $symbol = call_user_func($toSymbolSt, $data['symbol']); | ||
| 98 | + /** @var SymbolInfo $symbolInfo */ | ||
| 99 | + $symbolInfo = $symbolInfos[$symbol] ?? null; | ||
| 100 | + if ($symbolInfo === null) { | ||
| 101 | + return null; | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + return new WsDataPos( | ||
| 105 | + $symbol, | ||
| 106 | + 'BOTH', | ||
| 107 | + (float)$data['total'], | ||
| 108 | + (float)$data['total'], | ||
| 109 | + (float)$data['averageOpenPrice'], | ||
| 110 | + false, | ||
| 111 | + (float)$data['unrealizedPL'] | ||
| 112 | + ); | ||
| 113 | + } | ||
| 95 | } | 114 | } |
| @@ -139,4 +139,35 @@ class WsDataTrade | @@ -139,4 +139,35 @@ class WsDataTrade | ||
| 139 | $lever = 0; | 139 | $lever = 0; |
| 140 | return new WsDataTrade($platform, $posSide, $symbol, $side, $price, $qty, $lot, $pnl, $fee, $quoteVol, $ts, $tradeId, $ordId, $cliOrdId, $lever); | 140 | return new WsDataTrade($platform, $posSide, $symbol, $side, $price, $qty, $lot, $pnl, $fee, $quoteVol, $ts, $tradeId, $ordId, $cliOrdId, $lever); |
| 141 | } | 141 | } |
| 142 | + public static function TransferBitgetOrder(array $data, array $symbolInfos, callable $toSymbolSt): WsDataTrade|null | ||
| 143 | + { | ||
| 144 | + if ($data['status'] != 'filled' && $data['status'] != 'partial-filled') { | ||
| 145 | + return null; | ||
| 146 | + } | ||
| 147 | + | ||
| 148 | + $symbol = call_user_func($toSymbolSt, $data['symbol']); | ||
| 149 | + /** @var SymbolInfo $symbolInfo */ | ||
| 150 | + $symbolInfo = $symbolInfos[$symbol] ?? null; | ||
| 151 | + if ($symbolInfo === null) { | ||
| 152 | + return null; | ||
| 153 | + } | ||
| 154 | + | ||
| 155 | + return new WsDataTrade( | ||
| 156 | + 'bitget', | ||
| 157 | + 'BOTH', | ||
| 158 | + $symbol, | ||
| 159 | + strtoupper($data['side']), | ||
| 160 | + (float)$data['fillPrice'], | ||
| 161 | + (float)$data['fillQuantity'], | ||
| 162 | + (float)$data['fillQuantity'], | ||
| 163 | + 0, | ||
| 164 | + (float)$data['fee'], | ||
| 165 | + (float)$data['fillPrice'] * (float)$data['fillQuantity'], | ||
| 166 | + (int)($data['cTime']), | ||
| 167 | + $data['tradeId'], | ||
| 168 | + $data['orderId'], | ||
| 169 | + $data['clientOid'], | ||
| 170 | + 1 | ||
| 171 | + ); | ||
| 172 | + } | ||
| 142 | } | 173 | } |
-
请 注册 或 登录 后发表评论