gera 0.3.4

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 (164) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +674 -0
  3. data/README.md +67 -0
  4. data/Rakefile +43 -0
  5. data/app/assets/config/gera_manifest.js +2 -0
  6. data/app/assets/javascripts/gera/application.js +14 -0
  7. data/app/assets/stylesheets/gera/application.css +15 -0
  8. data/app/authorizers/gera/application_authorizer.rb +6 -0
  9. data/app/authorizers/gera/currency_rate_authorizer.rb +9 -0
  10. data/app/authorizers/gera/currency_rate_mode_authorizer.rb +7 -0
  11. data/app/authorizers/gera/direction_rate_authorizer.rb +8 -0
  12. data/app/authorizers/gera/exchange_rate_authorizer.rb +8 -0
  13. data/app/authorizers/gera/payment_system_authorizer.rb +13 -0
  14. data/app/authorizers/gera/rate_source_authorizer.rb +7 -0
  15. data/app/controllers/gera/application_controller.rb +30 -0
  16. data/app/controllers/gera/currencies_controller.rb +8 -0
  17. data/app/controllers/gera/currency_rate_history_intervals_controller.rb +53 -0
  18. data/app/controllers/gera/currency_rate_mode_snapshots_controller.rb +83 -0
  19. data/app/controllers/gera/currency_rate_modes_controller.rb +60 -0
  20. data/app/controllers/gera/currency_rates_controller.rb +70 -0
  21. data/app/controllers/gera/direction_rate_history_intervals_controller.rb +62 -0
  22. data/app/controllers/gera/direction_rates_controller.rb +28 -0
  23. data/app/controllers/gera/exchange_rates_controller.rb +25 -0
  24. data/app/controllers/gera/external_rate_snapshots_controller.rb +35 -0
  25. data/app/controllers/gera/external_rates_controller.rb +28 -0
  26. data/app/controllers/gera/payment_systems_controller.rb +78 -0
  27. data/app/controllers/gera/rate_sources_controller.rb +8 -0
  28. data/app/decorators/gera/currency_rate_decorator.rb +148 -0
  29. data/app/decorators/gera/direction_rate_decorator.rb +47 -0
  30. data/app/decorators/gera/payment_system_decorator.rb +19 -0
  31. data/app/helpers/gera/application_helper.rb +110 -0
  32. data/app/helpers/gera/currency_rate_helper.rb +19 -0
  33. data/app/helpers/gera/currency_rate_modes_helper.rb +73 -0
  34. data/app/helpers/gera/direction_rate_helper.rb +36 -0
  35. data/app/helpers/gera/exchange_rates_helper.rb +21 -0
  36. data/app/jobs/gera/application_job.rb +6 -0
  37. data/app/models/concerns/gera/currency_pair_generator.rb +14 -0
  38. data/app/models/concerns/gera/currency_pair_support.rb +42 -0
  39. data/app/models/concerns/gera/currency_rate_mode_builder_support.rb +35 -0
  40. data/app/models/concerns/gera/direction_support.rb +14 -0
  41. data/app/models/concerns/gera/history_interval_concern.rb +46 -0
  42. data/app/models/gera/application_record.rb +8 -0
  43. data/app/models/gera/cbr_external_rate.rb +13 -0
  44. data/app/models/gera/cross_rate_mode.rb +13 -0
  45. data/app/models/gera/currency_rate.rb +78 -0
  46. data/app/models/gera/currency_rate_history_interval.rb +25 -0
  47. data/app/models/gera/currency_rate_history_interval_filter.rb +30 -0
  48. data/app/models/gera/currency_rate_mode.rb +20 -0
  49. data/app/models/gera/currency_rate_mode_snapshot.rb +26 -0
  50. data/app/models/gera/currency_rate_snapshot.rb +12 -0
  51. data/app/models/gera/direction.rb +44 -0
  52. data/app/models/gera/direction_rate.rb +136 -0
  53. data/app/models/gera/direction_rate_history_interval.rb +33 -0
  54. data/app/models/gera/direction_rate_history_interval_filter.rb +30 -0
  55. data/app/models/gera/direction_rate_snapshot.rb +7 -0
  56. data/app/models/gera/exchange_rate.rb +130 -0
  57. data/app/models/gera/external_rate.rb +38 -0
  58. data/app/models/gera/external_rate_snapshot.rb +20 -0
  59. data/app/models/gera/payment_system.rb +98 -0
  60. data/app/models/gera/rate_source.rb +77 -0
  61. data/app/models/gera/rate_source_auto.rb +25 -0
  62. data/app/models/gera/rate_source_bitfinex.rb +9 -0
  63. data/app/models/gera/rate_source_cbr.rb +13 -0
  64. data/app/models/gera/rate_source_cbr_avg.rb +6 -0
  65. data/app/models/gera/rate_source_exmo.rb +9 -0
  66. data/app/models/gera/rate_source_manual.rb +13 -0
  67. data/app/views/application/_exchange_rate_monitor.slim +1 -0
  68. data/app/views/gera/application/_attributes_table.slim +5 -0
  69. data/app/views/gera/application/_currencies_table.slim +18 -0
  70. data/app/views/gera/application/_currency_rate_mode_snapshots.slim +6 -0
  71. data/app/views/gera/application/_direction_rate.slim +6 -0
  72. data/app/views/gera/application/_directions_submenu.slim +11 -0
  73. data/app/views/gera/application/_directions_table.slim +15 -0
  74. data/app/views/gera/application/_exchange_rate.slim +6 -0
  75. data/app/views/gera/application/_external_rate.slim +6 -0
  76. data/app/views/gera/application/_header.slim +12 -0
  77. data/app/views/gera/application/_table.slim +28 -0
  78. data/app/views/gera/application/_topnav.slim +21 -0
  79. data/app/views/gera/application/direction_details.slim +75 -0
  80. data/app/views/gera/application/page.html.slim +1 -0
  81. data/app/views/gera/currencies/index.html.slim +20 -0
  82. data/app/views/gera/currency_rate_history_intervals/_filter.slim +11 -0
  83. data/app/views/gera/currency_rate_history_intervals/index.slim +39 -0
  84. data/app/views/gera/currency_rate_mode_snapshots/_snapshot.slim +20 -0
  85. data/app/views/gera/currency_rate_mode_snapshots/edit.html.slim +30 -0
  86. data/app/views/gera/currency_rate_modes/_cross_rate_mode_fields.slim +21 -0
  87. data/app/views/gera/currency_rate_modes/_currency_rate_mode.slim +7 -0
  88. data/app/views/gera/currency_rate_modes/_form.slim +29 -0
  89. data/app/views/gera/currency_rate_modes/edit.slim +11 -0
  90. data/app/views/gera/currency_rate_modes/index.html.slim +12 -0
  91. data/app/views/gera/currency_rates/_currency_rate.slim +22 -0
  92. data/app/views/gera/currency_rates/_currency_rate_humanized.slim +35 -0
  93. data/app/views/gera/currency_rates/diff.slim +12 -0
  94. data/app/views/gera/currency_rates/index.slim +27 -0
  95. data/app/views/gera/currency_rates/modes.slim +14 -0
  96. data/app/views/gera/currency_rates/show.slim +4 -0
  97. data/app/views/gera/direction_rate_history_intervals/_filter.slim +11 -0
  98. data/app/views/gera/direction_rate_history_intervals/index.slim +41 -0
  99. data/app/views/gera/direction_rates/_direction_rate_example_calculator.slim +28 -0
  100. data/app/views/gera/direction_rates/diff.slim +19 -0
  101. data/app/views/gera/direction_rates/index.slim +26 -0
  102. data/app/views/gera/direction_rates/legacy.html.slim +5 -0
  103. data/app/views/gera/direction_rates/minimals.slim +5 -0
  104. data/app/views/gera/direction_rates/show.slim +14 -0
  105. data/app/views/gera/external_rate_snapshots/index.slim +25 -0
  106. data/app/views/gera/external_rate_snapshots/show.slim +22 -0
  107. data/app/views/gera/external_rates/_flat_list.slim +22 -0
  108. data/app/views/gera/external_rates/index.html.slim +28 -0
  109. data/app/views/gera/external_rates/show.slim +1 -0
  110. data/app/views/gera/payment_systems/_fields.slim +4 -0
  111. data/app/views/gera/payment_systems/_fields_table.slim +18 -0
  112. data/app/views/gera/payment_systems/_form.slim +9 -0
  113. data/app/views/gera/payment_systems/edit.slim +7 -0
  114. data/app/views/gera/payment_systems/index.slim +3 -0
  115. data/app/views/gera/payment_systems/new.slim +9 -0
  116. data/app/views/gera/payment_systems/show.slim +1 -0
  117. data/app/views/gera/rate_sources/_list.slim +13 -0
  118. data/app/views/gera/rate_sources/_rate_source.slim +23 -0
  119. data/app/views/gera/rate_sources/index.html.slim +6 -0
  120. data/app/views/layouts/application.html.slim +15 -0
  121. data/app/views/layouts/gera/blank.html.slim +10 -0
  122. data/app/workers/concerns/gera/rates_worker.rb +87 -0
  123. data/app/workers/gera/bitfinex_rates_worker.rb +65 -0
  124. data/app/workers/gera/cbr_avg_rates_worker.rb +42 -0
  125. data/app/workers/gera/cbr_rates_worker.rb +192 -0
  126. data/app/workers/gera/create_history_intervals_worker.rb +38 -0
  127. data/app/workers/gera/currency_rates_worker.rb +64 -0
  128. data/app/workers/gera/directions_rates_worker.rb +51 -0
  129. data/app/workers/gera/exmo_rates_worker.rb +62 -0
  130. data/app/workers/gera/purge_currency_rates_worker.rb +21 -0
  131. data/app/workers/gera/purge_direction_rates_worker.rb +30 -0
  132. data/config/currencies.yml +559 -0
  133. data/config/routes.rb +30 -0
  134. data/db/migrate/20180912130000_setup.rb +228 -0
  135. data/db/migrate/20190314114844_add_is_available_to_payment_systems.rb +7 -0
  136. data/db/migrate/20190315113046_add_icon_url_to_payment_systems.rb +7 -0
  137. data/db/migrate/20190315132137_add_commission_to_payment_systems.rb +7 -0
  138. data/lib/banks/currency_exchange.rb +12 -0
  139. data/lib/builders/currency_rate_auto_builder.rb +38 -0
  140. data/lib/builders/currency_rate_builder.rb +60 -0
  141. data/lib/builders/currency_rate_cross_builder.rb +70 -0
  142. data/lib/builders/currency_rate_direct_builder.rb +26 -0
  143. data/lib/gera.rb +32 -0
  144. data/lib/gera/bitfinex_fetcher.rb +36 -0
  145. data/lib/gera/configuration.rb +47 -0
  146. data/lib/gera/currencies_purger.rb +28 -0
  147. data/lib/gera/currency_pair.rb +93 -0
  148. data/lib/gera/engine.rb +14 -0
  149. data/lib/gera/mathematic.rb +156 -0
  150. data/lib/gera/money_support.rb +89 -0
  151. data/lib/gera/numeric.rb +25 -0
  152. data/lib/gera/railtie.rb +8 -0
  153. data/lib/gera/rate.rb +22 -0
  154. data/lib/gera/rate_from_multiplicator.rb +54 -0
  155. data/lib/gera/repositories/currency_rate_modes_repository.rb +37 -0
  156. data/lib/gera/repositories/currency_rates_repository.rb +25 -0
  157. data/lib/gera/repositories/direction_rates_repository.rb +46 -0
  158. data/lib/gera/repositories/exchange_rates_repository.rb +23 -0
  159. data/lib/gera/repositories/payment_systems_repository.rb +21 -0
  160. data/lib/gera/repositories/universe.rb +47 -0
  161. data/lib/gera/version.rb +3 -0
  162. data/lib/tasks/auto_generate_diagram.rake +6 -0
  163. data/lib/tasks/gera_tasks.rake +4 -0
  164. metadata +765 -0
