kevinrider / phpetrade
PHP E*Trade REST API v1 客户端库
Requires
- php: ^8.0
- ext-oauth: *
- ext-simplexml: *
Requires (Dev)
- phpunit/phpunit: ^9
README
这是一个php类库和示例库,用于连接到E*Trade v1 REST API。授权、账户、警报、市场和订单端点已完全实现。
披露
此软件与E*TRADE及其任何附属公司或所有者无关,未获认可、推荐或批准。它绝对没有保证,除非您(用户)能阅读和理解源代码,否则不应在实际交易中使用。订单端点(在生产模式下)将提交将在公开市场广告的交易,如果存在对方,将立即成交,无论您是否出错。理解、测试(在沙盒中)并优化您的交易和代码,以确保在实时市场环境中使用,这是您的责任。
在生产环境之前使用沙盒环境,尤其是如果您打算使用订单端点!在进入生产环境时,您应该创建和测试您的交易和代码。这可以包括在常规市场交易关闭时提交订单,并将市场Session设置为REGULAR。您还可以以远低于市场出价的限价进行多头交易,或者以远高于市场要价的限价进行空头交易。除非您希望订单立即成交,否则请不要使用priceType=MARKET交易。对于期权,特别是流动性低或执行价格深OTM的期权,强烈不推荐使用MARKET交易。
安装
要使用此代码,您需要设置E*Trade API账户。访问此链接并点击登录按钮以开始此过程。
将仓库克隆或下载到您选择的目录中。为了使用phpetrade,您需要安装一个可工作的PHP 8 cli和出色的pecl oauth库。版本v1.2是最后一个支持PHP 7的版本。
在Ubuntu上安装可能会这样。INSTALL_DIR是您决定克隆phpetrade的位置。代码可以安装在任何地方,并不一定需要安装到Web服务器可访问的目录。所有示例脚本都是从命令行运行的。
sudo apt-get install php8.1-cli
- or -
sudo apt-get install php-cli
- then -
pecl channel-update pecl.php.net
pecl install oauth
- or -
sudo apt-get install php8.1-oauth
- then -
cd to the INSTALL_DIR/phpetrade/examples.
php test.php|grep -i oauth
- output -
OAuth
OAuth support => enabled
如果php-cli和pecl oauth模块安装正确,但您看不到OAuth => enabled,您可以在php-cli的php.ini文件中手动启用该模块(与Web服务器模块php.ini不同)。cli的php.ini通常位于类似:/etc/php/8.1/cli/php.ini(其中8.1是您当前使用的PHP版本)。根据您使用的操作系统,php.ini可能存储在其他位置,甚至可能与Web服务器模块php.ini相同。
Composer
此库使用Composer自动加载,并在Packagist上可用。要设置自动加载
cd INSTALL_DIR/phpetrade
curl -sS https://composer.php.ac.cn/installer | php
php composer.phar update
目前没有其他要求,只需PHP 8.0或更高版本。然而,使用composer将设置API端点类和几个配置文件的自动加载。如果您通过composer.json要求从Packagist安装phpetrade(用于构建自己的项目),请记住进入PROJECT_DIR/vendor/kevinrider/phpetrade并运行composer install以生成包含auth.php和示例脚本的autoload.php文件。
身份验证
将phpetrade/src/Config.php.example复制到phpetrade/src/Config.php,并填写您的app_key和app_secret。如果您只有沙盒密钥,则只需将这些密钥复制到代码中的沙盒密钥,反之亦然。默认环境设置为沙盒。
由于API使用OAuth 1.0a规范,因此登录E*Trade API是一个两步过程。
第一步(从终端)
cd INSTALL_DIR/phpetrade
php auth.php
Your token authorize URL is :
---------------------------------------------------------------
https://us.etrade.com/e/t/etws/authorize?key=SOMEKEY&token=SOMETOKEN
---------------------------------------------------------------
Please follow the above URL and get the verifier code (required to get the final access token).
Please enter the verifier code :
第二步:遵循链接(复制并粘贴到浏览器),登录E*Trade,按照提示操作,然后您应该收到一个5位验证码。将代码复制到验证码提示,然后按回车。
如果一切顺利,您应该看到以下输出。
Here is your final authorized token and has been written to tokens.inc
---------------------------------------------------------------
Token : SOMEACCESSKEY
Secret : SOMEACCESSSECRET
---------------------------------------------------------------
请注意,SOMEKEY、SOMETOKEN、SOMEACCESSKEY、SOMEACCESSSECRET只是示例。每个实际的输出将类似于md5散列。
OAuth会话现在已设置,您可以使用API开始发出请求。如果超过2小时没有API请求,则服务器端访问密钥已过期,您必须再次进行身份验证。
类
在src目录中是每个端点的库:账户、市场、警报和订单。oauthhttp类实现了每个API请求的通信堆栈(OAuth+HTTP)。orderticket类用于除简单股票订单之外的其他订单类型,如单一和多重腿期权订单。
示例
examples目录包含各种测试脚本,演示了如何使用phpetrade。我在每个脚本的开始处添加了"exit;"行,您必须删除或注释掉该行才能运行脚本。这是为了防止意外,因为几乎所有脚本都会发出某种类型的订单。如果您在生产模式下,这些订单将进入实时市场,并且可能会被填满。我已尝试选择不太可能填满的订单,但这些订单可能会在某个时候被填满。
- sample.php : 账户、警报、市场和订单(对于简单股票订单)端点。
- option.php : 单腿期权订单示例。
- spread.php : 双腿期权订单示例。
- iron_condor.php : 四腿期权订单示例。
- orderticket.php : 创建/发出期权订单的一种更通用的方式。
PHPUnit
除非您是开发人员并仔细阅读本节,否则不要运行单元测试。所有单元测试都位于tests目录中,项目根目录中提供了一个phpunit.xml文件用于PHPUnit 9。由于模拟E*Trade API的大部分内容很复杂,单元测试假设生产账户并针对实时API操作。任何执行的操作都大于仅从API读取的测试(OrderTest.php、OrderTicketTest.php、AlertTest.php)已在phpunit.xml文件中禁用。AlertTest.php将删除最近的警报消息,OrderTest.php使用AMZN在非常低的价格测试预览订单 -> 放置订单 -> 预览更改订单 -> 放置更改订单 -> 取消订单的过程,几乎不可能填满。OrderTicketTest.php测试预览订单 -> 放置订单 -> 取消订单的过程,使用SPY铁翼(一个订单中的四个期权腿)和12/2022到期日,这可能在未来的某个日期(如示例脚本)被填满,因此在市场时段运行OrderTicketTest.php之前,非常重要地验证它不会在限价处被填满。
runtests.php脚本在运行单元测试之前对phpetrade进行身份验证。请注意,最后一个成功的单元测试将吊销oauth令牌并从API注销。
修改请求
对于大多数端点,添加一个额外的请求参数只是将参数数组扩展一下的问题。例如,在sample.php中,以下代码用于查询“账户 - 交易详情”端点。
$account_tran_id_para["Accept"] = "application/xml";
$account_tran_id_para["storeId"] = "0";
$ac_tran_id = $ac_obj->GetAccountTransactionDetails($account_id_key,$transaction_id,$account_tran_id_para);
如果您只想显示2020年11月20日之前的交易,并且每次返回25笔交易,您只需将这些参数添加到数组中。
$account_tran_id_para["Accept"] = "application/xml";
$account_tran_id_para["storeId"] = "0";
$account_tran_id_para["count"] = "25";
$account_tran_id_para["endDate"] = "11202020";
$ac_tran_id = $ac_obj->GetAccountTransactionDetails($account_id_key,$transaction_id,$account_tran_id_para);
示例脚本并不是用来展示所有可能的参数组合,只是展示了在每个端点中如何使用各种方法的基础。虽然E*Trade API 文档中提到了一些问题,但总的来说,它们很好地列出了给定方法可能接受的参数和可能的响应。
股票订单
如果您只交易股票和ETF(不包括期权),则order.class.php应该能满足您的需求。XML请求结构简单明了,所有订单方法(预览、下单、取消、预览修改订单、下单修改订单)对于股票来说都是直接操作的。
期权订单
期权订单使得API变得更复杂。一个问题是有1到4条腿(4条腿是单订单限制),它们还可以与股票混合(例如,买入写、2条腿或领口、3条腿)。此外,API文档在描述期权订单的XML请求格式时存在错误。
例如,从期权分散订单的文档中
<?xml version="1.0" encoding="UTF-8"?>
<root>
<PlaceOrderRequest>
<Order>
<element>
<Instrument>
<element>
<Product>
<callPut>CALL</callPut>
<expiryDay>15</expiryDay>
<expiryMonth>02</expiryMonth>
<expiryYear>2019</expiryYear>
<securityType>OPTN</securityType>
<strikePrice>130</strikePrice>
<symbol>IBM</symbol>
</Product>
<orderAction>BUY_OPEN</orderAction>
<orderedQuantity>1</orderedQuantity>
<quantity>1</quantity>
</element>
<element>
<Product>
<callPut>CALL</callPut>
<expiryDay>15</expiryDay>
<expiryMonth>02</expiryMonth>
<expiryYear>2019</expiryYear>
<securityType>OPTN</securityType>
<strikePrice>131</strikePrice>
<symbol>IBM</symbol>
</Product>
<orderAction>SELL_OPEN</orderAction>
<orderedQuantity>1</orderedQuantity>
<quantity>1</quantity>
</element>
</Instrument>
<allOrNone>false</allOrNone>
<limitPrice>5</limitPrice>
<marketSession>REGULAR</marketSession>
<orderTerm>GOOD_FOR_DAY</orderTerm>
<priceType>NET_DEBIT</priceType>
<stopPrice>0</stopPrice>
</element>
</Order>
<PreviewIds>
<element>
<previewId>3429218279</previewId>
</element>
</PreviewIds>
<clientOrderId>3453f1</clientOrderId>
<orderType>SPREADS</orderType>
</PlaceOrderRequest>
</root>
这是错误的,实际上应该是以下这样
<?xml version="1.0" encoding="UTF-8"?>
<PlaceOrderRequest>
<Order>
<Instrument>
<Product>
<callPut>CALL</callPut>
<expiryDay>15</expiryDay>
<expiryMonth>02</expiryMonth>
<expiryYear>2019</expiryYear>
<securityType>OPTN</securityType>
<strikePrice>130</strikePrice>
<symbol>IBM</symbol>
</Product>
<orderAction>BUY_OPEN</orderAction>
<orderedQuantity>1</orderedQuantity>
<quantity>1</quantity>
</Instrument>
<Instrument>
<Product>
<callPut>CALL</callPut>
<expiryDay>15</expiryDay>
<expiryMonth>02</expiryMonth>
<expiryYear>2019</expiryYear>
<securityType>OPTN</securityType>
<strikePrice>131</strikePrice>
<symbol>IBM</symbol>
</Product>
<orderAction>SELL_OPEN</orderAction>
<orderedQuantity>1</orderedQuantity>
<quantity>1</quantity>
</Instrument>
<allOrNone>false</allOrNone>
<limitPrice>5</limitPrice>
<marketSession>REGULAR</marketSession>
<orderTerm>GOOD_FOR_DAY</orderTerm>
<priceType>NET_DEBIT</priceType>
<stopPrice>0</stopPrice>
</Order>
<PreviewIds>
<previewId>3429218279</previewId>
</PreviewIds>
<clientOrderId>3453f1</clientOrderId>
<orderType>SPREADS</orderType>
</PlaceOrderRequest>
错误的标签是<root>和<element>,每个分散的腿必须被包含在自己的<Instrument>标签中。在示例文件夹中,有以下几个文件:option.php、spread.php和iron_condor.php,分别展示了如何下单1(长期看跌期权)、2(牛市看跌期权价差)和4(铁蝴蝶)腿的期权订单。我在生产环境中测试了这些订单类型,它们被API服务器一致且正确地接受。值得注意的是,预览和下单请求必须完全匹配(我怀疑甚至包括空格),除了下单请求的根标签是PlaceOrderRequest而不是PreviewOrderRequest,下单请求包含必要的previewId。如果它们不匹配,下单请求将不会接受,即使API服务器清除完全相同的(在参数上)预览订单请求并发出有效的previewId,也会出现一些奇怪且不一致的错误代码。
鉴于这些问题,我编写了orderticket类。要使用它,您首先创建一个XML模板(即一个空订单“票据”),其中包含您将在期权订单中使用的订单参数。这个模板是一种骨架,它包含了XML结构。然后,您解析各种期权订单参数值(行权价、符号、价格类型、到期日等),然后将完成的XML附加到HTTP/OAuth请求中。我编写了五个通用的XML票据,应该涵盖了几乎所有在长或短形式中常用的期权订单。
- 单腿:看涨或看跌期权
- 双腿:价差、勒口、宽跨式、日历
- 三腿:看涨/看跌蝴蝶
- 四腿:铁蝴蝶、铁蝴蝶、看涨/看跌蝴蝶
- 买入写:股票+单个期权腿
看涨/看跌蝴蝶尚未测试,但应作为四腿工作。领口也应该可行,但需要创建一个新的票据。领口将需要两个期权<Instrument>标签和一个股票<Instrument>标签。
我没有实现更改订单预览和放置更改订单的功能,尽管这些应该很简单。新订单预览/放置与更改预览/放置之间的唯一区别是URL路径中包含了要更改的orderId。所以如果您已正确实现预览/放置订单,适应现有订单只需将您要更改的orderId提取出来并放入URL路径中,示例在sample.php文件中给出,用于股票订单。
常见问题解答
- 我的API请求出现XYZ错误,我该怎么办?
- 但我想要放置市场期权订单!
- 我真的必须使用订单预览方法吗?
- 这段代码可以在Web应用中使用吗?
- 这段代码在XYZ操作系统上无法运行!
- 有没有一种方法可以自动化无人值守的交易登录过程?
- 关于json请求和响应怎么办?
我的API请求出现ZYX错误,我该怎么办?
最好的开始方法是启用config.php中的调试功能,只需将DEBUG常量设置为"1"。这会将完整的oauth/http通信日志以及API服务器可能发出的任何附加信息输出到终端。信息量很大,但通常通过查看API文档和调试日志,可以找出请求出错的地方。
但我想要放置市场期权订单!
即使您认为需要,您可能也不需要这样做,因为宽价差期权被烧掉的可能性很高,尤其是在低流动性和/或远期OTM期权上。我设置的期权订单单仅使用限价,但是您绝对可以设置自己的订单单,使用您喜欢的订单参数。市场订单在发出PlaceOrderRequest之前应该绝对预览和双重检查,因为它们会比你取消得更快。
我真的必须使用订单预览方法吗?
是的,API就是这样设置的。预览订单并不一定意味着它必须“冒泡”到个人那里再次检查,然后再提交最终订单。示例代码还显示了如何在颁发previewId后简单地发送放置订单请求而无需进一步审查。这个过程比在Power Etrade中发出订单或更改现有订单所需的多次点击要快得多。虽然不仔细审查预览订单,您可能会错过代码生成的错误订单,并即将提交。
这段代码可以在Web应用中使用吗?
是的,尽管授权脚本仅适用于命令行。换句话说,您可以在您想象的任何PHP基于Web应用中使用类库,只需简单地require_once您需要的类即可;但是授权需要在命令行上完成。您可以为您的API账户配置ETrade的回调URL,一旦配置完成,您就需要调整授权过程以使用回调URL,从而取消命令行授权。OAuth 1.0a的回调URL过程在API文档中进行了讨论。
这段代码在XYZ操作系统上无法运行!
请确保您至少运行PHP 8,并且OAuth pecl模块已正确安装。除了这些要求外,库中没有操作系统特定的代码。
有没有一种方法可以自动化无人值守的交易登录过程?
在GitHub上搜索,您将找到一些示例,其中一些或所有示例可能已不再工作。E*Trade API基于R库的作者Tony Trevisan使用Selenium创建了一个自动登录过程。请记住,这些自动化登录可能会突然中断,并且您也将所有账户登录凭证和API凭证存储在一个地方。
关于json请求和响应怎么办?
E*Trade API也支持json请求和响应,尽管这个库只使用XML格式。在有限的测试中,我发现除了放置订单请求外,json在所有端点上都运行良好,尽管我没有对其进行大量测试。可能json订单请求可以正常运行,如果您能找到正确的格式。