富途牛牛APP下载地址:

https://www.futunn.com/?lang=zh-CN

1.下载APP后更直观查看接口调用结果

2.可以使用模拟盘去测试相应接口

富途OpenD下载地址:

https://www.futunn.com/download/OpenAPI

1.新注册的账号在使用命令行登陆openD前需要先完成调查问卷

2.第一次使用命令行登陆openD需要一个手机号验证码验证

3.每个用户需要运行一个openD客户端,不同用户可以分配不同的端口去启动openD,然后openAPI调用不同的端口去链接不同用户的openD进行不同的操作

解压后命令行启动命令:

./FutuOpenD -login_account=账户 -login_pwd=密码 -lang=chs -api_port=11111

1.密码不是账号的登录密码,是注册号设置的交易密码

2.更详细的命令参考https://openapi.futunn.com/futu-api-doc/opend/opend-cmd.html

富途OpenAPI介绍文档地址:

https://openapi.futunn.com/futu-api-doc/

1.PHP安装了swoole 4.7.1扩展

PHP封装的openAPI类(class.ftnn.php)

Github仓库地址:

https://github.com/hkargv/PHP-For-FutuOpenD

<?php
/*** 富途行情及交易接口*/
class futu{/*** @var Swoole\\Client*/private $cli = null;private $die = false;private $push = false; //是否推送模式private $host = '127.0.0.1';private $port = '11111';private $pass = '';/*** 请求的序列号* @var integer*/private $sequence = 0;/*** 心跳包定时器* @var integer*/private $timer = 0;/*** 初始化的返回* @var integer*/private $serverVer = 0;/*** 初始化的返回,心跳间隔* @var integer*/private $keepAliveInterval = 5;/*** AES加密通信CBC加密模式的iv,固定为16字节长字符串* @var string*/private $aesCBCiv = '';/*** 同一个连接只解锁一次* @var string*/public $unlock = null;/*** 订单推送订阅*/public $accPush = false;/*** 行情市场:0未知1港股2港期11美股12美期21沪股22深股* @var integer*/public $market = 1;/*** 交易环境:0仿真1真实* @var integer*/public $trdEnv = 1;/*** 交易市场:0未知1港股2美股3大陆4香港A股通5期货市场* @var integer*/public $trdMarket = 1;/*** 是否通讯加密,需同时设置$private_key(对性能肯定有影响)* @var bool*/public $encrypt = false;/*** 密钥文件绝对路径,与FutuOpenD配置文件中的一致* @var string*/public $private_key = __DIR__.'/private.key';/*** 交易账号列表* @var array*/public $accList = [];public $connID = 0;public $loginUserID = 0;/*** 初始化接口返回的加密密码* @var string*/public $connAESKey = '';/*** futu constructor.* @param string $host* @param string $port* @param string $pass 交易解锁密码* @param int $trdEnv 0:模拟 1真实*/public function __construct($host, $port, $pass='',$trdEnv){$this->host = $host;$this->port = $port;$this->pass = $pass;$this->trdEnv = $trdEnv;if(! class_exists('Swoole\\Client')){ //强制使用die('http://pecl.php.net/package/swoole');}}/*** @param Swoole\\Coroutine\\Client $cli* @return boolean*/public function push($cli){if(! $cli instanceof Swoole\\Coroutine\\Client){var_dump("客户端有问题");return false;}$this->cli = $cli;$this->push = true; //是否推送模式}private function connect(){if($this->cli === null){$this->timer = time(); //初始化心跳时间$this->cli = new Swoole\\Client(preg_match('/^[0-9\\.]+$/', $this->host)?(SWOOLE_SOCK_TCP/**|SWOOLE_KEEP**/):(SWOOLE_SOCK_UNIX_STREAM/**|SWOOLE_KEEP**/));$this->cli->set(array('socket_buffer_size' => 1024*1024*32, //32M缓存区'open_length_check'     => 1,'package_length_type'   => 'L','package_length_offset' => 12,       //第N个字节是包长度的值'package_body_offset'   => 44,       //第几个字节开始计算长度'package_max_length'    => 8*1024*1024,  //协议最大长度'open_tcp_nodelay' => false));if(! @$this->cli->connect($this->host, $this->port, 8)){$this->errorlog('Connect Error.'.socket_strerror($this->cli->errCode), 0);}}if(! $this->cli){$this->errorlog('Client Error.', 0);}if($this->timer && (time() - $this->timer >= $this->keepAliveInterval)){ //每N秒发一次心跳(只有同步模式会初始化时间)$this->timer = time(); //锁住$this->KeepAlive();}return $this->cli;}/*** 关闭连接,销毁之前的数据*/public function close(){if($this->cli instanceof Swoole\\Coroutine\\Client){$this->cli->close();exit(0);}if($this->cli instanceof Swoole\\Client){$this->cli->isConnected() && $this->cli->close(true);}$this->cli = null;$this->connID = 0;$this->unlock = false;}/*** 初始化连接* @return string*/public function InitConnect(){if($this->connID){ //已经初始化过return $this->connID;}$C2S = array("clientVer" => 0,'clientID' => '0','recvNotify' => $this->push ? true : false,'packetEncAlgo' => $this->encrypt ? 0 : -1, //0富途修改过的AES的ECB加密模式;-1不加密;1标准的AES的ECB加密模式;2标准的AES的CBC加密模式'pushProtoFmt' => 1, //0:pb,1:json'programmingLanguage' => '');if(! $ret = $this->send('1001', $C2S)){return '链接失败';}//        var_dump($ret);$this->serverVer = (int)$ret['serverVer'];$this->loginUserID = (string)$ret['loginUserID']; //uint64$this->connID = (string)$ret['connID']; //uint64$this->connAESKey = (string)$ret['connAESKey'];$this->keepAliveInterval = (int)$ret['keepAliveInterval'];$this->aesCBCiv = (string)$ret['aesCBCiv'];return $this->connID;}/*** 获取全局状态* @return array*/public function GetGlobalState(){if(! $this->InitConnect()){return array();}$C2S = array('userID' => 0 //历史原因,目前已废弃,填0即可);if(! $ret = $this->send('1002', $C2S)){return array();}return (array)$ret;}/*** 保活心跳* @return int*/public function KeepAlive(){if(! $this->InitConnect()){return 0;}$C2S = array('time' => (int)time(),);if(! $ret = $this->send('1004', $C2S)){return 0;}return (int)$ret['time'];}/*** 获取当前用户信息* @param int $flag 1昵称用户头像牛牛号;2API权限信息;4市场的行情权限;8免责;16升级类型;1024WebKey* @return array*/public function GetUserInfo($flag=-1){if(! $this->InitConnect()){return array();}$C2S = array('flag' => (int)$flag,);if(! $ret = $this->send('1005', $C2S)){return array();}return (array)$ret;}/*** 订阅或者反订阅,同时注册或者取消推送(股票个数*K线种类<=100)* @param array $codes* @param array $subTypeList 1报价;2摆盘;4逐笔;5分时;6日K;7五分K;8十五分K;9三十K;10六十K;11一分K;12周K;13月K;14经纪队列;15季K;16年K;17三分K;18委托明细* @param bool $isSubOrUnSub true订阅false反订阅* @param bool $isRegOrUnRegPush 是否注册或反注册该连接上面行情的推送,该参数不指定不做注册反注册操作* @param array $regPushRehabTypeList 复权类型:0不复权1前复权2后复权* @param bool $isFirstPush 注册后如果本地已有数据是否首推一次已存在数据* @param bool $isUnsubAll 一键取消当前连接的所有订阅,当被设置为true时忽略其他参数* @param bool $isSubOrderBookDetail 是否订阅摆盘明细* @param bool $extendedTime 是否允许美股盘前盘后数据* @return bool*/public function Qot_Sub($codes, $subTypeList, $isSubOrUnSub=true, $isRegOrUnRegPush=null, $regPushRehabTypeList=[], $isFirstPush=false, $isUnsubAll=false, $isSubOrderBookDetail=false, $extendedTime=false){if(! $this->InitConnect()){return false;}$C2S = array('isSubOrUnSub' => (bool)$isSubOrUnSub, //true订阅false反订阅'isFirstPush' => (bool)$isFirstPush,'isUnsubAll' => (bool)$isUnsubAll);$securityList = array();foreach ((array)$codes as $code){$securityList[] = array('market' => $this->market,'code' => (string)$code,);}if(($isUnsubAll == false) && empty($securityList)){return false;}if($securityList){$C2S['securityList'] = $securityList;}if($subTypeList = (array)$subTypeList){ //订阅数据类型$C2S['subTypeList'] = array_unique(array_values($subTypeList));}if($isRegOrUnRegPush !== null){$C2S['isRegOrUnRegPush'] = (bool)$isRegOrUnRegPush;}if($isRegOrUnRegPush && $regPushRehabTypeList){$C2S['regPushRehabTypeList'] = (array)$regPushRehabTypeList;}if($isSubOrderBookDetail){$C2S['isSubOrderBookDetail'] = (bool)$isSubOrderBookDetail;}if($extendedTime){$C2S['extendedTime'] = (bool)$extendedTime;}if(! $ret = $this->send('3001', $C2S)){return false;}return isset($ret['result']) ? (bool)$ret['result'] : true;}/*** 获取订阅信息* @param bool $isReqAllConn 是否返回所有连接的订阅状态,不传或者传false只返回当前连接数据*/public function Qot_GetSubInfo($isReqAllConn=false){if(! $this->InitConnect()){return array();}$C2S = array('isReqAllConn' => (bool)$isReqAllConn);if(! $ret = $this->send('3003', $C2S)){return array();}return (array)$ret;}/*** 获取股票基本行情* @param array $codes* @return array*/public function Qot_GetBasicQot($codes){if(! $this->Qot_Sub($codes, [1], true)){return array();}$securityList = array();foreach ((array)$codes as $code){$securityList[] = array('market' => $this->market,'code' => (string)$code,);}if(! $securityList){return array();}$C2S = array('securityList' => (array)$securityList,);if(! $ret = $this->send('3004', $C2S)){return array();}return (array)$ret['basicQotList'];}/*** 获取K线* @param string $code* @param int $klType K线类型:1一分K;2日K;3周K;4月K;5年K;6五分K;7十五分K;8三十分K;9六十分K;10三分K;11季K* @param int $reqNum K线条数* @param int $rehabType 复权类型:0不复权1前复权2后复权*/public function Qot_GetKL($code, $klType, $reqNum=1000, $rehabType=1){$map = array(1 => 11,2 => 6,3 => 12,4 => 13,5 => 16,6 => 7,7 => 8,8 => 9,9 => 10,10 => 17,11 => 15);if(! $this->Qot_Sub($code, [$map[$klType]], true)){return array();}$C2S = array('security' => array('market' => $this->market,'code' => (string)$code,),'klType' => (int)$klType,'reqNum' => (int)$reqNum,'rehabType' => (int)$rehabType,);if(! $ret = $this->send('3006', $C2S)){return array();}return (array)$ret['klList'];}/*** 获取分时* @param string $code* @return array*/public function Qot_GetRT($code){if(! $this->Qot_Sub($code, [5], true)){return array();}$C2S = array('security' => array('market' => $this->market,'code' => (string)$code,));if(! $ret = $this->send('3008', $C2S)){return array();}return (array)$ret['rtList'];}/*** 获取逐笔* @param string $code* @param int $maxRetNum* @return array*/public function Qot_GetTicker($code, $maxRetNum=1000){if(! $this->Qot_Sub($code, [4], true)){return array();}$C2S = array('security' => array('market' => $this->market,'code' => (string)$code,),'maxRetNum' => (int)$maxRetNum,);if(! $ret = $this->send('3010', $C2S)){return array();}return (array)$ret['tickerList'];}/*** 获取买卖盘* @param string $code* @param int $num* @return array*/public function Qot_GetOrderBook($code, $num=10){if(! $this->Qot_Sub($code, [2], true)){return array();}$C2S = array('security' => array('market' => $this->market,'code' => (string)$code,),'num' => (int)$num,);if(! $ret = $this->send('3012', $C2S)){return array();}return (array)$ret;}/*** 获取经纪队列* @param string $code* @return array*/public function Qot_GetBroker($code){if(! $this->Qot_Sub($code, [14], true)){return array();}$C2S = array('security' => array('market' => $this->market,'code' => (string)$code,));if(! $ret = $this->send('3014', $C2S)){return array();}return (array)$ret;}/*** 获取委托明细* @param string $code* @return array*/public function Qot_GetOrderDetail($code){if(! $this->Qot_Sub($code, [18], true)){return array();}$C2S = array('security' => array('market' => $this->market,'code' => (string)$code,));if(! $ret = $this->send('3016', $C2S)){return array();}return (array)$ret;}/*** 获取单只股票一段历史K线(有限额)分K提供最近2年数据,日K及以上提供近10年数据* @param string $code* @param int $klType K线类型:1一分K;2日K;3周K;4月K;5年K;6五分K;7十五分K;8三十分K;9六十分K;10三分K;11季K* @param int $beginTime* @param int $endTime* @param int $maxAckKLNum 最多返回多少根K线,如果未指定表示不限制* @param int $nextReqKey 分页请求key(在返回的数组中)* @param int $needKLFieldsFlag 指定返回K线结构体特定某几项数据,KLFields枚举值或组合,如果未指定返回全部字段* @param int $rehabType 复权类型* @param bool $extendedTime 是否获取美股盘前盘后数据,当前仅支持1分K* @return array*/public function Qot_RequestHistoryKL($code, $klType, $beginTime, $endTime, $maxAckKLNum=0, $nextReqKey=null, $needKLFieldsFlag=[], $rehabType=1, $extendedTime=false){if($nextReqKey == null){if(! $this->limit(__LINE__, 30, 60)){return array();}}if(! $this->InitConnect()){return array();}$C2S = array('security' => array('market' => $this->market,'code' => (string)$code,),'klType' => (int)$klType,'beginTime' => date("Y-m-d H:i:s", $beginTime),'endTime' => date("Y-m-d H:i:s", $endTime),'rehabType' => (int)$rehabType,);if($maxAckKLNum){$C2S['maxAckKLNum'] = (int)$maxAckKLNum;}if($nextReqKey){$C2S['nextReqKey'] = (string)$nextReqKey;}if($needKLFieldsFlag){$C2S['needKLFieldsFlag'] = (array)$needKLFieldsFlag;}if($extendedTime){$C2S['extendedTime'] = (bool)$extendedTime;}if(! $ret = $this->send('3103', $C2S)){return array();}return (array)$ret['klList'];}/*** 拉取历史K线已经用掉的额度* @param bool $bGetDetail 是否拉取详细列表* @return array*/public function Qot_RequestHistoryKLQuota($bGetDetail=false){if(! $this->InitConnect()){return array();}$C2S = array('bGetDetail' => (bool)$bGetDetail,);if(! $ret = $this->send('3104', $C2S)){return array();}return (array)$ret;}/*** 获取复权信息* @param array $codes* @return array/false*/public function Qot_RequestRehab($code){if(! $this->limit(__LINE__, 30, 60)){return false;}if(! $this->InitConnect()){return false;}$C2S = array('security' => array('market' => $this->market,'code' => (string)$code,));if(! $ret = $this->send('3105', $C2S)){return array();}return (array)$ret['rehabList'];}/*** 获取股票列表* @param int $secType 0未知或指定股票1债券2权证3正股4基金5涡轮6指数7板块8期权9板块集合* @param array $codes 股票,若该参数存在,忽略其他参数* @return array*/public function Qot_GetStaticInfo($secType, $codes=array()){if(! $this->InitConnect()){return array();}$securityList = array();foreach ((array)$codes as $code){$securityList[] = array('market' => $this->market,'code' => (string)$code,);}if(! $securityList){$C2S = array('market' => $this->market,'secType' => (int)$secType,);}if($securityList){$C2S = array('securityList' => (array)$securityList,);}if(! $ret = $this->send('3202', $C2S)){return array();}return (array)$ret['staticInfoList'];}/*** 获取一批股票的快照信息,每次最多400支* @param array $codes* @return array*/public function Qot_GetSecuritySnapshot($codes){if(! $this->limit(__LINE__, 30, 60)){return array();}if(! $this->InitConnect()){return array();}$securityList = array();foreach ((array)$codes as $code){if($GLOBALS['FUTU_DELISTED'][$code]){continue;}$securityList[] = array('market' => $this->market,'code' => (string)$code,);}if(! $securityList){return array();}$C2S = array('securityList' => (array)$securityList,);if(! $ret = $this->send('3203', $C2S)){return array();}return (array)$ret['snapshotList'];}/*** 获取板块集合下的板块(30秒10次)* @param int $plateSetType 0所有版块1行业板块2地域板块3概念版块* @return array*/public function Qot_GetPlateSet($plateSetType){if(! $this->limit(__LINE__, 30, 10)){return array();}if(! $this->InitConnect()){return array();}$C2S = array('plateSetType' => (int)$plateSetType,'market' => $this->market,);if(! $ret = $this->send('3204', $C2S)){return array();}$gets = array();foreach ((array)$ret['plateInfoList'] as $v){$gets[] = array('market'=>$v['plate']['market'],'code'=>$v['plate']['code'],'name'=>$v['name']);}return (array)$gets;}/*** 获取板块下的股票(30秒10次)* @param string $code 版块编号(如期货板块:BK1986)* @param int $sortField 排序字段* @param bool $ascend 是否升序* @return array*/public function Qot_GetPlateSecurity($code, $sortField=1, $ascend=true){if(! $this->limit(__LINE__, 30, 10)){return array();}if(! $this->InitConnect()){return array();}$C2S = array('plate' => array('code' => (string)$code,'market' => $this->market,),'sortField' => (int)$sortField,'ascend' => (bool)$ascend,);if(! $ret = $this->send('3205', $C2S)){return array();}return (array)$ret['staticInfoList'];}/*** 获取正股相关股票* @param string $code 正股代码* @param int $referenceType 1正股相关的窝轮;2期货主连的相关合约* @return array*/public function Qot_GetReference($code, $referenceType=1){if(! $this->limit(__LINE__, 30, 10)){return array();}if(! $this->InitConnect()){return array();}$C2S = array('security' => array('market' => $this->market,'code' => (string)$code,),'referenceType' => (int)$referenceType,);if(! $ret = $this->send('3206', $C2S)){return array();}return (array)$ret['staticInfoList'];}/*** 获取股票所属板块(30秒10次)* @param array $codes 最多200个,仅支持正股和指数*/public function Qot_GetOwnerPlate($codes){if(! $this->limit(__LINE__, 30, 10)){return array();}if(! $this->InitConnect()){return array();}$securityList = array();foreach ((array)$codes as $code){$securityList[] = array('market' => $this->market,'code' => (string)$code,);}if(! $securityList){return array();}$C2S = array('securityList' => (array)$securityList,);if(! $ret = $this->send('3207', $C2S)){return array();}$gets = array();foreach ((array)$ret['ownerPlateList'] as $v){foreach ((array)$v['plateInfoList'] as $vv){$vv['plate_code'] = $vv['plate']['code'];unset($vv['plate']);$gets[$v['security']['code']][] = $vv;}}return (array)$gets;}/*** 获取持股变化列表*/public function Qot_GetHoldingChangeList(){}/*** 获取期权链*/public function Qot_GetOptionChain(){}/*** 获取涡轮* @param array $filter* @param number $begin 数据起始点* @param number $num 请求数据个数,最大200* @param number $sortField 根据哪个字段排序* @param string $ascend 升序ture,降序false*/public function Qot_GetWarrant($code, $filter=array(), $begin=0, $num=200, $sortField=10, $ascend=false){if(! $this->limit(__LINE__, 30, 60)){return array();}if(! $this->InitConnect()){return array();}$C2S = array('begin' => (int)$begin,'num' => (int)$num,'sortField' => (int)$sortField,'ascend' => (bool)$ascend);foreach ((array)$filter as $k => $v){$C2S[$k] = $v;}if($code = trim($code)){$C2S['owner'] = array('market' => $this->market,'code' => (string)$code,);}if(! $ret = $this->send('3210', $C2S)){return array();}$gets = array();foreach ((array)$ret['warrantDataList'] as $v){if(! $v['code'] = (string)$v['stock']['code']){continue;}if(! $v['owner'] = (string)$v['owner']['code']){continue;}unset($v['stock']);$gets[$v['code']] = $v;}return (array)$gets;}/*** 获取资金流向,仅支持正股、窝轮和基金* @param string $code* @return array*/public function Qot_GetCapitalFlow($code){if(! $this->limit(__LINE__, 30, 30)){return array();}if(! $this->InitConnect()){return array();}$C2S = array('security' => array('market' => $this->market,'code' => (string)$code,));if(! $ret = $this->send('3211', $C2S)){return array();}return (array)$ret['flowItemList'];}/*** 获取资金分布* @param string $code* @return array*/public function Qot_GetCapitalDistribution($code){if(! $this->limit(__LINE__, 30, 30)){return array();}if(! $this->InitConnect()){return array();}$C2S = array('security' => array('market' => $this->market,'code' => (string)$code,));if(! $ret = $this->send('3212', $C2S)){return array();}return (array)$ret;}/*** 获取自选股分组下的股票* @param string $groupName* @return array*/public function Qot_GetUserSecurity($groupName){if(! $this->limit(__LINE__, 30, 10)){return array();}if(! $this->InitConnect()){return array();}$C2S = array('groupName' => (string)$groupName);if(! $ret = $this->send('3213', $C2S)){return array();}return (array)$ret['staticInfoList'];}/*** 修改自选股分组下的股票* @param string $groupName* @param int $op 1新增 2删除3移出* @param array $codes* @return boolean*/public function Qot_ModifyUserSecurity($groupName, $op, $codes){if(! $this->limit(__LINE__, 30, 10)){return false;}if(! $this->InitConnect()){return false;}$securityList = array();foreach ((array)$codes as $code){$securityList[] = array('market' => $this->market,'code' => (string)$code,);}if(! $securityList){return false;}$C2S = array('groupName' => (string)$groupName,'op' => (int)$op,'securityList' => (array)$securityList,);if(! $ret = $this->send('3214', $C2S)){return false;}return (bool)$ret;}/*** 获取条件选股* @param array $filter* @param number $plate* @param number $begin* @param number $num* @return array*/public function Qot_StockFilter($filter=array(), $plate=0, $begin=0, $num=200){if(! $this->limit(__LINE__, 30, 10)){return array();}if(! $this->InitConnect()){return array();}$C2S = array('begin' => (int)$begin,'num' => (int)$num,'market' => $this->market);foreach ((array)$filter as $k => $v){$C2S[$k] = $v;}if($plate = trim($plate)){$C2S['plate'] = array('market' => $this->market,'code' => (string)$plate,);}if(! $ret = $this->send('3215', $C2S)){return array();}$gets = array();foreach ($ret['dataList'] as $v){if(! $v['code'] = $v['security']['code']){continue;}$v['market'] = $v['security']['market'];unset($v['security']);$gets[$v['code']] = $v;}return (array)$gets;}/*** 获取股票代码变更信息* @param array $codes* @param array $typeList 根据类型筛选1创业板转主板2买卖未缴款供股权3更改买卖单位4拆股5合股6股份先并后拆7股份先拆后并8其他* @param array $timeFilter 根据时间筛选* @return array*/public function Qot_GetCodeChange($codes=array(), $typeList=array(), $timeFilter=array()){if(! $this->InitConnect()){return array();}$C2S = $securityList = array();foreach ((array)$codes as $code){$securityList[] = array('market' => $this->market,'code' => (string)$code,);}if($securityList){$C2S['securityList'] = $securityList;}if($typeList = (array)$typeList){$C2S['typeList'] = $typeList;}if($timeFilter = (array)$timeFilter){$C2S['timeFilter'] = $timeFilter;}if($placeHolder = (int)$placeHolder){ //占位$C2S['placeHolder'] = (int)$placeHolder;}$C2S or ($C2S['placeHolder'] = 0); //返回全部if(! $C2S){return array();}if(! $ret = $this->send('3216', $C2S)){return array();}return (array)$ret['codeChangeList'];}/*** 获取IPO信息* @return array*/public function Qot_GetIpoList(){if(! $this->limit(__LINE__, 30, 10)){return array();}if(! $this->InitConnect()){return array();}$C2S = array('market' => $this->market);if(! $ret = $this->send('3217', $C2S)){return array();}$gets = array();foreach ((array)$ret['ipoList'] as $v){if($v['basic']){$v['basic']['code'] = $v['basic']['security']['code'];$v['basic']['market'] = $v['basic']['security']['market'];unset($v['basic']['security']);}$a = array_merge((array)$v['basic'], (array)$v['hkExData']);if(! $a['code']){continue;}$gets[] = $a;}return (array)$gets;}/*** 获取期货合约资料* @param array $codes 传入股票最多 200 个* @return array|boolean*/public function Qot_GetFutureInfo($codes){if(! $this->limit(__LINE__, 30, 30)){return array();}if(! $this->InitConnect()){return array();}$securityList = array();foreach ((array)$codes as $code){$securityList[] = array('market' => $this->market,'code' => (string)$code,);}if(! $securityList){return array();}$C2S = array('securityList' => (array)$securityList);if(! $ret = $this->send('3218', $C2S)){return array();}$gets = array();foreach ((array)$ret['futureInfoList'] as $v){if($v['basic']){$v['basic']['code'] = $v['basic']['security']['code'];$v['basic']['market'] = $v['basic']['security']['market'];unset($v['basic']['security']);}if($v['warrantExData']){$v['warrantExData']['owner_code'] = $v['warrantExData']['owner']['code'];$v['warrantExData']['owner_market'] = $v['warrantExData']['owner']['market'];unset($v['warrantExData']['owner']);}$v['code'] = $v['security']['code'];$v['market'] = $v['security']['market'];unset($v['security']);$v['owner_code'] = $v['owner']['code'];$v['owner_market'] = $v['owner']['market'];unset($v['owner']);$gets[$v['code']] = $v;}return (array)$gets;}/*** 在线请求交易日* @param int $beginTime* @param int $endTime* @return array tradeDateType: 0全天;1上午;2下午*/public function Qot_RequestTradeDate($beginTime, $endTime){if(! $this->limit(__LINE__, 30, 30)){return array();}if(! $this->InitConnect()){return array();}$C2S = array('market' => $this->trdMarket,'beginTime' => date('Y-m-d', $beginTime), //开始时间字符串'endTime' => date('Y-m-d', $endTime) //结束时间字符串);if(! $ret = $this->send('3219', $C2S)){return array();}return (array)$ret['tradeDateList'];}/*** 设置到价提醒* @param string $code* @param int $op 操作类型:1新增 2删除 3启用 4禁用 5修改6删除该支股票下所有到价提醒* @param int $key 到价提醒的标识,Qot_GetPriceReminder协议可获得,用于指定要操作的到价提醒项,对于新增的情况不需要填* @param int $type Qot_Common::PriceReminderType,提醒类型,删除/启用/禁用的情况下会忽略该字段* @param int $freq Qot_Common::PriceReminderFreq,提醒频率类型,删除/启用/禁用的情况下会忽略该字段 1持续提醒 2每日一次 3仅提醒一次* @param float $value 提醒值,删除/启用/禁用的情况下会忽略该字段* @param string $note 用户设置到价提醒时的标注,最多10个字符,删除/启用/禁用的情况下会忽略该字段*/public function Qot_SetPriceReminder($code, $op, $key=0, $type=0, $freq=0, $value=0, $note=''){if(! $this->limit(__LINE__, 30, 60)){return array();}if(! $this->InitConnect()){return array();}$C2S = array('security' => array('market' => $this->market,'code' => (string)$code,),'op' => (int)$op);if($key = (int)$key){$C2S['key'] = $key;}if($type = (int)$type){$C2S['type'] = $type;}if($freq = (int)$freq){$C2S['freq'] = $freq;}if($value = (float)$value){$C2S['value'] = $value;}if($note = (string)$note){$C2S['note'] = $note;}if(! $ret = $this->send('3220', $C2S)){return array();}return (array)$ret;}/*** 获取到价提醒* @param string $code* @return array*/public function Qot_GetPriceReminder($code=null){if(! $this->limit(__LINE__, 30, 10)){return array();}if(! $this->InitConnect()){return array();}$C2S = array('market' => $this->market);if($code = trim($code)){$C2S['security'] = array('market' => $this->market,'code' => (string)$code,);}if(! $ret = $this->send('3221', $C2S)){return array();}$gets = array();foreach ((array)$ret['priceReminderList'] as $v){if(! $v['code'] = $v['security']['code']){continue;}$v['market'] = $v['security']['market'];unset($v['security']);$gets[$v['code']] = $v;}return (array)$gets;}/*** 获取自选股分组列表* @param int $groupType 1自定义分组2系统分组3全部分组* @return array*/public function Qot_GetUserSecurityGroup($groupType){if(! $this->limit(__LINE__, 30, 10)){return array();}if(! $this->InitConnect()){return array();}$C2S = array('groupType' => (int)$groupType);if(! $ret = $this->send('3222', $C2S)){return array();}$gets = array();foreach ((array)$ret['groupList'] as $v){$gets[] = $v;}return (array)$gets;}/*** 获取指定品种的市场状态* @return array*/public function Qot_GetMarketState($codes){if(! $this->limit(__LINE__, 30, 10)){return array();}if(! $this->InitConnect()){return array();}$securityList = array();foreach ((array)$codes as $code){$securityList[] = array('market' => $this->market,'code' => (string)$code,);}if(! $securityList = (array)$securityList){return array();}$C2S = array('securityList' => $securityList,);if(! $ret = $this->send('3223', $C2S)){return array();}return (array)$ret['marketInfoList'];}/*** 获取交易账户列表* @return array*/public function Trd_GetAccList(){if($this->accList){return $this->accList;}if(! $this->InitConnect()){return array();}$C2S = array('userID' => (string)$this->loginUserID,);if(! $ret = $this->send('2001', $C2S)){return array();}// trdMarket :此账户能交易哪些市场 0:未知市场 1:香港市场(证券、期权)2:美国市场(证券、期权) 3:A股市场(仅用于模拟交易) 4:A股通市场(股票)5:期货市场(环球期货)// TrdEnv:交易环境 0:模拟环境 1:真实环境// accType:交易环境 0; 未知类型 1:现金账户 2:保证金账户// simAccType:交易环境 0; 未知类型 1:股票模拟账户(仅用于交易证券类产品,不支持交易期权 2:期权模拟账户(仅用于交易期权,不支持交易股票证券类产品)// accID:业务账号// cardNum:卡号foreach ((array)$ret['accList'] as $v){foreach ((array)$v['trdMarketAuthList'] as $trdMarket){ //可拥有多个交易市场权限,目前仅单个if ($v['trdEnv'] == 0){$this->accList[$trdMarket][$v['trdEnv']][$v['accType']][$v['simAccType']] = (string)$v['accID'];}else{$this->accList[$trdMarket][$v['trdEnv']][$v['accType']] = (string)$v['accID'];}}}return (array)$this->accList;}/*** 根据环境选择交易账户** @param int $accType 交易环境 0; 未知类型 1:现金账户 2:保证金账户* @param int $simAccType 交易环境 0; 未知类型 1:股票模拟账户 2:期权模拟账户* @return string*/private function chooseAccount($accType=1,$simAccType=1){$list = $this->accList[$this->trdMarket][$this->trdEnv][$accType];if ($this->trdEnv == 0){//模拟环境return (string)$list[$simAccType];}else{//真实环境return (string)$list;}}/*** 解锁交易(30秒10次)* @param string $unlock true解锁false锁定* @return bool*/public function Trd_UnlockTrade($unlock){$unlock = (bool)$unlock;if($this->unlock === $unlock){return true;}if($this->trdEnv == 0){ //仿真环境无需解锁return $this->unlock = true;}if(! $this->limit(__LINE__, 30, 10)){return false;}if(! $this->InitConnect()){return false;}$C2S = array('unlock' => (bool)$unlock,'pwdMD5' => strtolower(md5($this->pass,FALSE)),'securityFirm' => 1,);if(! $ret = $this->send('2005', $C2S)){return false;}$this->unlock = $unlock;return true;}/*** 订阅接收交易账户的推送数据* @return array*/public function Trd_SubAccPush(){if($this->accPush){return true;}if(! $this->Trd_UnlockTrade(true)){return false;}if(! $this->Trd_GetAccList()){return false;}$accIDList = array();foreach ([1,5] as $trdMarket){ //接收港股及期货的推送if($accID = (string)$this->accList[$trdMarket][$this->trdEnv]){$accIDList[] = $accID;}}if(! $accIDList){return false;}$C2S = array('accIDList' => (array)$accIDList);if(! $ret = $this->send('2008', $C2S)){return array();}return $this->accPush = (bool)$ret;}/*** 获取账户资金* @param bool $refreshCache 是否强制从服务器获取数据* @param int $currency 1港币,2美元,3离岸人民币;货币种类,期货账户必填,其它账户忽略* @return array*/public function Trd_GetFunds($refreshCache=false, $currency=1){if(! $this->Trd_UnlockTrade(true)){return array();}if(! $this->Trd_GetAccList()){return array();}if(! $accID = (string)$this->chooseAccount()){return array();}if($refreshCache){if(! $this->limit(__LINE__, 30, 10)){return array();}}$C2S = array('header' => array('trdEnv' => $this->trdEnv,'trdMarket' => $this->trdMarket,'accID' => (string)$accID,),'refreshCache' => (bool)$refreshCache,);if(in_array($this->trdMarket, [5])){ //期货账户必填,其它账户忽略$C2S['currency'] = (int)$currency;}if(! $ret = $this->send('2101', $C2S)){return array();}return (array)$ret['funds'];}/*** 获取持仓列表* @param array $codeList 代码过滤,只返回包含这些代码的数据,没传不过滤* @param array $idList ID 主键过滤,只返回包含这些 ID 的数据,没传不过滤* @param number $filterPLRatioMin 过滤盈亏百分比下限,高于此比例的会返回,比如传10.0,返回盈亏比例大于10%的持仓* @param number $filterPLRatioMax 过滤盈亏百分比上限,低于此比例的会返回,比如传20.0,返回盈亏比例小于20%的持仓* @param bool $refreshCache 立即刷新 OpenD 缓存的此数据* @return array*/public function Trd_GetPositionList($codeList=[], $idList=[], $filterPLRatioMin=0, $filterPLRatioMax=0, $refreshCache=false){if(! $this->Trd_UnlockTrade(true)){return array();}if(! $this->Trd_GetAccList()){return array();}if(! $accID = (string)$this->chooseAccount()){return array();}if($refreshCache){if(! $this->limit(__LINE__, 30, 10)){return array();}}$C2S = array('header' => array('trdEnv' => $this->trdEnv,'trdMarket' => $this->trdMarket,'accID' => (string)$accID,
//                'accID' => "7246205",),'refreshCache' => (bool)$refreshCache);if($codeList){$C2S['filterConditions']['codeList'] = (array)$codeList;}if($idList){$C2S['filterConditions']['idList'] = (array)$idList;}if($filterPLRatioMin){$C2S['filterPLRatioMin'] = (float)$filterPLRatioMin;}if($filterPLRatioMax){$C2S['filterPLRatioMax'] = (float)$filterPLRatioMax;}if(! $ret = $this->send('2102', $C2S)){return array();}return (array)(isset($ret['positionList']) ? $ret['positionList'] : array());}/*** 获取最大交易数量(30秒10次)* @param string $code* @param float $price* @param int $orderType 1普通单2市价单(仅美股)5绝对限价订单6竞价订单7竞价限价订单8特别限价订单* @param number $orderID* @param string $adjustPrice* @param number $adjustSideAndLimit* @return array*/public function Trd_GetMaxTrdQtys($code, $price, $orderType=1, $orderID=0, $adjustPrice=false, $adjustSideAndLimit=0){if(! $this->Trd_UnlockTrade(true)){return array();}if(! $this->Trd_GetAccList()){return array();}if(! $accID = (string)$this->chooseAccount()){return array();}if(! $this->limit(__LINE__, 30, 10)){return array();}$C2S = array('header' => array('trdEnv' => $this->trdEnv,'trdMarket' => $this->trdMarket,'accID' => (string)$accID,),'orderType' => (int)$orderType,'code' => (string)$code,'price' => (float)$price,);if($orderID){$C2S['orderID'] = (string)$orderID;}if($adjustPrice){$C2S['adjustPrice'] = (bool)$adjustPrice;}if($adjustSideAndLimit){$C2S['adjustSideAndLimit'] = (float)$adjustSideAndLimit;}if(! $ret = $this->send('2111', $C2S)){return array();}return (array)$ret['maxTrdQtys'];}/*** 获取订单列表* @param array $filterStatusList 状态-1未知0未提交1等待提交2提交中3提交失败4处理超时结果未知5已提交待成交10部分成交11全部成交12撤单剩余部分13撤单中14剩余部分撤单成功15全部已撤单21下单失败22已失效23已删除* @param array $codeList* @param array $idList* @param bool $refreshCache* @return array*/public function Trd_GetOrderList($filterStatusList=[], $beginTime=0, $endTime=0, $codeList=[], $idList=[], $refreshCache=false){if(! $this->Trd_UnlockTrade(true)){return array();}if(! $this->Trd_GetAccList()){return array();}if(! $accID = (string)$this->chooseAccount()){return array();}if($refreshCache){if(! $this->limit(__LINE__, 30, 10)){return array();}}$C2S = array('header' => array('trdEnv' => $this->trdEnv,'trdMarket' => $this->trdMarket,'accID' => (string)$accID,),'refreshCache' => (bool)$refreshCache);if($filterStatusList){$C2S['filterStatusList'] = (array)$filterStatusList;}if($beginTime){$C2S['filterConditions']['beginTime'] = date('Y-m-d H:i:s', $beginTime);}if($endTime){$C2S['filterConditions']['endTime'] = date('Y-m-d H:i:s', $endTime);}if($codeList){$C2S['filterConditions']['codeList'] = (array)$codeList;}if($idList){$C2S['filterConditions']['idList'] = (array)$idList;}if(! $ret = $this->send('2201', $C2S)){return array();}return (array)(isset($ret['orderList']) ? $ret['orderList'] : array());}/*** 下单(30秒15次)* @param string $code 股票代码* @param int $trdSide 0未知1买入2卖出3卖空(是美股订单时有此方向)4买回(目前不存在)* @param float $qty 数量,期权单位是"张"(精确到小数点后 0 位,超出部分会被舍弃。期权期货单位是"张")* @param float $price 价格,(证券账户精确到小数点后 3 位,期货账户精确到小数点后 9 位,超出部分会被舍弃)* @param int $orderType 0未知1普通单2市价单(仅美股)5绝对限价订单6竞价订单7竞价限价订单8特别限价订单* @param bool $adjustPrice 是否调整价格:如果挂单价格不合理是否调整到合理的档位* @param float $adjustSideAndLimit 如果调整价格,是向上调整(正)还是向下调整(负),最多调整多少百分比* @param int $secMarket 证券所属市场 1港股正股/涡轮;2美股正股/期权;31沪市;32深市* @param string $remark 用户备注字符串,最多只能传64字节,可用于标识订单唯一信息等,下单填上订单结构就会带上* @param int $timeInForce 0当日有效1撤单前有效最多持续90自然日* @param bool $fillOutsideRTH 是否允许盘前盘后成交仅适用于美股限价单* @return id 订单ID*/public function Trd_PlaceOrder($code, $trdSide, $qty, $price, $orderType=1, $adjustPrice=false, $adjustSideAndLimit=0, $secMarket=1, $remark='', $timeInForce=0, $fillOutsideRTH=false){if(! $this->Trd_UnlockTrade(true)){return 0;}if(! $this->Trd_GetAccList()){return 0;}if(! $accID = (string)$this->chooseAccount()){return 0;}if(! $this->limit(__LINE__, 30, 15)){return 0;}if(! $this->limit(__LINE__, 1, 5)){return 0;}$C2S = array('header' => array('trdEnv' => $this->trdEnv,'trdMarket' => $this->trdMarket,'accID' => (string)$accID,),'packetID' => array('connID' => (string)$this->connID,'serialNo' => (int)$this->sequence),'code' => (string)$code,'trdSide' => (int)$trdSide,'orderType' => (int)$orderType,'qty' => (float)$qty,'price' => (float)$price,'secMarket' => (int)$secMarket,'remark' => (string)$remark);if($adjustPrice){$C2S['adjustPrice'] = (bool)$adjustPrice;}if($adjustSideAndLimit){$C2S['adjustSideAndLimit'] = (float)$adjustSideAndLimit;}if($timeInForce){$C2S['timeInForce'] = (int)$timeInForce;}if($fillOutsideRTH){$C2S['fillOutsideRTH'] = (bool)$fillOutsideRTH;}if(! $ret = $this->send('2202', $C2S)){return 0;}return (string)$ret['orderID'];}/*** 修改订单(改价/改量/改状态等)(30秒20次)* @param string $orderID $forAll为true时传0* @param int $modifyOrderOp 0未知1改单(价格/数量)2撤单3失效4生效5删除//下面的字段仅针对单个订单,且 modifyOrderOp 为 ModifyOrderOp_Normal 有效* @param float $qty 数量,期权单位是"张"(精确到小数点后 0 位,超出部分会被舍弃)* @param float $price 价格,(证券账户精确到小数点后 3 位,期货账户精确到小数点后 9 位,超出部分会被舍弃)//以下为调整价格使用,都传才有效,对港、A 股有意义,因为港股有价位,A 股2位精度,美股可不传* @param bool $forAll 是否对此业务账户的全部订单操作,true 是,false 否(对单个订单),无此字段代表 false,仅对单个订单* @param bool $adjustPrice 是否调整价格,如果价格不合法,是否调整到合法价位,true 调整,false 不调整。如果价格不合法又不允许调整,则会返回错误* @param float $adjustSideAndLimit 调整方向和调整幅度百分比限制,正数代表向上调整,负数代表向下调整,具体值代表调整幅度限制,如:0.015代表向上调整且幅度不超过1.5%;-0.01代表向下调整且幅度不超过* @return number*/public function Trd_ModifyOrder($orderID, $modifyOrderOp, $qty=0, $price=0, $forAll=false, $adjustPrice=false, $adjustSideAndLimit=0){if(! $this->Trd_UnlockTrade(true)){return 0;}if(! $this->Trd_GetAccList()){return 0;}if(! $accID = (string)$this->chooseAccount()){return 0;}if(! $this->limit(__LINE__, 30, 20)){return 0;}if(! $this->limit(__LINE__, 1, 5)){return 0;}$C2S = array('header' => array('trdEnv' => $this->trdEnv,'trdMarket' => $this->trdMarket,'accID' => (string)$accID,),'packetID' => array('connID' => (string)$this->connID,'serialNo' => (int)$this->sequence),'orderID' => (string)$orderID,'modifyOrderOp' => (int)$modifyOrderOp,'forAll' => (bool)$forAll,);if($modifyOrderOp == 1){$C2S['qty'] = (float)$qty;$C2S['price'] = (float)$price;}if($adjustPrice){$C2S['adjustPrice'] = (bool)$adjustPrice;}if($adjustSideAndLimit){$C2S['adjustSideAndLimit'] = (float)$adjustSideAndLimit;}if(! $ret = $this->send('2205', $C2S)){return 0;}return (string)$ret['orderID'];}/*** 获取成交列表* @param array $codeList* @param array $idList* @param bool $refreshCache* @return array*/public function Trd_GetOrderFillList($codeList=[], $idList=[], $refreshCache=false){if(! $this->Trd_UnlockTrade(true)){return array();}if(! $this->Trd_GetAccList()){return array();}if(! $accID = (string)$this->chooseAccount()){return array();}if($refreshCache){if(! $this->limit(__LINE__, 30, 10)){return array();}}$C2S = array('header' => array('trdEnv' => $this->trdEnv,'trdMarket' => $this->trdMarket,'accID' => (string)$accID,),'refreshCache' => (bool)$refreshCache);if($codeList){$C2S['filterConditions']['codeList'] = (array)$codeList;}if($idList){$C2S['filterConditions']['idList'] = (array)$idList;}if(! $ret = $this->send('2211', $C2S)){return array();}return (array)$ret['orderFillList'];}/*** 获取历史订单列表(30秒10次)* @param int $beginTime* @param int $endTime* @param array $filterStatusList 状态-1未知0未提交1等待提交2提交中3提交失败4处理超时结果未知5已提交待成交10部分成交11全部成交12撤单剩余部分13撤单中14剩余部分撤单成功15全部已撤单21下单失败22已失效23已删除* @param array $codeList 股票代码过滤['00700','00388']* @param array $idList 订单ID过滤* @return array*/public function Trd_GetHistoryOrderList($beginTime, $endTime, $filterStatusList=[], $codeList=[], $idList=[]){if(! $this->Trd_UnlockTrade(true)){return array();}if(! $this->Trd_GetAccList()){return array();}if(! $accID = (string)$this->chooseAccount()){return array();}if(! $this->limit(__LINE__, 30, 10)){return array();}$C2S = array('header' => array('trdEnv' => $this->trdEnv,'trdMarket' => $this->trdMarket,'accID' => (string)$accID,),'filterConditions' => array('beginTime' => date('Y-m-d H:i:s', $beginTime),'endTime' => date('Y-m-d H:i:s', $endTime),),);if($codeList){$C2S['filterConditions']['codeList'] = (array)$codeList;}if($idList){$C2S['filterConditions']['idList'] = (array)$idList;}if($filterStatusList){$C2S['filterStatusList'] = (array)$filterStatusList;}if(! $ret = $this->send('2221', $C2S)){return array();}return (array)$ret['orderList'];}/*** 获取历史成交列表(30秒10次)* @param int $beginTime* @param int $endTime* @param array $codeList* @param array $idList* @return array*/public function Trd_GetHistoryOrderFillList($beginTime, $endTime, $codeList=[], $idList=[]){if(! $this->Trd_UnlockTrade(true)){return array();}if(! $this->Trd_GetAccList()){return array();}if(! $accID = (string)$this->chooseAccount()){return array();}if(! $this->limit(__LINE__, 30, 10)){return array();}$C2S = array('header' => array('trdEnv' => $this->trdEnv,'trdMarket' => $this->trdMarket,'accID' => (string)$accID,),'filterConditions' => array('beginTime' => date('Y-m-d H:i:s', $beginTime),'endTime' => date('Y-m-d H:i:s', $endTime),),);if($codeList){$C2S['filterConditions']['codeList'] = (array)$codeList;}if($idList){$C2S['filterConditions']['idList'] = (array)$idList;}if(! $ret = $this->send('2222', $C2S)){return array();}return (array)$ret['orderFillList'];}/*** 编码* @param int $proto* @param string $C2S* @return boolean|string*/public function encode($proto, $C2S){if(! $proto = (int)$proto){return false;}$body = $C2S; //默认不加密if($this->encrypt && ($proto == 1001)){$private_pkey = openssl_pkey_get_private(file_get_contents($this->private_key));$details_pkey = openssl_pkey_get_details($private_pkey); //由私钥计算得到公钥$public_pkey = openssl_pkey_get_public($details_pkey['key']);$C2S_encrypted = '';for($i=0, $s=substr($C2S, 0, 100); $s; $i++,$s=substr($C2S, $i*100, 100)){$encrypted = '';openssl_public_encrypt($s, $encrypted, $public_pkey, OPENSSL_PKCS1_PADDING);$C2S_encrypted .= $encrypted;}$body = $C2S_encrypted;}if($this->encrypt && ($proto != 1001)){$mod = strlen($C2S)%16;$multiplier = $mod ? (16 - $mod) : 0;$body = openssl_encrypt($C2S . str_repeat("\\0", $multiplier), 'AES-128-ECB', $this->connAESKey, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING);$body .= str_repeat("\\0", 15);$body .= chr($mod);}$ret = 'FT';$ret .= pack("L", (int)$proto); //协议ID$ret .= pack("C", 1); //协议格式类型,0为Protobuf格式,1为Json格式$ret .= pack("C", 0); //协议版本,用于迭代兼容$ret .= pack("L", ++$this->sequence); //包序列号,用于对应请求包和回包$ret .= pack("L", strlen($body)); //包体长度$ret .= sha1($C2S, true); //包体原始数据(解密后)的SHA1哈希值$ret .= pack("@8");//保留8字节扩展$ret .= $body;return (string)$ret;}/*** 解码回包* @param string $recv* @param string $C2S* @return array*/public function decode($recv, $C2S){if(empty($recv) === true){return array();}$head = substr($recv, 0, 44);$body = substr($recv, 44);$pack = unpack("CF/CT/Lproto/CProtoFmtType/CProtoVer/LSerialNo/LBodyLen", $head, 0);if($this->encrypt && ($pack['proto'] == 1001)){$private_pkey = openssl_pkey_get_private(file_get_contents($this->private_key));$body_decrypted = '';for($i=0, $s=substr($body, 0, 128); $s; $i++,$s=substr($body, $i*128, 128)){$decrypted = '';openssl_private_decrypt($s, $decrypted, $private_pkey, OPENSSL_PKCS1_PADDING);$body_decrypted .= $decrypted;}$body = $body_decrypted;}if($this->encrypt && ($pack['proto'] != 1001)){$mod = ord(substr($body, -1)); //补了多少个0$body = openssl_decrypt(substr($body, 0, -16), 'AES-128-ECB', $this->connAESKey, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING);$body = $mod ? substr($body, 0, $mod-16) : $body;}if(! $ret = json_decode($body, true)){$this->errorlog("json Error:{$pack['proto']}:{$C2S} - {$body}", 1);return array();}echo "返回数据:".PHP_EOL,var_dump($ret);if($ret['retType'] != 0){if(in_array($pack['proto'], array(3001,3203)) && preg_match('/(\\d{5}$)/i', trim($ret['retMsg']), $m)){$GLOBALS['FUTU_DELISTED'][$m[1]] = 1;}else{$this->errorlog("ret Error:{$pack['proto']}:{$C2S} - {$ret['retType']}:{$ret['retMsg']}", in_array($ret['retType'], array(-1)) ? 4 : 0);}return array();}if($ret['errCode'] != 0){$this->errorlog("err Error:{$pack['proto']}:{$C2S} - {$ret['errCode']}:{$ret['retMsg']}", 1);return array();}$f = __DIR__ . "/futu_{$pack['proto']}.txt";if(false && is_file($f)){ //记录服务器的返回file_put_contents($f, var_export($pack, true)."\
".var_export($ret, true));}return array('proto'=>$pack['proto'], 's2c'=>isset($ret['s2c'])?$ret['s2c']:['result'=>true]);}/*** 私有限额方法* @param int $typ 限额类型* @param int $sec 多少秒* @param int $cnt 多少次 比如订单为30秒20次* @return boolean 是否在限额内*/private function limit($typ, $sec, $cnt){if(! $typ = $this->checkInt($typ)){return false;}if(! $sec = $this->checkInt($sec)){return false;}if(! $cnt = $this->checkInt($cnt)){return false;}//此处实现限额逻辑,可以用一个环形的存储结构return true;}/**** @param unknown $Protocol* @param unknown $ReqParam* @return array*/private function send($proto, $C2S){if(! $this->connect()){var_dump("链接失败");return array();}if(! $C2S = json_encode(array('c2s' => $C2S))){return array();}echo "发送协议代码:".$proto.PHP_EOL."发送数据:";print_r($C2S.PHP_EOL);if(! $data = $this->encode($proto, $C2S)){return array();}if(! $length = @$this->cli->send("{$data}")){$this->errorlog("Send Error:{$proto} - {$C2S} - ".swoole_last_error()." - ".swoole_strerror(swoole_last_error(), 9), 0);return array();}if($this->push){ //推送模式不需要接收返回(此处必须返回空值)return array();}if(! $recv = @$this->cli->recv()){$this->errorlog("Recv Error:{$proto} - {$C2S} - ".swoole_last_error()." - ".swoole_strerror(swoole_last_error(), 9), 0);return array();}if(! $ret = $this->decode($recv, $C2S)){var_dump("解码失败");return array();}
//        echo "返回结果:";
//        var_dump($ret);if($ret['proto'] && ($ret['proto'] != $proto) && ! in_array($proto, [3001,3002])){$this->errorlog("proto Error:{$proto} - {$C2S}", 1);return array();}return (array)$ret['s2c'];}/*** 记录错误,加上断线自动重连* @param unknown $msg* @param number $level 0退出+日志1断线+日志2退出3断线4日志* @return boolean*/private function errorlog($msg, $level=0){var_dump($msg);//in_array($level, [0,1,4]) && file_put_contents(); //此处记录日志in_array($level, [1,3]) && $this->close();in_array($level, [0,2]) && die();$this->die && die("失败");return false;}public function __destruct(){$this->close();}/*** @param $number* @return bool|int*/private function checkInt($number){if (!is_int($number)){return false;}return (int)$number;}
}

