istox 0.1.86 → 0.1.87
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +7 -8
- data/Gemfile.lock +1 -1
- data/lib/istox.rb +2 -1
- data/lib/istox/helpers/order_book_price_time.rb +167 -0
- data/lib/istox/helpers/order_book_prorate.rb +80 -0
- data/lib/istox/quant/bond.rb +41 -23
- data/lib/istox/version.rb +1 -1
- metadata +4 -3
- data/lib/istox/helpers/order_book.rb +0 -88
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b9516beb23b5b52b1a86c7a7ab8d03b7e7c377d74163b7a81400041c4501a4e0
|
4
|
+
data.tar.gz: fdaa33a8f434fc799eaa9416ef2287d384fd28e1610a4d68cbf637007c4d5af9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 957c7d9762478fa79949b747676a14361052ad9667175bd0824233fc1ac2dae72469b69e7addc8d47eb2b6ab75803edc36be3699e14cbbdbfd01daa81ffceef8
|
7
|
+
data.tar.gz: ab95640711cb08298c6a68e0b7a14a320d8fa38fbfebb9a44d1bf56752e1abc999459e982329addd2b48579ccd221b6565218f45afe47332571401cbd5e28928
|
data/.rubocop.yml
CHANGED
@@ -34,7 +34,7 @@ Metrics/AbcSize:
|
|
34
34
|
Max: 100
|
35
35
|
|
36
36
|
Metrics/BlockLength:
|
37
|
-
CountComments: false
|
37
|
+
CountComments: false # count full line comments?
|
38
38
|
Max: 25
|
39
39
|
Exclude:
|
40
40
|
- config/**/*
|
@@ -45,7 +45,7 @@ Metrics/BlockNesting:
|
|
45
45
|
Max: 4
|
46
46
|
|
47
47
|
Metrics/ClassLength:
|
48
|
-
CountComments: false
|
48
|
+
CountComments: false # count full line comments?
|
49
49
|
Max: 200
|
50
50
|
|
51
51
|
# Avoid complex methods.
|
@@ -53,15 +53,15 @@ Metrics/CyclomaticComplexity:
|
|
53
53
|
Max: 20
|
54
54
|
|
55
55
|
Metrics/MethodLength:
|
56
|
-
CountComments: false
|
56
|
+
CountComments: false # count full line comments?
|
57
57
|
Max: 100
|
58
58
|
|
59
59
|
Metrics/ModuleLength:
|
60
|
-
CountComments: false
|
60
|
+
CountComments: false # count full line comments?
|
61
61
|
Max: 200
|
62
62
|
|
63
63
|
Metrics/LineLength:
|
64
|
-
Max:
|
64
|
+
Max: 150
|
65
65
|
# To make it possible to copy or click on URIs in the code, we allow lines
|
66
66
|
# containing a URI to be longer than Max.
|
67
67
|
AllowURI: true
|
@@ -70,7 +70,7 @@ Metrics/LineLength:
|
|
70
70
|
- https
|
71
71
|
|
72
72
|
Metrics/ParameterLists:
|
73
|
-
Max:
|
73
|
+
Max: 8
|
74
74
|
CountKeywordArgs: true
|
75
75
|
|
76
76
|
Metrics/PerceivedComplexity:
|
@@ -91,7 +91,6 @@ RSpec/ExampleLength:
|
|
91
91
|
Lint/BigDecimalNew:
|
92
92
|
Enabled: false
|
93
93
|
|
94
|
-
|
95
94
|
RSpec/MessageSpies:
|
96
95
|
Enabled: false
|
97
96
|
|
@@ -99,4 +98,4 @@ RSpec/AnyInstance:
|
|
99
98
|
Enabled: false
|
100
99
|
|
101
100
|
RSpec/VerifiedDoubles:
|
102
|
-
Enabled: false
|
101
|
+
Enabled: false
|
data/Gemfile.lock
CHANGED
data/lib/istox.rb
CHANGED
@@ -16,7 +16,8 @@ module Istox
|
|
16
16
|
require 'istox/helpers/publisher'
|
17
17
|
require 'istox/helpers/bunny_boot'
|
18
18
|
require 'istox/helpers/vault'
|
19
|
-
require 'istox/helpers/
|
19
|
+
require 'istox/helpers/order_book_price_time'
|
20
|
+
require 'istox/helpers/order_book_prorate'
|
20
21
|
require 'istox/helpers/grpc_client'
|
21
22
|
require 'istox/helpers/graphql_client'
|
22
23
|
require 'istox/helpers/gruf_listener_hook'
|
@@ -0,0 +1,167 @@
|
|
1
|
+
module Istox
|
2
|
+
class OrderBookPriceTime
|
3
|
+
class << self
|
4
|
+
def allocation(soft_cap, total_supply, investments)
|
5
|
+
# sort by token price desc, and id asc
|
6
|
+
investments = investments.sort do |a, b|
|
7
|
+
[b[:token_price], a[:id]] <=> [a[:token_price], b[:id]]
|
8
|
+
end
|
9
|
+
|
10
|
+
total_supply = ::BigDecimal.new(total_supply.to_s)
|
11
|
+
|
12
|
+
interests = investments
|
13
|
+
cutoff_price = 0.0
|
14
|
+
total_bid_token = 0.0
|
15
|
+
total_allocated = 0.0
|
16
|
+
total_unallocated = 0.0
|
17
|
+
total_investment = 0.0
|
18
|
+
cutoff_price = ::Istox::FMath.round_up(::Istox::FMath.div(soft_cap, total_supply), 2)
|
19
|
+
is_cutoff = false
|
20
|
+
|
21
|
+
# return result immediately if no interest
|
22
|
+
unless interests.count.positive?
|
23
|
+
return { total_supply: total_supply.to_s, total_investment: '0',
|
24
|
+
cutoff_price: cutoff_price, total_bid_token: '0',
|
25
|
+
total_allocated: '0', total_unallocated: total_supply.to_s,
|
26
|
+
interests: [] }
|
27
|
+
end
|
28
|
+
|
29
|
+
interests = interests.map do |item|
|
30
|
+
bid_token = Istox::FMath.round_down(::Istox::FMath.div(item[:fiat_amount], item[:token_price]), 0)
|
31
|
+
total_bid_token = ::Istox::FMath.add(total_bid_token, bid_token)
|
32
|
+
allocated = 0.0
|
33
|
+
unallocated = bid_token
|
34
|
+
unless is_cutoff
|
35
|
+
allocated = bid_token
|
36
|
+
unallocated = 0.0
|
37
|
+
if ::BigDecimal.new(::Istox::FMath.add(total_allocated, bid_token)) >= total_supply
|
38
|
+
unallocated = ::Istox::FMath.sub(::Istox::FMath.add(total_allocated, bid_token), total_supply)
|
39
|
+
allocated = ::Istox::FMath.sub(bid_token, unallocated)
|
40
|
+
cutoff_price = item[:token_price]
|
41
|
+
is_cutoff = true
|
42
|
+
end
|
43
|
+
total_allocated = ::Istox::FMath.add(total_allocated, allocated)
|
44
|
+
total_unallocated = ::Istox::FMath.add(total_unallocated, unallocated)
|
45
|
+
end
|
46
|
+
{
|
47
|
+
id: item[:id],
|
48
|
+
# user bidding total fiat amount
|
49
|
+
fiat_amount: item[:fiat_amount],
|
50
|
+
# user bidding price
|
51
|
+
bid_price: item[:token_price],
|
52
|
+
# total user bidding token derived from fiat_amount / token_price
|
53
|
+
bid_token: bid_token,
|
54
|
+
# round down allocated token to whole number
|
55
|
+
allocated: ::Istox::FMath.round_down(allocated, 0),
|
56
|
+
# round up unallocated token to whole number
|
57
|
+
unallocated: ::Istox::FMath.round_up(unallocated, 0)
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
interests = interests.map do |item|
|
62
|
+
investment = ::Istox::FMath.mul(item[:allocated], cutoff_price)
|
63
|
+
total_investment = ::Istox::FMath.add(total_investment, investment)
|
64
|
+
|
65
|
+
# each user maximum total investment amount in fiat
|
66
|
+
item.merge!(investment: investment)
|
67
|
+
end
|
68
|
+
|
69
|
+
{
|
70
|
+
# total token supply
|
71
|
+
total_supply: total_supply.to_s,
|
72
|
+
# total investment in fiat
|
73
|
+
total_investment: total_investment,
|
74
|
+
# cut off price
|
75
|
+
cutoff_price: cutoff_price.to_s,
|
76
|
+
# total bid tokens, more than total supply for oversubscribe case
|
77
|
+
total_bid_token: total_bid_token,
|
78
|
+
# total allocated tokens
|
79
|
+
total_allocated: total_allocated,
|
80
|
+
# total unallocated tokens
|
81
|
+
total_unallocated: total_unallocated,
|
82
|
+
# all investor interest results
|
83
|
+
interests: interests
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
# rubocop:disable Metrics/BlockNesting
|
88
|
+
def prorate_allocation(token_price, min_investment, bid_block,
|
89
|
+
invest_step, hard_cap, interests)
|
90
|
+
# sort by id asc
|
91
|
+
interests = interests.sort do |a, b|
|
92
|
+
a[:id] <=> b[:id]
|
93
|
+
end
|
94
|
+
total_interests = 0
|
95
|
+
|
96
|
+
max_allowed_investor = Istox::FMath.round_down(::Istox::FMath.div(
|
97
|
+
::Istox::FMath.div(hard_cap, bid_block), token_price
|
98
|
+
), 0).to_i
|
99
|
+
|
100
|
+
interests.each_with_index.map do |interest, i|
|
101
|
+
total_interests = ::Istox::FMath.add(total_investment, interest.fiat_amount) if i < max_allowed_investor
|
102
|
+
end
|
103
|
+
|
104
|
+
# only need to handle when oversubscribe
|
105
|
+
if total_interests.to_d > hard_cap.to_s.to_d
|
106
|
+
|
107
|
+
# prorate the interests
|
108
|
+
interests = interests.each_with_index.map do |interest, i|
|
109
|
+
{
|
110
|
+
id: interest.id,
|
111
|
+
investment: i < max_allowed_investor ? ::FMath.mul(::FMath.div(interest.fiat_amount, total_interests), hard_cap) : '0'
|
112
|
+
}
|
113
|
+
end
|
114
|
+
|
115
|
+
# do adjustments for interest if needed
|
116
|
+
final_interests = interests.map do |interest|
|
117
|
+
if interest[:investment].to_d < min_investment.to_d
|
118
|
+
# calculate the adjusted amount
|
119
|
+
lacking_amount = ::Istox::FMath.sub(min_investment, interest[:investment])
|
120
|
+
# if some investor has less than min_investment allocated, auto adjust to min investment
|
121
|
+
interest[:investment] = min_investment.to_s
|
122
|
+
|
123
|
+
# must deduct the adjust amount from later book building joiners who has more amount allocated
|
124
|
+
# than min-investment
|
125
|
+
if lacking_amount.to_d.positive?
|
126
|
+
interests.reverse_each do |inner_interest|
|
127
|
+
# skip the same interest from deduction candidate
|
128
|
+
next unless inner_interest[:id] != interest[:id]
|
129
|
+
|
130
|
+
# when investor investment is higher than min_investment, he will become a candidate to
|
131
|
+
# get investment deduction
|
132
|
+
while inner_interest[:investment].to_d > min_investment.to_d && lacking_amount.to_d.positive?
|
133
|
+
lacking_amount = ::Istox::FMath.sub(lacking_amount, invest_step)
|
134
|
+
inner_interest[:investment] = ::Istox::FMath.sub(inner_interest[:investment], invest_step)
|
135
|
+
|
136
|
+
# add back the difference if over deduct an investor invest amount
|
137
|
+
if lacking_amount.to_d.negative?
|
138
|
+
inner_interest[:investment] = ::Istox::FMath.add(inner_interest[:investment],
|
139
|
+
BigDecimal(lacking_amount).abs)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
{
|
146
|
+
id: interest[:id],
|
147
|
+
investment: interest[:investment]
|
148
|
+
}
|
149
|
+
end
|
150
|
+
else
|
151
|
+
final_interests = interests.each_with_index.map do |interest, i|
|
152
|
+
{
|
153
|
+
id: interest.id,
|
154
|
+
investment: i < max_allowed_investor ? interest.fiat_amount : '0'
|
155
|
+
}
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
{
|
160
|
+
cutoff_price: token_price,
|
161
|
+
interests: final_interests
|
162
|
+
}
|
163
|
+
end
|
164
|
+
end
|
165
|
+
# rubocop:enable Metrics/BlockNesting
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Istox
|
2
|
+
class OrderBookProrate
|
3
|
+
class << self
|
4
|
+
def allocation(token_price:, min_investment:, bid_block:,
|
5
|
+
invest_step:, hard_cap:, investments:)
|
6
|
+
|
7
|
+
# sort by id asc
|
8
|
+
interests = investments.sort do |a, b|
|
9
|
+
a[:id] <=> b[:id]
|
10
|
+
end
|
11
|
+
total_interests = 0
|
12
|
+
|
13
|
+
max_allowed_investor = Istox::FMath.round_down(::Istox::FMath.div(hard_cap, bid_block), 0).to_i
|
14
|
+
|
15
|
+
interests.each_with_index.map do |interest, i|
|
16
|
+
total_interests = ::Istox::FMath.add(total_interests, interest[:fiat_amount]) if i < max_allowed_investor
|
17
|
+
end
|
18
|
+
|
19
|
+
# only need to handle when oversubscribe
|
20
|
+
if total_interests.to_d > hard_cap.to_s.to_d
|
21
|
+
|
22
|
+
# prorate the interests
|
23
|
+
interests = interests.each_with_index.map do |interest, i|
|
24
|
+
investment = if i < max_allowed_investor
|
25
|
+
::Istox::FMath.round_down(::Istox::FMath.mul(::Istox::FMath.div(interest[:fiat_amount], total_interests), hard_cap), 0)
|
26
|
+
else
|
27
|
+
::BigDecimal.new('0').to_s
|
28
|
+
end
|
29
|
+
investment = investment.to_d - investment.to_d.modulo(invest_step.to_s.to_d)
|
30
|
+
|
31
|
+
investment = min_investment.to_d if investment < min_investment.to_d
|
32
|
+
|
33
|
+
{
|
34
|
+
id: interest[:id],
|
35
|
+
investment: investment.to_s
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
new_total_interests = '0'
|
40
|
+
interests.each do |interest|
|
41
|
+
new_total_interests = ::Istox::FMath.add(new_total_interests, interest[:investment])
|
42
|
+
end
|
43
|
+
|
44
|
+
# need to do adjustment if summation of new rounded values still larger than
|
45
|
+
if new_total_interests.to_d > hard_cap.to_s.to_d
|
46
|
+
total_deducting = ::Istox::FMath.sub(new_total_interests, hard_cap)
|
47
|
+
interests.reverse_each do |interest|
|
48
|
+
next unless interest[:investment].to_d > min_investment.to_d
|
49
|
+
|
50
|
+
deductable = ::Istox::FMath.sub(interest[:investment], min_investment)
|
51
|
+
|
52
|
+
if deductable.to_d > total_deducting.to_d
|
53
|
+
interest[:investment] = ::Istox::FMath.sub(interest[:investment], total_deducting)
|
54
|
+
total_deducting = 0
|
55
|
+
else
|
56
|
+
interest[:investment] = ::Istox::FMath.sub(interest[:investment], deductable)
|
57
|
+
total_deducting = ::Istox::FMath.sub(total_deducting, deductable)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
final_interests = interests
|
63
|
+
|
64
|
+
else
|
65
|
+
final_interests = interests.each_with_index.map do |interest, i|
|
66
|
+
{
|
67
|
+
id: interest[:id],
|
68
|
+
investment: i < max_allowed_investor ? ::BigDecimal.new(interest[:fiat_amount]).to_s : ::BigDecimal.new('0').to_s
|
69
|
+
}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
{
|
74
|
+
cutoff_price: token_price.to_s,
|
75
|
+
interests: final_interests
|
76
|
+
}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/istox/quant/bond.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'date'
|
2
|
+
require 'rails'
|
2
3
|
|
3
4
|
# https://en.wikipedia.org/wiki/Day_count_convention#30/360_Bond_Basis
|
4
5
|
# For 360 basis: 30/360 Bond Basis
|
@@ -15,25 +16,25 @@ module Istox
|
|
15
16
|
def initialize(coupon: nil, maturity_date: nil, years: nil, coupon_frequency: nil, coupon_payment_dates: nil, face_value: 100, days_of_year: 365)
|
16
17
|
raise "Invalid coupon #{coupon}" if (coupon.nil? || !is_number?(coupon))
|
17
18
|
raise "Invalid maturity_date #{maturity_date}" if (maturity_date.nil? || maturity_date.methods.include?("strftime"))
|
18
|
-
raise "Invalid years #{years}" if (years.nil? || !
|
19
|
+
raise "Invalid years #{years}" if (years.nil? || !is_number?(years))
|
19
20
|
raise "Invalid coupon_frequency #{days_of_year}" if (coupon_frequency.nil? || !is_number?(coupon))
|
20
21
|
raise "Invalid coupon_payment_dates #{coupon_payment_dates}" if (coupon_payment_dates.nil? || coupon_payment_dates.count == 0 || coupon_payment_dates.any? { |date| date > maturity_date })
|
21
22
|
raise "Invalid days_of_year #{days_of_year}" if (days_of_year != 365 && days_of_year != 360)
|
22
23
|
|
23
|
-
@coupon = coupon
|
24
|
+
@coupon = coupon.to_d
|
24
25
|
@maturity_date = maturity_date
|
25
26
|
@years = years
|
26
|
-
@coupon_frequency = coupon_frequency
|
27
|
-
@days_of_year = days_of_year
|
27
|
+
@coupon_frequency = coupon_frequency.to_d
|
28
|
+
@days_of_year = days_of_year.to_d
|
28
29
|
@face_value = face_value
|
29
30
|
@coupon_payment_dates = coupon_payment_dates.sort
|
30
|
-
@start_date = (@maturity_date
|
31
|
+
@start_date = (@maturity_date-(years*12).to_i.months)
|
31
32
|
end
|
32
33
|
|
33
34
|
def price(ytm, date, ex_coupon_date: nil, fees: 0)
|
34
35
|
# price_for_irr(irr_from_ytm(ytm), date, fees: fees)
|
35
36
|
price = price_for_irr(ytm/@coupon_frequency, date, ex_coupon_date: ex_coupon_date, fees: fees)
|
36
|
-
price
|
37
|
+
price
|
37
38
|
end
|
38
39
|
|
39
40
|
# def interest_payments(from_date)
|
@@ -67,7 +68,7 @@ module Istox
|
|
67
68
|
private
|
68
69
|
|
69
70
|
def is_365?
|
70
|
-
@days_of_year == 365
|
71
|
+
@days_of_year == 365.to_d
|
71
72
|
end
|
72
73
|
|
73
74
|
def add_month(mydate, n)
|
@@ -129,9 +130,9 @@ module Istox
|
|
129
130
|
if payment_dates.count == 0
|
130
131
|
# no more coupon payments, we do only 1 discount of accrued interest and face value to the current date
|
131
132
|
if is_365?
|
132
|
-
discount_factor = 1/(1+irr*(@maturity_date-date)/accrued_interest_discount_days)
|
133
|
+
discount_factor = 1.0/(1+irr*(@maturity_date-date)/accrued_interest_discount_days)
|
133
134
|
else
|
134
|
-
discount_factor = 1/(1+irr*@coupon_frequency*day_count_factor(date, @maturity_date, nil))
|
135
|
+
discount_factor = 1.0/(1+irr*@coupon_frequency*day_count_factor(date, @maturity_date, nil))
|
135
136
|
end
|
136
137
|
discounted_accrued_interest = @coupon*@face_value*accrued_interest_factor*discount_factor
|
137
138
|
discounted_face_value = @face_value*discount_factor
|
@@ -139,14 +140,15 @@ module Istox
|
|
139
140
|
else
|
140
141
|
# we discount face value and coupons and accrued interest (coupon) at maturity (if any)
|
141
142
|
# to the first coupon payment left, and then discount the total to current date
|
142
|
-
value_at_first_coupon = 0
|
143
|
+
value_at_first_coupon = 0.to_d
|
143
144
|
if payment_dates.include?(@maturity)
|
144
145
|
# last coupon payment is at maturity
|
145
|
-
discount_factor = 1/(1+irr)**(payment_dates.count-1)
|
146
|
+
discount_factor = 1.0/(1+irr)**(payment_dates.count-1)
|
146
147
|
value_at_first_coupon = @face_value*discount_factor
|
147
148
|
else
|
149
|
+
period = @coupon_payment_dates.include?(date) ? payment_dates.count : payment_dates.count-1
|
148
150
|
# last coupon is not at maturity, need to add accrued interest (coupon)
|
149
|
-
discount_factor = 1/((1+irr)**
|
151
|
+
discount_factor = 1.0/((1+irr)**period)/(1+irr*@coupon_frequency*accrued_interest_factor)
|
150
152
|
discounted_accrued_interest = @coupon*@face_value*accrued_interest_factor*discount_factor
|
151
153
|
discounted_face_value = @face_value*discount_factor
|
152
154
|
value_at_first_coupon = discounted_accrued_interest + discounted_face_value
|
@@ -154,26 +156,42 @@ module Istox
|
|
154
156
|
|
155
157
|
# first coupon can be pro-rata
|
156
158
|
value_at_first_coupon += first_coupon(date, ex_coupon_date)
|
157
|
-
if payment_dates.count
|
158
|
-
|
159
|
-
|
159
|
+
if payment_dates.count >= 1
|
160
|
+
if @coupon_payment_dates.include?(date)
|
161
|
+
# today is on one of the coupon payment date, the coupon will not be payed today
|
162
|
+
# discount all coupon payments to first coupon date
|
163
|
+
for n in 1..payment_dates.count do
|
164
|
+
value_at_first_coupon += @coupon*@face_value/@coupon_frequency/(1+irr)**n
|
165
|
+
end
|
166
|
+
else
|
167
|
+
# discount all coupon payments excluding first one to first coupon date
|
168
|
+
for n in 1..payment_dates.count-1 do
|
169
|
+
value_at_first_coupon += @coupon*@face_value/@coupon_frequency/(1+irr)**n
|
170
|
+
end
|
160
171
|
end
|
161
172
|
end
|
162
173
|
|
163
|
-
|
164
|
-
|
165
|
-
price = value_at_first_coupon
|
174
|
+
if @coupon_payment_dates.include?(date)
|
175
|
+
# today is one of the coupon payment, no need to discount
|
176
|
+
price = value_at_first_coupon
|
166
177
|
else
|
167
|
-
|
168
|
-
|
178
|
+
# discount value at first coupon date to present value
|
179
|
+
if is_365?
|
180
|
+
price = value_at_first_coupon/(1+irr*(payment_dates.first-date)/first_coupon_discount_days(date))/(1+fees)
|
181
|
+
else
|
182
|
+
price = value_at_first_coupon/(1+irr*@coupon_frequency*day_count_factor(date, payment_dates.first, nil))/(1+fees)
|
183
|
+
end
|
184
|
+
end
|
169
185
|
end
|
170
186
|
|
171
187
|
end
|
172
188
|
|
173
189
|
def first_coupon(date, ex_coupon_date)
|
174
|
-
if !ex_coupon_date.nil? && date > ex_coupon_date
|
190
|
+
if @coupon_payment_dates.include?(date) || (!ex_coupon_date.nil? && date > ex_coupon_date)
|
191
|
+
# if today is on coupon payment date, we won't include the coupon
|
192
|
+
# as by default it's passing ex_coupon_date in our calculation
|
175
193
|
# after ex coupon date, buyer won't receive next coupon
|
176
|
-
return 0
|
194
|
+
return 0.to_d
|
177
195
|
end
|
178
196
|
|
179
197
|
coupon = @coupon*@face_value/@coupon_frequency
|
@@ -223,7 +241,7 @@ module Istox
|
|
223
241
|
if d1 == 30
|
224
242
|
d2 = [d2, 30].min
|
225
243
|
end
|
226
|
-
(360*(date2.year - date1.year) + 30*(date2.month - date1.month) + d2 - d1)
|
244
|
+
(360*(date2.year - date1.year) + 30*(date2.month - date1.month) + d2 - d1)/@days_of_year
|
227
245
|
end
|
228
246
|
end
|
229
247
|
|
data/lib/istox/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: istox
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.87
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Siong Leng
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-09-
|
11
|
+
date: 2019-09-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bunny
|
@@ -299,7 +299,8 @@ files:
|
|
299
299
|
- lib/istox/helpers/gruf_listener_hook.rb
|
300
300
|
- lib/istox/helpers/message_service.rb
|
301
301
|
- lib/istox/helpers/my_open_struct.rb
|
302
|
-
- lib/istox/helpers/
|
302
|
+
- lib/istox/helpers/order_book_price_time.rb
|
303
|
+
- lib/istox/helpers/order_book_prorate.rb
|
303
304
|
- lib/istox/helpers/publisher.rb
|
304
305
|
- lib/istox/helpers/vault.rb
|
305
306
|
- lib/istox/interfaces/chainhub/transaction.rb
|
@@ -1,88 +0,0 @@
|
|
1
|
-
module Istox
|
2
|
-
class OrderBook
|
3
|
-
class << self
|
4
|
-
def allocation(soft_cap, total_supply, investments)
|
5
|
-
# sort by token price desc, and id asc
|
6
|
-
investments = investments.sort do |a, b|
|
7
|
-
[b[:token_price], a[:id]] <=> [a[:token_price], b[:id]]
|
8
|
-
end
|
9
|
-
|
10
|
-
total_supply = ::BigDecimal.new(total_supply.to_s)
|
11
|
-
|
12
|
-
interests = investments
|
13
|
-
cutoff_price = 0.0
|
14
|
-
total_bid_token = 0.0
|
15
|
-
total_allocated = 0.0
|
16
|
-
total_unallocated = 0.0
|
17
|
-
total_investment = 0.0
|
18
|
-
cutoff_price = ::Istox::FMath.round_up(::Istox::FMath.div(soft_cap, total_supply), 2)
|
19
|
-
is_cutoff = false
|
20
|
-
|
21
|
-
# return result immediately if no interest
|
22
|
-
unless interests.count.positive?
|
23
|
-
return { total_supply: total_supply.to_s, total_investment: '0',
|
24
|
-
cutoff_price: cutoff_price, total_bid_token: '0',
|
25
|
-
total_allocated: '0', total_unallocated: total_supply.to_s,
|
26
|
-
interests: [] }
|
27
|
-
end
|
28
|
-
|
29
|
-
interests = interests.map do |item|
|
30
|
-
bid_token = Istox::FMath.round_down(::Istox::FMath.div(item[:fiat_amount], item[:token_price]), 0)
|
31
|
-
total_bid_token = ::Istox::FMath.add(total_bid_token, bid_token)
|
32
|
-
allocated = 0.0
|
33
|
-
unallocated = bid_token
|
34
|
-
unless is_cutoff
|
35
|
-
allocated = bid_token
|
36
|
-
unallocated = 0.0
|
37
|
-
if ::BigDecimal.new(::Istox::FMath.add(total_allocated, bid_token)) >= total_supply
|
38
|
-
unallocated = ::Istox::FMath.sub(::Istox::FMath.add(total_allocated, bid_token), total_supply)
|
39
|
-
allocated = ::Istox::FMath.sub(bid_token, unallocated)
|
40
|
-
cutoff_price = item[:token_price]
|
41
|
-
is_cutoff = true
|
42
|
-
end
|
43
|
-
total_allocated = ::Istox::FMath.add(total_allocated, allocated)
|
44
|
-
total_unallocated = ::Istox::FMath.add(total_unallocated, unallocated)
|
45
|
-
end
|
46
|
-
{
|
47
|
-
id: item[:id],
|
48
|
-
# user bidding total fiat amount
|
49
|
-
fiat_amount: item[:fiat_amount],
|
50
|
-
# user bidding price
|
51
|
-
bid_price: item[:token_price],
|
52
|
-
# total user bidding token derived from fiat_amount / token_price
|
53
|
-
bid_token: bid_token,
|
54
|
-
# round down allocated token to whole number
|
55
|
-
allocated: ::Istox::FMath.round_down(allocated, 0),
|
56
|
-
# round up unallocated token to whole number
|
57
|
-
unallocated: ::Istox::FMath.round_up(unallocated, 0)
|
58
|
-
}
|
59
|
-
end
|
60
|
-
|
61
|
-
interests = interests.map do |item|
|
62
|
-
investment = ::Istox::FMath.mul(item[:allocated], cutoff_price)
|
63
|
-
total_investment = ::Istox::FMath.add(total_investment, investment)
|
64
|
-
|
65
|
-
# each user maximum total investment amount in fiat
|
66
|
-
item.merge!(investment: investment)
|
67
|
-
end
|
68
|
-
|
69
|
-
{
|
70
|
-
# total token supply
|
71
|
-
total_supply: total_supply.to_s,
|
72
|
-
# total investment in fiat
|
73
|
-
total_investment: total_investment,
|
74
|
-
# cut off price
|
75
|
-
cutoff_price: cutoff_price.to_s,
|
76
|
-
# total bid tokens, more than total supply for oversubscribe case
|
77
|
-
total_bid_token: total_bid_token,
|
78
|
-
# total allocated tokens
|
79
|
-
total_allocated: total_allocated,
|
80
|
-
# total unallocated tokens
|
81
|
-
total_unallocated: total_unallocated,
|
82
|
-
# all investor interest results
|
83
|
-
interests: interests
|
84
|
-
}
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|