Wednesday, August 19, 2009

Ruby - Rails - PostgreSQL Issues

I 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:
Operating System: Windows XP
Ruby: 1.8.6
Rails: 2.3.3
PostgreSQL: 8.4.0

A) The first error occured when starting WEBrick server. A pop up message displayed:
ruby.exe Ordinal Not Found
The ordinal 284 could not be located in the dynamic link library SSLEAY32.DLL

Searching the web led me to this post at Stackoverflow. My solution was to:

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.

2) Install Visual C++ 2008 Redistributables which can be obtained from Microsoft site. This is needed prior to installing OpenSSL below.

3) Install the latest OpenSSL from Shining Light Productions. I used "Win32 OpenSSL v0.9.8k Light".

4) Copy libeay32.dll and ssleay32.dll from OpenSSL folder to h ruby\bin and posgresql\lib folder.

5) Restart WEBrick server and the error should be gone.


B) 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:
Rake aborted!
undefined method 'quote_indent' for PGconn:class

Searching the web led me to this post at HighDot Forums. My solution was to:

1) Open Rails file config/initializers/new_rails_defaults.rb

2) Add the following code at the end of the file:


# Postgres "quote_indent for PGconn:class" fix from http://www.highdots.com/forums/ruby-rails-talk/quote_ident-283323.html
def PGconn.quote_ident(name)
%("#{name}")
end


After that, I did not experience any other issues with the development environment.

Sunday, August 16, 2009

Ruby & Ruby On Rails Installation Complete


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

1) Go to Ruby on Rails download page to Download and install Ruby. Enable RubyGems option. I used Windows 1.8.6-27 Release Candidate 2.

2) When finish, open a command prompt and install rails 2.3.3 :
>gem install rails

3) Install PostgrSQL driver:
>gem install postgres-pr

4) Create sample Rails application:
>rails c:\temp\railstest

That's all there is to it. For a quick tutorial, I suggest going to http://guides.rubyonrails.org/ .

Tuesday, August 11, 2009

PostgreSQL 8.4.0 Setup Complete




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.



Once the database is running, connect to it and create a 'tradingbot' database.


C:\temp>psql -U postgres
Password for user postgres:
psql (8.4.0)
WARNING: Console code page (437) differs from Windows code page (1252)
8-bit characters might not work correctly. See psql reference
page "Notes for Windows users" for details.
Type "help" for help.

postgres=# create database tradebot;
CREATE DATABASE
postgres=# \l
List of databases
Name | Owner | Encoding | Collation | Ctype
| Access privileges
-----------+----------+----------+----------------------------+-----------------
-----------+-----------------------
postgres | postgres | UTF8 | English_United States.1252 | English_United States.1252 |
template0 | postgres | UTF8 | English_United States.1252 | English_United States.1252 | =c/postgres

: postgres=CTc/postgres
template1 | postgres | UTF8 | English_United States.1252 | English_United States.1252 | =c/postgres

: postgres=CTc/postgres
tradebot | postgres | UTF8 | English_United States.1252 | English_United States.1252 |
(4 rows)

postgres=# create user master with password 'blaster';
CREATE ROLE
postgres=# grant all on database tradebot to master;
GRANT
postgres=# \q

Line 1: Connect to default database
Line 9: Create database "tradebot"
Line 27: Create new user called "master"
Line 29: Grant privileges to "master"

Next step is to log into the new database and create a test table.


C:\temp>psql -d tradebot -U master
Password for user master:
psql (8.4.0)
WARNING: Console code page (437) differs from Windows code page (1252)
8-bit characters might not work correctly. See psql reference
page "Notes for Windows users" for details.
Type "help" for help.

tradebot=>\d
No relations found.
tradebot=> create table tester (id varchar(10) not null);
CREATE TABLE
tradebot=> \d
List of relations
Schema | Name | Type | Owner
--------+--------+-------+--------
public | tester | table | master
(1 row)


tradebot=> \d tester
Table "public.tester"
Column | Type | Modifiers
--------+-----------------------+-----------
id | character varying(10) | not null


tradebot=>

Line 1: Connect to tradebot database
Line 9: Get listing of all tables in the database. None returned because database is empty.
Line 11: Create "tester" table
Line 13: Get listing of all tables in the database. "Tester" table appears.
Line 21: Get "tester" table information

Saturday, August 8, 2009

Specifications: Watcher

The 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.






Thursday, July 23, 2009

Proof Of Concept Recap and Game Plan

With 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:


- Use JRuby to interface with TWS API

- Get market quote

- Get historical quote

- Check account information (balance and open position info)

- Check order status

- Submit stock order (market and limit orders)

- Submit options order

- Submit complex options order




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:


- 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.

- 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.

- 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.

- openOrder() callback method sometimes get triggered more than once for unknown reasons.

- 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.

- 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.


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:

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.




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.




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.

Wednesday, July 15, 2009

Proof Of Concept 8 of 8g: Complex Options Order

Complex (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:

# of Legs Strategies
2 vertical, diagonal, calendar, straddle, strangle
3 butterfly
4 iron condor, iron butterfly, double diagonal


In this coding example, I will create a market order to buy a vertical call spread which is composed of:
BUY 1 SPY AUG 50 CALL
SELL 1 SPY AUG 55 CALL

The steps of creating a complex order are:
1) Use EClientSocket.reqContractDetails() method to get contract id for each option leg.
2) Create a ComboLeg object for each option leg using contract id from step 1).
3) Create a Contract object and add ComboLeg objects created from step 2).
4) Create an Order object and submit it using Contract object from step 3).



# To change this template, choose Tools | Templates
# and open the template in the editor.
require 'java'
require 'TwsApi.jar'

include_class 'ib.client.EClientSocket'
include_class 'ib.client.Contract'
include_class 'ib.client.Order'
include_class 'ib.client.AnyWrapper'
include_class 'ib.client.EWrapper'
include_class 'ib.client.ComboLeg'

class TestComboOptionsOrder
include EWrapper #note "class TestMktData < EWrapper" syntax is no longer supported in JRuby 1.0

attr_accessor :order_id, :contractArray
@mySocket


def initialize
puts 'init'
@mySocket = EClientSocket.new(self)
@contractArray = Array.new # array of hash
end

def eConnect
@mySocket.eConnect("127.0.0.1", 7496, 0)
end

# Request all open orders that were placed from all API clients linked to one TWS,
# and also from the TWS
# This is a one time shot call. When its done you DO NOT continue to receive
# information about new placed orders
def reqAllOpenOrders
@mySocket.reqAllOpenOrders
end

def submitOptionsOrder(order_id, symbol, call_put, expiry, strike,
buy_sell, qty, order_type, limit_price, tif)
# --------------------------------
myContract = Contract.new
myContract.m_symbol = symbol
myContract.m_secType = 'OPT'
myContract.m_exchange = 'SMART'
myContract.m_currency = 'USD'
# options specific below ---
myContract.m_expiry = expiry # string format YYYYMM
myContract.m_right = call_put # CALL, PUT
myContract.m_strike = strike # number

myOrder = Order.new
myOrder.m_action = buy_sell # BUY, SELL
myOrder.m_totalQuantity = qty
myOrder.m_orderType = order_type # MKT, LMT, STP, SPLMT, TRAIL, etc
myOrder.m_lmtPrice = limit_price
myOrder.m_tif = tif # DAY, GTC
@mySocket.placeOrder(order_id, myContract, myOrder)
end

# new method to submit order
def submitOrder(order_id, myContract, myOrder)
@mySocket.placeOrder(order_id, myContract, myOrder)
end

def cancelOrder(order_id)
@mySocket.cancelOrder(order_id)
end

# contract details will be received via the contractDetails() function on the EWrapper
def reqContractDetails (req_id, contract)
@mySocket.reqContractDetails(req_id, contract)
end

def eDisconnect
@mySocket.eDisconnect
end


def contract_msg(contract)
puts ' contract_msg:'
puts ' conid = ' + contract.m_conId.to_s
puts ' symbol = ' + contract.m_symbol
puts ' secType = ' + contract.m_secType
puts ' exchange = ' + contract.m_exchange.to_s
puts ' currency = ' + contract.m_currency
puts ' localSymbol = ' + contract.m_localSymbol.to_s
puts ' multiplier = ' + contract.m_multiplier.to_s
puts ' expiry = ' + contract.m_expiry.to_s
puts ' strike = ' + contract.m_strike.to_s
puts ' right = ' + contract.m_right.to_s
end

def contract_details_msg(contract_detail)
puts ' contract_details_msg:'
puts ' marketName = ' + contract_detail.m_marketName.to_s
puts ' tradingClass = ' + contract_detail.m_tradingClass.to_s
puts ' minTick = ' + contract_detail.m_minTick.to_s
puts ' price magnifier = ' + contract_detail.m_priceMagnifier.to_s
puts ' orderTypes = ' + contract_detail.m_orderTypes.to_s
puts ' validExchanges = ' + contract_detail.m_validExchanges.to_s
puts ' underConId = ' + contract_detail.m_underConId.to_s
end

def order_msg(order)
puts ' order_msg:'
puts ' order id =' + order.m_orderId.to_s
puts ' client id =' + order.m_clientId.to_s
puts ' action =' + order.m_action.to_s
puts ' quantity =' + order.m_totalQuantity.to_s
puts ' type =' + order.m_orderType.to_s
puts ' lmtPrice =' + order.m_lmtPrice.to_s
puts ' TIF =' + order.m_tif.to_s
puts ' parent Id =' + order.m_parentId.to_s
puts ' permId =' + order.m_permId.to_s


end

def order_state_msg(order_state)
puts ' order_state_msg:'
puts ' status =' + order_state.m_status.to_s
puts ' initMargin =' + order_state.m_initMargin.to_s
puts ' maintMargin =' + order_state.m_maintMargin.to_s
puts ' commission =' + order_state.m_commission.to_s

end

