<commit_before>require 'nodus/fx'

module Nodus
  module FX
    class Oanda < Nodus::Node
      PARAMS = {production: {access_var:  'OANDA_ACCESS_TOKEN',
                             api_root:    'https://api-fxtrade.oanda.com/',
                             stream_root: 'https://stream-fxtrade.oanda.com/'},
                practice:   {access_var:  'OANDA_ACCESS_TOKEN_PRACTICE',
                             api_root:    'https://api-fxpractice.oanda.com/',
                             stream_root: 'https://stream-fxpractice.oanda.com/'}}

      def defaults() PARAMS[@source] || {} end

      def parameterize(opts={})
        @source       = opts.delete(:source)       || :production
        error NotImplementedError, "Sandbox Oanda access not implemented- should be easy if you want it." if @source == :sandbox
        error ArgumentError,       "Oanda generator source must be one of [:production, :practice, :sandbox]" unless defaults.present?
        @access_token = opts.delete(:access_token) || ENV[defaults[:access_var] || '']
        error ArgumentError,       "No access_token specified. Can also put in env: #{PARAMS.map{|_,d|d[:access_var]}.join(' and/or ')}" if @access_token.blank? && @source != :sandbox
        @api_root     = opts.delete(:api_root)     || defaults[:api_root]
        @stream_root  = opts.delete(:stream_root)  || defaults[:stream_root]
        @account_id   = opts.delete(:account_id)   # will get looked up later if needed and not specified here

        # Default to all available instruments if none specified
        @requested_instruments =  opts.delete(:instruments) || opts.delete(:pairs)
        instruments.each{|i| output("#{i.base}_#{i.counter}".to_sym) } # TODO: specify type (when there is one)
      end

      private def api_conn
        @api_conn ||= Excon.new(@api_root, headers: {'Authorization' => "Bearer #{@access_token}"}, persistent: true)
      end

      def all_available_instruments
        @all_available_instruments ||= api_conn.get!(path: 'v1/instruments', query: {accountId: account_id}).payload['instruments']
      end

      def account_id
        return @account_id unless @account_id.blank?
        resp     = api_conn.get!(path: 'v1/accounts', idempotent: true)
        accounts = resp.payload['accounts']
        error RuntimeError,        "There are no accounts associated with this auth token (#{resp.payload})"  unless accounts.present? && accounts[0].present?
        error NotImplementedError, "Choosing between multiple accounts not implemented"                           if accounts.size > 1<commit_msg>Avoid querying for ENV[''], which was happening under certain circumstances.<commit_after>require 'nodus/fx'

module Nodus
  module FX
    class Oanda < Nodus::Node
      PARAMS = {production: {access_var:  'OANDA_ACCESS_TOKEN',
                             api_root:    'https://api-fxtrade.oanda.com/',
                             stream_root: 'https://stream-fxtrade.oanda.com/'},
                practice:   {access_var:  'OANDA_ACCESS_TOKEN_PRACTICE',
                             api_root:    'https://api-fxpractice.oanda.com/',
                             stream_root: 'https://stream-fxpractice.oanda.com/'}}

      def defaults() PARAMS[@source] || {} end

      def parameterize(opts={})
        @source       = opts.delete(:source)       || :production
        error NotImplementedError, "Sandbox Oanda access not implemented- should be easy if you want it." if @source == :sandbox
        error ArgumentError,       "Oanda generator source must be one of [:production, :practice, :sandbox]" unless defaults.present?
        @access_token = opts.delete(:access_token) || (defaults[:access_var].blank? ? nil : ENV[defaults[:access_var]])
        error ArgumentError,       "No access_token specified. Can also put in env: #{PARAMS.map{|_,d|d[:access_var]}.join(' and/or ')}" if @access_token.blank? && @source != :sandbox
        @api_root     = opts.delete(:api_root)     || defaults[:api_root]
        @stream_root  = opts.delete(:stream_root)  || defaults[:stream_root]
        @account_id   = opts.delete(:account_id)   # will get looked up later if needed and not specified here

        # Default to all available instruments if none specified
        @requested_instruments =  opts.delete(:instruments) || opts.delete(:pairs)
        instruments.each{|i| output("#{i.base}_#{i.counter}".to_sym) } # TODO: specify type (when there is one)
      end

      private def api_conn
        @api_conn ||= Excon.new(@api_root, headers: {'Authorization' => "Bearer #{@access_token}"}, persistent: true)
      end

      def all_available_instruments
        @all_available_instruments ||= api_conn.get!(path: 'v1/instruments', query: {accountId: account_id}).payload['instruments']
      end

      def account_id
        return @account_id unless @account_id.blank?
        resp     = api_conn.get!(path: 'v1/accounts', idempotent: true)
        accounts = resp.payload['accounts']
        error RuntimeError,        "There are no accounts associated with this auth token (#{resp.payload})"  unless accounts.present? && accounts[0].present?
        error NotImplementedError, "Choosing between multiple accounts not implemented"                           if accounts.size > 1
{'fraction_non_alphanumeric': 0.12144751799924214, 'fraction_numerical': 0.0015157256536566881, 'mean_word_length': 2.7333804809052333, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 8, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1837655', 'n_tokens_mistral': 1544, 'n_tokens_neox': 1498, 'n_words': 376}