coinsync 0.2.0 → 0.2.1
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/lib/coinsync/config.rb +2 -7
- data/lib/coinsync/currency_conversion_task.rb +2 -2
- data/lib/coinsync/currency_converters/base.rb +8 -11
- data/lib/coinsync/currency_converters/exchangeratesapi.rb +14 -1
- data/lib/coinsync/currency_converters/nbp.rb +15 -1
- data/lib/coinsync/formatter.rb +2 -1
- data/lib/coinsync/importers/binance_api.rb +25 -16
- data/lib/coinsync/importers/bitbay20.rb +6 -3
- data/lib/coinsync/importers/bitbay_api.rb +17 -8
- data/lib/coinsync/importers/bitcurex.rb +4 -2
- data/lib/coinsync/importers/bitstamp_csv.rb +73 -0
- data/lib/coinsync/importers/bittrex_csv.rb +1 -1
- data/lib/coinsync/importers/default.rb +4 -1
- data/lib/coinsync/importers/kucoin_api.rb +11 -2
- data/lib/coinsync/outputs/list.rb +5 -1
- data/lib/coinsync/version.rb +1 -1
- metadata +24 -3
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: c0072156912886d0dda428c95b5421b230ce73bb
         | 
| 4 | 
            +
              data.tar.gz: 3cb523a19af17908648fad02113d63fea9c7cb47
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 57fe540967120d3926ad5ac5e7883aae9f49147b6e6eba82e0f4b1c38aa1ea54af70adfd1140545e75fa1255907b9bd4a6860094dd75f9f77e96d78d227a52ce
         | 
| 7 | 
            +
              data.tar.gz: 4aa898c645ca40f083720d22b517e7d3dc8c53ab8246bf977c5fbe00dccbc8188c7b44e34b220c119076b9597a373ba031e6c8bc5a047002c0923ea8b9f7d272
         | 
    
        data/lib/coinsync/config.rb
    CHANGED
    
    | @@ -1,4 +1,5 @@ | |
| 1 1 | 
             
            require 'ostruct'
         | 
| 2 | 
            +
            require 'tzinfo'
         | 
| 2 3 | 
             
            require 'yaml'
         | 
| 3 4 |  | 
| 4 5 | 
             
            require_relative 'currencies'
         | 
| @@ -28,8 +29,6 @@ module CoinSync | |
| 28 29 | 
             
                      require(File.expand_path(File.join(*directory, file)))
         | 
| 29 30 | 
             
                    end
         | 
| 30 31 | 
             
                  end
         | 
| 31 | 
            -
             | 
| 32 | 
            -
                  set_timezone(timezone) if timezone
         | 
| 33 32 | 
             
                end
         | 
| 34 33 |  | 
| 35 34 | 
             
                def sources
         | 
| @@ -55,10 +54,6 @@ module CoinSync | |
| 55 54 | 
             
                  Hash[included.map { |source| [source.key, source] }]
         | 
| 56 55 | 
             
                end
         | 
| 57 56 |  | 
| 58 | 
            -
                def set_timezone(timezone)
         | 
| 59 | 
            -
                  ENV['TZ'] = timezone
         | 
| 60 | 
            -
                end
         | 
| 61 | 
            -
             | 
| 62 57 | 
             
                def base_cryptocurrencies
         | 
| 63 58 | 
             
                  settings['base_cryptocurrencies'] || ['USDT', 'BTC', 'ETH', 'BNB', 'KCS', 'LTC', 'BCH', 'NEO']
         | 
| 64 59 | 
             
                end
         | 
| @@ -88,7 +83,7 @@ module CoinSync | |
| 88 83 | 
             
                end
         | 
| 89 84 |  | 
| 90 85 | 
             
                def timezone
         | 
| 91 | 
            -
                  settings['timezone']
         | 
| 86 | 
            +
                  settings['timezone'] && TZInfo::Timezone.get(settings['timezone'])
         | 
| 92 87 | 
             
                end
         | 
| 93 88 |  | 
| 94 89 | 
             
                def translate(label)
         | 
| @@ -26,7 +26,7 @@ module CoinSync | |
| 26 26 | 
             
                          BigDecimal.new(1),
         | 
| 27 27 | 
             
                          from: tx.bought_currency,
         | 
| 28 28 | 
             
                          to: @target_currency,
         | 
