cryptum 0.0.230

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/main.yml +16 -0
  3. data/.gitignore +30 -0
  4. data/.rspec +3 -0
  5. data/.rspec_status +0 -0
  6. data/.rubocop.yml +5 -0
  7. data/.rubocop_todo.yml +250 -0
  8. data/.ruby-gemset +1 -0
  9. data/.ruby-version +1 -0
  10. data/CODE_OF_CONDUCT.md +84 -0
  11. data/Gemfile +36 -0
  12. data/LICENSE +674 -0
  13. data/README.md +72 -0
  14. data/Rakefile +19 -0
  15. data/bin/cryptum +72 -0
  16. data/bin/cryptum-forecast +199 -0
  17. data/bin/cryptum-repl +73 -0
  18. data/bin/cryptum_autoinc_version +38 -0
  19. data/build_cryptum_gem.sh +52 -0
  20. data/cryptum.gemspec +50 -0
  21. data/cryptum_container.sh +1 -0
  22. data/docker/cryptum.json +60 -0
  23. data/docker/cryptum_container.sh +59 -0
  24. data/docker/packer_secrets.json.EXAMPLE +7 -0
  25. data/docker/provisioners/cryptum.sh +11 -0
  26. data/docker/provisioners/docker_bashrc.sh +2 -0
  27. data/docker/provisioners/docker_rvm.sh +22 -0
  28. data/docker/provisioners/init_image.sh +28 -0
  29. data/docker/provisioners/post_install.sh +6 -0
  30. data/docker/provisioners/ruby.sh +16 -0
  31. data/docker/provisioners/upload_globals.sh +49 -0
  32. data/etc/bot_confs/.gitkeep +0 -0
  33. data/etc/bot_confs/BOT_CONF.TEMPLATE +10 -0
  34. data/etc/coinbase_pro.yaml.EXAMPLE +8 -0
  35. data/git_commit.sh +22 -0
  36. data/lib/cryptum/api.rb +693 -0
  37. data/lib/cryptum/bot_conf.rb +76 -0
  38. data/lib/cryptum/event/buy.rb +144 -0
  39. data/lib/cryptum/event/cancel.rb +49 -0
  40. data/lib/cryptum/event/history.rb +64 -0
  41. data/lib/cryptum/event/key_press.rb +64 -0
  42. data/lib/cryptum/event/sell.rb +120 -0
  43. data/lib/cryptum/event.rb +168 -0
  44. data/lib/cryptum/log.rb +34 -0
  45. data/lib/cryptum/matrix.rb +181 -0
  46. data/lib/cryptum/option/choice.rb +26 -0
  47. data/lib/cryptum/option.rb +161 -0
  48. data/lib/cryptum/order_book/generate.rb +111 -0
  49. data/lib/cryptum/order_book/indicator.rb +16 -0
  50. data/lib/cryptum/order_book/market_trend.rb +161 -0
  51. data/lib/cryptum/order_book/profit_margin.rb +55 -0
  52. data/lib/cryptum/order_book/weighted_avg.rb +157 -0
  53. data/lib/cryptum/order_book.rb +156 -0
  54. data/lib/cryptum/portfolio/balance.rb +123 -0
  55. data/lib/cryptum/portfolio.rb +15 -0
  56. data/lib/cryptum/ui/command.rb +274 -0
  57. data/lib/cryptum/ui/key_press_event.rb +22 -0
  58. data/lib/cryptum/ui/market_trend.rb +117 -0
  59. data/lib/cryptum/ui/order_execution.rb +478 -0
  60. data/lib/cryptum/ui/order_plan.rb +376 -0
  61. data/lib/cryptum/ui/order_timer.rb +119 -0
  62. data/lib/cryptum/ui/portfolio.rb +231 -0
  63. data/lib/cryptum/ui/signal_engine.rb +122 -0
  64. data/lib/cryptum/ui/terminal_window.rb +95 -0
  65. data/lib/cryptum/ui/ticker.rb +317 -0
  66. data/lib/cryptum/ui.rb +306 -0
  67. data/lib/cryptum/version.rb +5 -0
  68. data/lib/cryptum/web_sock/coinbase.rb +94 -0
  69. data/lib/cryptum/web_sock/event_machine.rb +182 -0
  70. data/lib/cryptum/web_sock.rb +16 -0
  71. data/lib/cryptum.rb +183 -0
  72. data/order_books/.gitkeep +0 -0
  73. data/reinstall_cryptum_gemset.sh +29 -0
  74. data/spec/lib/cryptum/api_spec.rb +10 -0
  75. data/spec/lib/cryptum/event_spec.rb +10 -0
  76. data/spec/lib/cryptum/log_spec.rb +10 -0
  77. data/spec/lib/cryptum/option_spec.rb +10 -0
  78. data/spec/lib/cryptum/order_book/generate_spec.rb +10 -0
  79. data/spec/lib/cryptum/order_book/market_trend_spec.rb +10 -0
  80. data/spec/lib/cryptum/order_book_spec.rb +10 -0
  81. data/spec/lib/cryptum/ui/command_spec.rb +10 -0
  82. data/spec/lib/cryptum/ui/ticker_spec.rb +10 -0
  83. data/spec/lib/cryptum/ui_spec.rb +10 -0
  84. data/spec/lib/cryptum/web_sock_spec.rb +10 -0
  85. data/spec/lib/cryptum_spec.rb +10 -0
  86. data/spec/spec_helper.rb +3 -0
  87. data/upgrade_Gemfile_gems.sh +20 -0
  88. data/upgrade_cryptum.sh +13 -0
  89. data/upgrade_gem.sh +4 -0
  90. data/upgrade_ruby.sh +46 -0
  91. metadata +472 -0
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+
5
+ module Cryptum
6
+ # This plugin is used to instantiate a Cryptum logger with a custom message format
7
+ module Log
8
+ # Supported Method Parameters::
9
+ # Cryptum::Log.create(
10
+ # )
11
+ public_class_method def self.create
12
+ logger = Logger.new($stdout)
13
+ logger.level = Logger::INFO
14
+ logger.datetime_format = '%Y-%m-%d %H:%M:%S'
15
+
16
+ logger.formatter = proc do |severity, _datetime, _progname, msg|
17
+ # TODO: Include datetime & progname vars
18
+ "[#{severity}] #{msg}\n"
19
+ end
20
+
21
+ logger
22
+ rescue StandardError => e
23
+ raise e
24
+ end
25
+
26
+ # Display Usage for this Module
27
+
28
+ public_class_method def self.help
29
+ puts "USAGE:
30
+ logger = #{self}.create()
31
+ "
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,181 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+
5
+ module Cryptum
6
+ # This plugin is used for my own entertainment
7
+ module Matrix
8
+ # Supported Method Parameters::
9
+ # Cryptum::Matrix.generate(
10
+ # cols: cols
11
+ # )
12
+
13
+ public_class_method def self.generate(opts = {})
14
+ # Need to divide by two for wide characters
15
+ cols = opts[:cols].to_i
16
+
17
+ matrix_arr = [
18
+ 0x30a0.chr('UTF-8'),
19
+ 0x30a1.chr('UTF-8'),
20
+ 0x30a2.chr('UTF-8'),
21
+ 0x30a3.chr('UTF-8'),
22
+ 0x30a4.chr('UTF-8'),
23
+ 0x30a5.chr('UTF-8'),
24
+ 0x30a6.chr('UTF-8'),
25
+ 0x30a7.chr('UTF-8'),
26
+ 0x30a8.chr('UTF-8'),
27
+ 0x30a9.chr('UTF-8'),
28
+ 0x30aa.chr('UTF-8'),
29
+ 0x30ab.chr('UTF-8'),
30
+ 0x30ac.chr('UTF-8'),
31
+ 0x30ad.chr('UTF-8'),
32
+ 0x30ae.chr('UTF-8'),
33
+ 0x30af.chr('UTF-8'),
34
+ 0x30b0.chr('UTF-8'),
35
+ 0x30b1.chr('UTF-8'),
36
+ 0x30b2.chr('UTF-8'),
37
+ 0x30b3.chr('UTF-8'),
38
+ 0x30b4.chr('UTF-8'),
39
+ 0x30b5.chr('UTF-8'),
40
+ 0x30b6.chr('UTF-8'),
41
+ 0x30b7.chr('UTF-8'),
42
+ 0x30b8.chr('UTF-8'),
43
+ 0x30b9.chr('UTF-8'),
44
+ 0x30ba.chr('UTF-8'),
45
+ 0x30bb.chr('UTF-8'),
46
+ 0x30bc.chr('UTF-8'),
47
+ 0x30bd.chr('UTF-8'),
48
+ 0x30be.chr('UTF-8'),
49
+ 0x30bf.chr('UTF-8'),
50
+ 0x30c0.chr('UTF-8'),
51
+ 0x30c1.chr('UTF-8'),
52
+ 0x30c2.chr('UTF-8'),
53
+ 0x30c3.chr('UTF-8'),
54
+ 0x30c4.chr('UTF-8'),
55
+ 0x30c5.chr('UTF-8'),
56
+ 0x30c6.chr('UTF-8'),
57
+ 0x30c7.chr('UTF-8'),
58
+ 0x30c8.chr('UTF-8'),
59
+ 0x30c9.chr('UTF-8'),
60
+ 0x30ca.chr('UTF-8'),
61
+ 0x30cb.chr('UTF-8'),
62
+ 0x30cc.chr('UTF-8'),
63
+ 0x30cd.chr('UTF-8'),
64
+ 0x30ce.chr('UTF-8'),
65
+ 0x30cf.chr('UTF-8'),
66
+ 0x30d0.chr('UTF-8'),
67
+ 0x30d1.chr('UTF-8'),
68
+ 0x30d2.chr('UTF-8'),
69
+ 0x30d3.chr('UTF-8'),
70
+ 0x30d4.chr('UTF-8'),
71
+ 0x30d5.chr('UTF-8'),
72
+ 0x30d6.chr('UTF-8'),
73
+ 0x30d7.chr('UTF-8'),
74
+ 0x30d8.chr('UTF-8'),
75
+ 0x30d9.chr('UTF-8'),
76
+ 0x30da.chr('UTF-8'),
77
+ 0x30db.chr('UTF-8'),
78
+ 0x30dc.chr('UTF-8'),
79
+ 0x30dd.chr('UTF-8'),
80
+ 0x30de.chr('UTF-8'),
81
+ 0x30df.chr('UTF-8'),
82
+ 0x30e0.chr('UTF-8'),
83
+ 0x30e1.chr('UTF-8'),
84
+ 0x30e2.chr('UTF-8'),
85
+ 0x30e3.chr('UTF-8'),
86
+ 0x30e4.chr('UTF-8'),
87
+ 0x30e5.chr('UTF-8'),
88
+ 0x30e6.chr('UTF-8'),
89
+ 0x30e7.chr('UTF-8'),
90
+ 0x30e8.chr('UTF-8'),
91
+ 0x30e9.chr('UTF-8'),
92
+ 0x30ea.chr('UTF-8'),
93
+ 0x30eb.chr('UTF-8'),
94
+ 0x30ec.chr('UTF-8'),
95
+ 0x30ed.chr('UTF-8'),
96
+ 0x30ee.chr('UTF-8'),
97
+ 0x30ef.chr('UTF-8'),
98
+ 0x30f0.chr('UTF-8'),
99
+ 0x30f1.chr('UTF-8'),
100
+ 0x30f2.chr('UTF-8'),
101
+ 0x30f3.chr('UTF-8'),
102
+ 0x30f4.chr('UTF-8'),
103
+ 0x30f5.chr('UTF-8'),
104
+ 0x30f6.chr('UTF-8'),
105
+ 0x30f7.chr('UTF-8'),
106
+ 0x30f8.chr('UTF-8'),
107
+ 0x30f9.chr('UTF-8'),
108
+ 0x30fa.chr('UTF-8'),
109
+ 0x30fb.chr('UTF-8'),
110
+ 0x30fc.chr('UTF-8'),
111
+ 0x30fd.chr('UTF-8'),
112
+ 0x30fe.chr('UTF-8'),
113
+ '0 ',
114
+ '1 ',
115
+ '2 ',
116
+ '3 ',
117
+ '4 ',
118
+ '5 ',
119
+ '6 ',
120
+ '7 ',
121
+ '8 ',
122
+ '9 ',
123
+ 'A ',
124
+ 'c ',
125
+ 'R ',
126
+ 'y ',
127
+ 'P ',
128
+ 't ',
129
+ 'U ',
130
+ 'm ',
131
+ 'x ',
132
+ 'Z ',
133
+ ': ',
134
+ '{ ',
135
+ '[ ',
136
+ '} ',
137
+ '] ',
138
+ '| ',
139
+ '` ',
140
+ '~ ',
141
+ '! ',
142
+ '@ ',
143
+ '# ',
144
+ '$ ',
145
+ '% ',
146
+ '^ ',
147
+ '& ',
148
+ '* ',
149
+ '( ',
150
+ ') ',
151
+ '_ ',
152
+ '- ',
153
+ '= ',
154
+ '+ ',
155
+ '> ',
156
+ '< ',
157
+ '. ',
158
+ ', ',
159
+ ]
160
+
161
+ last_index = matrix_arr.length - 1
162
+
163
+ matrix_row = ''
164
+ most_cols = cols - 1
165
+ most_cols.times.each do
166
+ matrix_row += "#{matrix_arr[Random.rand(0..last_index)]} "
167
+ end
168
+ matrix_row += matrix_arr[Random.rand(0..last_index)]
169
+ rescue StandardError => e
170
+ raise e
171
+ end
172
+
173
+ # Display Usage for this Module
174
+
175
+ public_class_method def self.help
176
+ puts "USAGE:
177
+ logger = #{self}.create()
178
+ "
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cryptum
4
+ # This plugin is used to Cancel Open Limit Orders
5
+ module Option
6
+ # Keep Last Event History When Terminal Resize Occuers
7
+ class Choice
8
+ attr_accessor :autotrade,
9
+ :driver_name,
10
+ :list_products,
11
+ :proxy,
12
+ :repo_root,
13
+ :sandbox,
14
+ :symbol,
15
+ :market_trend_reset
16
+
17
+ rescue Interrupt
18
+ # Exit Gracefully if CTRL+C is Pressed During Session
19
+ Cryptum.exit_gracefully(which_self: self)
20
+ rescue StandardError => e
21
+ # Produce a Stacktrace for anything else
22
+ Curses.close_screen
23
+ raise e
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,161 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'optparse'
4
+ require 'yaml'
5
+
6
+ module Cryptum
7
+ # Cryptum::UI Module used for Presenting the
8
+ # Cryptum Curses Interface
9
+ module Option
10
+ autoload :Choice, 'cryptum/option/choice'
11
+
12
+ # Options for cryptum Driver
13
+ public_class_method def self.parser(opts = {})
14
+ option_choice = Option::Choice.new
15
+ option_choice.driver_name = opts[:driver_name]
16
+
17
+ OptionParser.new do |options|
18
+ options.banner = "USAGE: #{option_choice.driver_name} [opts]"
19
+
20
+ options.on(
21
+ '-sSYMBOL',
22
+ '--symbol=SYMBOL',
23
+ '<Required - Crypto Symbol.(e.g. btc-usd, eth-usd, etc.)'
24
+ ) { |s| option_choice.symbol = s.to_s.gsub('-', '_').downcase.to_sym }
25
+
26
+ options.on(
27
+ '-A',
28
+ '--[no-]autotrade',
29
+ '<Optional - Automatically Buy and Sell Crypto>'
30
+ ) { |a| option_choice.autotrade = a }
31
+
32
+ options.on(
33
+ '-l',
34
+ '--[no-]list-products',
35
+ '<Optional - List Supported Crypto Currency Products and Exit>'
36
+ ) { |l| option_choice.list_products = l }
37
+
38
+ options.on(
39
+ '-pPROXY',
40
+ '--proxy=PROXY',
41
+ '<Optional - HTTP Proxy e.g. "http://127.0.0.1:8080">'
42
+ ) { |p| option_choice.proxy = p }
43
+
44
+ options.on(
45
+ '-rPATH',
46
+ '--repo-root=PATH',
47
+ '<Optional - Directory of Cloned Repo (Defaults to ~/cryptum)>'
48
+ ) { |r| option_choice.repo_root = r }
49
+
50
+ options.on(
51
+ '-S',
52
+ '--[no-]sandbox',
53
+ '<Optional - Use Coinbase Sandbox Environment for Testing Ideas>'
54
+ ) { |n| option_choice.sandbox = n }
55
+
56
+ options.on(
57
+ '-tSECONDS',
58
+ '--time-between-orders=SECONDS',
59
+ '<Optional - Seconds Between Market Trend Reset (Default 60)>'
60
+ ) { |t| option_choice.market_trend_reset = t }
61
+ end.parse!
62
+
63
+ input_validation(option_choice: option_choice)
64
+
65
+ option_choice
66
+ rescue OptionParser::InvalidOption => e
67
+ # Print Usage if unsupported flags are passed
68
+ puts "ERROR: #{e.message}\n\n"
69
+ puts `#{option_choice.driver_name} --help`
70
+ exit 1
71
+ rescue StandardError => e
72
+ raise e
73
+ end
74
+
75
+ # Validate Options for cryptum Driver
76
+ public_class_method def self.input_validation(opts = {})
77
+ option_choice = opts[:option_choice]
78
+
79
+ # Conditions to display cryptum usage
80
+ if option_choice.symbol.nil? && option_choice.list_products.nil?
81
+ usage = true
82
+ reason = :symbol
83
+ end
84
+
85
+ option_choice.repo_root = "#{Dir.home}/cryptum" if option_choice.repo_root.nil?
86
+
87
+ unless Dir.exist?(option_choice.repo_root)
88
+ usage = true
89
+ reason = :repo_root
90
+ end
91
+
92
+ option_choice.market_trend_reset = 60 if option_choice.market_trend_reset.to_i.zero?
93
+ unless option_choice.market_trend_reset.to_f.positive?
94
+ usage = true
95
+ reason = :market_trend_reset
96
+ end
97
+
98
+ if usage
99
+ case reason
100
+ when :symbol
101
+ puts "ERROR: --symbol Flag is Required.\n\n"
102
+ when :repo_root
103
+ puts "ERROR: #{option_choice.repo_root} does not exist.\n\n"
104
+ when :market_trend_reset
105
+ puts "ERROR: #{option_choice.market_trend_reset} Must be a positive floating point number.\n\n"
106
+ end
107
+
108
+ puts `#{option_choice.driver_name} --help`
109
+ exit 1
110
+ end
111
+ rescue StandardError => e
112
+ raise e
113
+ end
114
+
115
+ # List Supported Cryptum Products and Exit
116
+ public_class_method def self.list_products_and_exit(opts = {})
117
+ option_choice = opts[:option_choice]
118
+ env = opts[:env]
119
+
120
+ puts `#{option_choice.driver_name} --help`
121
+ puts "\n#{option_choice.driver_name} Supports the Following Products:"
122
+ products = Cryptum::API.get_products(
123
+ option_choice: option_choice,
124
+ env: env
125
+ )
126
+
127
+ products.map do |product|
128
+ puts product[:id].downcase
129
+ end
130
+
131
+ exit 0
132
+ rescue StandardError => e
133
+ raise e
134
+ end
135
+
136
+ # Initialize Cryptum Session Environment
137
+ public_class_method def self.get_env(opts = {})
138
+ option_choice = opts[:option_choice]
139
+
140
+ yaml_conf_file = "#{option_choice.repo_root}/etc/coinbase_pro.yaml"
141
+ yaml_conf = YAML.load_file(
142
+ yaml_conf_file,
143
+ symbolize_names: true
144
+ )
145
+
146
+ env = yaml_conf[:prod]
147
+ env[:env] = :prod
148
+ env = yaml_conf[:sandbox] if option_choice.sandbox
149
+ env[:env] = :sandbox if option_choice.sandbox
150
+
151
+ env
152
+ rescue StandardError => e
153
+ raise e
154
+ end
155
+
156
+ # Display a List of Every UI Module
157
+ public_class_method def self.help
158
+ constants.sort
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'io/console'
4
+ require 'json'
5
+ require 'logger'
6
+ require 'tty-spinner'
7
+
8
+ module Cryptum
9
+ # This module is used to define the Order Book Data Structure
10
+ module OrderBook
11
+ module Generate
12
+ # Supported Method Parameters::
13
+ # Cryptum::OrderBook.generate(
14
+ # symbol: 'required - target symbol (e.g. btc-usd)',
15
+ # this_product: 'required - this_product',
16
+ # )
17
+
18
+ public_class_method def self.new_order_book(opts = {})
19
+ start_time = opts[:start_time]
20
+ option_choice = opts[:option_choice]
21
+ env = opts[:env]
22
+ bot_conf = opts[:bot_conf]
23
+
24
+ order_book_file = "#{option_choice.repo_root}/order_books/#{option_choice.symbol}.ORDER_BOOK.json"
25
+
26
+ order_history_meta = []
27
+ if File.exist?(order_book_file)
28
+ last_order_book = Cryptum::OrderBook.analyze(
29
+ order_book_file: order_book_file,
30
+ option_choice: option_choice
31
+ )
32
+ order_history_meta = last_order_book[:order_history_meta] unless last_order_book[:order_history_meta].empty?
33
+ end
34
+
35
+ # Only need to retrieve a product list once / session.
36
+ products = Cryptum::API.get_products(
37
+ option_choice: option_choice,
38
+ env: env
39
+ )
40
+ this_product_arr = products.select do |product|
41
+ product if product[:id] == option_choice.symbol.to_s.gsub('_', '-').upcase
42
+ end
43
+ this_product = this_product_arr.first
44
+
45
+ order_book = {
46
+ path: order_book_file,
47
+ symbol: option_choice.symbol,
48
+ open_24h: 0.00,
49
+ high_24h: 0.00,
50
+ low_24h: 0.00,
51
+ volume_24h: 0.00,
52
+ ticker_price: 0.00,
53
+ ticker_price_second_to_last: 0.00,
54
+ ticker_price_third_to_last: 0.00,
55
+ highest_pie_in_sky_buy_percent: 0.00,
56
+ highest_pie_in_sky_sell_percent: 0.00,
57
+ sequence: -1,
58
+ this_product: this_product,
59
+ portfolio: [],
60
+ fiat_portfolio: [],
61
+ fees: [],
62
+ order_plan: [],
63
+ last_trend_reset: Time.now.strftime('%Y-%m-%d %H:%M:%S.%N%z'),
64
+ last_order_exec: Time.now.strftime('%Y-%m-%d %H:%M:%S.%N%z'),
65
+ market_trend: {
66
+ buy: 0,
67
+ buy_start: '--',
68
+ buy_end: '--',
69
+ sell: 0,
70
+ sell_start: '--',
71
+ sell_end: '--'
72
+ },
73
+ order_history: [],
74
+ order_history_meta: order_history_meta
75
+ }
76
+
77
+ # Order History Retention ---------------------------------------#
78
+ # Instantiate Event History attr_accessible
79
+ # Object to Keep Track of Everything as Events
80
+ # are Parsed.
81
+ event_history = Cryptum::Event::History.new(
82
+ option_choice: option_choice,
83
+ start_time: start_time,
84
+ order_book: order_book
85
+ )
86
+
87
+ # Write order_book to file at session initiation
88
+ File.open(order_book_file, 'w') do |f|
89
+ f.puts order_book.to_json
90
+ end
91
+
92
+ event_history.order_book = order_book
93
+
94
+ event_history
95
+ rescue StandardError => e
96
+ raise e
97
+ end
98
+
99
+ # Display Usage for this Module
100
+
101
+ public_class_method def self.help
102
+ puts "USAGE:
103
+ order_book = #{self}.new_order_book(
104
+ symbol: 'required - target symbol (e.g. btc-usd)',
105
+ this_product: 'required - this_product'
106
+ )
107
+ "
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cryptum
4
+ # This plugin is used to calculate Exponential Moving Average
5
+ # Price of a Symbol over time.
6
+
7
+ module OrderBook
8
+ class Indicator
9
+ attr_accessor :market_trend,
10
+ :profit_margin,
11
+ :weighted_avg,
12
+ :action_signal,
13
+ :last_action_signal
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,161 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cryptum
4
+ # This plugin is used to indicate if the order trend
5
+
6
+ module OrderBook
7
+ module MarketTrend
8
+ # Supported Method Parameters::
9
+ # Cryptum::OrderBook::OrderTrend.status(
10
+ # )
11
+
12
+ public_class_method def self.status(opts = {})
13
+ event_history = opts[:event_history]
14
+ event = opts[:event]
15
+ indicator_status = opts[:indicator_status]
16
+
17
+ # Decent for debugging.
18
+ # puts event.inspect
19
+
20
+ if event_history.reset_market_trend
21
+ event_history.order_book[:market_trend][:buy] = 0
22
+ event_history.order_book[:market_trend][:sell] = 0
23
+ event_history.reset_market_trend = false
24
+ end
25
+
26
+ buy_or_sell = event[:changes].first[0].to_s.to_sym
27
+ case buy_or_sell
28
+ when :buy
29
+ event_history.order_book[:market_trend][:buy] += 1
30
+ when :sell
31
+ event_history.order_book[:market_trend][:sell] += 1
32
+ else
33
+ raise "UNKNOWN Value in event[:changes] => #{buy_or_sell}"
34
+ end
35
+
36
+ indicator_hash = {}
37
+ buy_total = event_history.order_book[:market_trend][:buy].to_i
38
+ indicator_hash[:buy] = buy_total
39
+
40
+ sell_total = event_history.order_book[:market_trend][:sell].to_i
41
+ indicator_hash[:sell] = sell_total
42
+
43
+ # order_difference = 0
44
+ if buy_total > sell_total
45
+ order_difference = buy_total - sell_total
46
+ indicator_hash[:color] = :green
47
+ indicator_hash[:ui] = "BUYS UP BY #{order_difference}"
48
+ indicator_hash[:status] = "B#{Cryptum.up_arrow}"
49
+ elsif buy_total < sell_total
50
+ order_difference = sell_total - buy_total
51
+ indicator_hash[:color] = :red
52
+ indicator_hash[:ui] = "SELLS UP BY #{order_difference}"
53
+ indicator_hash[:status] = "S#{Cryptum.up_arrow}"
54
+ else
55
+ # This Condition is Met When candle_buy_tot == candle_sell_tot
56
+ indicator_hash[:color] = :yellow
57
+ indicator_hash[:ui] = 'BUYS == SELLS'
58
+ indicator_hash[:status] = "F#{Cryptum.up_arrow}"
59
+ end
60
+
61
+ indicator_status.market_trend = indicator_hash
62
+ rescue StandardError => e
63
+ raise e
64
+ end
65
+
66
+ public_class_method def self.reset(opts = {})
67
+ # IT IS ABSOLUTELY CRITICAL THIS METHOD IS AS FAST AS POSSIBLE
68
+ # TO AVOID TICKER PRICE SYNCING ISSUES.
69
+ option_choice = opts[:option_choice]
70
+ terminal_win = opts[:terminal_win]
71
+ event_history = opts[:event_history]
72
+ bot_conf = opts[:bot_conf]
73
+
74
+ ai_enabled = bot_conf[:artifical_intelligence]
75
+ gross_tpm = bot_conf[:target_profit_margin_percent].to_f
76
+
77
+ # Only retain past 24 hours of
78
+ # order history meta for expired and sold
79
+ # keep open limit sell orders indefintely
80
+ before_twenty_four_hrs_ago = Time.now - 86400
81
+ order_history = event_history.order_book[:order_history]
82
+ order_history_meta = event_history.order_book[:order_history_meta]
83
+ order_history_meta.delete_if do |ohm|
84
+ next unless ohm[:done_at]
85
+ Time.parse(ohm[:done_at]) < before_twenty_four_hrs_ago &&
86
+ (
87
+ ohm[:color].to_sym == :black ||
88
+ ohm[:color].to_sym == :green ||
89
+ ohm[:color].to_sym == :cyan ||
90
+ ohm[:color].to_sym == :red
91
+ )
92
+ end
93
+
94
+ # Only keep order history meta for those
95
+ # hashes that exist in the last order history
96
+ # response
97
+ order_history_meta.keep_if do |ohm|
98
+ order_history.find do |oh|
99
+ oh[:id] == ohm[:buy_order_id] ||
100
+ oh[:id] == ohm[:sell_order_id] ||
101
+ ohm[:color].to_sym == :black ||
102
+ ohm[:color].to_sym == :green
103
+ end
104
+ end
105
+ event_history.order_book[:order_history_meta] = order_history_meta
106
+
107
+ # Refactor TPM to be 0.01 > than fee tier,
108
+ # particularly as fee tier goes up or down
109
+ fees = event_history.order_book[:fees]
110
+ taker_rate = 0.5
111
+ taker_rate = fees[:taker_fee_rate].to_f unless fees.empty?
112
+ taker_fee = format('%0.2f', taker_rate * 100)
113
+ default_net_tpm = 3
114
+
115
+ if ai_enabled
116
+ low_24h = event_history.order_book[:low_24h].to_f
117
+ high_24h = event_history.order_book[:high_24h].to_f
118
+ ai_net_tpm = (1 - (low_24h / high_24h)) * 100
119
+ default_net_tpm = ai_net_tpm if ai_net_tpm > default_net_tpm
120
+ end
121
+
122
+ min_gross_tpm = format(
123
+ '%0.2f',
124
+ (taker_fee.to_f * 2) + default_net_tpm
125
+ )
126
+
127
+ if ai_enabled && min_gross_tpm != gross_tpm.to_s
128
+ bot_conf[:target_profit_margin_percent] = min_gross_tpm.to_f
129
+ Cryptum::BotConf.update(
130
+ option_choice: option_choice,
131
+ bot_conf: bot_conf,
132
+ key: :target_profit_margin_percent,
133
+ value: min_gross_tpm.to_f
134
+ )
135
+ end
136
+
137
+ # Reload Bot Conf (i.e. Risk Allocation)
138
+ terminal_win.key_press_event.key_r = true
139
+
140
+ # Reset Market Trend Counter
141
+ event_history.reset_market_trend = true
142
+ event_history.order_book[:last_trend_reset] = Time.now.strftime(
143
+ '%Y-%m-%d %H:%M:%S.%N%z'
144
+ )
145
+
146
+ # Write Order Book to Disk
147
+ terminal_win.key_press_event.key_w = true
148
+ rescue StandardError => e
149
+ raise e
150
+ end
151
+
152
+ # Display Usage for this Module
153
+
154
+ public_class_method def self.help
155
+ puts "USAGE:
156
+ order_trend_indicator_hash = #{self}.status
157
+ "
158
+ end
159
+ end
160
+ end
161
+ end