Class: Mpex::Mpex
- Inherits:
-
Object
- Object
- Mpex::Mpex
- Defined in:
- lib/mpex/mpex.rb
Constant Summary collapse
- CONFIG_FILE_PATH =
File.join(Dir.home, ".mpex", "config.yaml")
- LOGFILE_PATH =
File.join(Dir.home, ".mpex", "response.log")
- TRADES_LOGFILE_PATH =
File.join(Dir.home, ".mpex", "trades.log")
Instance Method Summary collapse
- #book_formatted(stat) ⇒ Object
- #create_configfile_unless_exists ⇒ Object
- #cumulated_amounts(stat) ⇒ Object
- #cumulated_amounts_formatted(stat) ⇒ Object
- #decrypt(encrypted_data, opts) ⇒ Object
- #encrypt(signed_msg, opts) ⇒ Object
- #fetch_mpex_vwaps(url = nil, opts = nil) {|vwaps| ... } ⇒ Object
- #fetch_orderbook(opts = {}, &block) ⇒ Object
- #format_stat(stat) ⇒ Object
- #handle_answer(encrypted_answer, opts) ⇒ Object
- #holdings_avg_value(stat, vwaps) ⇒ Object
- #holdings_formatted(stat) ⇒ Object
-
#initialize ⇒ Mpex
constructor
A new instance of Mpex.
- #list_proxies(&block) ⇒ Object
- #log_trade_histroy(stat) ⇒ Object
- #orders_sum(stat) ⇒ Object
- #orders_vwap_sum(stat, vwaps) ⇒ Object
- #passphrase_callback(hook, uid_hint, passphrase_info, prev_was_bad, fd) ⇒ Object
- #portfolio(opts, stat, &block) ⇒ Object
- #read_config ⇒ Object
- #send_plain(cleartext_command, opts, &block) ⇒ Object
- #sign(msg, opts) ⇒ Object
- #statjson(opts, parsed = true, &block) ⇒ Object
- #track_id(signed_msg) ⇒ Object
- #trade_history_formatted(stat) ⇒ Object
- #validate_mpsic(mpsic) ⇒ Object
- #verify(msg) ⇒ Object
- #verify_opts_present(opts, req_opts) ⇒ Object
Constructor Details
#initialize ⇒ Mpex
Returns a new instance of Mpex.
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/mpex/mpex.rb', line 16 def initialize @crypto = GPGME::Crypto.new(:armor => true) unless File.exist?(File.(LOGFILE_PATH)) dirname = File.dirname(File.(LOGFILE_PATH)) Dir.mkdir(dirname) unless Dir.exist?(dirname) end @logger = Logger.new(LOGFILE_PATH, 'daily') @logger.formatter = proc do |severity, datetime, progname, msg| "#{severity} Log entry @ #{datetime}:\n#{msg}\n\n" end @trades_log = Logger.new(TRADES_LOGFILE_PATH, 'daily') @trades_log.formatter = proc do |severity, datetime, progname, msg| "#{msg}\n" end end |
Instance Method Details
#book_formatted(stat) ⇒ Object
302 303 304 305 306 307 308 309 310 311 312 313 314 |
# File 'lib/mpex/mpex.rb', line 302 def book_formatted(stat) book = "" stat["Book"].sort_by { |o| o.keys.first == "md5Checksum" ? -1 : o[o.keys.first]["Price"].to_i }.each do |o| order_number = o.keys.first unless order_number == "md5Checksum" book << " #{o[order_number]["MPSIC"]}: #{o[order_number]["BS"]}\t" book << "#{o[order_number]["Quantity"]}\t@" book << "#{Converter.satoshi_to_btc(o[order_number]['Price'])}" book << "\t(order ##{order_number})\n" end end book end |
#create_configfile_unless_exists ⇒ Object
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 |
# File 'lib/mpex/mpex.rb', line 387 def create_configfile_unless_exists return if File.exist?(File.(CONFIG_FILE_PATH)) begin current_dir = File.dirname(File.(__FILE__)) sample_config_file = File.(File.join(current_dir, "..", "..", "config.yaml.sample")) unless File.exist?(File.(CONFIG_FILE_PATH)) mpex_dir = File.dirname(File.(CONFIG_FILE_PATH)) Dir.mkdir(mpex_dir) unless Dir.exist?(mpex_dir) end FileUtils.cp sample_config_file, File.(CONFIG_FILE_PATH) return YAML.load_file(File.(CONFIG_FILE_PATH)) rescue puts "WARN: no sample config file found!" end end |
#cumulated_amounts(stat) ⇒ Object
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
# File 'lib/mpex/mpex.rb', line 161 def cumulated_amounts(stat) cumulated_amounts = {} stat["Book"].each do |order| mpsic = order[order.keys.first]["MPSIC"] if (mpsic and order[order.keys.first]["BS"] == "S") cumulated_amounts[mpsic] = cumulated_amounts[mpsic].to_i + order[order.keys.first]["Quantity"].to_i elsif (mpsic and order[order.keys.first]["BS"] == "B") cumulated_amounts["CxBTC"] = cumulated_amounts["CxBTC"].to_i + order[order.keys.first]["Quantity"].to_i * order[order.keys.first]["Price"].to_i end end stat["Holdings"].each do |h| mpsic = h.keys.first cumulated_amounts[mpsic] = cumulated_amounts[mpsic].to_i + h[mpsic].to_i if (mpsic and mpsic!="md5Checksum") end cumulated_amounts end |
#cumulated_amounts_formatted(stat) ⇒ Object
178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/mpex/mpex.rb', line 178 def cumulated_amounts_formatted(stat) formatted = "" cumulated_amounts(stat).each do |mpsic, amount| if (mpsic == "CxBTC") formatted << " #{mpsic}:\t#{Converter.satoshi_to_btc(amount)}\n" else formatted << " #{mpsic}:\t#{amount}\n" end end formatted end |
#decrypt(encrypted_data, opts) ⇒ Object
56 57 58 59 60 61 62 |
# File 'lib/mpex/mpex.rb', line 56 def decrypt(encrypted_data, opts) return encrypted_data unless (encrypted_data.start_with?("-----BEGIN PGP MESSAGE-----")) decrypted = @crypto.decrypt(encrypted_data, { :passphrase_callback => method(:passphrase_callback) }) do |signature| raise "Signature could not be verified" unless signature.valid? end decrypted.to_s end |
#encrypt(signed_msg, opts) ⇒ Object
51 52 53 54 |
# File 'lib/mpex/mpex.rb', line 51 def encrypt(signed_msg, opts) encrypted = @crypto.encrypt(signed_msg, :recipients => opts[:mpexkeyid]) encrypted.to_s end |
#fetch_mpex_vwaps(url = nil, opts = nil) {|vwaps| ... } ⇒ Object
216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/mpex/mpex.rb', line 216 def fetch_mpex_vwaps(url=nil, opts=nil, &block) vwaps = "" if $IRC_LEAK && $IRC_LEAK.connected? $IRC_LEAK.vwap do |resp| vwaps = JSON.parse(resp) end else url = url ? url : verify_opts_present(opts, [ :url ])[:url] vwaps_raw = Http.get(url, "/mpex-vwap.php") vwaps = JSON.parse(vwaps_raw) end yield vwaps end |
#fetch_orderbook(opts = {}, &block) ⇒ Object
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
# File 'lib/mpex/mpex.rb', line 230 def fetch_orderbook(opts = {}, &block) orderbook = [] if $IRC_LEAK && $IRC_LEAK.connected? $IRC_LEAK.depth do |depth| orderbook = JSON.parse depth end else opts = verify_opts_present(opts, [ :url ]) orderbook_res = Http.get(opts[:url], "/mpex-mktdepth.php") orderbook_res = orderbook_res.start_with?("JurovP") ? orderbook_res.match(/JurovP\((.+)\)/)[1] : orderbook_res orderbook = JSON.parse(orderbook_res) end orderbook.each do |s| puts s.first s.last["S"].sort_by { |price, amount| -price }.each do |o| puts "SELL price: #{Converter.satoshi_to_btc(o.first)} amount: #{o.last}" end s.last["B"].sort_by { |price, amount| price }.each do |o| puts "BUY price: #{Converter.satoshi_to_btc(o.first)} amount: #{o.last}" end end end |
#format_stat(stat) ⇒ Object
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/mpex/mpex.rb', line 116 def format_stat(stat) header = {} stat["Header"].map { |h| header[h.keys.first] = h[h.keys.first] } <<-STAT Stats for #{header["Name"]} (fingerprint #{header["Fingerprint"]}) Issued at #{header["DateTime"]} (#{header["Microtime"]}) Holdings: #{holdings_formatted(stat)} To which add orders in the book fully paid in advance: #{book_formatted(stat)} Options Cover: #{stat["OptionsCover"].size > 1 ? stat["OptionsCover"] : ""} Futures Cover: #{stat["IMMCover"].size > 1 ? stat["IMMCover"] : ""} Excercises: #{stat["Exercises"].size > 1 ? stat["Exercises"] : ""} Your transactions since 1 hour before your last STAT: #{trade_history_formatted(stat)} Dividends: #{stat["Dividends"].size > 1 ? stat["Dividends"] : ""} Formatted STATJSON. If you want the original run 'plain STAT'. Logs can be found here: #{LOGFILE_PATH}. STAT end |
#handle_answer(encrypted_answer, opts) ⇒ Object
86 87 88 89 90 91 92 93 |
# File 'lib/mpex/mpex.rb', line 86 def handle_answer(encrypted_answer, opts) decrypted_response = decrypt(encrypted_answer, opts) @logger.info(decrypted_response) verified_response = verify(decrypted_response) verified_response end |
#holdings_avg_value(stat, vwaps) ⇒ Object
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 |
# File 'lib/mpex/mpex.rb', line 326 def holdings_avg_value(stat, vwaps) holdings_value = stat["Holdings"].map do |h| mpsic = h.keys.first if h["CxBTC"] h["CxBTC"].to_i elsif h[mpsic] && vwaps[mpsic] amount = h[mpsic].to_i avg_price = 0 if vwaps[mpsic]["1d"]["avg"].to_i > 0 avg_price = vwaps[mpsic]["1d"]["avg"].to_i elsif vwaps[mpsic]["7d"]["avg"].to_i > 0 avg_price = vwaps[mpsic]["7d"]["avg"].to_i elsif vwaps[mpsic]["30d"]["avg"].to_i > 0 avg_price = vwaps[mpsic]["30d"]["avg"].to_i end amount * avg_price else 0 end end.inject(:+) end |
#holdings_formatted(stat) ⇒ Object
316 317 318 319 320 321 322 323 324 |
# File 'lib/mpex/mpex.rb', line 316 def holdings_formatted(stat) holdings = "" stat["Holdings"].each do |h| mpsic = h.keys.first amount = mpsic == "CxBTC" ? Converter.satoshi_to_btc(h[h.keys.first]) : h[h.keys.first] holdings = holdings + " #{mpsic}: #{amount}\n" unless h.keys.first == "md5Checksum" end holdings end |
#list_proxies(&block) ⇒ Object
253 254 255 256 257 258 259 260 261 |
# File 'lib/mpex/mpex.rb', line 253 def list_proxies(&block) if $IRC_LEAK && $IRC_LEAK.connected? $IRC_LEAK.list_proxies do |proxies| yield proxies end else puts "This command only works when connected to irc. Type 'irc' to connect." end end |
#log_trade_histroy(stat) ⇒ Object
107 108 109 110 111 112 113 114 |
# File 'lib/mpex/mpex.rb', line 107 def log_trade_histroy(stat) stat["TradeHistory"].each do |t| unixtime = t.keys.first unless unixtime == "md5Checksum" @trades_log.info(t) end end end |
#orders_sum(stat) ⇒ Object
154 155 156 157 158 159 |
# File 'lib/mpex/mpex.rb', line 154 def orders_sum(stat) orders_value = stat["Book"].map do |order| order[order.keys.first]["Price"].to_i * order[order.keys.first]["Quantity"].to_i end.inject(:+) orders_value end |
#orders_vwap_sum(stat, vwaps) ⇒ Object
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/mpex/mpex.rb', line 190 def orders_vwap_sum(stat, vwaps) orders_value = stat["Book"].map do |order| if order[order.keys.first]["BS"] == "B" order[order.keys.first]["Price"].to_i * order[order.keys.first]["Quantity"].to_i elsif order[order.keys.first]["BS"] == "S" mpsic = order[order.keys.first]["MPSIC"] if vwaps[mpsic] avg_price = 0 if vwaps[mpsic]["1d"]["avg"].to_i > 0 avg_price = vwaps[mpsic]["1d"]["avg"].to_i elsif vwaps[mpsic]["7d"]["avg"].to_i > 0 avg_price = vwaps[mpsic]["7d"]["avg"].to_i elsif vwaps[mpsic]["30d"]["avg"].to_i > 0 avg_price = vwaps[mpsic]["30d"]["avg"].to_i end avg_price * order[order.keys.first]["Quantity"].to_i else 0 end else 0 end end.inject(:+) orders_value end |
#passphrase_callback(hook, uid_hint, passphrase_info, prev_was_bad, fd) ⇒ Object
363 364 365 366 367 368 369 370 371 372 373 374 375 376 |
# File 'lib/mpex/mpex.rb', line 363 def passphrase_callback(hook, uid_hint, passphrase_info, prev_was_bad, fd) $stderr.write("Passphrase for #{uid_hint}: ") $stderr.flush begin system('stty -echo') io = IO.for_fd(fd, 'w') io.puts(ask(''){|q| q.echo = false}) io.flush ensure (0 ... $_.length).each do |i| $_[i] = ?0 end if $_ system('stty echo') end $stderr.puts end |
#portfolio(opts, stat, &block) ⇒ Object
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 |
# File 'lib/mpex/mpex.rb', line 263 def portfolio(opts, stat, &block) return unless stat opts = verify_opts_present(opts, [ :url, :keyid, :mpexkeyid ]) fetch_mpex_vwaps(opts[:url]) do |vwaps| return unless vwaps holdings_value = holdings_avg_value(stat, vwaps) optimistic_value = holdings_value + orders_sum(stat) vwap_valuation = holdings_value + orders_vwap_sum(stat, vwaps) portfolio = <<-PORTFOLIO Holdings: #{holdings_formatted(stat)} Totals: Your optimistic valuation: #{Converter.satoshi_to_btc(optimistic_value)} VWAP valuation: #{Converter.satoshi_to_btc(vwap_valuation)} Holdings including those stuck in open orders: #{cumulated_amounts_formatted(stat)} PORTFOLIO yield portfolio end end |
#read_config ⇒ Object
378 379 380 381 382 383 384 385 |
# File 'lib/mpex/mpex.rb', line 378 def read_config begin return YAML.load_file(File.(CONFIG_FILE_PATH)) rescue create_configfile_unless_exists end return {} end |
#send_plain(cleartext_command, opts, &block) ⇒ Object
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/mpex/mpex.rb', line 64 def send_plain(cleartext_command, opts, &block) puts "Sending order to MPEX: #{cleartext_command}" opts = verify_opts_present(opts, [ :url, :keyid, :mpexkeyid ]) signed_msg = sign(cleartext_command, opts) say("<%= color('Track-ID: #{track_id(signed_msg)}', :blue) %>") encrypted_msg = encrypt(signed_msg, opts) if $IRC_LEAK && $IRC_LEAK.connected? $IRC_LEAK.send_encrypted(encrypted_msg.to_s) do |encr_answer| if encr_answer yield handle_answer(encr_answer, opts) end end else res = Http.post_form(opts[:url], { 'msg' => "#{encrypted_msg}" }) yield handle_answer(res, opts) end end |
#sign(msg, opts) ⇒ Object
32 33 34 35 36 37 |
# File 'lib/mpex/mpex.rb', line 32 def sign(msg, opts) = @crypto.sign(msg, { :signer => opts[:keyid], :passphrase_callback => method(:passphrase_callback), :mode => GPGME::SIG_MODE_CLEAR } ) verify() .to_s end |
#statjson(opts, parsed = true, &block) ⇒ Object
95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/mpex/mpex.rb', line 95 def statjson(opts, parsed=true, &block) send_plain('STATJSON', opts) do |statjson| stat = JSON.parse(statjson) log_trade_histroy(stat) if parsed yield stat else yield statjson end end end |
#track_id(signed_msg) ⇒ Object
149 150 151 152 |
# File 'lib/mpex/mpex.rb', line 149 def track_id(signed_msg) md5hex = Digest::MD5.hexdigest(signed_msg) md5hex[0..3] end |
#trade_history_formatted(stat) ⇒ Object
290 291 292 293 294 295 296 297 298 299 300 |
# File 'lib/mpex/mpex.rb', line 290 def trade_history_formatted(stat) history = "" stat["TradeHistory"].each do |t| unixtime = t.keys.first total = t[unixtime]["Quantity"].to_i * t[unixtime]["Price"].to_i unless unixtime == "md5Checksum" history << " #{Time.at(unixtime.to_i)} #{t[unixtime]["MPSIC"]} - #{t[unixtime]["Quantity"]} #{t[unixtime]["BS"] == "S" ? "sold" : "bought"} @#{Converter.satoshi_to_btc(t[unixtime]["Price"])}, total: #{Converter.satoshi_to_btc(total)}\n" end end history end |
#validate_mpsic(mpsic) ⇒ Object
141 142 143 144 145 146 147 |
# File 'lib/mpex/mpex.rb', line 141 def validate_mpsic(mpsic) if mpsic.match(/^\w\./) return mpsic else raise "invalid MPSIC #{mpsic}" end end |
#verify(msg) ⇒ Object
39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/mpex/mpex.rb', line 39 def verify(msg) verified_plain_msg = @crypto.verify(msg) do |signature| if signature.valid? say("<%= color('#{signature}', :green) %>") else say("<%= color('WARNING', :red) %>: Invalid signature! Don't trust!") raise "WARNING: Invalid signature! Don't trust!" end end verified_plain_msg.to_s end |
#verify_opts_present(opts, req_opts) ⇒ Object
348 349 350 351 352 353 354 355 356 357 358 359 360 361 |
# File 'lib/mpex/mpex.rb', line 348 def verify_opts_present(opts, req_opts) config_opts = read_config req_opts.each do |r_opt| unless opts[r_opt] if config_opts[r_opt.to_s] opts[r_opt] = config_opts[r_opt.to_s] else $stderr.puts "--#{r_opt} option is required" exit 1 end end end opts end |