App-cryp-arbit
view release on metacpan or search on metacpan
lib/App/cryp/arbit/Strategy/merge_order_book.pm view on Meta::CPAN
$i++;
my ($bcur) = $op->{buy}{pair} =~ m!/(.+)!;
my ($scur) = $op->{sell}{pair} =~ m!/(.+)!;
if ($bcur eq $scur) {
# there is no forex spread
$op->{net_profit_margin} = $op->{trading_profit_margin};
$op->{net_profit} = $op->{trading_profit};
goto ADD;
}
my $spread;
$spread = $forex_spreads->{"$bcur/$scur"} if $forex_spreads;
unless (defined $spread) {
log_warn "Order pair #%d (buy %s - sell %s): didn't find ".
"forex spread for %s/%s, not adjusting for forex spread",
$i, $op->{buy}{pair}, $op->{sell}{pair}, $bcur, $scur;
next ORDER_PAIR;
}
log_trace "Order pair #%d (buy %s - sell %s, trading profit margin %.4f%%): adjusting ".
"with %s/%s forex spread %.4f%%",
$i, $op->{buy}{pair}, $op->{sell}{pair}, $op->{trading_profit_margin}, $bcur, $scur, $spread;
$op->{forex_spread} = $spread;
$op->{net_profit_margin} = $op->{trading_profit_margin} - $spread;
$op->{net_profit} = $op->{trading_profit} * $op->{net_profit_margin} / $op->{trading_profit_margin};
if ($op->{net_profit_margin} < $min_net_profit_margin) {
log_trace "Order pair #%d: After forex spread adjustment, net profit margin is too small (%.4f%%, wants >= %.4f%%), skipping this order pair",
$i, $op->{net_profit_margin}, $min_net_profit_margin;
next ORDER_PAIR;
}
ADD:
push @order_pairs, $op;
}
} # ADJUST_FOREX_SPREAD
# re-sort
@order_pairs = sort { $b->{net_profit_margin} <=> $a->{net_profit_margin} } @order_pairs;
(\@order_pairs, $opportunity);
}
sub calculate_order_pairs {
my ($pkg, %args) = @_;
my $r = $args{r};
my $dbh = $r->{_stash}{dbh};
my @order_pairs;
GET_ACCOUNT_BALANCES:
{
last if $r->{args}{ignore_balance};
App::cryp::arbit::_get_account_balances($r, 'no-cache');
} # GET_ACCOUNT_BALANCES
GET_FOREX_RATES:
{
# get foreign fiat currency vs USD exchange rate. we'll use the average
# rate for this first. but we'll adjust the price difference percentage
# with the buy-sell spread later.
my %seen;
$r->{_stash}{forex_rates} = {};
for my $cur (@{ $r->{_stash}{quote_currencies} }) {
next unless App::cryp::arbit::_is_fiat($cur);
next if $cur eq 'USD';
next if $seen{$cur}++;
require Finance::Currency::FiatX;
my $fxres_low = Finance::Currency::FiatX::get_spot_rate(
dbh => $dbh, from => $cur, to => 'USD', type => 'buy', source => ':lowest');
if ($fxres_low->[0] != 200) {
return [412, "Couldn't get conversion rate (lowest buy) from ".
"$cur to USD: $fxres_low->[0] - $fxres_low->[1]"];
}
my $fxres_high = Finance::Currency::FiatX::get_spot_rate(
dbh => $dbh, from => $cur, to => 'USD', type => 'sell', source => ':highest');
if ($fxres_high->[0] != 200) {
return [412, "Couldn't get conversion rate (highest sell) ".
"from $cur to USD: $fxres_high->[0] - ".
"$fxres_high->[1]"];
}
my $fxrate_avg = ($fxres_low->[2]{rate} + $fxres_high->[2]{rate})/2;
$r->{_stash}{forex_rates}{"$cur/USD"} = $fxrate_avg;
}
} # GET_FOREX_RATES
GET_FOREX_SPREADS:
{
# when we arbitrage using two different fiat currencies, e.g. BTC/USD
# and BTC/IDR, we want to take into account the USD/IDR buy-sell spread
# (the "forex spread") and subtract this from the price difference
# percentage to be safer.
$r->{_stash}{forex_spreads} = {};
my @curs;
for my $cur (@{ $r->{_stash}{quote_currencies} }) {
next unless App::cryp::arbit::_is_fiat($cur);
push @curs, $cur unless grep { $cur eq $_ } @curs;
}
last unless @curs;
require Finance::Currency::FiatX;
for my $cur1 (@curs) {
for my $cur2 (@curs) {
next if $cur1 eq $cur2;
my $fxres_low = Finance::Currency::FiatX::get_spot_rate(
dbh => $dbh, from => $cur1, to => $cur2, type => 'buy', source => ':lowest');
if ($fxres_low->[0] != 200) {
return [412, "Couldn't get conversion rate (lowest buy) for ".
"$cur1/$cur2: $fxres_low->[0] - $fxres_low->[1]"];
}
my $fxres_high = Finance::Currency::FiatX::get_spot_rate(
dbh => $dbh, from => $cur1, to => $cur2, type => 'sell', source => ':highest');
if ($fxres_high->[0] != 200) {
return [412, "Couldn't get conversion rate (highest sell) ".
"for $cur1/$cur2: $fxres_high->[0] - ".
"$fxres_high->[1]"];
}
my $r1 = $fxres_low->[2]{rate};
my $r2 = $fxres_high->[2]{rate};
my $spread = $r1 > $r2 ? ($r1-$r2)/$r2*100 : ($r2-$r1)/$r1*100;
$r->{_stash}{forex_spreads}{"$cur1/$cur2"} = abs $spread;
}
}
} # GET_FOREX_SPREADS
my %exchanges_for; # key="base currency"/"quote cryptocurrency or ':fiat'", value => [exchange, ...]
my %fiat_for; # key=exchange safename, val=[fiat currency, ...]
my %pairs_for; # key=exchange safename, val=[pair, ...]
DETERMINE_SETS:
for my $exchange (sort keys %{ $r->{_stash}{exchange_clients} }) {
my $pair_recs = $r->{_stash}{exchange_pairs}{$exchange};
for my $pair_rec (@$pair_recs) {
my $pair = $pair_rec->{name};
my ($basecur, $quotecur) = $pair =~ m!(.+)/(.+)!;
next unless grep { $_ eq $basecur } @{ $r->{_stash}{base_currencies} };
next unless grep { $_ eq $quotecur } @{ $r->{_stash}{quote_currencies} };
my $key;
if (App::cryp::arbit::_is_fiat($quotecur)) {
$key = "$basecur/:fiat";
$fiat_for{$exchange} //= [];
push @{ $fiat_for{$exchange} }, $quotecur
unless grep { $_ eq $quotecur } @{ $fiat_for{$exchange} };
} else {
$key = "$basecur/$quotecur";
}
( run in 1.532 second using v1.01-cache-2.11-cpan-99c4e6809bf )