trades 0.2.1 → 1.0
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.
- data/bin/trades +50 -5
- data/lib/trades.rb +2 -54
- data/lib/trades/options.rb +97 -0
- data/lib/trades/trade.rb +35 -0
- data/tests/trade.rb +37 -0
- metadata +55 -48
data/bin/trades
CHANGED
@@ -1,9 +1,54 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require 'trades'
|
2
|
+
# encoding: utf-8
|
4
3
|
|
5
4
|
begin
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
require 'bundler/setup'
|
6
|
+
require 'colored'
|
7
|
+
require 'optparse'
|
8
|
+
require 'socket'
|
9
|
+
require 'trades'
|
10
|
+
|
11
|
+
options = TradeOptions.parse(ARGV)
|
12
|
+
|
13
|
+
# Connect to Bitcoincharts.com
|
14
|
+
puts "Connecting to bitcoincharts.com..."
|
15
|
+
if socket = TCPSocket.open("bitcoincharts.com", 27007)
|
16
|
+
puts "Connected.\n".green
|
17
|
+
puts "Market | Time | Currency | Price | Volume"
|
18
|
+
|
19
|
+
options.logger.info "Trades has been started.\n" if options.log
|
20
|
+
end
|
21
|
+
|
22
|
+
last_price = {}
|
23
|
+
|
24
|
+
while data = socket.gets do
|
25
|
+
trade = Trade.new data
|
26
|
+
|
27
|
+
# Check if the trade should be displayed
|
28
|
+
if options.filter == :markets && options.markets.length > 0
|
29
|
+
next unless options.markets.include? trade.market
|
30
|
+
end
|
31
|
+
if options.filter == :currencies && options.currencies.length > 0
|
32
|
+
next unless options.currencies.include? trade.currency
|
33
|
+
end
|
34
|
+
|
35
|
+
# Compare price of this trade with last trade
|
36
|
+
unless last_price[trade.market].nil?
|
37
|
+
price = trade.price # we need to save an uncolored copy later
|
38
|
+
trade.price.green if price > last_price[trade.market]
|
39
|
+
trade.price.red if price < last_price[trade.market]
|
40
|
+
last_price[trade.market] = price
|
41
|
+
end
|
42
|
+
|
43
|
+
puts trade
|
44
|
+
options.logger.info trade if options.log
|
45
|
+
end
|
46
|
+
rescue Interrupt
|
47
|
+
puts " Disconnecting..."
|
48
|
+
|
49
|
+
options.logger.info "\nTrades has been closed.\n\n" if options.log
|
50
|
+
rescue OptionParser::ParseError => error
|
51
|
+
puts "Something went wrong".red + "\n#{error}\n\nType 'trades -h' for help"
|
52
|
+
rescue => error
|
53
|
+
puts "Disconnected: #{error}"
|
9
54
|
end
|
data/lib/trades.rb
CHANGED
@@ -1,54 +1,2 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require 'json'
|
4
|
-
require 'colored'
|
5
|
-
|
6
|
-
class Trades
|
7
|
-
def initialize(args = [])
|
8
|
-
begin
|
9
|
-
puts "Connecting to bitcoincharts.com..."
|
10
|
-
@socket = TCPSocket.open("bitcoincharts.com", 27007)
|
11
|
-
@markets = []
|
12
|
-
@last_prices = {}
|
13
|
-
|
14
|
-
# Convert the market names to lowercase
|
15
|
-
# (CHANGE THIS TO CASE INSENSITIVE REGEX)
|
16
|
-
args.each do |x| @markets.push(x.downcase) end
|
17
|
-
args.clear # no need to keep them in memory
|
18
|
-
|
19
|
-
# thUSD | 2011-06-27 23:00:55 | USD | 17.05 | 0.5 |
|
20
|
-
puts "Exchange | Date and time | Currency | Price | Volume |"
|
21
|
-
|
22
|
-
self.loop
|
23
|
-
rescue
|
24
|
-
puts "Error: #{$!}"
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def loop
|
29
|
-
while data = @socket.gets do
|
30
|
-
if data.length > 0 then
|
31
|
-
j = JSON.parse(data.strip.tr("\x00", '')) # Delete all \x00 weirdness
|
32
|
-
|
33
|
-
if @markets.empty? or @markets.include?(j['symbol'].downcase) then
|
34
|
-
line = j['symbol'].ljust(15) + "| " # market
|
35
|
-
line += Time.at(j['timestamp']).strftime("%Y-%m-%d %H:%M:%S") + " | "
|
36
|
-
line += j['currency'].ljust(9) + "| " # currency
|
37
|
-
|
38
|
-
# Price: display higher price in green / lower price in red
|
39
|
-
current_price = j['price'].round(2)
|
40
|
-
last_price = @last_prices[j['symbol']].to_f # Its nil on startup
|
41
|
-
@last_prices[j['symbol']] = current_price # store for next comparison
|
42
|
-
|
43
|
-
price = current_price.to_s.ljust(9)
|
44
|
-
price = price.green if current_price > last_price unless last_price.zero?
|
45
|
-
price = price.red if current_price < last_price
|
46
|
-
|
47
|
-
line += price + "| " + j['volume'].round(2).to_s.ljust(9) + " |"
|
48
|
-
|
49
|
-
puts line
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
1
|
+
require 'trades/trade'
|
2
|
+
require 'trades/options'
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
class TradeOptions
|
5
|
+
# Define markets, market aliases and currencies
|
6
|
+
MARKETS = %w[mtgoxUSD thUSD mtgoxEUR virwoxSLL mtgoxGBP intrsngGBP
|
7
|
+
virtexCAD btceUSD cbxUSD btcexUSD btcdeEUR wbxAUD mtgoxRUB bitmarketEUR
|
8
|
+
mtgoxPLN thEUR mtgoxCAD intrsngUSD mtgoxAUD intrsngEUR btcnCNY thAUD
|
9
|
+
bitstampUSD mtgoxHKD intsngPLN bitmarketUSD bitnzNZD thLRUSD thINR rockSSL
|
10
|
+
mtgoxSGD b2cUSD aqoinEUR mtgoxSEK ruxumUSD thCLP mrcdBRL mtgoxCNY mtgoxCHF
|
11
|
+
mtgoxDKK bitfloorUSD ruxumJPY bitmarketAUD mtgoxJPY ruxumZAR bitmarketPLN
|
12
|
+
ruxumSEK ruxumRUB rockEUR mtgoxNZD rockUSD ruxumCHF bbmBRL globalPLN
|
13
|
+
globalGBP bitmarketRUB ruxumHUF mtgoxTHB ruxumAUD ruxumEUR globalEUR
|
14
|
+
bitmarketGBP ruxumGBP ruxumPLN ruxumSGD ruxumTHB ruxumUAH globalUSD]
|
15
|
+
ALIASES = {"mtgox" => "mtgoxUSD", "th" => "thUSD", "tradehill" => "thUSD"}
|
16
|
+
CURRENCIES = {
|
17
|
+
"usd" => "USD", "dollar" => "USD", "us" => "USD",
|
18
|
+
"eur" => "EUR", "euro" => "EUR", "euros" => "EUR",
|
19
|
+
"gbp" => "GBP", "pound" => "GBP",
|
20
|
+
"ssl" => "SSL", "secondlife" => "SSL",
|
21
|
+
"cny" => "CNY",
|
22
|
+
"cad" => "CAD", "canadian" => "CAD",
|
23
|
+
"aud" => "AUD", "australian" => "AUD", "aus" => "AUD",
|
24
|
+
"rub" => "RUB", "russian" => "RUB", "rubble" => "RUB",
|
25
|
+
"pln" => "PLN", "polish" => "PLN",
|
26
|
+
"hkd" => "HKD", "hongkong" => "HKD",
|
27
|
+
"nzd" => "NZD", "newzealand" => "NZD", "nz" => "NZD",
|
28
|
+
"sgd" => "SGD",
|
29
|
+
"sek" => "SEK",
|
30
|
+
"clp" => "CLP",
|
31
|
+
"brl" => "BRL",
|
32
|
+
"chf" => "CHF",
|
33
|
+
"dkk" => "DKK",
|
34
|
+
"jpy" => "JPY", "yen" => "JPY", "japan" => "JPY",
|
35
|
+
"zar" => "ZAR",
|
36
|
+
"huf" => "HUF",
|
37
|
+
"thb" => "THB",
|
38
|
+
"uah" => "UAH"
|
39
|
+
}
|
40
|
+
|
41
|
+
def self.parse(args)
|
42
|
+
# Default options
|
43
|
+
options = OpenStruct.new
|
44
|
+
options.filter = :markets
|
45
|
+
options.markets = []
|
46
|
+
options.currencies = []
|
47
|
+
|
48
|
+
opts = OptionParser.new do |opts|
|
49
|
+
opts.banner = "Usage: trades [options]\n\n"
|
50
|
+
|
51
|
+
opts.separator "Options:"
|
52
|
+
|
53
|
+
# Filter on market
|
54
|
+
opts.on("-m", "--market MARKET", MARKETS, ALIASES,
|
55
|
+
"Filter on MARKET") do |m|
|
56
|
+
options.markets << m # << because of multiple currencies
|
57
|
+
end
|
58
|
+
|
59
|
+
# Filter on currency
|
60
|
+
opts.on("-c", "--currency CURRENCY", CURRENCIES.values.uniq, CURRENCIES,
|
61
|
+
"Filter on CURRENCY") do |c|
|
62
|
+
options.filter = :currencies # default is :markets
|
63
|
+
options.currencies << c # << because of multiple currencies
|
64
|
+
end
|
65
|
+
|
66
|
+
# Logging
|
67
|
+
opts.on("-l", "--log [FILENAME]", "Log the results to FILENAME") do |file|
|
68
|
+
file = "/var/log/trades.log" if file.nil?
|
69
|
+
options.log = true
|
70
|
+
options.logger = Logger.new File.new(file, 'a+')
|
71
|
+
options.logger.formatter = proc { |s, d, p, m| "#{m}\n" }
|
72
|
+
end
|
73
|
+
|
74
|
+
# Help
|
75
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
76
|
+
puts "#{opts}\n\nMore info: https://github.com/britishtea/Trades"
|
77
|
+
exit
|
78
|
+
end
|
79
|
+
|
80
|
+
# List of markets
|
81
|
+
opts.on_tail("--markets", "Show a list of all markets") do
|
82
|
+
puts "Markets: #{MARKETS.join ', '}\n\nAliases:"
|
83
|
+
ALIASES.each { |x,y| puts "#{x} for #{y}" }
|
84
|
+
exit
|
85
|
+
end
|
86
|
+
|
87
|
+
# List of currencies
|
88
|
+
opts.on_tail("--currencies", "Show a list of all currencies") do
|
89
|
+
puts "Currencies: #{CURRENCIES.values.uniq.join ', '}"
|
90
|
+
exit
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
opts.parse!(args)
|
95
|
+
options
|
96
|
+
end
|
97
|
+
end
|
data/lib/trades/trade.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
class Trade
|
4
|
+
attr_reader :id
|
5
|
+
attr_reader :market
|
6
|
+
attr_reader :timestamp
|
7
|
+
attr_reader :currency
|
8
|
+
attr_reader :volume
|
9
|
+
|
10
|
+
attr_accessor :price
|
11
|
+
|
12
|
+
def initialize(data)
|
13
|
+
parsed = JSON.parse data.strip.tr("\x00", '')
|
14
|
+
@id = parsed['id']
|
15
|
+
@market = parsed['symbol']
|
16
|
+
@timestamp = parsed['timestamp']
|
17
|
+
@currency = parsed['symbol'][-3, 3]
|
18
|
+
@price = parsed['price']
|
19
|
+
@volume = parsed['volume']
|
20
|
+
end
|
21
|
+
|
22
|
+
def timestamp(show_date = false)
|
23
|
+
format = show_date ? "%Y-%m-%d %H:%M:%S" : "%H:%M:%S"
|
24
|
+
Time.at(@timestamp).strftime(format)
|
25
|
+
end
|
26
|
+
|
27
|
+
def price(decimals = 4)
|
28
|
+
@price.round(decimals)
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_s
|
32
|
+
"#{@market.ljust(15)} | #{timestamp()} | #{@currency.ljust(9)} | " +
|
33
|
+
"#{price().to_s.ljust(9)} | #{@volume.to_s}"
|
34
|
+
end
|
35
|
+
end
|
data/tests/trade.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'colored'
|
3
|
+
require 'minitest/spec'
|
4
|
+
require 'minitest/autorun'
|
5
|
+
require 'trades/trade'
|
6
|
+
|
7
|
+
describe Trade do
|
8
|
+
before do
|
9
|
+
json = '{"volume": 1.26934, "timestamp": 1321111130,
|
10
|
+
"price": 2.998, "symbol": "btceUSD", "id": 15366282}'
|
11
|
+
@trade = Trade.new(json)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should have attributes of the appropriate type" do
|
15
|
+
@trade.id.must_be_kind_of Integer
|
16
|
+
@trade.market.must_be_kind_of String
|
17
|
+
@trade.timestamp.must_be_kind_of String
|
18
|
+
@trade.currency.must_be_kind_of String
|
19
|
+
@trade.currency.length.must_be :==, 3
|
20
|
+
@trade.price.must_be_kind_of Float
|
21
|
+
@trade.volume.must_be_kind_of Float
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should have timestamps in the correct formats" do
|
25
|
+
@trade.timestamp(true).must_be :==, "2011-11-12 16:18:50"
|
26
|
+
@trade.timestamp(false).must_be :==, "16:18:50"
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should have an option to specify decimals for the price attribute" do
|
30
|
+
@trade.price(2).must_be :==, 3.00
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should output a nicely formatted string" do
|
34
|
+
regex = /(.{15}) \| ((\d{4}-\d{2}-\d{2} )?\d{2}:\d{2}:\d{2}) \| ([a-zA-z ]{9}) \| ([0-9 \.]{9}) \| ([0-9 \.]+)/i
|
35
|
+
@trade.to_s.must_match regex
|
36
|
+
end
|
37
|
+
end
|
metadata
CHANGED
@@ -1,78 +1,85 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: trades
|
3
|
-
version: !ruby/object:Gem::Version
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '1.0'
|
4
5
|
prerelease:
|
5
|
-
version: 0.2.1
|
6
6
|
platform: ruby
|
7
|
-
authors:
|
7
|
+
authors:
|
8
8
|
- Waxjar
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
name: json
|
18
|
-
prerelease: false
|
19
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
12
|
+
date: 2011-11-16 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: &2156540440 !ruby/object:Gem::Requirement
|
20
17
|
none: false
|
21
|
-
requirements:
|
18
|
+
requirements:
|
22
19
|
- - ~>
|
23
|
-
- !ruby/object:Gem::Version
|
24
|
-
version: 1.
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.0'
|
25
22
|
type: :runtime
|
26
|
-
|
27
|
-
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2156540440
|
25
|
+
- !ruby/object:Gem::Dependency
|
28
26
|
name: colored
|
27
|
+
requirement: &2156539960 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '1.2'
|
33
|
+
type: :runtime
|
29
34
|
prerelease: false
|
30
|
-
|
35
|
+
version_requirements: *2156539960
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: json
|
38
|
+
requirement: &2156562520 !ruby/object:Gem::Requirement
|
31
39
|
none: false
|
32
|
-
requirements:
|
40
|
+
requirements:
|
33
41
|
- - ~>
|
34
|
-
- !ruby/object:Gem::Version
|
35
|
-
version:
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.6.1
|
36
44
|
type: :runtime
|
37
|
-
|
38
|
-
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *2156562520
|
47
|
+
description: Displays the trading data that Bitcoincharts monitors nicely
|
39
48
|
email:
|
40
|
-
executables:
|
49
|
+
executables:
|
41
50
|
- trades
|
42
51
|
extensions: []
|
43
|
-
|
44
52
|
extra_rdoc_files: []
|
45
|
-
|
46
|
-
files:
|
53
|
+
files:
|
47
54
|
- lib/trades.rb
|
55
|
+
- lib/trades/options.rb
|
56
|
+
- lib/trades/trade.rb
|
57
|
+
- tests/trade.rb
|
48
58
|
- bin/trades
|
49
|
-
|
50
|
-
homepage:
|
59
|
+
homepage: https://github.com/britishtea/Trades
|
51
60
|
licenses: []
|
52
|
-
|
53
61
|
post_install_message:
|
54
62
|
rdoc_options: []
|
55
|
-
|
56
|
-
require_paths:
|
63
|
+
require_paths:
|
57
64
|
- lib
|
58
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
66
|
none: false
|
60
|
-
requirements:
|
61
|
-
- -
|
62
|
-
- !ruby/object:Gem::Version
|
63
|
-
version:
|
64
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ! '>='
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
72
|
none: false
|
66
|
-
requirements:
|
67
|
-
- -
|
68
|
-
- !ruby/object:Gem::Version
|
69
|
-
version:
|
70
|
-
requirements:
|
71
|
-
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
requirements:
|
78
|
+
- Ruby version 1.9.2 or higher
|
72
79
|
rubyforge_project:
|
73
|
-
rubygems_version: 1.
|
80
|
+
rubygems_version: 1.8.11
|
74
81
|
signing_key:
|
75
82
|
specification_version: 3
|
76
|
-
summary:
|
77
|
-
test_files:
|
78
|
-
|
83
|
+
summary: Bitcoin trading data on the command line
|
84
|
+
test_files:
|
85
|
+
- tests/trade.rb
|