#///////////////////////////////////////////////////////////////////////
#// Interface methods
#///////////////////////////////////////////////////////////////////////

def tickPrice( ticker_id, field, price, can_auto_execute)
end

def tickSize( ticker_id, field, size)
end

def tickOptionComputation( ticker_id, field, implied_vol,
delta, model_price, pv_dividend)
end

def tickGeneric( ticker_id, tick_type, value)
end

def tickString( ticker_id, tick_type, value)
end

def tickEFP( ticker_id, tick_type, basis_points,
formatted_basis_points, implied_future, hold_days,
future_expiry, dividend_impact, dividends_to_expiry)
end

def orderStatus( order_id, status, filled, remaining,
avg_fill_price, perm_id, parent_id, last_fill_price,
client_id, why_held)
puts '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
puts 'orderStatus>: orderId=' + order_id.to_s + ', clientId=' + client_id.to_s + ', permId=' + perm_id.to_s +
', status=' + status + ' filled=' + filled.to_s + ', remaining=' + remaining.to_s + ', '
puts ' avgFillPrice=' + avg_fill_price.to_s + ', lastFillPrice=' + last_fill_price.to_s +
', parent Id=' + parent_id.to_s + ', whyHeld=' + why_held.to_s
end

def openOrder( order_id, contract, order, order_state)
puts '--------------------------------------------------------'
puts 'openOrder> open order: orderId=' + order_id.to_s + ' --- '
contract_msg(contract)
order_msg(order)
order_state_msg(order_state)
end

def updateAccountValue( key, value, currency, account_name)
end

def updatePortfolio( contract, position, market_price, market_value,
average_cost, unrealized_pnl, realized_pnl, account_name)
end

def updateAccountTime( time_stamp)
end

# triggered right after logging into TWS
def nextValidId( order_id)
@order_id = order_id #storing order id to be used when placing order
puts 'nextValidId> Next Valid Order Id = ' + @order_id.to_s
end

# call back with details. determine which request it was and store details
# data with the associated request.
def contractDetails( req_id, contract_details)
aContract = contract_details.m_summary
# find the hash with matching :req_id and set :conid
@contractArray.find{ |contract| contract[:req_id] == req_id } [:conid] = aContract.m_conId

puts 'contractDetails()> reqId = ' + req_id.to_s + ' -------------------'
contract_msg(aContract)
contract_details_msg(contract_details)
puts ' --------'
end

def bondContractDetails( req_id, contract_details)
end

def contractDetailsEnd( req_id)
puts 'contractDetailsEnd() ' + req_id.to_s + '> Ended ------------------------'
end

def execDetails( order_id, contract, execution)
end

def updateMktDepth( ticker_id, position, operation, side, price, size)
end

def updateMktDepthL2( ticker_id, position, market_maker, operation,
side, price, size)
end

def updateNewsBulletin( msg_id, msgType, message, orig_exchange)
end

def managedAccounts( accounts_list)
end

def receiveFA( fa_data_type, xml)
end

def historicalData( req_id, date, open, high, low,
close, volume, count, eWAP, has_gaps)
end

def scannerParameters( xml)
end

def scannerData( req_id, rank, contract_details, distance,
benchmark, projection, legs_str)
end

def scannerDataEnd( req_id)
end

def realtimeBar( req_id, time, open, high, low, close, volume, wap, count)
end

def currentTime( time)
end

def fundamentalData( req_id, data)
end

# ---------------------------------------------------
# new callback methods added in API 9.60
# ---------------------------------------------------
def openOrderEnd() # invoked when all orders are sent to a client as a response to reqOpenOrders(),
# reqAllOpenOrders and right after connected to TWS
puts 'openOrderEnd> Ended ------------------------'
end

def accountDownloadEnd(account_name) #invoked when a complete snapshot of an account state is sent to a client as a response to reqAccountUpdates()
end

def execDetailsEnd( req_id) # invoked when all executions are sent to a client as a response to reqExecutions()
end

def deltaNeutralValidation(req_id, under_comp) # for Delta-Neutral DN RFQ
end

# ---------------------------------------------------
# -- methods from AnyWrapper -- must be declared
# ---------------------------------------------------

def connectionClosed()
puts ' [API.connectionClosed] Closed connection with TWS'
end

def error(err_obj_str)
puts ' [API.msg1] ' + err_obj_str if err_obj_str.is_a?(String)
puts ' [API.msg3] ' + err_obj_str.getMessage() if err_obj_str.is_a?(Exception)
end

def error(one, two, str)
puts ' [API.msg2] ' + str + ' {' + one.to_s + ', ' + two.to_s + '}'
end

end # class

# *********************************************************************************************
# Running the code
# *********************************************************************************************
x = TestComboOptionsOrder.new()
begin
x.eConnect
puts 'connected ...'
sleep(2) # need this here to give time for nextValidId() to be called
puts '******************************'
puts '** STEP 1) ******************'
puts '.. Defining contract for Aug 90 C '
spyAug90c = Contract.new
spyAug90c.m_symbol = 'SPY'
spyAug90c.m_secType = 'OPT'
spyAug90c.m_exchange = 'SMART'
spyAug90c.m_currency = 'USD'
spyAug90c.m_expiry = '20090821' # string format YYYYMM or YYYYMMDD -- if YYYYMM used and normal and quarterly
# options exist, will return contract details for both
spyAug90c.m_right = 'CALL' # CALL, PUT
spyAug90c.m_strike = 90 # number

puts '.. Adding Aug 90 C to contract array'
request_id = 1
x.contractArray << {:req_id => request_id, :contract => spyAug90c, :conid => '' }

puts '.. Requesting contract detail and id info'
x.reqContractDetails(request_id, spyAug90c)
sleep(5);
puts ' '
puts '.. Defining contract for Aug 95 C '
spyAug95c = Contract.new
spyAug95c.m_symbol = 'SPY'
spyAug95c.m_secType = 'OPT'
spyAug95c.m_exchange = 'SMART'
spyAug95c.m_currency = 'USD'
spyAug95c.m_expiry = '20090821' # string format YYYYMM or YYYYMMDD -- if YYYYMM used and normal and quarterly
# options exist, will return contract details for both
spyAug95c.m_right = 'CALL' # CALL, PUT
spyAug95c.m_strike = 95 # number
puts '.. Adding Jul 95 C to contract array'
request_id = 2
x.contractArray << {:req_id => request_id, :contract => spyAug95c, :conid => '' }
puts 'Requesting contract detail and id info'
x.reqContractDetails(request_id, spyAug95c)
sleep(5);

puts '******************************'
puts '** STEP 2 ******************'
puts '.. Now have info on both contracts, create combo leg for each'
puts '.. Create leg #1'
leg1 = ComboLeg.new(x.contractArray.find{ |id| id[:req_id] == 1 } [:conid], # contract id
1, # ratio
'BUY', # BUY or SELL
'SMART', # p_exchange
0 # p_openClose- always 0 for retail customers
)
puts '.. Create leg #2'
leg2 = ComboLeg.new(x.contractArray.find{ |id| id[:req_id] == 2 } [:conid], # contract id
1, # ratio
'SELL', # BUY or SELL
'SMART', # p_exchange
0 # p_openClose- always 0 for retail customers
)
puts '******************************'
puts '** STEP 3 ******************'
puts '.. Create Contract'

myContract = Contract.new
myContract.m_symbol = 'SPY'
myContract.m_secType = 'BAG' # BAG is a combination type
myContract.m_exchange = 'SMART'
myContract.m_currency = 'USD'

puts '.. Add leg 1 to combo contract'
myContract.m_comboLegs.add(leg1)
puts '.. Add leg 2 to combo contract'
myContract.m_comboLegs.add(leg2)
puts '******************************'
puts '** STEP 4 ******************'
puts '.. Create combo Order'
myOrder = Order.new
myOrder.m_action = 'BUY' # BUY, SELL
myOrder.m_totalQuantity = 1
myOrder.m_orderType = 'MKT' # MKT, LMT, STP, SPLMT, TRAIL, etc
myOrder.m_tif = 'DAY' # DAY, GTC

puts '.. submitting order'
x.submitOrder(x.order_id += 1, myContract, myOrder)
sleep(5);
puts '==========================='
puts '*** requesting open orders ***'
x.reqAllOpenOrders
sleep(5)
x.eDisconnect
puts 'disconnected'
rescue Exception => e
puts 'Exception ' + e.message
x.eDisconnect
end





Running the code produces the following output:


init
Server Version:44TWS Time at connection:20090712 16:51:47 ESTconnected ...
nextValidId> Next Valid Order Id = 81
openOrderEnd> Ended ------------------------
[API.msg2] Market data farm connection is OK:usopt {-1, 2104}
[API.msg2] Market data farm connection is OK:usfarm {-1, 2104}
******************************
** STEP 1) ******************

.. Defining contract for Aug 90 C
.. Adding Aug 90 C to contract array
.. Requesting contract detail and id info
contractDetails()> reqId = 1 -------------------
contract_msg:
conid = 60388362
symbol = SPY
secType = OPT
exchange = SMART
currency = USD
localSymbol = SWGHL
multiplier = 100
expiry = 20090821
strike = 90.0
right = C
contract_details_msg:
marketName = SWG
tradingClass = SWG
minTick = 0.01
price magnifier = 1
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,
validExchanges = SMART,AMEX,BOX,CBOE,IBSX,ISE,MIBSX,NASDAQOM,PHLX,PSE
underConId = 756733
--------
contractDetailsEnd() 1> Ended ------------------------

.. Defining contract for Aug 95 C
.. Adding Jul 95 C to contract array
Requesting contract detail and id info
contractDetails()> reqId = 2 -------------------
contract_msg:
conid = 60388382
symbol = SPY
secType = OPT
exchange = SMART
currency = USD
localSymbol = SWGHQ
multiplier = 100
expiry = 20090821
strike = 95.0
right = C
contract_details_msg:
marketName = SWG
tradingClass = SWG
minTick = 0.01
price magnifier = 1
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,
validExchanges = SMART,AMEX,BOX,CBOE,IBSX,ISE,MIBSX,NASDAQOM,PHLX,PSE
underConId = 756733
--------
contractDetailsEnd() 2> Ended ------------------------
******************************
** STEP 2 ******************

