The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/env perl
use common::sense;
use WebService::MtGox;
use Data::Dump 'pp';
use Ouch;
use Try::Tiny;
use Getopt::Long;

# allow us to repurpose the thai baht symbol for bitcoins
binmode STDOUT, ":utf8";

my $user;
my $password;

if (not -e "$ENV{HOME}/.mgrc") {
print qq{Config file not found.

To get started, you need to add your mtgox.com login credentials
to a file that only you can read like this:

  echo username >  ~/.mgrc
  echo password >> ~/.mgrc
  chmod 0400 ~/.mgrc

Of course, you should replace username and password with your
own credentials.

To get started, run:

  mg help

};
exit 1;
} else {
  open my $fh, '<', "$ENV{HOME}/.mgrc" or die $!;
  ($user, $password) = map { chomp; $_ } <$fh>;
}

sub sei { print @_, "\n" }

my $m = WebService::MtGox->new(
  user     => $user,
  password => $password,
);

my %actions; %actions = (

  ticker => sub {
    my $r = $m->get_ticker;
    sei pp($r);
  },

  depth => sub {
    my $r = $m->get_depth;
    my $bids = $r->{bids};
    my $asks = $r->{asks};
    for (reverse @$bids) {
      sei sprintf('%-20.8f%20.8f', @$_);
    }
    sei "-" x 40;
    for (@$asks) {
      sei sprintf('%-20.8f%20.8f', @$_);
    }
  },

  trades => sub {
    my $r = $m->get_trades;
    sei pp($r);
  },

  balance => sub {
    my $r = $m->get_balance;
    sei pp($r);
  }, 

  buy => sub {
    GetOptions(
      \%_,
      'amount|a=f',
      'price|p=f',
    );
    ouch("MissingParameter", "--amount is required") if not $_{amount};
    ouch("MissingParameter", "--price is required")  if not $_{price};
    my $r = $m->buy(amount => $_{amount}, price => $_{price});
    sei pp($r);
  },
  
  sell => sub {
    GetOptions(
      \%_,
      'amount|a=f',
      'price|p=f',
    );
    ouch("MissingParameter", "--amount is required") if not $_{amount};
    ouch("MissingParameter", "--price is required")  if not $_{price};
    my $r = $m->sell(amount => $_{amount}, price => $_{price});
    sei pp($r);
  },

  list => sub {
    my $r = $m->list;
    sei pp($r);
  },
  
  cancel => sub {
    GetOptions(
      \%_,
      'oid|o=n',
      'type|t=s',
    );
    ouch("MissingParameter", "--oid is required")  if not ($_{oid});
    ouch("MissingParameter", "--type is required") if not ($_{type});
    my $r = $m->cancel(oid => $_{oid}, type => $_{type});
    sei pp($r);
  },
  
  send => sub {
    GetOptions(
      \%_,
      'address|addr|b=s',
      'amount=f',
    );
    ouch("MissingParameter", "--address is required") if not($_{address});
    ouch("MissingParameter", "--amount is required")  if not($_{amount});
    my $r = $m->send(bitcoin_address => $_{address}, amount => $_{amount});
    sei pp($r);
  },

  # value of assets denominated in USD
  usd => sub {
    my $ticker  = $m->get_ticker->{ticker};
    my $balance = $m->get_balance;
    my $rate = $ticker->{last};
    my $btc;
    my $usd;
    if (@ARGV) {
      $btc = shift(@ARGV);
      $usd = ($btc * $rate);
    } else {
      $btc = $balance->{btcs};
      $usd = ($btc * $rate) + $balance->{usds};
    }
    printf("\$%.2f\n", $usd);
  },

  # value of assets denominated in BTC
  btc => sub {
    my $ticker  = $m->get_ticker->{ticker};
    my $balance = $m->get_balance;
    my $rate = $ticker->{last};
    my $btc;
    my $usd;
    if (@ARGV) {
      $usd = shift(@ARGV);
      $btc = ($usd / $rate);
    } else {
      $btc = $balance->{btcs} + ($balance->{usds} / $rate);
    }
    printf("\x{0E3F}%.8f\n", $btc);
  },

  help => sub {
    require Pod::Usage;
    Pod::Usage->import;
    pod2usage(-verbose => 2);
  },
);

# aliases
$actions{ls} = $actions{list};

my $command = shift;

try {
  if ($command && exists $actions{$command}) {
    $actions{$command}->();
  } else {
    require Pod::Usage;
    Pod::Usage->import;
    pod2usage(-verbose => 1);
  }
}
catch {
  sei "$_";
  exit 1;
};
exit 0;

__END__

=head1 NAME

mg - a command line client for MtGox

=head1 SYNOPSIS

  mg <COMMAND> [OPTION]...

=head1 OPTIONS

=over 2

=item B<ticker>

Get ticker.

=item B<depth>

Get trading depth.

=item B<trades>

Get recent trades.

=item B<balance>

Get your balance.

=item B<buy>

Buy some bitcoins.

=over   4

=item   --amount=NUMBER

=item   --price=NUMBER

=back

=item B<sell>

Sell some bitcoins.

=over   4

=item   --amount=NUMBER

=item   --price=NUMBER

=back

=item B<ls>

=item B<list>

List your orders.

=item B<cancel>

Cancel an order.

=over   4

=item   --oid=ID

The order id.

=item   --type=NUMBER

The type may be C<1> for buy or C<2> for sell.

=back

=item B<send>

Send bitcoins from your MtGox account to a bitcoin address.

=over   4

=item   --amount=NUMBER

=item   --address=BITCOIN_ADDRESS

=back

=item B<usd> [BITCOINS]

Display the total value of your balance in US Dollars.
You can also use this to find the value of an arbitrary amount
of bitcoins in US Dollars.

  # How much is 1000 btc worth in US Dollars?
  mg usd 1000

=item B<btc> [USD]

Display the total value of your balance in bitcoins.
You can also use this to find the value of an arbitrary amount
of US Dollars in bitcoins.

  # How much is $400 worth in bitcoins?
  mg btc 400

=back

=head1 SEE ALSO

L<WebService::MtGox>, L<Finance::Bitcoin>

=head1 AUTHOR

John BEPPU E<lt>beppu (at) cpan.orgE<gt>

=head1 LICENSE

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=cut