IB-Ruby translates data received from the TWS in IB::Model objects which are subsequently stored in database classes prefixed by 'ib'(Namespace: IB).

The date-grid is implemented by database-classes prefixed by 'tg' (Namespace: TG).

It is advisable to put calculations and further analyses to another namespace.

HC::Portfolio

As living example, HC::Portfolio is included, which is initialized through bake:

module HC
 class Portfolio
  def self.bake  
   gateway= IB::OrientGateway.current                 # OrientDB-Instance
   gateway.get_account_data                           # ask TWS for portfolio-data
   gateway.clients.map do | the_client |              # perform task for all clients
     p = self.new
     p.import_account_data from: the_client           # implies update of p, assigns a rid
     p.import_positions    from: the_client           # assigns portfolio-values to the grid
     HC::MY_USER.create    from: p, to: the_client    # bidirectional link to account
     Date.today.to_tg.assign vertex: p, via: HC::D2F  # bidirectional link to the time-grid
     p.rid                                            # return_value
    end
  end
 end
end

The last command of the bake-method assigns the portfolio-object via HC::D2F to the time-grid.
In the process, a bidirectional link TimeGrid -- out HC::D2F -- in -- Portfolio is implemented.

to_human

Most classes in IB-Ruby ship with a to_human-method. HC::Portfolio#to_human summarizes the associated objects:

> HC::Portfolio.last.to_human
 => "<Portfolio[244:0]: account:<user U1****12>, 36 AccountValues; 18 Positions >"

Traverse through connected nodes

Active-Orient provides an intuitive interface to connected nodes

> "7.12.2020".to_tg.out( HC::D2F ).in         => Array of assigned Portfolio's
> HC::Portfolio.last.in( /d2f/ ).out.datum    => ["7.12.2020"] # Array of time-grid-objects

Edges can be pronounced either by the class-name or a regular expression, which relies on the database-class name.

Get Account-Data

In the IB-Gateway-Section a primitive approach to access Account-Value-Data is presented.

HC::Portfolio#import_account_data imports IB::AccountValue-Objects as hash into the database.

 HC::Portfolio.values[ :key ][:ALL][:EUR] => value
                                   [:USD] => value
                             [:S]  [:EUR] => Value
                             [:C]  [:EUR] => value

Thus to access the data from yesterday:

 Date.today.to_tg.prev.portfolios &.values.map{|y| y[:SMA][:ALL][:EUR] }
 => [-9033.37, 12565.67]
 # or
 Date.yesterday.to_tg.portfolios &.values.map{|y| y.slice /SM/ }
 => [{:SMA=>{:ALL=>{:EUR=>-4701.63}, :S=>{:EUR=>-4701.63}}}, (...) 

Database Solution

The same result is achieved using a simple database query

  select values[ key ] as key from hc_portfolio

which can be send to the database through execute

> V.db.execute{ "select values[\"SMA\"] as SMA   from hc_portfolio " }
  => [{:SMA=>{:ALL=>{:EUR=>-4701.63}, :S=>{:EUR=>-4701.63}}}, ... 

Monitor the Portfolio

HC::Portfolio.bake imports IB::PortfolioValues as well. The records are connected via HC::HAS_POSITION-edges.

 `TG::Tag -- out  `HC::D2F` in --  `HC::Portfolio` -- `out` HC_HAS_POSITION `in` -- `IB::PortfolioValue` 

To access PortfolioValues just join the edges.

> Date.today.to_tg.out( /d2F/).in.out( /has/).in.contract
 => [["#202:0", "#203:0", "#204:0"], ... 
# contracts are stored as rid-valus which have to be expanded:
> puts Date.today.to_tg.out(HC::D2F).in.out( HC::HAS_POSITION ).in.contract.expand.to_human
     <Stock: J36 USD SGX>
     <Stock: PRX EUR AEB>
     <Stock: WFC USD NYSE>
     <Stock: ALB USD NYSE>
		 (..)

Other properties of IB::PortfolioValue can be obtained through ruby methods

> portfolio_values =  Date.today.to_tg.out(TG::D2F).in.out( HC::HAS_POSITION ).in
> puts portfolio_values.map{|y| [ y.contract.expand.to_human, y.unrealized_pnl ].join " -> \t "}
  <Option: AIR 20210115 put 86.0 DTB EUR>   -> 277.44
  <Stock: BABA USD NYSE>                    -> 222.84
  <Stock: BEPC USD NYSE>                    -> 394.47
  <Option: BOSS 20210219 put 25.0 DTB EUR>  -> -83.07
   (...)  

Specific positions can be filtered in the process.

Instead of in and out, edges have to be accessed through Vertex#nodes.

# Get Jardine Matheson
> jardine = IB::Contract.where symbol: 'J36'
# Fetch PortfolioPositions
> portfolio = Date.today.to_tg.out(HC::D2F).in.first
> portfolio.nodes( via: /has/, where: { contract: jardine.rid } ).market_value
  #INFO->select  outE('hc_has_position').in[ contract in [202:0] ]  from #246:20
  => [6480.48] 

Monitor the progress of a position

This approach works with a range of datasets as well.

Assume, we »baked« a few HC::Portfolios in the last days. Then Date.today.to_tg.environment(7,0) addresses the TG::Tag vertices of the last week. Documentation: Time-Grid Gem

Then

 > Date.today.to_tg
             .environment(7,0)
             .out(HC::D2F).in
             .nodes( via: /has/, where: { contract: jardine.rid } )
             .market_value
 => [[[6424.77]], [[6522.31]], [[6480.48]]]

In this case, only one portfolio is monitored by IB-Ruby, thus only one element is left in each sub-array.

Note that only three TG_Tag vertices were populated. Empty TimeGrid-Entries are silently ignored.

IB::Account, IB::Advisor, IB::User

Finally each HC::Portfolio-record is connected to an IB::Account-reference. Again, the connection is bidirectional, each IB::Account knows how many HC::Portfolio records are saved and each HC::Portfolio is bind to an Account. Inheritance is maintained.

> IB::Account.first.to_human
  INFO->select from ib_account order by @rid limit  1
 => "<user U1****12>" 

 >  puts IB::Account.first.in.out.to_human
   <Portfolio[242:0]: account:<user U12****2>, 35 AccountValues; 19 Positions >
   <Portfolio[243:0]: account:<user U12****2>, 35 AccountValues; 19 Positions >
   <Portfolio[244:0]: account:<user U12****2>, 36 AccountValues; 18 Positions >
   <Portfolio[245:0]: account:<user U12****2>, 36 AccountValues; 18 Positions >
   <Portfolio[246:0]: account:<user U12****2>, 36 AccountValues; 18 Positions >
   <Portfolio[247:0]: account:<user U12****2>, 36 AccountValues; 18 Positions >
   <Portfolio[248:0]: account:<user U12****2>, 34 AccountValues; 20 Positions >
   <Portfolio[249:0]: account:<user U12****2>, 35 AccountValues; 19 Positions >
   <Portfolio[242:1]: account:<user U12****2>, 38 AccountValues; 21 Positions >
   <Portfolio[243:1]: account:<user U12****2>, 38 AccountValues; 22 Positions >

> IB::Account.first.in.out.in( /d2F/).out.datum
  => [[Mon, 07 Dec 2020], [Tue, 08 Dec 2020], [Wed, 09 Dec 2020], ... 

Tags: