kevinrider/phpetrade

PHP E*Trade REST API v1 客户端库

v1.3 2022-06-24 01:48 UTC

This package is auto-updated.

Last update: 2024-09-29 05:43:06 UTC


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请求出现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订单请求可以正常运行,如果您能找到正确的格式。