hashrate 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/hashrate.rb +248 -0
  2. metadata +79 -0
@@ -0,0 +1,248 @@
1
+ require 'open-uri'
2
+ require 'json'
3
+ require 'linefit'
4
+
5
+ class Hashrate
6
+ BTC_PER_BLOCK = 25
7
+ MH = 1e6
8
+ GH = 1e9
9
+ TH = 1e12
10
+
11
+ # Calculate expected earnings for a bitcoin miner
12
+ # based on a timespan and hashrate.
13
+ #
14
+ # Example (six months ago to now with 100 GH/s):
15
+ # >> Hashrate.earning(Time.new.to_i - (60 * 60 * 24 * 30 * 6), Time.new.to_i, 1000 * Hashrate::GH)
16
+ # => 201.08229099734106
17
+ #
18
+ # Arguments:
19
+ # start: starting mining time
20
+ # stop: stopping mining time
21
+ # hashrate: rate of mining in hashes per second
22
+ def self.earning(start, stop, hashrate)
23
+ # make sure this is loaded before doing anything else
24
+ # (mmmm, spaghetti)
25
+ self.get_difficulties
26
+
27
+ # ensure start and stop are in the right format and order
28
+ start, stop = [start, stop].map{|t|
29
+ # convert datetime to time
30
+ t = t.to_time if t.respond_to? :to_time
31
+
32
+ # convert time to unix timestamp
33
+ t.to_i
34
+ }.sort
35
+
36
+ difficulty = self.average_difficulty(start, stop)
37
+ # puts "difficulty: #{difficulty}"
38
+ # puts "time: #{(stop-start)}"
39
+
40
+ # time to find one share between start and stop (in seconds)
41
+ # with your hashrate
42
+ time_for_one_share = (difficulty * 2**32 / hashrate)
43
+
44
+ # number of shares we'll find in (stop-start) time
45
+ expected_shares = (stop-start) / time_for_one_share
46
+
47
+ # difficulty_time(start, stop) * hashrate * BTC_PER_BLOCK
48
+ # btc_per_second = average_difficulty(start, stop) * BTC_PER_BLOCK / hashrate
49
+
50
+ expected_shares * self::BTC_PER_BLOCK
51
+ end
52
+
53
+ private
54
+
55
+ def self.get_difficulties
56
+ # if the data hasn't been fetched
57
+ # or the last time it was fetched is > 6 hours ago
58
+ # re-download the data
59
+ # (best used in Rails production mode when object
60
+ # instances are persistant)
61
+
62
+ if (defined?(@@data)).nil? ||
63
+ (defined?(@@difficulties)).nil? ||
64
+ (defined?(@@difficulties_updated)).nil? ||
65
+ (Time.now.to_i - @@difficulties_updated) > 60 * 60 * 6
66
+
67
+ url = "https://blockchain.info/charts/difficulty?showDataPoints=false&timespan=all&show_header=true&daysAverageString=1&scale=0&format=json&address="
68
+
69
+ # consolidate the json to just the dates the difficulty changed
70
+ # data = JSON.parse(File.read('difficulties.json'))
71
+ @@data = JSON.parse(open(url).read)
72
+ @@difficulties = []
73
+ @@data.values.first.each{|value|
74
+ if @@difficulties.empty? || @@difficulties.last["y"] != value["y"]
75
+ @@difficulties << value
76
+ end
77
+ }
78
+
79
+ # add 24 months in the future based on the last 12 months
80
+ @@difficulties += extrapolate(24, 12)
81
+
82
+ @@difficulties_updated = Time.now.to_i
83
+ end
84
+
85
+ @@difficulties
86
+ end
87
+
88
+ # the actual or expected (if in the future)
89
+ # difficulty at the unix timestamp time
90
+ def self.difficulty(time)
91
+ self.get_difficulties[difficulty_index(time)]
92
+ end
93
+
94
+ # returns the weighted average difficulty between start and stop
95
+ def self.average_difficulty(start, stop)
96
+ # puts "> average_difficulty(#{start}, #{stop})"
97
+
98
+ difficulties = self.difficulties_between(start, stop)
99
+
100
+ # print "difficulties: "
101
+ # p difficulties
102
+
103
+ total = 0
104
+
105
+ 0.upto(difficulties.size-2){|i|
106
+ difficulty = difficulties[i]
107
+ time = difficulties[i+1]["x"] - difficulty["x"]
108
+
109
+ total += difficulty["y"] * time
110
+ }
111
+
112
+ # return the average
113
+ total / (stop - start)
114
+ end
115
+
116
+
117
+ def self.extrapolate(months_ahead=6, months_ago=12)
118
+ # add in expected future values for the next 6 months
119
+ # based on the last 12 months
120
+ index = difficulty_index(Time.new.to_i - 60 * 60 * 24 * 30 * months_ago)
121
+ past = @@difficulties[index..-1]
122
+ x = past.map{|o| o["x"]}
123
+ y = past.map{|o| Math.log2 o["y"]}
124
+
125
+ lineFit = LineFit.new
126
+ lineFit.setData(x,y)
127
+
128
+ intercept, slope = lineFit.coefficients
129
+
130
+ # def y(x)
131
+ # 2 ** (@slope * x + @intercept)
132
+ # end
133
+
134
+ # {
135
+ # intercept: intercept,
136
+ # slope: slope,
137
+ # rSquared: lineFit.rSquared,
138
+ # meanSqError: lineFit.meanSqError
139
+ # }
140
+
141
+ # calculate average interval
142
+ intervals = []
143
+ 0.upto(past.size-2){|i|
144
+ intervals << past[i+1]["x"] - past[i]["x"]
145
+ }
146
+ average_interval = intervals.inject{|a,b| a+b}/intervals.size
147
+ # puts "average: #{average_interval}"
148
+
149
+ difficulties = []
150
+
151
+ t = past.last["x"]
152
+ future_date = t + 60 * 60 * 24 * 30 * months_ahead
153
+ while t < future_date
154
+ difficulties << {
155
+ "x" => t,
156
+ "y" => 2 ** (slope * t + intercept) # y(t)
157
+ }
158
+ t += average_interval
159
+ end
160
+
161
+ difficulties
162
+ end
163
+
164
+ def self.difficulty_index(time)
165
+ i = -1
166
+ t = 0
167
+ while t <= time
168
+ i += 1
169
+ t = @@difficulties[i]["x"]
170
+ end
171
+
172
+ i-1
173
+ end
174
+
175
+ # returns a set of points between, and including, start and stop
176
+ # filling in the values at start and stop so that you could sum the
177
+ # differences between the x values and it would == stop-start
178
+ def self.difficulties_between(start, stop)
179
+ # puts "> difficulties_between(#{start}, #{stop})"
180
+
181
+ start_i = self.difficulty_index(start)
182
+ stop_i = self.difficulty_index(stop)
183
+
184
+ # print "start_i: "
185
+ # p start_i
186
+ # print "stop_i: "
187
+ # p stop_i
188
+
189
+ difficulties = self.get_difficulties[start_i..stop_i]
190
+
191
+ return [] if difficulties.empty?
192
+
193
+ # clone the last entry with the `stop` x-value
194
+ difficulties << {"x" => stop, "y" => difficulties.last["y"]}
195
+
196
+ # bump up the first entry to `start`
197
+ difficulties.first["x"] = start
198
+
199
+ difficulties
200
+ end
201
+
202
+ end
203
+
204
+
205
+ # binding.pry
206
+
207
+ # exit
208
+
209
+
210
+
211
+ # difficulties_between(1231524905+1, 1231524905+86400) == [{"x"=>1231524906, "y"=>1.0}, {"x"=>1231611305, "y"=>1.0}]
212
+
213
+ # difficulties_between(1231524905+1, 1262196905 + 1) == [{"x"=>1231524906, "y"=>1.0}, {"x"=>1262196905, "y"=>1.1828995343128408}, {"x"=>1262196906, "y"=>1.1828995343128408}]
214
+
215
+
216
+
217
+
218
+ # p average_difficulty(1231524905+1, 1231524905+86400) = 1.0
219
+
220
+ # p average_difficulty(1231524905+1, 1262196905 + 1) == 1.0000000059630783
221
+
222
+ # p average_difficulty(1231524905+1, 1263320105) == 1.0064611250566535
223
+
224
+
225
+
226
+
227
+ # double check here: http://www.bitcoinx.com/profit/
228
+ # difficulty: 1
229
+ # BTC/block: 25
230
+ # hash rate: 1 MH/s
231
+ # Coins per 24h at these conditions == 502.9142 BTC
232
+ # p earning(1231524905, 1231524905+86400, 1 * MH) == 502.9141902923584
233
+
234
+
235
+ # t = @difficulties[-2]["x"]
236
+
237
+ # t = 1287339305
238
+ # p Hashrate.earning(t - (60 * 60 * 24 * 30), t - 1, 1000 * Hashrate::GH)
239
+
240
+ # binding.pry
241
+
242
+
243
+
244
+ # should be 502.9142, I think?
245
+
246
+ # binding.pry
247
+
248
+ # Time.at(seconds_since_epoc_integer).to_datetime
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hashrate
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Christian Genco
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-01-18 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: json
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>'
20
+ - !ruby/object:Gem::Version
21
+ version: '1.7'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>'
28
+ - !ruby/object:Gem::Version
29
+ version: '1.7'
30
+ - !ruby/object:Gem::Dependency
31
+ name: linefit
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 0.3.1
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 0.3.1
46
+ description: A calculator for expected bitcoin mining profit based on the future difficulty
47
+ of the blockchain
48
+ email: christian.genco@gmail.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - lib/hashrate.rb
54
+ homepage: https://github.com/christiangenco/hashrate
55
+ licenses:
56
+ - MIT
57
+ post_install_message:
58
+ rdoc_options: []
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ! '>='
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ requirements: []
74
+ rubyforge_project:
75
+ rubygems_version: 1.8.24
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: Bitcoin mining profit calculator
79
+ test_files: []