作者 karlet

feat:增加trader

logs
vendor
composer.lock
... ...
{
"name": "jiaoyin/common",
"name": "jiaoyin/base",
"description": "indigo common php lib by jy",
"require": {
"ext-json": "*"
},
"autoload": {
"psr-4": {
"Jiaoyin\\": "./src/"
"Jiaoyin\\": "./src/",
"Trader\\": "./trader/"
},
"files": [ "src/functions.php" ]
}
"files": [ "src/functions.php", "trader/common/func.php" ]
},
"require-dev": {
"swoole/ide-helper": "^5.0"
}
}
... ...
<?php
namespace Jiaoyin;
namespace jiaoyin;
class Auth
{
... ...
<?php
namespace Jiaoyin;
namespace jiaoyin;
/*
binance合约接口
... ...
<?php
namespace Jiaoyin;
namespace jiaoyin;
/*
binance现货接口
... ...
<?php
namespace Jiaoyin;
namespace jiaoyin;
class Curl
{
... ...
<?php
namespace Jiaoyin;
namespace jiaoyin;
class Feishu{
const user_liquid = 'gd1d6b34';
... ...
<?php
namespace Jiaoyin;
namespace jiaoyin;
class KlineAssist
{
... ...
<?php
namespace Jiaoyin;
namespace jiaoyin;
require_once __DIR__ . '/functions.php';
class Logger
{
... ...
<?php
namespace Jiaoyin;
namespace jiaoyin;
class MongoCli
{
... ...
<?php
namespace Jiaoyin;
namespace jiaoyin;
use MongoDB\Client;
use Swoole\Coroutine;
... ...
<?php
namespace Jiaoyin;
namespace jiaoyin;
use Swoole\Database\MysqliConfig;
use Swoole\Database\MysqliPool;
... ...
<?php
namespace Jiaoyin;
namespace jiaoyin;
/*
所有接口
... ...
<?php
namespace Jiaoyin;
namespace jiaoyin;
use Swoole\Database\RedisConfig;
use Swoole\Database\RedisPool;
... ...
<?php
namespace Jiaoyin;
namespace jiaoyin;
class SimpleRequest
{
... ...
<?php
namespace Jiaoyin;
namespace jiaoyin;
use Swoole\Http\Request;
use Swoole\Http\Response;
use Swoole\Http\Server;
... ...
<?php
namespace Jiaoyin;
namespace jiaoyin;
use Swoole\Http\Request;
use Swoole\Http\Response;
use Swoole\Coroutine\Http\Server;
... ...
<?php
namespace Jiaoyin;
namespace jiaoyin;
use Swoole\Coroutine;
use Swoole\Coroutine\Http\Client;
... ... @@ -26,7 +26,7 @@ class Websocket
private int $timerPing = 0;
private string $desc = 'websocket未命名';
private int $pingCount = 0;
private int $pingDelay = 10000;//ping tick时间 10s
private int $pingDelay = 10000; //ping tick时间 10s
// 例如:wss://fstream.binance.com/stream、ws://175.178.36.217:9528/spot/stream
public function __construct($url, $desc = null)
... ... @@ -64,9 +64,13 @@ class Websocket
$this->client?->close();
}
public function connect(callable $onOpen = null, callable $onMessage = null,
callable $onClose = null, $onPing = null, $onPong = null): void
{
public function connect(
callable $onOpen = null,
callable $onMessage = null,
callable $onClose = null,
$onPing = null,
$onPong = null
): void {
$this->close();
$this->pingCount = 0;
$this->onOpen = $onOpen;
... ... @@ -80,8 +84,8 @@ class Websocket
$ret = $client->upgrade($this->path);
if ($ret) {
output($this->desc, "连接成功");
$this->client=&$client;
swoole_timer_after(50, function () use ($onOpen,$client) {
$this->client = &$client;
swoole_timer_after(50, function () use ($onOpen, $client) {
if ($onOpen) {
call_user_func($onOpen, $client);
}
... ... @@ -90,7 +94,7 @@ class Websocket
while ($client) {
$frame = $client->recv($this->recvTimeout);
if (!$frame && $client->errCode != 60) {
output($this->desc,'错误码:' . $client->errCode.",错误数据:", $frame);
output($this->desc, '错误码:' . $client->errCode . ",错误数据:", $frame);
break;
}
$this->lastRecvTime = time();
... ... @@ -185,4 +189,4 @@ class Websocket
}
});
}
}
\ No newline at end of file
}
... ...
<?php
namespace Jiaoyin;
namespace jiaoyin;
//格式化输出
use Swoole\Process;
use Swoole\Event;
... ...
<?php
namespace trader;
require_once __DIR__ . '/autoload.php';
require_once __DIR__ . '/../vendor/autoload.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\Pos;
use \Exception;
use function Jiaoyin\timeFormat;
use trader\okx\Api as OkApi;
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;
}
$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();
}
}
... ...
<?php
require_once __DIR__ . '/CmBroker.php';
require_once __DIR__ . '/common/tool.php';
require_once __DIR__ . '/struct/ApiInfo.php';
require_once __DIR__ . '/struct/SymbolInfo.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/WsDataOrder.php';
require_once __DIR__ . '/struct/Pos.php';
require_once __DIR__ . '/struct/Kline.php';
require_once __DIR__ . '/exchange/okx/ExBroker.php';
... ...
<?php
namespace trader;
function scToNum($scStr)
{
$check_str = '';
if (strpos($scStr, 'e') !== false) {
$check_str = 'e';
}
if (strpos($scStr, 'E') !== false) {
$check_str = 'E';
}
if (empty($check_str)) {
return $scStr; //非科学计数直接返回。
}
$num_length = 0;
$split_array = explode($check_str, $scStr);
$num_length += strlen($split_array[0]);
$num_length += (int)str_replace(['-', '+'], '', $split_array[1]);
$float_number = (float)$scStr;
$decimal_string = number_format($float_number, $num_length, '.', '');
$num = rtrim(rtrim($decimal_string, '0'), '.'); //去除小数后多余的0
return $num;
}
function tsToISO($timestamp)
{
$datetime = new \DateTime();
$datetime->setTimestamp(floor($timestamp / 1000));
$datetime->setTimezone(new \DateTimeZone('UTC'));
$milliseconds = sprintf('.%03d', $timestamp % 1000);
return $datetime->format('Y-m-d\TH:i:s') . $milliseconds . 'Z';
}
... ...
<?php
namespace trader\binance;
require_once __DIR__ . '/../../autoload.php';
require_once __DIR__ . '/../../../vendor/autoload.php';
use trader\struct\ApiInfo;
class Api
{
private $host = 'https://fapi.binance.com';
private ApiInfo $apiInfo;
public function __construct(ApiInfo $apiInfo)
{
$this->apiInfo = $apiInfo;
}
public function getPremiumIndex()
{
$url = "/fapi/v1/premiumIndex";
$method = "GET";
}
}
... ...
<?php
namespace trader\binance;
require_once __DIR__ . '/../../../vendor/autoload.php';
require_once __DIR__ . '/Api.php';
use trader\struct\ApiInfo;
use trader\binance\Api as BnApi;
use Jiaoyin\Websocket;
class ExBroker
{
// static private $host = 'wss://ws.okx.com:8443';
static private $host = 'ws://okws.keetu.com';
static private $pathPrivate = '/ws/v5/private';
static private $pathPublic = '/ws/v5/public';
static private $pathBusiness = '/ws/v5/business';
private ApiInfo $apiInfo;
private BnApi $api;
private ?Websocket $wsAcc;
private ?Websocket $wsKline;
public function __construct(ApiInfo $apiInfo)
{
$this->apiInfo = $apiInfo;
$this->api = new BnApi($apiInfo);
}
//获取所有品种资金费率
public function getAllPremium() {
$res = $this->api->getPremiumIndex();
}
}
... ...
<?php
namespace trader\okx;
require_once __DIR__ . '/../../autoload.php';
require_once __DIR__ . '/../../../vendor/autoload.php';
use trader\struct\ApiInfo;
use Jiaoyin\Curl;
use function Jiaoyin\getMicrotime;
use function trader\tsToISO;
class Api
{
private ?ApiInfo $apiInfo = null;
// private string $host = "https://www.okx.com";
private string $host = "http://okapi.keetu.com";
public function __construct($apiInfo = null, $host = "")
{
if ($apiInfo != null) {
$this->apiInfo = $apiInfo;
}
if ($host != "") {
$this->host = $host;
}
}
//-----------public interface ------------
//所有交易产品基础信息
public function instruments($param)
{
$path = '/api/v5/public/instruments';
$method = 'GET';
return $this->request($path, $method, $param);
}
public function positionTiers($param)
{
$path = '/api/v5/public/position-tiers';
$method = 'GET';
return $this->request($path, $method, $param);
}
//获取资金费率
public function fundingRate($param)
{
$path = '/api/v5/public/funding-rate';
$method = 'GET';
return $this->request($path, $method, $param);
}
//-----------private interface ------------
// 查询杠杆
public function leverageInfo($param)
{
$path = '/api/v5/account/leverage-info';
$method = 'GET';
return $this->request($path, $method, $param, $this->apiInfo);
}
//设置杠杆
public function setLeverage($param)
{
$path = '/api/v5/account/set-leverage';
$method = 'POST';
return $this->request($path, $method, $param, $this->apiInfo);
}
//下单
public function placeOrder($param)
{
$path = '/api/v5/trade/order';
$method = 'POST';
return $this->request($path, $method, $param, $this->apiInfo);
}
//-------------------------------------
private function createSign($timestamp, $method, $requestPath, $secretKey, $body = '')
{
$sign = base64_encode(hash_hmac('sha256', $timestamp . $method . $requestPath . $body, $secretKey, true));
return $sign;
}
private function request($path, $method, $param)
{
$header = [];
$body = '';
$fullPath = $path;
if ($method == 'GET' && count($param) > 0) {
$fullPath = $path . '?' . http_build_query($param);
} else {
$header[] = 'Content-Type: application/json';
$body = json_encode($param);
}
if ($this->apiInfo != null) {
$timestamp = tsToISO(getMicrotime());
$header[] = 'OK-ACCESS-KEY: ' . $this->apiInfo->key;
$header[] = 'OK-ACCESS-PASSPHRASE: ' . $this->apiInfo->passphrase;
$header[] = 'OK-ACCESS-TIMESTAMP: ' . $timestamp;
$sign = $this->createSign($timestamp, $method, $fullPath, $this->apiInfo->secret, $body);
$header[] = 'OK-ACCESS-SIGN: ' . $sign;
}
$url = $this->host . $path;
$result = "";
if ($method == 'POST') {
$result = Curl::httpPost($url, $param, $header);
} else if ($method == 'GET') {
$result = Curl::httpGet($url, $param, $header);
} else {
$result = Curl::httpGet($url, $param, $header);
}
return json_decode($result, true);
}
}
... ...
<?php
namespace trader\okx;
require_once __DIR__ . '/../../../vendor/autoload.php';
require_once __DIR__ . '/Api.php';
use trader\struct\ApiInfo;
use trader\okx\Api as OkxApi;
use Jiaoyin\Websocket;
use function Jiaoyin\output;
class ExBroker
{
// static private $host = 'wss://ws.okx.com:8443';
static private $host = 'ws://okws.keetu.com';
static private $pathPrivate = '/ws/v5/private';
static private $pathPublic = '/ws/v5/public';
static private $pathBusiness = '/ws/v5/business';
private ApiInfo $apiInfo;
private OkxApi $api;
private ?Websocket $wsAcc;
private ?Websocket $wsKline;
public function __construct($apiInfo)
{
$this->apiInfo = $apiInfo;
$this->api = new OkxApi($apiInfo);
}
public function accListen(callable $onWsData)
{
if (isset($this->wsAcc)) {
$this->wsAcc->close();
}
$this->wsAcc = new Websocket(self::$host . self::$pathPrivate);
$this->wsAcc->connect(
$onOpen = function () {
$this->wsLogin();
},
$onMessage = function ($data) use ($onWsData) {
$this->onWsDataPre($data, $onWsData);
},
$onClose = function () {
// 关闭链接
}
);
}
public function klineListen($symbol, $period, callable $onData)
{
if (isset($this->wsKline)) {
$this->wsKline->close();
}
$this->wsKline = new Websocket(self::$host . self::$pathBusiness);
$this->wsKline->connect(
$onOpen = function () use ($symbol, $period) {
$subData = [];
$subData['op'] = 'subscribe';
$subData['args'] = [
[
'channel' => $this->getKlineChannels()[$period],
'instId' => $symbol,
],
];
output("订阅", $subData);
$this->wsKline->push(json_encode($subData));
},
$onMessage = function ($data) use ($onData) {
$data = json_decode($data, true);
if (!isset($data['data'])) {
return;
}
$data = $data['data'][0];
$onData($data);
},
$onClose = function () {
// 关闭链接
}
);
}
private function crateWsSign($timeStamp)
{
$method = 'GET';
$path = '/users/self/verify';
$str = $timeStamp . $method . $path;
$sign = hash_hmac('sha256', $str, $this->apiInfo->secret, true);
$sign = base64_encode($sign);
return $sign;
}
private function wsLogin()
{
$ts = time();
$subData = [];
$subData['op'] = 'login';
$subData['args'] = [
[
'apiKey' => $this->apiInfo->key,
'passphrase' => $this->apiInfo->passphrase,
'timestamp' => $ts,
'sign' => $this->crateWsSign($ts),
]
];
$this->wsAcc->push(json_encode($subData));
}
// ws 消息预处理
private function onWsDataPre($data, callable $onWsData)
{
$data = json_decode($data, true);
if (isset($data['event'])) {
if ($data['event'] == 'login' && $data['code'] == '0') {
output('ws登录成功');
$this->wsSubscribe();
return;
}
if ($data['event'] == 'subscribe' || $data['event'] == 'unsubscribe' || $data['event'] == 'channel-conn-count') {
// output("ws 过滤数据", $data);
return;
}
output("ok ws 未处理数据", $data);
}
call_user_func($onWsData, $data);
}
// 订阅
private function wsSubscribe()
{
$subData = [];
$subData['op'] = 'subscribe';
$subData['args'] = [
[
'channel' => 'orders',
'instType' => 'SWAP',
],
[
'channel' => 'positions',
'instType' => 'SWAP',
],
];
$this->wsAcc->push(json_encode($subData));
}
public function placeOrder($param)
{
return $this->api->placeOrder($param);
}
public function getKlineChannels()
{
return [
'1s' => 'candle1s',
'1m' => 'candle1m',
'5m' => 'candle5m',
'15m' => 'candle15m',
'30m' => 'candle30m',
'1h' => 'candle1H',
'2h' => 'candle2H',
'4h' => 'candle4H',
'6h' => 'candle6H',
'12h' => 'candle12H',
'1d' => 'candle1D',
];
}
public function getSymbolInfos()
{
$res = $this->api->instruments(["instType" => "SWAP"]);
if ($res['code'] != '0') {
return [];
}
return $res['data'];
}
public function stopListen()
{
$this->wsAcc->close();
$this->wsKline->close();
}
}
... ...
<?php
namespace trader\struct;
class ApiInfo
{
public string $key = '';
public string $secret = '';
public string $passphrase = '';
public function __construct($key, $secret, $passphrase)
{
$this->key = $key;
$this->secret = $secret;
$this->passphrase = $passphrase;
}
}
... ...
<?php
namespace trader\struct;
class Kline
{
public $time;
public $open;
public $high;
public $low;
public $close;
public $vol;
public $volQuote;
public function __construct($time, $open, $high, $low, $close, $vol, $volQuote)
{
$this->time = $time;
$this->open = $open;
$this->high = $high;
$this->low = $low;
$this->close = $close;
$this->vol = $vol;
$this->volQuote = $volQuote;
}
public function toArray()
{
return [
'time' => $this->time,
'open' => $this->open,
'high' => $this->high,
'low' => $this->low,
'close' => $this->close,
'vol' => $this->vol,
'volQuote' => $this->volQuote,
];
}
public static function transferOkx($data)
{
$time = $data[0];
$open = $data[1];
$high = $data[2];
$low = $data[3];
$close = $data[4];
$vol = $data[6];
$volQuote = $data[7];
return new Kline($time, $open, $high, $low, $close, $vol, $volQuote);
}
}
... ...
<?php
namespace trader\struct;
class Order
{
public string $symbol; //大写
public string $ordType; // 订单类型 LIMIT 限价 IOC 不成则撤 FOK 全部成交或立即取消 MARKET 市价 ONLYMAKER 仅做maker单
public string $posSide; //大写
public string $side; //大写
public float $price; //成交价格
public float $qty; //币种真实数量
private string $cliOrdId = ''; // 客户端订单号
public function __construct($symbol, $ordType, $posSide, $side, $price, $qty)
{
$this->symbol = $symbol;
$this->ordType = $ordType;
$this->posSide = $posSide;
$this->side = $side;
$this->price = $price;
$this->qty = $qty;
}
public function setCliOrdId($cliOrdId)
{
$this->cliOrdId = $cliOrdId;
}
public function getCliOrdId()
{
return $this->cliOrdId;
}
public function toArray()
{
$arr = [
'symbol' => $this->symbol,
'ordType' => $this->ordType,
'posSide' => $this->posSide,
'side' => $this->side,
'price' => $this->price,
'qty' => $this->qty,
];
if ($this->cliOrdId != "") {
$arr['cliOrdId'] = $this->cliOrdId;
}
return $arr;
}
public function toOkxOrder(array $symbolInfos, callable $toSymbolOri)
{
$instId = call_user_func($toSymbolOri, $this->symbol);
/** @var SymbolInfo $symbolInfo */
$symbolInfo = $symbolInfos[$this->symbol];
$order = [
'instId' => $instId,
'tdMode' => 'cross',
'posSide' => strtolower($this->posSide),
'side' => strtolower($this->side),
'ordType' => $this->ordType,
];
if ($this->ordType == 'LIMIT' || $this->ordType == 'IOC') {
$order['px'] = round($this->price, $symbolInfo->pricePrec);
}
$lot = $this->qty / $symbolInfo->ctVal;
$order['sz'] = round($lot, $symbolInfo->lotPrec);
if ($this->ordType == 'ONLYMAKER') {
$order['ordType'] = 'POST_ONLY';
}
$order['ordType'] = strtolower($order['ordType']);
if ($this->cliOrdId != "") {
$order['clOrdId'] = $this->cliOrdId;
}
return $order;
}
}
... ...
<?php
namespace trader\struct;
require_once __DIR__ . '/WsDataPos.php';
use trader\struct\WsDataPos;
class Pos
{
public $symbol;
public $posSide;
public $qty;
public $lot;
public $avgPrice;
public $pnl;
public $lever;
public $margin;
public function __construct($symbol, $posSide, $qty, $lot, $avgPrice, $pnl, $lever, $margin)
{
$this->symbol = $symbol;
$this->posSide = $posSide;
$this->qty = $qty;
$this->lot = $lot;
$this->avgPrice = $avgPrice;
$this->pnl = $pnl;
$this->lever = $lever;
$this->margin = $margin;
}
public function toArray()
{
return [
'symbol' => $this->symbol,
'posSide' => $this->posSide,
'qty' => $this->qty,
'lot' => $this->lot,
'avgPrice' => $this->avgPrice,
'pnl' => $this->pnl,
'lever' => $this->lever,
'margin' => $this->margin,
];
}
public static function transferOkxPos($pos, $symbolInfos, callable $toSymbolSt)
{
$symbol = call_user_func($toSymbolSt, $pos['instId']);
/** @var SymbolInfo $symbolInfo */
$symbolInfo = $symbolInfos[$symbol];
$posSide = strtoupper($pos['posSide']);
$lot = abs($pos['pos']);
$qty = round($lot * $symbolInfo->ctVal, $symbolInfo->qtyPrec);
$avgPrice = $pos['avgPx'];
$pnl = $pos['upl'];
$lever = $pos['lever'];
$margin = $pos['imr'];
return new Pos($symbol, $posSide, $qty, $lot, $avgPrice, $pnl, $lever, $margin);
}
public static function transferWsDataPos(WsDataPos $wsDataPos)
{
return new Pos($wsDataPos->symbol, $wsDataPos->posSide, $wsDataPos->qty, $wsDataPos->lot, $wsDataPos->avgPrice, $wsDataPos->pnl, 0, 0);
}
}
... ...
<?php
namespace trader\PreOrd;
class Premium
{
private $symbol;
private $rate;
private $settleTs;
private $settleTime;
public function __construct($symbol, $rate, $settleTs, $settleTime)
{
$this->symbol = $symbol;
$this->rate = $rate;
$this->settleTs = $settleTs;
$this->settleTime = $settleTime;
}
public function toArray()
{
return [
'symbol' => $this->symbol,
'rate' => $this->rate,
'settleTs' => $this->settleTs,
'settleTime' => $this->settleTime,
];
}
}
... ...
<?php
namespace trader\struct;
require_once __DIR__ . '/../../vendor/autoload.php';
use function Jiaoyin\getPrecision;
class SymbolInfo
{
public string $symbolOri; // 原始交易对
public string $symbol; // 标准交易对
public string $ctVal; // 合约面值
public string $pricePrec;
public string $qtyPrec;
public string $lotPrec;
public string $minLot; // 最小下单张数
public string $minQty; // 最小下单数量
public function __construct($symbolOri, $symbol, $ctVal, $pricePrec, $qtyPrec, $lotPrec, $minLot, $minQty)
{
$this->symbolOri = $symbolOri;
$this->symbol = $symbol;
$this->ctVal = $ctVal;
$this->pricePrec = $pricePrec;
$this->qtyPrec = $qtyPrec;
$this->lotPrec = $lotPrec;
$this->minLot = $minLot;
$this->minQty = $minQty;
}
public function toArray()
{
return [
'symbolOri' => $this->symbolOri,
'symbol' => $this->symbol,
'ctVal' => $this->ctVal,
'pricePrec' => $this->pricePrec,
'qtyPrec' => $this->qtyPrec,
'lotPrec' => $this->lotPrec,
'minLot' => $this->minLot,
'minQty' => $this->minQty,
];
}
public static function transferOkx($data, callable $getSymbolSt): SymbolInfo
{
$symbolSt = $getSymbolSt($data["instId"]); //转换为标准交易对
$pricePrec = getPrecision($data['tickSz']);
$ctVal = $data['ctVal'];
$minLot = $data['minSz'];
$minQty = $minLot * $ctVal;
$qtyPrec = getPrecision($minQty);
$lotPrec = getPrecision($minLot);
$info = new SymbolInfo($data["instId"], $symbolSt, $ctVal, $pricePrec, $qtyPrec, $lotPrec, $minLot, $minQty);
return $info;
}
}
... ...
<?php
namespace trader\struct;
require_once __DIR__ . '/WsDataTrade.php';
use trader\struct\WsDataTrade;
use trader\struct\WsDataPos;
use trader\struct\WsDataOrder;
// 综合ws数据
class WsData
{
public string $platform;
public string $dataType; // trade | order | account | pos
public ?WsDataTrade $trade = null;
public ?WsDataPos $pos = null;
public ?WsDataOrder $order = null;
public function __construct(string $platform, string $dataType, WsDataTrade $trade = null, WsDataPos $pos = null, WsDataOrder $order = null)
{
$this->platform = $platform;
$this->dataType = $dataType;
$this->trade = $trade;
$this->pos = $pos;
$this->order = $order;
}
public function toArray()
{
return [
'platform' => $this->platform,
'dataType' => $this->dataType,
'trade' => $this->trade?->toArray(),
'pos' => $this->pos?->toArray(),
'order' => $this->order?->toArray(),
];
}
}
... ...
<?php
namespace trader\struct;
require_once __DIR__ . '/../autoload.php';
use trader\struct\SymbolInfo;
class WsDataOrder
{
public string $platform;
public string $posSide; //大写
public string $symbol; //大写
public string $side; //大写
public float $price; //下单价格
public float $avgPx; //成交均价
public float $qty; //币种真实数量
public float $lot; //合约张数
public float $pnl; //盈亏
public int $ts; //下单时间戳 ms
public int $uts; //更新时间戳 ms
public string $ordType; //下单类型
public string $cliOrdId; //客户端订单号
public string $ordId; //订单号
public string $status; //订单状态
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)
{
$this->platform = $platform;
$this->posSide = $posSide;
$this->symbol = $symbol;
$this->side = $side;
$this->price = $price;
$this->avgPx = $avgPx;
$this->qty = $qty;
$this->lot = $lot;
$this->pnl = $pnl;
$this->ts = $ts;
$this->uts = $uts;
$this->ordType = $ordType;
$this->cliOrdId = $cliOrdId;
$this->ordId = $ordId;
$this->status = $status;
}
public function toArray()
{
return [
'platform' => $this->platform,
'posSide' => $this->posSide,
'symbol' => $this->symbol,
'side' => $this->side,
'price' => $this->price,
'avgPx' => $this->avgPx,
'qty' => $this->qty,
'lot' => $this->lot,
'pnl' => $this->pnl,
'ts' => $this->ts,
'uts' => $this->uts,
'ordType' => $this->ordType,
'cliOrdId' => $this->cliOrdId,
'ordId' => $this->ordId,
'status' => $this->status,
];
}
public static function TransferOkxOrder(array $data, array $symbolInfos, callable $toSymbolSt): WsDataOrder|null
{
$symbol = call_user_func($toSymbolSt, $data['instId']);
/** @var SymbolInfo $symbolInfo */
$symbolInfo = $symbolInfos[$symbol] ?? null;
if ($symbolInfo == null) {
return null;
}
$platform = 'okx';
$posSide = strtoupper($data['posSide']);
$side = strtoupper($data['side']);
$price = (float)$data['px'];
$avgPx = (float)$data['avgPx'];
$lot = (float)$data['sz'];
$qty = round($lot * $symbolInfo->ctVal, $symbolInfo->qtyPrec);
$pnl = (float)$data['pnl'];
$ts = (int)$data['cTime'];
$uts = (int)$data['uTime'];
$ordType = strtoupper($data['ordType']);
$cliOrdId = $data['clOrdId'];
$ordId = $data['ordId'];
$status = self::getStatus($data['state']);
if ($data['cancelSource'] != '') {
$status = 'CANCELED';
}
return new WsDataOrder($platform, $posSide, $symbol, $side, $price, $avgPx, $qty, $lot, $pnl, $ts, $uts, $ordType, $cliOrdId, $ordId, $status);
}
private static function getStatus(string $state)
{
if ($state == 'live') {
return 'LIVE';
}
if ($state == 'partially_filled') {
return 'PARTIALLY_FILLED';
}
if ($state == 'filled') {
return 'FILLED';
}
if ($state == 'canceled') {
return 'CANCELED';
}
return 'UNKNOWN';
}
}
... ...
<?php
namespace trader\struct;
require_once __DIR__ . '/../autoload.php';
use trader\struct\SymbolInfo;
class WsDataPos
{
public string $symbol;
public string $posSide; //大写
public float $qty; //币种真实数量
public float $lot; //合约张数
public float $avgPrice; //平均持仓价
public bool $isSnapshot; //是否快照;
public float $pnl; //盈亏
public function __construct($symbol, $posSide, $qty, $lot, $avgPrice, $isSnapshot, $pnl)
{
$this->symbol = $symbol;
$this->posSide = $posSide;
$this->qty = $qty;
$this->lot = $lot;
$this->avgPrice = $avgPrice;
$this->isSnapshot = $isSnapshot;
$this->pnl = $pnl;
}
public function toArray()
{
return [
'symbol' => $this->symbol,
'posSide' => $this->posSide,
'qty' => $this->qty,
'lot' => $this->lot,
'avgPrice' => $this->avgPrice,
'isSnapshot' => $this->isSnapshot,
'pnl' => $this->pnl,
];
}
public static function TransferOkxPos(array $data, array $symbolInfos, callable $toSymbolSt): WsDataPos|null
{
$symbol = call_user_func($toSymbolSt, $data['instId']);
/** @var SymbolInfo $symbolInfo */
$symbolInfo = $symbolInfos[$symbol] ?? null;
if ($symbolInfo === null) {
return null;
}
$posSide = strtoupper($data['posSide']);
$lot = (float)$data['pos'];
$qty = round($lot * $symbolInfo->ctVal, $symbolInfo->qtyPrec);
$avgPrice = (float)$data['avgPx'];
$pnl = (float)$data['upl'];
return new WsDataPos($symbol, $posSide, $qty, $lot, $avgPrice, false, $pnl);
}
}
... ...
<?php
namespace trader\struct;
require_once __DIR__ . '/../autoload.php';
use trader\struct\SymbolInfo;
class WsDataTrade
{
public string $platform;
public string $posSide; //大写
public string $symbol; //大写
public string $side; //大写
public float $price; //成交价格
public float $qty; //币种真实数量
public float $lot; //合约张数
public float $pnl; //盈亏
public float $quoteVol; //计价货币数量 成交额
public int $ts; //时间戳 ms
public string $tradeId;
public function __construct($platform, $posSide, $symbol, $side, $price, $qty, $lot, $pnl, $quoteVol, $ts, $tradeId)
{
$this->platform = $platform;
$this->posSide = $posSide;
$this->symbol = $symbol;
$this->side = $side;
$this->price = $price;
$this->qty = $qty;
$this->lot = $lot;
$this->pnl = $pnl;
$this->quoteVol = $quoteVol;
$this->ts = $ts;
$this->tradeId = $tradeId;
}
public function toArray()
{
return [
'platform' => $this->platform,
'posSide' => $this->posSide,
'symbol' => $this->symbol,
'side' => $this->side,
'price' => $this->price,
'qty' => $this->qty,
'lot' => $this->lot,
'pnl' => $this->pnl,
'quoteVol' => $this->quoteVol,
'ts' => $this->ts,
'tradeId' => $this->tradeId,
];
}
public static function TransferOkxOrder(array $data, array $symbolInfos, callable $toSymbolSt): WsDataTrade|null
{
if ($data['fillSz'] != "" && $data['fillSz'] != "0") {
$symbol = call_user_func($toSymbolSt, $data['instId']);
/** @var SymbolInfo $symbolInfo */
$symbolInfo = $symbolInfos[$symbol] ?? null;
if ($symbolInfo == null) {
return null;
}
$platform = 'okx';
$posSide = strtoupper($data['posSide']);
$side = strtoupper($data['side']);
$price = (float)$data['fillPx'];
$lot = (float)$data['fillSz'];
$qty = round($lot * $symbolInfo->ctVal, $symbolInfo->qtyPrec);
$pnl = (float)$data['fillPnl'];
$quoteVol = $price * $qty;
$ts = $data['uTime'];
$tradeId = $data['tradeId'];
return new WsDataTrade($platform, $posSide, $symbol, $side, $price, $qty, $lot, $pnl, $quoteVol, $ts, $tradeId);
}
return null;
}
}
... ...