require 'ib/messages/outgoing/abstract_message'

# TODO: Don't instantiate messages, use their classes as just namespace for .encode/decode

module IB
  module Messages

    # Outgoing IB messages (sent to TWS/Gateway)
    module Outgoing
      extend Messages # def_message macros

      ### Defining (short) Outgoing Message classes for IB:

      ## Empty messages (no data)

      # Request the open orders that were placed from THIS client. Each open order
      # will be fed back through the OpenOrder and OrderStatus messages ONCE.
      # NB: Client with a client_id of 0 will also receive the TWS-owned open orders.
      # These orders will be associated with the client and a new orderId will be
      # generated. This association will persist over multiple API and TWS sessions.
      RequestOpenOrders = def_message 5

      # Request the open orders placed from all clients and also from TWS. Each open
      # order will be fed back through the OpenOrder and OrderStatus messages ONCE.
      # Note this does not re-bind those Orders to requesting Client!
      # Use RequestAutoOpenOrders to request such re-binding.
      RequestAllOpenOrders = def_message 16

      # Request that newly created TWS orders be implicitly associated with this client.
      # When a new TWS order is created, the order will be associated with this client
      # and automatically fed back through the OpenOrder and OrderStatus messages.
      # It is a 'continuous' request such that it gets turned 'on' when called with a
      # TRUE auto_bind parameter. When it's called with FALSE auto_bind, new TWS orders
      # will not bind to this client going forward. Note that TWS orders can only be
      # bound to clients with a client_id of 0. TODO: how to properly test this?
      # data = { :auto_bind => boolean }
      RequestAutoOpenOrders = def_message 15, :auto_bind

      # Requests an XML document that describes the valid parameters that a scanner
      # subscription can have (for outgoing RequestScannerSubscription message).
      RequestScannerParameters = def_message 24

		  RequestNewsArticle = def_message 84,
				 :request_id , # autogenerated
				 :provider_code,
				 :article_id,
				 :options  # taglist


			RequestNewsProviders =  def_message 85   # no further parameters
			RequestHistoricalNews =  def_message 86,
				 :request_id , # autogenerated
				 :con_id,
				 :provider_code,
				 :start,      # date
				 :total_results,
				 :options  # taglist


      CancelNewsBulletins = def_message 13
      RequestCurrentTime = def_message 49
      RequestGlobalCancel = def_message 58

      ## Data format is: @data = { :id => ticker_id}
      CancelMarketData = def_message [2, 2]
      CancelMarketDepth = def_message 11
      CancelScannerSubscription = def_message 23
      CancelHistoricalData = def_message 25
      CancelRealTimeBars = def_message 51

      ## Data format is: @data = { :id => request_id }
      CancelFundamentalData = def_message 53
      CancelCalculateImpliedVolatility = CancelImpliedVolatility = def_message 56
      CancelCalculateOptionPrice = CancelOptionPrice = def_message 57

      ## Data format is: @data ={ :id => local_id of order to cancel }
      CancelOrder = def_message 4

      # Request the next valid ID that can be used when placing an order. Responds with
      # NextValidId message, and the id returned is that next valid Id for orders.
      # That ID will reflect any autobinding that has occurred (which generates new
      # IDs and increments the next valid ID therein).
      # @data = { :number of ids requested => int } NB: :number option is ignored by TWS!
      RequestIds = def_message 8, [:number, 1]
      # data = { :all_messages => boolean }
      RequestNewsBulletins = def_message 12, :all_messages
      # data = { :log_level => int }
      SetServerLoglevel = def_message 14, :log_level
      # data = { :fa_data_type => int }  1 -> groups, 2 -> Profiles, 3 -> Account Aliases
      RequestFA = def_message 18, :fa_data_type
      # data = { :fa_data_type => int, :xml => String }
      ReplaceFA = def_message 19, :fa_data_type, :xml
      # data = { :market_data_type => int }


      # data => { :id => request_id (int), :contract => Contract }
      #
      # Special case for options: "wildcards" in the Contract fields retrieve Option chains
      #   strike = 0 means all strikes
      #   right = "" meanns both call and put
      #   expiry = "" means all expiries
      #   expiry = "2013" means all expiries in 2013
      #   expiry = "201311" means all expiries in Nov 2013
      # You'll get several ContractData (10) messages back if there is more than one match.
      # When all the matches are delivered you'll get ContractDataEnd (52) message.
      RequestContractDetails = RequestContractData =
          def_message([9, 8], :request_id , # autogenerated
                      [:contract, :serialize_long, [:sec_id_type]])
	
			# Requests security definition option parameters for viewing a contract's option chain 
			#  request_id:   The ID chosen for the request
      #  underlyingSymbol 
			#  futFopExchange:  The exchange on which the returned options are trading. 
			#										Can be set to the empty string "" for all exchanges. 
			#  underlyingSecType: The type of the underlying security, i.e. STK 
			#  underlyingConId: the contract ID of the underlying security.
			#  con_id:	
      #  Responses via Messages::Incoming::SecurityDefinitionOptionParameter


			RequestSecurityDefinitionOptionParameters = ReqSecDefOptParams  = RequestOptionChainDefinition = def_message [78,0],
																	:request_id,   # autogenerated if not specified
																	:symbol,				# underlyingSymbol
																	[:exchange, ""],			# futOptExchange
																	:sec_type,			# underlyingSecType
																	:con_id					# underlyingConId  (required)
																	
      # data = { :id => ticker_id (int), :contract => Contract, :num_rows => int }


