src/Controller/EngineController.php line 752

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  4. use Symfony\Component\HttpFoundation\Response;
  5. use Symfony\Component\HttpFoundation\JsonResponse;
  6. use Symfony\Component\Routing\Annotation\Route;
  7. use Symfony\Component\HttpFoundation\Request;
  8. use Doctrine\ODM\MongoDB\DocumentManager;
  9. use App\Document\Order;
  10. use App\Document\MarketOrder;
  11. use App\Document\Currencies;
  12. use App\Document\Wallet;
  13. use App\Document\Trade;
  14. use App\Document\Price;
  15. use App\Document\User;
  16. use Symfony\Contracts\Translation\TranslatorInterface;
  17. use App\Document\Setting;
  18. use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
  19. use Symfony\Component\Process\Exception\ProcessFailedException;
  20. use Symfony\Component\Process\Process;
  21. use KuCoin\SDK\Auth;
  22. use KuCoin\SDK\Http\SwooleHttp;
  23. use KuCoin\SDK\KuCoinApi;
  24. use KuCoin\SDK\PrivateApi\Order as KuCoinOrder;
  25. use KuCoin\SDK\PrivateApi\Account;
  26. use ccxt\coinex;
  27. class EngineController extends AbstractController
  28. {
  29.     private $translator;
  30.     private $doc;
  31.     public function __construct(TranslatorInterface $translatorDocumentManager $doctrine)
  32.     {
  33.         $this->translator $translator;
  34.         $this->doc $doctrine;
  35.     }
  36.     private function getLastPrice($crypto,$type='SELL'): float
  37.     {
  38.         $trade $this->doc->createQueryBuilder(Price::class)
  39.             ->field('pair')->equals($crypto)
  40.             ->field('type')->equals($type)
  41.             ->getQuery()
  42.             ->getSingleResult();
  43.         return is_null($trade) ? $trade->getPrice() ;
  44.     }
  45. private function submitLimitOrder($doctrine$data$crypto$userId$user_limit_price$wallet_rial$wallet_crypto$wallet_rial_balance$wallet_crypto_balance$last_usdt_price$side): array
  46. {
  47.       $key "8B830428825B4E8B9EF8483FB704FF62";
  48.         $secret "3BC71E2BE239C53AB91DCA3019FFD6195AB41B072DF7C571" ;
  49.     $amount $data->amount ?? null;
  50.     $price $data->price ?? null;
  51.     if ($crypto === "USDT") {
  52.         return [
  53.             "status" => "400",
  54.             "msg" => 'این نوع برای usdt  فعال نمی باشد'
  55.         ];
  56.     }
  57.     if ($price <= 0) {
  58.         $translated $this->translator->trans('Fill price');
  59.         return [
  60.             "status" => "400",
  61.             "msg" => $translated
  62.         ];
  63.     }
  64.     try {
  65.         $exchange = new \ccxt\coinex([
  66.             'apiKey' => $key,
  67.             'secret' => $secret,
  68.         ]);
  69.         $symbol $crypto '/USDT';
  70.         $params = ["createMarketBuyOrderRequiresPrice" => true];
  71.         $price_now $exchange->fetchTicker($symbol)['last'];
  72.         $cost $amount;
  73.         $deviation abs($user_limit_price $price_now) / $price_now 100;
  74.         if ($deviation 20) {
  75.             $suggested_price round($price_now4);
  76.             $translated $this->translator->trans(
  77.                 'Your price is too far from the market. Suggested price: ' $suggested_price
  78.             );
  79.             return [
  80.                 "status" => "400",
  81.                 "msg" => $translated
  82.             ];
  83.         }
  84.         if ($side === 'buy') {
  85.             $cost $amount $price_now;
  86.         }
  87.         $order $exchange->createOrder($symbol'limit'$side$cost$user_limit_price$params);
  88.         $dealFunds $order['cost'];
  89.         $fee_usdt $order['fee']['cost'] ?? 0;
  90.         $fee $fee_usdt $last_usdt_price;
  91.         $total $dealFunds $last_usdt_price;
  92.         if ($side === 'buy') {
  93.             $wallet_rial->setballance($wallet_rial_balance $total $fee);
  94.             $wallet_crypto->setballance($wallet_crypto_balance $amount);
  95.         } elseif ($side === 'sell') {
  96.             $wallet_rial->setballance($wallet_rial_balance $total $fee);
  97.             $wallet_crypto->setballance($wallet_crypto_balance $amount);
  98.         }
  99.         $doctrine->flush();
  100.         return [
  101.             "status" => "200",
  102.             "msg" => "Order submitted",
  103.             "order_id" => $order,
  104.             "order_status" => "PENDING",
  105.             "fee" => $fee,
  106.             "price" => $user_limit_price
  107.         ];
  108.     } catch (\Exception $e) {
  109.         return [
  110.             "status" => "400",
  111.             "msg" => "ERROR",
  112.             "e" => $e->getMessage()
  113.         ];
  114.     }
  115. }
  116.     #[Route('/api/order/marketsubmit'methods: ['POST'], name'order_marketsubmit')]
  117.     public function marketSubmit(Request $request): JsonResponse
  118.     {
  119.         $data json_decode($request->getContent(), false);
  120.         $user $this->getUser();
  121.         $user_id $user->getId();
  122.         $status $user->getStatus();
  123.         
  124.         
  125.         
  126.         $market_symbol $data->market ;
  127.         if ($status 1) {
  128.             $translated $this->translator->trans('your account not active');
  129.             return new JsonResponse(['status' => '400''msg' => $translated]);
  130.         }
  131.         $setting $this->doc->createQueryBuilder(Setting::class)
  132.             ->limit(1)
  133.             ->getQuery()
  134.             ->getSingleResult();
  135.         $amount floatval($data->amount);
  136.         $side $data->side;
  137.         $type $data->type;
  138.         $crypto $data->crypto;
  139.         $price = isset($data->price) ? $data->price 0;
  140.         
  141.         $user_limit_price = isset($data->price) ? $data->price ;
  142.         
  143.         if ($amount <= 0) {
  144.             $translated $this->translator->trans('amount must larger than 1');
  145.             return new JsonResponse(['status' => '400''msg' => $translated]);
  146.         }
  147.         $wallet_rial $this->doc->createQueryBuilder(Wallet::class)
  148.             ->field('userid')->equals($user_id)
  149.             ->field('pair')->equals($market_symbol)
  150.             ->getQuery()
  151.             ->getSingleResult();
  152.         $wallet_crypto $this->doc->createQueryBuilder(Wallet::class)
  153.             ->field('userid')->equals($user_id)
  154.             ->field('pair')->equals($crypto)
  155.             ->getQuery()
  156.             ->getSingleResult();
  157.         $usdt_buy_price $setting->getUsdtpricebuy();
  158.         $usdt_sell_price $setting->getUsdtpricesell();
  159.         if ($side === 'sell' && $amount $wallet_crypto->getballance()) {
  160.             $translated $this->translator->trans('ballance is LOw');
  161.             return new JsonResponse(['status' => '400''msg' => $translated]);
  162.         }
  163.         $crypto_buy $this->getLastPrice($crypto);
  164.         
  165.         $real_price_now =  $this->getLastPrice($crypto); 
  166.         
  167.         $crypto_buy += (($crypto_buy 0.1) / 100);
  168.         
  169.         if($market_symbol === "RIAL") {
  170.              $crypto_price_buy $crypto_buy $usdt_buy_price ;
  171.         }else {
  172.         $crypto_price_buy $crypto_buy ;
  173. }
  174.   if ($type === 'limit') {
  175.     if ($price <= 0) {
  176.         $translated $this->translator->trans('Price is too low');
  177.         return new JsonResponse(['status' => '400''msg' => $translated]);
  178.     }
  179.     $priceDifference abs(($price $real_price_now) / $real_price_now) * 100;
  180.     if ($priceDifference 20) {
  181.         $translated $this->translator->trans('Price must be within ±20% of market price');
  182.         return new JsonResponse(['status' => '400''msg' => $translated  's' => $crypto_price_buy]);
  183.     }
  184. }
  185.         // if ($type === 'limit' && !$this->isPriceWithinRange($crypto_buy, $price)) {
  186.         //     $translated = $this->translator->trans('price not correct');
  187.         //     return new JsonResponse(['status' => '400', 'msg' => $translated]);
  188.         // }
  189.         if ($type === 'market') {
  190.               
  191.            
  192.             
  193.             $price $crypto_buy;
  194.         }
  195.         if ($side === 'buy' && ($amount $crypto_price_buy) >= $wallet_rial->getballance()) {
  196.             $translated $this->translator->trans('ballance is LOw');
  197.             return new JsonResponse(['status' => '400''msg' => $translated]);
  198.         }
  199. if ($type === 'limit') {
  200.         // Freeze funds
  201.         if ($side === 'buy') {
  202.             $wallet_rial->setballance($wallet_rial->getballance() - ($amount $crypto_price_buy));
  203.             $wallet_rial->setFrozen(($amount $crypto_price_buy) + $wallet_rial->getFrozen());
  204.         } else {
  205.             $wallet_crypto->setballance($wallet_crypto->getballance() - $amount);
  206.             $wallet_crypto->setFrozen($amount $wallet_crypto->getFrozen());
  207.             
  208.         }
  209.         
  210.         
  211.         
  212.             
  213.         
  214.         
  215.         $this->doc->persist($wallet_rial);
  216.         $this->doc->persist($wallet_crypto);
  217.         $this->doc->flush();
  218.         
  219. }
  220.         
  221.               
  222.         $key "8B830428825B4E8B9EF8483FB704FF62";
  223.         $secret "3BC71E2BE239C53AB91DCA3019FFD6195AB41B072DF7C571" ;
  224.         if ($type === 'market') {
  225. try {
  226.     // Set up your CoinEx API credentials
  227.     $api_key $key;
  228.     $api_secret $secret;
  229.     // Instantiate the CoinEx exchange class
  230.     $exchange = new coinex([
  231.         'apiKey' => $api_key,
  232.         'secret' => $api_secret,
  233.     ]);
  234.     // Define the trading pair and order details
  235.     $symbol $crypto '/USDT';  // Market pair
  236.     $params = ["createMarketBuyOrderRequiresPrice" => false];
  237.     $cost $amount;
  238.     $price_now $exchange->fetchTicker($symbol)['last'];
  239.     if ($side === 'buy') {
  240.         $cost $amount $price_now;
  241.     }
  242.     try {
  243.         $api_order $exchange->createOrder($symbol'market'$side$cost0$params);
  244.         $order_submited true;
  245.         
  246.         
  247.         
  248.         
  249.     } catch (\ccxt\NetworkError $e) {
  250.         return new JsonResponse([
  251.             "status" => "400",
  252.             "msg" => "Network Error"
  253.         ]);
  254.     } catch (\ccxt\ExchangeError $e) {
  255.         return new JsonResponse([
  256.             "status" => "400",
  257.             "msg" => $e->getMessage()
  258.         ]);
  259.     } catch (Exception $e) {
  260.         return new JsonResponse([
  261.             "status" => "400",
  262.             "msg" => $e->getMessage()
  263.         ]);
  264.     }
  265. } catch (Exception $e) {
  266.     return new JsonResponse([
  267.         "status" => "400",
  268.         "msg" => $e->getMessage()
  269.     ]);
  270. }
  271.             $robot_id '666621eea320d65e3f6878ec';
  272.             $buy_user_id $side === 'buy' $user_id $robot_id;
  273.             $sell_user_id $side === 'sell' $user_id $robot_id;
  274.            $this->updateWallets($buy_user_id$sell_user_id$amount$crypto$price $market_symbol);
  275.         } else {
  276.             
  277.             if ($type === 'limit') {
  278.     $result $this->submitLimitOrder(
  279.         $this->doc,
  280.         $data,
  281.         $crypto,
  282.         $user_id,
  283.         $user_limit_price,
  284.         $wallet_rial,
  285.         $wallet_crypto,
  286.         $wallet_rial->getballance(),
  287.         $wallet_crypto->getballance(),
  288.         $usdt_buy_price,
  289.         $side
  290.     );
  291.     $api_order $result['order_id'];
  292.  
  293.    
  294. }
  295.         }
  296.         
  297.         
  298.         
  299.         $order = new MarketOrder();
  300.         $order->setUserid($user_id);
  301.         $order->setAmount($amount);
  302.         $order->setPair($crypto);
  303.         $order->setSide($side);
  304.         $order->setType($type);
  305.         $order->setPrice($price);
  306.         $order->setTakeprofit("0");
  307.         $order->setStoploss("0");
  308.         $order->setTime(time());
  309.         $order->setFee(0);
  310.         $order->setApi(json_encode($api_order));
  311.         $order->setStatus("Pending");
  312.         $this->doc->persist($order);
  313.         $this->doc->flush();
  314.         $translated $this->translator->trans('Order Submited');
  315.         return new JsonResponse(['status' => '200''msg' => $translated]);
  316.     }
  317. //     #[Route('/api/order/marketsubmit', methods: ['POST'], name: 'order_marketsubmit')]
  318. //     public function marketSubmit(Request $request): JsonResponse
  319. //     {
  320. //         $data = json_decode($request->getContent(), false);
  321. //         $user = $this->getUser();
  322. //         $user_id = $user->getId();
  323. //         $status = $user->getStatus();
  324.         
  325.         
  326.         
  327. //         $market_symbol = $data->market ;
  328. //         if ($status < 1) {
  329. //             $translated = $this->translator->trans('your account not active');
  330. //             return new JsonResponse(['status' => '400', 'msg' => $translated]);
  331. //         }
  332. //         $setting = $this->doc->createQueryBuilder(Setting::class)
  333. //             ->limit(1)
  334. //             ->getQuery()
  335. //             ->getSingleResult();
  336. //         $amount = floatval($data->amount);
  337. //         $side = $data->side;
  338. //         $type = $data->type;
  339. //         $crypto = $data->crypto;
  340. //         $price = isset($data->price) ? $data->price : 0;
  341. //         if ($amount <= 0) {
  342. //             $translated = $this->translator->trans('amount must larger than 1');
  343. //             return new JsonResponse(['status' => '400', 'msg' => $translated]);
  344. //         }
  345. //         $wallet_rial = $this->doc->createQueryBuilder(Wallet::class)
  346. //             ->field('userid')->equals($user_id)
  347. //             ->field('pair')->equals($market_symbol)
  348. //             ->getQuery()
  349. //             ->getSingleResult();
  350. //         $wallet_crypto = $this->doc->createQueryBuilder(Wallet::class)
  351. //             ->field('userid')->equals($user_id)
  352. //             ->field('pair')->equals($crypto)
  353. //             ->getQuery()
  354. //             ->getSingleResult();
  355. //         $usdt_buy_price = $setting->getUsdtpricebuy();
  356. //         $usdt_sell_price = $setting->getUsdtpricesell();
  357. //         if ($side === 'sell' && $amount > $wallet_crypto->getballance()) {
  358. //             $translated = $this->translator->trans('ballance is LOw');
  359. //             return new JsonResponse(['status' => '400', 'msg' => $translated]);
  360. //         }
  361. //         $crypto_buy = $this->getLastPrice($crypto);
  362. //         $crypto_buy += (($crypto_buy * 0.1) / 100);
  363.         
  364. //         if($market_symbol === "RIAL") {
  365. //              $crypto_price_buy = $crypto_buy * $usdt_buy_price ;
  366. //         }else {
  367. //         $crypto_price_buy = $crypto_buy ;
  368. // }
  369. //         if ($type === 'limit' && $price <= 0) {
  370. //             $translated = $this->translator->trans('Price is Low');
  371. //             return new JsonResponse(['status' => '400', 'msg' => $translated]);
  372. //         }
  373. // $priceDifference = abs(($price - $crypto_price_buy) / $crypto_price_buy) * 100;
  374. // if ($type === 'limit' && ($price <= 0 || $priceDifference > 2)) {
  375. //     $translated = $this->translator->trans('Price must be within ±2% range');
  376. //     return new JsonResponse(['status' => '400', 'msg' => $translated]);
  377. // }
  378. //         // if ($type === 'limit' && !$this->isPriceWithinRange($crypto_buy, $price)) {
  379. //         //     $translated = $this->translator->trans('price not correct');
  380. //         //     return new JsonResponse(['status' => '400', 'msg' => $translated]);
  381. //         // }
  382. //         if ($type === 'market') {
  383.               
  384.            
  385.             
  386. //             $price = $crypto_buy;
  387. //         }
  388. //         if ($side === 'buy' && ($amount * $crypto_price_buy) >= $wallet_rial->getballance()) {
  389. //             $translated = $this->translator->trans('ballance is LOw');
  390. //             return new JsonResponse(['status' => '400', 'msg' => $translated]);
  391. //         }
  392. // if ($type === 'limit') {
  393. //         // Freeze funds
  394. //         if ($side === 'buy') {
  395. //             $wallet_rial->setballance($wallet_rial->getballance() - ($amount * $crypto_price_buy));
  396. //             $wallet_rial->setFrozen(($amount * $crypto_price_buy) + $wallet_rial->getFrozen());
  397. //         } else {
  398. //             $wallet_crypto->setballance($wallet_crypto->getballance() - $amount);
  399. //             $wallet_crypto->setFrozen($amount + $wallet_crypto->getFrozen());
  400.             
  401. //         }
  402.         
  403.         
  404.         
  405.             
  406.         
  407.         
  408. //         $this->doc->persist($wallet_rial);
  409. //         $this->doc->persist($wallet_crypto);
  410. //         $this->doc->flush();
  411.         
  412. // }
  413.         
  414. //         $order = new MarketOrder();
  415. //         $order->setUserid($user_id);
  416. //         $order->setAmount($amount);
  417. //         $order->setPair($crypto);
  418. //         $order->setSide($side);
  419. //         $order->setType($type);
  420. //         $order->setPrice($price);
  421. //         $order->setTakeprofit("0");
  422. //         $order->setStoploss("0");
  423. //         $order->setTime(time());
  424. //         $order->setFee(0);
  425. //         $order->setApi('');
  426. //         $order->setStatus("Pending");
  427. //         $this->doc->persist($order);
  428. //         $this->doc->flush();
  429. //         if ($type === 'market') {
  430. //             // Create an opposite order for the robot
  431. //             $robot_id = '666621eea320d65e3f6878ec';
  432. //             // Mark the original order as filled
  433. //             $order->setStatus('Filled');
  434. //             $this->doc->persist($order);
  435. //             $this->doc->flush();
  436. //             // Create a new trade
  437. //             $trade_id = \Ramsey\Uuid\Uuid::uuid4()->toString(); // Generate a unique trade ID
  438. //             $buy_user_id = $side === 'buy' ? $user_id : $robot_id;
  439. //             $sell_user_id = $side === 'sell' ? $user_id : $robot_id;
  440. //             $this->createNewTrade($buy_user_id, $sell_user_id, $amount, $crypto,'market', $price, $trade_id, 'Completed');
  441. //             // Update wallets
  442. //             $this->updateWallets($buy_user_id, $sell_user_id, $amount, $crypto, $price , $market_symbol);
  443. //         }
  444. //         $translated = $this->translator->trans('Order Submited');
  445. //         return new JsonResponse(['status' => '200', 'msg' => $translated]);
  446. //     }
  447. #[Route('/api/order/cancelmarketsubmit'methods: ['POST'], name'order_cancelmarketsubmit')]
  448. public function cancelMarketSubmit(Request $request): JsonResponse
  449. {
  450.     $data json_decode($request->getContent(), false);
  451.     $user $this->getUser();
  452.     $order_id $data->id;
  453.     $order $this->doc->createQueryBuilder(MarketOrder::class)
  454.         ->field('id')->equals($order_id)
  455.         ->getQuery()
  456.         ->getSingleResult();
  457.     if (!$order || $order->getStatus() !== 'Pending') {
  458.         return new JsonResponse(['status' => '400''msg' => 'Order not found or not cancelable']);
  459.     }
  460.     // CoinEx API cancel logic
  461.     $apiinfo json_decode($order->getApi(), true);
  462.             $key "8B830428825B4E8B9EF8483FB704FF62";
  463.         $secret "3BC71E2BE239C53AB91DCA3019FFD6195AB41B072DF7C571" ;
  464.         
  465.     try {
  466.         $exchange = new \ccxt\coinex([
  467.             'apiKey' => $key,
  468.             'secret' => $secret,
  469.         ]);
  470.         $symbol $order->getPair() . '/USDT';
  471.         $exchange->cancelOrder($apiinfo['id'], $symbol);
  472.     } catch (\ccxt\NetworkError \ccxt\ExchangeError \Exception $e) {
  473.         return new JsonResponse(['status' => '400''msg' => $e->getMessage()]);
  474.     }
  475.     // Update local order status
  476.     $this->doc->createQueryBuilder(MarketOrder::class)
  477.         ->field('id')->equals($order_id)
  478.         ->findAndUpdate()
  479.         ->field('status')->set('cancel')
  480.         ->getQuery()
  481.         ->execute();
  482.     // Unfreeze logic
  483.     $setting $this->doc->createQueryBuilder(Setting::class)
  484.         ->limit(1)
  485.         ->getQuery()
  486.         ->getSingleResult();
  487.     $usdt_buy_price $setting->getUsdtpricebuy();
  488.     $usdt_sell_price $setting->getUsdtpricesell();
  489.     if ($order->getSide() === 'buy') {
  490.         $wallet_rial $this->doc->createQueryBuilder(Wallet::class)
  491.             ->field('userid')->equals($user->getId())
  492.             ->field('pair')->equals("RIAL")
  493.             ->getQuery()
  494.             ->getSingleResult();
  495.         $this->unfreezeFunds($wallet_rial$order->getAmount() * $order->getPrice() * $usdt_buy_price);
  496.     } else {
  497.         $wallet_crypto $this->doc->createQueryBuilder(Wallet::class)
  498.             ->field('userid')->equals($user->getId())
  499.             ->field('pair')->equals($order->getPair())
  500.             ->getQuery()
  501.             ->getSingleResult();
  502.         $this->unfreezeFunds($wallet_crypto$order->getAmount());
  503.     }
  504.     $translated $this->translator->trans('Order Canceled');
  505.     return new JsonResponse(['status' => '200''msg' => $translated]);
  506. }
  507.     private function unfreezeFunds($wallet$amount)
  508.     {
  509.         $wallet->setballance($wallet->getballance() + $amount);
  510.         $wallet->setFrozen($wallet->getFrozen() - $amount);
  511.         $this->doc->persist($wallet);
  512.         $this->doc->flush();
  513.     }
  514.     private function createNewTrade($buy_user_id$sell_user_id$amount$pair $type$price$trade_id$status)
  515.     {
  516.         $trade = new Trade();
  517.         $trade->setAmount($amount);
  518.         $trade->setPair($pair);
  519.         $trade->setType($type);
  520.         $trade->setPrice($price);
  521.         $trade->setTime(time());
  522.         $trade->setStatus($status);
  523.         $trade->setFee(0);
  524.         $trade->setTradeid($trade_id);
  525.         $trade->setbuyUserid($buy_user_id);
  526.         $trade->setsellUserid($sell_user_id);
  527.         $this->doc->persist($trade);
  528.         $this->doc->flush();
  529.     }
  530.     private function createOrder($user_id$amount$pair$side$type$price$robot$status)
  531.     {
  532.         $order = new MarketOrder();
  533.         $order->setUserid($user_id);
  534.         $order->setAmount($amount);
  535.         $order->setPair($pair);
  536.         $order->setSide($side);
  537.         $order->setType($type);
  538.         $order->setPrice($price);
  539.         $order->setTakeprofit("0");
  540.         $order->setStoploss("0");
  541.         $order->setTime(time());
  542.         $order->setFee(0);
  543.         $order->setRobot($robot);
  544.         $order->setStatus($status);
  545.         $this->doc->persist($order);
  546.         $this->doc->flush();
  547.     }
  548.     private function updateOrder($order)
  549.     {
  550.         $this->doc->persist($order);
  551.         $this->doc->flush();
  552.     }
  553.     private function updateWallets($buy_user_id$sell_user_id$amount$pair$price $market_symbol)
  554.     {
  555.         $usdt_buy_price $this->doc->createQueryBuilder(Setting::class)
  556.             ->limit(1)
  557.             ->getQuery()
  558.             ->getSingleResult()
  559.             ->getUsdtpricebuy();
  560.             if($market_symbol === "RIAL") {
  561.                 $rial_amount $amount $price $usdt_buy_price;
  562.             } else {
  563.         $rial_amount $amount $price ;  // Corrected calculation of rial_amount
  564. }
  565.         // Update buyer's wallet
  566.         $this->addMoney($buy_user_id$pair$amount);
  567.         $this->removeMoney($buy_user_id$market_symbol$rial_amount);
  568.         // Update seller's wallet
  569.         $this->addMoney($sell_user_id$market_symbol$rial_amount);
  570.         $this->removeMoney($sell_user_id$pair$amount);
  571.     }
  572.     private function addMoney($user_id$pair$amount)
  573.     {
  574.         $wallet $this->getWallet($user_id$pair);
  575.         if ($wallet) {
  576.             $wallet->setballance($wallet->getballance() + $amount);
  577.             $this->updateWallet($wallet);
  578.         }
  579.     }
  580.     private function removeMoney($user_id$pair$amount)
  581.     {
  582.         $wallet $this->getWallet($user_id$pair);
  583.         if ($wallet) {
  584.             $wallet->setballance($wallet->getballance() - $amount);
  585.             $this->updateWallet($wallet);
  586.         }
  587.     }
  588.     private function getWallet($user_id$pair)
  589.     {
  590.         return $this->doc->createQueryBuilder(Wallet::class)
  591.             ->field('userid')->equals($user_id)
  592.             ->field('pair')->equals($pair)
  593.             ->getQuery()
  594.             ->getSingleResult();
  595.     }
  596.     private function updateWallet($wallet)
  597.     {
  598.         $this->doc->persist($wallet);
  599.         $this->doc->flush();
  600.     }
  601.     
  602.     
  603.         private function isPriceWithinRange($lastPrice$newPrice): bool
  604.         {
  605.         $minPrice $lastPrice 0.1;
  606.         $maxPrice $lastPrice 0.02;
  607.         return $newPrice >= $minPrice && $newPrice <= $maxPrice;
  608.     }
  609.     
  610.         #[Route('/pub/order/matchengine'methods: ['GET'], name'trade_matchengine')]
  611.         public function matchEngine(): JsonResponse
  612.         {
  613.     $pairs = ["BTC""ETH""DOGE""ADA""SOL""DOT""TRX""BNB""LTC"];
  614.     foreach ($pairs as $pair) {
  615.         // Retrieve all limit buy and sell orders
  616.         $buy_limit_orders $this->getMarketOrderList($pair'buy''limit');
  617.         $sell_limit_orders $this->getMarketOrderList($pair'sell''limit');
  618.         // Get the last price of the pair
  619.         $last_price_buy round($this->getLastPrice($pair,'BUY'), 6);
  620.         
  621.         $last_price_sell round($this->getLastPrice($pair), 6);
  622.         
  623.   
  624.         // Process buy limit orders
  625.         foreach ($buy_limit_orders as $buy_order) {
  626.             
  627.             if ($buy_order->getPrice() >= $last_price_buy) {
  628.                 $this->processMatchingOrder($buy_order$buy_order->getPrice());
  629.             }
  630.         }
  631.         // Process sell limit orders
  632.         foreach ($sell_limit_orders as $sell_order) {
  633.             echo "o" $sell_order->getPrice() . 'o' $last_price_sell ;
  634.             if ($sell_order->getPrice() <= $last_price_sell) {
  635.                 $this->processMatchingOrder($sell_order$sell_order->getPrice());
  636.             }
  637.         }
  638.     }
  639.     return new JsonResponse(['status' => '200''msg' => 'Match Engine Run Successfully']);
  640. }
  641.         private function processMatchingOrder($order$last_price)
  642.         {
  643.     $user_id $order->getUserid();
  644.     $amount $order->getAmount();
  645.     $pair $order->getPair();
  646.     $side $order->getSide();
  647.     $opposite_side $side === 'buy' 'sell' 'buy';
  648.     $robot_id '666621eea320d65e3f6878ec';
  649.     // Create an opposite order for the robot
  650.     $this->createOrder($robot_id$amount$pair$opposite_side'market'$last_pricetrue'Filled');
  651.     // Mark the original order as filled
  652.     $order->setStatus('Filled');
  653.     $this->updateOrder($order);
  654.         $setting $this->doc->createQueryBuilder(Setting::class)
  655.             ->limit(1)
  656.             ->getQuery()
  657.             ->getSingleResult();
  658.         $usdt_buy_price $setting->getUsdtpricebuy();
  659.         $usdt_sell_price $setting->getUsdtpricesell();
  660.         $crypto_price_buy $this->getLastPrice($pair 'BUY') ;
  661.         $crypto_price_sell $this->getLastPrice($pair) ;
  662.         
  663.     // Create a new trade
  664.     $trade_id \Ramsey\Uuid\Uuid::uuid4()->toString();
  665.     $buy_user_id $side === 'buy' $user_id $robot_id;
  666.     $sell_user_id $side === 'sell' $user_id $robot_id;
  667.     $this->createNewTrade($buy_user_id$sell_user_id$amount$pair ,'limit'$last_price$trade_id'Completed');
  668.     
  669.     
  670.               $wallet_rial $this->doc->createQueryBuilder(Wallet::class)
  671.                     ->field('userid')->equals($user_id)
  672.                     ->field('pair')->equals("RIAL")
  673.                     ->getQuery()
  674.                     ->getSingleResult();
  675.                     
  676.                     
  677.             $wallet_crypto $this->doc->createQueryBuilder(Wallet::class)
  678.                     ->field('userid')->equals($user_id)
  679.                     ->field('pair')->equals($order->getPair())
  680.                     ->getQuery()
  681.                     ->getSingleResult();
  682.     
  683.     
  684.                if ($order->getSide() === 'buy') {
  685.       
  686.                     
  687.                $wallet_rial->setFrozen(  $wallet_rial->getFrozen()  - (($amount $crypto_price_buy) * $usdt_buy_price) );
  688.                $wallet_crypto->setballance($wallet_crypto->getballance() + $amount);
  689.                
  690.             } else {
  691.              
  692.                 $wallet_crypto->setFrozen$wallet_crypto->getFrozen() - $amount);
  693.                 $wallet_rial->setballance($wallet_rial->getballance() + (($amount $crypto_price_buy) * $usdt_buy_price));
  694.      
  695.    
  696.             }
  697.                  $this->updateWallet($wallet_rial);
  698.                 $this->updateWallet($wallet_crypto);
  699.     // Update wallets
  700.   //  $this->updateWallets($buy_user_id, $sell_user_id, $amount, $pair, $last_price);
  701. }
  702.     
  703.         private function getMarketOrderList($pair$side$type)
  704.         {
  705.         return $this->doc->createQueryBuilder(MarketOrder::class)
  706.             ->field('side')->equals($side)
  707.             ->field('type')->equals($type)
  708.             ->field('status')->equals('Pending')
  709.             ->field('pair')->equals($pair)
  710.          
  711.             ->limit(2000)
  712.             ->sort('time''asc')
  713.             ->getQuery()
  714.             ->execute();
  715.     }
  716.     
  717. }