.. Now have info on both contracts, create combo leg for each
.. Create leg #1
.. Create leg #2
******************************
** STEP 3 ******************

.. Create Contract
.. Add leg 1 to combo contract
.. Add leg 2 to combo contract
******************************
** STEP 4 ******************

.. Create combo Order
.. submitting order
[API.msg2] Order Message:
Warning: your order will not be placed at the exchange until 2009-07-13 09:30:00 US/Eastern {82, 399}
--------------------------------------------------------
openOrder> open order: orderId=82 ---
contract_msg:
conid = 28812380
symbol = SPY
secType = BAG
exchange = SMART
currency = USD
localSymbol = SPY
multiplier =
expiry =
strike = 0.0
right = ?
order_msg:
order id =82
client id =0
action =BUY
quantity =1
type =MKT
lmtPrice =0.0
TIF =DAY
parent Id =0
permId =1009111242
order_state_msg:
status =PreSubmitted
initMargin =
maintMargin =
commission =1.7976931348623157e+308
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
orderStatus>: orderId=82, clientId=0, permId=1009111242, status=PreSubmitted filled=0, remaining=1,
avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=
===========================
*** requesting open orders ***
--------------------------------------------------------
openOrder> open order: orderId=82 ---
contract_msg:
conid = 28812380
symbol = SPY
secType = BAG
exchange = SMART
currency = USD
localSymbol = SPY
multiplier =
expiry =
strike = 0.0
right = ?
order_msg:
order id =82
client id =0
action =BUY
quantity =1
type =MKT
lmtPrice =0.0
TIF =DAY
parent Id =0
permId =1009111242
order_state_msg:
status =PreSubmitted
initMargin =
maintMargin =
commission =1.7976931348623157e+308
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
orderStatus>: orderId=82, clientId=0, permId=1009111242, status=PreSubmitted filled=0, remaining=1,
avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=
openOrderEnd> Ended ------------------------
disconnected


I ran the code after hours and was able to confirm that the order had been placed in TWS.


To see the individual legs of the order, just right click on the order and select 'Show Legs'



Individual legs will be displayed.



When the market opens the next day, my vertical call spread order executed at market price.



That's all there is to it. Complex option orders are not all that complex after all.

Sunday, July 12, 2009

Progress 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.

Stay tuned....

Wednesday, April 1, 2009

Progress...

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: Proof Of Concept 8 of 8g : Complex Options Order.

Stay tuned....

Wednesday, March 18, 2009

Proof Of Concept 8 of 8f : Simple Options Order

Simple 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.

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.


require 'java'
require 'TwsApi.jar'

include_class 'ib.client.EClientSocket'
include_class 'ib.client.Contract'
include_class 'ib.client.Order'
include_class 'ib.client.AnyWrapper'
include_class 'ib.client.EWrapper'

class TestOptionsOrder
include EWrapper #note "class TestMktData < EWrapper" syntax is no longer supported in JRuby 1.0

attr_accessor :order_id
@mySocket


def initialize
puts 'init'
@mySocket = EClientSocket.new(self)
end

def eConnect
@mySocket.eConnect("127.0.0.1", 7496, 0)
end

# Request all open orders that were placed from all API clients linked to one TWS,
# and also from the TWS
# This is a one time shot call. When its done you DO NOT continue to receive
# information about new placed orders
def reqAllOpenOrders
@mySocket.reqAllOpenOrders
end

def submitOptionsOrder(order_id, symbol, call_put, expiry, strike,
buy_sell, qty, order_type, limit_price, tif)
# --------------------------------
myContract = Contract.new
myContract.m_symbol = symbol
myContract.m_secType = 'OPT'
myContract.m_exchange = 'SMART'
myContract.m_currency = 'USD'
# options specific below ---
myContract.m_expiry = expiry # string format YYYYMM
myContract.m_right = call_put # CALL, PUT
myContract.m_strike = strike # number

myOrder = Order.new
myOrder.m_action = buy_sell # BUY, SELL
myOrder.m_totalQuantity = qty
myOrder.m_orderType = order_type # MKT, LMT, STP, SPLMT, TRAIL, etc
myOrder.m_lmtPrice = limit_price
myOrder.m_tif = tif # DAY, GTC
@mySocket.placeOrder(order_id, myContract, myOrder)
end

def cancelOrder(order_id)
@mySocket.cancelOrder(order_id)
end

# contract details will be received via the contractDetails() function on the EWrapper
def reqContractDetails (req_id, contract)
@mySocket.reqContractDetails(req_id, contract)
end

def eDisconnect
@mySocket.eDisconnect
end


def contract_msg(contract)
puts ' contract_msg:'
puts ' conid = ' + contract.m_conId.to_s
puts ' symbol = ' + contract.m_symbol
puts ' secType = ' + contract.m_secType
puts ' exchange = ' + contract.m_exchange.to_s
puts ' currency = ' + contract.m_currency
puts ' localSymbol = ' + contract.m_localSymbol.to_s
puts ' multiplier = ' + contract.m_multiplier.to_s
puts ' expiry = ' + contract.m_expiry.to_s
puts ' strike = ' + contract.m_strike.to_s
puts ' right = ' + contract.m_right.to_s
end

def order_msg(order)
puts ' order_msg:'
puts ' order id =' + order.m_orderId.to_s
puts ' client id =' + order.m_clientId.to_s
puts ' action =' + order.m_action.to_s
puts ' quantity =' + order.m_totalQuantity.to_s
puts ' type =' + order.m_orderType.to_s
puts ' lmtPrice =' + order.m_lmtPrice.to_s
puts ' TIF =' + order.m_tif.to_s
puts ' parent Id =' + order.m_parentId.to_s
puts ' permId =' + order.m_permId.to_s
end

def order_state_msg(order_state)
puts ' order_state_msg:'
puts ' status =' + order_state.m_status.to_s
puts ' initMargin =' + order_state.m_initMargin.to_s
puts ' maintMargin =' + order_state.m_maintMargin.to_s
puts ' commission =' + order_state.m_commission.to_s

end

#///////////////////////////////////////////////////////////////////////
#// Interface methods
#///////////////////////////////////////////////////////////////////////

def tickPrice( ticker_id, field, price, can_auto_execute)
end

def tickSize( ticker_id, field, size)
end

def tickOptionComputation( ticker_id, field, implied_vol,
delta, model_price, pv_dividend)
end

def tickGeneric( ticker_id, tick_type, value)
end

def tickString( ticker_id, tick_type, value)
end

def tickEFP( ticker_id, tick_type, basis_points,
formatted_basis_points, implied_future, hold_days,
future_expiry, dividend_impact, dividends_to_expiry)
end

def orderStatus( order_id, status, filled, remaining,
avg_fill_price, perm_id, parent_id, last_fill_price,
client_id, why_held)
puts '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
puts 'orderStatus>: orderId=' + order_id.to_s + ', clientId=' + client_id.to_s + ', permId=' + perm_id.to_s +
', status=' + status + ' filled=' + filled.to_s + ', remaining=' + remaining.to_s + ', '
puts ' avgFillPrice=' + avg_fill_price.to_s + ', lastFillPrice=' + last_fill_price.to_s +
', parent Id=' + parent_id.to_s + ', whyHeld=' + why_held.to_s
end

def openOrder( order_id, contract, order, order_state)
puts '--------------------------------------------------------'
puts 'openOrder> open order: orderId=' + order_id.to_s + ' --- '
contract_msg(contract)
order_msg(order)
order_state_msg(order_state)
end

def updateAccountValue( key, value, currency, account_name)
end

def updatePortfolio( contract, position, market_price, market_value,
average_cost, unrealized_pnl, realized_pnl, account_name)
end

def updateAccountTime( time_stamp)
end

# triggered right after logging into TWS
def nextValidId( order_id)
@order_id = order_id #storing order id to be used when placing order
puts 'nextValidId> Next Valid Order Id = ' + @order_id.to_s
end

def contractDetails( req_id, contract_details)
end

def bondContractDetails( req_id, contract_details)
end

def contractDetailsEnd( req_id)
end

def execDetails( order_id, contract, execution)
end

def updateMktDepth( ticker_id, position, operation, side, price, size)
end

def updateMktDepthL2( ticker_id, position, market_maker, operation,
side, price, size)
end

def updateNewsBulletin( msg_id, msgType, message, orig_exchange)
end

def managedAccounts( accounts_list)
end

def receiveFA( fa_data_type, xml)
end

def historicalData( req_id, date, open, high, low,
close, volume, count, eWAP, has_gaps)
end

def scannerParameters( xml)
end

def scannerData( req_id, rank, contract_details, distance,
benchmark, projection, legs_str)
end

def scannerDataEnd( req_id)
end

def realtimeBar( req_id, time, open, high, low, close, volume, wap, count)
end

def currentTime( time)
end

def fundamentalData( req_id, data)
end

# ---------------------------------------------------
# new callback methods added in API 9.60
# ---------------------------------------------------
def openOrderEnd() # invoked when all orders are sent to a client as a response to reqOpenOrders(),
# reqAllOpenOrders and right after connected to TWS
puts 'openOrderEnd> Ended ------------------------'
end

def accountDownloadEnd(account_name) #invoked when a complete snapshot of an account state is sent to a client as a response to reqAccountUpdates()
end

def execDetailsEnd( req_id) # invoked when all executions are sent to a client as a response to reqExecutions()
end

def deltaNeutralValidation(req_id, under_comp) # for Delta-Neutral DN RFQ
end

# ---------------------------------------------------
# -- methods from AnyWrapper -- must be declared
# ---------------------------------------------------

