Compare row value when records are not successive
As already suggested by Rick James in the comments, if you are working with MySQL 8+, you can make this much easier by using the LAG() analytic function to return the previous row's price. You can also get the variation using the SIGN() function instead of the CASE expression, as demonstrated in the following query:

SELECT
  pprod.price,
  DATE_FORMAT(purch.date, '%d/%m/%Y')                                   AS date,
  prov.name                                                             AS provider,
  SIGN(pprod.price
     - LAG(pprod.price, 1, pprod.price) OVER (ORDER BY purch.date ASC)) AS variation
FROM
  tb_purchase_product AS pprod
  INNER JOIN tb_purchase AS purch ON pprod.id_purchase = purch.id
  INNER JOIN tb_provider AS prov ON purch.id_provider = prov.id
WHERE
  pprod.id_product = :id_product
  AND purch.date BETWEEN :date_initial AND :date_final
ORDER BY
  purch.date DESC
;


In this query I have also rewritten the joins using the JOIN syntax. Using the explicit JOIN syntax separates joining conditions from filter conditions more clearly, which, in my estimation, makes the code more understandable and easier to maintain (but your mileage may vary).

And with the use of the LAG() function there is no need to join a second instance of tb_purchase_product, which is another obvious difference of this query from the original.

One important note regarding this query is that, as written, it works for a single id_product, which should be all right based on your filter conditions. But if you ever need to run it for multiple products simultaneously, you need to introduce partitioning to the LAG function, like this:

LAG(pprod.price, 1, pprod.price) OVER (PARTITION BY pprod.id_product
                                       ORDER BY purch.date ASC)


The PARTITION BY subclause ensures that the previous pprod.price value is for the same product as the current row.