tag:blogger.com,1999:blog-65685845064742353212024-02-07T06:52:47.585-05:00Options Trading Robot DevelopmentSystem Traderhttp://www.blogger.com/profile/11376312889397858168noreply@blogger.comBlogger31125tag:blogger.com,1999:blog-6568584506474235321.post-35760092889114350632010-09-07T22:20:00.002-04:002010-09-07T22:24:35.500-04:00Project Status: HoldI've been contacted by several people on the status of this project. Here's my response to the inquiry.<br /><br /><br /><span style="font-style: italic; color: rgb(51, 51, 153);">No, I'm not working on the project at this time. While testing the api, I came across too many unexpected/undocumented behavior in the API and weird data coming through IB's system. I think in order to have a fully functional </span><span style="border-bottom: 2px dotted rgb(54, 99, 136); cursor: pointer; font-style: italic; color: rgb(51, 51, 153);" class="yshortcuts" id="lw_1283912418_0">trading system</span><span style="font-style: italic; color: rgb(51, 51, 153);">, I would have to spend quite a bit of time coming up with logic to correct and verify data that is coming through the API to make sure I'm not making trading decisions based on false data. And since this is the type of thing that you can't fully anticipate, I think this is a major problem to tackle (never mind the fact that IB is constantly updating their system, and there's no guarantee that the API won't be affected). I wanted to spend more time on defining/revising my trading rules, not on filtering and working around bad data coming through the system.</span><br /><br /><span style="font-style: italic; color: rgb(51, 51, 153);">On auto trading, one other issue to consider is how the system will perform during market gyrations like the flash crash we had a few months ago. I noticed that bid-ask spread were huge, and I wasn't getting filled on any orders. So that's a consideration.</span><br /><br /><span style="font-style: italic; color: rgb(51, 51, 153);">Maybe I'll pick up the project again if IB's API platform gets a little better, but for now, I have other things to concentrate on.</span><br /><span style="font-style: italic; color: rgb(0, 0, 0);"><br /><br /> .. regarding the details of the problems/learning experience ..</span><br /><br /><br /><span style="font-style: italic; color: rgb(51, 51, 153);">I don't remember exact details, but there were issues of getting order status and how to properly cancel them if they didn't get filled in reasonable amt of time. There were also issues where if IB exits, and reconnect is made, it was hard to determine which orders were submitted/ pending / closed. Of course this is not an issue if you just submit one order at a time.</span>System Traderhttp://www.blogger.com/profile/11376312889397858168noreply@blogger.com3tag:blogger.com,1999:blog-6568584506474235321.post-9439732857861034362009-08-19T08:15:00.003-04:002009-08-19T08:21:33.847-04:00Ruby - Rails - PostgreSQL IssuesI encountered a couple of issues with the development stack. Luckily, I was able to solve them since they were documented on the web. By way of review, my development environment is as follows:<br /> Operating System: Windows XP<br /> Ruby: 1.8.6<br /> Rails: 2.3.3<br /> PostgreSQL: 8.4.0<br /><br /><span style="font-weight: bold;">A)</span> The first error occured when starting WEBrick server. A pop up message displayed:<br /> <span style="font-weight: bold; color: rgb(255, 0, 0);">ruby.exe Ordinal Not Found</span><br /><span style="font-weight: bold; color: rgb(255, 0, 0);"> The ordinal 284 could not be located in the dynamic link library SSLEAY32.DLL</span><br /><br />Searching the web led me to <a href="http://stackoverflow.com/questions/377724/284-could-not-be-located-in-the-dynamic-link-library-ssleay32-dll">this post at Stackoverflow</a>. My solution was to:<br /><br />1) Rename libeay32.dll and ssleay32.dll in both ruby\bin folder and posgresql\lib folder to old_libeay32.dll and old_ssleay32.dll respectively. This way I don't lose these dlls in case my fix doesn't work.<br /><br />2) Install Visual C++ 2008 Redistributables which can be obtained from <a href="http://www.microsoft.com/downloads/thankyou.aspx?familyId=9b2da534-3e03-4391-8a4d-074b9f2bc1bf&displayLang=en">Microsoft site</a>. This is needed prior to installing OpenSSL below.<br /><br />3) Install the latest OpenSSL from <a href="http://www.slproweb.com/products/Win32OpenSSL.html">Shining Light Productions</a>. I used "Win32 OpenSSL v0.9.8k Light".<br /><br />4) Copy libeay32.dll and ssleay32.dll from OpenSSL folder to h ruby\bin and posgresql\lib folder.<br /><br />5) Restart WEBrick server and the error should be gone.<br /><br /><br /><span style="font-weight: bold;">B)</span> The second error occured in the browser when Rails tried to connect to PostgreSQL. I tried to migrate a model using rake db:migrate. An error displayed:<br /><span style="font-weight: bold; color: rgb(255, 0, 0);">Rake aborted!</span><br /><span style="font-weight: bold; color: rgb(255, 0, 0);">undefined method 'quote_indent' for PGconn:class</span><br /><br />Searching the web led me to <a href="http://www.highdots.com/forums/ruby-rails-talk/quote_ident-283323.html">this post at HighDot Forums</a>. My solution was to:<br /><br />1) Open Rails file config/initializers/new_rails_defaults.rb<br /><br />2) Add the following code at the end of the file:<br /><br /><code><br /># Postgres "quote_indent for PGconn:class" fix from http://www.highdots.com/forums/ruby-rails-talk/quote_ident-283323.html<br />def PGconn.quote_ident(name)<br />%("#{name}")<br />end<br /></code><br /><br />After that, I did not experience any other issues with the development environment.System Traderhttp://www.blogger.com/profile/11376312889397858168noreply@blogger.com4tag:blogger.com,1999:blog-6568584506474235321.post-82920679261926576342009-08-16T21:03:00.004-04:002009-08-16T21:18:03.652-04:00Ruby & Ruby On Rails Installation Complete<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZqg1A8wY0c4vzHIVbjipii-hhL04jdIUfag43qRUToOR5WEPdq-f_-gpvllnosSM99P3zRnsnC0o1rZQwG8tTAvKfzOxwxMpHmyU3AWrjnwifGyHeB_LwnOcMlqTVHkwjwqYVVu69Ruw3/s1600-h/ruby-logo-R.png"><img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 163px; height: 200px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZqg1A8wY0c4vzHIVbjipii-hhL04jdIUfag43qRUToOR5WEPdq-f_-gpvllnosSM99P3zRnsnC0o1rZQwG8tTAvKfzOxwxMpHmyU3AWrjnwifGyHeB_LwnOcMlqTVHkwjwqYVVu69Ruw3/s200/ruby-logo-R.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5370734962629582834" /></a><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbJQoZMEyHZggTSJfrKDOaoXK6h__BIt1Vg_RM3g0759C-TJ3cRNkRoKoJQ9Bp5PhMbMAECGOzV-C3AeSEz21cbOhDybX-5wLoTnQfNXTbM6V5FG6dIpok1hvfddICZ43idiagH_S1voEE/s1600-h/Ruby_on_Rails_logo.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 168px; height: 200px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbJQoZMEyHZggTSJfrKDOaoXK6h__BIt1Vg_RM3g0759C-TJ3cRNkRoKoJQ9Bp5PhMbMAECGOzV-C3AeSEz21cbOhDybX-5wLoTnQfNXTbM6V5FG6dIpok1hvfddICZ43idiagH_S1voEE/s200/Ruby_on_Rails_logo.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5370735616851666418" /></a><br />While there are numerous write ups on this process (just Google "install ruby on rails"), I thought I'd include a brief description for blog completeness<br /><br />1) Go to <a href="http://rubyonrails.org/download">Ruby on Rails download page</a> to Download and install Ruby. Enable RubyGems option. I used Windows 1.8.6-27 Release Candidate 2. <br /><br />2) When finish, open a command prompt and install rails 2.3.3 :<br /><code> >gem install rails </code><br /><br />3) Install PostgrSQL driver:<br /><code> >gem install postgres-pr </code><br /><br />4) Create sample Rails application:<br /><code> >rails c:\temp\railstest </code><br /><br />That's all there is to it. For a quick tutorial, I suggest going to http://guides.rubyonrails.org/ .System Traderhttp://www.blogger.com/profile/11376312889397858168noreply@blogger.com1tag:blogger.com,1999:blog-6568584506474235321.post-48353327112350406012009-08-11T11:08:00.018-04:002009-08-11T20:28:39.646-04:00PostgreSQL 8.4.0 Setup Complete<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqXl_98lf34G_jFJNVgxa9kBAAmEGxQlzl7SjRex0dkJEa7tNsHAGhxNJ4SCEZJijcE1NvWppVcJBd6AgUUuxvZY8fAfSjW6ExpX4GMkPfp5FmElvGEQuHub7qGjtnLSTze1FkkPBEODWC/s1600-h/PG95x51_4.gif"><img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 95px; height: 51px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqXl_98lf34G_jFJNVgxa9kBAAmEGxQlzl7SjRex0dkJEa7tNsHAGhxNJ4SCEZJijcE1NvWppVcJBd6AgUUuxvZY8fAfSjW6ExpX4GMkPfp5FmElvGEQuHub7qGjtnLSTze1FkkPBEODWC/s320/PG95x51_4.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5368790447636599714" /></a><br /><br /><br />The Installation process was very straightforward and provided no surprises. Simply go to www.postgreSQL.org and download release 8.4. Run the installer and it should be very easy. A windows service called "PostgreSQL Server 8.4" will be created and can be verified by checking Control Panel, Administrative Tools, Services. If it does not start automatically, simply right click on it and select Start.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqs8UEpFPskV022LXSqO76smbiexOGC3277bPt78GfpXwJlpDvjCqMyhBTTenxypb9I_S8dZWRK1KsWJDuQgFfVbPHVc_OMUR814Hzq1DKtqlBCn439HN2nbAxTSkTG-HddVV9lRYOnTWI/s1600-h/pgService.JPG"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 128px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqs8UEpFPskV022LXSqO76smbiexOGC3277bPt78GfpXwJlpDvjCqMyhBTTenxypb9I_S8dZWRK1KsWJDuQgFfVbPHVc_OMUR814Hzq1DKtqlBCn439HN2nbAxTSkTG-HddVV9lRYOnTWI/s320/pgService.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5368788274833034258" /></a><br /><br />Once the database is running, connect to it and create a 'tradingbot' database.<br /><br /><pre class="sql" name="code"><br />C:\temp>psql -U postgres<br />Password for user postgres:<br />psql (8.4.0)<br />WARNING: Console code page (437) differs from Windows code page (1252)<br /> 8-bit characters might not work correctly. See psql reference<br /> page "Notes for Windows users" for details.<br />Type "help" for help.<br /><br />postgres=# create database tradebot;<br />CREATE DATABASE<br />postgres=# \l<br /> List of databases<br /> Name | Owner | Encoding | Collation | Ctype<br /> | Access privileges<br />-----------+----------+----------+----------------------------+-----------------<br />-----------+-----------------------<br /> postgres | postgres | UTF8 | English_United States.1252 | English_United States.1252 |<br /> template0 | postgres | UTF8 | English_United States.1252 | English_United States.1252 | =c/postgres<br /><br /> : postgres=CTc/postgres<br /> template1 | postgres | UTF8 | English_United States.1252 | English_United States.1252 | =c/postgres<br /><br /> : postgres=CTc/postgres<br /> tradebot | postgres | UTF8 | English_United States.1252 | English_United States.1252 |<br />(4 rows)<br /><br />postgres=# create user master with password 'blaster';<br />CREATE ROLE<br />postgres=# grant all on database tradebot to master;<br />GRANT<br />postgres=# \q<br /></pre><br />Line 1: Connect to default database<br />Line 9: Create database "tradebot"<br />Line 27: Create new user called "master"<br />Line 29: Grant privileges to "master"<br /><br />Next step is to log into the new database and create a test table.<br /><br /><pre class="sql" name="code"><br />C:\temp>psql -d tradebot -U master<br />Password for user master:<br />psql (8.4.0)<br />WARNING: Console code page (437) differs from Windows code page (1252)<br /> 8-bit characters might not work correctly. See psql reference<br /> page "Notes for Windows users" for details.<br />Type "help" for help.<br /><br />tradebot=>\d<br />No relations found.<br />tradebot=> create table tester (id varchar(10) not null);<br />CREATE TABLE<br />tradebot=> \d<br /> List of relations<br /> Schema | Name | Type | Owner<br />--------+--------+-------+--------<br /> public | tester | table | master<br />(1 row)<br /><br /><br />tradebot=> \d tester<br /> Table "public.tester"<br /> Column | Type | Modifiers<br />--------+-----------------------+-----------<br /> id | character varying(10) | not null<br /><br /><br />tradebot=><br /></pre><br />Line 1: Connect to tradebot database<br />Line 9: Get listing of all tables in the database. None returned because database is empty.<br />Line 11: Create "tester" table<br />Line 13: Get listing of all tables in the database. "Tester" table appears.<br />Line 21: Get "tester" table informationSystem Traderhttp://www.blogger.com/profile/11376312889397858168noreply@blogger.com0tag:blogger.com,1999:blog-6568584506474235321.post-90061816740564650342009-08-08T21:58:00.007-04:002009-08-08T22:15:30.699-04:00Specifications: WatcherThe Watcher Application specifications document is now complete. I have published it using Google docs rather than posting in blog format because it will be easier to make changes over time. As with all software specifications, this is a living document and will be updated often as needed. I will keep a revision history at the bottom of the document to track any major changes.<br /><br /><br /><center><br /><iframe src="http://docs.google.com/View?id=dd9t5fdm_1dm9xfbfd" frameborder="1" height="800" width="620"></iframe></center><br /><br /><br />System Traderhttp://www.blogger.com/profile/11376312889397858168noreply@blogger.com0tag:blogger.com,1999:blog-6568584506474235321.post-10111990906903187262009-07-23T14:25:00.013-04:002009-07-23T17:53:51.880-04:00Proof Of Concept Recap and Game PlanWith Proof Of Concept postings completed, I now have a better understanding of what it will take to develop my trading robot. I have demonstrated that it’s possible to perform the following:<br /><br /><br /> - Use JRuby to interface with TWS API<br /><br /> - Get market quote<br /><br /> - Get historical quote<br /><br /> - Check account information (balance and open position info)<br /><br /> - Check order status<br /><br /> - Submit stock order (market and limit orders)<br /><br /> - Submit options order<br /><br /> - Submit complex options order<br /><br /> <br /><br /><br />Along the way, I have also identified some limitations and glitches within TWS API and JRuby. Most of these can be overcome, but they make development harder. Here are just a few:<br /><br /><br /> - JRuby error messages are not that readable and it can be difficult to determine where the error is coming from. So during development, it’s important to test early and often before too much coding is done.<br /><br /> - TWS API or JRuby upgrades may cause the application to break. Any decision to perform an upgrade should not be taken lightly and complete regression testing will be needed.<br /><br /> - If TWS pops up a dialog window, any API calls will be interrupted until the window is closed. The finished product will need a way to detect or prevent this from happening.<br /><br /> - openOrder() callback method sometimes get triggered more than once for unknown reasons. <br /><br /> - Retrieved data (such as market price) from TWS isn’t perfect. So it’s a good idea to build in some sort of redundant requests to make sure no trading decision is made because of bad pricing data.<br /><br /> - There is a need to be able to recover in the case where order is submitted and TWS crashes. Part of the recovery process would be to detect what orders have been submitted, and executed or not.<br /><br /><br />It is my intent to develop the program in three phases, with the actual trading robot phase being the last, and most difficult to create. The first phase of development will be a watch list of my investment portfolio that will issue buy/sell signals according to my plan. There is no trading automation in this phase, as these investments not in my Interactive Brokers account. The second phase will be the backbone of the Trading Robot. It will keep track of which positions I have in my Interactive Brokers account and provide a user interface to manage data. The following is a more detailed description of each phase:<br /><br /> Phase 1) Watcher - Ruby on Rails application which allows me to watch my investment portfolio (composed of various ETFs and mutual funds). It will get security prices at regular time intervals or on demand and will issue buy/sell signals according to my rules (mostly based on moving averages). Data will be stored in a database and pricing information will be gathered from various sources on the web. It will provide a web interface from which to edit watch list data.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBWC_8cmqTaNN_e2UZFjtzbE1yqI_o4s49Hlzq7Ay7qY8Y9oNA3MIuU3DONd_xoyhwJTNpv4VXp_Y69eS2Jx6n0To467iAABiJJRF8rCaXp_8JsAHw6XezR9YobfP9vyWmEpBSyGyAudbp/s1600-h/phase1.gif"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 202px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBWC_8cmqTaNN_e2UZFjtzbE1yqI_o4s49Hlzq7Ay7qY8Y9oNA3MIuU3DONd_xoyhwJTNpv4VXp_Y69eS2Jx6n0To467iAABiJJRF8rCaXp_8JsAHw6XezR9YobfP9vyWmEpBSyGyAudbp/s320/phase1.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5361724359889735154" /></a><br /><br /><br /> Phase 2) Account Monitor - Extension of the above Rails application. It will store open positions and trading rules in the database. It will provide a web interface to control application parameters and display activities performed by Trading Robot.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpANaeIoS0pH6yRl0XpkpCrVr6HOTYDFACwFmkx64hVd5ibC5YEiMVAhh3O-M00L2lz7FZnEOuO8QuI_eZPLl1yRU1tP-o_eNKs8C1vJVnkcj0kBCvzfIglTnC88QDGwm4fMVFbmZLRvoX/s1600-h/phase2.gif"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 202px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpANaeIoS0pH6yRl0XpkpCrVr6HOTYDFACwFmkx64hVd5ibC5YEiMVAhh3O-M00L2lz7FZnEOuO8QuI_eZPLl1yRU1tP-o_eNKs8C1vJVnkcj0kBCvzfIglTnC88QDGwm4fMVFbmZLRvoX/s320/phase2.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5361724642632968754" /></a><br /><br /><br /> Phase 3) Trading Robot - Separate JRuby application with connection to TWS and database. It will populate the database with data from TWS, apply trading rules, and perform trades when needed. It will run in a batch mode and not have a user interface. All of the Trading Robot's activities will be monitored through the Account Monitor.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJU3fzlfpPpbZ2kL7w3y_vgJYRDQT8W4JOZekUvi9iaPvgZzmN2LQYha9U8-l-r28ku6jAMZwL3N7jlvKoP0Mw8YPLsv5tUiJ159T9iX1r6XCo3wAqXEdfthSkfuVEwnm3Urkk7A8_G8vu/s1600-h/phase3.gif"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 310px; height: 320px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJU3fzlfpPpbZ2kL7w3y_vgJYRDQT8W4JOZekUvi9iaPvgZzmN2LQYha9U8-l-r28ku6jAMZwL3N7jlvKoP0Mw8YPLsv5tUiJ159T9iX1r6XCo3wAqXEdfthSkfuVEwnm3Urkk7A8_G8vu/s320/phase3.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5361724793849951554" /></a>System Traderhttp://www.blogger.com/profile/11376312889397858168noreply@blogger.com0tag:blogger.com,1999:blog-6568584506474235321.post-84016806070439459322009-07-15T10:38:00.025-04:002009-07-15T23:25:12.764-04:00Proof Of Concept 8 of 8g: Complex Options OrderComplex (or combination) option orders are orders composed of more than one leg for the same underlying security. They are submitted and executed as a single transaction and price. TWS complex orders allow for a maximum of up to four legs. Examples of different options strategies and corresponding legs are:<br /><br /><table border="1"><tr><td> # of Legs </td><td> Strategies </td></tr><tr><td> 2</td><td> vertical, diagonal, calendar, straddle, strangle </td></tr><tr><td> 3</td><td> butterfly </td></tr><tr><td> 4</td><td> iron condor, iron butterfly, double diagonal </td></tr></table><br /><br />In this coding example, I will create a market order to buy a vertical call spread which is composed of:<br /> BUY 1 SPY AUG 50 CALL<br /> SELL 1 SPY AUG 55 CALL<br /> <br />The steps of creating a complex order are:<br /> 1) Use EClientSocket.reqContractDetails() method to get contract id for each option leg.<br /> 2) Create a ComboLeg object for each option leg using contract id from step 1).<br /> 3) Create a Contract object and add ComboLeg objects created from step 2). <br /> 4) Create an Order object and submit it using Contract object from step 3).<br /><br /><br /><pre class="ruby" name="code"><br /># To change this template, choose Tools | Templates<br /># and open the template in the editor.<br />require 'java'<br />require 'TwsApi.jar'<br /><br />include_class 'ib.client.EClientSocket'<br />include_class 'ib.client.Contract'<br />include_class 'ib.client.Order'<br />include_class 'ib.client.AnyWrapper'<br />include_class 'ib.client.EWrapper'<br />include_class 'ib.client.ComboLeg'<br /><br />class TestComboOptionsOrder<br /> include EWrapper #note "class TestMktData < EWrapper" syntax is no longer supported in JRuby 1.0<br /><br /> attr_accessor :order_id, :contractArray<br /> @mySocket<br /><br /><br /> def initialize<br /> puts 'init'<br /> @mySocket = EClientSocket.new(self)<br /> @contractArray = Array.new # array of hash<br /> end<br /><br /> def eConnect<br /> @mySocket.eConnect("127.0.0.1", 7496, 0)<br /> end<br /><br /> # Request all open orders that were placed from all API clients linked to one TWS,<br /> # and also from the TWS<br /> # This is a one time shot call. When its done you DO NOT continue to receive<br /> # information about new placed orders<br /> def reqAllOpenOrders<br /> @mySocket.reqAllOpenOrders<br /> end<br /><br /> def submitOptionsOrder(order_id, symbol, call_put, expiry, strike,<br /> buy_sell, qty, order_type, limit_price, tif)<br /> # --------------------------------<br /> myContract = Contract.new<br /> myContract.m_symbol = symbol<br /> myContract.m_secType = 'OPT'<br /> myContract.m_exchange = 'SMART'<br /> myContract.m_currency = 'USD'<br /> # options specific below ---<br /> myContract.m_expiry = expiry # string format YYYYMM<br /> myContract.m_right = call_put # CALL, PUT<br /> myContract.m_strike = strike # number<br /><br /> myOrder = Order.new<br /> myOrder.m_action = buy_sell # BUY, SELL<br /> myOrder.m_totalQuantity = qty<br /> myOrder.m_orderType = order_type # MKT, LMT, STP, SPLMT, TRAIL, etc<br /> myOrder.m_lmtPrice = limit_price<br /> myOrder.m_tif = tif # DAY, GTC<br /> @mySocket.placeOrder(order_id, myContract, myOrder)<br /> end<br /><br /> # new method to submit order<br /> def submitOrder(order_id, myContract, myOrder)<br /> @mySocket.placeOrder(order_id, myContract, myOrder)<br /> end<br /><br /> def cancelOrder(order_id)<br /> @mySocket.cancelOrder(order_id)<br /> end<br /><br /> # contract details will be received via the contractDetails() function on the EWrapper<br /> def reqContractDetails (req_id, contract)<br /> @mySocket.reqContractDetails(req_id, contract)<br /> end<br /><br /> def eDisconnect<br /> @mySocket.eDisconnect<br /> end<br /><br /><br /> def contract_msg(contract)<br /> puts ' contract_msg:'<br /> puts ' conid = ' + contract.m_conId.to_s<br /> puts ' symbol = ' + contract.m_symbol<br /> puts ' secType = ' + contract.m_secType<br /> puts ' exchange = ' + contract.m_exchange.to_s<br /> puts ' currency = ' + contract.m_currency<br /> puts ' localSymbol = ' + contract.m_localSymbol.to_s<br /> puts ' multiplier = ' + contract.m_multiplier.to_s<br /> puts ' expiry = ' + contract.m_expiry.to_s<br /> puts ' strike = ' + contract.m_strike.to_s<br /> puts ' right = ' + contract.m_right.to_s<br /> end<br /><br /> def contract_details_msg(contract_detail)<br /> puts ' contract_details_msg:'<br /> puts ' marketName = ' + contract_detail.m_marketName.to_s<br /> puts ' tradingClass = ' + contract_detail.m_tradingClass.to_s<br /> puts ' minTick = ' + contract_detail.m_minTick.to_s<br /> puts ' price magnifier = ' + contract_detail.m_priceMagnifier.to_s<br /> puts ' orderTypes = ' + contract_detail.m_orderTypes.to_s<br /> puts ' validExchanges = ' + contract_detail.m_validExchanges.to_s<br /> puts ' underConId = ' + contract_detail.m_underConId.to_s<br /> end<br /><br /> def order_msg(order)<br /> puts ' order_msg:'<br /> puts ' order id =' + order.m_orderId.to_s<br /> puts ' client id =' + order.m_clientId.to_s<br /> puts ' action =' + order.m_action.to_s<br /> puts ' quantity =' + order.m_totalQuantity.to_s<br /> puts ' type =' + order.m_orderType.to_s<br /> puts ' lmtPrice =' + order.m_lmtPrice.to_s<br /> puts ' TIF =' + order.m_tif.to_s<br /> puts ' parent Id =' + order.m_parentId.to_s<br /> puts ' permId =' + order.m_permId.to_s<br /><br /><br /> end<br /><br /> def order_state_msg(order_state)<br /> puts ' order_state_msg:'<br /> puts ' status =' + order_state.m_status.to_s<br /> puts ' initMargin =' + order_state.m_initMargin.to_s<br /> puts ' maintMargin =' + order_state.m_maintMargin.to_s<br /> puts ' commission =' + order_state.m_commission.to_s<br /><br /> end<br /><br /> #///////////////////////////////////////////////////////////////////////<br /> #// Interface methods<br /> #///////////////////////////////////////////////////////////////////////<br /><br /> def tickPrice( ticker_id, field, price, can_auto_execute)<br /> end<br /><br /> def tickSize( ticker_id, field, size)<br /> end<br /><br /> def tickOptionComputation( ticker_id, field, implied_vol,<br /> delta, model_price, pv_dividend)<br /> end<br /><br /> def tickGeneric( ticker_id, tick_type, value)<br /> end<br /><br /> def tickString( ticker_id, tick_type, value)<br /> end<br /><br /> def tickEFP( ticker_id, tick_type, basis_points,<br /> formatted_basis_points, implied_future, hold_days,<br /> future_expiry, dividend_impact, dividends_to_expiry)<br /> end<br /><br /> def orderStatus( order_id, status, filled, remaining,<br /> avg_fill_price, perm_id, parent_id, last_fill_price,<br /> client_id, why_held)<br /> puts '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'<br /> puts 'orderStatus>: orderId=' + order_id.to_s + ', clientId=' + client_id.to_s + ', permId=' + perm_id.to_s +<br /> ', status=' + status + ' filled=' + filled.to_s + ', remaining=' + remaining.to_s + ', '<br /> puts ' avgFillPrice=' + avg_fill_price.to_s + ', lastFillPrice=' + last_fill_price.to_s +<br /> ', parent Id=' + parent_id.to_s + ', whyHeld=' + why_held.to_s<br /> end<br /><br /> def openOrder( order_id, contract, order, order_state)<br /> puts '--------------------------------------------------------'<br /> puts 'openOrder> open order: orderId=' + order_id.to_s + ' --- '<br /> contract_msg(contract)<br /> order_msg(order)<br /> order_state_msg(order_state)<br /> end<br /><br /> def updateAccountValue( key, value, currency, account_name)<br /> end<br /><br /> def updatePortfolio( contract, position, market_price, market_value,<br /> average_cost, unrealized_pnl, realized_pnl, account_name)<br /> end<br /><br /> def updateAccountTime( time_stamp)<br /> end<br /><br /> # triggered right after logging into TWS<br /> def nextValidId( order_id)<br /> @order_id = order_id #storing order id to be used when placing order<br /> puts 'nextValidId> Next Valid Order Id = ' + @order_id.to_s<br /> end<br /><br /> # call back with details. determine which request it was and store details<br /> # data with the associated request.<br /> def contractDetails( req_id, contract_details)<br /> aContract = contract_details.m_summary<br /> # find the hash with matching :req_id and set :conid<br /> @contractArray.find{ |contract| contract[:req_id] == req_id } [:conid] = aContract.m_conId<br /><br /> puts 'contractDetails()> reqId = ' + req_id.to_s + ' -------------------'<br /> contract_msg(aContract)<br /> contract_details_msg(contract_details)<br /> puts ' --------'<br /> end<br /><br /> def bondContractDetails( req_id, contract_details)<br /> end<br /><br /> def contractDetailsEnd( req_id)<br /> puts 'contractDetailsEnd() ' + req_id.to_s + '> Ended ------------------------'<br /> end<br /><br /> def execDetails( order_id, contract, execution)<br /> end<br /><br /> def updateMktDepth( ticker_id, position, operation, side, price, size)<br /> end<br /><br /> def updateMktDepthL2( ticker_id, position, market_maker, operation,<br /> side, price, size)<br /> end<br /><br /> def updateNewsBulletin( msg_id, msgType, message, orig_exchange)<br /> end<br /><br /> def managedAccounts( accounts_list)<br /> end<br /><br /> def receiveFA( fa_data_type, xml)<br /> end<br /><br /> def historicalData( req_id, date, open, high, low,<br /> close, volume, count, eWAP, has_gaps)<br /> end<br /><br /> def scannerParameters( xml)<br /> end<br /><br /> def scannerData( req_id, rank, contract_details, distance,<br /> benchmark, projection, legs_str)<br /> end<br /><br /> def scannerDataEnd( req_id)<br /> end<br /><br /> def realtimeBar( req_id, time, open, high, low, close, volume, wap, count)<br /> end<br /><br /> def currentTime( time)<br /> end<br /><br /> def fundamentalData( req_id, data)<br /> end<br /><br /> # ---------------------------------------------------<br /> # new callback methods added in API 9.60<br /> # ---------------------------------------------------<br /> def openOrderEnd() # invoked when all orders are sent to a client as a response to reqOpenOrders(), <br /> # reqAllOpenOrders and right after connected to TWS<br /> puts 'openOrderEnd> Ended ------------------------'<br /> end<br /><br /> def accountDownloadEnd(account_name) #invoked when a complete snapshot of an account state is sent to a client as a response to reqAccountUpdates()<br /> end<br /><br /> def execDetailsEnd( req_id) # invoked when all executions are sent to a client as a response to reqExecutions()<br /> end<br /><br /> def deltaNeutralValidation(req_id, under_comp) # for Delta-Neutral DN RFQ<br /> end<br /><br /> # ---------------------------------------------------<br /> # -- methods from AnyWrapper -- must be declared<br /> # ---------------------------------------------------<br /><br /> def connectionClosed()<br /> puts ' [API.connectionClosed] Closed connection with TWS'<br /> end<br /><br /> def error(err_obj_str)<br /> puts ' [API.msg1] ' + err_obj_str if err_obj_str.is_a?(String)<br /> puts ' [API.msg3] ' + err_obj_str.getMessage() if err_obj_str.is_a?(Exception)<br /> end<br /><br /> def error(one, two, str)<br /> puts ' [API.msg2] ' + str + ' {' + one.to_s + ', ' + two.to_s + '}'<br /> end<br /><br />end # class<br /><br /># *********************************************************************************************<br /># Running the code<br /># *********************************************************************************************<br /> x = TestComboOptionsOrder.new()<br /> begin<br /> x.eConnect<br /> puts 'connected ...'<br /> sleep(2) # need this here to give time for nextValidId() to be called<br /> puts '******************************'<br /> puts '** STEP 1) ******************'<br /> puts '.. Defining contract for Aug 90 C '<br /> spyAug90c = Contract.new<br /> spyAug90c.m_symbol = 'SPY'<br /> spyAug90c.m_secType = 'OPT'<br /> spyAug90c.m_exchange = 'SMART'<br /> spyAug90c.m_currency = 'USD'<br /> spyAug90c.m_expiry = '20090821' # string format YYYYMM or YYYYMMDD -- if YYYYMM used and normal and quarterly<br /> # options exist, will return contract details for both<br /> spyAug90c.m_right = 'CALL' # CALL, PUT<br /> spyAug90c.m_strike = 90 # number<br /><br /> puts '.. Adding Aug 90 C to contract array'<br /> request_id = 1<br /> x.contractArray << {:req_id => request_id, :contract => spyAug90c, :conid => '' }<br /><br /> puts '.. Requesting contract detail and id info'<br /> x.reqContractDetails(request_id, spyAug90c)<br /> sleep(5);<br /> puts ' '<br /> puts '.. Defining contract for Aug 95 C '<br /> spyAug95c = Contract.new<br /> spyAug95c.m_symbol = 'SPY'<br /> spyAug95c.m_secType = 'OPT'<br /> spyAug95c.m_exchange = 'SMART'<br /> spyAug95c.m_currency = 'USD'<br /> spyAug95c.m_expiry = '20090821' # string format YYYYMM or YYYYMMDD -- if YYYYMM used and normal and quarterly<br /> # options exist, will return contract details for both<br /> spyAug95c.m_right = 'CALL' # CALL, PUT<br /> spyAug95c.m_strike = 95 # number<br /> puts '.. Adding Jul 95 C to contract array'<br /> request_id = 2<br /> x.contractArray << {:req_id => request_id, :contract => spyAug95c, :conid => '' }<br /> puts 'Requesting contract detail and id info'<br /> x.reqContractDetails(request_id, spyAug95c)<br /> sleep(5);<br /><br /> puts '******************************'<br /> puts '** STEP 2 ******************'<br /> puts '.. Now have info on both contracts, create combo leg for each'<br /> puts '.. Create leg #1'<br /> leg1 = ComboLeg.new(x.contractArray.find{ |id| id[:req_id] == 1 } [:conid], # contract id<br /> 1, # ratio<br /> 'BUY', # BUY or SELL<br /> 'SMART', # p_exchange<br /> 0 # p_openClose- always 0 for retail customers<br /> )<br /> puts '.. Create leg #2'<br /> leg2 = ComboLeg.new(x.contractArray.find{ |id| id[:req_id] == 2 } [:conid], # contract id<br /> 1, # ratio<br /> 'SELL', # BUY or SELL<br /> 'SMART', # p_exchange<br /> 0 # p_openClose- always 0 for retail customers<br /> )<br /> puts '******************************'<br /> puts '** STEP 3 ******************'<br /> puts '.. Create Contract'<br /><br /> myContract = Contract.new<br /> myContract.m_symbol = 'SPY'<br /> myContract.m_secType = 'BAG' # BAG is a combination type<br /> myContract.m_exchange = 'SMART'<br /> myContract.m_currency = 'USD'<br /><br /> puts '.. Add leg 1 to combo contract'<br /> myContract.m_comboLegs.add(leg1)<br /> puts '.. Add leg 2 to combo contract'<br /> myContract.m_comboLegs.add(leg2)<br /> puts '******************************'<br /> puts '** STEP 4 ******************'<br /> puts '.. Create combo Order'<br /> myOrder = Order.new<br /> myOrder.m_action = 'BUY' # BUY, SELL<br /> myOrder.m_totalQuantity = 1<br /> myOrder.m_orderType = 'MKT' # MKT, LMT, STP, SPLMT, TRAIL, etc<br /> myOrder.m_tif = 'DAY' # DAY, GTC<br /><br /> puts '.. submitting order'<br /> x.submitOrder(x.order_id += 1, myContract, myOrder)<br /> sleep(5);<br /> puts '==========================='<br /> puts '*** requesting open orders ***'<br /> x.reqAllOpenOrders<br /> sleep(5)<br /> x.eDisconnect<br /> puts 'disconnected'<br /> rescue Exception => e<br /> puts 'Exception ' + e.message<br /> x.eDisconnect<br /> end<br /><br /></pre><br /><br /><br /><br />Running the code produces the following output:<br /><br /><code><br />init<br />Server Version:44TWS Time at connection:20090712 16:51:47 ESTconnected ...<br />nextValidId> Next Valid Order Id = 81<br />openOrderEnd> Ended ------------------------<br /> [API.msg2] Market data farm connection is OK:usopt {-1, 2104}<br /> [API.msg2] Market data farm connection is OK:usfarm {-1, 2104}<br /><b>******************************<br />** STEP 1) ******************</b><br />.. Defining contract for Aug 90 C <br />.. Adding Aug 90 C to contract array<br />.. Requesting contract detail and id info<br />contractDetails()> reqId = 1 -------------------<br /> contract_msg:<br /> conid = 60388362<br /> symbol = SPY<br /> secType = OPT<br /> exchange = SMART<br /> currency = USD<br /> localSymbol = SWGHL<br /> multiplier = 100<br /> expiry = 20090821<br /> strike = 90.0<br /> right = C<br /> contract_details_msg:<br /> marketName = SWG<br /> tradingClass = SWG<br /> minTick = 0.01<br /> price magnifier = 1<br /> orderTypes = ADJUST,ALERT,ALGO,ALLOC,AON,AVGCOST,BASKET,COND,CONDORDER,DAY,DEACT,DEACTEOD,FOK,GAT,GTC,GTD,GTT,HID,HPENNY,ICE,IOC,LIT,LMT,MIT,MKT,MTL,NONALGO,OCA,PAON,RELSTK,SCALE,SCALERST,STP,STPLMT,TRAIL,TRAILLIT,TRAILLMT,TRAILMIT,VOLAT,WHATIF,<br /> validExchanges = SMART,AMEX,BOX,CBOE,IBSX,ISE,MIBSX,NASDAQOM,PHLX,PSE<br /> underConId = 756733<br /> --------<br />contractDetailsEnd() 1> Ended ------------------------<br /> <br />.. Defining contract for Aug 95 C <br />.. Adding Jul 95 C to contract array<br />Requesting contract detail and id info<br />contractDetails()> reqId = 2 -------------------<br /> contract_msg:<br /> conid = 60388382<br /> symbol = SPY<br /> secType = OPT<br /> exchange = SMART<br /> currency = USD<br /> localSymbol = SWGHQ<br /> multiplier = 100<br /> expiry = 20090821<br /> strike = 95.0<br /> right = C<br /> contract_details_msg:<br /> marketName = SWG<br /> tradingClass = SWG<br /> minTick = 0.01<br /> price magnifier = 1<br /> orderTypes = ADJUST,ALERT,ALGO,ALLOC,AON,AVGCOST,BASKET,COND,CONDORDER,DAY,DEACT,DEACTEOD,FOK,GAT,GTC,GTD,GTT,HID,HPENNY,ICE,IOC,LIT,LMT,MIT,MKT,MTL,NONALGO,OCA,PAON,RELSTK,SCALE,SCALERST,STP,STPLMT,TRAIL,TRAILLIT,TRAILLMT,TRAILMIT,VOLAT,WHATIF,<br /> validExchanges = SMART,AMEX,BOX,CBOE,IBSX,ISE,MIBSX,NASDAQOM,PHLX,PSE<br /> underConId = 756733<br /> --------<br />contractDetailsEnd() 2> Ended ------------------------<br /><b>******************************<br />** STEP 2 ******************</b><br />.. Now have info on both contracts, create combo leg for each<br />.. Create leg #1<br />.. Create leg #2<br /><b>******************************<br />** STEP 3 ******************</b><br />.. Create Contract<br />.. Add leg 1 to combo contract<br />.. Add leg 2 to combo contract<br /><b>******************************<br />** STEP 4 ******************</b><br />.. Create combo Order<br />.. submitting order<br /> [API.msg2] Order Message:<br />Warning: your order will not be placed at the exchange until 2009-07-13 09:30:00 US/Eastern {82, 399}<br />--------------------------------------------------------<br />openOrder> open order: orderId=82 --- <br /> contract_msg:<br /> conid = 28812380<br /> symbol = SPY<br /> secType = BAG<br /> exchange = SMART<br /> currency = USD<br /> localSymbol = SPY<br /> multiplier = <br /> expiry = <br /> strike = 0.0<br /> right = ?<br /> order_msg:<br /> order id =82<br /> client id =0<br /> action =BUY<br /> quantity =1<br /> type =MKT<br /> lmtPrice =0.0<br /> TIF =DAY<br /> parent Id =0<br /> permId =1009111242<br /> order_state_msg:<br /> status =PreSubmitted<br /> initMargin =<br /> maintMargin =<br /> commission =1.7976931348623157e+308<br />~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br />orderStatus>: orderId=82, clientId=0, permId=1009111242, status=PreSubmitted filled=0, remaining=1, <br /> avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=<br />===========================<br />*** requesting open orders ***<br />--------------------------------------------------------<br />openOrder> open order: orderId=82 --- <br /> contract_msg:<br /> conid = 28812380<br /> symbol = SPY<br /> secType = BAG<br /> exchange = SMART<br /> currency = USD<br /> localSymbol = SPY<br /> multiplier = <br /> expiry = <br /> strike = 0.0<br /> right = ?<br /> order_msg:<br /> order id =82<br /> client id =0<br /> action =BUY<br /> quantity =1<br /> type =MKT<br /> lmtPrice =0.0<br /> TIF =DAY<br /> parent Id =0<br /> permId =1009111242<br /> order_state_msg:<br /> status =PreSubmitted<br /> initMargin =<br /> maintMargin =<br /> commission =1.7976931348623157e+308<br />~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br />orderStatus>: orderId=82, clientId=0, permId=1009111242, status=PreSubmitted filled=0, remaining=1, <br /> avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=<br />openOrderEnd> Ended ------------------------<br />disconnected</code><br /><br />I ran the code after hours and was able to confirm that the order had been placed in TWS.<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOVoTT_g64hDFe7M59e1bb_YL1umrzaxwWDgqG_mh0ZTtWy1WLJlGPDXCQYKOtfESYw7UyQOCuH2Y6jn9YBl5Q1rNif6KXxW5QGjeXRgMrPi6h1wrvwmxjqIXv4iT0K42TYm3SiJDhmrzr/s1600-h/ib_complexOrder_1.JPG"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 189px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOVoTT_g64hDFe7M59e1bb_YL1umrzaxwWDgqG_mh0ZTtWy1WLJlGPDXCQYKOtfESYw7UyQOCuH2Y6jn9YBl5Q1rNif6KXxW5QGjeXRgMrPi6h1wrvwmxjqIXv4iT0K42TYm3SiJDhmrzr/s320/ib_complexOrder_1.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5358697621280114514" /></a><br /><br />To see the individual legs of the order, just right click on the order and select 'Show Legs'<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilDeIQhvWeqrWIaQimgj3zKCLL_io5rzIXURPUo3NZmwVi9yd6bhTlsI_gqppZOmJOm8yY7-wljgRu-PDQUQmRhUuxvFaOGii9X10q8RZKnB-69iQffRmvgwIL-OTdCTWY7X4QV5YnOUxU/s1600-h/ib_complexOrder_2.JPG"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 123px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilDeIQhvWeqrWIaQimgj3zKCLL_io5rzIXURPUo3NZmwVi9yd6bhTlsI_gqppZOmJOm8yY7-wljgRu-PDQUQmRhUuxvFaOGii9X10q8RZKnB-69iQffRmvgwIL-OTdCTWY7X4QV5YnOUxU/s320/ib_complexOrder_2.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5358698043860638754" /></a><br /><br />Individual legs will be displayed.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghjAjhXpIBe89q1odMqUaRmk4uXP8qzATflHvlbhAIprfvu0_ln2Nnw3CNEVDaudoWN2pgkiSmUhR4VC6Y318G2_mUyb3Mj-e_WCjHrqNiA29QBf5dBnPYbRE3a5PfwjpzMt893AuR30qO/s1600-h/ib_complexOrder_3.JPG"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 188px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghjAjhXpIBe89q1odMqUaRmk4uXP8qzATflHvlbhAIprfvu0_ln2Nnw3CNEVDaudoWN2pgkiSmUhR4VC6Y318G2_mUyb3Mj-e_WCjHrqNiA29QBf5dBnPYbRE3a5PfwjpzMt893AuR30qO/s320/ib_complexOrder_3.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5358704844425530370" /></a><br /><br />When the market opens the next day, my vertical call spread order executed at market price.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhybG8bWMYAiXRZdNPFMPyidP6fr6ktWPPSt4bWPbqjn8vCrxjdjxBSFlykJ4wbjVt0MtsI8IRqHJ7Iyq1Bk_mZpqmjFcrQbvMcIFP-6sw16z78j-ccx0PuG5vph2noMh3BtYm0MmUPi6Mv/s1600-h/ib_complexOrder_4.JPG"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 90px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhybG8bWMYAiXRZdNPFMPyidP6fr6ktWPPSt4bWPbqjn8vCrxjdjxBSFlykJ4wbjVt0MtsI8IRqHJ7Iyq1Bk_mZpqmjFcrQbvMcIFP-6sw16z78j-ccx0PuG5vph2noMh3BtYm0MmUPi6Mv/s320/ib_complexOrder_4.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5358705143479174242" /></a><br /><br />That's all there is to it. Complex option orders are not all that complex after all.System Traderhttp://www.blogger.com/profile/11376312889397858168noreply@blogger.com10tag:blogger.com,1999:blog-6568584506474235321.post-32357902143765438522009-07-12T15:44:00.002-04:002009-07-12T15:48:20.492-04:00Progress Update...I finally have time to get back to working on my coding. I got a complex options order working today (a bull call spread). It'll take me a few days to clean up and document the code though.<br /> <br />Stay tuned....System Traderhttp://www.blogger.com/profile/11376312889397858168noreply@blogger.com0tag:blogger.com,1999:blog-6568584506474235321.post-23758312473406775172009-04-01T16:53:00.003-04:002009-04-01T16:58:03.014-04:00Progress...Wow, I've been busy! Due to the fact that I've had family visits, a new computer to configure, an old computer to re-configure, and upcoming vacation, I will not be able to get to the next installment of the blog for another week or two. The next entry should be a good one: <span style="font-weight:bold;">Proof Of Concept 8 of 8g : Complex Options Order</span>.<br /><br />Stay tuned....System Traderhttp://www.blogger.com/profile/11376312889397858168noreply@blogger.com0tag:blogger.com,1999:blog-6568584506474235321.post-62739027927186587202009-03-18T17:14:00.015-04:002009-03-20T07:45:23.413-04:00Proof Of Concept 8 of 8f : Simple Options OrderSimple options orders are orders that involve just one type of option contract or one 'leg' (such as purchase or sale of a call or put option). The logic and sequence of events are the same for stock orders. The only difference is that option specific information is defined. <br /><br />The following code example submits a limit order to purchase an IWM SEP 38 CALL option and a market order to sell an existing RUT MAY 400 CALL option. When the code has finished running, the IWM option should be in submitted status and the RUT option should be in filled status.<br /><br /><pre class="ruby" name="code"><br />require 'java'<br />require 'TwsApi.jar'<br /><br />include_class 'ib.client.EClientSocket'<br />include_class 'ib.client.Contract'<br />include_class 'ib.client.Order'<br />include_class 'ib.client.AnyWrapper'<br />include_class 'ib.client.EWrapper'<br /><br />class TestOptionsOrder<br /> include EWrapper #note "class TestMktData < EWrapper" syntax is no longer supported in JRuby 1.0<br /><br /> attr_accessor :order_id<br /> @mySocket<br /><br /><br /> def initialize<br /> puts 'init'<br /> @mySocket = EClientSocket.new(self)<br /> end<br /><br /> def eConnect<br /> @mySocket.eConnect("127.0.0.1", 7496, 0)<br /> end<br /><br /> # Request all open orders that were placed from all API clients linked to one TWS,<br /> # and also from the TWS<br /> # This is a one time shot call. When its done you DO NOT continue to receive<br /> # information about new placed orders<br /> def reqAllOpenOrders<br /> @mySocket.reqAllOpenOrders<br /> end<br /><br /> def submitOptionsOrder(order_id, symbol, call_put, expiry, strike,<br /> buy_sell, qty, order_type, limit_price, tif)<br /> # --------------------------------<br /> myContract = Contract.new<br /> myContract.m_symbol = symbol<br /> myContract.m_secType = 'OPT'<br /> myContract.m_exchange = 'SMART'<br /> myContract.m_currency = 'USD'<br /> # options specific below ---<br /> myContract.m_expiry = expiry # string format YYYYMM<br /> myContract.m_right = call_put # CALL, PUT<br /> myContract.m_strike = strike # number<br /><br /> myOrder = Order.new<br /> myOrder.m_action = buy_sell # BUY, SELL<br /> myOrder.m_totalQuantity = qty<br /> myOrder.m_orderType = order_type # MKT, LMT, STP, SPLMT, TRAIL, etc<br /> myOrder.m_lmtPrice = limit_price<br /> myOrder.m_tif = tif # DAY, GTC<br /> @mySocket.placeOrder(order_id, myContract, myOrder)<br /> end<br /><br /> def cancelOrder(order_id)<br /> @mySocket.cancelOrder(order_id)<br /> end<br /><br /> # contract details will be received via the contractDetails() function on the EWrapper<br /> def reqContractDetails (req_id, contract)<br /> @mySocket.reqContractDetails(req_id, contract)<br /> end<br /><br /> def eDisconnect<br /> @mySocket.eDisconnect<br /> end<br /><br /><br /> def contract_msg(contract)<br /> puts ' contract_msg:'<br /> puts ' conid = ' + contract.m_conId.to_s<br /> puts ' symbol = ' + contract.m_symbol<br /> puts ' secType = ' + contract.m_secType<br /> puts ' exchange = ' + contract.m_exchange.to_s<br /> puts ' currency = ' + contract.m_currency<br /> puts ' localSymbol = ' + contract.m_localSymbol.to_s<br /> puts ' multiplier = ' + contract.m_multiplier.to_s<br /> puts ' expiry = ' + contract.m_expiry.to_s<br /> puts ' strike = ' + contract.m_strike.to_s<br /> puts ' right = ' + contract.m_right.to_s<br /> end<br /><br /> def order_msg(order)<br /> puts ' order_msg:'<br /> puts ' order id =' + order.m_orderId.to_s<br /> puts ' client id =' + order.m_clientId.to_s<br /> puts ' action =' + order.m_action.to_s<br /> puts ' quantity =' + order.m_totalQuantity.to_s<br /> puts ' type =' + order.m_orderType.to_s<br /> puts ' lmtPrice =' + order.m_lmtPrice.to_s<br /> puts ' TIF =' + order.m_tif.to_s<br /> puts ' parent Id =' + order.m_parentId.to_s<br /> puts ' permId =' + order.m_permId.to_s<br /> end<br /><br /> def order_state_msg(order_state)<br /> puts ' order_state_msg:'<br /> puts ' status =' + order_state.m_status.to_s<br /> puts ' initMargin =' + order_state.m_initMargin.to_s<br /> puts ' maintMargin =' + order_state.m_maintMargin.to_s<br /> puts ' commission =' + order_state.m_commission.to_s<br /><br /> end<br /><br /> #///////////////////////////////////////////////////////////////////////<br /> #// Interface methods<br /> #///////////////////////////////////////////////////////////////////////<br /><br /> def tickPrice( ticker_id, field, price, can_auto_execute)<br /> end<br /><br /> def tickSize( ticker_id, field, size)<br /> end<br /><br /> def tickOptionComputation( ticker_id, field, implied_vol,<br /> delta, model_price, pv_dividend)<br /> end<br /><br /> def tickGeneric( ticker_id, tick_type, value)<br /> end<br /><br /> def tickString( ticker_id, tick_type, value)<br /> end<br /><br /> def tickEFP( ticker_id, tick_type, basis_points,<br /> formatted_basis_points, implied_future, hold_days,<br /> future_expiry, dividend_impact, dividends_to_expiry)<br /> end<br /><br /> def orderStatus( order_id, status, filled, remaining,<br /> avg_fill_price, perm_id, parent_id, last_fill_price,<br /> client_id, why_held)<br /> puts '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'<br /> puts 'orderStatus>: orderId=' + order_id.to_s + ', clientId=' + client_id.to_s + ', permId=' + perm_id.to_s +<br /> ', status=' + status + ' filled=' + filled.to_s + ', remaining=' + remaining.to_s + ', '<br /> puts ' avgFillPrice=' + avg_fill_price.to_s + ', lastFillPrice=' + last_fill_price.to_s +<br /> ', parent Id=' + parent_id.to_s + ', whyHeld=' + why_held.to_s<br /> end<br /><br /> def openOrder( order_id, contract, order, order_state)<br /> puts '--------------------------------------------------------'<br /> puts 'openOrder> open order: orderId=' + order_id.to_s + ' --- '<br /> contract_msg(contract)<br /> order_msg(order)<br /> order_state_msg(order_state)<br /> end<br /><br /> def updateAccountValue( key, value, currency, account_name)<br /> end<br /><br /> def updatePortfolio( contract, position, market_price, market_value,<br /> average_cost, unrealized_pnl, realized_pnl, account_name)<br /> end<br /><br /> def updateAccountTime( time_stamp)<br /> end<br /><br /> # triggered right after logging into TWS<br /> def nextValidId( order_id)<br /> @order_id = order_id #storing order id to be used when placing order<br /> puts 'nextValidId> Next Valid Order Id = ' + @order_id.to_s<br /> end<br /><br /> def contractDetails( req_id, contract_details)<br /> end<br /><br /> def bondContractDetails( req_id, contract_details)<br /> end<br /><br /> def contractDetailsEnd( req_id)<br /> end<br /><br /> def execDetails( order_id, contract, execution)<br /> end<br /><br /> def updateMktDepth( ticker_id, position, operation, side, price, size)<br /> end<br /><br /> def updateMktDepthL2( ticker_id, position, market_maker, operation,<br /> side, price, size)<br /> end<br /><br /> def updateNewsBulletin( msg_id, msgType, message, orig_exchange)<br /> end<br /><br /> def managedAccounts( accounts_list)<br /> end<br /><br /> def receiveFA( fa_data_type, xml)<br /> end<br /><br /> def historicalData( req_id, date, open, high, low,<br /> close, volume, count, eWAP, has_gaps)<br /> end<br /><br /> def scannerParameters( xml)<br /> end<br /><br /> def scannerData( req_id, rank, contract_details, distance,<br /> benchmark, projection, legs_str)<br /> end<br /><br /> def scannerDataEnd( req_id)<br /> end<br /><br /> def realtimeBar( req_id, time, open, high, low, close, volume, wap, count)<br /> end<br /><br /> def currentTime( time)<br /> end<br /><br /> def fundamentalData( req_id, data)<br /> end<br /><br /> # ---------------------------------------------------<br /> # new callback methods added in API 9.60<br /> # ---------------------------------------------------<br /> def openOrderEnd() # invoked when all orders are sent to a client as a response to reqOpenOrders(), <br /> # reqAllOpenOrders and right after connected to TWS<br /> puts 'openOrderEnd> Ended ------------------------'<br /> end<br /><br /> def accountDownloadEnd(account_name) #invoked when a complete snapshot of an account state is sent to a client as a response to reqAccountUpdates()<br /> end<br /><br /> def execDetailsEnd( req_id) # invoked when all executions are sent to a client as a response to reqExecutions()<br /> end<br /><br /> def deltaNeutralValidation(req_id, under_comp) # for Delta-Neutral DN RFQ<br /> end<br /><br /> # ---------------------------------------------------<br /> # -- methods from AnyWrapper -- must be declared<br /> # ---------------------------------------------------<br /><br /> def connectionClosed()<br /> puts ' [API.connectionClosed] Closed connection with TWS'<br /> end<br /><br /> def error(err_obj_str)<br /> puts ' [API.msg1] ' + err_obj_str if err_obj_str.is_a?(String)<br /> puts ' [API.msg3] ' + err_obj_str.getMessage() if err_obj_str.is_a?(Exception)<br /> end<br /><br /> def error(one, two, str)<br /> puts ' [API.msg2] ' + str + ' {' + one.to_s + ', ' + two.to_s + '}'<br /> end<br /><br />end # class<br /><br /># *********************************************************************************************<br /># Running the code<br /># *********************************************************************************************<br /> x = TestOptionsOrder.new()<br /> begin<br /> x.eConnect<br /> puts 'connected ...'<br /> sleep(2) # need this here to give time for nextValidId() to be called<br /><br /> puts '*****************************'<br /> puts '**** LIMIT ORDER ****'<br /> o1 = x.order_id<br /> x.submitOptionsOrder(o1, 'IWM', 'CALL', '200909', 38,<br /> 'BUY', 2, 'LMT', 5, 'DAY')<br /> sleep(5)<br /> puts '*** requesting open orders ***'<br /> x.reqAllOpenOrders<br /> sleep(5)<br /> puts '*****************************'<br /> puts '**** MARKET ORDER - STC EXISTING POSITION ****'<br /> o2 = x.order_id += 1<br /> x.submitOptionsOrder(o2, 'RUT', 'CALL', '200904', 400,<br /> 'SELL', 1, 'MKT', 0, 'DAY')<br /> sleep(5)<br /> puts '*** requesting open orders ***'<br /> x.reqAllOpenOrders<br /> sleep(5)<br /> x.eDisconnect<br /> puts 'disconnected'<br /> rescue Exception => e<br /> puts 'Exception ' + e.message<br /> x.eDisconnect<br /> end<br /></pre><br /><br /><br />Running the code produces the following output:<br /><code><br />init<br />Server Version:43<br /><br />TWS Time at connection:20090317 15:26:34 EST<br /><br />connected ...<br />nextValidId> Next Valid Order Id = 69<br />openOrderEnd> Ended ------------------------<br /> [API.msg2] Market data farm connection is OK:usopt {-1, 2104}<br /> [API.msg2] Market data farm connection is OK:usfarm {-1, 2104}<br />*****************************<br />**** LIMIT ORDER ****<br />--------------------------------------------------------<br />openOrder> open order: orderId=69 ---<br /> contract_msg:<br /> conid = 56413283<br /> symbol = IWM<br /> secType = OPT<br /> exchange = SMART<br /> currency = USD<br /> localSymbol = IQXIL<br /> multiplier =<br /> expiry = 20090930<br /> strike = 38.0<br /> right = C<br /> order_msg:<br /> order id =69<br /> client id =0<br /> action =BUY<br /> quantity =2<br /> type =LMT<br /> lmtPrice =5.0<br /> TIF =DAY<br /> parent Id =0<br /> permId =263195297<br /> order_state_msg:<br /> status =Submitted<br /> initMargin =<br /> maintMargin =<br /> commission =1.7976931348623157e+308<br />~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br />orderStatus>: orderId=69, clientId=0, permId=263195297, status=Submitted filled=0, remaining=2,<br /> avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=<br />*** requesting open orders ***<br />--------------------------------------------------------<br />openOrder> open order: orderId=69 ---<br /> contract_msg:<br /> conid = 56413283<br /> symbol = IWM<br /> secType = OPT<br /> exchange = SMART<br /> currency = USD<br /> localSymbol = IQXIL<br /> multiplier =<br /> expiry = 20090930<br /> strike = 38.0<br /> right = C<br /> order_msg:<br /> order id =69<br /> client id =0<br /> action =BUY<br /> quantity =2<br /> type =LMT<br /> lmtPrice =5.0<br /> TIF =DAY<br /> parent Id =0<br /> permId =263195297<br /> order_state_msg:<br /> status =Submitted<br /> initMargin =<br /> maintMargin =<br /> commission =1.7976931348623157e+308<br />~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br />orderStatus>: orderId=69, clientId=0, permId=263195297, status=Submitted filled=0, remaining=2,<br /> avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=<br />openOrderEnd> Ended ------------------------<br />*****************************<br />**** MARKET ORDER - STC EXISTING POSITION ****<br />*** requesting open orders ***<br />--------------------------------------------------------<br />openOrder> open order: orderId=69 ---<br /> contract_msg:<br /> conid = 56413283<br /> symbol = IWM<br /> secType = OPT<br /> exchange = SMART<br /> currency = USD<br /> localSymbol = IQXIL<br /> multiplier =<br /> expiry = 20090930<br /> strike = 38.0<br /> right = C<br /> order_msg:<br /> order id =69<br /> client id =0<br /> action =BUY<br /> quantity =2<br /> type =LMT<br /> lmtPrice =5.0<br /> TIF =DAY<br /> parent Id =0<br /> permId =263195297<br /> order_state_msg:<br /> status =Submitted<br /> initMargin =<br /> maintMargin =<br /> commission =1.7976931348623157e+308<br />~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br />orderStatus>: orderId=69, clientId=0, permId=263195297, status=Submitted filled=0, remaining=2,<br /> avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=<br />openOrderEnd> Ended ------------------------<br />disconnected<br /></code><br />Examining the output shows that the limit order for 2 IWM SEP 38 CALL @ 5.00 was created and is waiting to be filled. However, the market order for RUT APR 400 CALL wasn't even submitted. Why not? Switching over to TWS application shows the following pop up message box:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxx7g2o3MscVl3hgnfD1x2E49YJlMN1SmMUs7V0aDSLen8-bhpMhqocgp72zZpbuuKF-ooOUeY2Az4FnuO8pDDVsbWKjUSsxgOrs7nv5Ax5_7gdnT4vmg-c348NESNTmi8PmCWbGBTkckW/s1600-h/ib_order_op_5.JPG"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 139px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxx7g2o3MscVl3hgnfD1x2E49YJlMN1SmMUs7V0aDSLen8-bhpMhqocgp72zZpbuuKF-ooOUeY2Az4FnuO8pDDVsbWKjUSsxgOrs7nv5Ax5_7gdnT4vmg-c348NESNTmi8PmCWbGBTkckW/s320/ib_order_op_5.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5314980219347322386" /></a><br /><br />So it looks like this pop up is blocking my order coming through the API. After clicking on "Don't display this message again" and OK, control comes back to TWS. The IWM order is submitted:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHQVMPHhgk7go3hB-g-QS-qbTimwvJotLMN3UidQShATs0okL5RU6XdPRXSeNG66gcS8ghGwppjg6V1B-nYX-x3YoZ3zoSQjW52JGvTPQTMGHWYfYg1VBwju-mc6ifDfp37yaqU-hsT7CI/s1600-h/ib_order_op_2.JPG"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 74px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHQVMPHhgk7go3hB-g-QS-qbTimwvJotLMN3UidQShATs0okL5RU6XdPRXSeNG66gcS8ghGwppjg6V1B-nYX-x3YoZ3zoSQjW52JGvTPQTMGHWYfYg1VBwju-mc6ifDfp37yaqU-hsT7CI/s320/ib_order_op_2.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5314980445221401122" /></a><br />The RUT order still hasn't been created, but this makes sense since my API code had already disconnected from TWS. I'm glad this glitch has happened because it points out one of the pitfalls of using API to interface with TWS. TWS is its own program and must be carefully monitored so that any of these pop ups can be detected and handled right away. This also means that any TWS configuration changes, version upgrade, re-installs may cause old pop ups to reappear. I checked TWS configurations and docs and could not find reference describing how to suppress pop ups.<br /><br />To complete the original example, I modified the code to just submit the RUT order again<br /><br /><pre class="ruby:firstline[260]" name="code"><br /> puts '*****************************'<br /> puts '**** MARKET ORDER - STC EXISTING POSITION ****'<br /> o2 = x.order_id += 1<br /> x.submitOptionsOrder(o2, 'RUT', 'CALL', '200904', 400,<br /> 'SELL', 1, 'MKT', 0, 'DAY')<br /> sleep(5)<br /> puts '*** requesting open orders ***'<br /></pre><br />Running the code now successfully sells my RUT position. The status of the order is Filled. Note that openOrder() call back method was triggered multiple times for this single order. I'm not sure why this happens. It does make it harder to program the trading robot though.<br /><code><br />init<br />Server Version:43<br /><br />TWS Time at connection:20090317 15:42:52 EST<br /><br />connected ...<br />nextValidId> Next Valid Order Id = 73<br />openOrderEnd> Ended ------------------------<br /> [API.msg2] Market data farm connection is OK:usopt {-1, 2104}<br /> [API.msg2] Market data farm connection is OK:usfarm {-1, 2104}<br />**** MARKET ORDER - STC EXISTING POSITION ****<br />--------------------------------------------------------<br />openOrder> open order: orderId=74 ---<br /> contract_msg:<br /> conid = 57330039<br /> symbol = RUT<br /> secType = OPT<br /> exchange = SMART<br /> currency = USD<br /> localSymbol = RUWDX<br /> multiplier =<br /> expiry = 20090416<br /> strike = 400.0<br /> right = C<br /> order_msg:<br /> order id =74<br /> client id =0<br /> action =SELL<br /> quantity =1<br /> type =MKT<br /> lmtPrice =0.0<br /> TIF =DAY<br /> parent Id =0<br /> permId =263195311<br /> order_state_msg:<br /> status =Filled<br /> initMargin =<br /> maintMargin =<br /> commission =1.7976931348623157e+308<br />~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br />orderStatus>: orderId=74, clientId=0, permId=263195311, status=Filled filled=1, remaining=0,<br /> avgFillPrice=20.0, lastFillPrice=20.0, parent Id=0, whyHeld=<br />--------------------------------------------------------<br />openOrder> open order: orderId=74 ---<br /> contract_msg:<br /> conid = 57330039<br /> symbol = RUT<br /> secType = OPT<br /> exchange = SMART<br /> currency = USD<br /> localSymbol = RUWDX<br /> multiplier =<br /> expiry = 20090416<br /> strike = 400.0<br /> right = C<br /> order_msg:<br /> order id =74<br /> client id =0<br /> action =SELL<br /> quantity =1<br /> type =MKT<br /> lmtPrice =0.0<br /> TIF =DAY<br /> parent Id =0<br /> permId =263195311<br /> order_state_msg:<br /> status =Filled<br /> initMargin =<br /> maintMargin =<br /> commission =1.7976931348623157e+308<br />~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br />orderStatus>: orderId=74, clientId=0, permId=263195311, status=Filled filled=1, remaining=0,<br /> avgFillPrice=20.0, lastFillPrice=20.0, parent Id=0, whyHeld=<br />*** requesting open orders ***<br />openOrderEnd> Ended ------------------------<br />--------------------------------------------------------<br />openOrder> open order: orderId=74 ---<br /> contract_msg:<br /> conid = 57330039<br /> symbol = RUT<br /> secType = OPT<br /> exchange = SMART<br /> currency = USD<br /> localSymbol = RUWDX<br /> multiplier =<br /> expiry = 20090416<br /> strike = 400.0<br /> right = C<br /> order_msg:<br /> order id =74<br /> client id =0<br /> action =SELL<br /> quantity =1<br /> type =MKT<br /> lmtPrice =0.0<br /> TIF =DAY<br /> parent Id =0<br /> permId =263195311<br /> order_state_msg:<br /> status =Filled<br /> initMargin =<br /> maintMargin =<br /> commission =1.18<br />~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br />orderStatus>: orderId=74, clientId=0, permId=263195311, status=Filled filled=1, remaining=0,<br /> avgFillPrice=20.0, lastFillPrice=20.0, parent Id=0, whyHeld=<br />disconnected<br /></code><br /><br /><hr/><br />NOTE: If an order is created but there is no option available, it will automatically be cancelled and the error() method will be called with message 'Limit price too far outside of NBBO'. In this example, I submitted a limit order to buy IWM SEP 75 CALL @ 7.75. There are no 75 call strikes in September.<br /><code><br />init<br />Server Version:43<br /><br />TWS Time at connection:20090317 15:11:41 EST<br /><br />connected ...<br />nextValidId> Next Valid Order Id = 64<br />openOrderEnd> Ended ------------------------<br /> [API.msg2] Market data farm connection is OK:usopt {-1, 2104}<br /> [API.msg2] Market data farm connection is OK:usfarm {-1, 2104}<br />*****************************<br />**** LIMIT ORDER ****<br />--------------------------------------------------------<br />openOrder> open order: orderId=64 ---<br /> contract_msg:<br /> conid = 54336659<br /> symbol = IWM<br /> secType = OPT<br /> exchange = SMART<br /> currency = USD<br /> localSymbol = IQQIW<br /> multiplier =<br /> expiry = 20090930<br /> strike = 75.0<br /> right = C<br /> order_msg:<br /> order id =64<br /> client id =0<br /> action =BUY<br /> quantity =2<br /> type =LMT<br /> lmtPrice =7.75<br /> TIF =DAY<br /> parent Id =0<br /> permId =263195279<br /> order_state_msg:<br /> status =PendingCancel<br /> initMargin =<br /> maintMargin =<br /> commission =1.7976931348623157e+308<br />~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br />orderStatus>: orderId=64, clientId=0, permId=263195279, status=PendingCancel filled=0, remaining=2,<br /> avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=<br />--------------------------------------------------------<br />openOrder> open order: orderId=64 ---<br /> contract_msg:<br /> conid = 54336659<br /> symbol = IWM<br /> secType = OPT<br /> exchange = SMART<br /> currency = USD<br /> localSymbol = IQQIW<br /> multiplier =<br /> expiry = 20090930<br /> strike = 75.0<br /> right = C<br /> order_msg:<br /> order id =64<br /> client id =0<br /> action =BUY<br /> quantity =2<br /> type =LMT<br /> lmtPrice =7.75<br /> TIF =DAY<br /> parent Id =0<br /> permId =263195279<br /> order_state_msg:<br /> status =PendingCancel<br /> initMargin =<br /> maintMargin =<br /> commission =1.7976931348623157e+308<br />~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br />orderStatus>: orderId=64, clientId=0, permId=263195279, status=PendingCancel filled=0, remaining=2,<br /> avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=<br /><span style="font-weight:bold;"> [API.msg2] Order Canceled - reason:Limit price too far outside of NBBO {64, 202}</span><br /><br /><br /></code>System Traderhttp://www.blogger.com/profile/11376312889397858168noreply@blogger.com0tag:blogger.com,1999:blog-6568584506474235321.post-89655857777199797872009-03-11T16:40:00.017-04:002009-03-12T15:42:48.883-04:00Proof Of Concept 8 of 8e : Stock OrderCreating code to place a stock order is not complicated. However, a thorough understanding of the sequence of events is important.<br /><br />After the program connects to TWS using the eConnect() method, EWrapper's nextValidId() callback method is automatically triggered. The value passed into this method must be captured and stored, as it represents the next available unique id to be used when placing an order. Failure to use this order id may generate an error. In this example, I ignored the value returned to nextValidID() and issued an order with order id of 10, which causes an error.<br /><br /><code><br /> [API.msg2] Duplicate order id {10, 103}<br /></code><br /><br />Therefore, the value generated in nextValidID must be used and incremented for every additional order. As with some of the callback methods, it's impossible to know exactly when nextValidID() will be triggered. I will make a note that the program will have to check for this event before placing an actual order.<br /><br />Once an order id has been initialized by nextValidID(), it's safe to place an order using the EClientSocket.placeOrder() method. The status of the order can be checked in the EWrapper's orderStatus() call back method. When the status of the order callback is completed, openOrderEnd() call back is called. This signal, which indicates that the request has ended, is new to API v 5.60.<br /><br /><br />The following code example submits a limit order (done in after trading hours)<br /><br /><pre class="ruby" name="code"><br />require 'java'<br />require 'TwsApi.jar'<br /><br />include_class 'ib.client.EClientSocket'<br />include_class 'ib.client.Contract'<br />include_class 'ib.client.Order'<br />include_class 'ib.client.AnyWrapper'<br />include_class 'ib.client.EWrapper'<br /><br />class TestStockOrder<br /> include EWrapper #note "class TestMktData < EWrapper" syntax is no longer supported in JRuby 1.0<br /><br /> attr_accessor :order_id # makes order_id variable accessible <br /> @mySocket<br /><br /> def initialize<br /> puts 'init'<br /> @mySocket = EClientSocket.new(self)<br /> end<br /><br /> def eConnect<br /> @mySocket.eConnect("127.0.0.1", 7496, 0)<br /> end<br /><br /> # Request all open orders that were placed from all API clients linked to one TWS,<br /> # and also from the TWS<br /> # This is a one time shot call. When its done you DO NOT continue to receive<br /> # information about new placed orders<br /> def reqAllOpenOrders<br /> @mySocket.reqAllOpenOrders<br /> end<br /><br /> def submitStockOrder(order_id, symbol, buy_sell, qty, order_type, limit_price, tif)<br /> # --------------------------------<br /> myContract = Contract.new<br /> myContract.m_symbol = symbol<br /> myContract.m_secType = 'STK'<br /> myContract.m_exchange = 'SMART'<br /> myContract.m_currency = 'USD'<br /><br /> myOrder = Order.new<br /> myOrder.m_action = buy_sell # BUY, SELL<br /> myOrder.m_totalQuantity = qty<br /> myOrder.m_orderType = order_type # MKT, LMT, STP, SPLMT, TRAIL, etc<br /> myOrder.m_lmtPrice = limit_price<br /> myOrder.m_tif = tif # DAY, GTC<br /> @mySocket.placeOrder(order_id, myContract, myOrder)<br /> end<br /><br /> def cancelOrder(order_id)<br /> @mySocket.cancelOrder(order_id)<br /> end<br /><br /> def eDisconnect<br /> @mySocket.eDisconnect<br /> end<br /><br /> def contract_msg(contract)<br /> puts ' contract_msg:'<br /> puts ' conid = ' + contract.m_conId.to_s<br /> puts ' symbol = ' + contract.m_symbol<br /> puts ' secType = ' + contract.m_secType<br /> puts ' exchange = ' + contract.m_exchange.to_s<br /> puts ' currency = ' + contract.m_currency<br /> puts ' localSymbol = ' + contract.m_localSymbol.to_s<br /> end<br /><br /> def order_msg(order)<br /> puts ' order_msg:'<br /> puts ' order id =' + order.m_orderId.to_s<br /> puts ' client id =' + order.m_clientId.to_s<br /> puts ' action =' + order.m_action.to_s<br /> puts ' quantity =' + order.m_totalQuantity.to_s<br /> puts ' type =' + order.m_orderType.to_s<br /> puts ' lmtPrice =' + order.m_lmtPrice.to_s<br /> puts ' TIF =' + order.m_tif.to_s<br /> puts ' parent Id =' + order.m_parentId.to_s<br /> puts ' permId =' + order.m_permId.to_s<br /><br /><br /> end<br /><br /> def order_state_msg(order_state)<br /> puts ' order_state_msg:'<br /> puts ' status =' + order_state.m_status.to_s<br /> puts ' initMargin =' + order_state.m_initMargin.to_s<br /> puts ' maintMargin =' + order_state.m_maintMargin.to_s<br /> puts ' commission =' + order_state.m_commission.to_s<br /><br /> end<br /><br /> #///////////////////////////////////////////////////////////////////////<br /> #// Interface methods<br /> #///////////////////////////////////////////////////////////////////////<br /><br /> def tickPrice( ticker_id, field, price, can_auto_execute)<br /> end<br /><br /> def tickSize( ticker_id, field, size)<br /> end<br /><br /> def tickOptionComputation( ticker_id, field, implied_vol,<br /> delta, model_price, pv_dividend)<br /> end<br /><br /> def tickGeneric( ticker_id, tick_type, value)<br /> end<br /><br /> def tickString( ticker_id, tick_type, value)<br /> end<br /><br /> def tickEFP( ticker_id, tick_type, basis_points,<br /> formatted_basis_points, implied_future, hold_days,<br /> future_expiry, dividend_impact, dividends_to_expiry)<br /> end<br /><br /> def orderStatus( order_id, status, filled, remaining,<br /> avg_fill_price, perm_id, parent_id, last_fill_price,<br /> client_id, why_held)<br /> puts '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'<br /> puts 'orderStatus>: orderId=' + order_id.to_s + ', clientId=' + client_id.to_s + ', permId=' + perm_id.to_s +<br /> ', status=' + status + ' filled=' + filled.to_s + ', remaining=' + remaining.to_s + ', '<br /> puts ' avgFillPrice=' + avg_fill_price.to_s + ', lastFillPrice=' + last_fill_price.to_s +<br /> ', parent Id=' + parent_id.to_s + ', whyHeld=' + why_held.to_s<br /> end<br /><br /> def openOrder( order_id, contract, order, order_state)<br /> puts '--------------------------------------------------------'<br /> puts 'openOrder> open order: orderId=' + order_id.to_s + ' --- '<br /> contract_msg(contract)<br /> order_msg(order)<br /> order_state_msg(order_state)<br /> end<br /><br /> def updateAccountValue( key, value, currency, account_name)<br /> end<br /><br /> def updatePortfolio( contract, position, market_price, market_value,<br /> average_cost, unrealized_pnl, realized_pnl, account_name)<br /> end<br /><br /> def updateAccountTime( time_stamp)<br /> end<br /><br /> # triggered right after logging into TWS<br /> def nextValidId( order_id)<br /> @order_id = order_id #storing order id to be used when placing order<br /> puts 'nextValidId> Next Valid Order Id = ' + @order_id.to_s<br /> end<br /><br /> def contractDetails( req_id, contract_details)<br /> end<br /><br /> def bondContractDetails( req_id, contract_details)<br /> end<br /><br /> def contractDetailsEnd( req_id)<br /> end<br /><br /> def execDetails( order_id, contract, execution)<br /> end<br /><br /> def updateMktDepth( ticker_id, position, operation, side, price, size)<br /> end<br /><br /> def updateMktDepthL2( ticker_id, position, market_maker, operation,<br /> side, price, size)<br /> end<br /><br /> def updateNewsBulletin( msg_id, msgType, message, orig_exchange)<br /> end<br /><br /> def managedAccounts( accounts_list)<br /> end<br /><br /> def receiveFA( fa_data_type, xml)<br /> end<br /><br /> def historicalData( req_id, date, open, high, low,<br /> close, volume, count, eWAP, has_gaps)<br /> end<br /><br /> def scannerParameters( xml)<br /> end<br /><br /> def scannerData( req_id, rank, contract_details, distance,<br /> benchmark, projection, legs_str)<br /> end<br /><br /> def scannerDataEnd( req_id)<br /> end<br /><br /> def realtimeBar( req_id, time, open, high, low, close, volume, wap, count)<br /> end<br /><br /> def currentTime( time)<br /> end<br /><br /> def fundamentalData( req_id, data)<br /> end<br /><br /> # ---------------------------------------------------<br /> # new callback methods added in API 9.60<br /> # ---------------------------------------------------<br /> def openOrderEnd() # invoked when all orders are sent to a client as a response to reqOpenOrders(), <br /> # reqAllOpenOrders and right after connected to TWS<br /> puts 'openOrderEnd> Ended'<br /> end<br /><br /> def accountDownloadEnd(account_name) #invoked when a complete snapshot of an account state is sent to a client as a response to reqAccountUpdates()<br /> end<br /><br /> def execDetailsEnd( req_id) # invoked when all executions are sent to a client as a response to reqExecutions()<br /> end<br /><br /> def deltaNeutralValidation(req_id, under_comp) # for Delta-Neutral DN RFQ<br /> end<br /><br /> # ---------------------------------------------------<br /> # -- methods from AnyWrapper -- must be declared<br /> # ---------------------------------------------------<br /><br /> def connectionClosed()<br /> puts ' [API.connectionClosed] Closed connection with TWS'<br /> end<br /><br /> def error(err_obj_str)<br /> puts ' [API.msg1] ' + err_obj_str if err_obj_str.is_a?(String)<br /> puts ' [API.msg3] ' + err_obj_str.getMessage() if err_obj_str.is_a?(Exception)<br /> end<br /><br /> def error(one, two, str)<br /> puts ' [API.msg2] ' + str + ' {' + one.to_s + ', ' + two.to_s + '}'<br /> end<br /><br />end <br /><br /># *********************************************************************************************<br /># Running the code<br /># *********************************************************************************************<br /> x = TestStockOrder.new()<br /> begin<br /> x.eConnect<br /> puts 'connected ...'<br /> sleep(2) # need this here to give time for nextValidId() to be called<br /> puts '*****************************'<br /> puts '**** LIMIT ORDER - LIMIT ****'<br /> o1 = x.order_id<br /> x.submitStockOrder(o1, 'IWM', 'BUY', 50, 'LMT', 10, 'DAY')<br /> sleep(5)<br /> puts '*** requesting open orders ***'<br /> x.reqAllOpenOrders<br /> sleep(5)<br /> x.eDisconnect<br /> puts 'disconnected'<br /> rescue Exception => e<br /><br /> puts 'Exception ' + e.message<br /> x.eDisconnect<br /> end<br /></pre><br />Line 13: declaration of variable order_id to store order id <br />Line 147: initialize order_id after connected to TWS<br /><br />Running the code produces the following output:<br /><code>init<br />Server Version:43<br /><br />TWS Time at connection:20090311 22:22:42 EST<br /><br />connected ...<br />nextValidId> Next Valid Order Id = 47<br />openOrderEnd> Ended<br /> [API.msg2] Market data farm connection is OK:usopt {-1, 2104}<br /> [API.msg2] Market data farm connection is OK:usfarm {-1, 2104}<br />*****************************<br />**** LIMIT ORDER - LIMIT ****<br /> [API.msg2] Order Message:<br />Warning: your order will not be placed at the exchange until 2009-03-12 09:30:00 US/Eastern {47, 399}<br />--------------------------------------------------------<br />openOrder> open order: orderId=47 --- <br /> contract_msg:<br /> conid = 9579970<br /> symbol = IWM<br /> secType = STK<br /> exchange = SMART<br /> currency = USD<br /> localSymbol = IWM<br /> order_msg:<br /> order id =47<br /> client id =0<br /> action =BUY<br /> quantity =50<br /> type =LMT<br /> lmtPrice =10.0<br /> TIF =DAY<br /> parent Id =0<br /> permId =1309177719<br /> order_state_msg:<br /> status =PreSubmitted<br /> initMargin =<br /> maintMargin =<br /> commission =1.7976931348623157e+308<br />~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br />orderStatus>: orderId=45, clientId=0, permId=1309177719, status=PreSubmitted filled=0, remaining=50, <br /> avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=<br /><br />*** requesting open orders ***<br />--------------------------------------------------------<br />openOrder> open order: orderId=47 --- <br /> contract_msg:<br /> conid = 9579970<br /> symbol = IWM<br /> secType = STK<br /> exchange = SMART<br /> currency = USD<br /> localSymbol = IWM<br /> order_msg:<br /> order id =47<br /> client id =0<br /> action =BUY<br /> quantity =50<br /> type =LMT<br /> lmtPrice =10.0<br /> TIF =DAY<br /> parent Id =0<br /> permId =1309177719<br /> order_state_msg:<br /> status =PreSubmitted<br /> initMargin =<br /> maintMargin =<br /> commission =1.7976931348623157e+308<br />~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br />orderStatus>: orderId=47, clientId=0, permId=1309177719, status=PreSubmitted filled=0, remaining=50, <br /> avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=<br />openOrderEnd> Ended<br />disconnected<br /></code><br /><br />Note method openOrderEnd() was triggered right after connecting to TWS and after call to reqAllOpenOrders(). <br />openOrder() method was also triggered right after submitting the order using placeOrder(). <br />The order was successfully created and may be validated in TWS.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglUlW9vThGpJYZgHAtmG1GNJXTRJJCYHfk7W9Ibbn9NAtPbgQt2JLlzkCA51nByPNpW1ROr09ZwD_BPZFo2LINvEGbb1ghL6LxIUCw92T_z7ntoBQZj-3HTrtfWfIZ360of0amMFS6z8m0/s1600-h/ib_order_stock_1.JPG"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 71px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglUlW9vThGpJYZgHAtmG1GNJXTRJJCYHfk7W9Ibbn9NAtPbgQt2JLlzkCA51nByPNpW1ROr09ZwD_BPZFo2LINvEGbb1ghL6LxIUCw92T_z7ntoBQZj-3HTrtfWfIZ360of0amMFS6z8m0/s320/ib_order_stock_1.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5312128334586315762" /></a><br /><br /><hr/><br /><span style="font-weight:bold;">03/12/09 Update</span><br /><br />The market is now open and I'm testing order cancellation and market order type. A limit order for PFE is issued and then canceled. After that, a market order for INTC is issued. The code is as follows:<br /><pre class="ruby:firstline[238]" name="code"><br /># *********************************************************************************************<br /># Running the code<br /># *********************************************************************************************<br /> x = TestStockOrder.new()<br /> begin<br /> x.eConnect<br /> puts 'connected ...'<br /> sleep(2) # need this here to give time for nextValidId() to be called<br /> puts '*****************************'<br /> puts '**** LIMIT ORDER and CANCEL ****'<br /> o2 = x.order_id += 1<br /> x.submitStockOrder(o2, 'PFE', 'BUY', 10, 'LMT', 5.50, 'DAY') # Order 16<br /> sleep(5)<br /> puts '*** Cancelling order ***'<br /> x.cancelOrder(o2) # does not trigger anything, need to check status of order<br /> sleep(5)<br /> puts '*** requesting open orders ***'<br /> x.reqAllOpenOrders<br /> puts '*****************************'<br /> puts '**** MARKET ORDER ****'<br /> x.submitStockOrder(x.order_id += 1, 'INTC', 'BUY', 20, 'MKT', 0, 'DAY')<br /> sleep(5)<br /> puts '*** requesting open orders ***'<br /> x.reqAllOpenOrders<br /> sleep(5)<br /> x.eDisconnect<br /> puts 'disconnected'<br /> rescue Exception => e<br /> puts 'Exception ' + e.message<br /> x.eDisconnect<br /> end<br /></pre><br /><br />Running the code produces the following output:<br /><code><br />init<br />Server Version:43<br /><br />TWS Time at connection:20090312 15:10:08 EST<br /><br />connected ...<br />nextValidId> Next Valid Order Id = 51<br /><span style="color: rgb(0, 153, 0);">--------------------------------------------------------<br />openOrder> open order: orderId=47 --- <br /> contract_msg:<br /> conid = 9579970<br /> symbol = IWM<br /> secType = STK<br /> exchange = SMART<br /> currency = USD<br /> localSymbol = <br /> order_msg:<br /> order id =47<br /> client id =0<br /> action =BUY<br /> quantity =50<br /> type =LMT<br /> lmtPrice =10.0<br /> TIF =DAY<br /> parent Id =0<br /> permId =1309177719<br /> order_state_msg:<br /> status =Submitted<br /> initMargin =<br /> maintMargin =<br /> commission =1.7976931348623157e+308<br />~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br />orderStatus>: orderId=47, clientId=0, permId=1309177719, status=Submitted filled=0, remaining=50, <br /> avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=</span><br />openOrderEnd> Ended<br /> [API.msg2] Market data farm connection is OK:usopt {-1, 2104}<br /> [API.msg2] Market data farm connection is OK:usfarm {-1, 2104}<br />*****************************<br />**** LIMIT ORDER and CANCEL ****<br />--------------------------------------------------------<br />openOrder> open order: orderId=52 --- <br /> contract_msg:<br /> conid = 11031<br /> symbol = PFE<br /> secType = STK<br /> exchange = SMART<br /> currency = USD<br /> localSymbol = PFE<br /> order_msg:<br /> order id =52<br /> client id =0<br /> action =BUY<br /> quantity =10<br /> type =LMT<br /> lmtPrice =5.5<br /> TIF =DAY<br /> parent Id =0<br /> permId =888579469<br /> order_state_msg:<br /> status =Submitted<br /> initMargin =<br /> maintMargin =<br /> commission =1.7976931348623157e+308<br />~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br />orderStatus>: orderId=52, clientId=0, permId=888579469, status=Submitted filled=0, remaining=10, <br /> avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=<br /><span style="color: rgb(0, 0, 153);">*** Cancelling order ***<br /> [API.msg2] Order Canceled - reason: {52, 202}<br />~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br />orderStatus>: orderId=52, clientId=0, permId=888579469, status=Cancelled filled=0, remaining=10, <br /> avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=</span><br />*** requesting open orders ***<br />--------------------------------------------------------<br />openOrder> open order: orderId=47 --- <br /> contract_msg:<br /> conid = 9579970<br /> symbol = IWM<br /> secType = STK<br /> exchange = SMART<br /> currency = USD<br /> localSymbol = <br /> order_msg:<br /> order id =47<br /> client id =0<br /> action =BUY<br /> quantity =50<br /> type =LMT<br /> lmtPrice =10.0<br /> TIF =DAY<br /> parent Id =0<br /> permId =1309177719<br /> order_state_msg:<br /> status =Submitted<br /> initMargin =<br /> maintMargin =<br /> commission =1.7976931348623157e+308<br />~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br />orderStatus>: orderId=47, clientId=0, permId=1309177719, status=Submitted filled=0, remaining=50, <br /> avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=<br />openOrderEnd> Ended<br /><span style="color: rgb(255, 0, 0);">*****************************<br />**** MARKET ORDER ****<br />--------------------------------------------------------<br />openOrder> open order: orderId=53 --- <br /> contract_msg:<br /> conid = 270639<br /> symbol = INTC<br /> secType = STK<br /> exchange = SMART<br /> currency = USD<br /> localSymbol = INTC<br /> order_msg:<br /> order id =53<br /> client id =0<br /> action =BUY<br /> quantity =20<br /> type =MKT<br /> lmtPrice =0.0<br /> TIF =DAY<br /> parent Id =0<br /> permId =888579470<br /> order_state_msg:<br /> status =Filled<br /> initMargin =<br /> maintMargin =<br /> commission =1.7976931348623157e+308<br />~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br />orderStatus>: orderId=53, clientId=0, permId=888579470, status=Filled filled=20, remaining=0, <br /> avgFillPrice=14.36, lastFillPrice=14.36, parent Id=0, whyHeld=<br />--------------------------------------------------------<br />openOrder> open order: orderId=53 --- <br /> contract_msg:<br /> conid = 270639<br /> symbol = INTC<br /> secType = STK<br /> exchange = SMART<br /> currency = USD<br /> localSymbol = INTC<br /> order_msg:<br /> order id =53<br /> client id =0<br /> action =BUY<br /> quantity =20<br /> type =MKT<br /> lmtPrice =0.0<br /> TIF =DAY<br /> parent Id =0<br /> permId =888579470<br /> order_state_msg:<br /> status =Filled<br /> initMargin =<br /> maintMargin =<br /> commission =1.7976931348623157e+308<br />~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br />orderStatus>: orderId=53, clientId=0, permId=888579470, status=Filled filled=20, remaining=0, <br /> avgFillPrice=14.36, lastFillPrice=14.36, parent Id=0, whyHeld=<br />--------------------------------------------------------<br />openOrder> open order: orderId=53 --- <br /> contract_msg:<br /> conid = 270639<br /> symbol = INTC<br /> secType = STK<br /> exchange = SMART<br /> currency = USD<br /> localSymbol = INTC<br /> order_msg:<br /> order id =53<br /> client id =0<br /> action =BUY<br /> quantity =20<br /> type =MKT<br /> lmtPrice =0.0<br /> TIF =DAY<br /> parent Id =0<br /> permId =888579470<br /> order_state_msg:<br /> status =Filled<br /> initMargin =<br /> maintMargin =<br /> commission =1.0<br />~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br />orderStatus>: orderId=53, clientId=0, permId=888579470, status=Filled filled=20, remaining=0, <br /> avgFillPrice=14.36, lastFillPrice=14.36, parent Id=0, whyHeld=<br />--------------------------------------------------------</span><br />*** requesting open orders ***<br />openOrder> open order: orderId=47 --- <br /> contract_msg:<br /> conid = 9579970<br /> symbol = IWM<br /> secType = STK<br /> exchange = SMART<br /> currency = USD<br /> localSymbol = <br /> order_msg:<br /> order id =47<br /> client id =0<br /> action =BUY<br /> quantity =50<br /> type =LMT<br /> lmtPrice =10.0<br /> TIF =DAY<br /> parent Id =0<br /> permId =1309177719<br /> order_state_msg:<br /> status =Submitted<br /> initMargin =<br /> maintMargin =<br /> commission =1.7976931348623157e+308<br />~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br />orderStatus>: orderId=47, clientId=0, permId=1309177719, status=Submitted filled=0, remaining=50, <br /> avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=<br />openOrderEnd> Ended<br />disconnected<br /></code><br />Note:<br />- <span style="color: rgb(0, 153, 0);">Order for IWM submitted yesterday is in Submitted status(it was in PreSubmitted last night).</span> <br />- <span style="color: rgb(0, 0, 153);">After submitting an order for PFE, calling cancelOrder() method causes an 'Order Canceled' message to be returned and orderStatus() callback method is called with the order status of Cancelled.</span><br />- <span style="color: rgb(255, 0, 0);">Market order to purchase INTC status is Filled and triggers orderStatus() callback method multiple times.</span> <br />- Purchase of INTC can be verified in TWS.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzt3gmEQmjjDus5-keGZyW6H5ew6gveoqd8vZfx4gXDFE1Y8IoFSLhDjnO8ySIqKVibyswqePuvQGUIX_37LagSd1NvkmPP-mS3hkxgRpDKE8fkhKqzUwq7dn7PgKfjJtxE2nihQJC7M7i/s1600-h/ib_order_stock_1b.JPG"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 142px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzt3gmEQmjjDus5-keGZyW6H5ew6gveoqd8vZfx4gXDFE1Y8IoFSLhDjnO8ySIqKVibyswqePuvQGUIX_37LagSd1NvkmPP-mS3hkxgRpDKE8fkhKqzUwq7dn7PgKfjJtxE2nihQJC7M7i/s320/ib_order_stock_1b.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5312388127254280194" /></a>System Traderhttp://www.blogger.com/profile/11376312889397858168noreply@blogger.com0tag:blogger.com,1999:blog-6568584506474235321.post-71956381426247204342009-03-07T15:17:00.007-05:002009-03-09T20:16:00.051-04:00Upgrade To API v. 9.60It has come to my attention that IB released their newest API version 9.60 on Jan 20, 2009. As mentioned in the last post, one drawback of the API's asynchronous request and callback model is that there's no way to tell if all the information has been returned. In the case of reqAllOpenOrders() method, a workaround is to intentionally issue a reqMktData() with an invalid ticker to intentionally generate an error. The developers at IB must have agreed that this is a flaw in their model. So in the latest API release, new methods have been added to the EWrapper interface to indicate that callback requests have ended. <br /><br />I have decided to use this API version for my project. The process of replacing API version 9.51 with 9.60 is relatively simple. I just had to edit Java source code (<a href="http://tradingbot.blogspot.com/2009/01/proof-of-concept-4-of-7-ibs-java-test.html">Proof Of Concept - 4 of 8</a>) and create a new version of TwsApi.jar file (<a href="http://tradingbot.blogspot.com/2009/01/proof-of-concept-5-of-7-create-java.html">Proof of Concept - 5 of 8</a>). Finally I had to incorporate the new callback EWrapper methods in each of the code examples in Proof of Concepts - 8 of entries. The code to be added is as follows: <br /><pre class="ruby" name="code"><br /> # ---------------------------------------------------<br /> # new callback methods added in API 9.60<br /> # ---------------------------------------------------<br /> def openOrderEnd() # invoked when all orders are sent to a client as a response to reqOpenOrders()<br /> end<br /><br /> def accountDownloadEnd(account_name) #invoked when a complete snapshot of an account state is sent to a client as a response to reqAccountUpdates()<br /> end<br /><br /> def execDetailsEnd( req_id) # invoked when all executions are sent to a client as a response to reqExecutions()<br /> end<br /><br /> def deltaNeutralValidation(req_id, under_comp) # for Delta-Neutral DN RFQ<br /> end<br /></pre><br />I will go through each of the Proof Of Concept entries and update them to comply with API v. 9.60.System Traderhttp://www.blogger.com/profile/11376312889397858168noreply@blogger.com0tag:blogger.com,1999:blog-6568584506474235321.post-88381866776035790102009-03-01T16:32:00.016-05:002009-03-11T22:13:47.334-04:00Proof Of Concept 8 of 8d : Order StatusTWS API offers three methods to check open orders. They are reqOpenOrders(), reqAllOpenOrders(), and reqAutoOpenOrders(). Results are supplied through EWrapper's orderStatus() and openOrder() callback methods.<br /> <br />reqOpenOrders() and reqAllOpenOrders() are both one time calls and current open order information is provided through the callback methods.<br /><br />reqAutoOpenOrders() works a little bit differently. It is a 'continuous' method such that it gets turned 'on' when called with a TRUE parameter. Current open order information will be fed through the callback methods. In addition, any future open orders will also get reported until the method is turned 'off' with a FALSE parameter. I'm not sure how useful this 'continuous' method will be for my application, so I will not pay attention to this method now.<br /><br />In this example, I have set up two open orders. One is a day order to buy CAT stock and the other is a good-to-cancel options order to sell RUT MARCH 490 call.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyMay47Cz-oVoFqe6_9q-bSawIdwqlgG43DUPgmaFHsoKMZdsfebHc9aUnqym4knrqxxdwcCcTEcRuRXKZJx4412ZTKnkVOj49mGUjDBdqa2QnwGar3wrl_Dtw-wya-SH-sP0c_IdxNsUe/s1600-h/ib_order_status.JPG"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 134px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyMay47Cz-oVoFqe6_9q-bSawIdwqlgG43DUPgmaFHsoKMZdsfebHc9aUnqym4knrqxxdwcCcTEcRuRXKZJx4412ZTKnkVOj49mGUjDBdqa2QnwGar3wrl_Dtw-wya-SH-sP0c_IdxNsUe/s320/ib_order_status.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5308338681013424850" /></a><br /><br />The code demonstrating reqAllOpenOrders() is as follows:<br /><pre class="ruby" name="code"><br />require 'java'<br />require 'TwsApi.jar'<br /><br />include_class 'ib.client.EClientSocket'<br />include_class 'ib.client.Contract'<br />include_class 'ib.client.Order'<br />include_class 'ib.client.OrderState'<br />include_class 'ib.client.AnyWrapper'<br />include_class 'ib.client.EWrapper'<br /><br /><br /><br />class TestOrderStatus<br /> include EWrapper #note "class TestMktData < EWrapper" syntax is no longer supported in JRuby 1.0<br /><br /> @mySocket<br /><br /> def initialize<br /> puts 'init'<br /> @mySocket = EClientSocket.new(self)<br /> end<br /><br /> def eConnect<br /> @mySocket.eConnect("127.0.0.1", 7496, 0)<br /> end<br /><br /> # To request the open orders that were placed from this client. Each open<br /> # order will be fed back through the openOrder() and orderStatus() functions<br /> # on the EWrapper. <br /> # This is a one time shot call. When its done you DO NOT continue to receive<br /> # information about new placed orders<br /> def reqOpenOrders<br /> @mySocket.reqOpenOrders <br /> end<br /><br /> # Request all open orders that were placed from all API clients linked to one TWS,<br /> # and also from the TWS<br /> # This is a one time shot call. When its done you DO NOT continue to receive<br /> # information about new placed orders<br /> def reqAllOpenOrders<br /> @mySocket.reqAllOpenOrders<br /> end<br /><br /> # Request that newly created TWS orders be implicitly associated with the client<br /> # Bind - If set to TRUE, newly created TWS orders will be implicitly associated<br /> # with the client. If set to FALSE, no association will be made.<br /> #<br /> def reqAutoOpenOrders(bind)<br /> @mySocket.reqAutoOpenOrders(bind)<br /> end<br /> <br /><br /> def eDisconnect<br /> @mySocket.eDisconnect<br /> end<br /><br /> def requestStockQuote(id, ticker)<br /> # --------------------------------<br /> myContract = Contract.new<br /> myContract.m_symbol = ticker<br /> myContract.m_secType = 'STK'<br /> myContract.m_exchange = 'SMART'<br /> myContract.m_currency = 'USD'<br /> request_id = id<br /> ticklist = ''<br /><br /> puts '>requestStockQuote - calling reqMktData id='+id.to_s+', ticker='+ticker<br /> @mySocket.reqMktData(request_id, myContract, ticklist, true)<br /> end<br /><br /> def contract_msg(contract)<br /> puts ' contract_msg:'<br /> puts ' conid = ' + contract.m_conId.to_s<br /> puts ' symbol = ' + contract.m_symbol<br /> puts ' secType = ' + contract.m_secType<br /> puts ' expiry = ' + contract.m_expiry.to_s<br /> puts ' strike = ' + contract.m_strike.to_s<br /> puts ' right = ' + contract.m_right<br /> puts ' multiplier = ' + contract.m_multiplier.to_s<br /> puts ' exchange = ' + contract.m_exchange.to_s<br /> puts ' primaryExch = ' + contract.m_primaryExch.to_s<br /> puts ' currency = ' + contract.m_currency<br /> puts ' localSymbol = ' + contract.m_localSymbol<br /> end<br /><br /> def order_msg(order)<br /> puts ' order_msg:'<br /> puts ' order id =' + order.m_orderId.to_s<br /> puts ' client id =' + order.m_clientId.to_s<br /> puts ' action =' + order.m_action.to_s<br /> puts ' quantity =' + order.m_totalQuantity.to_s<br /> puts ' type =' + order.m_orderType.to_s<br /> puts ' lmtPrice =' + order.m_lmtPrice.to_s<br /> puts ' auxPrice =' + order.m_auxPrice.to_s<br /> puts ' TIF =' + order.m_tif.to_s<br /> puts ' parent Id =' + order.m_parentId.to_s<br /> puts ' permId =' + order.m_permId.to_s<br /> puts ' outsideRth =' + order.m_outsideRth.to_s<br /> puts ' hidden =' + order.m_hidden.to_s<br /> puts ' discretionaryAmt=' + order.m_discretionaryAmt.to_s<br /> puts ' triggerMethod=' + order.m_triggerMethod.to_s<br /> puts ' goodAfterTime=' + order.m_goodAfterTime.to_s<br /> puts ' goodTillDate =' + order.m_goodTillDate.to_s<br /> puts ' faGroup =' + order.m_faGroup.to_s<br /> puts ' faMethod =' + order.m_faMethod.to_s<br /> puts ' faPercentage =' + order.m_faPercentage.to_s<br /> puts ' faProfile =' + order.m_faProfile.to_s<br /> puts ' shortSaleSlot=' + order.m_shortSaleSlot.to_s<br /> puts ' designatedLocation=' + order.m_designatedLocation.to_s<br /> puts ' ocaGroup =' + order.m_ocaGroup.to_s<br /> puts ' ocaType =' + order.m_ocaType.to_s<br /> puts ' rule80A =' + order.m_rule80A.to_s<br /> puts ' allOrNone =' + order.m_allOrNone.to_s #yes=1, no=0<br /> puts ' minQty =' + order.m_minQty.to_s<br /> puts ' percentOffset=' + order.m_percentOffset.to_s<br /> puts ' eTradeOnly =' + order.m_eTradeOnly.to_s<br /> puts ' firmQuoteOnly=' + order.m_firmQuoteOnly.to_s<br /> puts ' nbboPriceCap =' + order.m_nbboPriceCap.to_s<br /> puts ' auctionStrategy=' + order.m_auctionStrategy.to_s <br /> puts ' startingPrice=' + order.m_startingPrice.to_s <br /> puts ' stockRefPrice=' + order.m_stockRefPrice.to_s <br /> puts ' delta =' + order.m_delta.to_s <br /> puts ' stockRangeLower=' + order.m_stockRangeLower.to_s <br /> puts ' stockRangeUpper=' + order.m_stockRangeUpper.to_s <br /> puts ' volatility =' + order.m_volatility.to_s <br /> puts ' volatilityType=' + order.m_volatilityType.to_s <br /> puts ' deltaNeutralOrderType=' + order.m_deltaNeutralOrderType.to_s<br /> puts ' deltaNeutralAuxPrice=' + order.m_deltaNeutralAuxPrice.to_s <br /> puts ' continuousUpdate=' + order.m_continuousUpdate.to_s <br /> puts ' referencePriceType=' + order.m_referencePriceType.to_s <br /> puts ' trailStopPrice=' + order.m_trailStopPrice.to_s <br /> puts ' account =' + order.m_account # institutional only<br /> puts ' settlingFirm =' + order.m_settlingFirm.to_s<br /> puts ' clearingAccount=' + order.m_clearingAccount.to_s<br /> puts ' clearingIntent=' + order.m_clearingIntent.to_s<br /> puts ' whatIf =' + order.m_whatIf.to_s <br /> end<br /><br /> def order_state_msg(order_state)<br /> puts ' order_state_msg:'<br /> puts ' status =' + order_state.m_status.to_s<br /> puts ' initMargin =' + order_state.m_initMargin.to_s<br /> puts ' maintMargin =' + order_state.m_maintMargin.to_s<br /> puts ' equityWithLoan=' + order_state.m_equityWithLoan.to_s<br /> puts ' commission =' + order_state.m_commission.to_s<br /> puts ' minCommission =' + order_state.m_minCommission.to_s<br /> puts ' maxCommission =' + order_state.m_maxCommission.to_s<br /> puts ' commissionCurrency=' + order_state.m_commissionCurrency.to_s <br /> puts ' warningText =' + order_state.m_warningText.to_s <br /> end<br /><br /><br /> #///////////////////////////////////////////////////////////////////////<br /> #// Interface methods<br /> #///////////////////////////////////////////////////////////////////////<br /><br /> def tickPrice( ticker_id, field, price, can_auto_execute)<br /> end<br /><br /> def tickSize( ticker_id, field, size)<br /> end<br /><br /> def tickOptionComputation( ticker_id, field, implied_vol,<br /> delta, model_price, pv_dividend)<br /> end<br /><br /> def tickGeneric( ticker_id, tick_type, value)<br /> end<br /><br /> def tickString( ticker_id, tick_type, value)<br /> end<br /><br /> def tickEFP( ticker_id, tick_type, basis_points,<br /> formatted_basis_points, implied_future, hold_days,<br /> future_expiry, dividend_impact, dividends_to_expiry)<br /> end<br /><br /> def orderStatus( order_id, status, filled, remaining,<br /> avg_fill_price, perm_id, parent_id, last_fill_price,<br /> client_id, why_held)<br /> puts 'orderStatus>: orderId=' + order_id.to_s + ', clientId=' + client_id.to_s + ', permId=' + perm_id.to_s +<br /> ', status=' + status + ' filled=' + filled.to_s + ', remaining=' + remaining.to_s + ', '<br /> puts ' avgFillPrice=' + avg_fill_price.to_s + ', lastFillPrice=' + last_fill_price.to_s +<br /> ', parent Id=' + parent_id.to_s + ', whyHeld=' + why_held.to_s<br /> end<br /><br /> def openOrder( order_id, contract, order, order_state)<br /> puts 'openOrder> open order: orderId=' + order_id.to_s + ' --- '<br /> contract_msg(contract)<br /> order_msg(order)<br /> order_state_msg(order_state)<br /> end<br /><br /> def updateAccountValue( key, value, currency, account_name)<br /> end<br /><br /> def updatePortfolio( contract, position, market_price, market_value,<br /> average_cost, unrealized_pnl, realized_pnl, account_name)<br /> end<br /><br /> def updateAccountTime( time_stamp)<br /> end<br /><br /> def nextValidId( order_id)<br /> end<br /><br /> def contractDetails( req_id, contract_details)<br /> end<br /><br /> def bondContractDetails( req_id, contract_details)<br /> end<br /><br /> def contractDetailsEnd( req_id)<br /> end<br /><br /> def execDetails( order_id, contract, execution)<br /> end<br /><br /> def updateMktDepth( ticker_id, position, operation, side, price, size)<br /> end<br /><br /> def updateMktDepthL2( ticker_id, position, market_maker, operation,<br /> side, price, size)<br /> end<br /><br /> def updateNewsBulletin( msg_id, msgType, message, orig_exchange)<br /> end<br /><br /> def managedAccounts( accounts_list)<br /> end<br /><br /> def receiveFA( fa_data_type, xml)<br /> end<br /><br /> def historicalData( req_id, date, open, high, low,<br /> close, volume, count, eWAP, has_gaps)<br /> end<br /><br /> def scannerParameters( xml)<br /> end<br /><br /> def scannerData( req_id, rank, contract_details, distance,<br /> benchmark, projection, legs_str)<br /> end<br /><br /> def scannerDataEnd( req_id)<br /> end<br /><br /> def realtimeBar( req_id, time, open, high, low, close, volume, wap, count)<br /> end<br /><br /> def currentTime( time)<br /> end<br /><br /> def fundamentalData( req_id, data)<br /> end<br /><br /> # ---------------------------------------------------<br /> # -- methods from AnyWrapper -- must be declared<br /> # ---------------------------------------------------<br /><br /> def connectionClosed()<br /> puts ' [API.connectionClosed] Closed connection with TWS'<br /> end<br /><br /> def error(err_obj_str)<br /> puts ' [API.msg1] ' + err_obj_str if err_obj_str.is_a?(String)<br /> puts ' [API.msg3] ' + err_obj_str.getMessage() if err_obj_str.is_a?(Exception)<br /> end<br /><br /> def error(one, two, str)<br /> puts ' [API.msg2] ' + str + ' {' + one.to_s + ', ' + two.to_s + '}'<br /> end<br /><br />end # class TestOrderStatus<br /><br />#*********************************************************************************************<br /># Running the code<br /># *********************************************************************************************<br /> x = TestOrderStatus.new()<br /> begin<br /> x.eConnect<br /> puts 'connected ...'<br /> x.reqAllOpenOrders # requesting open orders information<br /> x.requestStockQuote(-99, 'DONE_OPEN_ORDERS') # call to reqMktData() for invalid quote. <br /> # Will generate error AFTER all<br /> # open orders info are returned<br /> sleep(5)<br /> x.eDisconnect<br /> puts 'disconnected'<br /> rescue Exception => e<br /> puts 'Exception>>' + e.message<br /> x.eDisconnect<br /> end<br /></pre><br /><br />Examining the code shows the program structure to be consistent with my previous examples of TWS API. Information is requested and it is delivered asynchronously via callback methods. However, a major drawback to this model is that there is no event or indicator that the information being delivered is finished. This is where a workaround is needed. One way to do this is to immediately issue a reqMktData() with an invalid ticker symbol to intentionally cause an error. This error will be fed through error() callback method after the open order request is finished.<br />Line 287 demonstrates this workaround. It's not very elegant and I will have to keep this in mind when designing the application.<br /><br />Running the code produces the following output:<br /><br /><code><br />init<br />Server Version:43<br /><br />TWS Time at connection:20090301 16:07:19 EST<br /><br />connected ...<br />>requestStockQuote - calling reqMktData id=-99, ticker=DONE_OPEN_ORDERS<br />openOrder> <span style="color: rgb(0, 153, 0);">open order: orderId=2 --- <br /> contract_msg:<br /> conid = 54528396<br /> symbol = RUT<br /> secType = OPT<br /> expiry = 20090319<br /> strike = 490.0<br /> right = C<br /> multiplier = <br /> exchange = SMART<br /> primaryExch = <br /> currency = USD<br /> localSymbol = RUWCA<br /> order_msg:<br /> order id =2<br /> client id =0<br /> action =SELL<br /> quantity =1<br /> type =LMT<br /> lmtPrice =2.0<br /> auxPrice =0.0<br /> TIF =GTC<br /> parent Id =0<br /> permId =1664800509<br /> outsideRth =false<br /> hidden =false<br /> discretionaryAmt=0.0<br /> triggerMethod=0<br /> goodAfterTime=<br /> goodTillDate =<br /> faGroup =<br /> faMethod =<br /> faPercentage =<br /> faProfile =<br /> shortSaleSlot=0<br /> designatedLocation=<br /> ocaGroup =<br /> ocaType =3<br /> rule80A =<br /> allOrNone =false<br /> minQty =0<br /> percentOffset=0.0<br /> eTradeOnly =false<br /> firmQuoteOnly=false<br /> nbboPriceCap =0.0<br /> auctionStrategy=0<br /> startingPrice=0.0<br /> stockRefPrice=0.0<br /> delta =0.0<br /> stockRangeLower=0.0<br /> stockRangeUpper=0.0<br /> volatility =0.0<br /> volatilityType=0<br /> deltaNeutralOrderType=None<br /> deltaNeutralAuxPrice=0.0<br /> continuousUpdate=0<br /> referencePriceType=0<br /> trailStopPrice=0.0<br /> account =DU58265<br /> settlingFirm =DEMO<br /> clearingAccount=<br /> clearingIntent=IB<br /> whatIf =false<br /> order_state_msg:<br /> status =PendingSubmit<br /> initMargin =<br /> maintMargin =<br /> equityWithLoan=<br /> commission =1.7976931348623157e+308<br /> minCommission =1.7976931348623157e+308<br /> maxCommission =1.7976931348623157e+308<br /> commissionCurrency=USD<br /> warningText =<br />orderStatus>: orderId=2, clientId=0, permId=1664800509, status=PendingSubmit filled=0, remaining=1, <br /> avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=</span><br />openOrder> <span style="color: rgb(0, 0, 153);">open order: orderId=1 --- <br /> contract_msg:<br /> conid = 5437<br /> symbol = CAT<br /> secType = STK<br /> expiry = <br /> strike = 0.0<br /> right = ?<br /> multiplier = <br /> exchange = SMART<br /> primaryExch = <br /> currency = USD<br /> localSymbol = CAT<br /> order_msg:<br /> order id =1<br /> client id =0<br /> action =BUY<br /> quantity =100<br /> type =LMT<br /> lmtPrice =20.0<br /> auxPrice =0.0<br /> TIF =DAY<br /> parent Id =0<br /> permId =1664800505<br /> outsideRth =false<br /> hidden =false<br /> discretionaryAmt=0.0<br /> triggerMethod=0<br /> goodAfterTime=<br /> goodTillDate =<br /> faGroup =<br /> faMethod =<br /> faPercentage =<br /> faProfile =<br /> shortSaleSlot=0<br /> designatedLocation=<br /> ocaGroup =<br /> ocaType =3<br /> rule80A =<br /> allOrNone =false<br /> minQty =0<br /> percentOffset=0.0<br /> eTradeOnly =false<br /> firmQuoteOnly=false<br /> nbboPriceCap =0.0<br /> auctionStrategy=0<br /> startingPrice=0.0<br /> stockRefPrice=0.0<br /> delta =0.0<br /> stockRangeLower=0.0<br /> stockRangeUpper=0.0<br /> volatility =0.0<br /> volatilityType=0<br /> deltaNeutralOrderType=None<br /> deltaNeutralAuxPrice=0.0<br /> continuousUpdate=0<br /> referencePriceType=0<br /> trailStopPrice=0.0<br /> account =DU58265<br /> settlingFirm =DEMO<br /> clearingAccount=<br /> clearingIntent=IB<br /> whatIf =false<br /> order_state_msg:<br /> status =PreSubmitted<br /> initMargin =<br /> maintMargin =<br /> equityWithLoan=<br /> commission =1.7976931348623157e+308<br /> minCommission =1.7976931348623157e+308<br /> maxCommission =1.7976931348623157e+308<br /> commissionCurrency=USD<br /> warningText =<br />orderStatus>: orderId=1, clientId=0, permId=1664800505, status=PreSubmitted filled=0, remaining=100, <br /> avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=</span><br /> [API.msg2] Market data farm connection is OK:usopt {-1, 2104}<br /> [API.msg2] Market data farm connection is OK:usfarm {-1, 2104}<br />openOrder> <span style="color: rgb(0, 153, 0);">open order: orderId=2 --- <br /> contract_msg:<br /> conid = 54528396<br /> symbol = RUT<br /> secType = OPT<br /> expiry = 20090319<br /> strike = 490.0<br /> right = C<br /> multiplier = <br /> exchange = SMART<br /> primaryExch = <br /> currency = USD<br /> localSymbol = RUWCA<br /> order_msg:<br /> order id =2<br /> client id =0<br /> action =SELL<br /> quantity =1<br /> type =LMT<br /> lmtPrice =2.0<br /> auxPrice =0.0<br /> TIF =GTC<br /> parent Id =0<br /> permId =1664800509<br /> outsideRth =false<br /> hidden =false<br /> discretionaryAmt=0.0<br /> triggerMethod=0<br /> goodAfterTime=<br /> goodTillDate =<br /> faGroup =<br /> faMethod =<br /> faPercentage =<br /> faProfile =<br /> shortSaleSlot=0<br /> designatedLocation=<br /> ocaGroup =<br /> ocaType =3<br /> rule80A =<br /> allOrNone =false<br /> minQty =0<br /> percentOffset=0.0<br /> eTradeOnly =false<br /> firmQuoteOnly=false<br /> nbboPriceCap =0.0<br /> auctionStrategy=0<br /> startingPrice=0.0<br /> stockRefPrice=0.0<br /> delta =0.0<br /> stockRangeLower=0.0<br /> stockRangeUpper=0.0<br /> volatility =0.0<br /> volatilityType=0<br /> deltaNeutralOrderType=None<br /> deltaNeutralAuxPrice=0.0<br /> continuousUpdate=0<br /> referencePriceType=0<br /> trailStopPrice=0.0<br /> account =DU58265<br /> settlingFirm =DEMO<br /> clearingAccount=<br /> clearingIntent=IB<br /> whatIf =false<br /> order_state_msg:<br /> status =PendingSubmit<br /> initMargin =<br /> maintMargin =<br /> equityWithLoan=<br /> commission =1.7976931348623157e+308<br /> minCommission =1.7976931348623157e+308<br /> maxCommission =1.7976931348623157e+308<br /> commissionCurrency=USD<br /> warningText =<br />orderStatus>: orderId=2, clientId=0, permId=1664800509, status=PendingSubmit filled=0, remaining=1, <br /> avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=</span><br />openOrder> <span style="color: rgb(0, 0, 153);">open order: orderId=1 --- <br /> contract_msg:<br /> conid = 5437<br /> symbol = CAT<br /> secType = STK<br /> expiry = <br /> strike = 0.0<br /> right = ?<br /> multiplier = <br /> exchange = SMART<br /> primaryExch = <br /> currency = USD<br /> localSymbol = CAT<br /> order_msg:<br /> order id =1<br /> client id =0<br /> action =BUY<br /> quantity =100<br /> type =LMT<br /> lmtPrice =20.0<br /> auxPrice =0.0<br /> TIF =DAY<br /> parent Id =0<br /> permId =1664800505<br /> outsideRth =false<br /> hidden =false<br /> discretionaryAmt=0.0<br /> triggerMethod=0<br /> goodAfterTime=<br /> goodTillDate =<br /> faGroup =<br /> faMethod =<br /> faPercentage =<br /> faProfile =<br /> shortSaleSlot=0<br /> designatedLocation=<br /> ocaGroup =<br /> ocaType =3<br /> rule80A =<br /> allOrNone =false<br /> minQty =0<br /> percentOffset=0.0<br /> eTradeOnly =false<br /> firmQuoteOnly=false<br /> nbboPriceCap =0.0<br /> auctionStrategy=0<br /> startingPrice=0.0<br /> stockRefPrice=0.0<br /> delta =0.0<br /> stockRangeLower=0.0<br /> stockRangeUpper=0.0<br /> volatility =0.0<br /> volatilityType=0<br /> deltaNeutralOrderType=None<br /> deltaNeutralAuxPrice=0.0<br /> continuousUpdate=0<br /> referencePriceType=0<br /> trailStopPrice=0.0<br /> account =DU58265<br /> settlingFirm =DEMO<br /> clearingAccount=<br /> clearingIntent=IB<br /> whatIf =false<br /> order_state_msg:<br /> status =PreSubmitted<br /> initMargin =<br /> maintMargin =<br /> equityWithLoan=<br /> commission =1.7976931348623157e+308<br /> minCommission =1.7976931348623157e+308<br /> maxCommission =1.7976931348623157e+308<br /> commissionCurrency=USD<br /> warningText =<br />orderStatus>: orderId=1, clientId=0, permId=1664800505, status=PreSubmitted filled=0, remaining=100, <br /> avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=</span><br /> [API.msg2] No security definition has been found for the request {-99, 200}<br />disconnected<br /><br /></code><br /><br />Note that even though I currently have two open orders (RUT call option and CAT), callback methods get triggered twice for each order. I'm not sure why this happens, but fortunately, orderId is unique so I can use it to tell if an order is an original or duplicate. <br /><br />After experimenting some more, I found out that when a connection is made to TWS, a call to reqAllOpenOrders() is automatically issued. Since my code example explicitly calls reqAllOpenOrders(), two sets of information get printed. Changing the code to the following proves my point:<br /><pre class="ruby:firstline[279]" name="code"><br /># *********************************************************************************************<br /># Running the code<br /># *********************************************************************************************<br /> x = TestOrderStatus.new()<br /> begin<br /> x.eConnect<br /> puts 'connected ...'<br /> sleep(5)<br /> x.eDisconnect<br /> puts 'disconnected'<br /> rescue Exception => e<br /> puts 'Exception>>' + e.message<br /> x.eDisconnect<br /> end<br /></pre><br /><br /><code><br />init<br />Server Version:43<br /><br />TWS Time at connection:20090301 17:11:38 EST<br /><br />connected ...<br />openOrder> <span style="color: rgb(0, 153, 0);">open order: orderId=2 --- <br /> contract_msg:<br /> conid = 54528396<br /> symbol = RUT<br /> secType = OPT<br /> expiry = 20090319<br /> strike = 490.0<br /> right = C<br /> multiplier = <br /> exchange = SMART<br /> primaryExch = <br /> currency = USD<br /> localSymbol = RUWCA<br /> order_msg:<br /> order id =2<br /> client id =0<br /> action =SELL<br /> quantity =1<br /> type =LMT<br /> lmtPrice =2.0<br /> auxPrice =0.0<br /> TIF =GTC<br /> parent Id =0<br /> permId =1664800509<br /> outsideRth =false<br /> hidden =false<br /> discretionaryAmt=0.0<br /> triggerMethod=0<br /> goodAfterTime=<br /> goodTillDate =<br /> faGroup =<br /> faMethod =<br /> faPercentage =<br /> faProfile =<br /> shortSaleSlot=0<br /> designatedLocation=<br /> ocaGroup =<br /> ocaType =3<br /> rule80A =<br /> allOrNone =false<br /> minQty =0<br /> percentOffset=0.0<br /> eTradeOnly =false<br /> firmQuoteOnly=false<br /> nbboPriceCap =0.0<br /> auctionStrategy=0<br /> startingPrice=0.0<br /> stockRefPrice=0.0<br /> delta =0.0<br /> stockRangeLower=0.0<br /> stockRangeUpper=0.0<br /> volatility =0.0<br /> volatilityType=0<br /> deltaNeutralOrderType=None<br /> deltaNeutralAuxPrice=0.0<br /> continuousUpdate=0<br /> referencePriceType=0<br /> trailStopPrice=0.0<br /> account =DU58265<br /> settlingFirm =DEMO<br /> clearingAccount=<br /> clearingIntent=IB<br /> whatIf =false<br /> order_state_msg:<br /> status =PendingSubmit<br /> initMargin =<br /> maintMargin =<br /> equityWithLoan=<br /> commission =1.7976931348623157e+308<br /> minCommission =1.7976931348623157e+308<br /> maxCommission =1.7976931348623157e+308<br /> commissionCurrency=USD<br /> warningText =<br />orderStatus>: orderId=2, clientId=0, permId=1664800509, status=PendingSubmit filled=0, remaining=1, <br /> avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=</span><br />openOrder> <span style="color: rgb(0, 0, 153);">open order: orderId=1 --- <br /> contract_msg:<br /> conid = 5437<br /> symbol = CAT<br /> secType = STK<br /> expiry = <br /> strike = 0.0<br /> right = ?<br /> multiplier = <br /> exchange = SMART<br /> primaryExch = <br /> currency = USD<br /> localSymbol = CAT<br /> order_msg:<br /> order id =1<br /> client id =0<br /> action =BUY<br /> quantity =100<br /> type =LMT<br /> lmtPrice =20.0<br /> auxPrice =0.0<br /> TIF =DAY<br /> parent Id =0<br /> permId =1664800505<br /> outsideRth =false<br /> hidden =false<br /> discretionaryAmt=0.0<br /> triggerMethod=0<br /> goodAfterTime=<br /> goodTillDate =<br /> faGroup =<br /> faMethod =<br /> faPercentage =<br /> faProfile =<br /> shortSaleSlot=0<br /> designatedLocation=<br /> ocaGroup =<br /> ocaType =3<br /> rule80A =<br /> allOrNone =false<br /> minQty =0<br /> percentOffset=0.0<br /> eTradeOnly =false<br /> firmQuoteOnly=false<br /> nbboPriceCap =0.0<br /> auctionStrategy=0<br /> startingPrice=0.0<br /> stockRefPrice=0.0<br /> delta =0.0<br /> stockRangeLower=0.0<br /> stockRangeUpper=0.0<br /> volatility =0.0<br /> volatilityType=0<br /> deltaNeutralOrderType=None<br /> deltaNeutralAuxPrice=0.0<br /> continuousUpdate=0<br /> referencePriceType=0<br /> trailStopPrice=0.0<br /> account =DU58265<br /> settlingFirm =DEMO<br /> clearingAccount=<br /> clearingIntent=IB<br /> whatIf =false<br /> order_state_msg:<br /> status =PreSubmitted<br /> initMargin =<br /> maintMargin =<br /> equityWithLoan=<br /> commission =1.7976931348623157e+308<br /> minCommission =1.7976931348623157e+308<br /> maxCommission =1.7976931348623157e+308<br /> commissionCurrency=USD<br /> warningText =<br />orderStatus>: orderId=1, clientId=0, permId=1664800505, status=PreSubmitted filled=0, remaining=100, <br /> avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=</span><br /> [API.msg2] Market data farm connection is OK:usopt {-1, 2104}<br /> [API.msg2] Market data farm connection is OK:usfarm {-1, 2104}<br />disconnected<br /></code><br /><br /><hr/><br /><span style="font-weight:bold;">03/09/09 Update</span><br /><a href="http://tradingbot.blogspot.com/2009/03/upgrade-to-api-v-960.html"><br />Upgrading to API v. 9.60</a> requires addition of these new callback methods.<br /><br /><pre class="ruby:firstline[258]" name="code"> <br /> # ---------------------------------------------------<br /> # new callback methods added in API 9.60<br /> # ---------------------------------------------------<br /> def openOrderEnd() # invoked when all orders are sent to a client as a response to reqOpenOrders() and right after connected to TWS<br /> puts 'openOrderEnd()'<br /> end<br /><br /> def accountDownloadEnd(account_name) #invoked when a complete snapshot of an account state is sent to a client as a response to reqAccountUpdates()<br /> end<br /><br /> def execDetailsEnd( req_id) # invoked when all executions are sent to a client as a response to reqExecutions()<br /> end<br /><br /> def deltaNeutralValidation(req_id, under_comp) # for Delta-Neutral DN RFQ<br /> end<br /></pre><br />Running the code produces the same result except openOrderEnd() method is called back. <br /><code><br />...<br /><span style="font-weight:bold;">openOrderEnd()</span><br />disconnected<br /></code>System Traderhttp://www.blogger.com/profile/11376312889397858168noreply@blogger.com2tag:blogger.com,1999:blog-6568584506474235321.post-38782080411081090372009-02-24T12:36:00.012-05:002009-03-09T22:46:34.395-04:00Proof Of Concept 8 of 8c : Account InformationAccount information such as account value, margin requirements, cash balance, and current portfolio holdings may be retrieve by calling EClientSocket.reqAccountUpdates(). Data are returned in EWrapper's updateAccountTime(), updateAccountValue(), and updatePortfolio() methods. The code is as follows:<br /><pre class="ruby" name="code"><br />require 'java'<br />require 'TwsApi.jar'<br /><br />include_class 'ib.client.EClientSocket'<br />include_class 'ib.client.Contract'<br />include_class 'ib.client.AnyWrapper'<br />include_class 'ib.client.EWrapper'<br /><br /><br />class TestAccount<br /> include EWrapper #note "class TestMktData < EWrapper" syntax is no longer supported in JRuby 1.0<br /><br /> @mySocket<br /> <br /> def initialize<br /> @mySocket = EClientSocket.new(self)<br /> end<br /><br /> def eConnect<br /> @mySocket.eConnect("127.0.0.1", 7496, 0)<br /> end<br /><br /> def requestAccount(subscribe, acct_code)<br /> # --------------------------------<br /> @mySocket.reqAccountUpdates(subscribe,<br /> acct_code)<br /> end<br /><br /> def eDisconnect<br /> @mySocket.eDisconnect<br /> end<br /><br /> def contract_msg(contract)<br /> puts ' contract_msg:'<br /> puts ' conid = ' + contract.m_conId.to_s<br /> puts ' symbol = ' + contract.m_symbol<br /> puts ' secType = ' + contract.m_secType<br /> puts ' expiry = ' + contract.m_expiry.to_s<br /> puts ' strike = ' + contract.m_strike.to_s<br /> puts ' right = ' + contract.m_right<br /> puts ' multiplier = ' + contract.m_multiplier.to_s<br /> puts ' exchange = ' + contract.m_exchange.to_s<br /> puts ' primaryExch = ' + contract.m_primaryExch<br /> puts ' currency = ' + contract.m_currency<br /> puts ' localSymbol = ' + contract.m_localSymbol<br /> end<br /><br /> #///////////////////////////////////////////////////////////////////////<br /> #// Interface methods<br /> #///////////////////////////////////////////////////////////////////////<br /><br /> def tickPrice( ticker_id, field, price, can_auto_execute)<br /> end<br /><br /> def tickSize( ticker_id, field, size)<br /> end<br /><br /> def tickOptionComputation( ticker_id, field, implied_vol,<br /> delta, model_price, pv_dividend)<br /> end<br /><br /> def tickGeneric( ticker_id, tick_type, value)<br /> end<br /><br /> def tickString( ticker_id, tick_type, value)<br /> end<br /><br /> def tickEFP( ticker_id, tick_type, basis_points,<br /> formatted_basis_points, implied_future, hold_days,<br /> future_expiry, dividend_impact, dividends_to_expiry)<br /> end<br /><br /> def orderStatus( order_id, status, filled, remaining,<br /> avg_fill_price, perm_id, parent_id, last_fill_price,<br /> client_id, why_held)<br /> end<br /><br /> def openOrder( order_id, contract, order, order_state)<br /> end<br /><br /> def updateAccountValue( key, value, currency, account_name)<br /> puts 'updateAccountValue: ' + key + ' ' + value + ' ' + currency.to_s + ' ' + account_name<br /> end<br /><br /> def updatePortfolio( contract, position, market_price, market_value,<br /> average_cost, unrealized_pnl, realized_pnl, account_name)<br /> puts 'updatePortfolio: --- '<br /> contract_msg(contract)<br /> puts ' position '+position.to_s + ', mkt price ' + market_price.to_s + ', mkt value ' + market_value.to_s + <br /> ', avg cost ' + average_cost.to_s + ', unrealized p/l ' + unrealized_pnl.to_s + ', realized p/l ' + realized_pnl.to_s + ' ' + account_name<br /> end<br /><br /> def updateAccountTime( time_stamp)<br /> puts 'updateAccountTime: ' + time_stamp.to_s<br /> end<br /><br /> def nextValidId( order_id)<br /> end<br /><br /> def contractDetails( req_id, contract_details)<br /> end<br /><br /> def bondContractDetails( req_id, contract_details)<br /> end<br /><br /> def contractDetailsEnd( req_id)<br /> end<br /><br /> def execDetails( order_id, contract, execution)<br /> end<br /><br /> def updateMktDepth( ticker_id, position, operation, side, price, size)<br /> end<br /><br /> def updateMktDepthL2( ticker_id, position, market_maker, operation,<br /> side, price, size)<br /> end<br /><br /> def updateNewsBulletin( msg_id, msgType, message, orig_exchange)<br /> end<br /><br /> def managedAccounts( accounts_list)<br /> end<br /><br /> def receiveFA( fa_data_type, xml)<br /> end<br /> <br /> def historicalData( req_id, date, open, high, low,<br /> close, volume, count, pWAP, has_gaps)<br /> end<br /><br /> def scannerParameters( xml)<br /> end<br /><br /> def scannerData( req_id, rank, contract_details, distance,<br /> benchmark, projection, legs_str)<br /> end<br /><br /> def scannerDataEnd( req_id)<br /> end<br /><br /> def realtimeBar( req_id, time, open, high, low, close, volume, wap, count)<br /> end<br /><br /> def currentTime( time)<br /> end<br /><br /> def fundamentalData( req_id, data)<br /> end<br /><br /> # ---------------------------------------------------<br /> # -- methods from AnyWrapper -- must be declared<br /> # ---------------------------------------------------<br /><br /> def connectionClosed()<br /> puts ' [API.connectionClosed] Closed connection with TWS'<br /> end<br /> <br /> def error(err_obj_str)<br /> puts ' [API.msg1] ' + err_obj_str if err_obj_str.is_a?(String)<br /> puts ' [API.msg3] ' + err_obj_str.getMessage() if err_obj_str.is_a?(Exception)<br /> end<br /><br /> def error(one, two, str) <br /> puts ' [API.msg2] ' + str + ' {' + one.to_s + ', ' + two.to_s + '}'<br /> end<br /><br />end <br /><br /># *********************************************************************************************<br /># Running the code<br /># *********************************************************************************************<br /> x = TestAccount.new()<br /> begin<br /> x.eConnect<br /> puts 'connected ...'<br /> x.requestAccount(true, '') # subscribe to Account data<br /> sleep(5)<br /> x.requestAccount(false, '') # unsubscribe to Account data<br /> x.eDisconnect<br /> puts 'disconnected'<br /> rescue Exception => e<br /> puts 'can not connect'<br /> puts 'Exception' + e.message<br /> x.eDisconnect<br /> end<br /></pre><br />Lines 81-83: updateAccountValue() returns key-pair values of all the different account value types.<br />Lines 85-91: updatePortfolio() returns each individual equity positions held in the account.<br />Lines 93-95: updateAccountTime() returns last update time of the account information.<br /><br />This is a screen shot of my paper trading account's View Account Information in TWS. Information returned from the three interface methods should match what is displayed here.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiM3ZfQP786Y2p8uLPIC9dIuOWjcmM8hTWj0PEcCpZVXF37a1ED-8rOSvnDmv_K-K0p0eCe2VjIHtDBQkj2kRPkmeYrz2M7DokxgsVyC07BDc_BJMlpgzmKzE2KU7J5Vz_aVh7S4LnAkB14/s1600-h/ib_portfolio.JPG"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 230px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiM3ZfQP786Y2p8uLPIC9dIuOWjcmM8hTWj0PEcCpZVXF37a1ED-8rOSvnDmv_K-K0p0eCe2VjIHtDBQkj2kRPkmeYrz2M7DokxgsVyC07BDc_BJMlpgzmKzE2KU7J5Vz_aVh7S4LnAkB14/s320/ib_portfolio.JPG" alt="" id="BLOGGER_PHOTO_ID_5306420068250166274" border="0" /></a><br /><br />Run output contains contains various account values, account time, and all positions (<span style="font-weight: bold;"><span style="color: rgb(0, 153, 0);">GE</span></span>, <span style="font-weight: bold;"><span style="color: rgb(0, 0, 153);">RUT March 490 Call option</span></span>, and <span style="font-weight: bold;"><span style="color: rgb(255, 0, 0);">SPY</span></span>). Note I've color coded the output for better visibility.<br /><code><br />Server Version:43TWS Time at connection:20090224 12:01:06 ESTconnected ...<br /> [API.msg2] Market data farm connection is OK:usopt {-1, 2104}<br /> [API.msg2] Market data farm connection is OK:usfarm {-1, 2104}<br />updateAccountValue: AccountCode DU58265 DU58265<br />updateAccountValue: AccountReady true DU58265<br />updateAccountValue: AccountType UNIVERSAL DU58265<br />updateAccountValue: AccruedCash 0.00 BASE DU58265<br />updateAccountValue: AccruedCash 0.00 USD DU58265<br />updateAccountValue: AccruedCash-S 0.00 USD DU58265<br />updateAccountValue: AccruedDividend 0.00 USD DU58265<br />updateAccountValue: AccruedDividend-S 0.00 USD DU58265<br />updateAccountValue: AvailableFunds 984986.06 USD DU58265<br />updateAccountValue: AvailableFunds-S 984986.06 USD DU58265<br />updateAccountValue: Billable 0.00 USD DU58265<br />updateAccountValue: Billable-S 0.00 USD DU58265<br />updateAccountValue: BuyingPower 3939894.24 USD DU58265<br />updateAccountValue: CashBalance 978048.56 BASE DU58265<br />updateAccountValue: CashBalance 978048.56 USD DU58265<br />updateAccountValue: Currency BASE BASE DU58265<br />updateAccountValue: Currency USD USD DU58265<br />updateAccountValue: Cushion 0.997617 DU58265<br />updateAccountValue: DayTradesRemaining -1 DU58265<br />updateAccountValue: DayTradesRemainingT+1 -1 DU58265<br />updateAccountValue: DayTradesRemainingT+2 -1 DU58265<br />updateAccountValue: DayTradesRemainingT+3 -1 DU58265<br />updateAccountValue: DayTradesRemainingT+4 -1 DU58265<br />updateAccountValue: EquityWithLoanValue 987298.56 USD DU58265<br />updateAccountValue: EquityWithLoanValue-S 987298.56 USD DU58265<br />updateAccountValue: ExcessLiquidity 984986.06 USD DU58265<br />updateAccountValue: ExcessLiquidity-S 984986.06 USD DU58265<br />updateAccountValue: ExchangeRate 1.00 BASE DU58265<br />updateAccountValue: ExchangeRate 1.00 USD DU58265<br />updateAccountValue: FullAvailableFunds 984986.06 USD DU58265<br />updateAccountValue: FullAvailableFunds-S 984986.06 USD DU58265<br />updateAccountValue: FullExcessLiquidity 984986.06 USD DU58265<br />updateAccountValue: FullExcessLiquidity-S 984986.06 USD DU58265<br />updateAccountValue: FullInitMarginReq 2312.50 USD DU58265<br />updateAccountValue: FullInitMarginReq-S 2312.50 USD DU58265<br />updateAccountValue: FullMaintMarginReq 2312.50 USD DU58265<br />updateAccountValue: FullMaintMarginReq-S 2312.50 USD DU58265<br />updateAccountValue: FutureOptionValue 0.00 BASE DU58265<br />updateAccountValue: FutureOptionValue 0.00 USD DU58265<br />updateAccountValue: FuturesPNL 0.00 BASE DU58265<br />updateAccountValue: FuturesPNL 0.00 USD DU58265<br />updateAccountValue: GrossPositionValue 9290.00 USD DU58265<br />updateAccountValue: GrossPositionValue-S 9290.00 USD DU58265<br />updateAccountValue: InitMarginReq 2312.50 USD DU58265<br />updateAccountValue: InitMarginReq-S 2312.50 USD DU58265<br />updateAccountValue: Leverage-S 0.01 DU58265<br />updateAccountValue: LookAheadAvailableFunds 984986.06 USD DU58265<br />updateAccountValue: LookAheadAvailableFunds-S 984986.06 USD DU58265<br />updateAccountValue: LookAheadExcessLiquidity 984986.06 USD DU58265<br />updateAccountValue: LookAheadExcessLiquidity-S 984986.06 USD DU58265<br />updateAccountValue: LookAheadInitMarginReq 2312.50 USD DU58265<br />updateAccountValue: LookAheadInitMarginReq-S 2312.50 USD DU58265<br />updateAccountValue: LookAheadMaintMarginReq 2312.50 USD DU58265<br />updateAccountValue: LookAheadMaintMarginReq-S 2312.50 USD DU58265<br />updateAccountValue: LookAheadNextChange 0 DU58265<br />updateAccountValue: MaintMarginReq 2312.50 USD DU58265<br />updateAccountValue: MaintMarginReq-S 2312.50 USD DU58265<br />updateAccountValue: NetLiquidation 987338.56 USD DU58265<br />updateAccountValue: NetLiquidation-S 987338.56 USD DU58265<br />updateAccountValue: NetLiquidationByCurrency 987338.56 BASE DU58265<br />updateAccountValue: NetLiquidationByCurrency 987338.56 USD DU58265<br />updateAccountValue: OptionMarketValue 40.00 BASE DU58265<br />updateAccountValue: OptionMarketValue 40.00 USD DU58265<br />updateAccountValue: PNL true DU58265<br />updateAccountValue: PreviousDayEquityWithLoanValue 987286.06 USD DU58265<br />updateAccountValue: PreviousDayEquityWithLoanValue-S 987286.06 USD DU58265<br />updateAccountValue: RealizedPnL 0.00 BASE DU58265<br />updateAccountValue: RealizedPnL 0.00 USD DU58265<br />updateAccountValue: RegTEquity 987298.56 USD DU58265<br />updateAccountValue: RegTEquity-S 987298.56 USD DU58265<br />updateAccountValue: RegTMargin 4625.00 USD DU58265<br />updateAccountValue: RegTMargin-S 4625.00 USD DU58265<br />updateAccountValue: SMA 984635.56 USD DU58265<br />updateAccountValue: SMA-S 984635.56 USD DU58265<br />updateAccountValue: StockMarketValue 9250.00 BASE DU58265<br />updateAccountValue: StockMarketValue 9250.00 USD DU58265<br />updateAccountValue: TotalCashBalance 978048.56 BASE DU58265<br />updateAccountValue: TotalCashBalance 978048.56 USD DU58265<br />updateAccountValue: TotalCashValue 978048.56 USD DU58265<br />updateAccountValue: TotalCashValue-S 978048.56 USD DU58265<br />updateAccountValue: UnalteredInitMarginReq -1.00 USD DU58265<br />updateAccountValue: UnalteredMaintMarginReq -1.00 USD DU58265<br />updateAccountValue: UnrealizedPnL -8555.88 BASE DU58265<br />updateAccountValue: UnrealizedPnL -8555.88 USD DU58265<br />updateAccountValue: WhatIfPMEnabled true DU58265<br /><span style="color: rgb(0, 153, 0);">updatePortfolio: --- <br /> contract_msg:<br /> conid = 7516<br /> symbol = GE<br /> secType = STK<br /> expiry = <br /> strike = 0.0<br /> right = 0<br /> multiplier = <br /> exchange = <br /> primaryExch = ARCA<br /> currency = USD<br /> localSymbol = GE<br /> position 200, mkt price 8.5550003, mkt value 1711.0, avg cost 16.745, unrealized p/l -1638.0, realized p/l 0.0 DU58265</span><br />updateAccountTime: 11:58<br /><span style="color: rgb(0, 0, 153);">updatePortfolio: --- <br /> contract_msg:<br /> conid = 54528396<br /> symbol = RUT<br /> secType = OPT<br /> expiry = 20090319<br /> strike = 490.0<br /> right = C<br /> multiplier = 100<br /> exchange = <br /> primaryExch = AMEX<br /> currency = USD<br /> localSymbol = RUWCA<br /> position 1, mkt price 0.4, mkt value 40.0, avg cost 5180.88, unrealized p/l -5140.88, realized p/l 0.0 DU58265</span><br />updateAccountTime: 11:58<br /><span style="color: rgb(255, 0, 0);">updatePortfolio: --- <br /> contract_msg:<br /> conid = 756733<br /> symbol = SPY<br /> secType = STK<br /> expiry = <br /> strike = 0.0<br /> right = 0<br /> multiplier = <br /> exchange = <br /> primaryExch = ARCA<br /> currency = USD<br /> localSymbol = SPY<br /> position 100, mkt price 75.3899994, mkt value 7539.0, avg cost 93.16, unrealized p/l -1777.0, realized p/l 0.0 DU58265</span><br />updateAccountTime: 11:58<br />updateAccountTime: 11:58<br />disconnected<br /><br /></code><br /><br /><hr/><br /><span style="font-weight:bold;">03/09/09 Update</span><br /><a href="http://tradingbot.blogspot.com/2009/03/upgrade-to-api-v-960.html"><br />Upgrading to API v. 9.60</a> requires addition of these new callback methods.<br /><br /><pre class="ruby:firstline[150]" name="code"> <br /> # ---------------------------------------------------<br /> # new callback methods added in API 9.60<br /> # ---------------------------------------------------<br /> def openOrderEnd() # invoked when all orders are sent to a client as a response to reqOpenOrders()<br /> end<br /><br /> def accountDownloadEnd(account_name) #invoked when a complete snapshot of an account state is sent to a client as a response to reqAccountUpdates()<br /> end<br /><br /> def execDetailsEnd( req_id) # invoked when all executions are sent to a client as a response to reqExecutions()<br /> end<br /><br /> def deltaNeutralValidation(req_id, under_comp) # for Delta-Neutral DN RFQ<br /> end<br /></pre>System Traderhttp://www.blogger.com/profile/11376312889397858168noreply@blogger.com0tag:blogger.com,1999:blog-6568584506474235321.post-12485329665813115232009-02-16T09:29:00.013-05:002009-03-09T22:34:27.751-04:00Proof Of Concept 8 of 8b : Historical QuoteThe process of retrieving historical data is very much like getting market data. A call to reqHistoricalData() is made with specifications on how far back to request data and how that data is to be seen (also known as bar size settings). A call back function called historicalData() is used to display the data returned. Going into this, I thought it would be a snap to implement, but I encountered some technical difficulties. The problem this time is that Java code works, but JRuby code doesn't - results returned to historicalData() are incorrect. <br /><br />Here is the Java version. Note that it's pretty straight forward. HistoricalQuote implements EWrapper interface and historicalData() is where the call back happens:<br /><pre class="java" name="code"><br />package ibjava;<br /><br />import ib.client.*;<br /><br /><br />public class HistoricalQuote implements EWrapper {<br /><br /> public HistoricalQuote() throws InterruptedException {<br /> int i = 0;<br /> EClientSocket m_client = new EClientSocket(this);<br /> m_client.eConnect("127.0.0.1",7496, 0);<br /> System.out.println("Connected.");<br /><br /> // preparing to quote SPY<br /> Contract contract;<br /> contract = new Contract();<br /><br /> contract.m_symbol = "SPY";<br /> contract.m_secType = "STK";<br /> contract.m_expiry = "";<br /> contract.m_right = "";<br /> contract.m_multiplier = "";<br /> contract.m_exchange = "SMART";<br /> contract.m_primaryExch = "";<br /> contract.m_currency = "USD";<br /> contract.m_localSymbol = "";<br /> // submit historical data for SPY.<br /> // look for 3 days worth of trades ending on 02/13/2009,<br /> // grouping results to 1 day at a time<br /> // results will come in through historicalData() methods<br /> m_client.reqHistoricalData(2,<br /> contract,<br /> "20090213 20:00:0", //yyyymmdd hh:mm:ss<br /> "3 D", // duration,<br /> "1 DAY", // barSizeSetting,<br /> "TRADES", // whatToShow,<br /> 1, // useRTH,<br /> 1) ; // formatDate);<br /> Thread.sleep(4000); // sleep for 4 seconds<br /><br /> m_client.eDisconnect();<br /> System.out.println("Disconnected.");<br /> }<br /><br /> public static void main(String[] args) throws InterruptedException {<br /> System.out.println("Hello");<br /> HistoricalQuote myBot= new HistoricalQuote();<br /> }<br /><br /> // ---------------------------------------------------<br /> // -- methods from EWrapper -- must be declared<br /> // ---------------------------------------------------<br /> public void tickPrice( int tickerId, int field, double price, int canAutoExecute){}<br /> public void tickSize( int tickerId, int field, int size){}<br /> public void tickOptionComputation( int tickerId, int field, double impliedVol, double delta, double modelPrice, double pvDividend){}<br /> public void tickGeneric(int tickerId, int tickType, double value){}<br /> public void tickString(int tickerId, int tickType, String value){}<br /> public void tickEFP(int tickerId, int tickType, double basisPoints, String formattedBasisPoints, double impliedFuture, int holdDays, String futureExpiry, double dividendImpact, double dividendsToExpiry){}<br /> public void orderStatus( int orderId, String status, int filled, int remaining, double avgFillPrice, int permId, int parentId, double lastFillPrice, int clientId, String whyHeld){}<br /> public void openOrder( int orderId, Contract contract, Order order, OrderState orderState){}<br /> public void updateAccountValue(String key, String value, String currency, String accountName){}<br /> public void updatePortfolio(Contract contract, int position, double marketPrice, double marketValue, double averageCost, double unrealizedPNL, double realizedPNL, String accountName){}<br /> public void updateAccountTime(String timeStamp){}<br /> public void nextValidId( int orderId){}<br /> public void contractDetails(int reqId, ContractDetails contractDetails){}<br /> public void bondContractDetails(int reqId, ContractDetails contractDetails){}<br /> public void contractDetailsEnd(int reqId){}<br /> public void execDetails( int orderId, Contract contract, Execution execution){}<br /> public void updateMktDepth( int tickerId, int position, int operation, int side, double price, int size){}<br /> public void updateMktDepthL2( int tickerId, int position, String marketMaker, int operation, int side, double price, int size){}<br /> public void updateNewsBulletin( int msgId, int msgType, String message, String origExchange){}<br /> public void managedAccounts( String accountsList){}<br /> public void receiveFA(int faDataType, String xml){}<br /><br /> public void historicalData(int reqId, String date, double open, double high, double low, double close, int volume, int count, double WAP, boolean hasGaps)<br /> { System.out.println("Java> id=" + reqId + " date = " + date +<br /> " open=" + open + " high=" + high +<br /> " low=" + low + " close=" + close +<br /> " volume=" + volume + " count=" + count +<br /> " WAP=" + WAP + " hasGaps=" + hasGaps);<br /> }<br /><br /> public void scannerParameters(String xml){}<br /> public void scannerData(int reqId, int rank, ContractDetails contractDetails, String distance, String benchmark, String projection, String legsStr){}<br /> public void scannerDataEnd(int reqId){}<br /> public void realtimeBar(int reqId, long time, double open, double high, double low, double close, long volume, double wap, int count){}<br /> public void currentTime(long time){}<br /> public void fundamentalData(int reqId, String data){}<br /><br /> // ---------------------------------------------------<br /> // -- methods from AnyWrapper -- must be declared<br /> // ---------------------------------------------------<br /><br /> public void connectionClosed() {<br /> System.out.println(" [API.connectionClosed] Closed connection with TWS");<br /> }<br /> public void error(String str) {<br /> System.out.println(" [API.msg1] " + str);<br /> }<br /><br /> public void error(int one, int two, String str) {<br /> System.out.println(" [API.msg2] " + str + " {" + one + ", " + two + "}");<br /> }<br /><br /> public void error(Exception e) {<br /> System.out.println(" [API.msg3] " + e.getMessage());<br /> }<br /><br />}<br /></pre><br /><br />Running this produces the following output:<br /><code><br />Hello<br />Server Version:43<br />TWS Time at connection:20090215 22:48:29 EST<br />Connected.<br /> [API.msg2] Market data farm connection is OK:usopt {-1, 2104}<br /> [API.msg2] Market data farm connection is OK:usfarm {-1, 2104}<br /> [API.msg2] HMDS data farm connection is OK:ushmds2a {-1, 2106}<br />Java> id=2 date = 20090211 open=83.44 high=84.05 low=82.4 close=83.53 volume=2950030 count=855129 WAP=83.4 hasGaps=false<br />Java> id=2 date = 20090212 open=82.17 high=84.1 low=81.05 close=83.98 volume=4193652 count=1201155 WAP=82.25 hasGaps=false<br />Java> id=2 date = 20090213 open=83.55 high=84.24 low=82.35 close=82.46 volume=2681140 count=795253 WAP=83.44 hasGaps=false<br />Java> id=2 date = finished-20090210 20:00:00-20090213 20:00:00 open=-1.0 high=-1.0 low=-1.0 close=-1.0 volume=-1 count=-1 WAP=-1.0 hasGaps=false<br />Disconnected.</code><br /><br />Everything is ok at this point. I checked open, high, low, close for each day and the data is accurate. <br />Implementing this same code in JRuby is as follows:<br /><br /><pre class="ruby" name="code"><br />require 'java'<br />require 'TwsApi.jar'<br /><br />include_class 'ib.client.EClientSocket'<br />include_class 'ib.client.Contract'<br />include_class 'ib.client.AnyWrapper'<br />include_class 'ib.client.EWrapper'<br /><br /><br />class TestHistData<br /> include EWrapper #note "class TestMktData < EWrapper" syntax is no longer supported in JRuby 1.0<br /><br /> @mySocket<br /> <br /> def initialize<br /> @mySocket = EClientSocket.new(self)<br /> end<br /><br /> def eConnect<br /> @mySocket.eConnect("127.0.0.1", 7496, 0)<br /> end<br /><br /> def requestHistStockQuote(id, ticker)<br /> # --------------------------------<br /> myContract = Contract.new<br /> myContract.m_symbol = ticker<br /> myContract.m_secType = 'STK'<br /> myContract.m_exchange = 'SMART'<br /> myContract.m_currency = 'USD'<br /> request_id = id<br /> ticklist = ''<br /> # submit historical data for ticker<br /> # look for 3 days worth of trades ending on 02/13/2009,<br /> # grouping results to 1 day at a time<br /> # results will come in through historicalData() methods<br /> @mySocket.reqHistoricalData(request_id,<br /> myContract,<br /> '20090213 20:00:0', #yyyymmdd hh:mm:ss<br /> '3 D', # duration,<br /> '1 DAY', # barSizeSetting,<br /> 'TRADES', # whatToShow,<br /> 1, # useRTH,<br /> 1) ; # formatDate);<br /> end<br /><br /> def eDisconnect<br /> @mySocket.eDisconnect<br /> end<br /><br /><br /> #///////////////////////////////////////////////////////////////////////<br /> #// Interface methods<br /> #///////////////////////////////////////////////////////////////////////<br /><br /> def tickPrice( ticker_id, field, price, can_auto_execute)<br /> end<br /><br /> def tickSize( ticker_id, field, size)<br /> end<br /><br /> def tickOptionComputation( ticker_id, field, implied_vol,<br /> delta, model_price, pv_dividend)<br /> end<br /><br /> def tickGeneric( ticker_id, tick_type, value)<br /> end<br /><br /> def tickString( ticker_id, tick_type, value)<br /> end<br /><br /> def tickEFP( ticker_id, tick_type, basis_points,<br /> formatted_basis_points, implied_future, hold_days,<br /> future_expiry, dividend_impact, dividends_to_expiry)<br /> end<br /><br /> def orderStatus( order_id, status, filled, remaining,<br /> avg_fill_price, perm_id, parent_id, last_fill_price,<br /> client_id, why_held)<br /> end<br /><br /> def openOrder( order_id, contract, order, order_state)<br /> end<br /><br /> def updateAccountValue( key, value, currency, account_name)<br /> end<br /><br /> def updatePortfolio( contract, position, market_price, market_value,<br /> average_cost, unrealized_pnl, realized_pnl, account_name)<br /> end<br /><br /> def updateAccountTime( time_stamp)<br /> end<br /><br /> def nextValidId( order_id)<br /> end<br /><br /> def contractDetails( req_id, contract_details)<br /> end<br /><br /> def bondContractDetails( req_id, contract_details)<br /> end<br /><br /> def contractDetailsEnd( req_id)<br /> end<br /><br /> def execDetails( order_id, contract, execution)<br /> end<br /><br /> def updateMktDepth( ticker_id, position, operation, side, price, size)<br /> end<br /><br /> def updateMktDepthL2( ticker_id, position, market_maker, operation,<br /> side, price, size)<br /> end<br /><br /> def updateNewsBulletin( msg_id, msgType, message, orig_exchange)<br /> end<br /><br /> def managedAccounts( accounts_list)<br /> end<br /><br /> def receiveFA( fa_data_type, xml)<br /> end<br /> <br /> def historicalData( req_id, date, open, high, low,<br /> close, volume, count, pWAP, has_gaps)<br /> # using straight printing<br /> puts 'ruby> id=' + req_id.to_s + ' date = ' + date +<br /> ' open=' + open.to_s + ' high=' + high.to_s +<br /> ' low=' + low.to_s + ' close=' + close.to_s +<br /> ' volume=' + volume.to_s + ' count=' + count.to_s +<br /> ' WAP=' + pWAP.to_s + ' hasGaps=' + has_gaps.to_s<br /> end<br /><br /> def scannerParameters( xml)<br /> end<br /><br /> def scannerData( req_id, rank, contract_details, distance,<br /> benchmark, projection, legs_str)<br /> end<br /><br /> def scannerDataEnd( req_id)<br /> end<br /><br /> def realtimeBar( req_id, time, open, high, low, close, volume, wap, count)<br /> end<br /><br /> def currentTime( time)<br /> end<br /><br /> def fundamentalData( req_id, data)<br /> end<br /><br /> # ---------------------------------------------------<br /> # -- methods from AnyWrapper -- must be declared<br /> # ---------------------------------------------------<br /><br /> def connectionClosed()<br /> puts ' [API.connectionClosed] Closed connection with TWS'<br /> end<br /> <br /> def error(err_obj_str)<br /> puts ' [API.msg1] ' + err_obj_str if err_obj_str.is_a?(String)<br /> puts ' [API.msg3] ' + err_obj_str.getMessage() if err_obj_str.is_a?(Exception)<br /> end<br /><br /> def error(one, two, str) <br /> puts ' [API.msg2] ' + str + ' {' + one.to_s + ', ' + two.to_s + '}'<br /> end<br /><br />end # class TestHistData<br /><br /># *********************************************************************************************<br /># Running the code<br /># *********************************************************************************************<br /> x = TestHistData.new()<br /> begin<br /> x.eConnect<br /> puts 'connected ...'<br /> req_id = 0<br /> x.requestHistStockQuote(req_id += 1, 'SPY') # stock<br /> puts 'wait 5 sec'<br /> sleep(5)<br /> x.eDisconnect<br /> <br /> rescue Exception => e<br /> puts 'can not connect'<br /> puts 'Exception' + e.message<br /> x.eDisconnect<br /> end<br /></pre><br /><br />Running it produces the following output :<br /><code><br />Server Version:43TWS Time at connection:20090215 23:08:09 ESTconnected ...<br /> [API.msg2] Market data farm connection is OK:usopt {-1, 2104}<br />wait 5 sec<br /> [API.msg2] Market data farm connection is OK:usfarm {-1, 2104}<br /> [API.msg2] HMDS data farm connection is OK:ushmds2a {-1, 2106}<br />ruby> id=1 date = 20090211 open=83.44 high=-1.783545891855626e+259 low=84.05 close=4.667261648644354e-62 volume=1079286169 count=-1717986918 WAP=83.53 hasGaps=true<br />ruby> id=1 date = 20090212 open=82.17 high=1.9991592822007892e+37 low=84.1 close=1.9035983734140052e+185 volume=1079264051 count=858993459 WAP=83.98 hasGaps=true<br />ruby> id=1 date = 20090213 open=83.55 high=4.667261648670389e-62 low=84.24 close=2.262040992568159e-111 volume=1079285350 count=1717986918 WAP=82.46 hasGaps=true<br />ruby> id=1 date = finished-20090210 20:00:00-20090213 20:00:00 open=-1.0 high=1.590978778e-314 low=-1.0 close=1.590978778e-314 volume=-1074790400 count=0 WAP=-1.0 hasGaps=false<br /><br /></code><br />Compare this with Java version's output (same as displayed earlier on the post, I'm just repeating it here to make it more convenient to compare).<br /><code><br />Java> id=2 date = 20090211 open=83.44 high=84.05 low=82.4 close=83.53 volume=2950030 count=855129 WAP=83.4 hasGaps=false<br />Java> id=2 date = 20090212 open=82.17 high=84.1 low=81.05 close=83.98 volume=4193652 count=1201155 WAP=82.25 hasGaps=false<br />Java> id=2 date = 20090213 open=83.55 high=84.24 low=82.35 close=82.46 volume=2681140 count=795253 WAP=83.44 hasGaps=false<br />Java> id=2 date = finished-20090210 20:00:00-20090213 20:00:00 open=-1.0 high=-1.0 low=-1.0 close=-1.0 volume=-1 count=-1 WAP=-1.0 hasGaps=false<br /></code><br /><br />Note that id, date, and open values are the same for both Java and JRuby versions. From then on, beginning with high value, the numbers in the JRuby version just look wrong. What's interesting is that JRuby's low value matches up to Java's high value. Maybe there's something wrong with the way JRuby handles all these Java parameters with double and int datatypes? <br /> <br /><hr><br /><span style="font-weight:bold;"><br />02/17/09 Update </span><br /> <br />After some research, I suspect that the incorrect double values passed into historicalData() are a JRuby bug. It appears substantially similar to <a href="http://jira.codehaus.org/browse/JRUBY-2993">a reported bug</a> that was fixed in version 1.1.5.<br /><br />I have opened an issue on the JRuby site <a href="http://jira.codehaus.org/browse/JRUBY-3409">http://jira.codehaus.org/browse/JRUBY-3409</a> and will wait to see what they say...<br /><br /><hr><br /><span style="font-weight:bold;"><br />02/22/09 08:00 Update </span><br /><br />There has been no progress on my issue on the JRuby site. To make it easier for the developers there to see the problem, I have reconstructed the error using very simple classes and uploaded the files to their site. Hopefully this will give them what they need to come up with a fix...<br /><br /><hr><br /><span style="font-weight:bold;"><br />02/22/09 22:40 Update </span><br /><br /><span style="font-weight:bold;">Problem has been solved!</span> The issue has been on my end and I feel bad about having wasted all this time, but hey, lesson learned. The problem was that I had been using NetBeans IDE to perform my development and the JRuby which comes bundled with it is an old version (v 1.1.4). Although I had <a href="http://tradingbot.blogspot.com/2009/01/proof-of-concet-6-of-7-install-jruby.html">downloaded and installed JRuby v. 1.1.6</a>, this was NOT the version being run from NetBeans. It was definitely a surprising moment when I discovered the same JRuby code fails when running in NetBeans (JRuby v. 1.1.4), but succeeds when I ran it at the command prompt (JRuby 1.1.6). I have closed the issue on the JRuby site explaining the situation. <br /><br />To configure NetBeans to use JRuby 1.1.6, go to Tools, Ruby Platforms menu. It will open a new window showing current JRuby version. Here it is showing that it's version 1.1.4:<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLqCVNziLmM8Lieq0fdvVLJrVnofRLAI0ZDXuU9TKEuJTcGbJWS93t_aYEBswsUiOPebC2Jyy4MnfvgiEaNwxi3bP3-5mHgYTedXmVzybZO4Xp6oyFJJ1Gwz_RME2F0exUORsYNutPw-AT/s1600-h/nb_ruby_mgr1.JPG"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 194px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLqCVNziLmM8Lieq0fdvVLJrVnofRLAI0ZDXuU9TKEuJTcGbJWS93t_aYEBswsUiOPebC2Jyy4MnfvgiEaNwxi3bP3-5mHgYTedXmVzybZO4Xp6oyFJJ1Gwz_RME2F0exUORsYNutPw-AT/s320/nb_ruby_mgr1.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5305833545207176514" /></a><br /><br />Click on Add Platform and select 1.1.6 version of jruby.bat that was previously installed. This version should now appear on the Ruby Platform Manager:<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguGaez2tMuvXDJvmo7n265W58lpW35hasl72KscOYEI2toW6Ngk41G4fg-7YitrI4wLTeA40h2jvoxcSZTzZZlCtTPC8TE8j8tlEjlrrcYg2lURvC_i_PSS3-3rQwFKezoL3qYkbT0OsRX/s1600-h/nb_ruby_mgr2.JPG"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 195px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguGaez2tMuvXDJvmo7n265W58lpW35hasl72KscOYEI2toW6Ngk41G4fg-7YitrI4wLTeA40h2jvoxcSZTzZZlCtTPC8TE8j8tlEjlrrcYg2lURvC_i_PSS3-3rQwFKezoL3qYkbT0OsRX/s320/nb_ruby_mgr2.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5305833723817710098" /></a><br /><br />Last step is to change the project property by right clicking on the project in Projects window and select Properties. Click on Run in Categories, and change Ruby Platform to 1.1.6.<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjljDcPL01y7hjWtou41ZTH9qKse4SkzTFeu-j_TNj4mTOQ25wREBqWY6uquSZP4QC4RX2ka4RaNjv763_bd579gaT96mn6pDkk2waEkrM8lQKTU-0x7XtFgKPpUJkugbp2KALzz_zQQBWA/s1600-h/nb_ruby_mgr3.JPG"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 195px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjljDcPL01y7hjWtou41ZTH9qKse4SkzTFeu-j_TNj4mTOQ25wREBqWY6uquSZP4QC4RX2ka4RaNjv763_bd579gaT96mn6pDkk2waEkrM8lQKTU-0x7XtFgKPpUJkugbp2KALzz_zQQBWA/s320/nb_ruby_mgr3.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5305833844563496354" /></a><br /><br />Finally, here is the JRuby output with correct historical quote:<br /><code><br />Server Version:43<br /><br />TWS Time at connection:20090222 19:41:12 EST<br /><br />connected ...<br />wait 5 sec<br /> [API.msg2] Market data farm connection is OK:usopt {-1, 2104}<br /> [API.msg2] Market data farm connection is OK:usfarm {-1, 2104}<br /> [API.msg2] HMDS data farm connection is OK:ushmds2a {-1, 2106}<br />ruby> id=1 date = 20090211 open=83.44 high=84.05 low=82.4 close=83.53 volume=2950030 count=855129 WAP=83.4 hasGaps=false<br />ruby> id=1 date = 20090212 open=82.17 high=84.1 low=81.05 close=83.98 volume=4193652 count=1201155 WAP=82.25 hasGaps=false<br />ruby> id=1 date = 20090213 open=83.55 high=84.24 low=82.35 close=82.46 volume=2681140 count=795253 WAP=83.44 hasGaps=false<br />ruby> id=1 date = finished-20090210 20:00:00-20090213 20:00:00 open=-1.0 high=-1.0 low=-1.0 close=-1.0 volume=-1 count=-1 WAP=-1.0 hasGaps=false<br /></code><br /><hr/><br /><span style="font-weight:bold;">03/09/09 Update</span><br /><a href="http://tradingbot.blogspot.com/2009/03/upgrade-to-api-v-960.html"><br />Upgrading to API v. 9.60</a> requires addition of these new callback methods.<br /><br /><pre class="ruby:firstline[153]" name="code"> <br /> # ---------------------------------------------------<br /> # new callback methods added in API 9.60<br /> # ---------------------------------------------------<br /> def openOrderEnd() # invoked when all orders are sent to a client as a response to reqOpenOrders()<br /> end<br /><br /> def accountDownloadEnd(account_name) #invoked when a complete snapshot of an account state is sent to a client as a response to reqAccountUpdates()<br /> end<br /><br /> def execDetailsEnd( req_id) # invoked when all executions are sent to a client as a response to reqExecutions()<br /> end<br /><br /> def deltaNeutralValidation(req_id, under_comp) # for Delta-Neutral DN RFQ<br /> end<br /></pre>System Traderhttp://www.blogger.com/profile/11376312889397858168noreply@blogger.com0tag:blogger.com,1999:blog-6568584506474235321.post-26709612543567206592009-02-13T22:14:00.011-05:002009-03-09T22:29:36.694-04:00Proof Of Concept 8 of 8a : Market QuoteLessons learned from going native have paid off. I was able to fix JRuby errors and use TWS API's market quote functionality. As I have written in the prior post, errors in the JRuby code were due to the fact that interface methods from AnyWrapper were not implemented. Once I implemented connectionClosed() and overloaded error() methods, errors immediately went away . <br /><br />Obtaining market quote is performed with reqMktData() method. I was able to successfully get quotes for ETF, stock, index, and options. The code to request SPY ETF is as follows:<br /><br /><pre class="ruby" name="code"><br />require 'java'<br />require 'TwsApi.jar'<br /><br />include_class 'ib.client.EClientSocket'<br />include_class 'ib.client.Contract'<br />include_class 'ib.client.AnyWrapper'<br />include_class 'ib.client.EWrapper'<br /><br />class TestMktData<br /> include EWrapper #note "class TestMktData < EWrapper" syntax is no longer supported in JRuby 1.0<br /><br /> @mySocket<br /> <br /> def initialize<br /> puts 'init'<br /> @mySocket = EClientSocket.new(self)<br /> end<br /><br /> def eConnect<br /> @mySocket.eConnect("127.0.0.1", 7496, 0)<br /> end<br /><br /> def requestStockQuote(id, ticker)<br /> # --------------------------------<br /> myContract = Contract.new<br /> myContract.m_symbol = ticker<br /> myContract.m_secType = 'STK'<br /> myContract.m_exchange = 'SMART'<br /> myContract.m_currency = 'USD'<br /> request_id = id<br /> ticklist = ''<br /><br /> puts '>requestStockQuote - calling reqMktData id='+id.to_s+', ticker='+ticker<br /> @mySocket.reqMktData(request_id, myContract, ticklist, true)<br /> end<br /><br /> def requestIdxQuote(id, ticker)<br /> # --------------------------------<br /> myContract = Contract.new<br /> myContract.m_symbol = ticker<br /> myContract.m_secType = 'IND'<br /> myContract.m_exchange = 'CBOE' <br /> myContract.m_currency = 'USD'<br /> request_id = id<br /> ticklist = ''<br /><br /> puts '>requestIdxQuote - calling reqMktData id='+id.to_s+', ticker='+ticker<br /> @mySocket.reqMktData(request_id, myContract, ticklist, true)<br /> end<br /><br /> # expiry - string format YYYYMM<br /> # strike - number<br /> # call_put - CALL or PUT<br /> def requestOptionQuote(id, ticker, expiry, strike, call_put)<br /> # --------------------------------<br /> myContract = Contract.new<br /> myContract.m_symbol = ticker<br /> myContract.m_secType = 'OPT'<br /> myContract.m_exchange = 'SMART'<br /> myContract.m_currency = 'USD'<br /> # options specific below ---<br /> myContract.m_expiry = expiry # string format YYYYMM<br /> myContract.m_right = call_put # CALL, PUT<br /> myContract.m_strike = strike # number<br /> request_id = id<br /> ticklist = ''<br /><br /> puts '>requestOptionQuote - calling reqMktData id='+id.to_s+', ticker='+ticker+', '+expiry+', '+strike.to_s+', '+call_put<br /> @mySocket.reqMktData(request_id, myContract, ticklist, true)<br /> end<br /><br /><br /> def eDisconnect<br /> @mySocket.eDisconnect<br /> end<br /><br /><br /> #///////////////////////////////////////////////////////////////////////<br /> #// Interface methods<br /> #///////////////////////////////////////////////////////////////////////<br /><br /> def tickPrice( ticker_id, field, price, can_auto_execute)<br /> puts 'tickPrice() Price Update: Id: ' + ticker_id.to_s + ' Type: '+field.to_s+' Price: ' + price.to_s<br /> end<br /><br /> def tickSize( ticker_id, field, size)<br /> puts 'tickSize() Volume Update: Id: '+ticker_id.to_s+' Type: '+field.to_s+' Size: '+size.to_s<br /> end<br /><br /> def tickOptionComputation( ticker_id, field, implied_vol,<br /> delta, model_price, pv_dividend)<br /> puts 'tickOptionComputation() Update: Id:'+ticker_id.to_s+' Type: '+field.to_s+' IV: '+implied_vol.to_s+' Delta: '+delta.to_s<br /> end<br /><br /> def tickGeneric( ticker_id, tick_type, value)<br /> puts 'tickGeneric() Update: Id: '+ticker_id.to_s+' Type: '+tick_type.to_s+' Value: '+value.to_s<br /> end<br /><br /> def tickString( ticker_id, tick_type, value)<br /> puts 'tickString() Update: Id: '+ticker_id.to_s+' Type: '+tick_type.to_s+' Value: '+value<br /> end<br /><br /> def tickEFP( ticker_id, tick_type, basis_points,<br /> formatted_basis_points, implied_future, hold_days,<br /> future_expiry, dividend_impact, dividends_to_expiry)<br /> end<br /><br /> def orderStatus( order_id, status, filled, remaining,<br /> avg_fill_price, perm_id, parent_id, last_fill_price,<br /> client_id, why_held)<br /> end<br /><br /> def openOrder( order_id, contract, order, order_state)<br /> end<br /><br /> def updateAccountValue( key, value, currency, account_name)<br /> end<br /><br /> def updatePortfolio( contract, position, market_price, market_value,<br /> average_cost, unrealized_pnl, realized_pnl, account_name)<br /> end<br /><br /> def updateAccountTime( time_stamp)<br /> end<br /><br /> def nextValidId( order_id)<br /> end<br /><br /> def contractDetails( req_id, contract_details)<br /> end<br /><br /> def bondContractDetails( req_id, contract_details)<br /> end<br /><br /> def contractDetailsEnd( req_id)<br /> end<br /><br /> def execDetails( order_id, contract, execution)<br /> end<br /><br /> def updateMktDepth( ticker_id, position, operation, side, price, size)<br /> end<br /><br /> def updateMktDepthL2( ticker_id, position, market_maker, operation,<br /> side, price, size)<br /> end<br /><br /> def updateNewsBulletin( msg_id, msgType, message, orig_exchange)<br /> end<br /><br /> def managedAccounts( accounts_list)<br /> end<br /><br /> def receiveFA( fa_data_type, xml)<br /> end<br /><br /> def historicalData( req_id, date, open, high, low,<br /> close, volume, count, eWAP, has_gaps)<br /> end<br /><br /> def scannerParameters( xml)<br /> end<br /><br /> def scannerData( req_id, rank, contract_details, distance,<br /> benchmark, projection, legs_str)<br /> end<br /><br /> def scannerDataEnd( req_id)<br /> end<br /><br /> def realtimeBar( req_id, time, open, high, low, close, volume, wap, count)<br /> end<br /><br /> def currentTime( time)<br /> end<br /><br /> def fundamentalData( req_id, data)<br /> end<br /><br /> # ---------------------------------------------------<br /> # -- methods from AnyWrapper -- must be declared<br /> # ---------------------------------------------------<br /><br /> def connectionClosed()<br /> puts ' [API.connectionClosed] Closed connection with TWS'<br /> end<br /> <br /> def error(err_obj_str)<br /> puts ' [API.msg1] ' + err_obj_str if err_obj_str.is_a?(String)<br /> puts ' [API.msg3] ' + err_obj_str.getMessage() if err_obj_str.is_a?(Exception)<br /> end<br /><br /> def error(one, two, str) <br /> puts ' [API.msg2] ' + str + ' {' + one.to_s + ', ' + two.to_s + '}'<br /> end<br /><br />end # class TestMktData<br /><br /># *********************************************************************************************<br /># Running the code<br /># *********************************************************************************************<br /> x = TestMktData.new()<br /><br /> begin<br /> x.eConnect<br /> puts 'connected ...'<br /> req_id = 0<br /><br /> x.requestStockQuote(req_id += 1, 'SPY') # ETF<br /><br /> puts 'wait 5 sec'<br /> sleep(5)<br /> x.eDisconnect<br /> puts 'disconnected'<br /> rescue Exception => e<br /> puts 'can not connect'<br /> puts 'Exception' + e.message<br /> x.eDisconnect<br /> end<br /></pre><br />Lines 184-195: contain methods that were missing from my initial code. Note that in the Java version, there were actually three error() methods, with two of them having just one parameter (of different types). Since Ruby is a loosely typed language, creating two error() methods with one parameter will generate an error. To get around this, Ruby's version of error() actually handles the job of Java's two methods. Look at lines 189-190 to see how the parameter's types are determined.<br />Line 209: illustrates the calling method to request quote for SPY exchange traded fund<br /><br />Running this produces the following output:<br /><code><br />Server Version:43<br />TWS Time at connection:20090213 14:13:43 EST<br />connected ...<br />>requestStockQuote - calling reqMktData id=1, ticker=SPY [API.msg2] Market data farm connection is OK:usopt {-1, 2104}<br /><br />wait 5 sec<br /> [API.msg2] Market data farm connection is OK:usfarm {-1, 2104}<br />tickPrice() Price Update: Id: 1 Type: 1 Price: 83.68<br />tickSize() Volume Update: Id: 1 Type: 0 Size: 258<br />tickPrice() Price Update: Id: 1 Type: 2 Price: 83.69<br />tickSize() Volume Update: Id: 1 Type: 3 Size: 68<br />tickPrice() Price Update: Id: 1 Type: 4 Price: 83.69<br />tickSize() Volume Update: Id: 1 Type: 5 Size: 1<br />tickSize() Volume Update: Id: 1 Type: 8 Size: 1881804<br />tickPrice() Price Update: Id: 1 Type: 6 Price: 84.24<br />tickPrice() Price Update: Id: 1 Type: 7 Price: 82.75<br />tickPrice() Price Update: Id: 1 Type: 9 Price: 83.66<br />tickPrice() Price Update: Id: 1 Type: 14 Price: 83.55<br />tickString() Update: Id: 1 Type: 45 Value: 1234552423<br />disconnected<br /></code> <br /><br /><br />Line 209 can also be modified to request quote for American Express<br /><pre class="ruby:firstline[209]" name="code"><br /> x.requestStockQuote(req_id += 1, 'AXP') # stock<br /></pre><br />produces the following output:<br /><code><br />Server Version:43TWS Time at connection:20090213 22:13:09 ESTconnected ...<br />>requestStockQuote - calling reqMktData id=1, ticker=AXP<br />wait 5 sec<br /> [API.msg2] Market data farm connection is OK:usopt {-1, 2104}<br /> [API.msg2] Market data farm connection is OK:usfarm {-1, 2104}<br />tickSize() Volume Update: Id: 1 Type: 8 Size: 0<br />tickString() Update: Id: 1 Type: 45 Value: 1234562108<br />tickPrice() Price Update: Id: 1 Type: 4 Price: 15.69<br />tickSize() Volume Update: Id: 1 Type: 5 Size: 1<br />tickPrice() Price Update: Id: 1 Type: 6 Price: 16.33<br />tickPrice() Price Update: Id: 1 Type: 7 Price: 15.58<br />tickPrice() Price Update: Id: 1 Type: 9 Price: 16.19<br />tickPrice() Price Update: Id: 1 Type: 14 Price: 16.05<br />tickPrice() Price Update: Id: 1 Type: 1 Price: 15.56<br />tickSize() Volume Update: Id: 1 Type: 0 Size: 2<br />tickPrice() Price Update: Id: 1 Type: 2 Price: 15.95<br />tickSize() Volume Update: Id: 1 Type: 3 Size: 1<br />disconnected<br /></code><br /><br /><br />To request Russell 2000 small cap index from Chicago Board Options Exchange <br /><pre class="ruby:firstline[209]" name="code"><br /> x.requestIdxQuote(req_id += 1, 'RUT') # index<br /></pre><br />produces the following output:<br /><code><br />Server Version:43<br />TWS Time at connection:20090213 14:14:19 EST<br />connected ...<br />>requestIdxQuote - calling reqMktData id=1, ticker=RUT<br /> [API.msg2] Market data farm connection is OK:usopt {-1, 2104}<br />wait 5 sec<br /> [API.msg2] Market data farm connection is OK:usfarm {-1, 2104}<br />tickPrice() Price Update: Id: 1 Type: 4 Price: 451.93<br />tickSize() Volume Update: Id: 1 Type: 5 Size: 0<br />tickSize() Volume Update: Id: 1 Type: 8 Size: 0<br />tickPrice() Price Update: Id: 1 Type: 6 Price: 0.0<br />tickPrice() Price Update: Id: 1 Type: 7 Price: 0.0<br />tickPrice() Price Update: Id: 1 Type: 9 Price: 450.42<br />tickString() Update: Id: 1 Type: 45 Value: 1234552454<br />disconnected<br /></code><br /><br /><br />To request options quote for GE September, 2009 15 Call<br /><pre class="ruby:firstline[209]" name="code"><br /> x.requestOptionQuote(req_id += 1, 'GE', '200909', 15, 'CALL' )<br /></pre><br />produces the following output:<br /><code><br />Server Version:43<br />TWS Time at connection:20090213 14:14:44 EST<br />connected ...<br /> [API.msg2] Market data farm connection is OK:usopt {-1, 2104}<br />>requestOptionQuote - calling reqMktData id=1, ticker=GE, 200909, 15, CALL<br /> [API.msg2] Market data farm connection is OK:usfarm {-1, 2104}<br />wait 5 sec<br />tickPrice() Price Update: Id: 1 Type: 1 Price: 0.9<br />tickSize() Volume Update: Id: 1 Type: 0 Size: 1200<br />tickPrice() Price Update: Id: 1 Type: 2 Price: 0.94<br />tickSize() Volume Update: Id: 1 Type: 3 Size: 648<br />tickPrice() Price Update: Id: 1 Type: 4 Price: 0.94<br />tickSize() Volume Update: Id: 1 Type: 5 Size: 9<br />tickSize() Volume Update: Id: 1 Type: 8 Size: 116<br />tickPrice() Price Update: Id: 1 Type: 6 Price: 1.0<br />tickPrice() Price Update: Id: 1 Type: 7 Price: 0.88<br />tickPrice() Price Update: Id: 1 Type: 9 Price: 0.97<br />tickOptionComputation() Update: Id:1 Type: 10 IV: 0.6091767213831093 Delta: 3.3175946061912392e-87<br />tickOptionComputation() Update: Id:1 Type: 11 IV: 0.6211516922231228 Delta: -0.0<br />tickOptionComputation() Update: Id:1 Type: 12 IV: 0.6211516922231228 Delta: -0.0<br />tickString() Update: Id: 1 Type: 32 Value: CIXPB<br />tickString() Update: Id: 1 Type: 33 Value: CIXPB<br />tickString() Update: Id: 1 Type: 45 Value: 1234551366<br />tickString() Update: Id: 1 Type: 33 Value: CIPB<br />tickOptionComputation() Update: Id:1 Type: 10 IV: 0.6091767213831093 Delta: 3.3175946061941284e-87<br />tickOptionComputation() Update: Id:1 Type: 11 IV: 0.6211516922231228 Delta: -0.0<br />tickOptionComputation() Update: Id:1 Type: 12 IV: 0.6211516922231228 Delta: -0.0<br />tickOptionComputation() Update: Id:1 Type: 13 IV: 0.615161765120708 Delta: -0.0<br />tickOptionComputation() Update: Id:1 Type: 10 IV: 0.606880041775532 Delta: -0.0<br />tickOptionComputation() Update: Id:1 Type: 11 IV: 0.6188416326918191 Delta: -5.379410170061903e+268<br />tickOptionComputation() Update: Id:1 Type: 12 IV: 0.6188416326918191 Delta: -5.379410170061903e+268<br />tickOptionComputation() Update: Id:1 Type: 13 IV: 0.6128581636676679 Delta: -0.0<br />disconnected<br /></code><br /><br /><br />To request options quote for Russell 2000 index December, 2009 450 Put<br /><pre class="ruby:firstline[209]" name="code"><br /> x.requestOptionQuote(req_id += 1, 'RUT', '200912', 450, 'PUT' )<br /></pre><br />produces the following output:<br /><code><br />Server Version:43<br />TWS Time at connection:20090213 14:15:15 EST<br />connected ...<br /> [API.msg2] Market data farm connection is OK:usopt {-1, 2104}<br />>requestOptionQuote - calling reqMktData id=1, ticker=RUT, 200912, 450, PUT<br />wait 5 sec<br /> [API.msg2] Market data farm connection is OK:usfarm {-1, 2104}<br />tickPrice() Price Update: Id: 1 Type: 1 Price: 71.2<br />tickSize() Volume Update: Id: 1 Type: 0 Size: 311<br />tickPrice() Price Update: Id: 1 Type: 2 Price: 73.2<br />tickSize() Volume Update: Id: 1 Type: 3 Size: 201<br />tickSize() Volume Update: Id: 1 Type: 8 Size: 0<br />tickPrice() Price Update: Id: 1 Type: 9 Price: 71.85<br />tickOptionComputation() Update: Id:1 Type: 10 IV: 0.4371941774336616 Delta: 1.5035109594887676e+46<br />tickOptionComputation() Update: Id:1 Type: 11 IV: 0.44973728365842186 Delta: -1.7139093657526427e+170<br />tickOptionComputation() Update: Id:1 Type: 12 IV: 0.44126908872009857 Delta: -0.0<br />tickString() Update: Id: 1 Type: 32 Value: CIX<br />tickString() Update: Id: 1 Type: 33 Value: CIB<br />tickOptionComputation() Update: Id:1 Type: 10 IV: 0.4371941774336616 Delta: 1.503510959488021e+46<br />tickOptionComputation() Update: Id:1 Type: 11 IV: 0.44973728365842186 Delta: -1.713909365751876e+170<br />tickOptionComputation() Update: Id:1 Type: 12 IV: 0.44126908872009857 Delta: -0.0<br />tickOptionComputation() Update: Id:1 Type: 13 IV: 0.4434638974508833 Delta: 8.778371095955947e+183<br />tickOptionComputation() Update: Id:1 Type: 10 IV: 0.438042894164516 Delta: 8.899823344244101e-231<br />tickOptionComputation() Update: Id:1 Type: 11 IV: 0.45058242706938006 Delta: -0.0<br />tickOptionComputation() Update: Id:1 Type: 12 IV: 0.4421166435405054 Delta: -0.0<br />tickOptionComputation() Update: Id:1 Type: 13 IV: 0.44431082241353415 Delta: -0.0<br />tickString() Update: Id: 1 Type: 32 Value: CIXB<br />tickString() Update: Id: 1 Type: 32 Value: CIX<br />disconnected<br /></code><br /><hr/><br /><span style="font-weight:bold;">03/09/09 Update</span><br /><a href="http://tradingbot.blogspot.com/2009/03/upgrade-to-api-v-960.html"><br />Upgrading to API v. 9.60</a> requires addition of these new callback methods.<br /><br /><pre class="ruby:firstline[180]" name="code"> <br /> # ---------------------------------------------------<br /> # new callback methods added in API 9.60<br /> # ---------------------------------------------------<br /> def openOrderEnd() # invoked when all orders are sent to a client as a response to reqOpenOrders()<br /> end<br /><br /> def accountDownloadEnd(account_name) #invoked when a complete snapshot of an account state is sent to a client as a response to reqAccountUpdates()<br /> end<br /><br /> def execDetailsEnd( req_id) # invoked when all executions are sent to a client as a response to reqExecutions()<br /> end<br /><br /> def deltaNeutralValidation(req_id, under_comp) # for Delta-Neutral DN RFQ<br /> end<br /></pre>System Traderhttp://www.blogger.com/profile/11376312889397858168noreply@blogger.com0tag:blogger.com,1999:blog-6568584506474235321.post-71048345151956132772009-02-11T22:38:00.008-05:002009-02-11T23:01:17.381-05:00Proof Of Concept 8 of 8 : Yeah!If you keep at it long enough, something's bound to work. I had success today with requesting market quote. Building on my prior experience (ignoring the fake 'error' messages), code was added to instantiate Contract object and request quote for SPY ETF. <br /><br /><pre name="code" class="java"><br />package ibjava;<br /><br />import com.ib.client.*;<br /><br /><br />public class StockBot implements EWrapper {<br /><br /> public StockBot() throws InterruptedException {<br /> int i = 0;<br /> EClientSocket m_client = new EClientSocket(this);<br /> m_client.eConnect("127.0.0.1",7496, 0);<br /> System.out.println("Connected.");<br /><br /> // preparing to quote SPY<br /> Contract contract;<br /> contract = new Contract();<br /><br /> contract.m_symbol = "SPY";<br /> contract.m_secType = "STK";<br /> contract.m_expiry = "";<br /> contract.m_right = "";<br /> contract.m_multiplier = "";<br /> contract.m_exchange = "SMART";<br /> contract.m_primaryExch = "";<br /> contract.m_currency = "USD";<br /> contract.m_localSymbol = "";<br /> // submit quote for SPY. results will come in through<br /> // tickPrice() and tickSize() methods<br /> m_client.reqMktData(1,contract,null , true);<br /> <br /> Thread.sleep(4000); // sleep for 4 seconds<br /><br /> m_client.eDisconnect();<br /> System.out.println("Disconnected.");<br /> }<br /><br /> public static void main(String[] args) throws InterruptedException {<br /><br /> System.out.println("Hello");<br /> StockBot myBot= new StockBot();<br /> }<br /><br /> // ---------------------------------------------------<br /> // -- methods from EWrapper -- must be declared<br /> // ---------------------------------------------------<br /> public void tickPrice( int tickerId, int field, double price, int canAutoExecute){<br /> System.out.println("Price Update: Id: "+tickerId+" Type: "+field+" Price: "+price);<br /> }<br /> public void tickSize( int tickerId, int field, int size){<br /> System.out.println("Volume Update: Id: "+tickerId+" Type: "+field+" Size: "+size);<br /> }<br /> public void tickOptionComputation( int tickerId, int field, double impliedVol, double delta, double modelPrice, double pvDividend){}<br /> public void tickGeneric(int tickerId, int tickType, double value){}<br /> public void tickString(int tickerId, int tickType, String value){}<br /> public void tickEFP(int tickerId, int tickType, double basisPoints, String formattedBasisPoints, double impliedFuture, int holdDays, String futureExpiry, double dividendImpact, double dividendsToExpiry){}<br /> public void orderStatus( int orderId, String status, int filled, int remaining, double avgFillPrice, int permId, int parentId, double lastFillPrice, int clientId, String whyHeld){}<br /> public void openOrder( int orderId, Contract contract, Order order, OrderState orderState){}<br /><br /> public void updateAccountValue(String key, String value, String currency, String accountName){}<br /> public void updatePortfolio(Contract contract, int position, double marketPrice, double marketValue, double averageCost, double unrealizedPNL, double realizedPNL, String accountName){}<br /> public void updateAccountTime(String timeStamp){}<br /> public void nextValidId( int orderId){}<br /> public void contractDetails(int reqId, ContractDetails contractDetails){}<br /> public void bondContractDetails(int reqId, ContractDetails contractDetails){}<br /> public void contractDetailsEnd(int reqId){}<br /> public void execDetails( int orderId, Contract contract, Execution execution){}<br /> public void updateMktDepth( int tickerId, int position, int operation, int side, double price, int size){}<br /> public void updateMktDepthL2( int tickerId, int position, String marketMaker, int operation, int side, double price, int size){}<br /> public void updateNewsBulletin( int msgId, int msgType, String message, String origExchange){}<br /> public void managedAccounts( String accountsList){}<br /> public void receiveFA(int faDataType, String xml){}<br /> public void historicalData(int reqId, String date, double open, double high, double low, double close, int volume, int count, double WAP, boolean hasGaps){}<br /> public void scannerParameters(String xml){}<br /> public void scannerData(int reqId, int rank, ContractDetails contractDetails, String distance, String benchmark, String projection, String legsStr){}<br /> public void scannerDataEnd(int reqId){}<br /> public void realtimeBar(int reqId, long time, double open, double high, double low, double close, long volume, double wap, int count){}<br /> public void currentTime(long time){}<br /> public void fundamentalData(int reqId, String data){}<br /><br /> // ---------------------------------------------------<br /> // -- methods from AnyWrapper -- must be declared<br /> // ---------------------------------------------------<br /> <br /> public void connectionClosed() {<br /> System.out.println(" [API.connectionClosed] Closed connection with TWS");<br /> }<br /> public void error(String str) {<br /> System.out.println(" [API.msg1] " + str);<br /> }<br /><br /> public void error(int one, int two, String str) {<br /> System.out.println(" [API.msg2] " + str + " {" + one + ", " + two + "}");<br /> }<br /><br /> public void error(Exception e) {<br /> System.out.println(" [API.msg3] " + e.getMessage());<br /> }<br /> <br />}<br /></pre><br /><br />Running the code above with 'Incoming Connection' dialog ON and OFF works the same way. The output is:<br /><br /><code><br />Hello<br />Server Version:43<br />TWS Time at connection:20090211 22:58:59 EST<br />Connected.<br /> [API.msg2] Market data farm connection is OK:usopt {-1, 2104}<br /> [API.msg2] Market data farm connection is OK:usfarm {-1, 2104}<br />Price Update: Id: 1 Type: 1 Price: 83.6<br />Volume Update: Id: 1 Type: 0 Size: 5<br />Price Update: Id: 1 Type: 2 Price: 83.62<br />Volume Update: Id: 1 Type: 3 Size: 9<br />Price Update: Id: 1 Type: 4 Price: 83.61<br />Volume Update: Id: 1 Type: 5 Size: 1<br />Volume Update: Id: 1 Type: 8 Size: 3213164<br />Price Update: Id: 1 Type: 6 Price: 84.05<br />Price Update: Id: 1 Type: 7 Price: 82.4<br />Price Update: Id: 1 Type: 9 Price: 83.11<br />Price Update: Id: 1 Type: 14 Price: 83.45<br />Disconnected.<br />BUILD SUCCESSFUL (total time: 13 seconds)<br /><br /></code><br /><br />So I have proven that Java code works. What was missing from JRuby code were the methods from AnyWrapper. Adding them in will be the next step.System Traderhttp://www.blogger.com/profile/11376312889397858168noreply@blogger.com0tag:blogger.com,1999:blog-6568584506474235321.post-41906751439033054382009-02-08T21:22:00.017-05:002009-02-22T15:00:55.211-05:00Proof Of Concept - 8 of 8 : Going NativeAfter playing around with JRuby and TWS API trying to resolve the problems described in my <a href="http://tradingbot.blogspot.com/2009/01/proof-of-concept-8-of-8-stumbling-block.html">prior post</a>, there has been no real progress. I have decided to go native and try out the sample code in Java to ascertain if the problem is with Java or JRuby. In NetBeans, I created a new Java Application called IBJava, created a new class and copied TWS API classes under the <span style="font-weight: bold;">IBJava\Src</span> folder. As with everything in this project, nothing is as easy as it seems. The final code to just connect and disconnect from TWS is as follows:<br /><br /><pre name="code" class="java"><br />package ibjava;<br /><br />import com.ib.client.*;<br /><br /><br />public class StockBot implements EWrapper {<br /><br /> public StockBot() {<br /> EClientSocket m_client = new EClientSocket(this);<br /> m_client.eConnect("127.0.0.1",7496, 0);<br /> System.out.println("Connected.");<br /> while (i<990000) i++;<br /> m_client.eDisconnect();<br /> }<br /><br /> public static void main(String[] args) {<br /><br /> System.out.println("Hello");<br /> StockBot myBot= new StockBot();<br /> }<br /> // --------------------------------------------------- <br /> // -- methods from EWrapper -- must be declared <br /> // --------------------------------------------------- <br /> public void tickPrice( int tickerId, int field, double price, int canAutoExecute){<br /> System.out.println("Price Update: Id: "+tickerId+" Type: "+field+" Price: "+price);<br /> }<br /> public void tickSize( int tickerId, int field, int size){<br /> System.out.println("Volume Update: Id: "+tickerId+" Type: "+field+" Size: "+size);<br /> }<br /> public void tickOptionComputation( int tickerId, int field, double impliedVol,<br /> double delta, double modelPrice, double pvDividend){<br /> }<br /> public void tickGeneric(int tickerId, int tickType, double value){<br /> }<br /> public void tickString(int tickerId, int tickType, String value){<br /> }<br /> public void tickEFP(int tickerId, int tickType, double basisPoints,<br /> String formattedBasisPoints, double impliedFuture, int holdDays,<br /> String futureExpiry, double dividendImpact, double dividendsToExpiry){}<br /> public void orderStatus( int orderId, String status, int filled, int remaining,<br /> double avgFillPrice, int permId, int parentId, double lastFillPrice,<br /> int clientId, String whyHeld){<br /> }<br /> public void openOrder( int orderId, Contract contract, Order order, OrderState orderState){<br /> }<br /><br /> public void updateAccountValue(String key, String value, String currency, String accountName){}<br /> public void updatePortfolio(Contract contract, int position, double marketPrice, double marketValue,<br /> double averageCost, double unrealizedPNL, double realizedPNL, String accountName){}<br /> public void updateAccountTime(String timeStamp){}<br /> public void nextValidId( int orderId){}<br /> public void contractDetails(int reqId, ContractDetails contractDetails){}<br /> public void bondContractDetails(int reqId, ContractDetails contractDetails){}<br /> public void contractDetailsEnd(int reqId){}<br /> public void execDetails( int orderId, Contract contract, Execution execution){}<br /> public void updateMktDepth( int tickerId, int position, int operation, int side, double price, int size){}<br /> public void updateMktDepthL2( int tickerId, int position, String marketMaker, int operation,<br /> int side, double price, int size){}<br /> public void updateNewsBulletin( int msgId, int msgType, String message, String origExchange){}<br /> public void managedAccounts( String accountsList){}<br /> public void receiveFA(int faDataType, String xml){}<br /> public void historicalData(int reqId, String date, double open, double high, double low,<br /> double close, int volume, int count, double WAP, boolean hasGaps){}<br /> public void scannerParameters(String xml){}<br /> public void scannerData(int reqId, int rank, ContractDetails contractDetails, String distance,<br /> String benchmark, String projection, String legsStr){}<br /> public void scannerDataEnd(int reqId){}<br /> public void realtimeBar(int reqId, long time, double open, double high, double low, double close, long volume, double wap, int count){}<br /> public void currentTime(long time){}<br /> public void fundamentalData(int reqId, String data){}<br /><br /> // ---------------------------------------------------<br /> // -- methods from AnyWrapper -- must be declared<br /> // ---------------------------------------------------<br /> <br /> public void connectionClosed() {<br /> System.out.println(" [API.connectionClosed] Closed connection with TWS");<br /> }<br /> public void error(String str) {<br /> System.out.println(" [API.msg1] " + str);<br /> }<br /><br /> public void error(int one, int two, String str) {<br /> System.out.println(" [API.msg2] " + str + " {" + one + ", " + two + "}");<br /> }<br /><br /> public void error(Exception e) {<br /> System.out.println(" [API.msg3] " + e.getMessage());<br /> }<br /> <br />}<br /></pre><br /><br />Some interesting things to point out here:<br /><br /><span style="font-weight:bold;">1)</span> If lines 76-90 are omitted, the following error is generated:<br /><br /><code><br />java.lang.ExceptionInInitializerError<br />Caused by: java.lang.RuntimeException: Uncompilable source code - ibjava.StockBot is not abstract and does not override abstract method connectionClosed() in com.ib.client.AnyWrapper<br /> at ibjava.StockBot.<clinit>(StockBot.java:11)<br />Exception in thread "main" <br />Exception in thread "main" Java Result: 1<br /></code><br /><br />This is due to the fact that EWrapper interface is a subclass of AnyWrapper interface, which means that I have to create AnyWrapper interface methods in addition to EWrapper interface methods. I did not do this in my JRuby code due to my limited Java knowledge. This error needs to be fixed in the JRuby version.<br /><br /><br /><span style="font-weight:bold;">2)</span> Running the code above with 'Incoming Connection' dialog ON seems to work. The output is:<br /><br /><code><br />run:<br />Hello<br />Server Version:43<br />TWS Time at connection:20090208 21:45:55 EST<br />Connected.<br /> [API.connectionClosed] Closed connection with TWS<br />BUILD SUCCESSFUL (total time: 2 seconds)<br /><br /></code><br /><br /><span style="font-weight:bold;">3)</span> Running the code above with 'Incoming Connection' dialog OFF generates the error:<br /><code><br />run:<br />Hello<br />Server Version:43<br />TWS Time at connection:20090208 21:49:48 EST<br />Connected.<br /> [API.msg2] Market data farm connection is OK:usopt {-1, 2104}<br /> [API.msg2] Market data farm connection is OK:usfarm {-1, 2104}<br /> [API.msg3] socket closed<br /> [API.connectionClosed] Closed connection with TWS<br />BUILD SUCCESSFUL (total time: 0 seconds)<br /></code><br /><br />It's clear to me now that there are issues in Java that I need to understand and resolve before even attempting to get JRuby to work. By working at Java level, I at least get some useful error messages instead of JRuby's cryptic ones.<br /><br />C'est la vie! My initial approach to this project was to be free from working with Java, but that turns out not to be the case. I obviously need to get my hands 'dirty' and this process will take a little more time. The bright side to this is that Java resources are everywhere and I'm sure I'll be able to get this fixed. Stay tuned...<br /><br /><hr><br /><br /><span style="font-weight:bold;">02/11/09 Update</span><br />It occurred to me that the 'errors' I experienced may not actually be errors at all. After some research, it appears that just because the error() methods were triggered, doesn't actually mean errors occurred. They may just be messages that I can ignore. This is good news. With this in mind, I will proceed with Java coding.<br /><br /></br>System Traderhttp://www.blogger.com/profile/11376312889397858168noreply@blogger.com0tag:blogger.com,1999:blog-6568584506474235321.post-20914541237144376632009-01-30T22:24:00.083-05:002009-02-22T15:03:09.134-05:00Proof Of Concept - 8 of 8 : Stumbling BlockHere's where things get interesting. Now that I'm able to successfully connect to TWS, the next step is to do something useful such as getting current market quote. I found a nice site called <a href="http://www.stockbotprogramming.com/">StockBotProgramming.com</a> which has sample code to do this in <a href="http://www.stockbotprogramming.com/javaibtutorial3.php">Java</a>. I find that this <a href="http://www.stockbotprogramming.com/source/java3/Main.java">code is more direct and easier to follow</a> than the one in JavaAPIGettingStarted.pdf.<br /><br />Some notes about this Java code:<br /><ul><li>Main class implements EWrapper interface</li><li>A class must implement all of the methods described in the interface</li><li>The methods in the example is specific to that TWS API, and will not be the same as my version of API (9.51)</li><li>Methods TickPrice() and TickSize() are callback methods that automatically get called when the quote become available<br /></li></ul><br />My task would be to create a class in Ruby which implements this EWrapper interface. More specifically, my version of the EWrapper. So the code will be a little different. Per Java interface definition, all the functions of EWrapper class needs to be declared. The easiest way to do this is to go directly the the EWrapper Java class file, copy, paste into Ruby class and convert to Ruby syntax.<br /><br />This isn't quite as easy as it looks and I'm getting some strange behavior in the coding. Before handling this issue, I noticed there's a problem with prior post concerning Skipping Incoming connection. So I'm not defining a trusted IP anymore.<br /><br />In order for readers to follow along, I have to post my code here. This is going to be hard to see. But I've found Google's <a href="http://code.google.com/p/syntaxhighlighter/">syntax highlighter tool</a>, so I'll be incorporating that into this blog from now on. The instructions are not too clear. I found <a href="http://fahdshariff.blogspot.com/2008/07/syntax-highlighting-code-in-webpages.html">this blog article</a> to be much better.<br /><br /><b>A) </b>This is my test code to request market data:<br /><br /><pre class="ruby" name="code"><br />require 'java'<br />require 'TwsApi.jar'<br /><br />include_class 'ib.client.EClientSocket'<br />include_class 'ib.client.Contract'<br />include_class 'ib.client.AnyWrapper'<br />include_class 'ib.client.EWrapper'<br /><br /># classes for interface method parameters. Not sure if necessary. Can't hurt.<br />include_class 'ib.client.ContractDetails'<br />include_class 'ib.client.Execution'<br />include_class 'ib.client.OrderState'<br /><br />class TestMktData include EWrapper #note class TestMktData < EWrapper syntax is no longer supported in JRuby 1.0<br /><br /> @mySocket <br /><br /> def initialize <br /> puts 'init' <br /> @mySocket = EClientSocket.new(self)<br /> end<br /><br /> def eConnect <br /> @mySocket.eConnect("127.0.0.1", 7496, 0)<br /> end<br /><br /> def requestQuote <br /> puts 'running requestQuote' <br /> # -------------------------------- <br /> myContract = Contract.new <br /> myContract.m_symbol = 'SPY' <br /> myContract.m_secType = 'STK' <br /> myContract.m_exchange = 'SMART' <br /> myContract.m_currency = 'USD' <br /> # options specific below --- <br /> myContract.m_expiry = '' <br /> myContract.m_right = '' <br /> request_id = 1 <br /> ticklist = '' <br /> puts 'created new contract, calling reqMktData' <br /> @mySocket.reqMktData(request_id, myContract, ticklist, true) <br /> puts 'sleeping for 10 sec...' <br /> sleep(10)<br /> end<br /><br /> def eDisconnect<br /> @mySocket.eDisconnect<br /> end<br /><br /> #///////////////////////////////////////////////////////////////////////<br /> #// Interface methods<br /> #///////////////////////////////////////////////////////////////////////<br /><br /> def tickPrice( ticker_id, field, price, can_auto_execute)<br /> puts 'in tickPrice' <br /> #puts 'tickPrice() id=' + ticker_id.to_s + ', price = ' + price.to_s<br /> end<br /><br /> def tickSize( tickerId, field, size)<br /> puts 'in tickSize' <br /> #puts 'tickSize() Id: ' + tickerId.to_s + ' Type: ' + field.to_s + ' Size: ' + size.to_s<br /> end<br /><br /> def tickOptionComputation( ticker_id, field, implied_vol, delta, model_price, pv_dividend)<br /> end<br /><br /> def tickGeneric( ticker_id, tick_type, value)<br /> end<br /><br /> def tickString( ticker_id, tick_type, value)<br /> end<br /><br /> def tickEFP( ticker_id, tick_type, basis_points, formatted_basis_points, implied_future, hold_days, future_expiry, dividend_impact, dividends_to_expiry)<br /> end<br /><br /> def orderStatus( order_id, status, filled, remaining, avg_fill_price, perm_id, parent_id, last_fill_price, client_id, why_held)<br /> end <br /><br /> def openOrder( order_id, contract, order, order_state)<br /> end<br /><br /> def updateAccountValue( key, value, currency, account_name)<br /> end<br /><br /> def updatePortfolio( contract, position, market_price, market_value, average_cost, unrealized_pnl, realized_pnl, account_name)<br /> end<br /><br /> def updateAccountTime( time_stamp)<br /> end<br /><br /> def nextValidId( order_id)<br /> end<br /><br /> def contractDetails( req_id, contract_details)<br /> end<br /><br /> def bondContractDetails( req_id, contract_details)<br /> end<br /><br /> def contractDetailsEnd( req_id)<br /> end<br /><br /> def execDetails( order_id, contract, execution)<br /> end<br /><br /> def updateMktDepth( ticker_id, position, operation, side, price, size)<br /> end<br /><br /> def updateMktDepthL2( ticker_id, position, market_maker, operation, side, price, size)<br /> end<br /><br /> def updateNewsBulletin( msg_id, msgType, message, orig_exchange)<br /> end<br /><br /> def managedAccounts( accounts_list)<br /> end<br /><br /> def receiveFA( fa_data_type, xml)<br /> end<br /><br /> def historicalData( req_id, date, open, high, low, close, volume, count, eWAP, has_gaps)<br /> end<br /><br /> def scannerParameters( xml)<br /> end<br /><br /> def scannerData( req_id, rank, contract_details, distance, benchmark, projection, legs_str)<br /> end<br /><br /> def scannerDataEnd( req_id)<br /> end<br /><br /> def realtimeBar( req_id, time, open, high, low, close, volume, wap, count)<br /> end<br /><br /> def currentTime( time)<br /> end<br /><br /> def fundamentalData( req_id, data)<br /> end<br /><br />end # class TestMktData<br /><br /># *********************************************************************************************<br /># Running the code<br /># *********************************************************************************************<br /><br /> x = TestMktData.new()<br /> begin<br /> x.eConnect<br /> puts 'connected ...'<br /> x.requestQuote<br /> puts 'wait 10 sec'<br /> sleep(10)<br /> x.eDisconnect<br /> puts 'disconnected'<br /> rescue Exception => e<br /> puts 'can not connect'<br /> puts 'Exception' + e.message<br /> x.eDisconnect<br /> end<br /></pre><br /><br />Line 14: specifies that I'm using a Java interface from TWS API called EWrapper<br />Line 41: submits market quote for ticker SPY<br />Lines 54-62: methods that automatically gets called when market data is received<br />Lines 64-140: the rest of method specifications as required by EWrapper interface<br />Lines 148-161: runs the code by connecting, requesting market quote, and disconnecting<br /><br />Running the code produces error:<br /><code><br />init<br />Server Version:43<br /><br />TWS Time at connection:20090201 22:48:24 EST<br /><br />connected ...<br />running requestQuote<br />created new contract, calling reqMktData<br />sleeping for 10 sec...<br /><span style="color:red;">Exception in thread "EReader" :1: #< nameerror::message:0x1de0c09 > (NoMethodError)<br /> ...internal jruby stack elided...<br /> from (unknown).(unknown)(:1)<br /></span><nameerror::message:0x1de0c09><span style="color:red;"></span><br />disconnected<br /></nameerror::message:0x1de0c09></code><br /><br />What causes this 'stack elided' error? It seems like it has something to do with requesting market data, waiting (sleep), and receiving market data. So I went about commenting out lines of code to see what the behavior is and here's what I've found:<br /><br /><b>B) </b>Commenting out sleep():<br /><pre class="ruby:firstline[40]" name="code"><br />puts 'created new contract, calling reqMktData' <br />@mySocket.reqMktData(request_id, myContract, ticklist, true) <br />puts 'sleeping for 10 sec...' <br /># sleep(10)<br /></pre><br /><br /><br />produces no errors, but then again, the program didn't wait around to receive quote either:<br /><br /><code><br />init<br />Server Version:43<br /><br />TWS Time at connection:20090201 23:09:01 EST<br /><br />connected ...<br />running requestQuote<br />created new contract, calling reqMktData<br />sleeping for 10 sec...<br />disconnected<br /></code><br /><br /><b>C) </b>Can it be that sleep() is the actual problem and has nothing to do with reqMktData()? I tried just connecting, sleeping, and disconnecting:<br /><br /><pre class="ruby:firstline[148]" name="code"><br />x = TestMktData.new() <br />begin<br /> x.eConnect<br /> puts 'connected ...'<br /> # x.requestQuote <br /> puts 'wait 10 sec' <br /> sleep(10) <br /> x.eDisconnect <br /> puts 'disconnected' <br />rescue Exception => e <br /> puts 'can not connect' <br /> puts 'Exception' + e.message <br /> x.eDisconnect <br />end<br /></pre><br /><br />Produces the same 'stack elided' error:<br /><br /><code><br />init<br />Server Version:43<br /><br />TWS Time at connection:20090201 23:24:53 EST<br /><br />connected ...<br />wait 10 sec<br /><span style="color:red;">Exception in thread "EReader" :1: #< nameerror::message:0xd510e8> (NoMethodError)</span> <span style="color:red;"> ...internal jruby stack elided...</span> <span style="color:red;"> from (unknown).(unknown)(:1)</span><br /><br />disconnected<br /></code><br /><br /><b>D) </b>So going back to my previous post on <a href="http://tradingbot.blogspot.com/2009/01/tws-note-skipping-incoming-connection.html">TWS Note - Skipping 'Incoming Connection'</a>. I modify the connect and disconnect code to add the sleep():<br /><br /><pre class="ruby" name="code"><br />require 'java'<br />require 'TwsApi.jar'<br />include_class 'ib.client.EClientSocket' <br /><br />mySocket = EClientSocket.new(self) <br />begin <br /> mySocket.eConnect("127.0.0.1", 7496, 0) # api <br /> puts 'connected ...' <br /> sleep(10) # this produces error <br /> mySocket.eDisconnect <br /> puts 'disconnected' <br /><br />rescue Exception => e <br /> puts 'can not connect' <br /> puts e.message<br />end<br /></pre><br /><br />Produces 'EReader' error, which is different from 'stack elided' error from before (although consistent with error in post on <a href="http://tradingbot.blogspot.com/2009/01/tws-note-skipping-incoming-connection.html">TWS Note - Skipping 'Incoming Connection'</a>):<br /><code><br />Server Version:43<br /><br />TWS Time at connection:20090201 23:31:00 EST<br /><br />connected ...<br />wait 10 sec<br /><span style="color:red;">Exception in thread "EReader" java.lang.ClassCastException: InterfaceImpl326315435</span> <span style="color:red;"> at ib.client.EReader.eWrapper(EReader.java:47)</span> <span style="color:red;"> at ib.client.EReader.run(EReader.java:66)</span><br /><br />was connected, now disconnected<br /></code><br /><br />So what gives? Why do A and C give 'stack elided' errors and D give an 'EReader' error? Both have sleep() but the difference is that A and C define a new class which implements EWrapper. D doesn't do that. It could be that there are two different error conditions going on. I'm not sure what the answer is yet, but I will look into it some more.<br /><br />Stay tuned ..System Traderhttp://www.blogger.com/profile/11376312889397858168noreply@blogger.com0tag:blogger.com,1999:blog-6568584506474235321.post-82923024366050096212009-01-26T22:13:00.028-05:002009-02-01T20:43:06.543-05:00TWS Note - Skipping 'Incoming Connection'To avoid 'Accept incoming connection?' dialog box from popping up while using API to connect to TWS, simply go to TWS menu. Choose Configure | API | All API Settings...<br />
Then enter 127.0.0.1 as the trusted IP address.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmwIU-OjcVQ3xcq38TR-bTdn5qimksf-YYvUTXNbxASSTAazv688r_bJsCrvlW71HKtxvMODKVas1Z35GH9XfzvVaVGP3fXzPs91BCMq6YUCbzk6UGqpc3UuyiiO-_FblV8cDvByMN6uUR/s1600-h/incoming_connection.JPG" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img alt="" border="0" id="BLOGGER_PHOTO_ID_5297924100055827586" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmwIU-OjcVQ3xcq38TR-bTdn5qimksf-YYvUTXNbxASSTAazv688r_bJsCrvlW71HKtxvMODKVas1Z35GH9XfzvVaVGP3fXzPs91BCMq6YUCbzk6UGqpc3UuyiiO-_FblV8cDvByMN6uUR/s320/incoming_connection.JPG" style="cursor: pointer; display: block; height: 103px; margin: 0px auto 10px; text-align: center; width: 320px;" /></a><br />
<hr /><br />
<span style="font-weight: bold;">02/01/2009 Update</span><br />
<br />
Changing the configuration above sounds innocent enough. By skipping this dialog box, it saves a mouse click during development, but it seems there is an unintended side effect. Using this very simple to connect and disconnect.<br />
<br />
<pre name="code" class="ruby">require 'java'
require 'TwsApi.jar'
include_class 'ib.client.EClientSocket'
mySocket = EClientSocket.new(self)
begin
mySocket.eConnect("127.0.0.1", 7496, 0) # api
puts 'connected ...'
mySocket.eDisconnect
puts 'disconnected'
rescue Exception => e
puts 'can not connect'
puts e.message
end
</pre><br />
<br />
Produces this error:<br />
<code><br />
Server Version:43<br />
<br />
<span id="SPELLING_ERROR_0">TWS</span> Time at connection:20090201 15:14:39 EST<br />
<br />
connected ...<br />
disconnected<br />
<span style="color: red;">Exception in thread "<span id="SPELLING_ERROR_1">EReader</span>" java.lang.ClassCastException: <span id="SPELLING_ERROR_2">InterfaceImpl</span>1026612999</span><br />
<span style="color: red;"> at ib.client.EReader.eWrapper(EReader.java:47)</span><br />
<span style="color: red;"> at ib.client.EReader.run(EReader.java:66)</span><br />
<br />
</code><br />
<br />
<br />
What would cause this? This error is very consistent and happens at every run. So I went back and changed the trusted <span id="SPELLING_ERROR_3">IP</span> address to 127.0.0.2 to get the 'Accept incoming connection?' dialog again. The output is now as expected. No errors. So maybe this trusted <span id="SPELLING_ERROR_4">IP</span> configuration doesn't work <span id="SPELLING_ERROR_5">after all</span> :(<br />
<code><br />
Server Version:43<br />
<br />
<span id="SPELLING_ERROR_6">TWS</span> Time at connection:20090201 15:24:50 EST<br />
<br />
connected ...<br />
disconnected<br />
</code>System Traderhttp://www.blogger.com/profile/11376312889397858168noreply@blogger.com0tag:blogger.com,1999:blog-6568584506474235321.post-71844236996675127572009-01-09T23:37:00.034-05:002009-02-22T23:16:38.493-05:00Proof Of Concept - 7 of 8 : NetBeans Ruby PluginsLet's review what we have so far for my development environment. Java SDK is installed and IB's Java API successfully compiles in NetBeans. I was able to compile and run IB's test client program and create a JAR file of IB's API. JRuby is installed and runs on top of Java SDK. So now I'm ready to write Ruby code and call IB's Java API.<br /><br />Before I can do that though, I need to have a development environment for Ruby. Fortunately, NetBeans also supports Ruby development along with Java. To enable Ruby support, go to Tools | Plugins | Available Plugins tab. Choose Ruby and JRuby plugins and click install. It may take a while to fr NetBeans to download all the libraries.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhobHgOzkj6NfWRoHYf8p64FbaRavCVdQmtsoXW_ctDwvPn3G27zmfor1q8iozWFU3jmd26G2Il_XPHpdKk575PIVqZrGQ-PaEF-yE0s05if0sBkUq9k5JKrzGoqRGU0i_apacYAypttB3h/s1600-h/nb_ruby_plugin.JPG"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 215px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhobHgOzkj6NfWRoHYf8p64FbaRavCVdQmtsoXW_ctDwvPn3G27zmfor1q8iozWFU3jmd26G2Il_XPHpdKk575PIVqZrGQ-PaEF-yE0s05if0sBkUq9k5JKrzGoqRGU0i_apacYAypttB3h/s320/nb_ruby_plugin.JPG" alt="" id="BLOGGER_PHOTO_ID_5289880075722785378" border="0" /></a><br />Test out Ruby plugin by creating a Hello World program. Go to File | New Project | Ruby Application. Name it HelloWorld.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKsw43kcu1AqzSUrr0OjxL_bc8LA81-fdFAOiiQJYMYIvpsazuU8PRGHb0xjntV7e8D7jgUideCBrvxwCYCEBJqhL4TirNI_va9S48Xz7hU9vGr5Miln2OPzf4SOhW1j6wZ-0ksBVyMwrj/s1600-h/nb_ruby_test.JPG"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 231px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKsw43kcu1AqzSUrr0OjxL_bc8LA81-fdFAOiiQJYMYIvpsazuU8PRGHb0xjntV7e8D7jgUideCBrvxwCYCEBJqhL4TirNI_va9S48Xz7hU9vGr5Miln2OPzf4SOhW1j6wZ-0ksBVyMwrj/s320/nb_ruby_test.JPG" alt="" id="BLOGGER_PHOTO_ID_5289882631763384194" border="0" /></a><br /><br />Run the application by pressing F6 and 'Hello World' should display in the output window.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDDZp39J4fFXpucoqIGPMjwxy6aej3BT5zv_Za8O3jePB4JGhy6Cg9ACGuLXsRMh43YIqvATdq6N4yu6cvDxmIJP8DhcLVx-Emih6EqP6fKwkIVcH2r7h1fK-H1Unt9EbtkoTaMq8C8-za/s1600-h/nb_ruby_helloWorld.JPG"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 174px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDDZp39J4fFXpucoqIGPMjwxy6aej3BT5zv_Za8O3jePB4JGhy6Cg9ACGuLXsRMh43YIqvATdq6N4yu6cvDxmIJP8DhcLVx-Emih6EqP6fKwkIVcH2r7h1fK-H1Unt9EbtkoTaMq8C8-za/s320/nb_ruby_helloWorld.JPG" alt="" id="BLOGGER_PHOTO_ID_5289883855185306898" border="0" /></a><br />Success!! Ruby development is enabled in NetBeans.<br /><br /><hr /><br /><br />Next step is to see if Java classes are accessible. This is a sample Ruby code that calls Java class TreeSet. Note how seamless it is to call Java classes using JRuby.<br /><br /><pre name="code" class="ruby"><br />require 'java'<br />include_class 'java.util.TreeSet' <br /> <br />set = TreeSet.new <br />set.add "foo" <br />set.add "Bar" <br />set.add "baz" <br />set.each do |v| <br /> puts "value: #{v}"<br />end<br /></pre><br /><br />Press F6 will produce the following in the output window.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZgcabHNeySef6Yyx2FuLcwuRQ2wA4SaqecHIWP7LB_H06Z-i4osnVnPE-bLuK7Kc6V2817_NIu1JUUYgFd-W7yFe0n7MaXCyldtwJAzj7fOUxY3kHkTJjuodzufbvb4VFYdZ5uiIqjewd/s1600-h/nb_ruby_callJava.JPG"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 224px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZgcabHNeySef6Yyx2FuLcwuRQ2wA4SaqecHIWP7LB_H06Z-i4osnVnPE-bLuK7Kc6V2817_NIu1JUUYgFd-W7yFe0n7MaXCyldtwJAzj7fOUxY3kHkTJjuodzufbvb4VFYdZ5uiIqjewd/s320/nb_ruby_callJava.JPG" alt="" id="BLOGGER_PHOTO_ID_5290871025851908946" border="0" /></a><br /><br /><br />Another way of testing this is (just plain JRuby, not NetBeans) to run it via command line. It produces the same output:<br /><br /><pre><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhV6YaJvM0PewEK63iuJP1dMSoGhwptt65M1CniqzrXJRz7otE7Sc7I8EsYOd3ab3qthLRdZVWGbrsWjR28TFQpETnvzpvulyJj4ijEZd-cM5J_gozVidpw59s8pDGj2V1mu4I-rF5xYgVS/s1600-h/cmd_ruby_callJava.JPG"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 60px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhV6YaJvM0PewEK63iuJP1dMSoGhwptt65M1CniqzrXJRz7otE7Sc7I8EsYOd3ab3qthLRdZVWGbrsWjR28TFQpETnvzpvulyJj4ijEZd-cM5J_gozVidpw59s8pDGj2V1mu4I-rF5xYgVS/s320/cmd_ruby_callJava.JPG" alt="" id="BLOGGER_PHOTO_ID_5290872399221107810" border="0" /></a></pre><br /><br /><hr /><br /><span style="font-weight: bold;"><br />02/22/09 Update </span><br /><br />JRuby plugin automatically installs version 1.1.4. This is an old version and NetBeans must be manually configured to use version 1.1.6 (<a href="http://tradingbot.blogspot.com/2009/01/proof-of-concet-6-of-7-install-jruby.html">installed in Proof Of Concept - 6 of 8</a>). Please see <a href="http://tradingbot.blogspot.com/2009/02/proof-of-concept-8-of-8b-historical.html">Proof Of Concept 8 of 8b : Historical Quote</a> for how to do this.<br /><br /><hr /><br /><br />Last step is to see if IB's Java API archived file is accessible. Copy file TwsApi.jar (created in <a href="http://tradingbot.blogspot.com/2009/01/proof-of-concept-5-of-7-create-java.html">Proof Of Concept - 5 of 8</a>) and put it in <span style="font-weight: bold;">HelloWorld\lib</span> folder. Run TWS in paper trading mode as this is necessary for for the API to connect to.<br /><br />Code the following in main.rb. This is really simple code to connect and disconnect from TWS using IB's API.<br /><br /><pre name="code" class="ruby"> <br />require 'java'<br />require 'TwsApi.jar'<br />include_class 'ib.client.EClientSocket' <br /><br />mySocket = EClientSocket.new(self) <br />begin <br /> mySocket.eConnect("127.0.0.1", 7496, 0) # api <br /> puts 'connected ...' <br /> mySocket.eDisconnect <br /> puts 'disconnected' <br />rescue<br /> Exception => e <br /> puts 'can not connect' <br /> puts e.message<br />end<br /></pre><br />Press F6 will produce the following output:<br /><br /><pre><code>Server Version:43TWS Time at connection:20090113 15:27:27 ESTconnected ...disconnected</code></pre><br /><br />Running it via command line produces the same output:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgvOSYxx3Fj8s2itCp_LTKNWnOWMsLBmlCGQhB_f3klzT8YJ73x6DVL2fEV9ynEGw5CSXrnowwVdGrgg27JTZvJi4wxQ9S3puot1GxUjka657x7fKx2Nl2NaIqkmiAK3M0phW49D0Gohqe/s1600-h/cmd_ruby_callApi.JPG"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 45px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgvOSYxx3Fj8s2itCp_LTKNWnOWMsLBmlCGQhB_f3klzT8YJ73x6DVL2fEV9ynEGw5CSXrnowwVdGrgg27JTZvJi4wxQ9S3puot1GxUjka657x7fKx2Nl2NaIqkmiAK3M0phW49D0Gohqe/s320/cmd_ruby_callApi.JPG" alt="" id="BLOGGER_PHOTO_ID_5290878711980675666" border="0" /></a><br /><br />At this point, the development environment is set up and Java and API classes are accessible. It is now time to start using IB's API to see its full power.System Traderhttp://www.blogger.com/profile/11376312889397858168noreply@blogger.com3tag:blogger.com,1999:blog-6568584506474235321.post-35862759096039475442009-01-09T22:48:00.017-05:002009-01-11T11:14:28.054-05:00Proof Of Concept - 6 of 8 : Install JRubyGo to <a href="http://jruby.codehaus.org/">JRuby home page</a> and download the zip file. As of this writing, the current version is 1.1.6.<br /><br /><ol><li>I extracted the zip file into <span style="font-weight: bold;">C:\jruby\jruby-1.1.6RC1</span></li><li>Added the above path on to the end of <span style="font-weight: bold;">PATH</span> environment variable</li><li>Add new environment variable <span style="font-weight: bold;">JAVA_HOME </span>with value <span style="font-weight: bold;">C:\Program Files\Java\jdk1.5.0_17 </span> (this directory was created with installation of Java SDK in <a href="http://tradingbot.blogspot.com/2009/01/proof-of-concept-3-of-7-install-java.html">Proof Of Concept 3 of 8</a>)</li><li style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 0, 0);">To test whether it installed correctly, run:</span><pre><code><br />jruby -v <br /></code></pre><br /></li></ol> <p style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 0, 0);">The above command will return the current version.</span></p><p style="color: rgb(0, 102, 0);"><span style="color: rgb(51, 0, 51);"><br /></span></p><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlVeKD8sr6dM3TPNw1ZQ6cmdbodhKAM2ZVuAQM97DAOJ2q33DsWSZgKGNJtej2wI4Af69dYqBbj-uXP6Y6318wSA-E7-avne2DfhZIQorIMqaxhRNBnK9ERH-Dp39O1tG16o5wGSwzPnv6/s1600-h/jruby.JPG"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 161px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlVeKD8sr6dM3TPNw1ZQ6cmdbodhKAM2ZVuAQM97DAOJ2q33DsWSZgKGNJtej2wI4Af69dYqBbj-uXP6Y6318wSA-E7-avne2DfhZIQorIMqaxhRNBnK9ERH-Dp39O1tG16o5wGSwzPnv6/s320/jruby.JPG" alt="" id="BLOGGER_PHOTO_ID_5289515105949320930" border="0" /></a>System Traderhttp://www.blogger.com/profile/11376312889397858168noreply@blogger.com0tag:blogger.com,1999:blog-6568584506474235321.post-50727862756502310622009-01-09T22:19:00.007-05:002009-01-10T23:17:02.222-05:00Proof Of Concept - 5 of 8 : Create Java Archive File (JAR)A Java JAR file is used for aggregating many files into one. It is generally used to distribute Java classes. All IB's API classes in <span style="font-weight: bold;">twsapi\com\ib\client </span>needs to be packaged into .jar file in order to be used in JRuby.<br /><br />Run NetBeans and choose new project for Java class library. I have named my project TwsApi. Click Finish and the new project is created.<br />In Explorer, copy <span style="font-weight: bold;">com\ib\client</span> folder (created in <a href="http://tradingbot.blogspot.com/2009/01/proof-of-concept-4-of-7-ibs-java-test.html">Proof Of Concept - 4 of 8</a>) and paste it into <span style="font-weight: bold;">TwsApi\src</span> folder.<br />In NetBeans, right click on the project node and select Properties. In Categories: Sources, press Add Folder button and select <span style="font-weight: bold;">TwsApi\src\com</span>.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgociu_aSb1l36HmFNmCOcRcqHVO4LP3X1P_977mgRYY304o6laElqAr-v6dRyypiGTCuM5Q2V3w3zIg5DFhLZqxigO-4m_hhe95M_rfM2_XojQ5IpqfVwGibSQpHsqHdN9h0xBTleSFMG/s1600-h/nb_jar.JPG"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 223px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgociu_aSb1l36HmFNmCOcRcqHVO4LP3X1P_977mgRYY304o6laElqAr-v6dRyypiGTCuM5Q2V3w3zIg5DFhLZqxigO-4m_hhe95M_rfM2_XojQ5IpqfVwGibSQpHsqHdN9h0xBTleSFMG/s320/nb_jar.JPG" alt="" id="BLOGGER_PHOTO_ID_5289504311664225298" border="0" /></a><br />Click OK and the folder is now added to the project. Press F11 to build the project and TwsApi.jar should be created in <span style="font-weight: bold;">TwsApi\dist</span> folder.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgntoCThGYgSopj5ZCTWVnddHyd6gI587T9r7itGWLH_Ot8nj3og7A97w4kfivA1ZWF33aTC7sFTf1QwbVgqHaAeqS9rSPoK_MXU0_FhOy0ZFy5FZ4awcKsU8aP_JcYVJKT9A-xOAeQHwvq/s1600-h/nb_jar_done.JPG"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 135px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgntoCThGYgSopj5ZCTWVnddHyd6gI587T9r7itGWLH_Ot8nj3og7A97w4kfivA1ZWF33aTC7sFTf1QwbVgqHaAeqS9rSPoK_MXU0_FhOy0ZFy5FZ4awcKsU8aP_JcYVJKT9A-xOAeQHwvq/s320/nb_jar_done.JPG" alt="" id="BLOGGER_PHOTO_ID_5289505632134454482" border="0" /></a>System Traderhttp://www.blogger.com/profile/11376312889397858168noreply@blogger.com0tag:blogger.com,1999:blog-6568584506474235321.post-67498853487232348562009-01-07T20:39:00.015-05:002009-01-10T23:17:24.783-05:00Proof Of Concept – 4 of 8 : IB's Java Test ClientNext step is to use NetBeans to compile and run IB's Java test client. The process is described in documentation <a href="http://www.interactivebrokers.com/download/JavaAPIGettingStarted.pdf">JavaAPIGettingStarted.pdf</a> (Chapter 5) on IB's web site. It's a good idea to read this doc and follow the directions. Running the Java test client basically consists of:<br /><ol><li>Create a new Java application project in NetBeans</li><li>Attach packages in <span style="font-weight: bold;">Java\com</span> and <span style="font-weight: bold;">Java\TestJav</span><span style="font-weight: bold;">aClient</span> folders (see <a href="http://tradingbot.blogspot.com/2009/01/proof-of-concept-2-of-7-tws-api.html">Proof Of Concept - 2 of 8</a>) to the project</li><li>Press F6 to run</li><li>Test client magically runs and ready to connect to TWS</li></ol><p class="MsoNormal" style="text-indent: 0.5in;"><span style=";font-family:Arial;font-size:10;" ><o:p></o:p></span></p><br />Well, this didn't quite work out for me. NetBeans was unable to compile the Java program. Note the red exclamation marks in the upper left corner.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCWA6TVh_vygSkK8uL0lJbb_-7-3tCjF0vqCHKiokbrKrtVcyVldOO9ergfVxD3mDnkvVwV6b5tAsqex9IX6-fB_ZAorKFrBEr3HCORLrcwwPmCSPSZpVwJ5Z5jrBQGJceJOQu94g-Mt9H/s1600-h/ibSample_err1.JPG"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 206px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCWA6TVh_vygSkK8uL0lJbb_-7-3tCjF0vqCHKiokbrKrtVcyVldOO9ergfVxD3mDnkvVwV6b5tAsqex9IX6-fB_ZAorKFrBEr3HCORLrcwwPmCSPSZpVwJ5Z5jrBQGJceJOQu94g-Mt9H/s320/ibSample_err1.JPG" alt="" id="BLOGGER_PHOTO_ID_5288736244154850754" border="0" /></a><br /><br />Looking a little deeper at the API code, looks like there's some sort of problem with loading the classes in IB's packages. NetBeans just doesn't like the package names on all the Java class files.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLTOc4CKHTb9_oeHQNrqFx9-eCi8e0fyYX7abNKBgTERRQRaqdIgIDeUkIA1_he7cSnRxLLX1LuOEHCaYdhpry2PR1jQppssOLeyaFn2YFEo018TC8O5ArI6edb6jneJgTspwzU_G5Rit-/s1600-h/ibSample_err2.JPG"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 206px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLTOc4CKHTb9_oeHQNrqFx9-eCi8e0fyYX7abNKBgTERRQRaqdIgIDeUkIA1_he7cSnRxLLX1LuOEHCaYdhpry2PR1jQppssOLeyaFn2YFEo018TC8O5ArI6edb6jneJgTspwzU_G5Rit-/s320/ibSample_err2.JPG" alt="" id="BLOGGER_PHOTO_ID_5288736414810393874" border="0" /></a><br />I was stuck at this step for a little while but was able to resolve it (with a little bit of hacking). The issue is that NetBeans is unable to load IB's package names com.ib.client and TestJavaClient defined in Java classes in <span style="font-weight: bold;">Java\com</span> and <span style="font-weight: bold;">Java\TestJavaClient</span> folders. So some editing of the Java class files are required.<br /><br /><br />1. In Explorer, copy the two folders in <span style="font-weight: bold;">Java\</span> folder and paste them into a new folder. This is where edited versions will be. I just don't like to lose the original version in case something goes wrong. I named this new folder <span style="font-weight: bold;">twsapi\</span>.<br /><br />2. In NetBeans, for each .java file in ib.client package:<br /><ul><li>At the top of the file, change text <span style="font-weight: bold;font-family:trebuchet ms;font-size:85%;" >package com.ib.client;</span> to <span style="font-weight: bold;font-family:trebuchet ms;font-size:85%;" >package ib.client;</span></li><li>Save, and there shouldn't be any errors</li></ul> 3. In Explorer, rename folder hierarchy from <span style="font-weight: bold;">twsapi\TestJavaClient</span> to <span style="font-weight: bold;">twsapi\x\y\TestJavaClient</span><br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwyp_ZPDkPMBsIwIvFOPE1Nv-_YrF0ZFlN5TnbfPDlDnKZvSgoKaccnkSpo-xHdcimdiPF4tZSMaswRwGQ1a77Ip8qdfTZTpL7HqrRSQ3hYJOPaCNxmnhaqMlAoBQ1RyAe4ch2amiYtcAW/s1600-h/ibSample_folder_after.JPG"><img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer; width: 254px; height: 145px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwyp_ZPDkPMBsIwIvFOPE1Nv-_YrF0ZFlN5TnbfPDlDnKZvSgoKaccnkSpo-xHdcimdiPF4tZSMaswRwGQ1a77Ip8qdfTZTpL7HqrRSQ3hYJOPaCNxmnhaqMlAoBQ1RyAe4ch2amiYtcAW/s320/ibSample_folder_after.JPG" alt="" id="BLOGGER_PHOTO_ID_5288750007292948274" border="0" /></a><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirsUE3AJnTzcrhIKxxpKDDJX4QYMHgmkMSmxHB4sJg1hDU0z2xpwyAIbJHi75BYdTerGyhLsgbORMzWVeuZ-3SoR6IfzSuxhun9I-RaV9NH9TFMqN9Ivk_R8YNb67YCMxbcgOUxj_DuaeD/s1600-h/ibSample_folder_before.JPG"><img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 209px; height: 93px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirsUE3AJnTzcrhIKxxpKDDJX4QYMHgmkMSmxHB4sJg1hDU0z2xpwyAIbJHi75BYdTerGyhLsgbORMzWVeuZ-3SoR6IfzSuxhun9I-RaV9NH9TFMqN9Ivk_R8YNb67YCMxbcgOUxj_DuaeD/s320/ibSample_folder_before.JPG" alt="" id="BLOGGER_PHOTO_ID_5288750841526135266" border="0" /></a><br />now becomes<br /><br /><br /><br /><br /><br /><br />4. In NetBeans, for each .java file in TestJavaClient package:<br /><ul><li>At the top of the file, change text <span style="font-weight: bold;font-family:trebuchet ms;font-size:85%;" >package TestJavaClient;</span> to <span style="font-weight: bold;font-family:trebuchet ms;font-size:85%;" >package y.TestJavaClient;</span></li><li>In the rest of the file, change any occurrence of text <span style="font-weight: bold;font-family:trebuchet ms;font-size:85%;" >import com.ib.client.</span> to <span style="font-weight: bold;font-family:trebuchet ms;font-size:85%;" >import ib.client</span></li><li>Save, and there shouldn't be any errors</li></ul><br />5. Create a new Java application project.<br /><br />6. Attach packages in <span style="font-weight: bold;">twsapi\com</span> and <span style="font-weight: bold;">twsapi\x</span> folders to the project. Note no more red exclamation marks in the upper left corner.<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjY9-lEnGMNLYP4AJBRwCvya4vu3nDn-sB4VodTHSPj9OgNURRrAeo5XY-ORS7fSyKKqpof4r__K9xeDxnaR4bST3A4i8TqlKTWg-G6H_d-8Jj1OgrlLmda1TOcSZ4iMxjb65oK6JjLYwGn/s1600-h/ibSample_success.JPG"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 206px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjY9-lEnGMNLYP4AJBRwCvya4vu3nDn-sB4VodTHSPj9OgNURRrAeo5XY-ORS7fSyKKqpof4r__K9xeDxnaR4bST3A4i8TqlKTWg-G6H_d-8Jj1OgrlLmda1TOcSZ4iMxjb65oK6JjLYwGn/s320/ibSample_success.JPG" alt="" id="BLOGGER_PHOTO_ID_5288763890669238626" border="0" /></a><br /><br />7. Press F6 to run.<br /><br />8. Test client runs and ready to connect to TWS. Try out its functionalities as specified in documentation <a href="http://www.interactivebrokers.com/download/JavaAPIGettingStarted.pdf">JavaAPIGettingStarted.pdf</a>.<br /><br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEio0iWg9-t9grTrjaOv3xBuDNKm1LL0WNqUmc5HJT9crrlngCHJAF3WSXjkafJ60imVPvE3XZtD9oYIbaHY0nIisO0G6pk5Rg30Inx3uT9TvdN0Yd9ZGhnXYq6QyXBOVxjVsK4dKpwln0hm/s1600-h/ibSample_connected.JPG"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 186px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEio0iWg9-t9grTrjaOv3xBuDNKm1LL0WNqUmc5HJT9crrlngCHJAF3WSXjkafJ60imVPvE3XZtD9oYIbaHY0nIisO0G6pk5Rg30Inx3uT9TvdN0Yd9ZGhnXYq6QyXBOVxjVsK4dKpwln0hm/s320/ibSample_connected.JPG" alt="" id="BLOGGER_PHOTO_ID_5288736972172909186" border="0" /></a>System Traderhttp://www.blogger.com/profile/11376312889397858168noreply@blogger.com0tag:blogger.com,1999:blog-6568584506474235321.post-66651990015926999572009-01-06T11:13:00.013-05:002009-01-10T22:54:59.693-05:00Proof Of Concept - 3 of 8: Install Java SDK and NetbeansJava Standard Edition Software Development Kit ( SE SDK) and NetBeans IDE must be installed in order to compile and run Java programs. Go to Sun's <a href="http://java.sun.com/javase/downloads/index.jsp">Java SE Downloads page</a> and locate <span style="font-weight: bold;">JDK 5.0 Update 17 with NetBeans IDE 6.5</span>. As of this writing, Java latest release is JDK 6 Update 11. I was going to use this latest release, but after reading through some postings on <a href="http://www.interactivebrokers.com/smf/index.php">IB's TWS API forum</a>, some users experienced performance problems with JDK 6 and IB's API. This was not the case with JDK 5. So I've decided to use this the older.<br /><br />The installation process installs Java SE SDK, Java SE Runtime Environment, and NetBeans IDE in separate folders. Mine are in <span style="font-weight: bold;">c:\Program Files\Java\jdk1.5.0_17</span>, <span style="font-weight: bold;">c:\Program Files\Java\jre1.5.0_17</span>, and <span style="font-weight: bold;">c:\Program Files\NetBeans 6.5</span>. Make a note of these as they will be needed later.<br /><br />To check installation, run NetBeans and run sample projects that came with it. Just select the project and Run the project (F6).System Traderhttp://www.blogger.com/profile/11376312889397858168noreply@blogger.com0