def connectionClosed()
puts ' [API.connectionClosed] Closed connection with TWS'
end

def error(err_obj_str)
puts ' [API.msg1] ' + err_obj_str if err_obj_str.is_a?(String)
puts ' [API.msg3] ' + err_obj_str.getMessage() if err_obj_str.is_a?(Exception)
end

def error(one, two, str)
puts ' [API.msg2] ' + str + ' {' + one.to_s + ', ' + two.to_s + '}'
end

end # class

# *********************************************************************************************
# Running the code
# *********************************************************************************************
x = TestOptionsOrder.new()
begin
x.eConnect
puts 'connected ...'
sleep(2) # need this here to give time for nextValidId() to be called

puts '*****************************'
puts '**** LIMIT ORDER ****'
o1 = x.order_id
x.submitOptionsOrder(o1, 'IWM', 'CALL', '200909', 38,
'BUY', 2, 'LMT', 5, 'DAY')
sleep(5)
puts '*** requesting open orders ***'
x.reqAllOpenOrders
sleep(5)
puts '*****************************'
puts '**** MARKET ORDER - STC EXISTING POSITION ****'
o2 = x.order_id += 1
x.submitOptionsOrder(o2, 'RUT', 'CALL', '200904', 400,
'SELL', 1, 'MKT', 0, 'DAY')
sleep(5)
puts '*** requesting open orders ***'
x.reqAllOpenOrders
sleep(5)
x.eDisconnect
puts 'disconnected'
rescue Exception => e
puts 'Exception ' + e.message
x.eDisconnect
end



Running the code produces the following output:

init
Server Version:43

TWS Time at connection:20090317 15:26:34 EST

connected ...
nextValidId> Next Valid Order Id = 69
openOrderEnd> Ended ------------------------
[API.msg2] Market data farm connection is OK:usopt {-1, 2104}
[API.msg2] Market data farm connection is OK:usfarm {-1, 2104}
*****************************
**** LIMIT ORDER ****
--------------------------------------------------------
openOrder> open order: orderId=69 ---
contract_msg:
conid = 56413283
symbol = IWM
secType = OPT
exchange = SMART
currency = USD
localSymbol = IQXIL
multiplier =
expiry = 20090930
strike = 38.0
right = C
order_msg:
order id =69
client id =0
action =BUY
quantity =2
type =LMT
lmtPrice =5.0
TIF =DAY
parent Id =0
permId =263195297
order_state_msg:
status =Submitted
initMargin =
maintMargin =
commission =1.7976931348623157e+308
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
orderStatus>: orderId=69, clientId=0, permId=263195297, status=Submitted filled=0, remaining=2,
avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=
*** requesting open orders ***
--------------------------------------------------------
openOrder> open order: orderId=69 ---
contract_msg:
conid = 56413283
symbol = IWM
secType = OPT
exchange = SMART
currency = USD
localSymbol = IQXIL
multiplier =
expiry = 20090930
strike = 38.0
right = C
order_msg:
order id =69
client id =0
action =BUY
quantity =2
type =LMT
lmtPrice =5.0
TIF =DAY
parent Id =0
permId =263195297
order_state_msg:
status =Submitted
initMargin =
maintMargin =
commission =1.7976931348623157e+308
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
orderStatus>: orderId=69, clientId=0, permId=263195297, status=Submitted filled=0, remaining=2,
avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=
openOrderEnd> Ended ------------------------
*****************************
**** MARKET ORDER - STC EXISTING POSITION ****
*** requesting open orders ***
--------------------------------------------------------
openOrder> open order: orderId=69 ---
contract_msg:
conid = 56413283
symbol = IWM
secType = OPT
exchange = SMART
currency = USD
localSymbol = IQXIL
multiplier =
expiry = 20090930
strike = 38.0
right = C
order_msg:
order id =69
client id =0
action =BUY
quantity =2
type =LMT
lmtPrice =5.0
TIF =DAY
parent Id =0
permId =263195297
order_state_msg:
status =Submitted
initMargin =
maintMargin =
commission =1.7976931348623157e+308
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
orderStatus>: orderId=69, clientId=0, permId=263195297, status=Submitted filled=0, remaining=2,
avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=
openOrderEnd> Ended ------------------------
disconnected

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:



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:


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.

To complete the original example, I modified the code to just submit the RUT order again


puts '*****************************'
puts '**** MARKET ORDER - STC EXISTING POSITION ****'
o2 = x.order_id += 1
x.submitOptionsOrder(o2, 'RUT', 'CALL', '200904', 400,
'SELL', 1, 'MKT', 0, 'DAY')
sleep(5)
puts '*** requesting open orders ***'

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.

init
Server Version:43

TWS Time at connection:20090317 15:42:52 EST

connected ...
nextValidId> Next Valid Order Id = 73
openOrderEnd> Ended ------------------------
[API.msg2] Market data farm connection is OK:usopt {-1, 2104}
[API.msg2] Market data farm connection is OK:usfarm {-1, 2104}
**** MARKET ORDER - STC EXISTING POSITION ****
--------------------------------------------------------
openOrder> open order: orderId=74 ---
contract_msg:
conid = 57330039
symbol = RUT
secType = OPT
exchange = SMART
currency = USD
localSymbol = RUWDX
multiplier =
expiry = 20090416
strike = 400.0
right = C
order_msg:
order id =74
client id =0
action =SELL
quantity =1
type =MKT
lmtPrice =0.0
TIF =DAY
parent Id =0
permId =263195311
order_state_msg:
status =Filled
initMargin =
maintMargin =
commission =1.7976931348623157e+308
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
orderStatus>: orderId=74, clientId=0, permId=263195311, status=Filled filled=1, remaining=0,
avgFillPrice=20.0, lastFillPrice=20.0, parent Id=0, whyHeld=
--------------------------------------------------------
openOrder> open order: orderId=74 ---
contract_msg:
conid = 57330039
symbol = RUT
secType = OPT
exchange = SMART
currency = USD
localSymbol = RUWDX
multiplier =
expiry = 20090416
strike = 400.0
right = C
order_msg:
order id =74
client id =0
action =SELL
quantity =1
type =MKT
lmtPrice =0.0
TIF =DAY
parent Id =0
permId =263195311
order_state_msg:
status =Filled
initMargin =
maintMargin =
commission =1.7976931348623157e+308
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
orderStatus>: orderId=74, clientId=0, permId=263195311, status=Filled filled=1, remaining=0,
avgFillPrice=20.0, lastFillPrice=20.0, parent Id=0, whyHeld=
*** requesting open orders ***
openOrderEnd> Ended ------------------------
--------------------------------------------------------
openOrder> open order: orderId=74 ---
contract_msg:
conid = 57330039
symbol = RUT
secType = OPT
exchange = SMART
currency = USD
localSymbol = RUWDX
multiplier =
expiry = 20090416
strike = 400.0
right = C
order_msg:
order id =74
client id =0
action =SELL
quantity =1
type =MKT
lmtPrice =0.0
TIF =DAY
parent Id =0
permId =263195311
order_state_msg:
status =Filled
initMargin =
maintMargin =
commission =1.18
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
orderStatus>: orderId=74, clientId=0, permId=263195311, status=Filled filled=1, remaining=0,
avgFillPrice=20.0, lastFillPrice=20.0, parent Id=0, whyHeld=
disconnected




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.

init
Server Version:43

TWS Time at connection:20090317 15:11:41 EST

connected ...
nextValidId> Next Valid Order Id = 64
openOrderEnd> Ended ------------------------
[API.msg2] Market data farm connection is OK:usopt {-1, 2104}
[API.msg2] Market data farm connection is OK:usfarm {-1, 2104}
*****************************
**** LIMIT ORDER ****
--------------------------------------------------------
openOrder> open order: orderId=64 ---
contract_msg:
conid = 54336659
symbol = IWM
secType = OPT
exchange = SMART
currency = USD
localSymbol = IQQIW
multiplier =
expiry = 20090930
strike = 75.0
right = C
order_msg:
order id =64
client id =0
action =BUY
quantity =2
type =LMT
lmtPrice =7.75
TIF =DAY
parent Id =0
permId =263195279
order_state_msg:
status =PendingCancel
initMargin =
maintMargin =
commission =1.7976931348623157e+308
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
orderStatus>: orderId=64, clientId=0, permId=263195279, status=PendingCancel filled=0, remaining=2,
avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=
--------------------------------------------------------
openOrder> open order: orderId=64 ---
contract_msg:
conid = 54336659
symbol = IWM
secType = OPT
exchange = SMART
currency = USD
localSymbol = IQQIW
multiplier =
expiry = 20090930
strike = 75.0
right = C
order_msg:
order id =64
client id =0
action =BUY
quantity =2
type =LMT
lmtPrice =7.75
TIF =DAY
parent Id =0
permId =263195279
order_state_msg:
status =PendingCancel
initMargin =
maintMargin =
commission =1.7976931348623157e+308
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
orderStatus>: orderId=64, clientId=0, permId=263195279, status=PendingCancel filled=0, remaining=2,
avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=
[API.msg2] Order Canceled - reason:Limit price too far outside of NBBO {64, 202}


Wednesday, March 11, 2009

Proof Of Concept 8 of 8e : Stock Order

Creating code to place a stock order is not complicated. However, a thorough understanding of the sequence of events is important.

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.


[API.msg2] Duplicate order id {10, 103}


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.

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.


The following code example submits a limit order (done in after trading hours)


require 'java'
require 'TwsApi.jar'

include_class 'ib.client.EClientSocket'
include_class 'ib.client.Contract'
include_class 'ib.client.Order'
include_class 'ib.client.AnyWrapper'
include_class 'ib.client.EWrapper'

class TestStockOrder
include EWrapper #note "class TestMktData < EWrapper" syntax is no longer supported in JRuby 1.0

attr_accessor :order_id # makes order_id variable accessible
@mySocket

def initialize
puts 'init'
@mySocket = EClientSocket.new(self)
end

