Finance-BTCIndo
view release on metacpan or search on metacpan
lib/Finance/BTCIndo.pm view on Meta::CPAN
package Finance::BTCIndo;
our $DATE = '2017-12-09'; # DATE
our $VERSION = '0.007'; # VERSION
use 5.010001;
use strict;
use warnings;
use Log::ger;
use Digest::SHA qw(hmac_sha512_hex);
use Time::HiRes qw(time);
my $url_prefix = "https://vip.bitcoin.co.id";
sub new {
my ($class, %args) = @_;
my $self = {};
if (my $key = delete $args{key}) {
$self->{key} = $key;
}
if (my $secret = delete $args{secret}) {
$self->{secret} = $secret;
}
if (keys %args) {
die "Unknown argument(s): ".join(", ", sort keys %args);
}
require HTTP::Tiny;
$self->{_http} = HTTP::Tiny->new;
require JSON::XS;
$self->{_json} = JSON::XS->new;
require URI::Encode;
$self->{_urienc} = URI::Encode->new;
bless $self, $class;
}
sub _get_json {
my ($self, $url) = @_;
log_trace("JSON API request: %s", $url);
my $res = $self->{_http}->get($url);
die "Can't retrieve $url: $res->{status} - $res->{reason}"
unless $res->{success};
my $decoded;
eval { $decoded = $self->{_json}->decode($res->{content}) };
die "Can't decode response from $url: $@" if $@;
log_trace("JSON API response: %s", $decoded);
$decoded;
}
sub tapi {
my ($self, $method, %args) = @_;
$self->{key} or die "Please supply API key in new()";
$self->{secret} or die "Please supply API secret in new()";
my $time = time();
my $form = {
%args,
method => $method,
# ms after 2015-01-01
nonce => int(1000 * (time() - 1_420_045_200)),
};
log_trace("TAPI request: %s", $form);
my $encoded_form = join(
"&",
map { $self->{_urienc}->encode($_ // ''). "=" .
$self->{_urienc}->encode($form->{$_} // '') }
sort keys(%$form),
);
my $options = {
headers => {
Key => $self->{key},
Sign => hmac_sha512_hex($encoded_form, $self->{secret}),
# XXX why do i have to do this manually?
"Content-Length" => length($encoded_form),
"Content-Type" => "application/x-www-form-urlencoded",
},
content => $encoded_form,
};
my $url = "$url_prefix/tapi/";
my $res = $self->{_http}->post($url, $options);
die "Can't retrieve $url: $res->{status} - $res->{reason}"
unless $res->{success};
my $decoded;
eval { $decoded = $self->{_json}->decode($res->{content}) };
die "Can't decode response from $url: $@" if $@;
log_trace("TAPI response: %s", $decoded);
die "API response not a hash: $decoded" unless ref $decoded eq 'HASH';
die "API response is not success: $decoded->{error}" unless $decoded->{success};
$decoded;
}
sub _check_pair {
my $pair = shift;
$pair =~ /\A(\w{3,5})_(\w{3,5})\z/
or die "Invalid pair: must be in the form of 'abc_xyz'";
}
sub get_ticker {
my ($self, %args) = @_;
$args{pair} //= "btc_idr";
_check_pair($args{pair});
$self->_get_json("$url_prefix/api/$args{pair}/ticker");
}
sub get_trades {
my ($self, %args) = @_;
$args{pair} //= "btc_idr";
_check_pair($args{pair});
$self->_get_json("$url_prefix/api/$args{pair}/trades");
}
sub get_depth {
my ($self, %args) = @_;
$args{pair} //= "btc_idr";
_check_pair($args{pair});
$self->_get_json("$url_prefix/api/$args{pair}/depth");
}
sub get_price_history {
my ($self, %args) = @_;
$args{pair} //= "btc_idr";
_check_pair($args{pair});
$args{period} //= 'day';
$args{period} =~ /\A(day|all)\z/
or die "Invalid period: must be day|all";
if ($args{period} eq 'all') {
$self->_get_json("$url_prefix/api/$args{pair}/chartdata");
} else {
$self->_get_json("$url_prefix/api/$args{pair}/chart_1d");
}
}
sub get_info {
my ($self, %args) = @_;
$self->tapi("getInfo");
}
sub get_tx_history {
my ($self, %args) = @_;
$self->tapi("transHistory");
}
sub get_trade_history {
my ($self, %args) = @_;
die "Please specify pair" unless $args{pair};
$self->tapi(
( run in 0.792 second using v1.01-cache-2.11-cpan-0bb4e1dffa6 )