| 29 | 
            -
                           | 
| 29 | 
            +
                          time: tx.time
         | 
| 30 30 | 
             
                        )
         | 
| 31 31 | 
             
                        tx.converted.bought_amount = tx.bought_amount * tx.converted.exchange_rate
         | 
| 32 32 | 
             
                      else
         | 
| @@ -44,7 +44,7 @@ module CoinSync | |
| 44 44 | 
             
                          BigDecimal.new(1),
         | 
| 45 45 | 
             
                          from: tx.sold_currency,
         | 
| 46 46 | 
             
                          to: @target_currency,
         | 
| 47 | 
            -
                           | 
| 47 | 
            +
                          time: tx.time
         | 
| 48 48 | 
             
                        )
         | 
| 49 49 | 
             
                        tx.converted.sold_amount = tx.sold_amount * tx.converted.exchange_rate
         | 
| 50 50 | 
             
                      else
         | 
| @@ -23,20 +23,17 @@ module CoinSync | |
| 23 23 | 
             
                    @cache = Cache.new(self.class.name.downcase.split('::').last)
         | 
| 24 24 | 
             
                  end
         | 
| 25 25 |  | 
| 26 | 
            -
                  def convert(amount, from:, to:,  | 
| 26 | 
            +
                  def convert(amount, from:, to:, time:)
         | 
| 27 27 | 
             
                    (amount > 0) or raise "#{self.class}: amount should be positive"
         | 
| 28 28 | 
             
                    (amount.is_a?(BigDecimal)) or raise "#{self.class}: 'amount' should be a BigDecimal"
         | 
| 29 | 
            -
                    (from.is_a?(FiatCurrency)) or raise "#{self.class}: 'from' should be a FiatCurrency"
         | 
| 30 | 
            -
                    (to.is_a?(FiatCurrency)) or raise "#{self.class}: 'to' should be a FiatCurrency"
         | 
| 31 | 
            -
                    (date.is_a?(Date)) or raise "#{self.class}: 'date' should be a Date"
         | 
| 32 29 |  | 
| 33 | 
            -
                     | 
| 34 | 
            -
             | 
| 35 | 
            -
                     | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 39 | 
            -
                     | 
| 30 | 
            +
                    rate = get_conversion_rate(from: from, to: to, time: time)
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                    rate * amount
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  def get_conversion_rate(from:, to:, time:)
         | 
| 36 | 
            +
                    raise "not implemented"
         | 
| 40 37 | 
             
                  end
         | 
| 41 38 |  | 
| 42 39 | 
             
                  def finalize
         | 
| @@ -10,12 +10,23 @@ module CoinSync | |
| 10 10 | 
             
                  register_converter :exchangeratesapi
         | 
| 11 11 |  | 
| 12 12 | 
             
                  BASE_URL = "https://exchangeratesapi.io/api"
         | 
| 13 | 
            +
                  ECB_TIMEZONE = TZInfo::Timezone.get('Europe/Berlin')
         | 
| 13 14 |  | 
| 14 15 | 
             
                  class Exception < StandardError; end
         | 
| 15 16 | 
             
                  class NoDataException < Exception; end
         | 
| 16 17 | 
             
                  class BadRequestException < Exception; end
         | 
| 17 18 |  | 
| 18 | 
            -
                  def  | 
| 19 | 
            +
                  def get_conversion_rate(from:, to:, time:)
         | 
| 20 | 
            +
                    (from.is_a?(FiatCurrency)) or raise "#{self.class}: 'from' should be a FiatCurrency"
         | 
| 21 | 
            +
                    (to.is_a?(FiatCurrency)) or raise "#{self.class}: 'to' should be a FiatCurrency"
         | 
| 22 | 
            +
                    (time.is_a?(Time)) or raise "#{self.class}: 'time' should be a Time"
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    date = ECB_TIMEZONE.utc_to_local(time.utc).to_date
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                    if rate = @cache[from, to, date]
         | 
| 27 | 
            +
                      return rate
         | 
| 28 | 
            +
                    end
         | 
| 29 | 
            +
             | 
| 19 30 | 
             
                    response = Request.get("#{BASE_URL}/#{date}?base=#{from.code}")
         | 
| 20 31 |  | 
| 21 32 | 
             
                    case response
         | 
| @@ -24,6 +35,8 @@ module CoinSync | |
| 24 35 | 
             
                      rate = json['rates'][to.code.upcase]
         | 
| 25 36 | 
             
                      raise NoDataException.new("No exchange rate found for #{to.code.upcase}") if rate.nil?
         | 
| 26 37 |  | 
| 38 | 
            +
                      @cache[from, to, date] = rate
         | 
| 39 | 
            +
             | 
| 27 40 | 
             
                      return rate
         | 
| 28 41 | 
             
                    when Net::HTTPBadRequest
         | 
| 29 42 | 
             
                      raise BadRequestException.new(response)
         | 
| @@ -1,5 +1,6 @@ | |
| 1 1 | 
             
            require 'json'
         | 
| 2 2 | 
             
            require 'net/http'
         | 
| 3 | 
            +
            require 'tzinfo'
         | 
| 3 4 |  | 
| 4 5 | 
             
            require_relative 'base'
         | 
| 5 6 | 
             
            require_relative '../request'
         | 
| @@ -10,14 +11,25 @@ module CoinSync | |
| 10 11 | 
             
                  register_converter :nbp
         | 
| 11 12 |  | 
| 12 13 | 
             
                  BASE_URL = "https://api.nbp.pl/api"
         | 
| 14 | 
            +
                  POLISH_TIMEZONE = TZInfo::Timezone.get('Europe/Warsaw')
         | 
| 13 15 |  | 
| 14 16 | 
             
                  class Exception < StandardError; end
         | 
| 15 17 | 
             
                  class NoDataException < Exception; end
         | 
| 16 18 | 
             
                  class BadRequestException < Exception; end
         | 
| 17 19 |  | 
| 18 | 
            -
                  def  | 
| 20 | 
            +
                  def get_conversion_rate(from:, to:, time:)
         | 
| 21 | 
            +
                    (from.is_a?(FiatCurrency)) or raise "#{self.class}: 'from' should be a FiatCurrency"
         | 
| 22 | 
            +
                    (to.is_a?(FiatCurrency)) or raise "#{self.class}: 'to' should be a FiatCurrency"
         | 
| 23 | 
            +
                    (time.is_a?(Time)) or raise "#{self.class}: 'time' should be a Time"
         | 
| 24 | 
            +
             | 
| 19 25 | 
             
                    raise "Only conversions to PLN are supported" if to.code != 'PLN'
         | 
| 20 26 |  | 
| 27 | 
            +
                    date = POLISH_TIMEZONE.utc_to_local(time.utc).to_date
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                    if rate = @cache[from, to, date]
         | 
| 30 | 
            +
                      return rate
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
             | 
| 21 33 | 
             
                    response = Request.get("#{BASE_URL}/exchangerates/rates/a/#{from.code}/#{date - 8}/#{date - 1}/?format=json")
         | 
| 22 34 |  | 
| 23 35 | 
             
                    case response
         | 
| @@ -26,6 +38,8 @@ module CoinSync | |
| 26 38 | 
             
                      rate = json['rates'] && json['rates'].last && json['rates'].last['mid']
         | 
| 27 39 | 
             
                      raise NoDataException.new("No exchange rate found for #{from.code.upcase}") if rate.nil?
         | 
| 28 40 |  | 
| 41 | 
            +
                      @cache[from, to, date] = rate
         | 
| 42 | 
            +
             | 
| 29 43 | 
             
                      return rate
         | 
| 30 44 | 
             
                    when Net::HTTPBadRequest
         | 
| 31 45 | 
             
                      raise BadRequestException.new(response)
         | 
    
        data/lib/coinsync/formatter.rb
    CHANGED
    
    | @@ -33,7 +33,8 @@ module CoinSync | |
| 33 33 | 
             
                end
         | 
| 34 34 |  | 
| 35 35 | 
             
                def format_time(time)
         | 
| 36 | 
            -
                   | 
| 36 | 
            +
                  local_time = @config.timezone ? @config.timezone.utc_to_local(time.utc) : time
         | 
| 37 | 
            +
                  local_time.strftime(@config.time_format || '%Y-%m-%d %H:%M:%S')
         | 
| 37 38 | 
             
                end
         | 
| 38 39 |  | 
| 39 40 | 
             
                def parse_decimal(string)
         | 
| @@ -68,23 +68,30 @@ module CoinSync | |
| 68 68 | 
             
                    transactions = []
         | 
| 69 69 |  | 
| 70 70 | 
             
                    @traded_pairs.uniq.each do |pair|
         | 
| 71 | 
            -
                       | 
| 72 | 
            -
             | 
| 73 | 
            -
                       | 
| 74 | 
            -
             | 
| 75 | 
            -
             | 
| 76 | 
            -
             | 
| 77 | 
            -
                         | 
| 78 | 
            -
                           | 
| 71 | 
            +
                      lastId = 0
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                      loop do
         | 
| 74 | 
            +
                        response = make_request('/v3/myTrades', limit: 500, fromId: lastId + 1, symbol: pair)
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                        case response
         | 
| 77 | 
            +
                        when Net::HTTPSuccess
         | 
| 78 | 
            +
                          json = JSON.parse(response.body)
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                          if !json.is_a?(Array)
         | 
| 81 | 
            +
                            raise "Binance importer: Invalid response: #{response.body}"
         | 
| 82 | 
            +
                          elsif json.empty?
         | 
| 83 | 
            +
                            break
         | 
| 84 | 
            +
                          else
         | 
| 85 | 
            +
                            json.each { |tx| tx['symbol'] = pair }
         | 
| 86 | 
            +
                            lastId = json.map { |j| j['id'] }.sort.last
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                            transactions.concat(json)
         | 
| 89 | 
            +
                          end
         | 
| 90 | 
            +
                        when Net::HTTPBadRequest
         | 
| 91 | 
            +
                          raise "Binance importer: Bad request: #{response} (#{response.body})"
         | 
| 92 | 
            +
                        else
         | 
| 93 | 
            +
                          raise "Binance importer: Bad response: #{response}"
         | 
| 79 94 | 
             
                        end
         | 
| 80 | 
            -
             | 
| 81 | 
            -
                        json.each { |tx| tx['symbol'] = pair }
         | 
| 82 | 
            -
             | 
| 83 | 
            -
                        transactions.concat(json)
         | 
| 84 | 
            -
                      when Net::HTTPBadRequest
         | 
| 85 | 
            -
                        raise "Binance importer: Bad request: #{response} (#{response.body})"
         | 
| 86 | 
            -
                      else
         | 
| 87 | 
            -
                        raise "Binance importer: Bad response: #{response}"
         | 
| 88 95 | 
             
                      end
         | 
| 89 96 | 
             
                    end
         | 
| 90 97 |  | 
| @@ -187,6 +194,8 @@ module CoinSync | |
| 187 194 | 
             
                  private
         | 
| 188 195 |  | 
| 189 196 | 
             
                  def make_request(path, params = {}, signed = true)
         | 
| 197 | 
            +
                    print '.'
         | 
| 198 | 
            +
             | 
| 190 199 | 
             
                    if signed
         | 
| 191 200 | 
             
                      (@api_key && @secret_key) or raise "Public and secret API keys must be provided"
         | 
| 192 201 |  | 
| @@ -1,6 +1,7 @@ | |
| 1 1 | 
             
            require 'bigdecimal'
         | 
| 2 2 | 
             
            require 'csv'
         | 
| 3 3 | 
             
            require 'time'
         | 
| 4 | 
            +
            require 'tzinfo'
         | 
| 4 5 |  | 
| 5 6 | 
             
            require_relative 'base'
         | 
| 6 7 | 
             
            require_relative '../currencies'
         | 
| @@ -24,10 +25,12 @@ module CoinSync | |
| 24 25 | 
             
                  class HistoryEntry
         | 
| 25 26 | 
             
                    attr_accessor :date, :accounting_date, :type, :amount, :currency
         | 
| 26 27 |  | 
| 28 | 
            +
                    POLISH_TIMEZONE = TZInfo::Timezone.get('Europe/Warsaw')
         | 
| 29 | 
            +
             | 
| 27 30 | 
             
                    def initialize(line)
         | 
| 28 | 
            -
                       | 
| 29 | 
            -
                      @ | 
| 30 | 
            -
             | 
| 31 | 
            +
                      @date = POLISH_TIMEZONE.local_to_utc(Time.parse(line[0])) unless line[0] == '-'
         | 
| 32 | 
            +
                      @accounting_date = POLISH_TIMEZONE.local_to_utc(Time.parse(line[1])) unless line[1] == '-'
         | 
| 33 | 
            +
             | 
| 31 34 | 
             
                      @type = line[2]
         | 
| 32 35 |  | 
| 33 36 | 
             
                      amount, currency = line[3].split(' ')
         | 
| @@ -3,6 +3,7 @@ require 'json' | |
| 3 3 | 
             
            require 'net/http'
         | 
| 4 4 | 
             
            require 'openssl'
         | 
| 5 5 | 
             
            require 'time'
         | 
| 6 | 
            +
            require 'tzinfo'
         | 
| 6 7 | 
             
            require 'uri'
         | 
| 7 8 |  | 
| 8 9 | 
             
            require_relative 'base'
         | 
| @@ -22,14 +23,16 @@ module CoinSync | |
| 22 23 | 
             
                  OP_SALE = '-pay_for_currency'
         | 
| 23 24 | 
             
                  OP_FEE = '-fee'
         | 
| 24 25 |  | 
| 25 | 
            -
                  MAX_TIME_DIFFERENCE =  | 
| 26 | 
            +
                  MAX_TIME_DIFFERENCE = 5.0
         | 
| 26 27 | 
             
                  TRANSACTION_TYPES = [OP_PURCHASE, OP_SALE, OP_FEE]
         | 
| 27 28 |  | 
| 29 | 
            +
                  POLISH_TIMEZONE = TZInfo::Timezone.get('Europe/Warsaw')
         | 
| 30 | 
            +
             | 
| 28 31 | 
             
                  class HistoryEntry
         | 
| 29 32 | 
             
                    attr_accessor :date, :amount, :type, :currency
         | 
| 30 33 |  | 
| 31 34 | 
             
                    def initialize(hash)
         | 
| 32 | 
            -
                      @date = Time.parse(hash['time']) | 
| 35 | 
            +
                      @date = POLISH_TIMEZONE.local_to_utc(Time.parse(hash['time']))
         | 
| 33 36 | 
             
                      @amount = BigDecimal.new(hash['amount'])
         | 
| 34 37 | 
             
                      @type = hash['operation_type']
         | 
| 35 38 | 
             
                      @currency = parse_currency(hash['currency'])
         | 
| @@ -94,7 +97,8 @@ module CoinSync | |
| 94 97 | 
             
                    currencies.each do |currency|
         | 
| 95 98 | 
             
                      sleep 1  # rate limiting
         | 
| 96 99 |  | 
| 97 | 
            -
                       | 
| 100 | 
            +
                      # TODO: does this limit really work? (no way to test it really and docs don't mention a max value)
         | 
| 101 | 
            +
                      response = make_request('history', currency: currency, limit: 10000)
         | 
| 98 102 |  | 
| 99 103 | 
             
                      case response
         | 
| 100 104 | 
             
                      when Net::HTTPSuccess
         | 
| @@ -144,15 +148,17 @@ module CoinSync | |
| 144 148 |  | 
| 145 149 | 
             
                      next unless TRANSACTION_TYPES.include?(entry.type)
         | 
| 146 150 |  | 
| 147 | 
            -
                      if !matching.empty? | 
| 148 | 
            -
                         | 
| 151 | 
            +
                      if !matching.empty?
         | 
| 152 | 
            +
                        must_match = matching.any? { |e| (e.date - entry.date).abs > MAX_TIME_DIFFERENCE }
         | 
| 153 | 
            +
                        transaction = process_matched(matching, must_match)
         | 
| 154 | 
            +
                        transactions << transaction if transaction
         | 
| 149 155 | 
             
                      end
         | 
| 150 156 |  | 
| 151 157 | 
             
                      matching << entry
         | 
| 152 158 | 
             
                    end
         | 
| 153 159 |  | 
| 154 160 | 
             
                    if !matching.empty?
         | 
| 155 | 
            -
                      transactions << process_matched(matching)
         | 
| 161 | 
            +
                      transactions << process_matched(matching, true)
         | 
| 156 162 | 
             
                    end
         | 
| 157 163 |  | 
| 158 164 | 
             
                    transactions
         | 
| @@ -161,7 +167,7 @@ module CoinSync | |
| 161 167 |  | 
| 162 168 | 
             
                  private
         | 
| 163 169 |  | 
| 164 | 
            -
                  def process_matched(matching)
         | 
| 170 | 
            +
                  def process_matched(matching, must_match)
         | 
| 165 171 | 
             
                    if matching.length % 3 == 0
         | 
| 166 172 | 
             
                      purchases = matching.select { |tx| tx.type == OP_PURCHASE }
         | 
| 167 173 | 
             
                      sales = matching.select { |tx| tx.type == OP_SALE }
         | 
| @@ -186,7 +192,10 @@ module CoinSync | |
| 186 192 | 
             
                      end
         | 
| 187 193 | 
             
                    end
         | 
| 188 194 |  | 
| 189 | 
            -
                     | 
| 195 | 
            +
                    if must_match
         | 
| 196 | 
            +
                      raise "BitBay API importer error: Couldn't match some history lines: " +
         | 
| 197 | 
            +
                        matching.map { |m| "\n#{m.inspect}" }.join
         | 
| 198 | 
            +
                    end
         | 
| 190 199 | 
             
                  end
         | 
| 191 200 |  | 
| 192 201 | 
             
                  def make_request(method, params = {})
         | 
| @@ -1,6 +1,7 @@ | |
| 1 1 | 
             
            require 'bigdecimal'
         | 
| 2 2 | 
             
            require 'csv'
         | 
| 3 3 | 
             
            require 'time'
         | 
| 4 | 
            +
            require 'tzinfo'
         | 
| 4 5 |  | 
| 5 6 | 
             
            require_relative 'base'
         | 
| 6 7 | 
             
            require_relative '../currencies'
         | 
| @@ -14,11 +15,12 @@ module CoinSync | |
| 14 15 | 
             
                  class HistoryEntry
         | 
| 15 16 | 
             
                    attr_accessor :lp, :type, :date, :market, :amount, :price, :total, :fee, :fee_currency, :id
         | 
| 16 17 |  | 
| 18 | 
            +
                    POLISH_TIMEZONE = TZInfo::Timezone.get('Europe/Warsaw')
         | 
| 19 | 
            +
             | 
| 17 20 | 
             
                    def initialize(line)
         | 
| 18 | 
            -
                      # TODO: force parsing in Polish timezone
         | 
| 19 21 | 
             
                      @lp = line[0].to_i
         | 
| 20 22 | 
             
                      @type = line[1]
         | 
| 21 | 
            -
                      @date = Time.parse(line[2])
         | 
| 23 | 
            +
                      @date = POLISH_TIMEZONE.local_to_utc(Time.parse(line[2]))
         | 
| 22 24 | 
             
                      @market = FiatCurrency.new(line[3])
         | 
| 23 25 | 
             
                      @amount = BigDecimal.new(line[4])
         | 
| 24 26 | 
             
                      @price = BigDecimal.new(line[5].split(' ').first)
         | 
| @@ -0,0 +1,73 @@ | |
| 1 | 
            +
            require 'bigdecimal'
         | 
| 2 | 
            +
            require 'csv'
         | 
| 3 | 
            +
            require 'time'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            require_relative 'base'
         | 
| 6 | 
            +
            require_relative '../currencies'
         | 
| 7 | 
            +
            require_relative '../transaction'
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            module CoinSync
         | 
| 10 | 
            +
              module Importers
         | 
| 11 | 
            +
                class BitstampCSV < Base
         | 
| 12 | 
            +
                  register_importer :bitstamp_csv
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  class HistoryEntry
         | 
| 15 | 
            +
                    attr_accessor :lp, :type, :date, :market, :amount, :price, :total, :fee, :fee_currency, :id
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                    def initialize(line)
         | 
| 18 | 
            +
                      @type = line[0]
         | 
| 19 | 
            +
                      @date = Time.parse(line[1])
         | 
| 20 | 
            +
                      @account = line[2]
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                      
         | 
| 23 | 
            +
                      @lp = line[0].to_i
         | 
| 24 | 
            +
                      @market = FiatCurrency.new(line[3])
         | 
| 25 | 
            +
                      @amount = BigDecimal.new(line[4])
         | 
| 26 | 
            +
                      @price = BigDecimal.new(line[5].split(' ').first)
         | 
| 27 | 
            +
                      @total = BigDecimal.new(line[6].split(' ').first)
         | 
| 28 | 
            +
                      @fee = BigDecimal.new(line[7].split(' ').first)
         | 
| 29 | 
            +
                      @fee_currency = line[7].split(' ').last
         | 
| 30 | 
            +
                      @id = line[8].to_i
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  def read_transaction_list(source)
         | 
| 35 | 
            +
                    csv = CSV.new(source, col_sep: ',')
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                    transactions = []
         | 
| 38 | 
            +
                    bitcoin = CryptoCurrency.new('BTC')
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    csv.each do |line|
         | 
| 41 | 
            +
                      next if line.empty?
         | 
| 42 | 
            +
                      next if line[0] == 'Type'
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                      entry = HistoryEntry.new(line)
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                      if entry.type == 'Kup'
         | 
| 47 | 
            +
                        transactions << Transaction.new(
         | 
| 48 | 
            +
                          exchange: 'Bitcurex',
         | 
| 49 | 
            +
                          bought_currency: bitcoin,
         | 
| 50 | 
            +
                          sold_currency: entry.market,
         | 
| 51 | 
            +
                          time: entry.date,
         | 
| 52 | 
            +
                          bought_amount: entry.amount,
         | 
| 53 | 
            +
                          sold_amount: entry.total
         | 
| 54 | 
            +
                        )
         | 
| 55 | 
            +
                      elsif entry.type == 'Sprzedaj'
         | 
| 56 | 
            +
                        transactions << Transaction.new(
         | 
| 57 | 
            +
                          exchange: 'Bitcurex',
         | 
| 58 | 
            +
                          bought_currency: entry.market,
         | 
| 59 | 
            +
                          sold_currency: bitcoin,
         | 
| 60 | 
            +
                          time: entry.date,
         | 
| 61 | 
            +
                          bought_amount: entry.total,
         | 
| 62 | 
            +
                          sold_amount: entry.amount
         | 
| 63 | 
            +
                        )
         | 
| 64 | 
            +
                      else
         | 
| 65 | 
            +
                        raise "Bitcurex importer error: unexpected entry type '#{entry.type}'"
         | 
| 66 | 
            +
                      end
         | 
| 67 | 
            +
                    end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                    transactions.reverse
         | 
| 70 | 
            +
                  end
         | 
| 71 | 
            +
                end
         | 
| 72 | 
            +
              end
         | 
| 73 | 
            +
            end
         | 
| @@ -58,7 +58,7 @@ module CoinSync | |
| 58 58 | 
             
                        transactions << Transaction.new(
         | 
| 59 59 | 
             
                          exchange: 'Bittrex',
         | 
| 60 60 | 
             
                          time: entry.time_closed,
         | 
| 61 | 
            -
                          bought_amount: entry.price - entry.commission, | 
| 61 | 
            +
                          bought_amount: entry.price - entry.commission,
         | 
| 62 62 | 
             
                          bought_currency: entry.currency,
         | 
| 63 63 | 
             
                          sold_amount: entry.quantity,
         | 
| 64 64 | 
             
                          sold_currency: entry.asset
         | 
| @@ -68,7 +68,10 @@ module CoinSync | |
| 68 68 | 
             
                    entry.lp = line[0].to_i
         | 
| 69 69 | 
             
                    entry.exchange = line[1]
         | 
| 70 70 | 
             
                    entry.type = line[2]
         | 
| 71 | 
            -
             | 
| 71 | 
            +
             | 
| 72 | 
            +
                    time = Time.parse(line[3])
         | 
| 73 | 
            +
                    entry.date = @config.timezone ? @config.timezone.local_to_utc(time) : time
         | 
| 74 | 
            +
             | 
| 72 75 | 
             
                    entry.amount = @formatter.parse_decimal(line[4])
         | 
| 73 76 | 
             
                    entry.asset = CryptoCurrency.new(line[5])
         | 
| 74 77 | 
             
                    entry.total = @formatter.parse_decimal(line[6])
         | 
| @@ -43,7 +43,8 @@ module CoinSync | |
| 43 43 | 
             
                  end
         | 
| 44 44 |  | 
| 45 45 | 
             
                  def import_transactions(filename)
         | 
| 46 | 
            -
                     | 
| 46 | 
            +
                    # TODO: what if there's more than 100? (looks like we might need to switch to loading each market separately…)
         | 
| 47 | 
            +
                    response = make_request('/order/dealt', limit: 100)
         | 
| 47 48 |  | 
| 48 49 | 
             
                    case response
         | 
| 49 50 | 
             
                    when Net::HTTPSuccess
         | 
| @@ -128,8 +129,16 @@ module CoinSync | |
| 128 129 | 
             
                          sold_amount: entry.deal_value,
         | 
| 129 130 | 
             
                          sold_currency: entry.coin_type_pair
         | 
| 130 131 | 
             
                        )
         | 
| 132 | 
            +
                      elsif entry.direction == 'SELL'
         | 
| 133 | 
            +
                        transactions << Transaction.new(
         | 
| 134 | 
            +
                          exchange: 'Kucoin',
         | 
| 135 | 
            +
                          time: entry.created_at,
         | 
| 136 | 
            +
                          sold_amount: entry.amount,
         | 
| 137 | 
            +
                          sold_currency: entry.coin_type,
         | 
| 138 | 
            +
                          bought_amount: entry.deal_value - entry.fee,
         | 
| 139 | 
            +
                          bought_currency: entry.coin_type_pair
         | 
| 140 | 
            +
                        )
         | 
| 131 141 | 
             
                      else
         | 
| 132 | 
            -
                        # TODO sell
         | 
| 133 142 | 
             
                        raise "Kucoin importer error: unexpected entry direction '#{entry.direction}'"
         | 
| 134 143 | 
             
                      end
         | 
| 135 144 | 
             
                    end
         | 
| @@ -16,7 +16,7 @@ module CoinSync | |
| 16 16 | 
             
                      csv << headers
         | 
| 17 17 |  | 
| 18 18 | 
             
                      transactions.each do |tx|
         | 
| 19 | 
            -
                         | 
| 19 | 
            +
                        process_transaction(tx, csv)
         | 
| 20 20 | 
             
                      end
         | 
| 21 21 | 
             
                    end
         | 
| 22 22 | 
             
                  end
         | 
| @@ -45,6 +45,10 @@ module CoinSync | |
| 45 45 | 
             
                    line
         | 
| 46 46 | 
             
                  end
         | 
| 47 47 |  | 
| 48 | 
            +
                  def process_transaction(tx, csv)
         | 
| 49 | 
            +
                    csv << transaction_to_csv(tx)
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
             | 
| 48 52 | 
             
                  def transaction_to_csv(tx)
         | 
| 49 53 | 
             
                    if tx.purchase? || tx.sale?
         | 
| 50 54 | 
             
                      fiat_transaction_to_csv(tx)
         | 
    
        data/lib/coinsync/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,15 +1,35 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: coinsync
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.2. | 
| 4 | 
            +
              version: 0.2.1
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Kuba Suder
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2018-04- | 
| 12 | 
            -
            dependencies: | 
| 11 | 
            +
            date: 2018-04-26 00:00:00.000000000 Z
         | 
| 12 | 
            +
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: tzinfo
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - ">="
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: 1.2.5
         | 
| 20 | 
            +
                - - "<"
         | 
| 21 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 22 | 
            +
                    version: '2.0'
         | 
| 23 | 
            +
              type: :runtime
         | 
| 24 | 
            +
              prerelease: false
         | 
| 25 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 26 | 
            +
                requirements:
         | 
| 27 | 
            +
                - - ">="
         | 
| 28 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 29 | 
            +
                    version: 1.2.5
         | 
| 30 | 
            +
                - - "<"
         | 
| 31 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 32 | 
            +
                    version: '2.0'
         | 
| 13 33 | 
             
            description: 
         | 
| 14 34 | 
             
            email:
         | 
| 15 35 | 
             
            - jakub.suder@gmail.com
         | 
| @@ -45,6 +65,7 @@ files: | |
| 45 65 | 
             
            - lib/coinsync/importers/bitbay20.rb
         | 
| 46 66 | 
             
            - lib/coinsync/importers/bitbay_api.rb
         | 
| 47 67 | 
             
            - lib/coinsync/importers/bitcurex.rb
         | 
| 68 | 
            +
            - lib/coinsync/importers/bitstamp_csv.rb
         | 
| 48 69 | 
             
            - lib/coinsync/importers/bittrex_api.rb
         | 
| 49 70 | 
             
            - lib/coinsync/importers/bittrex_csv.rb
         | 
| 50 71 | 
             
            - lib/coinsync/importers/changelly.rb
         |