def eConnect
@mySocket.eConnect("127.0.0.1", 7496, 0)
end

# Request all open orders that were placed from all API clients linked to one TWS,
# and also from the TWS
# This is a one time shot call. When its done you DO NOT continue to receive
# information about new placed orders
def reqAllOpenOrders
@mySocket.reqAllOpenOrders
end

def submitStockOrder(order_id, symbol, buy_sell, qty, order_type, limit_price, tif)
# --------------------------------
myContract = Contract.new
myContract.m_symbol = symbol
myContract.m_secType = 'STK'
myContract.m_exchange = 'SMART'
myContract.m_currency = 'USD'

myOrder = Order.new
myOrder.m_action = buy_sell # BUY, SELL
myOrder.m_totalQuantity = qty
myOrder.m_orderType = order_type # MKT, LMT, STP, SPLMT, TRAIL, etc
myOrder.m_lmtPrice = limit_price
myOrder.m_tif = tif # DAY, GTC
@mySocket.placeOrder(order_id, myContract, myOrder)
end

def cancelOrder(order_id)
@mySocket.cancelOrder(order_id)
end

def eDisconnect
@mySocket.eDisconnect
end

def contract_msg(contract)
puts ' contract_msg:'
puts ' conid = ' + contract.m_conId.to_s
puts ' symbol = ' + contract.m_symbol
puts ' secType = ' + contract.m_secType
puts ' exchange = ' + contract.m_exchange.to_s
puts ' currency = ' + contract.m_currency
puts ' localSymbol = ' + contract.m_localSymbol.to_s
end

def order_msg(order)
puts ' order_msg:'
puts ' order id =' + order.m_orderId.to_s
puts ' client id =' + order.m_clientId.to_s
puts ' action =' + order.m_action.to_s
puts ' quantity =' + order.m_totalQuantity.to_s
puts ' type =' + order.m_orderType.to_s
puts ' lmtPrice =' + order.m_lmtPrice.to_s
puts ' TIF =' + order.m_tif.to_s
puts ' parent Id =' + order.m_parentId.to_s
puts ' permId =' + order.m_permId.to_s


end

def order_state_msg(order_state)
puts ' order_state_msg:'
puts ' status =' + order_state.m_status.to_s
puts ' initMargin =' + order_state.m_initMargin.to_s
puts ' maintMargin =' + order_state.m_maintMargin.to_s
puts ' commission =' + order_state.m_commission.to_s

end

#///////////////////////////////////////////////////////////////////////
#// Interface methods
#///////////////////////////////////////////////////////////////////////

def tickPrice( ticker_id, field, price, can_auto_execute)
end

def tickSize( ticker_id, field, size)
end

def tickOptionComputation( ticker_id, field, implied_vol,
delta, model_price, pv_dividend)
end

def tickGeneric( ticker_id, tick_type, value)
end

def tickString( ticker_id, tick_type, value)
end

def tickEFP( ticker_id, tick_type, basis_points,
formatted_basis_points, implied_future, hold_days,
future_expiry, dividend_impact, dividends_to_expiry)
end

def orderStatus( order_id, status, filled, remaining,
avg_fill_price, perm_id, parent_id, last_fill_price,
client_id, why_held)
puts '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
puts 'orderStatus>: orderId=' + order_id.to_s + ', clientId=' + client_id.to_s + ', permId=' + perm_id.to_s +
', status=' + status + ' filled=' + filled.to_s + ', remaining=' + remaining.to_s + ', '
puts ' avgFillPrice=' + avg_fill_price.to_s + ', lastFillPrice=' + last_fill_price.to_s +
', parent Id=' + parent_id.to_s + ', whyHeld=' + why_held.to_s
end

def openOrder( order_id, contract, order, order_state)
puts '--------------------------------------------------------'
puts 'openOrder> open order: orderId=' + order_id.to_s + ' --- '
contract_msg(contract)
order_msg(order)
order_state_msg(order_state)
end

def updateAccountValue( key, value, currency, account_name)
end

def updatePortfolio( contract, position, market_price, market_value,
average_cost, unrealized_pnl, realized_pnl, account_name)
end

def updateAccountTime( time_stamp)
end

# triggered right after logging into TWS
def nextValidId( order_id)
@order_id = order_id #storing order id to be used when placing order
puts 'nextValidId> Next Valid Order Id = ' + @order_id.to_s
end

def contractDetails( req_id, contract_details)
end

def bondContractDetails( req_id, contract_details)
end

def contractDetailsEnd( req_id)
end

def execDetails( order_id, contract, execution)
end

def updateMktDepth( ticker_id, position, operation, side, price, size)
end

def updateMktDepthL2( ticker_id, position, market_maker, operation,
side, price, size)
end

def updateNewsBulletin( msg_id, msgType, message, orig_exchange)
end

def managedAccounts( accounts_list)
end

def receiveFA( fa_data_type, xml)
end

def historicalData( req_id, date, open, high, low,
close, volume, count, eWAP, has_gaps)
end

def scannerParameters( xml)
end

def scannerData( req_id, rank, contract_details, distance,
benchmark, projection, legs_str)
end

def scannerDataEnd( req_id)
end

def realtimeBar( req_id, time, open, high, low, close, volume, wap, count)
end

def currentTime( time)
end

def fundamentalData( req_id, data)
end

# ---------------------------------------------------
# new callback methods added in API 9.60
# ---------------------------------------------------
def openOrderEnd() # invoked when all orders are sent to a client as a response to reqOpenOrders(),
# reqAllOpenOrders and right after connected to TWS
puts 'openOrderEnd> Ended'
end

def accountDownloadEnd(account_name) #invoked when a complete snapshot of an account state is sent to a client as a response to reqAccountUpdates()
end

def execDetailsEnd( req_id) # invoked when all executions are sent to a client as a response to reqExecutions()
end

def deltaNeutralValidation(req_id, under_comp) # for Delta-Neutral DN RFQ
end

# ---------------------------------------------------
# -- methods from AnyWrapper -- must be declared
# ---------------------------------------------------

def connectionClosed()
puts ' [API.connectionClosed] Closed connection with TWS'
end

def error(err_obj_str)
puts ' [API.msg1] ' + err_obj_str if err_obj_str.is_a?(String)
puts ' [API.msg3] ' + err_obj_str.getMessage() if err_obj_str.is_a?(Exception)
end

def error(one, two, str)
puts ' [API.msg2] ' + str + ' {' + one.to_s + ', ' + two.to_s + '}'
end

end

# *********************************************************************************************
# Running the code
# *********************************************************************************************
x = TestStockOrder.new()
begin
x.eConnect
puts 'connected ...'
sleep(2) # need this here to give time for nextValidId() to be called
puts '*****************************'
puts '**** LIMIT ORDER - LIMIT ****'
o1 = x.order_id
x.submitStockOrder(o1, 'IWM', 'BUY', 50, 'LMT', 10, 'DAY')
sleep(5)
puts '*** requesting open orders ***'
x.reqAllOpenOrders
sleep(5)
x.eDisconnect
puts 'disconnected'
rescue Exception => e

puts 'Exception ' + e.message
x.eDisconnect
end

Line 13: declaration of variable order_id to store order id
Line 147: initialize order_id after connected to TWS

Running the code produces the following output:
init
Server Version:43

TWS Time at connection:20090311 22:22:42 EST

connected ...
nextValidId> Next Valid Order Id = 47
openOrderEnd> Ended
[API.msg2] Market data farm connection is OK:usopt {-1, 2104}
[API.msg2] Market data farm connection is OK:usfarm {-1, 2104}
*****************************
**** LIMIT ORDER - LIMIT ****
[API.msg2] Order Message:
Warning: your order will not be placed at the exchange until 2009-03-12 09:30:00 US/Eastern {47, 399}
--------------------------------------------------------
openOrder> open order: orderId=47 ---
contract_msg:
conid = 9579970
symbol = IWM
secType = STK
exchange = SMART
currency = USD
localSymbol = IWM
order_msg:
order id =47
client id =0
action =BUY
quantity =50
type =LMT
lmtPrice =10.0
TIF =DAY
parent Id =0
permId =1309177719
order_state_msg:
status =PreSubmitted
initMargin =
maintMargin =
commission =1.7976931348623157e+308
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
orderStatus>: orderId=45, clientId=0, permId=1309177719, status=PreSubmitted filled=0, remaining=50,
avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=

*** requesting open orders ***
--------------------------------------------------------
openOrder> open order: orderId=47 ---
contract_msg:
conid = 9579970
symbol = IWM
secType = STK
exchange = SMART
currency = USD
localSymbol = IWM
order_msg:
order id =47
client id =0
action =BUY
quantity =50
type =LMT
lmtPrice =10.0
TIF =DAY
parent Id =0
permId =1309177719
order_state_msg:
status =PreSubmitted
initMargin =
maintMargin =
commission =1.7976931348623157e+308
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
orderStatus>: orderId=47, clientId=0, permId=1309177719, status=PreSubmitted filled=0, remaining=50,
avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=
openOrderEnd> Ended
disconnected


Note method openOrderEnd() was triggered right after connecting to TWS and after call to reqAllOpenOrders().
openOrder() method was also triggered right after submitting the order using placeOrder().
The order was successfully created and may be validated in TWS.





03/12/09 Update

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:

# *********************************************************************************************
# Running the code
# *********************************************************************************************
x = TestStockOrder.new()
begin
x.eConnect
puts 'connected ...'
sleep(2) # need this here to give time for nextValidId() to be called
puts '*****************************'
puts '**** LIMIT ORDER and CANCEL ****'
o2 = x.order_id += 1
x.submitStockOrder(o2, 'PFE', 'BUY', 10, 'LMT', 5.50, 'DAY') # Order 16
sleep(5)
puts '*** Cancelling order ***'
x.cancelOrder(o2) # does not trigger anything, need to check status of order
sleep(5)
puts '*** requesting open orders ***'
x.reqAllOpenOrders
puts '*****************************'
puts '**** MARKET ORDER ****'
x.submitStockOrder(x.order_id += 1, 'INTC', 'BUY', 20, 'MKT', 0, 'DAY')
sleep(5)
puts '*** requesting open orders ***'
x.reqAllOpenOrders
sleep(5)
x.eDisconnect
puts 'disconnected'
rescue Exception => e
puts 'Exception ' + e.message
x.eDisconnect
end


