# frozen_string_literal: true require 'eventmachine' require 'json' module Cryptum # This plugin is used to Establish a Web # Socket Connection with Coinbase module WebSock module EventMachine # Supported Method Parameters:: # Cryptum::WS.run( # ) public_class_method def self.run(opts = {}) env = opts[:env] option_choice = opts[:option_choice] terminal_win = opts[:terminal_win] event_history = opts[:event_history] indicator_status = opts[:indicator_status] max_conn_attempts = 30 conn_attempt = 0 begin conn_attempt += 1 event_history.reconnected = true if conn_attempt > 1 # Automatically Create Bot Configs if they don't # Exist and Initialize Automated Trading Parameters bot_conf = Cryptum.read_bot_conf(option_choice: option_choice) EM.run do # Iterate as fast as possible # This ensures candle timing is accurate # and everything is fast as possible # Defaults to 100ms, 5ms is the lowest possible delay_ms = 5 delay_ms_cast_as_decimal = delay_ms * 0.001 EM.set_quantum(delay_ms) ws = Cryptum::WebSock::Coinbase.connect( option_choice: option_choice, env: env ) ws.on :open do |_event| ws.send( Cryptum::WebSock::Coinbase.subscribe_message( option_choice: option_choice, env: env ) ) end ws.on :message do |event| # TODO: The Speed of the UI is dictated by the # Frequency of WebSocket Messages Coming Through. # Explore another way to decouple required events # (such as keypresses) from only being triggered # when messages come through. ai_enabled = bot_conf[:artifical_intelligence] event_history.event = JSON.parse( event.data, symbolize_names: true ) event_history.event_type = event_history.event[:type].to_s.to_sym event_history = Cryptum::Event.parse( env: env, terminal_win: terminal_win, option_choice: option_choice, event_history: event_history, indicator_status: indicator_status, bot_conf: bot_conf, ai_enabled: ai_enabled ) # Detect Key Press Events Cryptum::Event::KeyPress.detect(terminal_win: terminal_win) # Cancel ALL Open Orders when # C is pressed if terminal_win.key_press_event.key_c Cryptum::Event::Cancel.open_orders( terminal_win: terminal_win, option_choice: option_choice, env: env ) end # Get the F* Out (GTFO) when # G is pressed if terminal_win.key_press_event.key_g event_history = Cryptum::Event::GTFO.now( terminal_win: terminal_win, option_choice: option_choice, env: env, event_history: event_history, bot_conf: bot_conf ) end # Reload Bot Conf when r is Pressed if terminal_win.key_press_event.key_r bot_conf = Cryptum::Event::BotConf.reload( terminal_win: terminal_win, event_history: event_history, option_choice: option_choice ) end # Only Write Order Book to File when # W is Pressed if terminal_win.key_press_event.key_w Cryptum::Event::OrderBook.write( terminal_win: terminal_win, event_history: event_history ) end # Exit if x is Pressed if terminal_win.key_press_event.key_x Cryptum::Event::Exit.gracefully( terminal_win: terminal_win, event_history: event_history, option_choice: option_choice, env: env ) end # TAB through Order Plan / Order Execution Window Panes if terminal_win.key_press_event.key_tab Cryptum::Event::Pane.switch( terminal_win: terminal_win, event_history: event_history ) end # Scroll Up Order Plan / Order Execution Window Panes if terminal_win.key_press_event.key_up_arrow Cryptum::Event::Scroll.up( terminal_win: terminal_win, event_history: event_history ) end # Scroll Down Order Plan / Order Execution Window Panes if terminal_win.key_press_event.key_down_arrow Cryptum::Event::Scroll.down( terminal_win: terminal_win, event_history: event_history ) end # Scroll Up Order Plan / Order Execution Window Panes Faster if terminal_win.key_press_event.key_page_up Cryptum::Event::Scroll.page_up( terminal_win: terminal_win, event_history: event_history ) end # Scroll Down Order Plan / Order Execution Window Panes Faster if terminal_win.key_press_event.key_page_down Cryptum::Event::Scroll.page_down( terminal_win: terminal_win, event_history: event_history ) end # Scroll to Top of Order Plan / Order Execution Window Panes if terminal_win.key_press_event.key_home Cryptum::Event::Scroll.top( terminal_win: terminal_win, event_history: event_history ) end # Scroll to Bottom of Order Plan / Order Execution Window Panes if terminal_win.key_press_event.key_end Cryptum::Event::Scroll.bottom( terminal_win: terminal_win, event_history: event_history ) end # Open / Close Order Plan / Order Execution Details Window if terminal_win.key_press_event.key_enter Cryptum::Event::Pane.toggle_details( terminal_win: terminal_win, event_history: event_history ) end end ws.on :close do raise Errno::ECONNRESET end EM.add_periodic_timer(delay_ms_cast_as_decimal) do order_countdown = Cryptum::UI::OrderTimer.refresh( option_choice: option_choice, event_history: event_history, order_timer_win: terminal_win.order_timer_section, indicator_status: indicator_status, key_press_event: terminal_win.key_press_event ) if order_countdown.zero? || order_countdown.negative? # Ready to Submit a BUY order event_history.order_ready = true # Reload Bot Conf (i.e. Risk Allocation), # Recalculate Order Plan, and Write to File terminal_win.key_press_event.key_r = true # Write Order Book to Disk # terminal_win.key_press_event.key_w = true end end EM.add_periodic_timer(option_choice.market_trend_reset) do Cryptum::OrderBook::MarketTrend.reset( option_choice: option_choice, terminal_win: terminal_win, event_history: event_history, bot_conf: bot_conf ) # Reload Bot Conf (i.e. Risk Allocation) # Recalculate Order Plan, and Write to File terminal_win.key_press_event.key_r = true # IMPORTANT: # Wait for Order Plan recalculation to occur # once Cryptum::UI::OrderPlan is refreshed # in Cryptum::Event _BEFORE_ writing the order # book to file. end end rescue Faye::WebSocket::API::ErrorEvent, Errno::ECONNREFUSED, Errno::ECONNRESET => e File.open('/tmp/cryptum-errors.txt', 'a') do |f| f.puts Time.now.strftime('%Y-%m-%d %H:%M:%S.%N %z') f.puts "Module: #{self}" f.puts "#{e}\n\n\n" end raise e if conn_attempt > max_conn_attempts sleep 1 retry end rescue Interrupt # Exit Gracefully if CTRL+C is Pressed During Session Cryptum.exit_gracefully( which_self: self, event_history: event_history, option_choice: option_choice, env: env ) rescue StandardError => e raise e end # Display Usage for this Module public_class_method def self.help puts "USAGE: logger = #{self}.create() " end end end end