@@ -0,0 +1 @@
1
+ = render 'external_rate', external_rate: external_rate
@@ -0,0 +1,4 @@
1
+ h2 Поля в заявках
2
+
3
+ = render 'fields_table', title: 'Входящие поля', direction: :income, scope: payment_system.income_form_fields, payment_system: payment_system
4
+ = render 'fields_table', title: 'Иходящие поля', direction: :outcome, scope: payment_system.outcome_form_fields, payment_system: payment_system
@@ -0,0 +1,18 @@
1
+ - columns = %i(key i18n_key title checkbox_title is_required field_type actions)
2
+ h3= title
3
+
4
+ = render 'table',
5
+ scope: scope,
6
+ decorator_class: PaymentSystemFormFieldDecorator,
7
+ parent: payment_system,
8
+ columns: columns,
9
+ allow_edit: true
10
+
11
+ - if current_user.has_permission?(payment_system, :update)
12
+ - if unused_payment_system_form_fields_collection(payment_system, direction).any?
13
+ = link_to 'Добавить поле',
14
+ new_payment_system_payment_system_form_field_path(payment_system, payment_system_form_field: { direction: direction }),
15
+ class: 'btn btn-sm btn-success'
16
+ - else
17
+ .alert.alert-info Поля всех видов добавлены
18
+ hr
@@ -0,0 +1,9 @@
1
+ = simple_form_for payment_system, wrapper_mappings: { boolean: :vertical_boolean } do |f|
2
+ .row
3
+ - columns.each do |column|
4
+ .col-md-3
5
+ = smart_input f, column
6
+ hr
7
+ .row
8
+ .col-md-12
9
+ = f.button :submit, class: 'btn btn-lg btn-primary'
@@ -0,0 +1,7 @@
1
+ = link_to '← назад в список платежных систем'.html_safe, payment_systems_path
2
+ .page-header
3
+ h2 Редактируем платежную систему #{ps_icon payment_system} ##{payment_system.id}
4
+
5
+ .row
6
+ .col-md-9
7
+ = render 'form', payment_system: payment_system, columns: columns
@@ -0,0 +1,3 @@
1
+ = render 'table', scope: payment_systems, decorator_class: Gera.payment_system_decorator, columns: columns, allow_edit: true
2
+ hr
3
+ = link_to 'Добавить новую ПС', new_payment_system_path, class: 'btn btn-success' if current_user.has_permission?(Gera::PaymentSystem, :create)
@@ -0,0 +1,9 @@
1
+ = link_to '← назад в список платежных систем'.html_safe, payment_systems_path
2
+ .page-header
3
+ h2 Создаем новую платежную ситсему
4
+
5
+ .row
6
+ .col-md-6
7
+ = render 'form', payment_system: payment_system, columns: columns
8
+ .col-md-6
9
+ .alert.alert-info Сохраните форму, чтобы иметь возможность редактировать банки карт заршеренные к приему
@@ -0,0 +1 @@
1
+ = render 'attributes_table', object: Gera.payment_system_decorator.decorate(payment_system), columns: columns
@@ -0,0 +1,13 @@
1
+ table.table.table-condensed.table-hover.table-striped.table-narrow.text-small
2
+ thead
3
+ tr
4
+ th.text-right Принимаем
5
+ th.text-right Отдаем
6
+ th.text-right Актуальность
7
+ tbody
8
+ - external_rates.each do |er|
9
+ tr
10
+ td.text-right= er.currency_pair.first
11
+ td.text-right= rate_cell_with_currency er.rate_value, er.currency_to
12
+ td.text-right.text-muted= l er.created_at, format: :long
13
+
@@ -0,0 +1,23 @@
1
+ h4
2
+ = source
3
+ span.m-l-md.text-small.text-muted= link_to 'Журнал курсов', external_rate_snapshots_path(rate_source_id: source.id)
4
+
5
+ - snapshot = source.actual_snapshot
6
+
7
+ p Дата создания #{l source.updated_at, format: :long}
8
+
9
+ - if snapshot.present?
10
+ -# = render 'list', external_rates: snapshot.external_rates
11
+ = render layout: 'currencies_table', locals: { currencies: source.supported_currencies } do |pair|
12
+ td
13
+ - if pair.same?
14
+ .text-muted= '1'
15
+ - else
16
+ - er = snapshot.external_rates.find_by_currency_pair(pair)
17
+ - if er.present?
18
+ = humanized_rate er.rate_value
19
+ - else
20
+ .text-muted= middot
21
+
22
+ - else
23
+ .alert.alert-warning Нет актуальных данных
@@ -0,0 +1,6 @@
1
+ .pull-right
2
+ ul.nav
3
+ li= link_to 'Базовый курс', currency_rates_path
4
+
5
+ - Gera::RateSource.ordered.each do |source|
6
+ = render 'rate_source', source: source
@@ -0,0 +1,15 @@
1
+ doctype html
2
+ html
3
+ head
4
+ title= app_title
5
+ = csrf_meta_tags
6
+ = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload'
7
+ = javascript_include_tag 'application', 'data-turbolinks-track': 'reload'
8
+ body
9
+ = render 'header'
10
+
11
+ .container-fluid.container-margin-bottom style='margin-top: 80px'
12
+ - if breadcrumbs.nil?
13
+ ol.breadcrumb= render_breadcrumbs tag: :li, separator: '', class: '', item_class: '', divider_class: '', active_class: 'active'
14
+ = yield
15
+ = noty_flashes
@@ -0,0 +1,10 @@
1
+ doctype html
2
+ html
3
+ head
4
+ title= app_title
5
+ = csrf_meta_tags
6
+ = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload'
7
+ = javascript_include_tag 'application', 'data-turbolinks-track': 'reload'
8
+ = javascript_include_tag 'bestchange_widget', 'data-turbolinks-track': 'reload'
9
+ body
10
+ = yield
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'open-uri'
4
+ require 'rest-client'
5
+
6
+ module Gera
7
+ # Import rates from all sources
8
+ #
9
+ module RatesWorker
10
+ Error = Class.new StandardError
11
+
12
+ def perform
13
+ # Alternative approach is `Model.uncached do`
14
+ ActiveRecord::Base.connection.clear_query_cache
15
+
16
+ rates # Load before a translaction
17
+
18
+ rate_source.with_lock do
19
+ create_snapshot
20
+ rates.each do |pair, data|
21
+ save_rate pair, data
22
+ end
23
+ rate_source.update actual_snapshot_id: snapshot.id
24
+ end
25
+
26
+ CurrencyRatesWorker.new.perform
27
+
28
+ snapshot.id
29
+
30
+ # EXMORatesWorker::Error: Error 40016: Maintenance work in progress
31
+ rescue ActiveRecord::RecordNotUnique, RestClient::TooManyRequests => error
32
+ raise error if Rails.env.test?
33
+
34
+ logger.error error
35
+ Bugsnag.notify error do |b|
36
+ b.severity = :warning
37
+ b.meta_data = { error: error }
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ attr_reader :snapshot
44
+
45
+ delegate :actual_for, to: :snapshot
46
+
47
+ def create_snapshot
48
+ @snapshot ||= rate_source.snapshots.create! actual_for: Time.zone.now
49
+ end
50
+
51
+ def rates
52
+ @rates ||= load_rates
53
+ end
54
+
55
+ def create_external_rates(currency_pair, data, sell_price:, buy_price:)
56
+ logger.warn "Ignore #{currency_pair}" unless CurrencyPair.all.include? currency_pair
57
+
58
+ logger.info "save_rate_for_date #{actual_for}, #{currency_pair} #{data}"
59
+ ExternalRate.create!(
60
+ currency_pair: currency_pair,
61
+ snapshot: snapshot,
62
+ source: rate_source,
63
+ rate_value: buy_price.to_f
64
+ )
65
+ ExternalRate.create!(
66
+ currency_pair: currency_pair.inverse,
67
+ snapshot: snapshot,
68
+ source: rate_source,
69
+ rate_value: 1.0 / sell_price.to_f
70
+ )
71
+ rescue ActiveRecord::RecordNotUnique => err
72
+ raise error if Rails.env.test?
73
+
74
+ if err.message.include? 'external_rates_unique_index'
75
+ logger.debug "save_rate_for_date: #{actual_for} , #{currency_pair} -> #{err}"
76
+ if defined? Bugsnag
77
+ Bugsnag.notify 'Try to rewrite rates' do |b|
78
+ b.meta_data = { actual_for: actual_for, snapshot_id: snapshot.id, currency_pair: currency_pair }
79
+ end
80
+ end
81
+ else
82
+ logger.error "save_rate_for_date: #{actual_for} , #{pair} -> #{err}"
83
+ raise error
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gera
4
+ # Import rates from Bitfinex
5
+ #
6
+ class BitfinexRatesWorker
7
+ include Sidekiq::Worker
8
+ include AutoLogger
9
+
10
+ prepend RatesWorker
11
+
12
+ # Stolen from: https://api.bitfinex.com/v1/symbols
13
+ AVAILABLE_TICKETS = %i[btcusd ltcusd ltcbtc ethusd ethbtc etcbtc etcusd rrtusd rrtbtc zecusd zecbtc xmrusd xmrbtc dshusd dshbtc btceur btcjpy xrpusd
14
+ xrpbtc iotusd iotbtc ioteth eosusd eosbtc eoseth sanusd sanbtc saneth omgusd omgbtc omgeth neousd neobtc neoeth etpusd etpbtc
15
+ etpeth qtmusd qtmbtc qtmeth avtusd avtbtc avteth edousd edobtc edoeth btgusd btgbtc datusd datbtc dateth qshusd qshbtc qsheth
16
+ yywusd yywbtc yyweth gntusd gntbtc gnteth sntusd sntbtc snteth ioteur batusd batbtc bateth mnausd mnabtc mnaeth funusd funbtc
17
+ funeth zrxusd zrxbtc zrxeth tnbusd tnbbtc tnbeth spkusd spkbtc spketh trxusd trxbtc trxeth rcnusd rcnbtc rcneth rlcusd rlcbtc
18
+ rlceth aidusd aidbtc aideth sngusd sngbtc sngeth repusd repbtc repeth elfusd elfbtc elfeth btcgbp etheur ethjpy ethgbp neoeur
19
+ neojpy neogbp eoseur eosjpy eosgbp iotjpy iotgbp iosusd iosbtc ioseth aiousd aiobtc aioeth requsd reqbtc reqeth rdnusd rdnbtc
20
+ rdneth lrcusd lrcbtc lrceth waxusd waxbtc waxeth daiusd daibtc daieth agiusd agibtc agieth bftusd bftbtc bfteth mtnusd mtnbtc
21
+ mtneth odeusd odebtc odeeth antusd antbtc anteth dthusd dthbtc dtheth mitusd mitbtc miteth stjusd stjbtc stjeth xlmusd xlmeur
22
+ xlmjpy xlmgbp xlmbtc xlmeth xvgusd xvgeur xvgjpy xvggbp xvgbtc xvgeth bciusd bcibtc mkrusd mkrbtc mkreth kncusd kncbtc knceth
23
+ poausd poabtc poaeth lymusd lymbtc lymeth utkusd utkbtc utketh veeusd veebtc veeeth dadusd dadbtc dadeth orsusd orsbtc orseth
24
+ aucusd aucbtc auceth poyusd poybtc poyeth fsnusd fsnbtc fsneth cbtusd cbtbtc cbteth zcnusd zcnbtc zcneth senusd senbtc seneth
25
+ ncausd ncabtc ncaeth cndusd cndbtc cndeth ctxusd ctxbtc ctxeth paiusd paibtc seeusd seebtc seeeth essusd essbtc esseth atmusd
26
+ atmbtc atmeth hotusd hotbtc hoteth dtausd dtabtc dtaeth iqxusd iqxbtc iqxeos wprusd wprbtc wpreth zilusd zilbtc zileth bntusd
27
+ bntbtc bnteth absusd abseth xrausd xraeth manusd maneth bbnusd bbneth niousd nioeth dgxusd dgxeth vetusd vetbtc veteth utnusd
28
+ utneth tknusd tkneth gotusd goteur goteth xtzusd xtzbtc cnnusd cnneth boxusd boxeth trxeur trxgbp trxjpy mgousd mgoeth rteusd
29
+ rteeth yggusd yggeth mlnusd mlneth wtcusd wtceth csxusd csxeth omnusd omnbtc intusd inteth drnusd drneth pnkusd pnketh dgbusd
30
+ dgbbtc bsvusd bsvbtc babusd babbtc wlousd wloxlm vldusd vldeth enjusd enjeth onlusd onleth rbtusd rbtbtc ustusd euteur eutusd
31
+ gsdusd udcusd tsdusd paxusd rifusd rifbtc pasusd paseth vsyusd vsybtc zrxdai mkrdai omgdai bttusd bttbtc btcust ethust clousd
32
+ clobtc].freeze
33
+
34
+ # NOTE: formar tickers neousd neobtc neoeth neoeur
35
+ TICKERS = %i[].freeze
36
+
37
+ private
38
+
39
+ def rate_source
40
+ @rate_source ||= RateSourceBitfinex.get!
41
+ end
42
+
43
+ # {"mid":"8228.25",
44
+ # "bid":"8228.2",
45
+ # "ask":"8228.3",
46
+ # "last_price":"8228.3",
47
+ # "low":"8055.0",
48
+ # "high":"8313.3",
49
+ # "volume":"13611.826947359996",
50
+ # "timestamp":"1532874580.9087598"}
51
+ def save_rate(ticker, data)
52
+ currency_pair = pair_from_ticker ticker
53
+ create_external_rates currency_pair, data, sell_price: data['high'], buy_price: data['low']
54
+ end
55
+
56
+ def pair_from_ticker(ticker)
57
+ ticker = ticker.to_s
58
+ CurrencyPair.new ticker[0, 3], ticker[3, 3]
59
+ end
60
+
61
+ def load_rates
62
+ TICKERS.each_with_object({}) { |ticker, ag| ag[ticker] = BitfinexFetcher.new(ticker: ticker).perform }
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gera
4
+ class CBRAvgRatesWorker
5
+ include Sidekiq::Worker
6
+ include AutoLogger
7
+
8
+ def perform
9
+ ActiveRecord::Base.connection.clear_query_cache
10
+ source.with_lock do
11
+ source.available_pairs.each do |pair|
12
+ create_rate pair
13
+ end
14
+ source.update_attribute :actual_snapshot_id, snapshot.id
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def source
21
+ @source ||= RateSourceCBRAvg.get!
22
+ end
23
+
24
+ def snapshot
25
+ @snapshot ||= source.snapshots.create!
26
+ end
27
+
28
+ def create_rate(pair)
29
+ er = RateSource.cbr.find_rate_by_currency_pair pair
30
+
31
+ price = (er.sell_price + er.buy_price) / 2.0
32
+
33
+ ExternalRate.create!(
34
+ source: source,
35
+ snapshot: snapshot,
36
+ currency_pair: pair,
37
+ sell_price: price,
38
+ buy_price: price
39
+ )
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,192 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'open-uri'
4
+ require 'business_time'
5
+
6
+ module Gera
7
+ # Import rates from Russian Central Bank
8
+ # http://www.cbr.ru/scripts/XML_daily.asp?date_req=08/04/2018
9
+ #
10
+ class CBRRatesWorker
11
+ include Sidekiq::Worker
12
+ include AutoLogger
13
+
14
+ CURRENCIES = %w[USD KZT EUR UAH].freeze
15
+
16
+ CBR_IDS = {
17
+ 'USD' => 'R01235',
18
+ 'KZT' => 'R01335',
19
+ 'EUR' => 'R01239',
20
+ 'UAH' => 'R01720'
21
+ }.freeze
22
+
23
+ ROUND = 15
24
+
25
+ Error = Class.new StandardError
26
+ WrongDate = Class.new Error
27
+
28
+ URL = 'http://www.cbr.ru/scripts/XML_daily.asp'
29
+
30
+ def perform
31
+ ActiveRecord::Base.connection.clear_query_cache
32
+ cbr.with_lock do
33
+ days.each do |date|
34
+ fetch_and_save_rate date
35
+ end
36
+
37
+ make_snapshot
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def currencies
44
+ @currencies ||= CURRENCIES.map { |iso_code| Money::Currency.find! iso_code }
45
+ end
46
+
47
+ def snapshot
48
+ @snapshot ||= cbr.snapshots.create!
49
+ end
50
+
51
+ def avg_snapshot
52
+ @avg_snapshot ||= cbr_avg.snapshots.create!
53
+ end
54
+
55
+ def make_snapshot
56
+ save_snapshot_rate USD, RUB
57
+ save_snapshot_rate KZT, RUB
58
+ save_snapshot_rate EUR, RUB
59
+ save_snapshot_rate UAH, RUB
60
+
61
+ cbr.update_attribute :actual_snapshot_id, snapshot.id
62
+ cbr_avg.update_attribute :actual_snapshot_id, avg_snapshot.id
63
+ end
64
+
65
+ def save_snapshot_rate(cur_from, cur_to)
66
+ pair = CurrencyPair.new cur_from, cur_to
67
+
68
+ min_rate, max_rate = CbrExternalRate
69
+ .where(cur_from: cur_from.iso_code, cur_to: cur_to.iso_code)
70
+ .order('date asc')
71
+ .last(2)
72
+ .sort
73
+
74
+ raise "No minimal rate #{cur_from}, #{cur_to}" unless min_rate
75
+ raise "No maximal rate #{cur_from}, #{cur_to}" unless max_rate
76
+
77
+ ExternalRate.create!(
78
+ source: cbr,
79
+ snapshot: snapshot,
80
+ currency_pair: pair,
81
+ rate_value: min_rate.rate
82
+ )
83
+
84
+ ExternalRate.create!(
85
+ source: cbr,
86
+ snapshot: snapshot,
87
+ currency_pair: pair.inverse,
88
+ rate_value: 1.0 / max_rate.rate
89
+ )
90
+
91
+ avg_rate = (max_rate.rate + min_rate.rate) / 2.0
92
+
93
+ ExternalRate.create!(
94
+ source: cbr_avg,
95
+ snapshot: avg_snapshot,
96
+ currency_pair: pair,
97
+ rate_value: avg_rate
98
+ )
99
+
100
+ ExternalRate.create!(
101
+ source: cbr_avg,
102
+ snapshot: avg_snapshot,
103
+ currency_pair: pair.inverse,
104
+ rate_value: 1.0 / avg_rate
105
+ )
106
+ end
107
+
108
+ def days
109
+ today = Date.today
110
+ logger.info "Start import for #{today}"
111
+
112
+ [
113
+ 1.business_day.ago(today),
114
+ today.yesterday.yesterday,
115
+ today.yesterday,
116
+ today,
117
+ today.tomorrow,
118
+ 1.business_day.from_now(today)
119
+ ].uniq.sort
120
+ end
121
+
122
+ def fetch_and_save_rate(date)
123
+ fetch_rates date
124
+ rescue WrongDate => err
125
+ logger.warn err
126
+
127
+ # HTTP redirection loop: http://www.cbr.ru/scripts/XML_daily.asp?date_req=09/01/2019
128
+ rescue RuntimeError => err
129
+ raise err unless err.message.include? 'HTTP redirection loop'
130
+
131
+ logger.error err
132
+ end
133
+
134
+ def cbr_avg
135
+ @cbr_avg ||= RateSourceCBRAvg.get!
136
+ end
137
+
138
+ def cbr
139
+ @cbr ||= RateSourceCBR.get!
140
+ end
141
+
142
+ def fetch_rates(date)
143
+ return if CbrExternalRate.where(date: date, cur_from: currencies.map(&:iso_code)).count == currencies.count
144
+
145
+ root = build_root date
146
+
147
+ currencies.each do |cur|
148
+ save_rate get_rate(root, CBR_IDS[cur.iso_code]), cur, date unless CbrExternalRate.where(date: date, cur_from: cur.iso_code).exists?
149
+ end
150
+ end
151
+
152
+ def build_root(date)
153
+ uri = URI.parse URL
154
+ uri.query = 'date_req=' + date.strftime('%d/%m/%Y')
155
+
156
+ logger.info "fetch rates for #{date} from #{uri}"
157
+
158
+ doc = Nokogiri::XML open uri
159
+ root = doc.xpath('/ValCurs')
160
+
161
+ root_date = root.attr('Date').text
162
+ validate_date = date.strftime('%d.%m.%Y')
163
+
164
+ return root if validate_date == root_date
165
+
166
+ raise WrongDate, "Request and response dates are different #{uri}: #{validate_date} <> #{root_date}"
167
+ end
168
+
169
+ def get_rate(root, id)
170
+ valute = root.xpath("Valute[@ID=\"#{id}\"]")
171
+ original_rate = valute.xpath('Value').text.sub(',', '.').to_f
172
+ nominal = valute.xpath('Nominal').text.sub(',', '.').to_f
173
+ OpenStruct.new original_rate: original_rate, nominal: nominal
174
+ end
175
+
176
+ def save_rate(rate_struct, cur, date)
177
+ original_rate = rate_struct.original_rate
178
+ nominal = rate_struct.nominal
179
+
180
+ rate = (original_rate / nominal).round(ROUND)
181
+
182
+ CbrExternalRate.create!(
183
+ cur_from: cur.iso_code,
184
+ cur_to: RUB.iso_code,
185
+ rate: rate,
186
+ original_rate: original_rate,
187
+ nominal: nominal,
188
+ date: date
189
+ )
190
+ end
191
+ end
192
+ end