Running the code produces the following output:

init
Server Version:43

TWS Time at connection:20090312 15:10:08 EST

connected ...
nextValidId> Next Valid Order Id = 51
--------------------------------------------------------
openOrder> open order: orderId=47 ---
contract_msg:
conid = 9579970
symbol = IWM
secType = STK
exchange = SMART
currency = USD
localSymbol =
order_msg:
order id =47
client id =0
action =BUY
quantity =50
type =LMT
lmtPrice =10.0
TIF =DAY
parent Id =0
permId =1309177719
order_state_msg:
status =Submitted
initMargin =
maintMargin =
commission =1.7976931348623157e+308
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
orderStatus>: orderId=47, clientId=0, permId=1309177719, status=Submitted filled=0, remaining=50,
avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=

openOrderEnd> Ended
[API.msg2] Market data farm connection is OK:usopt {-1, 2104}
[API.msg2] Market data farm connection is OK:usfarm {-1, 2104}
*****************************
**** LIMIT ORDER and CANCEL ****
--------------------------------------------------------
openOrder> open order: orderId=52 ---
contract_msg:
conid = 11031
symbol = PFE
secType = STK
exchange = SMART
currency = USD
localSymbol = PFE
order_msg:
order id =52
client id =0
action =BUY
quantity =10
type =LMT
lmtPrice =5.5
TIF =DAY
parent Id =0
permId =888579469
order_state_msg:
status =Submitted
initMargin =
maintMargin =
commission =1.7976931348623157e+308
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
orderStatus>: orderId=52, clientId=0, permId=888579469, status=Submitted filled=0, remaining=10,
avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=
*** Cancelling order ***
[API.msg2] Order Canceled - reason: {52, 202}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
orderStatus>: orderId=52, clientId=0, permId=888579469, status=Cancelled filled=0, remaining=10,
avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=

*** requesting open orders ***
--------------------------------------------------------
openOrder> open order: orderId=47 ---
contract_msg:
conid = 9579970
symbol = IWM
secType = STK
exchange = SMART
currency = USD
localSymbol =
order_msg:
order id =47
client id =0
action =BUY
quantity =50
type =LMT
lmtPrice =10.0
TIF =DAY
parent Id =0
permId =1309177719
order_state_msg:
status =Submitted
initMargin =
maintMargin =
commission =1.7976931348623157e+308
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
orderStatus>: orderId=47, clientId=0, permId=1309177719, status=Submitted filled=0, remaining=50,
avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=
openOrderEnd> Ended
*****************************
**** MARKET ORDER ****
--------------------------------------------------------
openOrder> open order: orderId=53 ---
contract_msg:
conid = 270639
symbol = INTC
secType = STK
exchange = SMART
currency = USD
localSymbol = INTC
order_msg:
order id =53
client id =0
action =BUY
quantity =20
type =MKT
lmtPrice =0.0
TIF =DAY
parent Id =0
permId =888579470
order_state_msg:
status =Filled
initMargin =
maintMargin =
commission =1.7976931348623157e+308
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
orderStatus>: orderId=53, clientId=0, permId=888579470, status=Filled filled=20, remaining=0,
avgFillPrice=14.36, lastFillPrice=14.36, parent Id=0, whyHeld=
--------------------------------------------------------
openOrder> open order: orderId=53 ---
contract_msg:
conid = 270639
symbol = INTC
secType = STK
exchange = SMART
currency = USD
localSymbol = INTC
order_msg:
order id =53
client id =0
action =BUY
quantity =20
type =MKT
lmtPrice =0.0
TIF =DAY
parent Id =0
permId =888579470
order_state_msg:
status =Filled
initMargin =
maintMargin =
commission =1.7976931348623157e+308
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
orderStatus>: orderId=53, clientId=0, permId=888579470, status=Filled filled=20, remaining=0,
avgFillPrice=14.36, lastFillPrice=14.36, parent Id=0, whyHeld=
--------------------------------------------------------
openOrder> open order: orderId=53 ---
contract_msg:
conid = 270639
symbol = INTC
secType = STK
exchange = SMART
currency = USD
localSymbol = INTC
order_msg:
order id =53
client id =0
action =BUY
quantity =20
type =MKT
lmtPrice =0.0
TIF =DAY
parent Id =0
permId =888579470
order_state_msg:
status =Filled
initMargin =
maintMargin =
commission =1.0
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
orderStatus>: orderId=53, clientId=0, permId=888579470, status=Filled filled=20, remaining=0,
avgFillPrice=14.36, lastFillPrice=14.36, parent Id=0, whyHeld=
--------------------------------------------------------

*** requesting open orders ***
openOrder> open order: orderId=47 ---
contract_msg:
conid = 9579970
symbol = IWM
secType = STK
exchange = SMART
currency = USD
localSymbol =
order_msg:
order id =47
client id =0
action =BUY
quantity =50
type =LMT
lmtPrice =10.0
TIF =DAY
parent Id =0
permId =1309177719
order_state_msg:
status =Submitted
initMargin =
maintMargin =
commission =1.7976931348623157e+308
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
orderStatus>: orderId=47, clientId=0, permId=1309177719, status=Submitted filled=0, remaining=50,
avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=
openOrderEnd> Ended
disconnected

Note:
- Order for IWM submitted yesterday is in Submitted status(it was in PreSubmitted last night).
- 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.
- Market order to purchase INTC status is Filled and triggers orderStatus() callback method multiple times.
- Purchase of INTC can be verified in TWS.

Saturday, March 7, 2009

Upgrade To API v. 9.60

It 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.

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 (Proof Of Concept - 4 of 8) and create a new version of TwsApi.jar file (Proof of Concept - 5 of 8). 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:

# ---------------------------------------------------
# new callback methods added in API 9.60
# ---------------------------------------------------
def openOrderEnd() # invoked when all orders are sent to a client as a response to reqOpenOrders()
end

def accountDownloadEnd(account_name) #invoked when a complete snapshot of an account state is sent to a client as a response to reqAccountUpdates()
end

def execDetailsEnd( req_id) # invoked when all executions are sent to a client as a response to reqExecutions()
end

def deltaNeutralValidation(req_id, under_comp) # for Delta-Neutral DN RFQ
end

I will go through each of the Proof Of Concept entries and update them to comply with API v. 9.60.

Sunday, March 1, 2009

Proof Of Concept 8 of 8d : Order Status

TWS 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.

reqOpenOrders() and reqAllOpenOrders() are both one time calls and current open order information is provided through the callback methods.

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.

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.



The code demonstrating reqAllOpenOrders() is as follows:

require 'java'
require 'TwsApi.jar'

include_class 'ib.client.EClientSocket'
include_class 'ib.client.Contract'
include_class 'ib.client.Order'
include_class 'ib.client.OrderState'
include_class 'ib.client.AnyWrapper'
include_class 'ib.client.EWrapper'



class TestOrderStatus
include EWrapper #note "class TestMktData < EWrapper" syntax is no longer supported in JRuby 1.0

@mySocket

def initialize
puts 'init'
@mySocket = EClientSocket.new(self)
end

def eConnect
@mySocket.eConnect("127.0.0.1", 7496, 0)
end

# To request the open orders that were placed from this client. Each open
# order will be fed back through the openOrder() and orderStatus() functions
# on the EWrapper.
# This is a one time shot call. When its done you DO NOT continue to receive
# information about new placed orders
def reqOpenOrders
@mySocket.reqOpenOrders
end

# Request all open orders that were placed from all API clients linked to one TWS,
# and also from the TWS
# This is a one time shot call. When its done you DO NOT continue to receive
# information about new placed orders
def reqAllOpenOrders
@mySocket.reqAllOpenOrders
end

# Request that newly created TWS orders be implicitly associated with the client
# Bind - If set to TRUE, newly created TWS orders will be implicitly associated
# with the client. If set to FALSE, no association will be made.
#
def reqAutoOpenOrders(bind)
@mySocket.reqAutoOpenOrders(bind)
end


def eDisconnect
@mySocket.eDisconnect
end

def requestStockQuote(id, ticker)
# --------------------------------
myContract = Contract.new
myContract.m_symbol = ticker
myContract.m_secType = 'STK'
myContract.m_exchange = 'SMART'
myContract.m_currency = 'USD'
request_id = id
ticklist = ''

puts '>requestStockQuote - calling reqMktData id='+id.to_s+', ticker='+ticker
@mySocket.reqMktData(request_id, myContract, ticklist, true)
end

def contract_msg(contract)
puts ' contract_msg:'
puts ' conid = ' + contract.m_conId.to_s
puts ' symbol = ' + contract.m_symbol
puts ' secType = ' + contract.m_secType
puts ' expiry = ' + contract.m_expiry.to_s
puts ' strike = ' + contract.m_strike.to_s
puts ' right = ' + contract.m_right
puts ' multiplier = ' + contract.m_multiplier.to_s
puts ' exchange = ' + contract.m_exchange.to_s
puts ' primaryExch = ' + contract.m_primaryExch.to_s
puts ' currency = ' + contract.m_currency
puts ' localSymbol = ' + contract.m_localSymbol
end

def order_msg(order)
puts ' order_msg:'
puts ' order id =' + order.m_orderId.to_s
puts ' client id =' + order.m_clientId.to_s
puts ' action =' + order.m_action.to_s
puts ' quantity =' + order.m_totalQuantity.to_s
puts ' type =' + order.m_orderType.to_s
puts ' lmtPrice =' + order.m_lmtPrice.to_s
puts ' auxPrice =' + order.m_auxPrice.to_s
puts ' TIF =' + order.m_tif.to_s
puts ' parent Id =' + order.m_parentId.to_s
puts ' permId =' + order.m_permId.to_s
puts ' outsideRth =' + order.m_outsideRth.to_s
puts ' hidden =' + order.m_hidden.to_s
puts ' discretionaryAmt=' + order.m_discretionaryAmt.to_s
puts ' triggerMethod=' + order.m_triggerMethod.to_s
puts ' goodAfterTime=' + order.m_goodAfterTime.to_s
puts ' goodTillDate =' + order.m_goodTillDate.to_s
puts ' faGroup =' + order.m_faGroup.to_s
puts ' faMethod =' + order.m_faMethod.to_s
puts ' faPercentage =' + order.m_faPercentage.to_s
puts ' faProfile =' + order.m_faProfile.to_s
puts ' shortSaleSlot=' + order.m_shortSaleSlot.to_s
puts ' designatedLocation=' + order.m_designatedLocation.to_s
puts ' ocaGroup =' + order.m_ocaGroup.to_s
puts ' ocaType =' + order.m_ocaType.to_s
puts ' rule80A =' + order.m_rule80A.to_s
puts ' allOrNone =' + order.m_allOrNone.to_s #yes=1, no=0
puts ' minQty =' + order.m_minQty.to_s
puts ' percentOffset=' + order.m_percentOffset.to_s
puts ' eTradeOnly =' + order.m_eTradeOnly.to_s
puts ' firmQuoteOnly=' + order.m_firmQuoteOnly.to_s
puts ' nbboPriceCap =' + order.m_nbboPriceCap.to_s
puts ' auctionStrategy=' + order.m_auctionStrategy.to_s
puts ' startingPrice=' + order.m_startingPrice.to_s
puts ' stockRefPrice=' + order.m_stockRefPrice.to_s
puts ' delta =' + order.m_delta.to_s
puts ' stockRangeLower=' + order.m_stockRangeLower.to_s
puts ' stockRangeUpper=' + order.m_stockRangeUpper.to_s
puts ' volatility =' + order.m_volatility.to_s
puts ' volatilityType=' + order.m_volatilityType.to_s
puts ' deltaNeutralOrderType=' + order.m_deltaNeutralOrderType.to_s
puts ' deltaNeutralAuxPrice=' + order.m_deltaNeutralAuxPrice.to_s
puts ' continuousUpdate=' + order.m_continuousUpdate.to_s
puts ' referencePriceType=' + order.m_referencePriceType.to_s
puts ' trailStopPrice=' + order.m_trailStopPrice.to_s
puts ' account =' + order.m_account # institutional only
puts ' settlingFirm =' + order.m_settlingFirm.to_s
puts ' clearingAccount=' + order.m_clearingAccount.to_s
puts ' clearingIntent=' + order.m_clearingIntent.to_s
puts ' whatIf =' + order.m_whatIf.to_s
end

def order_state_msg(order_state)
puts ' order_state_msg:'
puts ' status =' + order_state.m_status.to_s
puts ' initMargin =' + order_state.m_initMargin.to_s
puts ' maintMargin =' + order_state.m_maintMargin.to_s
puts ' equityWithLoan=' + order_state.m_equityWithLoan.to_s
puts ' commission =' + order_state.m_commission.to_s
puts ' minCommission =' + order_state.m_minCommission.to_s
puts ' maxCommission =' + order_state.m_maxCommission.to_s
puts ' commissionCurrency=' + order_state.m_commissionCurrency.to_s
puts ' warningText =' + order_state.m_warningText.to_s
end


#///////////////////////////////////////////////////////////////////////
#// Interface methods
#///////////////////////////////////////////////////////////////////////

def tickPrice( ticker_id, field, price, can_auto_execute)
end

def tickSize( ticker_id, field, size)
end

def tickOptionComputation( ticker_id, field, implied_vol,
delta, model_price, pv_dividend)
end

def tickGeneric( ticker_id, tick_type, value)
end

def tickString( ticker_id, tick_type, value)
end

def tickEFP( ticker_id, tick_type, basis_points,
formatted_basis_points, implied_future, hold_days,
future_expiry, dividend_impact, dividends_to_expiry)
end

def orderStatus( order_id, status, filled, remaining,
avg_fill_price, perm_id, parent_id, last_fill_price,
client_id, why_held)
puts 'orderStatus>: orderId=' + order_id.to_s + ', clientId=' + client_id.to_s + ', permId=' + perm_id.to_s +
', status=' + status + ' filled=' + filled.to_s + ', remaining=' + remaining.to_s + ', '
puts ' avgFillPrice=' + avg_fill_price.to_s + ', lastFillPrice=' + last_fill_price.to_s +
', parent Id=' + parent_id.to_s + ', whyHeld=' + why_held.to_s
end

def openOrder( order_id, contract, order, order_state)
puts 'openOrder> open order: orderId=' + order_id.to_s + ' --- '
contract_msg(contract)
order_msg(order)
order_state_msg(order_state)
end

def updateAccountValue( key, value, currency, account_name)
end

def updatePortfolio( contract, position, market_price, market_value,
average_cost, unrealized_pnl, realized_pnl, account_name)
end

def updateAccountTime( time_stamp)
end

def nextValidId( order_id)
end

def contractDetails( req_id, contract_details)
end

def bondContractDetails( req_id, contract_details)
end

def contractDetailsEnd( req_id)
end

def execDetails( order_id, contract, execution)
end

def updateMktDepth( ticker_id, position, operation, side, price, size)
end

def updateMktDepthL2( ticker_id, position, market_maker, operation,
side, price, size)
end

def updateNewsBulletin( msg_id, msgType, message, orig_exchange)
end

def managedAccounts( accounts_list)
end

def receiveFA( fa_data_type, xml)
end

def historicalData( req_id, date, open, high, low,
close, volume, count, eWAP, has_gaps)
end

def scannerParameters( xml)
end

def scannerData( req_id, rank, contract_details, distance,
benchmark, projection, legs_str)
end

def scannerDataEnd( req_id)
end

def realtimeBar( req_id, time, open, high, low, close, volume, wap, count)
end

def currentTime( time)
end

def fundamentalData( req_id, data)
end

# ---------------------------------------------------
# -- methods from AnyWrapper -- must be declared
# ---------------------------------------------------

def connectionClosed()
puts ' [API.connectionClosed] Closed connection with TWS'
end

def error(err_obj_str)
puts ' [API.msg1] ' + err_obj_str if err_obj_str.is_a?(String)
puts ' [API.msg3] ' + err_obj_str.getMessage() if err_obj_str.is_a?(Exception)
end

def error(one, two, str)
puts ' [API.msg2] ' + str + ' {' + one.to_s + ', ' + two.to_s + '}'
end

end # class TestOrderStatus

#*********************************************************************************************
# Running the code
# *********************************************************************************************
x = TestOrderStatus.new()
begin
x.eConnect
puts 'connected ...'
x.reqAllOpenOrders # requesting open orders information
x.requestStockQuote(-99, 'DONE_OPEN_ORDERS') # call to reqMktData() for invalid quote.
# Will generate error AFTER all
# open orders info are returned
sleep(5)
x.eDisconnect
puts 'disconnected'
rescue Exception => e
puts 'Exception>>' + e.message
x.eDisconnect
end


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.
Line 287 demonstrates this workaround. It's not very elegant and I will have to keep this in mind when designing the application.

Running the code produces the following output:


init
Server Version:43

TWS Time at connection:20090301 16:07:19 EST

connected ...
>requestStockQuote - calling reqMktData id=-99, ticker=DONE_OPEN_ORDERS
openOrder> open order: orderId=2 ---
contract_msg:
conid = 54528396
symbol = RUT
secType = OPT
expiry = 20090319
strike = 490.0
right = C
multiplier =
exchange = SMART
primaryExch =
currency = USD
localSymbol = RUWCA
order_msg:
order id =2
client id =0
action =SELL
quantity =1
type =LMT
lmtPrice =2.0
auxPrice =0.0
TIF =GTC
parent Id =0
permId =1664800509
outsideRth =false
hidden =false
discretionaryAmt=0.0
triggerMethod=0
goodAfterTime=
goodTillDate =
faGroup =
faMethod =
faPercentage =
faProfile =
shortSaleSlot=0
designatedLocation=
ocaGroup =
ocaType =3
rule80A =
allOrNone =false
minQty =0
percentOffset=0.0
eTradeOnly =false
firmQuoteOnly=false
nbboPriceCap =0.0
auctionStrategy=0
startingPrice=0.0
stockRefPrice=0.0
delta =0.0
stockRangeLower=0.0
stockRangeUpper=0.0
volatility =0.0
volatilityType=0
deltaNeutralOrderType=None
deltaNeutralAuxPrice=0.0
continuousUpdate=0
referencePriceType=0
trailStopPrice=0.0
account =DU58265
settlingFirm =DEMO
clearingAccount=
clearingIntent=IB
whatIf =false
order_state_msg:
status =PendingSubmit
initMargin =
maintMargin =
equityWithLoan=
commission =1.7976931348623157e+308
minCommission =1.7976931348623157e+308
maxCommission =1.7976931348623157e+308
commissionCurrency=USD
warningText =
orderStatus>: orderId=2, clientId=0, permId=1664800509, status=PendingSubmit filled=0, remaining=1,
avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=

