ib-ruby 0.7.9 → 0.7.10
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY +4 -0
- data/README.md +26 -1
- data/VERSION +1 -1
- data/db/migrate/101_add_executions.rb +24 -0
- data/db/migrate/111_add_bars.rb +18 -0
- data/db/migrate/121_add_order_states.rb +28 -0
- data/db/migrate/131_add_orders.rb +105 -0
- data/db/migrate/141_add_combo_legs.rb +18 -0
- data/db/migrate/151_add_underlyings.rb +13 -0
- data/db/migrate/161_add_contract_details.rb +40 -0
- data/db/migrate/171_add_contracts.rb +26 -0
- data/lib/ib-ruby/models/bag.rb +2 -2
- data/lib/ib-ruby/models/combo_leg.rb +6 -6
- data/lib/ib-ruby/models/contract.rb +5 -5
- data/lib/ib-ruby/models/contract_detail.rb +7 -7
- data/lib/ib-ruby/models/execution.rb +7 -7
- data/lib/ib-ruby/models/model.rb +6 -3
- data/lib/ib-ruby/models/model_properties.rb +58 -55
- data/lib/ib-ruby/models/option.rb +1 -1
- data/lib/ib-ruby/models/order.rb +34 -35
- data/lib/ib-ruby/models/order_state.rb +14 -2
- data/lib/ib-ruby/models/underlying.rb +1 -1
- data/spec/db_helper.rb +65 -17
- data/spec/ib-ruby/models/execution_spec.rb +45 -29
- data/spec/ib-ruby/models/order_spec.rb +12 -10
- data/spec/ib-ruby/models/order_state_spec.rb +14 -5
- data/spec/model_helper.rb +13 -0
- metadata +11 -3
data/HISTORY
CHANGED
data/README.md
CHANGED
@@ -71,7 +71,6 @@ other API implementations. The choice is yours.
|
|
71
71
|
| 0.6.1 | 921-923 | 966 |
|
72
72
|
| 0.7.1+ | 924+ | 967 |
|
73
73
|
|
74
|
-
|
75
74
|
4. Start Interactive Broker's Trader Work Station or Gateway before your code
|
76
75
|
attempts to connect to it. Note that TWS and Gateway listen to different ports,
|
77
76
|
this library assumes connection to Gateway on the same machine (localhost:4001)
|
@@ -143,6 +142,32 @@ You can easily create your own tests following the guide in 'spec/README'.
|
|
143
142
|
Help the development! See 'spec/TODO' for the list of use cases/scenarios
|
144
143
|
that still need to be tested.
|
145
144
|
|
145
|
+
## DB BACKEND:
|
146
|
+
|
147
|
+
Latest versions of the gem added (optional and experimental) support for data
|
148
|
+
persistance (ActiveRecord ORM). In order to use this support, you have to set up
|
149
|
+
the database (SQLite recommended for simplicity) and run migrations located at
|
150
|
+
'db/migrate' folder.
|
151
|
+
|
152
|
+
You further need to:
|
153
|
+
|
154
|
+
require 'ib-ruby/db'
|
155
|
+
|
156
|
+
IB::DB.connect :adapter => 'sqlite3',
|
157
|
+
:database => 'db/test.sqlite3'
|
158
|
+
|
159
|
+
require 'ib-ruby'
|
160
|
+
|
161
|
+
Only require 'ib-ruby' AFTER you connected to DB, otherwise your Models will not
|
162
|
+
inherit from ActiveRecord::Base and won't be persistent. If you are using Rails,
|
163
|
+
you don't need IB::DB.connect part, Rails will take care of it for you. So, just use:
|
164
|
+
|
165
|
+
require 'ib-ruby/db'
|
166
|
+
require 'ib-ruby'
|
167
|
+
|
168
|
+
Now, all your IB Models are just ActiveRecords and you can do whatever you want with them:
|
169
|
+
persist to DB, use in Rails applications, develop controllers and views.
|
170
|
+
|
146
171
|
## LICENSE:
|
147
172
|
|
148
173
|
This software is available under the LGPL.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.7.
|
1
|
+
0.7.10
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class AddExecutions < ActiveRecord::Migration
|
2
|
+
|
3
|
+
def change
|
4
|
+
create_table(:executions) do |t|
|
5
|
+
# TWS orders have fixed local_id of 0 AND client id of 0
|
6
|
+
t.references :order
|
7
|
+
t.integer :local_id # TWS orders have a fixed order id of 0
|
8
|
+
t.integer :client_id # Id of the client that placed the order
|
9
|
+
t.integer :perm_id # Permanent order id, remains the same over TWS sessions
|
10
|
+
t.string :order_ref # Order reference
|
11
|
+
t.string :exec_id # Unique order execution id
|
12
|
+
t.string :side, :limit => 1 # Was the transaction a buy or a sale: BOT|SLD
|
13
|
+
t.integer :quantity # The number of shares filled
|
14
|
+
t.integer :cumulative_quantity # Cumulative quantity
|
15
|
+
t.float :price # double: The order execution price
|
16
|
+
t.float :average_price # double: Average price (for all executions?)
|
17
|
+
t.string :exchange # Exchange that executed the order
|
18
|
+
t.string :account_name # The customer account number
|
19
|
+
t.boolean :liquidation, :limit => 1 # This position to be liquidated last should the need arise
|
20
|
+
t.string :time, :limit => 18 # String! The order execution time
|
21
|
+
t.timestamps
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class AddBars < ActiveRecord::Migration
|
2
|
+
|
3
|
+
def change
|
4
|
+
# This is a single data point delivered by HistoricData or RealTimeBar messages.
|
5
|
+
create_table(:bars) do |t|
|
6
|
+
t.float :open # double:
|
7
|
+
t.float :high # double:
|
8
|
+
t.float :low # double:
|
9
|
+
t.float :close # double:
|
10
|
+
t.float :wap # double:
|
11
|
+
t.integer :volume #
|
12
|
+
t.integer :trades # Number of trades during the time period the bar covers
|
13
|
+
t.boolean :has_gaps, :limit => 1 # Whether or not there are gaps in the data
|
14
|
+
t.string :time, :limit => 18 # String! The order execution time
|
15
|
+
t.timestamps
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class AddOrderStates < ActiveRecord::Migration
|
2
|
+
|
3
|
+
def change
|
4
|
+
# OrderState represents dynamic (changeable) info about a single Order
|
5
|
+
create_table(:order_states) do |t|
|
6
|
+
t.references :order
|
7
|
+
t.integer :local_id # int: Order id associated with client (volatile).
|
8
|
+
t.integer :client_id # int: The id of the client that placed this order.
|
9
|
+
t.integer :perm_id # int: TWS permanent id, remains the same over TWS sessions.
|
10
|
+
t.integer :parent_id # int: The order ID of the parent (original) order, used
|
11
|
+
t.string :status # String: Displays the order status.Possible values include:
|
12
|
+
t.integer :filled
|
13
|
+
t.integer :remaining
|
14
|
+
t.float :price # double
|
15
|
+
t.float :average_price # double
|
16
|
+
t.float :init_margin # Float: The impact the order would have on your initial margin.
|
17
|
+
t.float :maint_margin # Float: The impact the order would have on your maintenance margin.
|
18
|
+
t.float :equity_with_loan # Float: The impact the order would have on your equity
|
19
|
+
t.float :commission # double: Shows the commission amount on the order.
|
20
|
+
t.float :min_commission # The possible min range of the actual order commission.
|
21
|
+
t.float :max_commission # The possible max range of the actual order commission.
|
22
|
+
t.string :commission_currency, :limit => 4 # String: Shows the currency of the commission.
|
23
|
+
t.string :why_held # String: comma-separated list of reasons for order to be held.
|
24
|
+
t.string :warning_text # String: Displays a warning message if warranted.
|
25
|
+
t.timestamps
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
class AddOrders < ActiveRecord::Migration
|
2
|
+
|
3
|
+
def change
|
4
|
+
# OrderState represents dynamic (changeable) info about a single Order
|
5
|
+
create_table(:orders) do |t|
|
6
|
+
|
7
|
+
t.integer :local_id # int: Order id associated with client (volatile).
|
8
|
+
t.integer :client_id # int: The id of the client that placed this order.
|
9
|
+
t.integer :perm_id # int: TWS permanent id, remains the same over TWS sessions.
|
10
|
+
t.integer :parent_id # int: Order ID of the parent (original) order
|
11
|
+
t.string :order_ref # String: Order reference. Customer defined order ID tag.
|
12
|
+
t.string :order_type, :limit => 20 # Order type.
|
13
|
+
t.string :tif, :limit => 3 # Time in Force (time to market): DAY/GAT/GTD/GTC/IOC
|
14
|
+
t.string :side, :limit => 1 # Action/side: BUY/SELL/SSHORT/SSHORTX
|
15
|
+
t.integer :quantity # int: The order quantity.
|
16
|
+
t.float :limit_price # double: LIMIT price, used for limit, stop-limit and relative
|
17
|
+
t.float :aux_price # double: STOP price for stop-limit orders, and the OFFSET amount
|
18
|
+
t.integer :open_close # same as ComboLeg: SAME = 0; OPEN = 1; CLOSE = 2; UNKNOWN = 3
|
19
|
+
t.integer :oca_type # int: Tells how to handle remaining orders in an OCA group
|
20
|
+
t.string :oca_group # String: Identifies a member of a one-cancels-all group.
|
21
|
+
|
22
|
+
t.boolean :transmit, :limit => 1 # If false, order will be created but not transmitted.
|
23
|
+
t.boolean :what_if, :limit => 1 # Only return pre-trade commissions and margin info, do not place
|
24
|
+
t.boolean :outside_rth, :limit => 1 # Order may trigger or fill outside of regular hours.
|
25
|
+
t.boolean :not_held, :limit => 1 # Not Held
|
26
|
+
t.boolean :hidden, :limit => 1 # Order will not be visible in market depth. ISLAND only.
|
27
|
+
t.boolean :block_order, :limit => 1 # This is an ISE Block order.
|
28
|
+
t.boolean :sweep_to_fill, :limit => 1 # This is a Sweep-to-Fill order.
|
29
|
+
t.boolean :all_or_none, :limit => 1 # AON
|
30
|
+
t.boolean :etrade_only, :limit => 1 # Trade with electronic quotes.
|
31
|
+
t.boolean :firm_quote_only, :limit => 1 # Trade with firm quotes.
|
32
|
+
t.boolean :opt_out_smart_routing, :limit => 1 # Australian exchange only, default false
|
33
|
+
t.boolean :override_percentage_constraints, :limit => 1
|
34
|
+
|
35
|
+
t.integer :min_quantity # int: Identifies a minimum quantity order type.
|
36
|
+
t.integer :display_size # int: publicly disclosed order size for Iceberg orders.
|
37
|
+
t.integer :trigger_method # Specifies how Simulated Stop, Stop-Limit and Trailing
|
38
|
+
t.integer :origin # 0=Customer, 1=Firm
|
39
|
+
|
40
|
+
t.string :good_after_time # Indicates that the trade should be submitted after the
|
41
|
+
t.string :good_till_date # Indicates that the trade should remain working until the
|
42
|
+
t.string :rule_80a # Individual = 'I', Agency = 'A', AgentOtherMember = 'W',
|
43
|
+
|
44
|
+
t.float :percent_offset # double: percent offset amount for relative (REL)orders only
|
45
|
+
t.float :trail_stop_price # double: for TRAILLIMIT orders only
|
46
|
+
t.float :trailing_percent
|
47
|
+
|
48
|
+
t.string :fa_group
|
49
|
+
t.string :fa_profile
|
50
|
+
t.string :fa_method
|
51
|
+
t.string :fa_percentage
|
52
|
+
|
53
|
+
t.integer :short_sale_slot # 1 - you hold the shares,
|
54
|
+
t.string :designated_location # String: set when slot==2 only
|
55
|
+
t.integer :exempt_code # int
|
56
|
+
t.string :account # String: The account. For institutional customers only.
|
57
|
+
t.string :settling_firm # String: Institutional only
|
58
|
+
t.string :clearing_account # String: For IBExecution customers: Specifies the
|
59
|
+
t.string :clearing_intent # IBExecution customers: "", IB, Away, PTA (post trade allocation).
|
60
|
+
t.float :discretionary_amount # double: The amount off the limit price
|
61
|
+
t.float :nbbo_price_cap # double: Maximum Smart order distance from the NBBO.
|
62
|
+
t.integer :auction_strategy # For BOX exchange only. Valid values:
|
63
|
+
t.float :starting_price # double: Starting price. Valid on BOX orders only.
|
64
|
+
t.float :stock_ref_price # double: The stock reference price, used for VOL
|
65
|
+
|
66
|
+
t.float :delta # double: Stock delta. Valid on BOX orders only.
|
67
|
+
t.float :stock_range_lower # double: The lower value for the acceptable
|
68
|
+
t.float :stock_range_upper # double The upper value for the acceptable
|
69
|
+
t.float :volatility # double: What the price is, computed via TWSs Options
|
70
|
+
t.integer :volatility_type # int: How the volatility is calculated: 1=daily, 2=annual
|
71
|
+
t.integer :reference_price_type # int: For dynamic management of volatility orders:
|
72
|
+
t.integer :continuous_update # int: Used for dynamic management of volatility orders.
|
73
|
+
t.string :delta_neutral_order_type # String: Enter an order type to instruct TWS
|
74
|
+
t.string :delta_neutral_aux_price # double: Use this field to enter a value if
|
75
|
+
t.integer :delta_neutral_con_id
|
76
|
+
t.string :delta_neutral_settling_firm
|
77
|
+
t.string :delta_neutral_clearing_account
|
78
|
+
t.string :delta_neutral_clearing_intent
|
79
|
+
t.string :hedge_type # String: D = Delta, B = Beta, F = FX or P = Pair
|
80
|
+
t.string :hedge_param # String; value depends on the hedgeType; sent from the API
|
81
|
+
t.float :basis_points # double: EFP orders only
|
82
|
+
t.float :basis_points_type # double: EFP orders only
|
83
|
+
t.string :algo_strategy
|
84
|
+
|
85
|
+
# t.string :algo_params # public Vector<TagValue> m_algoParams; ?!
|
86
|
+
# t.string :leg_prices # Vector<OrderComboLeg> m_orderComboLegs
|
87
|
+
# t.string :combo_params # not used yet
|
88
|
+
|
89
|
+
t.integer :scale_init_level_size # int: Size of the first (initial) order component.
|
90
|
+
t.integer :scale_subs_level_size # int: Order size of the subsequent scale order
|
91
|
+
t.float :scale_price_increment # double: Price increment between scale components.
|
92
|
+
t.float :scale_price_adjust_value
|
93
|
+
t.integer :scale_price_adjust_interval
|
94
|
+
t.float :scale_profit_offset
|
95
|
+
t.integer :scale_init_position
|
96
|
+
t.integer :scale_init_fill_qty
|
97
|
+
t.boolean :scale_auto_reset, :limit => 1
|
98
|
+
t.boolean :scale_random_percent, :limit => 1
|
99
|
+
|
100
|
+
t.timestamp :placed_at
|
101
|
+
t.timestamp :modified_at
|
102
|
+
t.timestamps
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class AddComboLegs < ActiveRecord::Migration
|
2
|
+
|
3
|
+
def change
|
4
|
+
# ComboLeg objects represent individual security legs in a "BAG"
|
5
|
+
create_table(:combo_legs) do |t|
|
6
|
+
t.references :contract
|
7
|
+
t.integer :con_id # # int: The unique contract identifier specifying the security.
|
8
|
+
t.integer :ratio # int: Select the relative number of contracts for the leg you
|
9
|
+
t.string :exchange # String: exchange to which the complete combo order will be routed.
|
10
|
+
t.string :side, :limit => 1 # Action/side: BUY/SELL/SSHORT/SSHORTX
|
11
|
+
t.integer :exempt_code # int:
|
12
|
+
t.integer :short_sale_slot # int: 0 - retail(default), 1 = clearing broker, 2 = third party
|
13
|
+
t.string :designated_location # Otherwise leave blank or orders will be rejected.:status # String: Displays the order status.Possible values include:
|
14
|
+
t.integer :open_close # SAME = 0; OPEN = 1; CLOSE = 2; UNKNOWN = 3
|
15
|
+
t.timestamps
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class AddUnderlyings < ActiveRecord::Migration
|
2
|
+
|
3
|
+
def change
|
4
|
+
# Calculated characteristics of underlying Contract (volatile)
|
5
|
+
create_table(:underlyings) do |t|
|
6
|
+
t.references :contract
|
7
|
+
t.integer :con_id # # int: The unique contract identifier specifying the security
|
8
|
+
t.float :delta # double: The underlying stock or future delta
|
9
|
+
t.float :price # double: The price of the underlying
|
10
|
+
t.timestamps
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class AddContractDetails < ActiveRecord::Migration
|
2
|
+
|
3
|
+
def change
|
4
|
+
# ComboLeg objects represent individual security legs in a "BAG"
|
5
|
+
create_table(:contract_details) do |t|
|
6
|
+
t.references :contract
|
7
|
+
t.string :market_name # The market name for this contract.
|
8
|
+
t.string :trading_class # The trading class name for this contract.
|
9
|
+
t.float :min_tick # double: The minimum price tick.
|
10
|
+
t.integer :price_magnifier # int: Z on LIFFE is reported in index points not GBP.
|
11
|
+
t.string :order_types # The list of valid order types for this contract.
|
12
|
+
t.string :valid_exchanges # The list of exchanges this contract is traded on.
|
13
|
+
t.integer :under_con_id # int: The underlying contract ID.
|
14
|
+
t.string :long_name # Descriptive name of the asset.
|
15
|
+
t.string :contract_month # The contract month of the underlying futures contract.
|
16
|
+
t.string :industry # Wide industry. For example Financial.
|
17
|
+
t.string :category # Industry category. For example InvestmentSvc.
|
18
|
+
t.string :subcategory # Subcategory. For example Brokerage.
|
19
|
+
t.string :time_zone # Time zone for the trading hours (e.g. EST)
|
20
|
+
t.string :trading_hours # The trading hours of the product. 20090507:0700-18301830-2330;20090508:CLOSED.
|
21
|
+
t.string :liquid_hours # The liquid trading hours of the product.
|
22
|
+
t.string :cusip # The nine-character bond CUSIP or the 12-character SEDOL.
|
23
|
+
t.string :ratings # Credit rating of the issuer. Higher rating is less risky investment.
|
24
|
+
t.string :desc_append # Additional descriptive information about the bond.
|
25
|
+
t.string :bond_type # The type of bond such as "CORP"
|
26
|
+
t.string :coupon_type # The type of bond coupon.
|
27
|
+
t.float :coupon # double: The interest rate used to calculate the amount you
|
28
|
+
t.string :maturity # The date on which the issuer must repay bond face value
|
29
|
+
t.string :issue_date # The date the bond was issued.
|
30
|
+
t.string :next_option_date # only if bond has embedded options.
|
31
|
+
t.string :next_option_type # only if bond has embedded options.
|
32
|
+
t.string :notes # Additional notes if populated for the bond in IB's database
|
33
|
+
t.boolean :callable, :limit => 1 # Can be called by the issuer under certain conditions.
|
34
|
+
t.boolean :puttable, :limit => 1 # Can be sold back to the issuer under certain conditions
|
35
|
+
t.boolean :convertible, :limit => 1 # Can be converted to stock under certain conditions.
|
36
|
+
t.boolean :next_option_partial, :limit => 1 # # only if bond has embedded options.
|
37
|
+
t.timestamps
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class AddContracts < ActiveRecord::Migration
|
2
|
+
|
3
|
+
def change
|
4
|
+
create_table(:contracts) do |t|
|
5
|
+
|
6
|
+
t.integer :con_id # int: The unique contract identifier.
|
7
|
+
t.string :sec_type, :limit => 5 # Security type. Valid values are: SECURITY_TYPES
|
8
|
+
t.float :strike # double: The strike price.
|
9
|
+
t.string :currency, :limit => 4 # Only needed if there is an ambiguity e.g. when SMART exchange
|
10
|
+
t.string :sec_id_type, :limit => 5 # Security identifier when querying contract details or
|
11
|
+
t.integer :sec_id # Unique identifier of the given secIdType.
|
12
|
+
t.string :legs_description # received in OpenOrder for all combos
|
13
|
+
t.string :symbol # This is the symbol of the underlying asset.
|
14
|
+
t.string :local_symbol # Local exchange symbol of the underlying asset
|
15
|
+
t.integer :multiplier
|
16
|
+
t.string :expiry # The expiration date. Use the format YYYYMM or YYYYMMDD
|
17
|
+
t.string :exchange # The order destination such as Smart.
|
18
|
+
t.string :primary_exchange # Non-SMART exchange where the contract trades.
|
19
|
+
t.boolean :include_expired, :limit => 1 # When true contract details requests and historical
|
20
|
+
t.string :right, :limit => 1 # Specifies a Put or Call. Valid input values are: P PUT C CALL
|
21
|
+
|
22
|
+
t.string :type # Contract Subclasses STI
|
23
|
+
t.timestamps
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/ib-ruby/models/bag.rb
CHANGED
@@ -39,12 +39,12 @@ module IB
|
|
39
39
|
:message => "should be blank or orders will be rejected"
|
40
40
|
|
41
41
|
def default_attributes
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
42
|
+
super.merge :con_id => 0,
|
43
|
+
:open_close => :same, # The only option for retail customers.
|
44
|
+
:short_sale_slot => :default,
|
45
|
+
:designated_location => '',
|
46
|
+
:exchange => 'SMART', # Unless SMART, Order modification fails
|
47
|
+
:exempt_code => -1
|
48
48
|
end
|
49
49
|
|
50
50
|
# Leg's weight is a combination of action and ratio
|
@@ -95,11 +95,11 @@ module IB
|
|
95
95
|
validates_numericality_of :multiplier, :strike, :allow_nil => true
|
96
96
|
|
97
97
|
def default_attributes
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
98
|
+
super.merge :con_id => 0,
|
99
|
+
:strike => 0.0,
|
100
|
+
:right => :none, # Not an option
|
101
|
+
:exchange => 'SMART',
|
102
|
+
:include_expired => false
|
103
103
|
end
|
104
104
|
|
105
105
|
# This returns an Array of data from the given contract.
|
@@ -56,13 +56,13 @@ module IB
|
|
56
56
|
validates_format_of :time_zone, :with => /^\w{3}$/, :message => 'should be XXX'
|
57
57
|
|
58
58
|
def default_attributes
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
59
|
+
super.merge :coupon => 0.0,
|
60
|
+
:under_con_id => 0,
|
61
|
+
:min_tick => 0,
|
62
|
+
:callable => false,
|
63
|
+
:puttable => false,
|
64
|
+
:convertible => false,
|
65
|
+
:next_option_partial => false
|
66
66
|
end
|
67
67
|
|
68
68
|
end # class ContractDetail
|
@@ -7,7 +7,7 @@ module IB
|
|
7
7
|
|
8
8
|
belongs_to :order
|
9
9
|
|
10
|
-
prop
|
10
|
+
prop :local_id, # int: order id. TWS orders have a fixed order id of 0.
|
11
11
|
:client_id, # int: client id. TWS orders have a fixed client id of 0.
|
12
12
|
:perm_id, # int: TWS id used to identify orders over TWS sessions
|
13
13
|
:exec_id, # String: Unique order execution id over TWS sessions.
|
@@ -30,12 +30,12 @@ module IB
|
|
30
30
|
validates_numericality_of :local_id, :client_id, :perm_id, :only_integer => true
|
31
31
|
|
32
32
|
def default_attributes
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
33
|
+
super.merge :local_id => 0,
|
34
|
+
:client_id => 0,
|
35
|
+
:quantity => 0,
|
36
|
+
:price => 0,
|
37
|
+
:perm_id => 0,
|
38
|
+
:liquidation => false
|
39
39
|
end
|
40
40
|
|
41
41
|
# Comparison
|
data/lib/ib-ruby/models/model.rb
CHANGED
@@ -21,7 +21,7 @@ module IB
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
attr_accessor :
|
24
|
+
#attr_accessor :attributes
|
25
25
|
|
26
26
|
# If a opts hash is given, keys are taken as attribute names, values as data.
|
27
27
|
# The model instance fields are then set automatically from the opts Hash.
|
@@ -29,8 +29,7 @@ module IB
|
|
29
29
|
run_callbacks :initialize do
|
30
30
|
error "Argument must be a Hash", :args unless opts.is_a?(Hash)
|
31
31
|
|
32
|
-
|
33
|
-
attrs.keys.each { |key| self.send("#{key}=", attrs[key]) }
|
32
|
+
self.attributes = default_attributes.merge(opts)
|
34
33
|
end
|
35
34
|
end
|
36
35
|
|
@@ -40,6 +39,10 @@ module IB
|
|
40
39
|
@attributes ||= HashWithIndifferentAccess.new
|
41
40
|
end
|
42
41
|
|
42
|
+
def attributes= attrs
|
43
|
+
attrs.keys.each { |key| self.send("#{key}=", attrs[key]) }
|
44
|
+
end
|
45
|
+
|
43
46
|
# ActiveModel-style read/write_attribute accessors
|
44
47
|
def [] key
|
45
48
|
attributes[key.to_sym]
|
@@ -49,13 +49,6 @@ module IB
|
|
49
49
|
|
50
50
|
included do
|
51
51
|
|
52
|
-
# Extending AR-backed Model class with attribute defaults
|
53
|
-
if defined?(ActiveRecord::Base) && ancestors.include?(ActiveRecord::Base)
|
54
|
-
def initialize opts={}
|
55
|
-
super default_attributes.merge(opts)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
52
|
### Class macros
|
60
53
|
|
61
54
|
def self.prop *properties
|
@@ -82,59 +75,69 @@ module IB
|
|
82
75
|
def self.define_property_methods name, body={}
|
83
76
|
#p name, body
|
84
77
|
case body
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
78
|
+
when '' # default getter and setter
|
79
|
+
define_property_methods name
|
80
|
+
|
81
|
+
when Array # [setter, getter, validators]
|
82
|
+
define_property_methods name,
|
83
|
+
:get => body[0],
|
84
|
+
:set => body[1],
|
85
|
+
:validate => body[2]
|
86
|
+
|
87
|
+
when Hash # recursion base case
|
88
|
+
getter = case # Define getter
|
89
|
+
when body[:get].respond_to?(:call)
|
90
|
+
body[:get]
|
91
|
+
when body[:get]
|
92
|
+
proc { self[name].send "to_#{body[:get]}" }
|
93
|
+
when VALUES[name] # property is encoded
|
94
|
+
proc { VALUES[name][self[name]] }
|
95
|
+
#when respond_to?(:column_names) && column_names.include?(name.to_s)
|
96
|
+
# # noop, ActiveRecord will take care of it...
|
97
|
+
# p "#{name} => get noop"
|
98
|
+
# p respond_to?(:column_names) && column_names
|
99
|
+
else
|
100
|
+
proc { self[name] }
|
101
|
+
end
|
102
|
+
define_method name, &getter if getter
|
103
|
+
|
104
|
+
setter = case # Define setter
|
105
|
+
when body[:set].respond_to?(:call)
|
106
|
+
body[:set]
|
107
|
+
when body[:set]
|
108
|
+
proc { |value| self[name] = value.send "to_#{body[:set]}" }
|
109
|
+
when CODES[name] # property is encoded
|
110
|
+
proc { |value| self[name] = CODES[name][value] || value }
|
111
|
+
else
|
112
|
+
proc { |value| self[name] = value } # p name, value;
|
113
|
+
end
|
114
|
+
define_method "#{name}=", &setter if setter
|
115
|
+
|
116
|
+
# Define validator(s)
|
117
|
+
[body[:validate]].flatten.compact.each do |validator|
|
118
|
+
case validator
|
119
|
+
when Proc
|
120
|
+
validates_each name, &validator
|
121
|
+
when Hash
|
122
|
+
validates name, validator.dup
|
123
|
+
end
|
130
124
|
end
|
131
|
-
end
|
132
125
|
|
133
126
|
# TODO define self[:name] accessors for :virtual and :flag properties
|
134
127
|
|
135
|
-
|
136
|
-
|
128
|
+
else # setter given
|
129
|
+
define_property_methods name, :set => body, :get => body
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Extending AR-backed Model class with attribute defaults
|
134
|
+
if defined?(ActiveRecord::Base) && ancestors.include?(ActiveRecord::Base)
|
135
|
+
def initialize opts={}
|
136
|
+
super default_attributes.merge(opts)
|
137
137
|
end
|
138
|
+
else
|
139
|
+
# Timestamps
|
140
|
+
prop :created_at, :updated_at
|
138
141
|
end
|
139
142
|
|
140
143
|
end # included
|
data/lib/ib-ruby/models/order.rb
CHANGED
@@ -12,7 +12,7 @@ module IB
|
|
12
12
|
# your own Order IDs to avoid conflicts between orders placed from your API application.
|
13
13
|
|
14
14
|
# Main order fields
|
15
|
-
prop
|
15
|
+
prop :local_id, # int: Order id associated with client (volatile).
|
16
16
|
:client_id, # int: The id of the client that placed this order.
|
17
17
|
:perm_id, # int: TWS permanent id, remains the same over TWS sessions.
|
18
18
|
[:quantity, :total_quantity], # int: The order quantity.
|
@@ -238,10 +238,10 @@ module IB
|
|
238
238
|
|
239
239
|
def order_state= state
|
240
240
|
self.order_states.push case state
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
241
|
+
when IB::OrderState
|
242
|
+
state
|
243
|
+
when Symbol, String
|
244
|
+
IB::OrderState.new :status => state
|
245
245
|
end
|
246
246
|
end
|
247
247
|
|
@@ -267,13 +267,14 @@ module IB
|
|
267
267
|
:why_held, # String: comma-separated list of reasons for order to be held.
|
268
268
|
# Testing Order state:
|
269
269
|
:new?,
|
270
|
+
:submitted?,
|
270
271
|
:pending?,
|
271
272
|
:active?,
|
272
273
|
:inactive?,
|
273
274
|
:complete_fill?,
|
274
275
|
].each { |property| define_method(property) { order_state.send(property) } }
|
275
276
|
|
276
|
-
# Order is not valid without correct :local_id
|
277
|
+
# Order is not valid without correct :local_id
|
277
278
|
validates_numericality_of :local_id, :perm_id, :client_id, :parent_id,
|
278
279
|
:quantity, :min_quantity, :display_size,
|
279
280
|
:only_integer => true, :allow_nil => true
|
@@ -282,28 +283,26 @@ module IB
|
|
282
283
|
|
283
284
|
|
284
285
|
def default_attributes
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
# TODO: Add simple defaults to prop ?
|
306
|
-
}.merge super
|
286
|
+
super.merge :aux_price => 0.0,
|
287
|
+
:discretionary_amount => 0.0,
|
288
|
+
:parent_id => 0,
|
289
|
+
:tif => :day,
|
290
|
+
:order_type => :limit,
|
291
|
+
:open_close => :open,
|
292
|
+
:origin => :customer,
|
293
|
+
:short_sale_slot => :default,
|
294
|
+
:trigger_method => :default,
|
295
|
+
:oca_type => :none,
|
296
|
+
:auction_strategy => :none,
|
297
|
+
:designated_location => '',
|
298
|
+
:exempt_code => -1,
|
299
|
+
:display_size => 0,
|
300
|
+
:continuous_update => 0,
|
301
|
+
:delta_neutral_con_id => 0,
|
302
|
+
:algo_strategy => '',
|
303
|
+
:transmit => true,
|
304
|
+
:what_if => false,
|
305
|
+
:order_state => IB::OrderState.new(:status => 'New')
|
307
306
|
end
|
308
307
|
|
309
308
|
#after_initialize do #opts = {}
|
@@ -319,12 +318,12 @@ module IB
|
|
319
318
|
[contract.serialize_long(:con_id, :sec_id),
|
320
319
|
# main order fields
|
321
320
|
case side
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
321
|
+
when :short
|
322
|
+
'SSHORT'
|
323
|
+
when :short_exempt
|
324
|
+
'SSHORTX'
|
325
|
+
else
|
326
|
+
side.to_sup
|
328
327
|
end,
|
329
328
|
quantity,
|
330
329
|
self[:order_type], # Internal code, 'LMT' instead of :limit
|
@@ -469,7 +468,7 @@ module IB
|
|
469
468
|
modify contract, connection, self.placed_at
|
470
469
|
end
|
471
470
|
|
472
|
-
# Modify Order (convenience wrapper for send_message :PlaceOrder). Returns
|
471
|
+
# Modify Order (convenience wrapper for send_message :PlaceOrder). Returns local_id.
|
473
472
|
def modify contract, connection, time=Time.now
|
474
473
|
self.modified_at = time
|
475
474
|
connection.send_message :PlaceOrder,
|
@@ -27,7 +27,7 @@ module IB
|
|
27
27
|
:why_held # String: comma-separated list of reasons for order to be held.
|
28
28
|
|
29
29
|
# Properties arriving in both messages:
|
30
|
-
prop
|
30
|
+
prop :local_id, # int: Order id associated with client (volatile).
|
31
31
|
:perm_id, # int: TWS permanent id, remains the same over TWS sessions.
|
32
32
|
:client_id, # int: The id of the client that placed this order.
|
33
33
|
:parent_id, # int: The order ID of the parent (original) order, used
|
@@ -63,15 +63,27 @@ module IB
|
|
63
63
|
validates_numericality_of :local_id, :perm_id, :client_id, :parent_id, :filled,
|
64
64
|
:remaining, :only_integer => true, :allow_nil => true
|
65
65
|
|
66
|
+
def default_attributes
|
67
|
+
super.merge :filled => 0,
|
68
|
+
:remaining => 0,
|
69
|
+
:price => 0.0,
|
70
|
+
:average_price => 0.0
|
71
|
+
end
|
72
|
+
|
66
73
|
## Testing Order state:
|
67
74
|
|
68
75
|
def new?
|
69
76
|
status.empty? || status == 'New'
|
70
77
|
end
|
71
78
|
|
79
|
+
# Order is in a valid, working state on TWS side
|
80
|
+
def submitted?
|
81
|
+
status == 'PreSubmitted' || status == 'Submitted'
|
82
|
+
end
|
83
|
+
|
72
84
|
# Order is in a valid, working state on TWS side
|
73
85
|
def pending?
|
74
|
-
|
86
|
+
submitted? || status == 'PendingSubmit'
|
75
87
|
end
|
76
88
|
|
77
89
|
# Order is in invalid state
|
data/spec/db_helper.rb
CHANGED
@@ -73,29 +73,77 @@ shared_examples_for 'Invalid DB-backed Model' do
|
|
73
73
|
end
|
74
74
|
|
75
75
|
shared_examples_for 'Model with associations' do
|
76
|
+
|
76
77
|
it 'works with associations, if any' do
|
77
78
|
if defined? associations
|
78
|
-
associations.each do |assoc, items|
|
79
|
-
proxy = subject.association(assoc).reflection
|
80
|
-
#pp proxy
|
81
79
|
|
82
|
-
|
80
|
+
subject_name_plural = described_class.to_s.demodulize.tableize
|
81
|
+
|
82
|
+
associations.each do |name, item|
|
83
|
+
puts "Testing single association #{name}"
|
84
|
+
subject.association(name).reflection.should_not be_collection
|
85
|
+
|
86
|
+
# Assign item to association
|
87
|
+
expect { subject.send "#{name}=", item }.to_not raise_error
|
88
|
+
|
89
|
+
association = subject.send name #, :reload
|
90
|
+
association.should == item
|
91
|
+
association.should be_new_record
|
92
|
+
|
93
|
+
# Reverse association does not include subject
|
94
|
+
reverse_association = association.send(subject_name_plural)
|
95
|
+
reverse_association.should be_empty
|
96
|
+
|
97
|
+
# Now let's save subject
|
98
|
+
if subject.valid?
|
99
|
+
subject.save
|
100
|
+
|
101
|
+
association = subject.send name
|
102
|
+
association.should_not be_new_record
|
103
|
+
|
104
|
+
# Reverse association now DOES include subject (if reloaded!)
|
105
|
+
reverse_association = association.send(subject_name_plural, :reload)
|
106
|
+
reverse_association.should include subject
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'works with associated collections, if any' do
|
113
|
+
if defined? collections
|
114
|
+
|
115
|
+
subject_name = described_class.to_s.demodulize.tableize.singularize
|
116
|
+
|
117
|
+
collections.each do |name, items|
|
118
|
+
puts "Testing associated collection #{name}"
|
119
|
+
subject.association(name).reflection.should be_collection
|
120
|
+
|
83
121
|
[items].flatten.each do |item|
|
84
|
-
|
85
|
-
|
86
|
-
|
122
|
+
association = subject.send name #, :reload
|
123
|
+
|
124
|
+
# Add item to collection
|
125
|
+
expect { association << item }.to_not raise_error
|
126
|
+
association.should include item
|
127
|
+
|
128
|
+
# Reverse association does NOT point to subject
|
129
|
+
reverse_association = association.first.send(subject_name)
|
130
|
+
#reverse_association.should be_nil # But not always!
|
131
|
+
|
132
|
+
#association.size.should == items.size # Not for Order, +1 OrderState
|
133
|
+
end
|
134
|
+
|
135
|
+
# Now let's save subject
|
136
|
+
if subject.valid?
|
137
|
+
subject.save
|
138
|
+
|
139
|
+
[items].flatten.each do |item|
|
140
|
+
association = subject.send name #, :reload
|
87
141
|
|
88
|
-
p 'collection'
|
89
142
|
association.should include item
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
else
|
95
|
-
subject.send "#{assoc}=", item
|
96
|
-
association = subject.send("#{assoc}")
|
97
|
-
p 'not a collection'
|
98
|
-
association.should == item
|
143
|
+
|
144
|
+
# Reverse association DOES point to subject now
|
145
|
+
reverse_association = association.first.send(subject_name)
|
146
|
+
reverse_association.should == subject
|
99
147
|
end
|
100
148
|
end
|
101
149
|
|
@@ -31,14 +31,14 @@ describe IB::Models::Execution do # AKA IB::Execution
|
|
31
31
|
end
|
32
32
|
|
33
33
|
let(:assigns) do
|
34
|
-
{[:perm_id, :client_id, :cumulative_quantity, :price, :average_price] =>
|
34
|
+
{[:local_id, :perm_id, :client_id, :cumulative_quantity, :price, :average_price] =>
|
35
|
+
numeric_assigns,
|
35
36
|
:liquidation => boolean_assigns,
|
36
37
|
}
|
37
38
|
end
|
38
39
|
|
39
40
|
let(:aliases) do
|
40
41
|
{[:side, :action] => buy_sell_assigns,
|
41
|
-
[:local_id, :order_id] => numeric_assigns,
|
42
42
|
[:quantity, :shares] => numeric_assigns,
|
43
43
|
[:account_name, :account_number]=> string_assigns,
|
44
44
|
}
|
@@ -50,54 +50,70 @@ describe IB::Models::Execution do # AKA IB::Execution
|
|
50
50
|
:client_id => 1111,
|
51
51
|
:parent_id => 0,
|
52
52
|
:quantity => 100,
|
53
|
+
:side => :buy,
|
53
54
|
:order_type => :market)
|
54
55
|
}
|
55
56
|
end
|
56
57
|
|
57
58
|
it_behaves_like 'Model'
|
58
59
|
|
59
|
-
|
60
|
-
context 'associations' do
|
60
|
+
context 'DB backed associations', :db => true do
|
61
61
|
subject { IB::Execution.new props }
|
62
62
|
|
63
|
-
before(:all) { DatabaseCleaner.clean
|
63
|
+
before(:all) { DatabaseCleaner.clean }
|
64
64
|
|
65
65
|
it 'saves associated order' do
|
66
66
|
order = associations[:order]
|
67
|
-
|
68
|
-
#p order.save
|
69
|
-
|
70
67
|
subject.order = order
|
68
|
+
subject.order.should == order
|
69
|
+
subject.order.should be_new_record
|
71
70
|
|
72
|
-
|
73
|
-
|
71
|
+
subject.save
|
72
|
+
subject.order.should_not be_new_record
|
73
|
+
subject.order.executions.should include subject
|
74
|
+
end
|
74
75
|
|
76
|
+
it 'loads saved association with execution' do
|
77
|
+
order = IB::Order.find(:first)
|
75
78
|
|
76
|
-
|
77
|
-
p subject.order.executions
|
78
|
-
p subject.to_xml
|
79
|
-
p subject.serializable_hash
|
80
|
-
p subject.to_json
|
81
|
-
p subject.as_json
|
79
|
+
execution = IB::Execution.first
|
82
80
|
|
83
|
-
|
81
|
+
execution.should == subject
|
84
82
|
|
85
|
-
|
83
|
+
execution.order.should == order
|
84
|
+
order.executions.first.should == execution
|
86
85
|
end
|
86
|
+
end
|
87
87
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
#s1 = IB::Execution.first
|
92
|
-
#p s1
|
93
|
-
#p s1.order.executions
|
88
|
+
context 'extra ActiveModel goodness' do
|
89
|
+
subject { IB::Execution.new props }
|
94
90
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
91
|
+
it 'correctly serializes Model into hash and json' do
|
92
|
+
{"account_name"=>"DU111110",
|
93
|
+
"average_price"=>1.31075,
|
94
|
+
"client_id"=>1111,
|
95
|
+
"cumulative_quantity"=>20000,
|
96
|
+
"exchange"=>"IDEALPRO",
|
97
|
+
"exec_id"=>"0001f4e8.4f5d48f1.01.01",
|
98
|
+
"id"=>nil,
|
99
|
+
"liquidation"=>true,
|
100
|
+
"local_id"=>373,
|
101
|
+
"order_ref"=>nil,
|
102
|
+
"perm_id"=>1695693619,
|
103
|
+
"price"=>1.31075,
|
104
|
+
"quantity"=>20000,
|
105
|
+
"time"=>"20120312 15:41:09",
|
106
|
+
"side"=>:buy, }.each do |key, value|
|
107
|
+
|
108
|
+
subject.serializable_hash[key].should == value
|
109
|
+
subject.as_json["execution"][key].should == value
|
110
|
+
end
|
111
|
+
|
112
|
+
subject.to_xml.should =~ /<account-name>DU111110<.account-name>\n <average-price type=\"float\">1.31075<.average-price>\n <client-id type=\"integer\">1111<.client-id>/
|
113
|
+
subject.to_json.should =~ /\{\"execution\":\{\"account_name\":\"DU111110\",\"average_price\":1.31075,\"client_id\":1111,\"/
|
114
|
+
|
115
|
+
IB::Execution.new.from_json(subject.to_json).should == subject
|
99
116
|
end
|
100
|
-
|
101
117
|
end
|
102
118
|
|
103
119
|
end # describe IB::Models::Contract
|
@@ -50,17 +50,18 @@ describe IB::Models::Order do
|
|
50
50
|
:sweep_to_fill, :override_percentage_constraints, :all_or_none,
|
51
51
|
:etrade_only, :firm_quote_only, :opt_out_smart_routing, :scale_auto_reset,
|
52
52
|
:scale_random_percent] => boolean_assigns,
|
53
|
+
|
54
|
+
[:local_id, :perm_id, :parent_id] => numeric_or_nil_assigns,
|
53
55
|
}
|
54
56
|
end
|
55
57
|
|
56
58
|
let(:aliases) do
|
57
59
|
{[:side, :action] => buy_sell_short_assigns,
|
58
|
-
[:local_id, :order_id] => numeric_or_nil_assigns,
|
59
60
|
[:quantity, :total_quantity] => numeric_or_nil_assigns,
|
60
61
|
}
|
61
62
|
end
|
62
63
|
|
63
|
-
let(:
|
64
|
+
let(:collections) do
|
64
65
|
{:order_states => [IB::OrderState.new(:status => :Foo),
|
65
66
|
IB::OrderState.new(:status => 'Bar'),],
|
66
67
|
|
@@ -127,16 +128,17 @@ describe IB::Models::Order do
|
|
127
128
|
subject.init_margin.should be_nil
|
128
129
|
subject.maint_margin.should be_nil
|
129
130
|
subject.equity_with_loan.should be_nil
|
130
|
-
# Properties arriving via OrderStatus
|
131
|
-
subject.filled.should
|
132
|
-
subject.remaining.should
|
133
|
-
subject.price.should
|
134
|
-
subject.last_fill_price.should
|
135
|
-
subject.average_price.should
|
136
|
-
subject.average_fill_price.should
|
131
|
+
# Properties arriving via OrderStatus message
|
132
|
+
subject.filled.should == 0
|
133
|
+
subject.remaining.should == 0
|
134
|
+
subject.price.should == 0
|
135
|
+
subject.last_fill_price.should == 0
|
136
|
+
subject.average_price.should == 0
|
137
|
+
subject.average_fill_price.should == 0
|
137
138
|
subject.why_held.should be_nil
|
138
|
-
# Testing Order
|
139
|
+
# Testing Order state
|
139
140
|
subject.should be_new
|
141
|
+
subject.should_not be_submitted
|
140
142
|
subject.should_not be_pending
|
141
143
|
subject.should be_active
|
142
144
|
subject.should_not be_inactive
|
@@ -38,13 +38,14 @@ describe IB::Models::OrderState do
|
|
38
38
|
let(:assigns) do
|
39
39
|
{[:status] =>
|
40
40
|
{[nil, ''] => /must not be empty/,
|
41
|
-
['Zorro', :Zorro] => 'Zorro'}
|
41
|
+
['Zorro', :Zorro] => 'Zorro'},
|
42
|
+
|
43
|
+
:local_id => numeric_or_nil_assigns,
|
42
44
|
}
|
43
45
|
end
|
44
46
|
|
45
47
|
let(:aliases) do
|
46
|
-
{[:
|
47
|
-
[:price, :last_fill_price] => float_or_nil_assigns,
|
48
|
+
{[:price, :last_fill_price] => float_or_nil_assigns,
|
48
49
|
[:average_price, :average_fill_price] => float_or_nil_assigns,
|
49
50
|
}
|
50
51
|
end
|
@@ -53,9 +54,11 @@ describe IB::Models::OrderState do
|
|
53
54
|
it_behaves_like 'Self-equal Model'
|
54
55
|
|
55
56
|
context '#update_missing' do
|
57
|
+
let(:nil_state) { IB::OrderState.new(:filled => nil, :remaining => nil,
|
58
|
+
:price => nil, :average_price => nil) }
|
56
59
|
context 'updating with Hash' do
|
57
60
|
|
58
|
-
subject {
|
61
|
+
subject { nil_state.update_missing(props) }
|
59
62
|
|
60
63
|
it_behaves_like 'Model instantiated with properties'
|
61
64
|
|
@@ -63,7 +66,7 @@ describe IB::Models::OrderState do
|
|
63
66
|
|
64
67
|
context 'updating with Model' do
|
65
68
|
|
66
|
-
subject {
|
69
|
+
subject { nil_state.update_missing(IB::OrderState.new(props)) }
|
67
70
|
|
68
71
|
it_behaves_like 'Model instantiated with properties'
|
69
72
|
|
@@ -74,6 +77,8 @@ describe IB::Models::OrderState do
|
|
74
77
|
it 'has extra test methods' do
|
75
78
|
empty_state = IB::OrderState.new
|
76
79
|
empty_state.should be_new
|
80
|
+
subject.should_not be_pending
|
81
|
+
subject.should_not be_submitted
|
77
82
|
empty_state.should be_active
|
78
83
|
empty_state.should_not be_inactive
|
79
84
|
empty_state.should_not be_complete_fill
|
@@ -86,6 +91,7 @@ describe IB::Models::OrderState do
|
|
86
91
|
state.should_not be_inactive
|
87
92
|
state.should_not be_complete_fill
|
88
93
|
state.should be_pending
|
94
|
+
status == 'PendingSubmit' ? state.should_not(be_submitted) : state.should(be_submitted)
|
89
95
|
end
|
90
96
|
|
91
97
|
['PendingCancel', 'Cancelled', 'ApiCancelled', 'Inactive'].each do |status|
|
@@ -95,6 +101,7 @@ describe IB::Models::OrderState do
|
|
95
101
|
state.should be_inactive
|
96
102
|
state.should_not be_complete_fill
|
97
103
|
state.should_not be_pending
|
104
|
+
subject.should_not be_submitted
|
98
105
|
end
|
99
106
|
|
100
107
|
state.status = 'Filled'
|
@@ -103,6 +110,7 @@ describe IB::Models::OrderState do
|
|
103
110
|
state.should be_inactive
|
104
111
|
state.should_not be_complete_fill
|
105
112
|
state.should_not be_pending
|
113
|
+
subject.should_not be_submitted
|
106
114
|
|
107
115
|
state.remaining = 0
|
108
116
|
state.should_not be_new
|
@@ -110,6 +118,7 @@ describe IB::Models::OrderState do
|
|
110
118
|
state.should be_inactive
|
111
119
|
state.should be_complete_fill
|
112
120
|
state.should_not be_pending
|
121
|
+
subject.should_not be_submitted
|
113
122
|
end
|
114
123
|
|
115
124
|
end # describe IB::Order
|
data/spec/model_helper.rb
CHANGED
@@ -190,6 +190,7 @@ end
|
|
190
190
|
shared_examples_for 'Model instantiated with properties' do
|
191
191
|
it 'auto-assigns all properties given to initializer' do
|
192
192
|
props.each do |name, value|
|
193
|
+
#p subject, name, value
|
193
194
|
subject.send(name).should == value
|
194
195
|
end
|
195
196
|
end
|
@@ -200,6 +201,18 @@ end
|
|
200
201
|
|
201
202
|
shared_examples_for 'Model properties' do
|
202
203
|
|
204
|
+
it 'leaves order_id alone, no aliasing' do
|
205
|
+
if subject.respond_to?(:order_id)
|
206
|
+
subject.order_id.should be_nil
|
207
|
+
if subject.respond_to?(:local_id=)
|
208
|
+
subject.local_id = 1313
|
209
|
+
subject.order_id.should be_nil
|
210
|
+
subject.order_id = 2222
|
211
|
+
subject.local_id.should == 1313
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
203
216
|
it 'allows setting properties' do
|
204
217
|
expect {
|
205
218
|
props.each do |name, value|
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: ib-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.7.
|
5
|
+
version: 0.7.10
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Paul Legato
|
@@ -11,7 +11,7 @@ autorequire:
|
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
13
|
|
14
|
-
date: 2012-04-
|
14
|
+
date: 2012-04-25 00:00:00 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|
@@ -77,7 +77,7 @@ dependencies:
|
|
77
77
|
- - ">="
|
78
78
|
- !ruby/object:Gem::Version
|
79
79
|
version: "0"
|
80
|
-
type: :
|
80
|
+
type: :runtime
|
81
81
|
version_requirements: *id006
|
82
82
|
- !ruby/object:Gem::Dependency
|
83
83
|
name: database_cleaner
|
@@ -248,6 +248,14 @@ files:
|
|
248
248
|
- tasks/git.rake
|
249
249
|
- tasks/spec.rake
|
250
250
|
- tasks/version.rake
|
251
|
+
- db/migrate/101_add_executions.rb
|
252
|
+
- db/migrate/111_add_bars.rb
|
253
|
+
- db/migrate/121_add_order_states.rb
|
254
|
+
- db/migrate/131_add_orders.rb
|
255
|
+
- db/migrate/141_add_combo_legs.rb
|
256
|
+
- db/migrate/151_add_underlyings.rb
|
257
|
+
- db/migrate/161_add_contract_details.rb
|
258
|
+
- db/migrate/171_add_contracts.rb
|
251
259
|
- Rakefile
|
252
260
|
- README.md
|
253
261
|
- LICENSE
|