PHP测试的同步调用文件(futu_get_test.php)

<?php
/*** Created by PhpStorm.* Date: 2021/9/27* Time: 2:38 PM*//*** 同步模式*/
include('class.ftnn.php');
//$o = new futu('127.0.0.1', 11111, '739146');
//$trdEnv = 0; //0:模拟账户 1:真实账户
$o = new futu('127.0.0.1', 11111, '123456',0);/*** 获取股票板块列表*
$l = $o->Qot_GetPlateSet(0);
foreach ($l as $v){echo "行情市场:{$v['market']},代码:{$v['code']},名称:{$v['name']}".PHP_EOL;
}
//print_r($l);
die();*//*** 获取板块下的股票(30秒10次)*
$l=$o->Qot_GetPlateSecurity("BK1283");
foreach($l as $v){$vv = $v["basic"];echo "行情市场:{$vv['security']['market']},代码:{$vv['security']['code']},股票ID:{$vv['id']},每手数量:{$vv['lotSize']},股票类型:{$vv['secType']},股票名字:{$vv['name']},上市时间:{$vv['listTime']},是否退市:{$vv['delisting']},上市时间戳:{$vv['listTimestamp']},所属交易所:{$vv['exchType']}".PHP_EOL;}
die();*///$l = $o->Qot_GetStaticInfo(3);
//$l = $o->Qot_RequestHistoryKLQuota(3);
//$l = $o->GetUserInfo();
//$l = $o->Qot_GetSubInfo();
//$l = $o->Qot_Sub(["301069"],[1]);//先订阅
//var_dump($l);
//die();
//sleep(5);
/*** 获取股票基本行情$l = $o->Qot_GetBasicQot(["06055"]);
//$o->Qot_Sub(["06055"],[1],false);//先订阅
foreach($l as $k => $v){$string_info =  "";$string_info .= "行情市场:{$v['security']['market']},";$string_info .= "代码:{$v['security']['code']},";$v['isSuspended'] = $v['isSuspended']?'是':'否';$string_info .= "是否停牌:{$v['isSuspended']},";$string_info .= "上市日期:{$v['listTime']},";$string_info .= "价差:{$v['priceSpread']},";$string_info .= "最新价的更新时间:{$v['updateTime']},";$string_info .= "最高价:{$v['highPrice']},";$string_info .= "开盘价:{$v['openPrice']},";$string_info .= "最低价:{$v['lowPrice']},";$string_info .= "最新价:{$v['curPrice']},";$string_info .= "昨收价:{$v['lastClosePrice']},";$string_info .= "成交量:{$v['volume']},";$string_info .= "成交额:{$v['turnover']},";$string_info .= "换手率:{$v['turnoverRate']}%,";$string_info .= "振幅:{$v['amplitude']}%,";switch($v['darkStatus']){case 0:$v['darkStatus'] = "无暗盘交易";break;case 1:$v['darkStatus'] = "暗盘交易中";break;case 2:$v['darkStatus'] = "暗盘交易结束";break;}$string_info .= "暗盘交易状态:{$v['darkStatus']},";$string_info .= "上市日期时间戳:{$v['listTimestamp']},";$string_info .= "最新价的更新时间戳:{$v['updateTimestamp']},";$string_info .= "股票状态:{$v['secStatus']}";echo $string_info.PHP_EOL;
}//print_r($l);*//*** 获取交易业务账户列表$l = $o->Trd_GetAccList();
//print_r($l);
//die();
foreach ($l as $k => $v){switch($k){case 0:$k = "未知市场";break;case 1:$k = "香港市场";break;case 2:$k = "美国市场";break;case 3:$k = "A股市场(仅用于模拟交易)";break;case 4:$k = "A股通市场";break;case 5:$k = "期货市场";break;}foreach ($v as $kk => $vv){switch($kk){case 0:$kk = "模拟环境";$trdEnv = 0;break;case 1:$kk = "真实环境";$trdEnv = 1;break;}foreach ($vv as $kkk => $vvv){switch($kkk){case 0:$kkk = "未知类型";break;case 1:$kkk = "现金";break;case 2:$kkk = "保证金";break;}if ($trdEnv == 0){foreach ($vvv as $kkkk => $vvvv){switch($kkkk){case 0:$kkkk = "未知类型";break;case 1:$kkkk = "股票模拟";break;case 2:$kkkk = "期权模拟";break;}echo $k."-".$kk."-".$kkk."-".$kkkk."-"."账户:".$vvvv.PHP_EOL;}continue;}echo $k."-".$kk."-".$kkk."-"."账户:".$vvv.PHP_EOL;}}
}
//print_r($l);
//die();*//*** 获取账户资金$l = $o->Trd_GetFunds();
print_r($l);
die();*//*** 查询今日订单* (下午时候查不到今日订单,待查原因)$beginTime = time()-86400;
$endTime = time()+86400;
$l = $o->Trd_GetOrderList([0,-1,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],$beginTime,$endTime);
print_r($l);*//*** 查询当日成交* 模拟交易不支持成交数据$l = $o->Trd_GetOrderFillList();
print_r($l);*//*** 查询历史成交* 模拟交易不支持成交数据$beginTime = time()-86400;
$endTime = time()+86400;
$l = $o->Trd_GetHistoryOrderFillList($beginTime,$endTime);
print_r($l);*//*** 获取静态数据(股票基本信息)*/
$l = $o->Qot_GetStaticInfo(0,["00700"]);
if(empty(!$l)){foreach ($l as $k => $v){if (isset($v["basic"])){$basic = $v["basic"];$info = "";$info .= "市场:".$basic["security"]["market"];$info .= "_代码:".$basic["security"]["code"];$info .= "_id:".$basic["id"];$info .= "_每手数量:".$basic["lotSize"];$info .= "_股票类型:".$basic["secType"];$info .= "_股票名字:".$basic["name"];$info .= "_上市时间:".$basic["listTime"];$info .= "_是否退市:".$basic["delisting"];$info .= "_上市时间戳:".$basic["listTimestamp"];$info .= "_所属交易所:".$basic["exchType"];print_r($info);}}
}/*** 买入$l = $o->Trd_PlaceOrder("09988",1,100,141.1,1);
$orderID = $l;
if($orderID >0){print_r("成功,订单号:".$orderID);
}else{print_r("失败");
}*//*** 查询历史订单$beginTime = time()-86400;
$endTime = time()+86400;//$l = $o->Trd_GetHistoryOrderList($beginTime,$endTime,[0,-1,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]); //查询全部
$l = $o->Trd_GetHistoryOrderList($beginTime,$endTime,[0,-1,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],["09988"],["4635407981036560905"]);   //查询单个
print_r($l);
die();*//*** 查询持仓$l = $o->Trd_GetPositionList();
print_r($l);*//*** 撤单$l = $o->Trd_ModifyOrder("4635483404958038286",2,100,141.1,false);
$orderID = $l;
if($orderID >0){
print_r("成功,订单号:".$orderID);
}else{
print_r("失败");
}*//*** 卖出$l = $o->Trd_PlaceOrder("00700",2,100,456,1); //卖出单个股票部分
$orderID = $l;
if($orderID >0){
print_r("成功,订单号:".$orderID);
}else{
print_r("失败");
}*/

