src/Controller/EngineController.php line 374

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. class EngineController extends AbstractController
  27. {
  28.     private $translator;
  29.     private $doc;
  30.     public function __construct(TranslatorInterface $translatorDocumentManager $doctrine)
  31.     {
  32.         $this->translator $translator;
  33.         $this->doc $doctrine;
  34.     }
  35.     private function getLastPrice($crypto,$type='SELL'): float
  36.     {
  37.         $trade $this->doc->createQueryBuilder(Price::class)
  38.             ->field('pair')->equals($crypto)
  39.             ->field('type')->equals($type)
  40.             ->getQuery()
  41.             ->getSingleResult();
  42.         return is_null($trade) ? $trade->getPrice() ;
  43.     }
  44.     #[Route('/api/order/marketsubmit'methods: ['POST'], name'order_marketsubmit')]
  45.     public function marketSubmit(Request $request): JsonResponse
  46.     {
  47.         $data json_decode($request->getContent(), false);
  48.         $user $this->getUser();
  49.         $user_id $user->getId();
  50.         $status $user->getStatus();
  51.         
  52.         
  53.         
  54.         $market_symbol $data->market ;
  55.         if ($status 1) {
  56.             $translated $this->translator->trans('your account not active');
  57.             return new JsonResponse(['status' => '400''msg' => $translated]);
  58.         }
  59.         $setting $this->doc->createQueryBuilder(Setting::class)
  60.             ->limit(1)
  61.             ->getQuery()
  62.             ->getSingleResult();
  63.         $amount floatval($data->amount);
  64.         $side $data->side;
  65.         $type $data->type;
  66.         $crypto $data->crypto;
  67.         $price = isset($data->price) ? $data->price 0;
  68.         if ($amount <= 0) {
  69.             $translated $this->translator->trans('amount must larger than 1');
  70.             return new JsonResponse(['status' => '400''msg' => $translated]);
  71.         }
  72.         $wallet_rial $this->doc->createQueryBuilder(Wallet::class)
  73.             ->field('userid')->equals($user_id)
  74.             ->field('pair')->equals($market_symbol)
  75.             ->getQuery()
  76.             ->getSingleResult();
  77.         $wallet_crypto $this->doc->createQueryBuilder(Wallet::class)
  78.             ->field('userid')->equals($user_id)
  79.             ->field('pair')->equals($crypto)
  80.             ->getQuery()
  81.             ->getSingleResult();
  82.         $usdt_buy_price $setting->getUsdtpricebuy();
  83.         $usdt_sell_price $setting->getUsdtpricesell();
  84.         if ($side === 'sell' && $amount $wallet_crypto->getballance()) {
  85.             $translated $this->translator->trans('ballance is LOw');
  86.             return new JsonResponse(['status' => '400''msg' => $translated]);
  87.         }
  88.         $crypto_buy $this->getLastPrice($crypto);
  89.         $crypto_buy += (($crypto_buy 0.1) / 100);
  90.         
  91.         if($market_symbol === "RIAL") {
  92.              $crypto_price_buy $crypto_buy $usdt_buy_price ;
  93.         }else {
  94.         $crypto_price_buy $crypto_buy ;
  95. }
  96.         if ($type === 'limit' && $price <= 0) {
  97.             $translated $this->translator->trans('Price is Low');
  98.             return new JsonResponse(['status' => '400''msg' => $translated]);
  99.         }
  100. $priceDifference abs(($price $crypto_price_buy) / $crypto_price_buy) * 100;
  101. if ($type === 'limit' && ($price <= || $priceDifference 2)) {
  102.     $translated $this->translator->trans('Price must be within ±2% range');
  103.     return new JsonResponse(['status' => '400''msg' => $translated]);
  104. }
  105.         // if ($type === 'limit' && !$this->isPriceWithinRange($crypto_buy, $price)) {
  106.         //     $translated = $this->translator->trans('price not correct');
  107.         //     return new JsonResponse(['status' => '400', 'msg' => $translated]);
  108.         // }
  109.         if ($type === 'market') {
  110.               
  111.            
  112.             
  113.             $price $crypto_buy;
  114.         }
  115.         if ($side === 'buy' && ($amount $crypto_price_buy) >= $wallet_rial->getballance()) {
  116.             $translated $this->translator->trans('ballance is LOw');
  117.             return new JsonResponse(['status' => '400''msg' => $translated]);
  118.         }
  119. if ($type === 'limit') {
  120.         // Freeze funds
  121.         if ($side === 'buy') {
  122.             $wallet_rial->setballance($wallet_rial->getballance() - ($amount $crypto_price_buy));
  123.             $wallet_rial->setFrozen(($amount $crypto_price_buy) + $wallet_rial->getFrozen());
  124.         } else {
  125.             $wallet_crypto->setballance($wallet_crypto->getballance() - $amount);
  126.             $wallet_crypto->setFrozen($amount $wallet_crypto->getFrozen());
  127.             
  128.         }
  129.         
  130.         
  131.         
  132.             
  133.         
  134.         
  135.         $this->doc->persist($wallet_rial);
  136.         $this->doc->persist($wallet_crypto);
  137.         $this->doc->flush();
  138.         
  139. }
  140.         
  141.         $order = new MarketOrder();
  142.         $order->setUserid($user_id);
  143.         $order->setAmount($amount);
  144.         $order->setPair($crypto);
  145.         $order->setSide($side);
  146.         $order->setType($type);
  147.         $order->setPrice($price);
  148.         $order->setTakeprofit("0");
  149.         $order->setStoploss("0");
  150.         $order->setTime(time());
  151.         $order->setFee(0);
  152.         $order->setApi('');
  153.         $order->setStatus("Pending");
  154.         $this->doc->persist($order);
  155.         $this->doc->flush();
  156.         if ($type === 'market') {
  157.             // Create an opposite order for the robot
  158.             $robot_id '666621eea320d65e3f6878ec';
  159.             // Mark the original order as filled
  160.             $order->setStatus('Filled');
  161.             $this->doc->persist($order);
  162.             $this->doc->flush();
  163.             // Create a new trade
  164.             $trade_id \Ramsey\Uuid\Uuid::uuid4()->toString(); // Generate a unique trade ID
  165.             $buy_user_id $side === 'buy' $user_id $robot_id;
  166.             $sell_user_id $side === 'sell' $user_id $robot_id;
  167.             $this->createNewTrade($buy_user_id$sell_user_id$amount$crypto,'market'$price$trade_id'Completed');
  168.             // Update wallets
  169.             $this->updateWallets($buy_user_id$sell_user_id$amount$crypto$price $market_symbol);
  170.         }
  171.         $translated $this->translator->trans('Order Submited');
  172.         return new JsonResponse(['status' => '200''msg' => $translated]);
  173.     }
  174.     #[Route('/api/order/cancelmarketsubmit'methods: ['POST'], name'order_cancelmarketsubmit')]
  175.     public function cancelMarketSubmit(Request $request): JsonResponse
  176.     {
  177.         $data json_decode($request->getContent(), false);
  178.         $user $this->getUser();
  179.         $order_id $data->id;
  180.         $order $this->doc->createQueryBuilder(MarketOrder::class)
  181.             ->field('id')->equals($order_id)
  182.             ->getQuery()
  183.             ->getSingleResult();
  184.         if ($order && $order->getStatus() === 'Pending') {
  185.             $this->doc->createQueryBuilder(MarketOrder::class)
  186.                 ->field('id')->equals($order_id)
  187.                 ->findAndUpdate()
  188.                 ->field('status')->set('cancel')
  189.                 ->getQuery()
  190.                 ->execute();
  191.             // Unfreeze funds
  192.             if ($order->getSide() === 'buy') {
  193.                 $wallet_rial $this->doc->createQueryBuilder(Wallet::class)
  194.                     ->field('userid')->equals($user->getId())
  195.                     ->field('pair')->equals("RIAL")
  196.                     ->getQuery()
  197.                     ->getSingleResult();
  198.                 $this->unfreezeFunds($wallet_rial$order->getAmount() * $order->getPrice());
  199.             } else {
  200.                 $wallet_crypto $this->doc->createQueryBuilder(Wallet::class)
  201.                     ->field('userid')->equals($user->getId())
  202.                     ->field('pair')->equals($order->getPair())
  203.                     ->getQuery()
  204.                     ->getSingleResult();
  205.                 $this->unfreezeFunds($wallet_crypto$order->getAmount());
  206.             }
  207.             $translated $this->translator->trans('Order Canceled');
  208.             return new JsonResponse(['status' => '200''msg' => $translated]);
  209.         }
  210.         return new JsonResponse(['status' => '400''msg' => 'Order not found or not cancelable']);
  211.     }
  212.     private function unfreezeFunds($wallet$amount)
  213.     {
  214.         $wallet->setballance($wallet->getballance() + $amount);
  215.         $wallet->setFrozen($wallet->getFrozen() - $amount);
  216.         $this->doc->persist($wallet);
  217.         $this->doc->flush();
  218.     }
  219.     private function createNewTrade($buy_user_id$sell_user_id$amount$pair $type$price$trade_id$status)
  220.     {
  221.         $trade = new Trade();
  222.         $trade->setAmount($amount);
  223.         $trade->setPair($pair);
  224.         $trade->setType($type);
  225.         $trade->setPrice($price);
  226.         $trade->setTime(time());
  227.         $trade->setStatus($status);
  228.         $trade->setFee(0);
  229.         $trade->setTradeid($trade_id);
  230.         $trade->setbuyUserid($buy_user_id);
  231.         $trade->setsellUserid($sell_user_id);
  232.         $this->doc->persist($trade);
  233.         $this->doc->flush();
  234.     }
  235.     private function createOrder($user_id$amount$pair$side$type$price$robot$status)
  236.     {
  237.         $order = new MarketOrder();
  238.         $order->setUserid($user_id);
  239.         $order->setAmount($amount);
  240.         $order->setPair($pair);
  241.         $order->setSide($side);
  242.         $order->setType($type);
  243.         $order->setPrice($price);
  244.         $order->setTakeprofit("0");
  245.         $order->setStoploss("0");
  246.         $order->setTime(time());
  247.         $order->setFee(0);
  248.         $order->setRobot($robot);
  249.         $order->setStatus($status);
  250.         $this->doc->persist($order);
  251.         $this->doc->flush();
  252.     }
  253.     private function updateOrder($order)
  254.     {
  255.         $this->doc->persist($order);
  256.         $this->doc->flush();
  257.     }
  258.     private function updateWallets($buy_user_id$sell_user_id$amount$pair$price $market_symbol)
  259.     {
  260.         $usdt_buy_price $this->doc->createQueryBuilder(Setting::class)
  261.             ->limit(1)
  262.             ->getQuery()
  263.             ->getSingleResult()
  264.             ->getUsdtpricebuy();
  265.             if($market_symbol === "RIAL") {
  266.                 $rial_amount $amount $price $usdt_buy_price;
  267.             } else {
  268.         $rial_amount $amount $price ;  // Corrected calculation of rial_amount
  269. }
  270.         // Update buyer's wallet
  271.         $this->addMoney($buy_user_id$pair$amount);
  272.         $this->removeMoney($buy_user_id$market_symbol$rial_amount);
  273.         // Update seller's wallet
  274.         $this->addMoney($sell_user_id$market_symbol$rial_amount);
  275.         $this->removeMoney($sell_user_id$pair$amount);
  276.     }
  277.     private function addMoney($user_id$pair$amount)
  278.     {
  279.         $wallet $this->getWallet($user_id$pair);
  280.         if ($wallet) {
  281.             $wallet->setballance($wallet->getballance() + $amount);
  282.             $this->updateWallet($wallet);
  283.         }
  284.     }
  285.     private function removeMoney($user_id$pair$amount)
  286.     {
  287.         $wallet $this->getWallet($user_id$pair);
  288.         if ($wallet) {
  289.             $wallet->setballance($wallet->getballance() - $amount);
  290.             $this->updateWallet($wallet);
  291.         }
  292.     }
  293.     private function getWallet($user_id$pair)
  294.     {
  295.         return $this->doc->createQueryBuilder(Wallet::class)
  296.             ->field('userid')->equals($user_id)
  297.             ->field('pair')->equals($pair)
  298.             ->getQuery()
  299.             ->getSingleResult();
  300.     }
  301.     private function updateWallet($wallet)
  302.     {
  303.         $this->doc->persist($wallet);
  304.         $this->doc->flush();
  305.     }
  306.     
  307.     
  308.         private function isPriceWithinRange($lastPrice$newPrice): bool
  309.     {
  310.         $minPrice $lastPrice 0.1;
  311.         $maxPrice $lastPrice 0.02;
  312.         return $newPrice >= $minPrice && $newPrice <= $maxPrice;
  313.     }
  314.     
  315. #[Route('/pub/order/matchengine'methods: ['GET'], name'trade_matchengine')]
  316. public function matchEngine(): JsonResponse
  317. {
  318.     $pairs = ["BTC""ETH""DOGE""ADA""SOL""DOT""TRX""BNB""LTC"];
  319.     foreach ($pairs as $pair) {
  320.         // Retrieve all limit buy and sell orders
  321.         $buy_limit_orders $this->getMarketOrderList($pair'buy''limit');
  322.         $sell_limit_orders $this->getMarketOrderList($pair'sell''limit');
  323.         // Get the last price of the pair
  324.         $last_price_buy round($this->getLastPrice($pair,'BUY'), 6);
  325.         
  326.         $last_price_sell round($this->getLastPrice($pair), 6);
  327.         
  328.   
  329.         // Process buy limit orders
  330.         foreach ($buy_limit_orders as $buy_order) {
  331.             
  332.             if ($buy_order->getPrice() >= $last_price_buy) {
  333.                 $this->processMatchingOrder($buy_order$buy_order->getPrice());
  334.             }
  335.         }
  336.         // Process sell limit orders
  337.         foreach ($sell_limit_orders as $sell_order) {
  338.             echo "o" $sell_order->getPrice() . 'o' $last_price_sell ;
  339.             if ($sell_order->getPrice() <= $last_price_sell) {
  340.                 $this->processMatchingOrder($sell_order$sell_order->getPrice());
  341.             }
  342.         }
  343.     }
  344.     return new JsonResponse(['status' => '200''msg' => 'Match Engine Run Successfully']);
  345. }
  346. private function processMatchingOrder($order$last_price)
  347. {
  348.     $user_id $order->getUserid();
  349.     $amount $order->getAmount();
  350.     $pair $order->getPair();
  351.     $side $order->getSide();
  352.     $opposite_side $side === 'buy' 'sell' 'buy';
  353.     $robot_id '666621eea320d65e3f6878ec';
  354.     // Create an opposite order for the robot
  355.     $this->createOrder($robot_id$amount$pair$opposite_side'market'$last_pricetrue'Filled');
  356.     // Mark the original order as filled
  357.     $order->setStatus('Filled');
  358.     $this->updateOrder($order);
  359.         $setting $this->doc->createQueryBuilder(Setting::class)
  360.             ->limit(1)
  361.             ->getQuery()
  362.             ->getSingleResult();
  363.         $usdt_buy_price $setting->getUsdtpricebuy();
  364.         $usdt_sell_price $setting->getUsdtpricesell();
  365.         $crypto_price_buy $this->getLastPrice($pair 'BUY') ;
  366.         $crypto_price_sell $this->getLastPrice($pair) ;
  367.         
  368.     // Create a new trade
  369.     $trade_id \Ramsey\Uuid\Uuid::uuid4()->toString();
  370.     $buy_user_id $side === 'buy' $user_id $robot_id;
  371.     $sell_user_id $side === 'sell' $user_id $robot_id;
  372.     $this->createNewTrade($buy_user_id$sell_user_id$amount$pair ,'limit'$last_price$trade_id'Completed');
  373.     
  374.     
  375.               $wallet_rial $this->doc->createQueryBuilder(Wallet::class)
  376.                     ->field('userid')->equals($user_id)
  377.                     ->field('pair')->equals("RIAL")
  378.                     ->getQuery()
  379.                     ->getSingleResult();
  380.                     
  381.                     
  382.             $wallet_crypto $this->doc->createQueryBuilder(Wallet::class)
  383.                     ->field('userid')->equals($user_id)
  384.                     ->field('pair')->equals($order->getPair())
  385.                     ->getQuery()
  386.                     ->getSingleResult();
  387.     
  388.     
  389.                if ($order->getSide() === 'buy') {
  390.       
  391.                     
  392.                $wallet_rial->setFrozen(  $wallet_rial->getFrozen()  - (($amount $crypto_price_buy) * $usdt_buy_price) );
  393.                $wallet_crypto->setballance($wallet_crypto->getballance() + $amount);
  394.                
  395.             } else {
  396.              
  397.                 $wallet_crypto->setFrozen$wallet_crypto->getFrozen() - $amount);
  398.                 $wallet_rial->setballance($wallet_rial->getballance() + (($amount $crypto_price_buy) * $usdt_buy_price));
  399.      
  400.    
  401.             }
  402.                  $this->updateWallet($wallet_rial);
  403.                 $this->updateWallet($wallet_crypto);
  404.     // Update wallets
  405.   //  $this->updateWallets($buy_user_id, $sell_user_id, $amount, $pair, $last_price);
  406. }
  407.     
  408.         private function getMarketOrderList($pair$side$type)
  409.     {
  410.         return $this->doc->createQueryBuilder(MarketOrder::class)
  411.             ->field('side')->equals($side)
  412.             ->field('type')->equals($type)
  413.             ->field('status')->equals('Pending')
  414.             ->field('pair')->equals($pair)
  415.          
  416.             ->limit(2000)
  417.             ->sort('time''asc')
  418.             ->getQuery()
  419.             ->execute();
  420.     }
  421.     
  422. }