正在显示
36 个修改的文件
包含
1211 行增加
和
29 行删除
| 1 | { | 1 | { |
| 2 | - "name": "jiaoyin/common", | 2 | + "name": "jiaoyin/base", |
| 3 | "description": "indigo common php lib by jy", | 3 | "description": "indigo common php lib by jy", |
| 4 | "require": { | 4 | "require": { |
| 5 | "ext-json": "*" | 5 | "ext-json": "*" |
| 6 | }, | 6 | }, |
| 7 | "autoload": { | 7 | "autoload": { |
| 8 | "psr-4": { | 8 | "psr-4": { |
| 9 | - "Jiaoyin\\": "./src/" | 9 | + "Jiaoyin\\": "./src/", |
| 10 | + "Trader\\": "./trader/" | ||
| 10 | }, | 11 | }, |
| 11 | - "files": [ "src/functions.php" ] | ||
| 12 | - } | 12 | + "files": [ "src/functions.php", "trader/common/func.php" ] |
| 13 | + }, | ||
| 14 | + "require-dev": { | ||
| 15 | + "swoole/ide-helper": "^5.0" | ||
| 16 | + } | ||
| 13 | } | 17 | } |
| 1 | <?php | 1 | <?php |
| 2 | 2 | ||
| 3 | -namespace Jiaoyin; | 3 | +namespace jiaoyin; |
| 4 | 4 | ||
| 5 | use Swoole\Coroutine; | 5 | use Swoole\Coroutine; |
| 6 | use Swoole\Coroutine\Http\Client; | 6 | use Swoole\Coroutine\Http\Client; |
| @@ -26,7 +26,7 @@ class Websocket | @@ -26,7 +26,7 @@ class Websocket | ||
| 26 | private int $timerPing = 0; | 26 | private int $timerPing = 0; |
| 27 | private string $desc = 'websocket未命名'; | 27 | private string $desc = 'websocket未命名'; |
| 28 | private int $pingCount = 0; | 28 | private int $pingCount = 0; |
| 29 | - private int $pingDelay = 10000;//ping tick时间 10s | 29 | + private int $pingDelay = 10000; //ping tick时间 10s |
| 30 | 30 | ||
| 31 | // 例如:wss://fstream.binance.com/stream、ws://175.178.36.217:9528/spot/stream | 31 | // 例如:wss://fstream.binance.com/stream、ws://175.178.36.217:9528/spot/stream |
| 32 | public function __construct($url, $desc = null) | 32 | public function __construct($url, $desc = null) |
| @@ -64,9 +64,13 @@ class Websocket | @@ -64,9 +64,13 @@ class Websocket | ||
| 64 | $this->client?->close(); | 64 | $this->client?->close(); |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | - public function connect(callable $onOpen = null, callable $onMessage = null, | ||
| 68 | - callable $onClose = null, $onPing = null, $onPong = null): void | ||
| 69 | - { | 67 | + public function connect( |
| 68 | + callable $onOpen = null, | ||
| 69 | + callable $onMessage = null, | ||
| 70 | + callable $onClose = null, | ||
| 71 | + $onPing = null, | ||
| 72 | + $onPong = null | ||
| 73 | + ): void { | ||
| 70 | $this->close(); | 74 | $this->close(); |
| 71 | $this->pingCount = 0; | 75 | $this->pingCount = 0; |
| 72 | $this->onOpen = $onOpen; | 76 | $this->onOpen = $onOpen; |
| @@ -80,8 +84,8 @@ class Websocket | @@ -80,8 +84,8 @@ class Websocket | ||
| 80 | $ret = $client->upgrade($this->path); | 84 | $ret = $client->upgrade($this->path); |
| 81 | if ($ret) { | 85 | if ($ret) { |
| 82 | output($this->desc, "连接成功"); | 86 | output($this->desc, "连接成功"); |
| 83 | - $this->client=&$client; | ||
| 84 | - swoole_timer_after(50, function () use ($onOpen,$client) { | 87 | + $this->client = &$client; |
| 88 | + swoole_timer_after(50, function () use ($onOpen, $client) { | ||
| 85 | if ($onOpen) { | 89 | if ($onOpen) { |
| 86 | call_user_func($onOpen, $client); | 90 | call_user_func($onOpen, $client); |
| 87 | } | 91 | } |
| @@ -90,7 +94,7 @@ class Websocket | @@ -90,7 +94,7 @@ class Websocket | ||
| 90 | while ($client) { | 94 | while ($client) { |
| 91 | $frame = $client->recv($this->recvTimeout); | 95 | $frame = $client->recv($this->recvTimeout); |
| 92 | if (!$frame && $client->errCode != 60) { | 96 | if (!$frame && $client->errCode != 60) { |
| 93 | - output($this->desc,'错误码:' . $client->errCode.",错误数据:", $frame); | 97 | + output($this->desc, '错误码:' . $client->errCode . ",错误数据:", $frame); |
| 94 | break; | 98 | break; |
| 95 | } | 99 | } |
| 96 | $this->lastRecvTime = time(); | 100 | $this->lastRecvTime = time(); |
| @@ -185,4 +189,4 @@ class Websocket | @@ -185,4 +189,4 @@ class Websocket | ||
| 185 | } | 189 | } |
| 186 | }); | 190 | }); |
| 187 | } | 191 | } |
| 188 | -} | ||
| 192 | +} |
trader/CmBroker.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +namespace trader; | ||
| 4 | + | ||
| 5 | +require_once __DIR__ . '/autoload.php'; | ||
| 6 | +require_once __DIR__ . '/../vendor/autoload.php'; | ||
| 7 | + | ||
| 8 | +use trader\struct\ApiInfo; | ||
| 9 | +use trader\okx\ExBroker as OkxBroker; | ||
| 10 | +use trader\binance\ExBroker as BinanceBroker; | ||
| 11 | +use trader\struct\Kline; | ||
| 12 | +use trader\struct\Order; | ||
| 13 | +use trader\struct\WsData; | ||
| 14 | +use trader\struct\WsDataTrade; | ||
| 15 | +use trader\struct\WsDataPos; | ||
| 16 | +use trader\struct\SymbolInfo; | ||
| 17 | +use trader\struct\WsDataOrder; | ||
| 18 | +use trader\struct\Pos; | ||
| 19 | +use \Exception; | ||
| 20 | +use function Jiaoyin\timeFormat; | ||
| 21 | +use trader\okx\Api as OkApi; | ||
| 22 | + | ||
| 23 | +class CmBroker | ||
| 24 | +{ | ||
| 25 | + private $plat; | ||
| 26 | + private ?OkxBroker $exBroker; | ||
| 27 | + /** @var SymbolInfo[] $symbolInfos */ | ||
| 28 | + public $symbolInfos = []; | ||
| 29 | + /** @var Pos[] $positions */ | ||
| 30 | + private $positions = []; | ||
| 31 | + private $name = ''; | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + public function __construct($plat, ApiInfo $apiInfo) | ||
| 35 | + { | ||
| 36 | + $this->plat = $plat; | ||
| 37 | + $exBroker = null; | ||
| 38 | + if ($plat == 'okx') { | ||
| 39 | + $exBroker = new OkxBroker($apiInfo); | ||
| 40 | + } | ||
| 41 | + if ($plat == 'binance') { | ||
| 42 | + $exBroker = new BinanceBroker($apiInfo); | ||
| 43 | + } | ||
| 44 | + $this->exBroker = $exBroker; | ||
| 45 | + } | ||
| 46 | + public function setName($name) | ||
| 47 | + { | ||
| 48 | + $this->name = $name; | ||
| 49 | + } | ||
| 50 | + public function init() | ||
| 51 | + { | ||
| 52 | + $this->initSymbolInfos(); | ||
| 53 | + } | ||
| 54 | + public function accListen(callable $onData) | ||
| 55 | + { | ||
| 56 | + $this->exBroker->accListen(function ($data) use ($onData) { | ||
| 57 | + // output("ws 有效数据", $data); | ||
| 58 | + if ($this->plat == 'binance') { | ||
| 59 | + $this->msg("binance 无处理ws数据实现"); | ||
| 60 | + } | ||
| 61 | + if ($this->plat == 'okx') { | ||
| 62 | + if (isset($data['arg']) && $data['arg']['channel'] == 'orders') { | ||
| 63 | + foreach ($data['data'] as $key => $value) { | ||
| 64 | + $wsDataTrade = WsDataTrade::TransferOkxOrder($value, $this->symbolInfos, function ($symbol) { | ||
| 65 | + return $this->getSymbolSt($symbol); | ||
| 66 | + }); | ||
| 67 | + if ($wsDataTrade != null) { | ||
| 68 | + $wsData = new WsData($this->plat, 'trade', $trade = $wsDataTrade); | ||
| 69 | + $onData($wsData); | ||
| 70 | + } | ||
| 71 | + $wsDataOrd = WsDataOrder::TransferOkxOrder($value, $this->symbolInfos, function ($symbol) { | ||
| 72 | + return $this->getSymbolSt($symbol); | ||
| 73 | + }); | ||
| 74 | + if ($wsDataOrd != null) { | ||
| 75 | + $wsData = new WsData($this->plat, 'order', $trade = null, $pos = null, $order = $wsDataOrd); | ||
| 76 | + $onData($wsData); | ||
| 77 | + } | ||
| 78 | + } | ||
| 79 | + return; | ||
| 80 | + } | ||
| 81 | + if (isset($data['arg']) && $data['arg']['channel'] == 'positions') { | ||
| 82 | + $positions = []; | ||
| 83 | + foreach ($data['data'] as $key => $value) { | ||
| 84 | + $wsDataPos = WsDataPos::TransferOkxPos($value, $this->symbolInfos, function ($symbol) { | ||
| 85 | + return $this->getSymbolSt($symbol); | ||
| 86 | + }); | ||
| 87 | + if ($wsDataPos) { | ||
| 88 | + $pos = Pos::transferWsDataPos($wsDataPos); | ||
| 89 | + $positions[$wsDataPos->symbol . "_" . $wsDataPos->posSide] = $pos; | ||
| 90 | + $wsData = new WsData($this->plat, 'pos', $trade = null, $pos = $wsDataPos); | ||
| 91 | + $onData($wsData); | ||
| 92 | + } | ||
| 93 | + } | ||
| 94 | + $this->positions = $positions; | ||
| 95 | + return; | ||
| 96 | + } | ||
| 97 | + $this->msg("okx 无处理ws数据实现", $data); | ||
| 98 | + } | ||
| 99 | + }); | ||
| 100 | + } | ||
| 101 | + public function klineListen($symbol, $peroid, $onData) | ||
| 102 | + { | ||
| 103 | + $symbol = $this->getSymbolOri($symbol, $this->plat); | ||
| 104 | + $this->exBroker->klineListen($symbol, $peroid, function ($data) use ($onData) { | ||
| 105 | + $kline = Kline::transferOkx($data); | ||
| 106 | + $onData($kline); | ||
| 107 | + }); | ||
| 108 | + } | ||
| 109 | + //转换为标准交易对 | ||
| 110 | + public function getSymbolSt($symbol) | ||
| 111 | + { | ||
| 112 | + $symbol = str_replace('-USDT-SWAP', 'USDT', $symbol); | ||
| 113 | + if (preg_match('/^[A-Z0-9]+USDT$/', $symbol)) { | ||
| 114 | + return $symbol; | ||
| 115 | + } else { | ||
| 116 | + throw new Exception('转换标准交易对错误' + $symbol + ' to ', $symbol); | ||
| 117 | + } | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + //转换为原始交易对 | ||
| 121 | + public function getSymbolOri($symbol, $platTarget) | ||
| 122 | + { | ||
| 123 | + $symbolSt = $this->getSymbolSt($symbol); | ||
| 124 | + if ($platTarget == 'binance') { | ||
| 125 | + return $symbolSt; | ||
| 126 | + } | ||
| 127 | + if ($platTarget == 'okx') { | ||
| 128 | + return str_replace('USDT', '-USDT-SWAP', $symbolSt); | ||
| 129 | + } | ||
| 130 | + throw new Exception('平台错误' + $platTarget); | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | + public function placeOrder(Order $order) | ||
| 134 | + { | ||
| 135 | + if ($this->plat == 'okx') { | ||
| 136 | + $orderOri = $order->toOkxOrder($this->symbolInfos, function ($symbol) { | ||
| 137 | + return $this->getSymbolOri($symbol, $this->plat); | ||
| 138 | + }); | ||
| 139 | + $this->msg("下单", $orderOri); | ||
| 140 | + if ($orderOri['sz'] == 0) { | ||
| 141 | + $this->msg("下单数量为0,不下单", $orderOri); | ||
| 142 | + return ["code" => 1, "msg" => "下单数量为0,不下单"]; | ||
| 143 | + } | ||
| 144 | + $res = $this->exBroker->placeOrder($orderOri); | ||
| 145 | + $this->msg("下单结果", $res); | ||
| 146 | + return $res; | ||
| 147 | + } | ||
| 148 | + } | ||
| 149 | + | ||
| 150 | + public function msg() | ||
| 151 | + { | ||
| 152 | + $args = func_get_args(); | ||
| 153 | + $outStr = '[' . timeFormat('ms') . ']:' . $this->name . ": "; | ||
| 154 | + foreach ($args as $key => $value) { | ||
| 155 | + $value = is_array($value) ? json_encode($value) : $value; | ||
| 156 | + if (is_bool($value)) { | ||
| 157 | + $value = $value ? 'bool:true' : 'bool:false'; | ||
| 158 | + } | ||
| 159 | + $outStr .= count($args) - $key > 1 ? $value . ' ' : $value; | ||
| 160 | + } | ||
| 161 | + echo $outStr . PHP_EOL; | ||
| 162 | + } | ||
| 163 | + public function getPosQty($symbol, $posSide) | ||
| 164 | + { | ||
| 165 | + $key = $symbol . "_" . $posSide; | ||
| 166 | + if (!isset($this->positions[$key])) { | ||
| 167 | + return 0; | ||
| 168 | + } | ||
| 169 | + /** @var Pos $pos */ | ||
| 170 | + $pos = $this->positions[$key]; | ||
| 171 | + return abs($pos->qty); | ||
| 172 | + } | ||
| 173 | + private function initSymbolInfos() | ||
| 174 | + { | ||
| 175 | + if ($this->plat == 'okx') { | ||
| 176 | + //获取所有USDT SWAP 交易对 | ||
| 177 | + $res = $this->exBroker->getSymbolInfos(); | ||
| 178 | + $infos = []; | ||
| 179 | + foreach ($res as $key => $value) { | ||
| 180 | + if ($value["settleCcy"] == "USDT") { | ||
| 181 | + $info = SymbolInfo::transferOkx($value, function ($symbol) { | ||
| 182 | + return $this->getSymbolSt($symbol); | ||
| 183 | + }); | ||
| 184 | + $infos[$info->symbol] = $info; | ||
| 185 | + } | ||
| 186 | + } | ||
| 187 | + $this->symbolInfos = $infos; | ||
| 188 | + } | ||
| 189 | + //定时10m刷新 | ||
| 190 | + swoole_timer_after(1000 * 60 * 10, function () { | ||
| 191 | + $this->initSymbolInfos(); | ||
| 192 | + }); | ||
| 193 | + } | ||
| 194 | + public function stopListen() | ||
| 195 | + { | ||
| 196 | + $this->exBroker->stopListen(); | ||
| 197 | + } | ||
| 198 | +} |
trader/autoload.php
0 → 100644
| 1 | +<?php | ||
| 2 | +require_once __DIR__ . '/CmBroker.php'; | ||
| 3 | +require_once __DIR__ . '/common/tool.php'; | ||
| 4 | +require_once __DIR__ . '/struct/ApiInfo.php'; | ||
| 5 | +require_once __DIR__ . '/struct/SymbolInfo.php'; | ||
| 6 | +require_once __DIR__ . '/struct/Order.php'; | ||
| 7 | +require_once __DIR__ . '/struct/WsData.php'; | ||
| 8 | +require_once __DIR__ . '/struct/WsDataTrade.php'; | ||
| 9 | +require_once __DIR__ . '/struct/WsDataPos.php'; | ||
| 10 | +require_once __DIR__ . '/struct/WsDataOrder.php'; | ||
| 11 | +require_once __DIR__ . '/struct/Pos.php'; | ||
| 12 | +require_once __DIR__ . '/struct/Kline.php'; | ||
| 13 | +require_once __DIR__ . '/exchange/okx/ExBroker.php'; |
trader/common/func.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +namespace trader; | ||
| 4 | + | ||
| 5 | + | ||
| 6 | +function scToNum($scStr) | ||
| 7 | +{ | ||
| 8 | + $check_str = ''; | ||
| 9 | + if (strpos($scStr, 'e') !== false) { | ||
| 10 | + $check_str = 'e'; | ||
| 11 | + } | ||
| 12 | + if (strpos($scStr, 'E') !== false) { | ||
| 13 | + $check_str = 'E'; | ||
| 14 | + } | ||
| 15 | + if (empty($check_str)) { | ||
| 16 | + return $scStr; //非科学计数直接返回。 | ||
| 17 | + } | ||
| 18 | + $num_length = 0; | ||
| 19 | + $split_array = explode($check_str, $scStr); | ||
| 20 | + $num_length += strlen($split_array[0]); | ||
| 21 | + $num_length += (int)str_replace(['-', '+'], '', $split_array[1]); | ||
| 22 | + $float_number = (float)$scStr; | ||
| 23 | + $decimal_string = number_format($float_number, $num_length, '.', ''); | ||
| 24 | + $num = rtrim(rtrim($decimal_string, '0'), '.'); //去除小数后多余的0 | ||
| 25 | + return $num; | ||
| 26 | +} | ||
| 27 | +function tsToISO($timestamp) | ||
| 28 | +{ | ||
| 29 | + $datetime = new \DateTime(); | ||
| 30 | + $datetime->setTimestamp(floor($timestamp / 1000)); | ||
| 31 | + $datetime->setTimezone(new \DateTimeZone('UTC')); | ||
| 32 | + $milliseconds = sprintf('.%03d', $timestamp % 1000); | ||
| 33 | + return $datetime->format('Y-m-d\TH:i:s') . $milliseconds . 'Z'; | ||
| 34 | +} |
trader/exchange/binance/Api.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +namespace trader\binance; | ||
| 4 | + | ||
| 5 | +require_once __DIR__ . '/../../autoload.php'; | ||
| 6 | +require_once __DIR__ . '/../../../vendor/autoload.php'; | ||
| 7 | + | ||
| 8 | +use trader\struct\ApiInfo; | ||
| 9 | + | ||
| 10 | +class Api | ||
| 11 | +{ | ||
| 12 | + private $host = 'https://fapi.binance.com'; | ||
| 13 | + private ApiInfo $apiInfo; | ||
| 14 | + public function __construct(ApiInfo $apiInfo) | ||
| 15 | + { | ||
| 16 | + $this->apiInfo = $apiInfo; | ||
| 17 | + } | ||
| 18 | + | ||
| 19 | + public function getPremiumIndex() | ||
| 20 | + { | ||
| 21 | + $url = "/fapi/v1/premiumIndex"; | ||
| 22 | + $method = "GET"; | ||
| 23 | + } | ||
| 24 | +} |
trader/exchange/binance/ExBroker.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +namespace trader\binance; | ||
| 4 | + | ||
| 5 | +require_once __DIR__ . '/../../../vendor/autoload.php'; | ||
| 6 | +require_once __DIR__ . '/Api.php'; | ||
| 7 | + | ||
| 8 | +use trader\struct\ApiInfo; | ||
| 9 | +use trader\binance\Api as BnApi; | ||
| 10 | +use Jiaoyin\Websocket; | ||
| 11 | + | ||
| 12 | +class ExBroker | ||
| 13 | +{ | ||
| 14 | + // static private $host = 'wss://ws.okx.com:8443'; | ||
| 15 | + static private $host = 'ws://okws.keetu.com'; | ||
| 16 | + static private $pathPrivate = '/ws/v5/private'; | ||
| 17 | + static private $pathPublic = '/ws/v5/public'; | ||
| 18 | + static private $pathBusiness = '/ws/v5/business'; | ||
| 19 | + private ApiInfo $apiInfo; | ||
| 20 | + private BnApi $api; | ||
| 21 | + private ?Websocket $wsAcc; | ||
| 22 | + private ?Websocket $wsKline; | ||
| 23 | + | ||
| 24 | + public function __construct(ApiInfo $apiInfo) | ||
| 25 | + { | ||
| 26 | + $this->apiInfo = $apiInfo; | ||
| 27 | + $this->api = new BnApi($apiInfo); | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + //获取所有品种资金费率 | ||
| 31 | + public function getAllPremium() { | ||
| 32 | + $res = $this->api->getPremiumIndex(); | ||
| 33 | + } | ||
| 34 | +} |
trader/exchange/okx/Api.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +namespace trader\okx; | ||
| 4 | + | ||
| 5 | +require_once __DIR__ . '/../../autoload.php'; | ||
| 6 | +require_once __DIR__ . '/../../../vendor/autoload.php'; | ||
| 7 | + | ||
| 8 | +use trader\struct\ApiInfo; | ||
| 9 | +use Jiaoyin\Curl; | ||
| 10 | +use function Jiaoyin\getMicrotime; | ||
| 11 | +use function trader\tsToISO; | ||
| 12 | + | ||
| 13 | +class Api | ||
| 14 | +{ | ||
| 15 | + private ?ApiInfo $apiInfo = null; | ||
| 16 | + // private string $host = "https://www.okx.com"; | ||
| 17 | + private string $host = "http://okapi.keetu.com"; | ||
| 18 | + | ||
| 19 | + public function __construct($apiInfo = null, $host = "") | ||
| 20 | + { | ||
| 21 | + if ($apiInfo != null) { | ||
| 22 | + $this->apiInfo = $apiInfo; | ||
| 23 | + } | ||
| 24 | + if ($host != "") { | ||
| 25 | + $this->host = $host; | ||
| 26 | + } | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + //-----------public interface ------------ | ||
| 30 | + //所有交易产品基础信息 | ||
| 31 | + public function instruments($param) | ||
| 32 | + { | ||
| 33 | + $path = '/api/v5/public/instruments'; | ||
| 34 | + $method = 'GET'; | ||
| 35 | + return $this->request($path, $method, $param); | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + public function positionTiers($param) | ||
| 39 | + { | ||
| 40 | + $path = '/api/v5/public/position-tiers'; | ||
| 41 | + $method = 'GET'; | ||
| 42 | + return $this->request($path, $method, $param); | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + //获取资金费率 | ||
| 46 | + public function fundingRate($param) | ||
| 47 | + { | ||
| 48 | + $path = '/api/v5/public/funding-rate'; | ||
| 49 | + $method = 'GET'; | ||
| 50 | + return $this->request($path, $method, $param); | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + | ||
| 54 | + //-----------private interface ------------ | ||
| 55 | + // 查询杠杆 | ||
| 56 | + public function leverageInfo($param) | ||
| 57 | + { | ||
| 58 | + $path = '/api/v5/account/leverage-info'; | ||
| 59 | + $method = 'GET'; | ||
| 60 | + return $this->request($path, $method, $param, $this->apiInfo); | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + //设置杠杆 | ||
| 64 | + public function setLeverage($param) | ||
| 65 | + { | ||
| 66 | + $path = '/api/v5/account/set-leverage'; | ||
| 67 | + $method = 'POST'; | ||
| 68 | + return $this->request($path, $method, $param, $this->apiInfo); | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + //下单 | ||
| 72 | + public function placeOrder($param) | ||
| 73 | + { | ||
| 74 | + $path = '/api/v5/trade/order'; | ||
| 75 | + $method = 'POST'; | ||
| 76 | + return $this->request($path, $method, $param, $this->apiInfo); | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + //------------------------------------- | ||
| 80 | + | ||
| 81 | + private function createSign($timestamp, $method, $requestPath, $secretKey, $body = '') | ||
| 82 | + { | ||
| 83 | + $sign = base64_encode(hash_hmac('sha256', $timestamp . $method . $requestPath . $body, $secretKey, true)); | ||
| 84 | + return $sign; | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + private function request($path, $method, $param) | ||
| 88 | + { | ||
| 89 | + $header = []; | ||
| 90 | + $body = ''; | ||
| 91 | + $fullPath = $path; | ||
| 92 | + if ($method == 'GET' && count($param) > 0) { | ||
| 93 | + $fullPath = $path . '?' . http_build_query($param); | ||
| 94 | + } else { | ||
| 95 | + $header[] = 'Content-Type: application/json'; | ||
| 96 | + $body = json_encode($param); | ||
| 97 | + } | ||
| 98 | + if ($this->apiInfo != null) { | ||
| 99 | + $timestamp = tsToISO(getMicrotime()); | ||
| 100 | + $header[] = 'OK-ACCESS-KEY: ' . $this->apiInfo->key; | ||
| 101 | + $header[] = 'OK-ACCESS-PASSPHRASE: ' . $this->apiInfo->passphrase; | ||
| 102 | + $header[] = 'OK-ACCESS-TIMESTAMP: ' . $timestamp; | ||
| 103 | + $sign = $this->createSign($timestamp, $method, $fullPath, $this->apiInfo->secret, $body); | ||
| 104 | + $header[] = 'OK-ACCESS-SIGN: ' . $sign; | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + $url = $this->host . $path; | ||
| 108 | + $result = ""; | ||
| 109 | + if ($method == 'POST') { | ||
| 110 | + $result = Curl::httpPost($url, $param, $header); | ||
| 111 | + } else if ($method == 'GET') { | ||
| 112 | + $result = Curl::httpGet($url, $param, $header); | ||
| 113 | + } else { | ||
| 114 | + $result = Curl::httpGet($url, $param, $header); | ||
| 115 | + } | ||
| 116 | + return json_decode($result, true); | ||
| 117 | + } | ||
| 118 | +} |
trader/exchange/okx/ExBroker.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +namespace trader\okx; | ||
| 4 | + | ||
| 5 | +require_once __DIR__ . '/../../../vendor/autoload.php'; | ||
| 6 | +require_once __DIR__ . '/Api.php'; | ||
| 7 | + | ||
| 8 | +use trader\struct\ApiInfo; | ||
| 9 | +use trader\okx\Api as OkxApi; | ||
| 10 | +use Jiaoyin\Websocket; | ||
| 11 | +use function Jiaoyin\output; | ||
| 12 | + | ||
| 13 | + | ||
| 14 | +class ExBroker | ||
| 15 | +{ | ||
| 16 | + // static private $host = 'wss://ws.okx.com:8443'; | ||
| 17 | + static private $host = 'ws://okws.keetu.com'; | ||
| 18 | + static private $pathPrivate = '/ws/v5/private'; | ||
| 19 | + static private $pathPublic = '/ws/v5/public'; | ||
| 20 | + static private $pathBusiness = '/ws/v5/business'; | ||
| 21 | + private ApiInfo $apiInfo; | ||
| 22 | + private OkxApi $api; | ||
| 23 | + private ?Websocket $wsAcc; | ||
| 24 | + private ?Websocket $wsKline; | ||
| 25 | + | ||
| 26 | + public function __construct($apiInfo) | ||
| 27 | + { | ||
| 28 | + $this->apiInfo = $apiInfo; | ||
| 29 | + $this->api = new OkxApi($apiInfo); | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + public function accListen(callable $onWsData) | ||
| 33 | + { | ||
| 34 | + if (isset($this->wsAcc)) { | ||
| 35 | + $this->wsAcc->close(); | ||
| 36 | + } | ||
| 37 | + $this->wsAcc = new Websocket(self::$host . self::$pathPrivate); | ||
| 38 | + $this->wsAcc->connect( | ||
| 39 | + $onOpen = function () { | ||
| 40 | + $this->wsLogin(); | ||
| 41 | + }, | ||
| 42 | + $onMessage = function ($data) use ($onWsData) { | ||
| 43 | + $this->onWsDataPre($data, $onWsData); | ||
| 44 | + }, | ||
| 45 | + $onClose = function () { | ||
| 46 | + // 关闭链接 | ||
| 47 | + } | ||
| 48 | + ); | ||
| 49 | + } | ||
| 50 | + public function klineListen($symbol, $period, callable $onData) | ||
| 51 | + { | ||
| 52 | + if (isset($this->wsKline)) { | ||
| 53 | + $this->wsKline->close(); | ||
| 54 | + } | ||
| 55 | + $this->wsKline = new Websocket(self::$host . self::$pathBusiness); | ||
| 56 | + $this->wsKline->connect( | ||
| 57 | + $onOpen = function () use ($symbol, $period) { | ||
| 58 | + $subData = []; | ||
| 59 | + $subData['op'] = 'subscribe'; | ||
| 60 | + $subData['args'] = [ | ||
| 61 | + [ | ||
| 62 | + 'channel' => $this->getKlineChannels()[$period], | ||
| 63 | + 'instId' => $symbol, | ||
| 64 | + ], | ||
| 65 | + ]; | ||
| 66 | + output("订阅", $subData); | ||
| 67 | + $this->wsKline->push(json_encode($subData)); | ||
| 68 | + }, | ||
| 69 | + $onMessage = function ($data) use ($onData) { | ||
| 70 | + $data = json_decode($data, true); | ||
| 71 | + if (!isset($data['data'])) { | ||
| 72 | + return; | ||
| 73 | + } | ||
| 74 | + $data = $data['data'][0]; | ||
| 75 | + $onData($data); | ||
| 76 | + }, | ||
| 77 | + $onClose = function () { | ||
| 78 | + // 关闭链接 | ||
| 79 | + } | ||
| 80 | + ); | ||
| 81 | + } | ||
| 82 | + private function crateWsSign($timeStamp) | ||
| 83 | + { | ||
| 84 | + $method = 'GET'; | ||
| 85 | + $path = '/users/self/verify'; | ||
| 86 | + $str = $timeStamp . $method . $path; | ||
| 87 | + $sign = hash_hmac('sha256', $str, $this->apiInfo->secret, true); | ||
| 88 | + $sign = base64_encode($sign); | ||
| 89 | + return $sign; | ||
| 90 | + } | ||
| 91 | + private function wsLogin() | ||
| 92 | + { | ||
| 93 | + $ts = time(); | ||
| 94 | + $subData = []; | ||
| 95 | + $subData['op'] = 'login'; | ||
| 96 | + $subData['args'] = [ | ||
| 97 | + [ | ||
| 98 | + 'apiKey' => $this->apiInfo->key, | ||
| 99 | + 'passphrase' => $this->apiInfo->passphrase, | ||
| 100 | + 'timestamp' => $ts, | ||
| 101 | + 'sign' => $this->crateWsSign($ts), | ||
| 102 | + ] | ||
| 103 | + ]; | ||
| 104 | + $this->wsAcc->push(json_encode($subData)); | ||
| 105 | + } | ||
| 106 | + // ws 消息预处理 | ||
| 107 | + private function onWsDataPre($data, callable $onWsData) | ||
| 108 | + { | ||
| 109 | + $data = json_decode($data, true); | ||
| 110 | + if (isset($data['event'])) { | ||
| 111 | + if ($data['event'] == 'login' && $data['code'] == '0') { | ||
| 112 | + output('ws登录成功'); | ||
| 113 | + $this->wsSubscribe(); | ||
| 114 | + return; | ||
| 115 | + } | ||
| 116 | + if ($data['event'] == 'subscribe' || $data['event'] == 'unsubscribe' || $data['event'] == 'channel-conn-count') { | ||
| 117 | + // output("ws 过滤数据", $data); | ||
| 118 | + return; | ||
| 119 | + } | ||
| 120 | + output("ok ws 未处理数据", $data); | ||
| 121 | + } | ||
| 122 | + call_user_func($onWsData, $data); | ||
| 123 | + } | ||
| 124 | + // 订阅 | ||
| 125 | + private function wsSubscribe() | ||
| 126 | + { | ||
| 127 | + $subData = []; | ||
| 128 | + $subData['op'] = 'subscribe'; | ||
| 129 | + $subData['args'] = [ | ||
| 130 | + [ | ||
| 131 | + 'channel' => 'orders', | ||
| 132 | + 'instType' => 'SWAP', | ||
| 133 | + ], | ||
| 134 | + [ | ||
| 135 | + 'channel' => 'positions', | ||
| 136 | + 'instType' => 'SWAP', | ||
| 137 | + ], | ||
| 138 | + ]; | ||
| 139 | + $this->wsAcc->push(json_encode($subData)); | ||
| 140 | + } | ||
| 141 | + public function placeOrder($param) | ||
| 142 | + { | ||
| 143 | + return $this->api->placeOrder($param); | ||
| 144 | + } | ||
| 145 | + public function getKlineChannels() | ||
| 146 | + { | ||
| 147 | + return [ | ||
| 148 | + '1s' => 'candle1s', | ||
| 149 | + '1m' => 'candle1m', | ||
| 150 | + '5m' => 'candle5m', | ||
| 151 | + '15m' => 'candle15m', | ||
| 152 | + '30m' => 'candle30m', | ||
| 153 | + '1h' => 'candle1H', | ||
| 154 | + '2h' => 'candle2H', | ||
| 155 | + '4h' => 'candle4H', | ||
| 156 | + '6h' => 'candle6H', | ||
| 157 | + '12h' => 'candle12H', | ||
| 158 | + '1d' => 'candle1D', | ||
| 159 | + ]; | ||
| 160 | + } | ||
| 161 | + public function getSymbolInfos() | ||
| 162 | + { | ||
| 163 | + $res = $this->api->instruments(["instType" => "SWAP"]); | ||
| 164 | + if ($res['code'] != '0') { | ||
| 165 | + return []; | ||
| 166 | + } | ||
| 167 | + return $res['data']; | ||
| 168 | + } | ||
| 169 | + public function stopListen() | ||
| 170 | + { | ||
| 171 | + $this->wsAcc->close(); | ||
| 172 | + $this->wsKline->close(); | ||
| 173 | + } | ||
| 174 | +} |
trader/struct/ApiInfo.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +namespace trader\struct; | ||
| 4 | + | ||
| 5 | +class ApiInfo | ||
| 6 | +{ | ||
| 7 | + public string $key = ''; | ||
| 8 | + public string $secret = ''; | ||
| 9 | + public string $passphrase = ''; | ||
| 10 | + | ||
| 11 | + public function __construct($key, $secret, $passphrase) | ||
| 12 | + { | ||
| 13 | + $this->key = $key; | ||
| 14 | + $this->secret = $secret; | ||
| 15 | + $this->passphrase = $passphrase; | ||
| 16 | + } | ||
| 17 | +} |
trader/struct/Kline.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +namespace trader\struct; | ||
| 4 | + | ||
| 5 | +class Kline | ||
| 6 | +{ | ||
| 7 | + public $time; | ||
| 8 | + public $open; | ||
| 9 | + public $high; | ||
| 10 | + public $low; | ||
| 11 | + public $close; | ||
| 12 | + public $vol; | ||
| 13 | + public $volQuote; | ||
| 14 | + | ||
| 15 | + public function __construct($time, $open, $high, $low, $close, $vol, $volQuote) | ||
| 16 | + { | ||
| 17 | + $this->time = $time; | ||
| 18 | + $this->open = $open; | ||
| 19 | + $this->high = $high; | ||
| 20 | + $this->low = $low; | ||
| 21 | + $this->close = $close; | ||
| 22 | + $this->vol = $vol; | ||
| 23 | + $this->volQuote = $volQuote; | ||
| 24 | + } | ||
| 25 | + public function toArray() | ||
| 26 | + { | ||
| 27 | + return [ | ||
| 28 | + 'time' => $this->time, | ||
| 29 | + 'open' => $this->open, | ||
| 30 | + 'high' => $this->high, | ||
| 31 | + 'low' => $this->low, | ||
| 32 | + 'close' => $this->close, | ||
| 33 | + 'vol' => $this->vol, | ||
| 34 | + 'volQuote' => $this->volQuote, | ||
| 35 | + ]; | ||
| 36 | + } | ||
| 37 | + public static function transferOkx($data) | ||
| 38 | + { | ||
| 39 | + $time = $data[0]; | ||
| 40 | + $open = $data[1]; | ||
| 41 | + $high = $data[2]; | ||
| 42 | + $low = $data[3]; | ||
| 43 | + $close = $data[4]; | ||
| 44 | + $vol = $data[6]; | ||
| 45 | + $volQuote = $data[7]; | ||
| 46 | + return new Kline($time, $open, $high, $low, $close, $vol, $volQuote); | ||
| 47 | + } | ||
| 48 | +} |
trader/struct/Order.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +namespace trader\struct; | ||
| 4 | + | ||
| 5 | +class Order | ||
| 6 | +{ | ||
| 7 | + public string $symbol; //大写 | ||
| 8 | + public string $ordType; // 订单类型 LIMIT 限价 IOC 不成则撤 FOK 全部成交或立即取消 MARKET 市价 ONLYMAKER 仅做maker单 | ||
| 9 | + public string $posSide; //大写 | ||
| 10 | + public string $side; //大写 | ||
| 11 | + public float $price; //成交价格 | ||
| 12 | + public float $qty; //币种真实数量 | ||
| 13 | + private string $cliOrdId = ''; // 客户端订单号 | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + public function __construct($symbol, $ordType, $posSide, $side, $price, $qty) | ||
| 17 | + { | ||
| 18 | + $this->symbol = $symbol; | ||
| 19 | + $this->ordType = $ordType; | ||
| 20 | + $this->posSide = $posSide; | ||
| 21 | + $this->side = $side; | ||
| 22 | + $this->price = $price; | ||
| 23 | + $this->qty = $qty; | ||
| 24 | + } | ||
| 25 | + public function setCliOrdId($cliOrdId) | ||
| 26 | + { | ||
| 27 | + $this->cliOrdId = $cliOrdId; | ||
| 28 | + } | ||
| 29 | + public function getCliOrdId() | ||
| 30 | + { | ||
| 31 | + return $this->cliOrdId; | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + public function toArray() | ||
| 35 | + { | ||
| 36 | + $arr = [ | ||
| 37 | + 'symbol' => $this->symbol, | ||
| 38 | + 'ordType' => $this->ordType, | ||
| 39 | + 'posSide' => $this->posSide, | ||
| 40 | + 'side' => $this->side, | ||
| 41 | + 'price' => $this->price, | ||
| 42 | + 'qty' => $this->qty, | ||
| 43 | + ]; | ||
| 44 | + if ($this->cliOrdId != "") { | ||
| 45 | + $arr['cliOrdId'] = $this->cliOrdId; | ||
| 46 | + } | ||
| 47 | + return $arr; | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + public function toOkxOrder(array $symbolInfos, callable $toSymbolOri) | ||
| 51 | + { | ||
| 52 | + $instId = call_user_func($toSymbolOri, $this->symbol); | ||
| 53 | + /** @var SymbolInfo $symbolInfo */ | ||
| 54 | + $symbolInfo = $symbolInfos[$this->symbol]; | ||
| 55 | + $order = [ | ||
| 56 | + 'instId' => $instId, | ||
| 57 | + 'tdMode' => 'cross', | ||
| 58 | + 'posSide' => strtolower($this->posSide), | ||
| 59 | + 'side' => strtolower($this->side), | ||
| 60 | + 'ordType' => $this->ordType, | ||
| 61 | + ]; | ||
| 62 | + if ($this->ordType == 'LIMIT' || $this->ordType == 'IOC') { | ||
| 63 | + $order['px'] = round($this->price, $symbolInfo->pricePrec); | ||
| 64 | + } | ||
| 65 | + $lot = $this->qty / $symbolInfo->ctVal; | ||
| 66 | + $order['sz'] = round($lot, $symbolInfo->lotPrec); | ||
| 67 | + if ($this->ordType == 'ONLYMAKER') { | ||
| 68 | + $order['ordType'] = 'POST_ONLY'; | ||
| 69 | + } | ||
| 70 | + $order['ordType'] = strtolower($order['ordType']); | ||
| 71 | + if ($this->cliOrdId != "") { | ||
| 72 | + $order['clOrdId'] = $this->cliOrdId; | ||
| 73 | + } | ||
| 74 | + return $order; | ||
| 75 | + } | ||
| 76 | +} |
trader/struct/Pos.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +namespace trader\struct; | ||
| 4 | + | ||
| 5 | +require_once __DIR__ . '/WsDataPos.php'; | ||
| 6 | + | ||
| 7 | +use trader\struct\WsDataPos; | ||
| 8 | + | ||
| 9 | +class Pos | ||
| 10 | +{ | ||
| 11 | + public $symbol; | ||
| 12 | + public $posSide; | ||
| 13 | + public $qty; | ||
| 14 | + public $lot; | ||
| 15 | + public $avgPrice; | ||
| 16 | + public $pnl; | ||
| 17 | + public $lever; | ||
| 18 | + public $margin; | ||
| 19 | + | ||
| 20 | + public function __construct($symbol, $posSide, $qty, $lot, $avgPrice, $pnl, $lever, $margin) | ||
| 21 | + { | ||
| 22 | + $this->symbol = $symbol; | ||
| 23 | + $this->posSide = $posSide; | ||
| 24 | + $this->qty = $qty; | ||
| 25 | + $this->lot = $lot; | ||
| 26 | + $this->avgPrice = $avgPrice; | ||
| 27 | + $this->pnl = $pnl; | ||
| 28 | + $this->lever = $lever; | ||
| 29 | + $this->margin = $margin; | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + public function toArray() | ||
| 33 | + { | ||
| 34 | + return [ | ||
| 35 | + 'symbol' => $this->symbol, | ||
| 36 | + 'posSide' => $this->posSide, | ||
| 37 | + 'qty' => $this->qty, | ||
| 38 | + 'lot' => $this->lot, | ||
| 39 | + 'avgPrice' => $this->avgPrice, | ||
| 40 | + 'pnl' => $this->pnl, | ||
| 41 | + 'lever' => $this->lever, | ||
| 42 | + 'margin' => $this->margin, | ||
| 43 | + ]; | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + public static function transferOkxPos($pos, $symbolInfos, callable $toSymbolSt) | ||
| 47 | + { | ||
| 48 | + $symbol = call_user_func($toSymbolSt, $pos['instId']); | ||
| 49 | + /** @var SymbolInfo $symbolInfo */ | ||
| 50 | + $symbolInfo = $symbolInfos[$symbol]; | ||
| 51 | + $posSide = strtoupper($pos['posSide']); | ||
| 52 | + $lot = abs($pos['pos']); | ||
| 53 | + $qty = round($lot * $symbolInfo->ctVal, $symbolInfo->qtyPrec); | ||
| 54 | + $avgPrice = $pos['avgPx']; | ||
| 55 | + $pnl = $pos['upl']; | ||
| 56 | + $lever = $pos['lever']; | ||
| 57 | + $margin = $pos['imr']; | ||
| 58 | + return new Pos($symbol, $posSide, $qty, $lot, $avgPrice, $pnl, $lever, $margin); | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + public static function transferWsDataPos(WsDataPos $wsDataPos) | ||
| 62 | + { | ||
| 63 | + return new Pos($wsDataPos->symbol, $wsDataPos->posSide, $wsDataPos->qty, $wsDataPos->lot, $wsDataPos->avgPrice, $wsDataPos->pnl, 0, 0); | ||
| 64 | + } | ||
| 65 | +} |
trader/struct/Premium.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +namespace trader\PreOrd; | ||
| 4 | + | ||
| 5 | +class Premium | ||
| 6 | +{ | ||
| 7 | + private $symbol; | ||
| 8 | + private $rate; | ||
| 9 | + private $settleTs; | ||
| 10 | + private $settleTime; | ||
| 11 | + | ||
| 12 | + public function __construct($symbol, $rate, $settleTs, $settleTime) | ||
| 13 | + { | ||
| 14 | + $this->symbol = $symbol; | ||
| 15 | + $this->rate = $rate; | ||
| 16 | + $this->settleTs = $settleTs; | ||
| 17 | + $this->settleTime = $settleTime; | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + public function toArray() | ||
| 21 | + { | ||
| 22 | + return [ | ||
| 23 | + 'symbol' => $this->symbol, | ||
| 24 | + 'rate' => $this->rate, | ||
| 25 | + 'settleTs' => $this->settleTs, | ||
| 26 | + 'settleTime' => $this->settleTime, | ||
| 27 | + ]; | ||
| 28 | + } | ||
| 29 | +} |
trader/struct/SymbolInfo.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +namespace trader\struct; | ||
| 4 | + | ||
| 5 | +require_once __DIR__ . '/../../vendor/autoload.php'; | ||
| 6 | + | ||
| 7 | +use function Jiaoyin\getPrecision; | ||
| 8 | + | ||
| 9 | +class SymbolInfo | ||
| 10 | +{ | ||
| 11 | + public string $symbolOri; // 原始交易对 | ||
| 12 | + public string $symbol; // 标准交易对 | ||
| 13 | + public string $ctVal; // 合约面值 | ||
| 14 | + public string $pricePrec; | ||
| 15 | + public string $qtyPrec; | ||
| 16 | + public string $lotPrec; | ||
| 17 | + public string $minLot; // 最小下单张数 | ||
| 18 | + public string $minQty; // 最小下单数量 | ||
| 19 | + | ||
| 20 | + public function __construct($symbolOri, $symbol, $ctVal, $pricePrec, $qtyPrec, $lotPrec, $minLot, $minQty) | ||
| 21 | + { | ||
| 22 | + $this->symbolOri = $symbolOri; | ||
| 23 | + $this->symbol = $symbol; | ||
| 24 | + $this->ctVal = $ctVal; | ||
| 25 | + $this->pricePrec = $pricePrec; | ||
| 26 | + $this->qtyPrec = $qtyPrec; | ||
| 27 | + $this->lotPrec = $lotPrec; | ||
| 28 | + $this->minLot = $minLot; | ||
| 29 | + $this->minQty = $minQty; | ||
| 30 | + } | ||
| 31 | + public function toArray() | ||
| 32 | + { | ||
| 33 | + return [ | ||
| 34 | + 'symbolOri' => $this->symbolOri, | ||
| 35 | + 'symbol' => $this->symbol, | ||
| 36 | + 'ctVal' => $this->ctVal, | ||
| 37 | + 'pricePrec' => $this->pricePrec, | ||
| 38 | + 'qtyPrec' => $this->qtyPrec, | ||
| 39 | + 'lotPrec' => $this->lotPrec, | ||
| 40 | + 'minLot' => $this->minLot, | ||
| 41 | + 'minQty' => $this->minQty, | ||
| 42 | + ]; | ||
| 43 | + } | ||
| 44 | + public static function transferOkx($data, callable $getSymbolSt): SymbolInfo | ||
| 45 | + { | ||
| 46 | + $symbolSt = $getSymbolSt($data["instId"]); //转换为标准交易对 | ||
| 47 | + $pricePrec = getPrecision($data['tickSz']); | ||
| 48 | + $ctVal = $data['ctVal']; | ||
| 49 | + $minLot = $data['minSz']; | ||
| 50 | + $minQty = $minLot * $ctVal; | ||
| 51 | + $qtyPrec = getPrecision($minQty); | ||
| 52 | + $lotPrec = getPrecision($minLot); | ||
| 53 | + $info = new SymbolInfo($data["instId"], $symbolSt, $ctVal, $pricePrec, $qtyPrec, $lotPrec, $minLot, $minQty); | ||
| 54 | + return $info; | ||
| 55 | + } | ||
| 56 | +} |
trader/struct/WsData.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +namespace trader\struct; | ||
| 4 | + | ||
| 5 | +require_once __DIR__ . '/WsDataTrade.php'; | ||
| 6 | + | ||
| 7 | +use trader\struct\WsDataTrade; | ||
| 8 | +use trader\struct\WsDataPos; | ||
| 9 | +use trader\struct\WsDataOrder; | ||
| 10 | + | ||
| 11 | +// 综合ws数据 | ||
| 12 | +class WsData | ||
| 13 | +{ | ||
| 14 | + public string $platform; | ||
| 15 | + public string $dataType; // trade | order | account | pos | ||
| 16 | + public ?WsDataTrade $trade = null; | ||
| 17 | + public ?WsDataPos $pos = null; | ||
| 18 | + public ?WsDataOrder $order = null; | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + public function __construct(string $platform, string $dataType, WsDataTrade $trade = null, WsDataPos $pos = null, WsDataOrder $order = null) | ||
| 22 | + { | ||
| 23 | + $this->platform = $platform; | ||
| 24 | + $this->dataType = $dataType; | ||
| 25 | + $this->trade = $trade; | ||
| 26 | + $this->pos = $pos; | ||
| 27 | + $this->order = $order; | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + public function toArray() | ||
| 31 | + { | ||
| 32 | + return [ | ||
| 33 | + 'platform' => $this->platform, | ||
| 34 | + 'dataType' => $this->dataType, | ||
| 35 | + 'trade' => $this->trade?->toArray(), | ||
| 36 | + 'pos' => $this->pos?->toArray(), | ||
| 37 | + 'order' => $this->order?->toArray(), | ||
| 38 | + ]; | ||
| 39 | + } | ||
| 40 | +} |
trader/struct/WsDataOrder.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +namespace trader\struct; | ||
| 4 | + | ||
| 5 | +require_once __DIR__ . '/../autoload.php'; | ||
| 6 | + | ||
| 7 | +use trader\struct\SymbolInfo; | ||
| 8 | + | ||
| 9 | +class WsDataOrder | ||
| 10 | +{ | ||
| 11 | + public string $platform; | ||
| 12 | + public string $posSide; //大写 | ||
| 13 | + public string $symbol; //大写 | ||
| 14 | + public string $side; //大写 | ||
| 15 | + public float $price; //下单价格 | ||
| 16 | + public float $avgPx; //成交均价 | ||
| 17 | + public float $qty; //币种真实数量 | ||
| 18 | + public float $lot; //合约张数 | ||
| 19 | + public float $pnl; //盈亏 | ||
| 20 | + public int $ts; //下单时间戳 ms | ||
| 21 | + public int $uts; //更新时间戳 ms | ||
| 22 | + public string $ordType; //下单类型 | ||
| 23 | + public string $cliOrdId; //客户端订单号 | ||
| 24 | + public string $ordId; //订单号 | ||
| 25 | + public string $status; //订单状态 | ||
| 26 | + | ||
| 27 | + 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) | ||
| 28 | + { | ||
| 29 | + $this->platform = $platform; | ||
| 30 | + $this->posSide = $posSide; | ||
| 31 | + $this->symbol = $symbol; | ||
| 32 | + $this->side = $side; | ||
| 33 | + $this->price = $price; | ||
| 34 | + $this->avgPx = $avgPx; | ||
| 35 | + $this->qty = $qty; | ||
| 36 | + $this->lot = $lot; | ||
| 37 | + $this->pnl = $pnl; | ||
| 38 | + $this->ts = $ts; | ||
| 39 | + $this->uts = $uts; | ||
| 40 | + $this->ordType = $ordType; | ||
| 41 | + $this->cliOrdId = $cliOrdId; | ||
| 42 | + $this->ordId = $ordId; | ||
| 43 | + $this->status = $status; | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + public function toArray() | ||
| 47 | + { | ||
| 48 | + return [ | ||
| 49 | + 'platform' => $this->platform, | ||
| 50 | + 'posSide' => $this->posSide, | ||
| 51 | + 'symbol' => $this->symbol, | ||
| 52 | + 'side' => $this->side, | ||
| 53 | + 'price' => $this->price, | ||
| 54 | + 'avgPx' => $this->avgPx, | ||
| 55 | + 'qty' => $this->qty, | ||
| 56 | + 'lot' => $this->lot, | ||
| 57 | + 'pnl' => $this->pnl, | ||
| 58 | + 'ts' => $this->ts, | ||
| 59 | + 'uts' => $this->uts, | ||
| 60 | + 'ordType' => $this->ordType, | ||
| 61 | + 'cliOrdId' => $this->cliOrdId, | ||
| 62 | + 'ordId' => $this->ordId, | ||
| 63 | + 'status' => $this->status, | ||
| 64 | + ]; | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + public static function TransferOkxOrder(array $data, array $symbolInfos, callable $toSymbolSt): WsDataOrder|null | ||
| 68 | + { | ||
| 69 | + $symbol = call_user_func($toSymbolSt, $data['instId']); | ||
| 70 | + /** @var SymbolInfo $symbolInfo */ | ||
| 71 | + $symbolInfo = $symbolInfos[$symbol] ?? null; | ||
| 72 | + if ($symbolInfo == null) { | ||
| 73 | + return null; | ||
| 74 | + } | ||
| 75 | + $platform = 'okx'; | ||
| 76 | + $posSide = strtoupper($data['posSide']); | ||
| 77 | + $side = strtoupper($data['side']); | ||
| 78 | + $price = (float)$data['px']; | ||
| 79 | + $avgPx = (float)$data['avgPx']; | ||
| 80 | + $lot = (float)$data['sz']; | ||
| 81 | + $qty = round($lot * $symbolInfo->ctVal, $symbolInfo->qtyPrec); | ||
| 82 | + $pnl = (float)$data['pnl']; | ||
| 83 | + $ts = (int)$data['cTime']; | ||
| 84 | + $uts = (int)$data['uTime']; | ||
| 85 | + $ordType = strtoupper($data['ordType']); | ||
| 86 | + $cliOrdId = $data['clOrdId']; | ||
| 87 | + $ordId = $data['ordId']; | ||
| 88 | + $status = self::getStatus($data['state']); | ||
| 89 | + if ($data['cancelSource'] != '') { | ||
| 90 | + $status = 'CANCELED'; | ||
| 91 | + } | ||
| 92 | + return new WsDataOrder($platform, $posSide, $symbol, $side, $price, $avgPx, $qty, $lot, $pnl, $ts, $uts, $ordType, $cliOrdId, $ordId, $status); | ||
| 93 | + } | ||
| 94 | + private static function getStatus(string $state) | ||
| 95 | + { | ||
| 96 | + if ($state == 'live') { | ||
| 97 | + return 'LIVE'; | ||
| 98 | + } | ||
| 99 | + if ($state == 'partially_filled') { | ||
| 100 | + return 'PARTIALLY_FILLED'; | ||
| 101 | + } | ||
| 102 | + if ($state == 'filled') { | ||
| 103 | + return 'FILLED'; | ||
| 104 | + } | ||
| 105 | + if ($state == 'canceled') { | ||
| 106 | + return 'CANCELED'; | ||
| 107 | + } | ||
| 108 | + return 'UNKNOWN'; | ||
| 109 | + } | ||
| 110 | +} |
trader/struct/WsDataPos.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +namespace trader\struct; | ||
| 4 | + | ||
| 5 | +require_once __DIR__ . '/../autoload.php'; | ||
| 6 | + | ||
| 7 | +use trader\struct\SymbolInfo; | ||
| 8 | + | ||
| 9 | +class WsDataPos | ||
| 10 | +{ | ||
| 11 | + public string $symbol; | ||
| 12 | + public string $posSide; //大写 | ||
| 13 | + public float $qty; //币种真实数量 | ||
| 14 | + public float $lot; //合约张数 | ||
| 15 | + public float $avgPrice; //平均持仓价 | ||
| 16 | + public bool $isSnapshot; //是否快照; | ||
| 17 | + public float $pnl; //盈亏 | ||
| 18 | + | ||
| 19 | + public function __construct($symbol, $posSide, $qty, $lot, $avgPrice, $isSnapshot, $pnl) | ||
| 20 | + { | ||
| 21 | + $this->symbol = $symbol; | ||
| 22 | + $this->posSide = $posSide; | ||
| 23 | + $this->qty = $qty; | ||
| 24 | + $this->lot = $lot; | ||
| 25 | + $this->avgPrice = $avgPrice; | ||
| 26 | + $this->isSnapshot = $isSnapshot; | ||
| 27 | + $this->pnl = $pnl; | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + public function toArray() | ||
| 31 | + { | ||
| 32 | + return [ | ||
| 33 | + 'symbol' => $this->symbol, | ||
| 34 | + 'posSide' => $this->posSide, | ||
| 35 | + 'qty' => $this->qty, | ||
| 36 | + 'lot' => $this->lot, | ||
| 37 | + 'avgPrice' => $this->avgPrice, | ||
| 38 | + 'isSnapshot' => $this->isSnapshot, | ||
| 39 | + 'pnl' => $this->pnl, | ||
| 40 | + ]; | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + public static function TransferOkxPos(array $data, array $symbolInfos, callable $toSymbolSt): WsDataPos|null | ||
| 44 | + { | ||
| 45 | + $symbol = call_user_func($toSymbolSt, $data['instId']); | ||
| 46 | + /** @var SymbolInfo $symbolInfo */ | ||
| 47 | + $symbolInfo = $symbolInfos[$symbol] ?? null; | ||
| 48 | + if ($symbolInfo === null) { | ||
| 49 | + return null; | ||
| 50 | + } | ||
| 51 | + $posSide = strtoupper($data['posSide']); | ||
| 52 | + $lot = (float)$data['pos']; | ||
| 53 | + $qty = round($lot * $symbolInfo->ctVal, $symbolInfo->qtyPrec); | ||
| 54 | + $avgPrice = (float)$data['avgPx']; | ||
| 55 | + $pnl = (float)$data['upl']; | ||
| 56 | + return new WsDataPos($symbol, $posSide, $qty, $lot, $avgPrice, false, $pnl); | ||
| 57 | + } | ||
| 58 | +} |
trader/struct/WsDataTrade.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +namespace trader\struct; | ||
| 4 | + | ||
| 5 | +require_once __DIR__ . '/../autoload.php'; | ||
| 6 | + | ||
| 7 | +use trader\struct\SymbolInfo; | ||
| 8 | + | ||
| 9 | +class WsDataTrade | ||
| 10 | +{ | ||
| 11 | + public string $platform; | ||
| 12 | + public string $posSide; //大写 | ||
| 13 | + public string $symbol; //大写 | ||
| 14 | + public string $side; //大写 | ||
| 15 | + public float $price; //成交价格 | ||
| 16 | + public float $qty; //币种真实数量 | ||
| 17 | + public float $lot; //合约张数 | ||
| 18 | + public float $pnl; //盈亏 | ||
| 19 | + public float $quoteVol; //计价货币数量 成交额 | ||
| 20 | + public int $ts; //时间戳 ms | ||
| 21 | + public string $tradeId; | ||
| 22 | + | ||
| 23 | + public function __construct($platform, $posSide, $symbol, $side, $price, $qty, $lot, $pnl, $quoteVol, $ts, $tradeId) | ||
| 24 | + { | ||
| 25 | + $this->platform = $platform; | ||
| 26 | + $this->posSide = $posSide; | ||
| 27 | + $this->symbol = $symbol; | ||
| 28 | + $this->side = $side; | ||
| 29 | + $this->price = $price; | ||
| 30 | + $this->qty = $qty; | ||
| 31 | + $this->lot = $lot; | ||
| 32 | + $this->pnl = $pnl; | ||
| 33 | + $this->quoteVol = $quoteVol; | ||
| 34 | + $this->ts = $ts; | ||
| 35 | + $this->tradeId = $tradeId; | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + public function toArray() | ||
| 39 | + { | ||
| 40 | + return [ | ||
| 41 | + 'platform' => $this->platform, | ||
| 42 | + 'posSide' => $this->posSide, | ||
| 43 | + 'symbol' => $this->symbol, | ||
| 44 | + 'side' => $this->side, | ||
| 45 | + 'price' => $this->price, | ||
| 46 | + 'qty' => $this->qty, | ||
| 47 | + 'lot' => $this->lot, | ||
| 48 | + 'pnl' => $this->pnl, | ||
| 49 | + 'quoteVol' => $this->quoteVol, | ||
| 50 | + 'ts' => $this->ts, | ||
| 51 | + 'tradeId' => $this->tradeId, | ||
| 52 | + ]; | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + public static function TransferOkxOrder(array $data, array $symbolInfos, callable $toSymbolSt): WsDataTrade|null | ||
| 56 | + { | ||
| 57 | + if ($data['fillSz'] != "" && $data['fillSz'] != "0") { | ||
| 58 | + $symbol = call_user_func($toSymbolSt, $data['instId']); | ||
| 59 | + /** @var SymbolInfo $symbolInfo */ | ||
| 60 | + $symbolInfo = $symbolInfos[$symbol] ?? null; | ||
| 61 | + if ($symbolInfo == null) { | ||
| 62 | + return null; | ||
| 63 | + } | ||
| 64 | + $platform = 'okx'; | ||
| 65 | + $posSide = strtoupper($data['posSide']); | ||
| 66 | + $side = strtoupper($data['side']); | ||
| 67 | + $price = (float)$data['fillPx']; | ||
| 68 | + $lot = (float)$data['fillSz']; | ||
| 69 | + $qty = round($lot * $symbolInfo->ctVal, $symbolInfo->qtyPrec); | ||
| 70 | + $pnl = (float)$data['fillPnl']; | ||
| 71 | + $quoteVol = $price * $qty; | ||
| 72 | + $ts = $data['uTime']; | ||
| 73 | + $tradeId = $data['tradeId']; | ||
| 74 | + return new WsDataTrade($platform, $posSide, $symbol, $side, $price, $qty, $lot, $pnl, $quoteVol, $ts, $tradeId); | ||
| 75 | + } | ||
| 76 | + return null; | ||
| 77 | + } | ||
| 78 | +} |
-
请 注册 或 登录 后发表评论