PHP测试的异步调用文件(futu_push_test.php)

<?php
/*** Created by PhpStorm.* Date: 2021/9/27* Time: 2:31 PM*//*** 异步模式*/
include('class.ftnn.php');
if(php_sapi_name() !== 'cli'){var_dump("不是cli");die();
}$GLOBALS['o'] = new futu('127.0.0.1', 11111, '123456');Swoole\\Coroutine::create(function(){try{$cli = new Swoole\\Coroutine\\Client(SWOOLE_SOCK_TCP);$cli->set(array('socket_buffer_size' => 1024*1024*32, //32M缓存区'open_length_check'     => 1,'package_length_type'   => 'L','package_length_offset' => 12,       //第N个字节是包长度的值'package_body_offset'   => 44,       //第几个字节开始计算长度'package_max_length'    => 8*1024*1024,  //协议最大长度'open_tcp_nodelay' => false));if($cli->connect('127.0.0.1', '11111', 20) === false){var_dump("链接失败");$cli->close();return true;}$GLOBALS['o']->push($cli); //关键$cli->lasttime = time(); //最后心跳时间$Timer = Swoole\\Timer::tick(1000, function($Timer, $cli){ //每N秒执行var_dump(111);if(! $GLOBALS['o']->InitConnect()){ //初始化连接var_dump("InitConnect失败");return true;}var_dump(23333);if(! $GLOBALS['o']->Trd_UnlockTrade(true)){ //解锁交易var_dump("Trd_UnlockTrade失败");return true;}var_dump(2222);if(! $GLOBALS['o']->Trd_GetAccList()){ //获取账户var_dump("Trd_GetAccList失败");return true;}if(! $GLOBALS['o']->Trd_SubAccPush()){ //订阅订单推送var_dump("Trd_SubAccPush失败");return true;}if(time() - $cli->lasttime >= 10){$cli->lasttime = time();$GLOBALS['o']->KeepAlive(); //保持连接}$GLOBALS['o']->Qot_Sub('00700', 1, true, true, [1], false);}, $cli);while($data = $cli->recv()){if(! $a = $GLOBALS['o']->decode($data, '')){exit(0);}if(! $proto = (int)$a['proto']){exit(0);}if(! $a = $a['s2c']){exit(0);}switch ($proto){case 1001: //初始化连接$GLOBALS['o']->connID = (string)$a['connID'];$GLOBALS['o']->loginUserID = (string)$a['loginUserID'];break;case 1003: //系统推送通知break;case 1004: //保持连接break;case 2001: //获取交易账号foreach ((array)$a['accList'] as $v){foreach ((array)$v['trdMarketAuthList'] as $vv){ //可拥有多个交易市场权限,目前仅单个$GLOBALS['o']->accList[$vv][$v['trdEnv']] = (string)$v['accID'];}}break;case 2005: //解锁完成$GLOBALS['o']->unlock = true;break;case 2008: //订阅订单推送(说明环境准备就绪)$GLOBALS['o']->accPush = true;break;case 2208: //推送订单更新(说明订单有了变化)break;case 2218: //推送新成交(说明持仓有了变化)break;case 3001: //订阅或者反订阅break;case 3005: //推送股票基本报价break;case 3007: //推送K线print_r($a);break;case 3009: //推送分时break;case 3011: //推送逐笔$code = $a['security']['code'];break;case 3013: //推送买卖盘$code = $a['security']['code'];break;case 3015: //推送经纪队列break;case 3019: //到价提醒通知break;default:break;}};var_dump("结束");}catch(Swoole\\ExitException $e){var_dump("报错".$e->getMessage());Swoole\\Timer::clear($Timer);}
});

PHP使用socket TCP通信对接富途牛牛相关推荐

  1. 使用NIO实现异步非阻塞Socket tcp通信

    使用NIO实现异步非阻塞Socket tcp通信 一.BIO与NIO IO(BIO)和NIO区别:其本质就是阻塞和非阻塞的区别 阻塞概念:应用程序在获取网络数据的时候,如果网络传输数据很慢,就会一直等 ...

  2. socket PHP:详细简单的socket TCP通信PHP实现

    如果你想直接运行程序实现效果:请直接看 3.3 本地服务器及客服端程序 1 背景介绍 目标:我希望通过套接字的TCP传输来搭建一个服务器,这个服务器的作用是:接受多个客户端的连接并完成他们的相互通信. ...

  3. 【iOS】Socket/TCP 通信 发送 NSString 字符串格式数据

    Socket/TCP 原理这里就不阐述了,网上一搜一大堆,直接上关键代码. [注]iOS 目前有非常著名的第三方库 CocoaAsyncSocket 可以使用,但是我们项目当时做大数据上报要求直接发送 ...

  4. 为什么说刚上市的富途,是最“腾讯”的腾讯系公司?

    富途用户可以在富途牛牛App上交易富途股票了! 2019年3月8日,腾讯又收获一家上市公司:国内首家互联网证券平台富途证券母公司富途控股登陆纳斯达克,其上市打破了最近一年来中概股上市即破发的魔咒,开盘 ...

  5. TCP通信的服务器端代码实现

    package com.learn.demo01.TCP;import java.io.IOException; import java.io.InputStream; import java.io. ...

  6. TCP通信的客户端代码实现

    package com.learn.demo01.TCP;import java.io.IOException; import java.io.InputStream; import java.io. ...

  7. Dubbo底层采用Socket进行通信详解

    由于Dubbo底层采用Socket进行通信,自己对通信理理论也不是很清楚,所以顺便把通信的知识也学习一下. n  通信理论 计算机与外界的信息交换称为通信.基本的通信方法有并行通信和串行通信两种. 1 ...

  8. 财报前被香港证监会点名,富途内控再受拷问

    财报发布前一天,富途证券再次被香港证监会点名.据悉,因旗下账户疑涉"唱高散货"骗局,富途被要求冻结有关客户账户.目前调查仍在进行中. 对此,富途回应将积极配合香港证监会的执法行动. ...

  9. 亮眼财报业绩背后,难掩富途控股估值陷阱

    亮眼财报业绩背后,难掩富途控股估值陷阱 出品 l 观点财经 作者 l 周更 为国内投资者搭桥进入海外资本市场的富途证券,其母公司富途控股(NASDAQ: FUTU)作为2019年互联网券商海外上市的金 ...

最新文章

  1. 讯飞语音的中的bug用户校验失败
  2. canvas转化图片并下载
  3. 知识图谱学习笔记-知识图谱介绍
  4. python大一考试_python 考试补缺
  5. Linux/Unix下tar命令详解
  6. Madagascar中的宏定义函数--绝对值函数和符号函数
  7. java 泛型示例_Java泛型示例教程–泛型方法,类,接口
  8. 直方图均衡化的代码解析
  9. Python基础笔记(手写版)
  10. mongodb 备份 导入导出
  11. 第二届“香山杯”网络安全大赛|MISC
  12. spring boot 项目重新搭建----------mvc配置:拦截器
  13. 如何在 XMind 中输入数学方程?LaTeX 简易入门
  14. 常见的分词方法接口+ jieba自定义领域内的词表然后加载词表进行分词
  15. 论文翻译[Deep Residual Learning for Image Recognition]
  16. USACO2011Open Silver Running Laps题解
  17. 网络监控神器!这7大免费开源工具可别错过
  18. webix, DataTable组件,给表头增加Tooltip
  19. 产品研发杂谈(GJB5000A)
  20. 如何让自己时刻冷静的方法_高三怎么让自己心静下来

热门文章

  1. Rtos的调研分析报告
  2. 黑客攻击-木马程序(2)
  3. debian live-buid
  4. @Autowired报空指针NullPoint
  5. matlab验证采样定理
  6. 二级计算机等级证水平高吗,全国计算机水平一级高还是二级高
  7. 数学:分数的加减乘除模板
  8. 传Google投资傲游100万美元 创始人陈明杰否认
  9. IP/TCP协议详解
  10. zbb20180929 zk Zookeeper的功能以及工作原理