gera 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +674 -0
- data/README.md +67 -0
- data/Rakefile +43 -0
- data/app/assets/config/gera_manifest.js +2 -0
- data/app/assets/javascripts/gera/application.js +14 -0
- data/app/assets/stylesheets/gera/application.css +15 -0
- data/app/authorizers/gera/application_authorizer.rb +6 -0
- data/app/authorizers/gera/currency_rate_authorizer.rb +9 -0
- data/app/authorizers/gera/currency_rate_mode_authorizer.rb +7 -0
- data/app/authorizers/gera/direction_rate_authorizer.rb +8 -0
- data/app/authorizers/gera/exchange_rate_authorizer.rb +8 -0
- data/app/authorizers/gera/payment_system_authorizer.rb +13 -0
- data/app/authorizers/gera/rate_source_authorizer.rb +7 -0
- data/app/controllers/gera/application_controller.rb +30 -0
- data/app/controllers/gera/currencies_controller.rb +8 -0
- data/app/controllers/gera/currency_rate_history_intervals_controller.rb +53 -0
- data/app/controllers/gera/currency_rate_mode_snapshots_controller.rb +83 -0
- data/app/controllers/gera/currency_rate_modes_controller.rb +60 -0
- data/app/controllers/gera/currency_rates_controller.rb +70 -0
- data/app/controllers/gera/direction_rate_history_intervals_controller.rb +62 -0
- data/app/controllers/gera/direction_rates_controller.rb +28 -0
- data/app/controllers/gera/exchange_rates_controller.rb +25 -0
- data/app/controllers/gera/external_rate_snapshots_controller.rb +35 -0
- data/app/controllers/gera/external_rates_controller.rb +28 -0
- data/app/controllers/gera/payment_systems_controller.rb +78 -0
- data/app/controllers/gera/rate_sources_controller.rb +8 -0
- data/app/decorators/gera/currency_rate_decorator.rb +148 -0
- data/app/decorators/gera/direction_rate_decorator.rb +47 -0
- data/app/decorators/gera/payment_system_decorator.rb +19 -0
- data/app/helpers/gera/application_helper.rb +110 -0
- data/app/helpers/gera/currency_rate_helper.rb +19 -0
- data/app/helpers/gera/currency_rate_modes_helper.rb +73 -0
- data/app/helpers/gera/direction_rate_helper.rb +36 -0
- data/app/helpers/gera/exchange_rates_helper.rb +21 -0
- data/app/jobs/gera/application_job.rb +6 -0
- data/app/models/concerns/gera/currency_pair_generator.rb +14 -0
- data/app/models/concerns/gera/currency_pair_support.rb +42 -0
- data/app/models/concerns/gera/currency_rate_mode_builder_support.rb +35 -0
- data/app/models/concerns/gera/direction_support.rb +14 -0
- data/app/models/concerns/gera/history_interval_concern.rb +46 -0
- data/app/models/gera/application_record.rb +8 -0
- data/app/models/gera/cbr_external_rate.rb +13 -0
- data/app/models/gera/cross_rate_mode.rb +13 -0
- data/app/models/gera/currency_rate.rb +78 -0
- data/app/models/gera/currency_rate_history_interval.rb +25 -0
- data/app/models/gera/currency_rate_history_interval_filter.rb +30 -0
- data/app/models/gera/currency_rate_mode.rb +20 -0
- data/app/models/gera/currency_rate_mode_snapshot.rb +26 -0
- data/app/models/gera/currency_rate_snapshot.rb +12 -0
- data/app/models/gera/direction.rb +44 -0
- data/app/models/gera/direction_rate.rb +136 -0
- data/app/models/gera/direction_rate_history_interval.rb +33 -0
- data/app/models/gera/direction_rate_history_interval_filter.rb +30 -0
- data/app/models/gera/direction_rate_snapshot.rb +7 -0
- data/app/models/gera/exchange_rate.rb +130 -0
- data/app/models/gera/external_rate.rb +38 -0
- data/app/models/gera/external_rate_snapshot.rb +20 -0
- data/app/models/gera/payment_system.rb +98 -0
- data/app/models/gera/rate_source.rb +77 -0
- data/app/models/gera/rate_source_auto.rb +25 -0
- data/app/models/gera/rate_source_bitfinex.rb +9 -0
- data/app/models/gera/rate_source_cbr.rb +13 -0
- data/app/models/gera/rate_source_cbr_avg.rb +6 -0
- data/app/models/gera/rate_source_exmo.rb +9 -0
- data/app/models/gera/rate_source_manual.rb +13 -0
- data/app/views/application/_exchange_rate_monitor.slim +1 -0
- data/app/views/gera/application/_attributes_table.slim +5 -0
- data/app/views/gera/application/_currencies_table.slim +18 -0
- data/app/views/gera/application/_currency_rate_mode_snapshots.slim +6 -0
- data/app/views/gera/application/_direction_rate.slim +6 -0
- data/app/views/gera/application/_directions_submenu.slim +11 -0
- data/app/views/gera/application/_directions_table.slim +15 -0
- data/app/views/gera/application/_exchange_rate.slim +6 -0
- data/app/views/gera/application/_external_rate.slim +6 -0
- data/app/views/gera/application/_header.slim +12 -0
- data/app/views/gera/application/_table.slim +28 -0
- data/app/views/gera/application/_topnav.slim +21 -0
- data/app/views/gera/application/direction_details.slim +75 -0
- data/app/views/gera/application/page.html.slim +1 -0
- data/app/views/gera/currencies/index.html.slim +20 -0
- data/app/views/gera/currency_rate_history_intervals/_filter.slim +11 -0
- data/app/views/gera/currency_rate_history_intervals/index.slim +39 -0
- data/app/views/gera/currency_rate_mode_snapshots/_snapshot.slim +20 -0
- data/app/views/gera/currency_rate_mode_snapshots/edit.html.slim +30 -0
- data/app/views/gera/currency_rate_modes/_cross_rate_mode_fields.slim +21 -0
- data/app/views/gera/currency_rate_modes/_currency_rate_mode.slim +7 -0
- data/app/views/gera/currency_rate_modes/_form.slim +29 -0
- data/app/views/gera/currency_rate_modes/edit.slim +11 -0
- data/app/views/gera/currency_rate_modes/index.html.slim +12 -0
- data/app/views/gera/currency_rates/_currency_rate.slim +22 -0
- data/app/views/gera/currency_rates/_currency_rate_humanized.slim +35 -0
- data/app/views/gera/currency_rates/diff.slim +12 -0
- data/app/views/gera/currency_rates/index.slim +27 -0
- data/app/views/gera/currency_rates/modes.slim +14 -0
- data/app/views/gera/currency_rates/show.slim +4 -0
- data/app/views/gera/direction_rate_history_intervals/_filter.slim +11 -0
- data/app/views/gera/direction_rate_history_intervals/index.slim +41 -0
- data/app/views/gera/direction_rates/_direction_rate_example_calculator.slim +28 -0
- data/app/views/gera/direction_rates/diff.slim +19 -0
- data/app/views/gera/direction_rates/index.slim +26 -0
- data/app/views/gera/direction_rates/legacy.html.slim +5 -0
- data/app/views/gera/direction_rates/minimals.slim +5 -0
- data/app/views/gera/direction_rates/show.slim +14 -0
- data/app/views/gera/external_rate_snapshots/index.slim +25 -0
- data/app/views/gera/external_rate_snapshots/show.slim +22 -0
- data/app/views/gera/external_rates/_flat_list.slim +22 -0
- data/app/views/gera/external_rates/index.html.slim +28 -0
- data/app/views/gera/external_rates/show.slim +1 -0
- data/app/views/gera/payment_systems/_fields.slim +4 -0
- data/app/views/gera/payment_systems/_fields_table.slim +18 -0
- data/app/views/gera/payment_systems/_form.slim +9 -0
- data/app/views/gera/payment_systems/edit.slim +7 -0
- data/app/views/gera/payment_systems/index.slim +3 -0
- data/app/views/gera/payment_systems/new.slim +9 -0
- data/app/views/gera/payment_systems/show.slim +1 -0
- data/app/views/gera/rate_sources/_list.slim +13 -0
- data/app/views/gera/rate_sources/_rate_source.slim +23 -0
- data/app/views/gera/rate_sources/index.html.slim +6 -0
- data/app/views/layouts/application.html.slim +15 -0
- data/app/views/layouts/gera/blank.html.slim +10 -0
- data/app/workers/concerns/gera/rates_worker.rb +87 -0
- data/app/workers/gera/bitfinex_rates_worker.rb +65 -0
- data/app/workers/gera/cbr_avg_rates_worker.rb +42 -0
- data/app/workers/gera/cbr_rates_worker.rb +192 -0
- data/app/workers/gera/create_history_intervals_worker.rb +38 -0
- data/app/workers/gera/currency_rates_worker.rb +64 -0
- data/app/workers/gera/directions_rates_worker.rb +51 -0
- data/app/workers/gera/exmo_rates_worker.rb +62 -0
- data/app/workers/gera/purge_currency_rates_worker.rb +21 -0
- data/app/workers/gera/purge_direction_rates_worker.rb +30 -0
- data/config/currencies.yml +559 -0
- data/config/routes.rb +30 -0
- data/db/migrate/20180912130000_setup.rb +228 -0
- data/db/migrate/20190314114844_add_is_available_to_payment_systems.rb +7 -0
- data/db/migrate/20190315113046_add_icon_url_to_payment_systems.rb +7 -0
- data/db/migrate/20190315132137_add_commission_to_payment_systems.rb +7 -0
- data/lib/banks/currency_exchange.rb +12 -0
- data/lib/builders/currency_rate_auto_builder.rb +38 -0
- data/lib/builders/currency_rate_builder.rb +60 -0
- data/lib/builders/currency_rate_cross_builder.rb +70 -0
- data/lib/builders/currency_rate_direct_builder.rb +26 -0
- data/lib/gera.rb +32 -0
- data/lib/gera/bitfinex_fetcher.rb +36 -0
- data/lib/gera/configuration.rb +47 -0
- data/lib/gera/currencies_purger.rb +28 -0
- data/lib/gera/currency_pair.rb +93 -0
- data/lib/gera/engine.rb +14 -0
- data/lib/gera/mathematic.rb +156 -0
- data/lib/gera/money_support.rb +89 -0
- data/lib/gera/numeric.rb +25 -0
- data/lib/gera/railtie.rb +8 -0
- data/lib/gera/rate.rb +22 -0
- data/lib/gera/rate_from_multiplicator.rb +54 -0
- data/lib/gera/repositories/currency_rate_modes_repository.rb +37 -0
- data/lib/gera/repositories/currency_rates_repository.rb +25 -0
- data/lib/gera/repositories/direction_rates_repository.rb +46 -0
- data/lib/gera/repositories/exchange_rates_repository.rb +23 -0
- data/lib/gera/repositories/payment_systems_repository.rb +21 -0
- data/lib/gera/repositories/universe.rb +47 -0
- data/lib/gera/version.rb +3 -0
- data/lib/tasks/auto_generate_diagram.rake +6 -0
- data/lib/tasks/gera_tasks.rake +4 -0
- 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,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,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,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
|