#			Requests venues for which market data is returned to updateMktDepthL2
			#			returns MarketDepthExchanges-Message
			#
      RequestMarketDepthExchanges =			# requires ServerVersion >= 112
		 	   def_message 82

 
			## actual Version supported is: 137 
			## changes:   MIN_SERVER_VER_SMART_DEPTH: 146  --> insert 'is_smarth_depth' after 'num_rows'
			##  then: 'is_smart_depth' (bool) has to be specified in  CancelMarketDepth, too
			#
      RequestMarketDepth = def_message([10, 5],
																	:request_id,   # autogenerated if not specified
                                       [:contract, :serialize_supershort ],
                                       :num_rows, 
				       "")  #  mktDataOptionsStr. ## not supported by api

      # When this message is sent, TWS responds with ExecutionData messages, each
      # containing the execution report that meets the specified criteria.
      # @data={:id =>         int: :request_id,
      #        :client_id => int: Filter the results based on the clientId.
      #        :account => Filter the results based on based on account code.
      #                      Note: this is only relevant for Financial Advisor accts.
      #        :sec_type =>  Filter the results based on the order security type.
      #        :time =>      Filter the results based on execution reports received
      #                      after the specified time - format "yyyymmdd-hh:mm:ss"
      #        :symbol   =>  Filter the results based on the order symbol.
      #        :exchange =>  Filter the results based on the order exchange
      #        :side =>  Filter the results based on the order action: BUY/SELL/SSHORT
      RequestExecutions = def_message([7, 3],
																			 :request_id,  # autogenerated if not specified
                                      :client_id,
                                      :account,
                                      :time, # Format "yyyymmdd-hh:mm:ss"
                                      :symbol,
                                      :sec_type,
                                      :exchange,
                                      :side)

      # data = { :id => ticker_id (int),
      #          :contract => IB::Contract,
      #          :exercise_action => int, 1 = exercise, 2 = lapse
      #          :exercise_quantity => int, The number of contracts to be exercised
      #          :account => string,
      #          :override => int: Specifies whether your setting will override the
      #                       system's natural action. For example, if your action
      #                       is "exercise" and the option is not in-the-money, by
      #                       natural action the option would not exercise. If you
      #                       have override set to "yes" the natural action would be
      #                       overridden and the out-of-the money option would be
      #                       exercised. Values are:
      #                              - 0 = do not override
      #                              - 1 = override
      ExerciseOptions = def_message([ 21, 2 ],  
																	#		:request_id, # id -> required  # todo : TEST
                                    [:contract, :serialize_short],
                                    :exercise_action,
                                    :exercise_quantity,
                                    :account,
                                    :override)


      # The API can receive frozen market data from Trader Workstation. Frozen market
      # data is the last data recorded in our system. During normal trading hours,
      # the API receives real-time market data. If you use this function, you are
      # telling TWS to automatically switch to frozen market data AFTER the close.
      # Then, before the opening of the next trading day, market data will automatically
      # switch back to real-time market data.
      # :market_data_type = 1 (:real_time) for real-time streaming, 2 (:frozen) for frozen market data
			#										= 3 (:delayed) for delayed streaming , 4 (:frozen_delayed)  for frozen delayed
      RequestMarketDataType =
          def_message 59, [:market_data_type,
                           lambda { |type| MARKET_DATA_TYPES.invert[type] || type }, []]

      # Send this message to receive Reuters global fundamental data. There must be
      # a subscription to Reuters Fundamental set up in Account Management before
      # you can receive this data.
      # data = { :id => int: :request_id,
      #          :contract => Contract,
      #          :report_type => String: one of the following:
      #                   'estimates' - Estimates
      #                   'finstat'   - Financial statements
      #                    'snapshot' - Summary   }a
			#                    ReportsFinSummary	Financial summary
			#ReportsOwnership	Company's ownership (Can be large in size)
			#ReportSnapshot	Company's financial overview
			#ReportsFinStatements	Financial Statements
			#RESC	Analyst Estimates
			#CalendarReport	Company's calendar 
      RequestFundamentalData =
          def_message([52,2],
                     :request_id, # autogenerated if not specified
											 [:contract, :serialize, :primary_exchange],
                      :report_type,
											""  )

			# Returns the timestamp of earliest available historical data for a contract and data type. 
					#  :what_to_show: type of data for head timestamp - "BID", "ASK", "TRADES", etc 
					#  :use_rth     : use regular trading hours only, 1 for yes or 0 for no 
					#  format_data	: set to 2 to obtain it like system time format in second ---> don't change
			RequestHeadTimeStamp = 
					def_message( [87,0], 	:request_id, # autogenerated
					[:contract, :serialize_short, [:primary_exchange,:include_expired] ],
					[:use_rth, 1 ],
					[:what_to_show, 'Trades' ],
					[:format_date, 2 ]  )    ## don't change!

			CancelHeadTimeStamp =
					def_message [90,0 ] # , :(request_)id #required



			RequestHistogramData = 
					def_message( [88, 0],  :request_id, #  autogenerated
					[:contract, :serialize_short, [:primary_exchange,:include_expired] ],
					[:use_rth, 1 ],
					[:time_period ]   )

			CancelHistogramData =
					def_message [89,0 ] # , :(request_)id  required

			## Attention: If not reasonable data are used, simply nothing is returned.
			## There is no error message either.
      RequestCalculateImpliedVolatility = CalculateImpliedVolatility =
          RequestImpliedVolatility =
              def_message([ 54,3 ],:request_id,  # autogenerated
                      [:contract, :serialize_short],
                          :option_price,
                          :under_price,
													[:implied_volatility_options_count, 0],
												  [:implied_volatility_options_conditions, ''])

      # data = { :request_id => int, :contract => Contract,
      #          :volatility => double, :under_price => double }
      RequestCalculateOptionPrice = CalculateOptionPrice = RequestOptionPrice =
          def_message([ 55, 3], :request_id, #autogenerated if not specified
                      [:contract, :serialize_short],
                      :volatility,
                      :under_price,
													[:implied_volatility_options_count, 0],
												  [:implied_volatility_options_conditions, ''])

      # Start receiving market scanner results through the ScannerData messages.
      # @data = { :id => ticker_id (int),
      #  :number_of_rows => int: number of rows of data to return for a query.
      #  :instrument => The instrument type for the scan. Values include
      #                                'STK', - US stocks
      #                                'STOCK.HK' - Asian stocks
      #                                'STOCK.EU' - European stocks
      #  :location_code => Legal Values include:
      #                           - STK.US - US stocks
      #                           - STK.US.MAJOR - US stocks (without pink sheet)
      #                           - STK.US.MINOR - US stocks (only pink sheet)
      #                           - STK.HK.SEHK - Hong Kong stocks
      #                           - STK.HK.ASX - Australian Stocks
      #                           - STK.EU - European stocks
      #  :scan_code => The type of the scan, such as HIGH_OPT_VOLUME_PUT_CALL_RATIO.
      #  :above_price => double: Only contracts with a price above this value.
      #  :below_price => double: Only contracts with a price below this value.
      #  :above_volume => int: Only contracts with a volume above this value.
      #  :market_cap_above => double: Only contracts with a market cap above this
      #  :market_cap_below => double: Only contracts with a market cap below this value.
      #  :moody_rating_above => Only contracts with a Moody rating above this value.
      #  :moody_rating_below => Only contracts with a Moody rating below this value.
      #  :sp_rating_above => Only contracts with an S&P rating above this value.
      #  :sp_rating_below => Only contracts with an S&P rating below this value.
      #  :maturity_date_above => Only contracts with a maturity date later than this
      #  :maturity_date_below => Only contracts with a maturity date earlier than this
      #  :coupon_rate_above => double: Only contracts with a coupon rate above this
      #  :coupon_rate_below => double: Only contracts with a coupon rate below this
      #  :exclude_convertible => Exclude convertible bonds.
      #  :scanner_setting_pairs => Used with the scan_code to help further narrow your query.
      #                            Scanner Setting Pairs are delimited by slashes, making
      #                            this parameter open ended. Example is "Annual,true" -
      #                            when used with 'Top Option Implied Vol % Gainers' scan
      #                            would return annualized volatilities.
      #  :average_option_volume_above =>  int: Only contracts with average volume above this
      #  :stock_type_filter => Valid values are:
      #                          'ALL' (excludes nothing)
      #                          'STOCK' (excludes ETFs)
      #                          'ETF' (includes ETFs) }
      # ------------
      # To learn all valid parameter values that a scanner subscription can have,
      # first subscribe to ScannerParameters and send RequestScannerParameters message.
      # Available scanner parameters values will be listed in received XML document.
      RequestScannerSubscription =
          def_message([22, 3], :request_id , 
                      [:number_of_rows, -1], # was: EOL,
                      :instrument,
                      :location_code,
                      :scan_code,
                      :above_price,
                      :below_price,
                      :above_volume,
                      :market_cap_above,
                      :market_cap_below,
                      :moody_rating_above,
                      :moody_rating_below,
                      :sp_rating_above,
                      :sp_rating_below,
                      :maturity_date_above,
                      :maturity_date_below,
                      :coupon_rate_above,
                      :coupon_rate_below,
                      :exclude_convertible,
                      :average_option_volume_above, # ?
                      :scanner_setting_pairs,
                      :stock_type_filter)


										  
      require 'ib/messages/outgoing/place_order'
      require 'ib/messages/outgoing/bar_requests'
      require 'ib/messages/outgoing/account_requests'
      require 'ib/messages/outgoing/request_marketdata'
      require 'ib/messages/outgoing/request_tick_data'

    end # module Outgoing
  end # module Messages