openOrder> open order: orderId=1 ---
contract_msg:
conid = 5437
symbol = CAT
secType = STK
expiry =
strike = 0.0
right = ?
multiplier =
exchange = SMART
primaryExch =
currency = USD
localSymbol = CAT
order_msg:
order id =1
client id =0
action =BUY
quantity =100
type =LMT
lmtPrice =20.0
auxPrice =0.0
TIF =DAY
parent Id =0
permId =1664800505
outsideRth =false
hidden =false
discretionaryAmt=0.0
triggerMethod=0
goodAfterTime=
goodTillDate =
faGroup =
faMethod =
faPercentage =
faProfile =
shortSaleSlot=0
designatedLocation=
ocaGroup =
ocaType =3
rule80A =
allOrNone =false
minQty =0
percentOffset=0.0
eTradeOnly =false
firmQuoteOnly=false
nbboPriceCap =0.0
auctionStrategy=0
startingPrice=0.0
stockRefPrice=0.0
delta =0.0
stockRangeLower=0.0
stockRangeUpper=0.0
volatility =0.0
volatilityType=0
deltaNeutralOrderType=None
deltaNeutralAuxPrice=0.0
continuousUpdate=0
referencePriceType=0
trailStopPrice=0.0
account =DU58265
settlingFirm =DEMO
clearingAccount=
clearingIntent=IB
whatIf =false
order_state_msg:
status =PreSubmitted
initMargin =
maintMargin =
equityWithLoan=
commission =1.7976931348623157e+308
minCommission =1.7976931348623157e+308
maxCommission =1.7976931348623157e+308
commissionCurrency=USD
warningText =
orderStatus>: orderId=1, clientId=0, permId=1664800505, status=PreSubmitted filled=0, remaining=100,
avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=

[API.msg2] Market data farm connection is OK:usopt {-1, 2104}
[API.msg2] Market data farm connection is OK:usfarm {-1, 2104}
openOrder> open order: orderId=2 ---
contract_msg:
conid = 54528396
symbol = RUT
secType = OPT
expiry = 20090319
strike = 490.0
right = C
multiplier =
exchange = SMART
primaryExch =
currency = USD
localSymbol = RUWCA
order_msg:
order id =2
client id =0
action =SELL
quantity =1
type =LMT
lmtPrice =2.0
auxPrice =0.0
TIF =GTC
parent Id =0
permId =1664800509
outsideRth =false
hidden =false
discretionaryAmt=0.0
triggerMethod=0
goodAfterTime=
goodTillDate =
faGroup =
faMethod =
faPercentage =
faProfile =
shortSaleSlot=0
designatedLocation=
ocaGroup =
ocaType =3
rule80A =
allOrNone =false
minQty =0
percentOffset=0.0
eTradeOnly =false
firmQuoteOnly=false
nbboPriceCap =0.0
auctionStrategy=0
startingPrice=0.0
stockRefPrice=0.0
delta =0.0
stockRangeLower=0.0
stockRangeUpper=0.0
volatility =0.0
volatilityType=0
deltaNeutralOrderType=None
deltaNeutralAuxPrice=0.0
continuousUpdate=0
referencePriceType=0
trailStopPrice=0.0
account =DU58265
settlingFirm =DEMO
clearingAccount=
clearingIntent=IB
whatIf =false
order_state_msg:
status =PendingSubmit
initMargin =
maintMargin =
equityWithLoan=
commission =1.7976931348623157e+308
minCommission =1.7976931348623157e+308
maxCommission =1.7976931348623157e+308
commissionCurrency=USD
warningText =
orderStatus>: orderId=2, clientId=0, permId=1664800509, status=PendingSubmit filled=0, remaining=1,
avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=

openOrder> open order: orderId=1 ---
contract_msg:
conid = 5437
symbol = CAT
secType = STK
expiry =
strike = 0.0
right = ?
multiplier =
exchange = SMART
primaryExch =
currency = USD
localSymbol = CAT
order_msg:
order id =1
client id =0
action =BUY
quantity =100
type =LMT
lmtPrice =20.0
auxPrice =0.0
TIF =DAY
parent Id =0
permId =1664800505
outsideRth =false
hidden =false
discretionaryAmt=0.0
triggerMethod=0
goodAfterTime=
goodTillDate =
faGroup =
faMethod =
faPercentage =
faProfile =
shortSaleSlot=0
designatedLocation=
ocaGroup =
ocaType =3
rule80A =
allOrNone =false
minQty =0
percentOffset=0.0
eTradeOnly =false
firmQuoteOnly=false
nbboPriceCap =0.0
auctionStrategy=0
startingPrice=0.0
stockRefPrice=0.0
delta =0.0
stockRangeLower=0.0
stockRangeUpper=0.0
volatility =0.0
volatilityType=0
deltaNeutralOrderType=None
deltaNeutralAuxPrice=0.0
continuousUpdate=0
referencePriceType=0
trailStopPrice=0.0
account =DU58265
settlingFirm =DEMO
clearingAccount=
clearingIntent=IB
whatIf =false
order_state_msg:
status =PreSubmitted
initMargin =
maintMargin =
equityWithLoan=
commission =1.7976931348623157e+308
minCommission =1.7976931348623157e+308
maxCommission =1.7976931348623157e+308
commissionCurrency=USD
warningText =
orderStatus>: orderId=1, clientId=0, permId=1664800505, status=PreSubmitted filled=0, remaining=100,
avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=

[API.msg2] No security definition has been found for the request {-99, 200}
disconnected



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.

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:

# *********************************************************************************************
# Running the code
# *********************************************************************************************
x = TestOrderStatus.new()
begin
x.eConnect
puts 'connected ...'
sleep(5)
x.eDisconnect
puts 'disconnected'
rescue Exception => e
puts 'Exception>>' + e.message
x.eDisconnect
end



init
Server Version:43

TWS Time at connection:20090301 17:11:38 EST

connected ...
openOrder> open order: orderId=2 ---
contract_msg:
conid = 54528396
symbol = RUT
secType = OPT
expiry = 20090319
strike = 490.0
right = C
multiplier =
exchange = SMART
primaryExch =
currency = USD
localSymbol = RUWCA
order_msg:
order id =2
client id =0
action =SELL
quantity =1
type =LMT
lmtPrice =2.0
auxPrice =0.0
TIF =GTC
parent Id =0
permId =1664800509
outsideRth =false
hidden =false
discretionaryAmt=0.0
triggerMethod=0
goodAfterTime=
goodTillDate =
faGroup =
faMethod =
faPercentage =
faProfile =
shortSaleSlot=0
designatedLocation=
ocaGroup =
ocaType =3
rule80A =
allOrNone =false
minQty =0
percentOffset=0.0
eTradeOnly =false
firmQuoteOnly=false
nbboPriceCap =0.0
auctionStrategy=0
startingPrice=0.0
stockRefPrice=0.0
delta =0.0
stockRangeLower=0.0
stockRangeUpper=0.0
volatility =0.0
volatilityType=0
deltaNeutralOrderType=None
deltaNeutralAuxPrice=0.0
continuousUpdate=0
referencePriceType=0
trailStopPrice=0.0
account =DU58265
settlingFirm =DEMO
clearingAccount=
clearingIntent=IB
whatIf =false
order_state_msg:
status =PendingSubmit
initMargin =
maintMargin =
equityWithLoan=
commission =1.7976931348623157e+308
minCommission =1.7976931348623157e+308
maxCommission =1.7976931348623157e+308
commissionCurrency=USD
warningText =
orderStatus>: orderId=2, clientId=0, permId=1664800509, status=PendingSubmit filled=0, remaining=1,
avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=

openOrder> open order: orderId=1 ---
contract_msg:
conid = 5437
symbol = CAT
secType = STK
expiry =
strike = 0.0
right = ?
multiplier =
exchange = SMART
primaryExch =
currency = USD
localSymbol = CAT
order_msg:
order id =1
client id =0
action =BUY
quantity =100
type =LMT
lmtPrice =20.0
auxPrice =0.0
TIF =DAY
parent Id =0
permId =1664800505
outsideRth =false
hidden =false
discretionaryAmt=0.0
triggerMethod=0
goodAfterTime=
goodTillDate =
faGroup =
faMethod =
faPercentage =
faProfile =
shortSaleSlot=0
designatedLocation=
ocaGroup =
ocaType =3
rule80A =
allOrNone =false
minQty =0
percentOffset=0.0
eTradeOnly =false
firmQuoteOnly=false
nbboPriceCap =0.0
auctionStrategy=0
startingPrice=0.0
stockRefPrice=0.0
delta =0.0
stockRangeLower=0.0
stockRangeUpper=0.0
volatility =0.0
volatilityType=0
deltaNeutralOrderType=None
deltaNeutralAuxPrice=0.0
continuousUpdate=0
referencePriceType=0
trailStopPrice=0.0
account =DU58265
settlingFirm =DEMO
clearingAccount=
clearingIntent=IB
whatIf =false
order_state_msg:
status =PreSubmitted
initMargin =
maintMargin =
equityWithLoan=
commission =1.7976931348623157e+308
minCommission =1.7976931348623157e+308
maxCommission =1.7976931348623157e+308
commissionCurrency=USD
warningText =
orderStatus>: orderId=1, clientId=0, permId=1664800505, status=PreSubmitted filled=0, remaining=100,
avgFillPrice=0.0, lastFillPrice=0.0, parent Id=0, whyHeld=

[API.msg2] Market data farm connection is OK:usopt {-1, 2104}
[API.msg2] Market data farm connection is OK:usfarm {-1, 2104}
disconnected




03/09/09 Update

Upgrading to API v. 9.60
requires addition of these new callback methods.

 
# ---------------------------------------------------
# new callback methods added in API 9.60
# ---------------------------------------------------
def openOrderEnd() # invoked when all orders are sent to a client as a response to reqOpenOrders() and right after connected to TWS
puts 'openOrderEnd()'
end

def accountDownloadEnd(account_name) #invoked when a complete snapshot of an account state is sent to a client as a response to reqAccountUpdates()
end

def execDetailsEnd( req_id) # invoked when all executions are sent to a client as a response to reqExecutions()
end

def deltaNeutralValidation(req_id, under_comp) # for Delta-Neutral DN RFQ
end

Running the code produces the same result except openOrderEnd() method is called back.

...
openOrderEnd()
disconnected