diff --git a/lib/ib/constants.rb b/lib/ib/constants.rb index 988f62b..a7d8101 100644 --- a/lib/ib/constants.rb +++ b/lib/ib/constants.rb @@ -205,7 +205,6 @@ module IB 'NONE' => :none, # Used to indicate no hedge in :delta_neutral_order_type 'None' => :none, # Used to indicate no hedge in :delta_neutral_order_type }.freeze - # Valid security types (sec_type attribute of IB::Contract) SECURITY_TYPES = { 'BAG' => :bag, @@ -223,7 +222,13 @@ module IB 'OPT' => :option, 'IOPT' => :dutch_option, 'STK' => :stock, - 'WAR' => :warrant}.freeze + 'WAR' => :warrant, + 'ICU' => :icu, + 'ICS' => :ics, + 'BILL' => :bill, + 'BSK' => :basket, + 'FWD' => :forward, + 'FIXED' => :fixed }.freeze # Obtain symbolic value from given property code: # VALUES[:side]['B'] -> :buy diff --git a/lib/models/ib/contract.rb b/lib/models/ib/contract.rb index deee5b6..51052db 100644 --- a/lib/models/ib/contract.rb +++ b/lib/models/ib/contract.rb @@ -1,6 +1,8 @@ require 'models/ib/contract_detail' require 'models/ib/underlying' + + module IB class Contract < IB::Model include BaseProperties @@ -203,6 +205,10 @@ def serialize_ib_ruby serialize_long.join(":") end + # extracts essential attributes of the contract, + # and returns a new contract. + # + # the link to contract-details is __not__ maintained. def essential self_attributes = [ :right, :sec_type] @@ -215,8 +221,11 @@ def essential end - # creates a new Contract substituting attributes by the provied key-value pairs + # creates a new Contract substituting attributes by the provied key-value pairs. + # + # con_id is resetted def merge **new_attributes + self.con_id = 0 self.class.new attributes.merge new_attributes end diff --git a/spec/contract_helper.rb b/spec/contract_helper.rb index acb8eef..dfab203 100644 --- a/spec/contract_helper.rb +++ b/spec/contract_helper.rb @@ -19,41 +19,23 @@ def request_con_id contract: SAMPLE end -shared_examples_for "correctly query's the tws" do - - it "query_contract does not raise an error" do - expect { contract.query_contract }.not_to raise_error - end - - - it "query_contract resets con_id" do - query_contract = contract.query_contract - unless contract.sec_type.nil? - expect( contract.con_id ).to be_zero - end - end - it "verify does intitialize con_id and contract_detail " do - contract.verify do | c | - expect( c.con_id ).not_to be_zero - expect( c.contract_detail).to be_a IB::ContractDetail - end - end - - it "verify returns a number" do - expect( contract.verify ).to be > 0 - end - - +RSpec.shared_examples 'a complete Contract Object' do + subject{ the_contract } + it_behaves_like 'a valid Contract Object' + it { is_expected.to be_an IB::Contract } + its( :contract_detail ){ is_expected.to be_a IB::ContractDetail } + its( :primary_exchange){ is_expected.to be_a String } end -shared_examples_for "invalid query of tws" do - - it "does not verify " do - contract.verify - expect( should_log /Not a valid Contract/ ).to be_truthy - end - - it "returns zero" do - expect( contract.verify ).to be_zero - end - +RSpec.shared_examples 'a valid Contract Object' do + subject{ the_contract } + it { is_expected.to be_an IB::Contract } + its( :con_id ){ is_expected.to be_empty.or be_a(Numeric) } + its( :contract_detail ){ is_expected.to be_nil.or be_a(IB::ContractDetail) } + its( :symbol ){ is_expected.to be_a String } + its( :local_symbol ){ is_expected.to be_a String } + its( :currency ){ is_expected.to be_a String } + its( :sec_type ){ is_expected.to be_a(Symbol).and satisfy { |sec_type| IB::SECURITY_TYPES.values.include?(sec_type) } } + its( :trading_class ){ is_expected.to be_a String } + its( :exchange ){ is_expected.to be_a String } + its( :primary_exchange){ is_expected.to be_nil.or be_a(String) } end diff --git a/spec/model/ib/contract_data_spec.rb b/spec/model/ib/contract_data_spec.rb new file mode 100644 index 0000000..defef6c --- /dev/null +++ b/spec/model/ib/contract_data_spec.rb @@ -0,0 +1,49 @@ +require 'main_helper' +require 'contract_helper' # provides request_con_id + +RSpec.shared_examples 'ContractData Message' do + subject{ the_message } + it { is_expected.to be_an IB::Messages::Incoming::ContractData } + its( :contract ){ is_expected.to be_a IB::Contract } + its( :contract_details ){ is_expected.to be_a IB::ContractDetail } + its( :message_id ){ is_expected.to eq 10 } + its( :version ){ is_expected.to eq 8 } + its( :buffer ){ is_expected.to be_empty } + it 'has class accessors as well' do + expect( subject.class.message_id).to eq 10 + expect( subject.class.message_type).to eq :ContractData + end +end + +RSpec.describe IB::Messages::Incoming::ContractData do + + # uses the SAMPLE Contract specified in spec_helper + + context IB::Stock, :connected => true do + before(:all) do + establish_connection + request_con_id # populate the recieved array with Contract and ContractDetail Message + end + + after(:all) { close_connection } + + it_behaves_like 'ContractData Message' do + let( :the_message ){ IB::Connection.current.received[:ContractData].first } + end + + + it_behaves_like 'a complete Contract Object' do + let( :the_contract ){ IB::Connection.current.received[:ContractData].first.contract } + end +# it "inspects" do # debugging +# ib = IB::Connection.current +# contract = ib.received[:ContractData].contract.first +# contract_details = ib.received[:ContractData].contract_details.first +# +# puts contract.inspect +# puts contract_details.inspect +# end + + end +end # describe IB::Messages:Incoming + diff --git a/spec/model/ib/contract_spec.rb b/spec/model/ib/contract_spec.rb new file mode 100644 index 0000000..741d386 --- /dev/null +++ b/spec/model/ib/contract_spec.rb @@ -0,0 +1,48 @@ +require 'main_helper' +require 'contract_helper' # provides request_con_id + +RSpec.shared_examples 'ContractData Message' do + subject{ the_message } + it { is_expected.to be_an IB::Messages::Incoming::ContractData } + its( :contract ){ is_expected.to be_a IB::Contract } + its( :contract_details ){ is_expected.to be_a IB::ContractDetail } + its( :message_id ){ is_expected.to eq 10 } + its( :version ){ is_expected.to eq 8 } + its( :buffer ){ is_expected.to be_empty } + it 'has class accessors as well' do + expect( subject.class.message_id).to eq 10 + expect( subject.class.message_type).to eq :ContractData + end +end + +RSpec.describe IB::Contract do + + # uses the SAMPLE Contract specified in spec_helper + + context IB::Stock, :connected => true do + before(:all) do + establish_connection + request_con_id # populate the recieved array with Contract and ContractDetail Message + end + + after(:all) { close_connection } + + + it_behaves_like 'a valid Contract Object' do + let( :the_contract ){ SAMPLE } + end + + + context '#merge' do + subject{ SAMPLE.merge( symbol: 'GE' ) } # returns a new object + its( :object_id ){is_expected.not_to eq SAMPLE.object_id } + its( :symbol ){is_expected.to eq 'GE'} + its( :con_id ){is_expected.to be_zero} + end + + + + + end +end # describe IB::Messages:Incoming +