end # module IB

__END__
## python: message.py
     REQ_MKT_DATA                  = 1
     CANCEL_MKT_DATA               = 2
     PLACE_ORDER                   = 3
     CANCEL_ORDER                  = 4
     REQ_OPEN_ORDERS               = 5
     REQ_ACCT_DATA                 = 6
     REQ_EXECUTIONS                = 7
     REQ_IDS                       = 8
     REQ_CONTRACT_DATA             = 9
     REQ_MKT_DEPTH                 = 10
     CANCEL_MKT_DEPTH              = 11
     REQ_NEWS_BULLETINS            = 12
     CANCEL_NEWS_BULLETINS         = 13
     SET_SERVER_LOGLEVEL           = 14
     REQ_AUTO_OPEN_ORDERS          = 15
     REQ_ALL_OPEN_ORDERS           = 16
     REQ_MANAGED_ACCTS             = 17
     REQ_FA                        = 18
     REPLACE_FA                    = 19
     REQ_HISTORICAL_DATA           = 20
     EXERCISE_OPTIONS              = 21
     REQ_SCANNER_SUBSCRIPTION      = 22
     CANCEL_SCANNER_SUBSCRIPTION   = 23
     REQ_SCANNER_PARAMETERS        = 24
     CANCEL_HISTORICAL_DATA        = 25
     REQ_CURRENT_TIME              = 49
     REQ_REAL_TIME_BARS            = 50
     CANCEL_REAL_TIME_BARS         = 51
     REQ_FUNDAMENTAL_DATA          = 52
     CANCEL_FUNDAMENTAL_DATA       = 53
     REQ_CALC_IMPLIED_VOLAT        = 54
     REQ_CALC_OPTION_PRICE         = 55
     CANCEL_CALC_IMPLIED_VOLAT     = 56
     CANCEL_CALC_OPTION_PRICE      = 57
     REQ_GLOBAL_CANCEL             = 58
     REQ_MARKET_DATA_TYPE          = 59  --> supported by ib-ruby 0.94
     
     REQ_POSITIONS                 = 61   supported now
     REQ_ACCOUNT_SUMMARY           = 62   supported now

     CANCEL_ACCOUNT_SUMMARY        = 63   supported now

     CANCEL_POSITIONS              = 64   supported now
     VERIFY_REQUEST                = 65
     VERIFY_MESSAGE                = 66
     QUERY_DISPLAY_GROUPS          = 67
     SUBSCRIBE_TO_GROUP_EVENTS     = 68
     UPDATE_DISPLAY_GROUP          = 69
     UNSUBSCRIBE_FROM_GROUP_EVENTS = 70
     START_API                     = 71
     VERIFY_AND_AUTH_REQUEST       = 72
     VERIFY_AND_AUTH_MESSAGE       = 73
     REQ_POSITIONS_MULTI           = 74   supported now
     CANCEL_POSITIONS_MULTI        = 75   supported now

     REQ_ACCOUNT_UPDATES_MULTI     = 76   supported now

     CANCEL_ACCOUNT_UPDATES_MULTI  = 77   supported now

     REQ_SEC_DEF_OPT_PARAMS        = 78   supported now
     REQ_SOFT_DOLLAR_TIERS         = 79
     REQ_FAMILY_CODES              = 80
     REQ_MATCHING_SYMBOLS          = 81
     REQ_MKT_DEPTH_EXCHANGES       = 82
     REQ_SMART_COMPONENTS          = 83
     REQ_NEWS_ARTICLE              = 84  in preparation
     REQ_NEWS_PROVIDERS            = 85		in preparatino
     REQ_HISTORICAL_NEWS           = 86 in preparation

     REQ_HEAD_TIMESTAMP            = 87   supported now

     REQ_HISTOGRAM_DATA            = 88   supported now

     CANCEL_HISTOGRAM_DATA         = 89   supported now

     CANCEL_HEAD_TIMESTAMP         = 90   supported now

     REQ_MARKET_RULE               = 91
     REQ_PNL                       = 92
     CANCEL_PNL                    = 93
     REQ_PNL_SINGLE                = 94
     CANCEL_PNL_SINGLE             = 95
     REQ_HISTORICAL_TICKS          = 96
     REQ_TICK_BY_TICK_DATA         = 97
     CANCEL_TICK_BY_TICK_DATA      = 98