App-cryp-arbit

 view release on metacpan or  search on metacpan

lib/App/cryp/arbit.pm  view on Meta::CPAN

        );
    };

    my $code_update_sell_filled_base_size = sub {
        my ($id, $size, $summary) = @_;
        local $dbh->{RaiseError};
        $dbh->do(
            "UPDATE order_pair SET sell_filled_base_size=? WHERE id=?",
            {},
            $size,
            $id,
        ) or do {
            log_warn "Couldn't update sell filled base size for order pair #%d: %s",
                $id, $dbh->errstr;
            return;
        };
        $dbh->do(
            "INSERT INTO arbit_order_log (order_pair_id, type, summary) VALUES (?,?,?)",
            {},
            $id, 'sell', "filled_base_size changed to $size" . ($summary ? ": $summary" : ""),
        );
    };

    my @open_order_pairs;
    my $sth = $dbh->prepare(
        "SELECT
           op.id id,
           op.ctime ctime,
           CONCAT(op.base_currency, '/', op.buy_quote_currency) buy_pair,
           op.buy_status buy_status,
           (SELECT safename FROM exchange WHERE id=op.buy_exchange_id) buy_exchange,
           (SELECT nickname FROM account WHERE id=op.buy_account_id) buy_account,
           op.buy_order_id buy_order_id,

           op.sell_status sell_status,
           CONCAT(op.base_currency, '/', op.sell_quote_currency) sell_pair,
           (SELECT safename FROM exchange WHERE id=op.sell_exchange_id) sell_exchange,
           (SELECT nickname FROM account WHERE id=op.sell_account_id) sell_account,
           op.sell_order_id sell_order_id
         FROM order_pair op
         WHERE
           (op.buy_order_id IS NOT NULL AND
            op.buy_status  NOT IN ('done','filled','cancelled')) OR
           (op.sell_order_id IS NOT NULL AND
            op.sell_status NOT IN ('done','filled','cancelled'))
         ORDER BY op.ctime");
    $sth->execute;
    while (my $row = $sth->fetchrow_hashref) {
        push @open_order_pairs, $row;
    }

    my $time = time();
    for my $op (@open_order_pairs) {
        log_debug "Checking order pair #%d (buy status=%s, sell status=%s) ...",
            $op->{id}, $op->{buy_status}, $op->{sell_status};

      CHECK_BUY_ORDER: {
            last if $op->{buy_status} =~ /\A(done|cancelled)\z/;
            my $client = _get_exchange_client($r, $op->{buy_exchange}, $op->{buy_account});
            my $res = $client->get_order(pair=>$op->{buy_pair}, type=>'buy', order_id=>$op->{buy_order_id});
            if ($res->[0] == 404) {
                # assume 404 as order which was never filled and got cancelled.
                # some exchanges, e.g. coinbase-pro returns 404 for such orders
                $code_update_buy_status->($op->{id}, 'cancelled', 'not found via get_order(), assume cancelled without being filled');
                last;
            } elsif ($res->[0] != 200) {
                log_error "Couldn't get buy order %s (pair %s): %s",
                    $op->{buy_order_id}, $op->{buy_pair}, $res;
                last;
            } else {
                my $status = $res->[2]{status};
                $code_update_buy_filled_base_size->($op->{id}, $res->[2]{filled_base_size});
                $code_update_buy_status->($op->{id}, $status);

                if ($status eq 'open' && $time - $op->{ctime} > $r->{args}{max_order_age}) {
                    log_info "Order %s (buy) has been open for too long (>%d secs), cancelling ...";
                    my $cancelres = $client->cancel_order(pair=>$op->{buy_pair}, type=>'buy', order_id=>$op->{buy_order_id});
                    if ($cancelres->[0] != 200) {
                        log_error "Couldn't cancel order %s (buy): %s", $op->{buy_order_id}, $cancelres;
                    } else {
                        $code_update_buy_status->($op->{id}, "cancelled");
                    }
                }
            }
        } # CHECK_BUY_ORDER

      CHECK_SELL_ORDER: {
            last if $op->{sell_status} =~ /\A(done|cancelled)\z/;
            my $client = _get_exchange_client($r, $op->{sell_exchange}, $op->{sell_account});
            my $res = $client->get_order(pair=>$op->{sell_pair}, type=>'sell', order_id=>$op->{sell_order_id});
            if ($res->[0] == 404) {
                # assume 404 as order which was never filled and got cancelled.
                # some exchanges, e.g. coinbase-pro returns 404 for such orders
                $code_update_sell_status->($op->{id}, 'cancelled', 'not found via get_order(), assume cancelled without being filled');
                last;
            } elsif ($res->[0] != 200) {
                log_error "Couldn't get sell order %s (pair %s): %s",
                    $op->{sell_order_id}, $op->{sell_pair}, $res;
                last;
            } else {
                my $status = $res->[2]{status};
                $code_update_sell_filled_base_size->($op->{id}, $res->[2]{filled_base_size});
                $code_update_sell_status->($op->{id}, $status);

                if ($status eq 'open' && $time - $op->{ctime} > $r->{args}{max_order_age}) {
                    log_info "Order %s (sell) has been open for too long (>%d secs), cancelling ...";
                    my $cancelres = $client->cancel_order(pair=>$op->{sell_pair}, type=>'sell', order_id=>$op->{sell_order_id});
                    if ($cancelres->[0] != 200) {
                        log_error "Couldn't cancel order %s (sell): %s", $op->{sell_order_id}, $cancelres;
                    } else {
                        $code_update_sell_status->($op->{id}, "cancelled");
                    }
                }
            }
        } # CHECK_SELL_ORDER
    }
}

$SPEC{check_orders} = {
    v => 1.1,
    summary => 'Check the orders that have been created',
    description => <<'_',

This subcommand will check the orders that have been created previously by
`arbit` subcommand. It will update the order status and filled size (if still
open). It will cancel (give up) the orders if deemed too old.

_
    args => {
        %args_db,
        %arg_max_order_age,
    },
};
sub check_orders {
    my %args = @_;

    my $r = $args{-cmdline_r};

    my $res;

    # [ux] remove extraneous arguments supplied by config
    delete $r->{args}{accounts};

    $res = _init($r); return $res unless $res->[0] == 200;

    _check_orders($r);
    [200];
}

$SPEC{list_order_pairs} = {
    v => 1.1,
    summary => 'List created order pairs',
    args => {



( run in 2.375 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )