src/Bundles/Portfolios/Controller/PortfolioController.php line 623

Open in your IDE?
  1. <?php
  2. namespace Bundles\Portfolios\Controller;
  3. use App\Entity\CurrencyHistory;
  4. use Bundles\Instruments\CashflowForward\Entity\HedgingPolitics;
  5. use Bundles\Portfolios\Repository\PortfolioRepository;
  6. use Bundles\RemoteSystems\Controller\CorpayController;
  7. use Doctrine\Persistence\ManagerRegistry;
  8. use Symfony\Component\HttpFoundation\Request;
  9. use Psr\Log\LoggerInterface;
  10. use Symfony\Component\Cache\Adapter\AdapterInterface;
  11. use App\Controller\ParentController;
  12. use Bundles\Portfolios\Entity\Portfolio;
  13. use Bundles\Portfolios\Form\PortfolioType;
  14. use Bundles\Instruments\Base\Entity\Forward;
  15. use Bundles\Instruments\Base\Entity\ForwardOperations;
  16. use Bundles\Instruments\Base\Entity\CashFlows;
  17. use App\Entity\Customer;
  18. use Bundles\Instruments\Base\Model\Report;
  19. use Bundles\Instruments\Swap\Entity\Swap;
  20. use Doctrine\DBAL\Connection;
  21. use Symfony\Component\HttpFoundation\JsonResponse;
  22. use App\Entity\CurrencyPair;
  23. use Bundles\Instruments\CashflowForward\Entity\BudgetRates;
  24. use Bundles\Messenger\Model\Actions;
  25. use Bundles\Messenger\Entity\Event;
  26. use Bundles\Messenger\Entity\Notification;
  27. use Psr\Cache\CacheItemPoolInterface;
  28. class PortfolioController extends ParentController {
  29. const STEP_PORTFOLIO = 2;
  30. const STEP_POLICY = 3;
  31. const STEP_BUDGET_RATES = 4;
  32. const STEP_CASH_FLOWS = 5;
  33. const STEP_TRADES = 6;
  34. const STEP_SUMMARY = 7;
  35. const STEP_ANALYSIS = 8;
  36. const STEP_REPORT = 9;
  37. protected $importValidatorModel;
  38. protected $currrentHedgingPolicy = null;
  39. protected $tradeTypes = [
  40. 'Cashflow committed' => 'committed_forward',
  41. 'Cashflow forecast' => 'cashflow_forward',
  42. 'Swap' => 'swap',
  43. 'Option' => 'option',
  44. ];
  45. protected $customerId = 0;
  46. protected $url = '/portfolios?action=list&menuIndex=1';
  47. protected $request;
  48. private ForwardOperations $tradeOperationRecord;
  49. protected bool $needFlush = false;
  50. private CorpayController $corpayController;
  51. public function __construct(CorpayController $corpayController) {
  52. $this->corpayController = $corpayController;
  53. }
  54. public function init(LoggerInterface $analyticsLogger, CacheItemPoolInterface $cache, Request $request, Connection $connection) {
  55. parent::initialize($request);
  56. $this->session = $this->get('session');
  57. $this->cache = $cache;
  58. $this->redirect = false;
  59. $this->connection = $connection;
  60. $this->step = $request->get('step') ?? self::STEP_PORTFOLIO;
  61. $this->show = $request->get('show') ?? ($this->session->get('show') ?? 'current');
  62. $this->sumGroup = $request->get('group');
  63. $this->tradeOperation = $request->get('operation');
  64. $this->tradeOperationId = $request->get('operationId');
  65. $this->analysisMethod = $request->get('analysisMethod');
  66. $this->cvarMethod = $request->get('cvarMethod');
  67. $this->edit = $request->get('edit') ?? '';
  68. $this->tradeFilter = [
  69. 'from' => ($request->get('customPeriodFrom') ?? ''),
  70. 'to' => ($request->get('customPeriodTo') ?? ''),
  71. ];
  72. $this->request = $request;
  73. $this->logger = $analyticsLogger;
  74. $this->portfolioId = $request->get('portfolioId') ?? 0;
  75. $oldPortfolioId = $this->session->get('portfolioId');
  76. if (!empty($this->portfolioId) && $this->portfolioId != $oldPortfolioId) {
  77. $this->session->set('portfolioId', $this->portfolioId);
  78. } elseif (!empty($oldPortfolioId) && empty($this->portfolioId)) {
  79. $this->portfolioId = $oldPortfolioId;
  80. }
  81. //licence restrictions
  82. $licence = $this->getCustomer()->getLicence();
  83. if ($this->step == self::STEP_PORTFOLIO) {
  84. $this->portfolioId = 0;
  85. $this->session->set('portfolioId', $this->portfolioId);
  86. $this->session->remove('analysisMethod');
  87. $this->session->remove('cvarMethod');
  88. $this->session->remove('sumGroup');
  89. $this->session->remove('sensitivity');
  90. $this->session->remove('show');
  91. $this->session->remove('tradeFilter');
  92. }
  93. if (!empty($this->analysisMethod)) {
  94. $this->session->set('analysisMethod', $this->analysisMethod);
  95. } else {
  96. $this->analysisMethod = $this->session->get('analysisMethod');
  97. }
  98. if (!empty($this->cvarMethod)) {
  99. $this->session->set('cvarMethod', $this->analysisMethod);
  100. } else {
  101. $this->cvarMethod = $this->session->get('cvarMethod') ?? 'full';
  102. }
  103. if (!empty($this->sumGroup)) {
  104. $this->session->set('sumGroup', $this->sumGroup);
  105. } else {
  106. $this->sumGroup = $this->session->get('sumGroup');
  107. }
  108. if (empty($this->sumGroup)) {
  109. $this->sumGroup = 'month';
  110. }
  111. $this->sensitivity = $request->get('sensitivity') ?? '';
  112. if (!empty($this->sensitivity)) {
  113. $this->session->set('sensitivity', $this->sensitivity);
  114. } else {
  115. $this->sensitivity = $this->session->get('sensitivity');
  116. }
  117. if (!empty($this->show)) {
  118. $this->session->set('show', $this->show);
  119. if ($this->show == 'custom' && (!empty($this->tradeFilter['from']) || !empty($this->tradeFilter['to']))) {
  120. $this->session->set('tradeFilter', $this->tradeFilter);
  121. }
  122. }
  123. $this->show = $this->session->get('show');
  124. $this->tradeFilter = $this->session->get('tradeFilter');
  125. $this->policyId = $request->get('policyId') ?? 0;
  126. $this->budgetRateId = $request->get('budgetRateId') ?? 0;
  127. $this->cashFlowId = $request->get('cashFlowId') ?? 0;
  128. $this->tradeId = $request->get('tradeId') ?? 0;
  129. }
  130. public function index(LoggerInterface $analyticsLogger, CacheItemPoolInterface $cache, Request $request, Connection $connection) {
  131. $this->init($analyticsLogger, $cache, $request, $connection);
  132. $this->portfolioId = $request->get('portfolioId') ?? (int)$this->session->get('portfolioId');
  133. if (!empty($this->portfolioId)) {
  134. $this->session->set('portfolioId', $this->portfolioId);
  135. $portfolio = $this->em->getRepository(Portfolio::class)->find($this->portfolioId);
  136. if (!empty($portfolio->getType()) && empty($this->edit)) {
  137. return $this->redirectToRoute('portfolio_'.$portfolio->getType(), [
  138. 'step' => $this->step,
  139. 'portfolioId' => $this->portfolioId,
  140. ]);
  141. }
  142. $this->data['portfolio'] = $portfolio;
  143. }
  144. $this->getPortfolios($request);
  145. $instrument = '@cashflowForward';
  146. $this->data['title'] = 'Portfolios';
  147. $this->data['url'] = $this->url;
  148. $this->data['show'] = 'current';
  149. $this->data['operation'] = $this->tradeOperation ?? null;
  150. $this->data['sumGroup'] = 'month';
  151. $this->data['edit'] = $this->edit;
  152. $this->data['tradeTypes'] = array_flip($this->tradeTypes);
  153. $this->data['portfolioId'] = $this->portfolioId;
  154. $this->data['functionalCurrency'] = $this->session->get('functionalCurr') ?? $this->getCustomer()->getFunctionalCurrency();
  155. // dump($this->data);
  156. return $this->render($instrument.'/index.html.twig', $this->data);
  157. }
  158. protected function getPortfolios($request) {
  159. $customer = $this->getCustomer();
  160. $this->customerId = $customer->getId();
  161. $customerPairs = $this->em->getRepository(Customer::class)->getCurrencyPairsByCustomer($this->customerId);
  162. $step = self::STEP_PORTFOLIO;
  163. $options = [
  164. 'tradeTypes' => $this->tradeTypes,
  165. 'customerPairs' => $customerPairs,
  166. 'licence' => $this->getCustomer()->getLicence(),
  167. ];
  168. $form = $this->getForm($step, null, $options);
  169. $form->handleRequest($request);
  170. if ($form->isSubmitted()) {
  171. $this->actionsPortfolio($form);
  172. }
  173. //Jeigu reikia patikrinti, ar useris yra prileidziamas prie visu portfelių
  174. $userPortfolios = $this->getUser()->getUserPortfolios();
  175. // dump($userPortfolios);
  176. if ($this->getUser()->isUser() && !$userPortfolios->isEmpty()) {
  177. $filtered = $userPortfolios->filter(function ($portfolio) {
  178. return $portfolio->getStatus() == 1;
  179. });
  180. // Rūšiuojam kolekciją, bet reikia konvertuoti į masyvą, kad galėtume naudoti usort
  181. $portfolios = $filtered->toArray();
  182. usort($portfolios, function ($a, $b) {
  183. return $b->getId() <=> $a->getId();
  184. });
  185. } else {
  186. $portfolios = $this->em->getRepository(Portfolio::class)->findBy([
  187. 'status' => 1,
  188. 'customer' => $this->customerId,
  189. ], ['id' => 'DESC']);
  190. }
  191. $this->data['portfolios'] = $portfolios;
  192. $this->data['portfolioValues'] = $this->getModel()->getPortfolioValues($this->data['portfolios'], $customer->getFunctionalCurrency());
  193. $this->data['form2'] = $this->getForm($step, ($this->data['portfolio'] ?? null), $options)->createView();
  194. $this->data['portfolioId'] = $this->session->get('portfolioId');
  195. $this->functionalCurrencyRate = $this->getModel()->getFunctionalCurrencyRate($customer->getFunctionalCurrency());
  196. if (!empty($this->data['portfolio'])) {
  197. $this->data['pair'] = $this->data['portfolio']->getCurrencyPair();
  198. $rateRecord = $this->em->getRepository(CurrencyHistory::class)->findLastRecord($this->data['pair']->getId());
  199. if (!empty($rateRecord)) {
  200. $this->data['rate'] = $rateRecord->getRate();
  201. $this->data['rateLastUpdate'] = $rateRecord->getUpdated();
  202. }
  203. $this->data['majorCurrency'] = $this->data['portfolio']->getCurrencyPair()->getCurrencyNameByNumber(1);
  204. $this->data['minorCurrency'] = $this->data['portfolio']->getCurrencyPair()->getCurrencyNameByNumber(2);
  205. $this->data['functionalCurrency'] = $this->data['majorCurrency'];
  206. $this->data['major'] = substr($this->data['majorCurrency'], 0, 2);
  207. $this->data['minor'] = substr($this->data['minorCurrency'], 0, 2);
  208. }
  209. }
  210. protected function actionsPortfolio($form = null) {
  211. $action = $form->get('delete')->isClicked() ? 'delete' : 'add';
  212. $formData = $form->getData();
  213. if ($action == 'delete') {
  214. if (empty($this->portfolioId)) {
  215. $this->addFlash('warning', 'No portfolio found');
  216. return false;
  217. }
  218. if (!empty($this->portfolioId)) {
  219. $portfolio = $this->em->getRepository(Portfolio::class)->findOneBy([
  220. 'status' => 1,
  221. 'id' => $this->portfolioId,
  222. 'customer' => $this->customerId,
  223. ]);
  224. }
  225. if (empty($this->portfolioId)) {
  226. $this->addFlash('warning', 'No portfolio found');
  227. return false;
  228. }
  229. $portfolio->setStatus(false);
  230. $this->em->persist($portfolio);
  231. $this->em->flush();
  232. $this->addFlash('success', 'Portfolio was deleted successfully');
  233. $this->portfolioId = 0;
  234. $this->redirect = true;
  235. } elseif ($action == 'add') {
  236. if (empty($this->portfolioId) || empty($formData->getId())) {
  237. $portfolio = new Portfolio();
  238. $portfolio->setCustomer($this->getCustomer());
  239. $portfolio->setStatus(true);
  240. } else {
  241. $portfolio = $this->em->getRepository(Portfolio::class)->findOneBy([
  242. 'status' => 1,
  243. 'id' => $this->portfolioId,
  244. 'customer' => $this->customerId,
  245. ]);
  246. }
  247. $currPair = $formData->getCurrencyPair();
  248. if (empty($currPair)) {
  249. $currPair = $this->em->getRepository(CurrencyPair::class)->find(1);
  250. }
  251. $portfolio->setCurrencyPair($currPair);
  252. $portfolio->setName($formData->getName());
  253. $portfolio->setDescription($formData->getDescription());
  254. $portfolio->setDirection($formData->getDirection());
  255. $portfolio->setType($formData->getType());
  256. $this->em->persist($portfolio);
  257. $this->em->flush();
  258. $this->session->set('portfolioId', $portfolio->getId());
  259. $this->addFlash('success', 'new portfolio was created/updated successfully');
  260. $this->redirect = true;
  261. }
  262. }
  263. protected function getForm($step, $object = null, $options = []) {
  264. switch ($step) {
  265. case self::STEP_PORTFOLIO:
  266. if (empty($object)) {
  267. $object = new Portfolio();
  268. }
  269. $objetType = PortfolioType::class;
  270. break;
  271. }
  272. $options['portfolioId'] = $this->portfolioId;
  273. $options['url'] = $this->url.'&step='.$step;
  274. return $this->createForm($objetType, $object, $options);
  275. }
  276. protected function getTradesOperations($request) {
  277. if (empty($this->data['trade'])) {
  278. return false;
  279. }
  280. $step = self::STEP_TRADES;
  281. /** @var Forward $trade */
  282. $trade = $this->data['trade'];
  283. $object = new ForwardOperations();
  284. $object->setRate($trade->getStrike());
  285. // $object->setDateMaturity($trade->getDateDelivery());
  286. $object->setAmount($trade->getAmountMinor());
  287. $object->setTradeNumber($trade->getTradeNumber());
  288. $object->setRate($trade->getStrike());
  289. $form = $this->getForm('tradesOperations', $object);
  290. $form->handleRequest($request);
  291. if ($form->isSubmitted() && $form->isValid()) {
  292. $this->actionsTradesOperations($form);
  293. }
  294. $this->data['tradeOperations'] = $this->em->getRepository(ForwardOperations::class)->findBy(['Trade' => $this->tradeId]);
  295. $this->data['formTradesOperations'] = $form->createView();
  296. if ($form->isSubmitted()) {
  297. return $this->redirect($this->url.'&step='.$step);
  298. }
  299. if (!empty($this->tradeOperationId) && $this->edit == 'delete') {
  300. //todo: ideti tikrinama ar turi teise useris
  301. /** @var ForwardOperations $operationRecord */
  302. $operationRecord = $this->em->getRepository(ForwardOperations::class)->find($this->tradeOperationId);
  303. if (empty($operationRecord)) {
  304. $this->addFlash('warning', 'No trade operation record was found');
  305. return false;
  306. }
  307. $tradeInfo = json_decode($operationRecord->getTradeOldData(), true);
  308. if (!empty($tradeInfo)) {
  309. //atstatome trado info
  310. /** @var Forward $tradeRecord */
  311. $tradeRecord = $this->em->getRepository(Forward::class)->find($operationRecord->getTrade());
  312. if (empty($tradeRecord)) {
  313. $this->addFlash('warning', 'No trade was found');
  314. return false;
  315. }
  316. $tradeRecord->setDateDelivery($tradeInfo['dateDelivery']);
  317. $tradeRecord->setAmount($tradeInfo['amount']);
  318. $tradeRecord->setAmountMinor($tradeInfo['amountMinor']);
  319. $this->em->persist($tradeRecord);
  320. }
  321. $cashflowInfo = json_decode($operationRecord->getCashflowOldData(), true);
  322. if (!empty($cashflowInfo)) {
  323. //atstatome cashflow info
  324. /** @var CashFlows $cashflowRecord */
  325. $cashflowRecord = $this->em->getRepository(CashFlows::class)->find($cashflowInfo['id']);
  326. if (empty($cashflowRecord)) {
  327. $this->addFlash('warning', 'No cashflow was found');
  328. return false;
  329. }
  330. $cashflowRecord->setCashAmount($cashflowInfo['cashAmount']);
  331. $cashflowRecord->setCashAmountMinor($cashflowInfo['cashAmountMinor']);
  332. $this->em->persist($cashflowRecord);
  333. }
  334. if ($operationRecord->getAction() == 'extension' && !empty($operationRecord->getExtensionTradeId())) {
  335. $forwardId = $operationRecord->getExtensionTradeId();
  336. $extensionTradeRecord = $this->em->getRepository(Forward::class)->find($forwardId);
  337. if (!empty($extensionTradeRecord)) {
  338. $swapRecord = $this->em->getRepository(Swap::class)->findOneBy(['Forward' => $forwardId]);
  339. if (!empty($swapRecord)) {
  340. $this->em->remove($swapRecord);
  341. }
  342. if (!empty($extensionTradeRecord->getCommittedCashflows())) {
  343. foreach ($extensionTradeRecord->getCommittedCashflows() as $cashflow) {
  344. $this->em->remove($cashflow);
  345. }
  346. }
  347. $this->em->remove($extensionTradeRecord);
  348. }
  349. }
  350. $this->em->remove($operationRecord);
  351. $this->em->flush();
  352. $this->addFlash('success', 'Trade operation was deleted and trade info was restored successfully ');
  353. $this->redirect = true;
  354. }
  355. }
  356. /**
  357. * @param $form
  358. * @return false|void
  359. * @throws \Doctrine\ORM\ORMException
  360. * @throws \Doctrine\ORM\OptimisticLockException
  361. */
  362. protected function actionsTradesOperations($form) {
  363. if (empty($this->tradeId)) {
  364. return false;
  365. }
  366. $formData = $form->getData();
  367. $trade = $this->data['trade'];
  368. $this->fillTradeOperationRecord($formData, $trade);
  369. /** @var Forward $tradeParent */
  370. $tradeParent = $this->data['trade'];
  371. $amount = $tradeParent->getAmountMinor() - $formData->getAmount();
  372. $newDate = $formData->getDateMaturity();
  373. if ($this->tradeOperation == 'extension') {
  374. if ($amount < 0) {
  375. $this->addFlash('warning', 'Amount cannot be greater than the original trade amount');
  376. return false;
  377. }
  378. if ($newDate < $tradeParent->getDateDelivery()) {
  379. $this->addFlash('warning', 'New date cannot be earlier than the original trade date');
  380. return false;
  381. }
  382. if ($amount == 0) { //koreguojam tik trade ir flowso datas
  383. $newTrade = new Forward();
  384. $newTrade->setPortfolio($this->data['portfolio']);
  385. $newTrade->setAmountMinor($formData->getAmount());
  386. $newTrade->setDateDelivery($formData->getDateMaturity());
  387. //reik sukurti ir cashflowsa nauja siai sumai
  388. $this->updateCashflows($tradeParent, $newTrade, $action = 'update');
  389. $tradeParent->setDateDelivery($newDate);
  390. $this->em->persist($tradeParent);
  391. $this->em->flush();
  392. return;
  393. }
  394. if ($amount > 0) {
  395. //mazinam seno trado amounta ir sukurti nauja trade
  396. //mazinam senam flowsui amounta ir sukurti nauja flowsa pagal nauja data ir amounta
  397. $newTrade = $this->createTradeExtension($tradeParent, $this->tradeOperationRecord);
  398. $this->updateCashflows($tradeParent, $newTrade, $action = 'extension');
  399. }
  400. } elseif ($this->tradeOperation == 'drawdown') {
  401. //koreguojam seno trade suma
  402. //dump($formData);
  403. $tradeParent->setStrike($formData->getRate());
  404. $tradeParent->setAmountMinor($amount);
  405. $tradeParent->setAmount($amount / $tradeParent->getStrike());
  406. //dump($tradeParent);
  407. $this->em->persist($tradeParent);
  408. $this->em->flush();
  409. // mazinam cashflowso amount
  410. $this->updateCashflows($tradeParent, null, $this->tradeOperation);
  411. $this->em->flush();
  412. }
  413. if (!empty($newTrade)) {
  414. $this->tradeOperationRecord->setExtensionTradeId($newTrade->getId());
  415. }
  416. $this->em->persist($this->tradeOperationRecord);
  417. $this->em->flush();
  418. $this->addFlash('success', 'Trade operation was created successfully');
  419. $this->redirect = true;
  420. }
  421. /**
  422. * @param Forward $trade
  423. * @param $cashflows
  424. * @return mixed
  425. */
  426. protected function setRelatedCashFlows($trade) {
  427. if (empty($trade->getCommittedCashflows())) {
  428. return null;
  429. }
  430. $relatedCashFlows = [];
  431. foreach ($trade->getCommittedCashflows() as $item) {
  432. $relatedCashFlows[] = $item->getId();
  433. }
  434. return $relatedCashFlows;
  435. }
  436. protected function fillTradeOperationRecord($formData, $trade) {
  437. $this->tradeOperationRecord = new ForwardOperations();
  438. $this->tradeOperationRecord->setAmount($formData->getAmount());
  439. $this->tradeOperationRecord->setDateMaturity($formData->getDateMaturity());
  440. $this->tradeOperationRecord->setRate($formData->getRate());
  441. $this->tradeOperationRecord->setComment($formData->getComment());
  442. $this->tradeOperationRecord->setTradeNumber($formData->getTradeNumber());
  443. $this->tradeOperationRecord->setAction($this->tradeOperation);
  444. $this->tradeOperationRecord->setTrade($trade);
  445. $tradeOldData = [
  446. 'tradeNumber' => $trade->getTradeNumber(),
  447. 'dateDelivery' => $trade->getDateDelivery(),
  448. 'amount' => (int)$trade->getAmount(),
  449. 'amountMinor' => (int)$trade->getAmountMinor(),
  450. 'strike' => $trade->getStrike(),
  451. 'policyRatio' => $trade->getPolicyRatio(),
  452. 'relatedCashFlows' => $this->setRelatedCashFlows($trade),
  453. ];
  454. $this->tradeOperationRecord->setTradeOldData(json_encode($tradeOldData));
  455. $this->em->persist($this->tradeOperationRecord);
  456. }
  457. /**
  458. * @param CashFlows $cashflow
  459. * @return void
  460. */
  461. protected function collectOldDataForRestore($cashflow) {
  462. $cashflowOldData = [
  463. 'id' => $cashflow->getId(),
  464. 'cashAmount' => (int)$cashflow->getCashAmount(),
  465. 'cashAmountMinor' => (int)$cashflow->getCashAmountMinor(),
  466. 'flowDate' => (int)$cashflow->getFlowDate(),
  467. ];
  468. $this->setTradeOperationByParam('cashflowOldData', json_encode($cashflowOldData));
  469. }
  470. protected function setTradeOperationByParam($param, $value) {
  471. $methodName = 'set'.ucfirst($param);
  472. if (method_exists($this->tradeOperationRecord, $methodName)) {
  473. $this->tradeOperationRecord->$methodName($value);
  474. }
  475. $this->em->persist($this->tradeOperationRecord);
  476. }
  477. protected function createTradeExtension($tradeParent, $formData) {
  478. /** @var Forward $tradeParent */
  479. $tradeParent = $this->data['trade'];
  480. /// naujas trade
  481. $tradeRecord = new Forward();
  482. $tradeRecord->setPortfolio($this->data['portfolio']);
  483. $tradeRecord->setParentId($tradeParent->getId());
  484. $tradeRecord->setCurrencyPair($tradeParent->getCurrencyPair());
  485. $tradeRecord->setAmount($formData->getAmount() / $formData->getRate());
  486. $tradeRecord->setAmountMinor($formData->getAmount());
  487. $tradeRecord->setDateExpire($tradeParent->getDateExpire());
  488. $tradeRecord->setDateDelivery($formData->getDateMaturity());
  489. $tradeRecord->setExposureDirection($tradeParent->getExposureDirection());
  490. $tradeRecord->setStatus(true);
  491. $tradeRecord->setStrike($formData->getRate());
  492. $tradeRecord->setCounterparty($tradeParent->getCounterparty());
  493. $tradeRecord->setComment($formData->getComment());
  494. $tradeRecord->setTradeNumber($formData->getTradeNumber());
  495. $dateExpire = date_create($tradeRecord->getDateExpire());
  496. $tradeRecord->setRate($this->getRate($tradeRecord->getCurrencyPair(), $dateExpire->format('Y-m-d')));
  497. $hedgePolicyRatio = $this->getHedgePolicy($this->currrentHedgingPolicy, $dateExpire->format('Y'), $dateExpire->format('W'));
  498. $tradeRecord->setPolicyRatio($hedgePolicyRatio);
  499. if (!empty($tradeParent->getCommittedCashflows())) {
  500. foreach ($tradeParent->getCommittedCashflows() as $cashflow) {
  501. $tradeRecord->addCommittedCashflows($cashflow);
  502. }
  503. }
  504. $this->em->persist($tradeRecord);
  505. $this->em->flush();
  506. //$tradeOperationRecord->setExtensionTradeId($tradeRecord->getId());
  507. return $tradeRecord;
  508. }
  509. protected function updateCashflows($tradeParent, $newTrade, $action) {
  510. return; //specifika priklauso pagal tipa, ziureti child klases
  511. }
  512. /**
  513. * naudoja ajax route /analysis/{portfolioId}/{rate}
  514. * @param $portfolioId
  515. * @param $rate
  516. * @return JsonResponse
  517. */
  518. public function analysisAjax(CacheItemPoolInterface $cache, Request $request, Connection $connection) {
  519. parent::initialize($request);
  520. $this->cache = $cache;
  521. $this->connection = $connection;
  522. $this->session = $this->container->get('session');
  523. $portfolioId = $request->get('portfolioId') ?? '';
  524. $this->portfolioId = $portfolioId;
  525. $rate = $request->get('rate') ?? '';
  526. $method = $request->get('analysisMethod') ?? '';
  527. if (empty((int)$portfolioId) || empty((float)$rate)) {
  528. return new JsonResponse('error data', 400);
  529. }
  530. $portfolio = $this->em->getRepository(Portfolio::class)->findOneBy([
  531. 'id' => $portfolioId,
  532. 'customer' => $this->getCustomer()->getId(),
  533. ], ['id' => 'DESC']);
  534. if (empty($portfolio)) {
  535. return new JsonResponse('unauthorized access', 400);
  536. }
  537. $trades = $this->em->getRepository(Forward::class)->findByDates([
  538. 'Portfolio' => (int)$portfolioId,
  539. 'show' => 'current',
  540. 'type_not' => 'spot',
  541. ]);
  542. $cashflows = $this->em->getRepository(CashFlows::class)->findByDates([
  543. 'Portfolio' => (int)$portfolioId,
  544. 'show' => 'current',
  545. ]);
  546. $budgetRates = $this->em->getRepository(BudgetRates::class)->findByDates(['Portfolio' => (int)$this->portfolioId]);
  547. $data = [
  548. 'cashFlows' => $cashflows,
  549. 'trades' => $trades,
  550. 'budgetRates' => $budgetRates,
  551. ];
  552. $budgetRates = $method == 'budget' ? $this->getBudgetRateList($data) : [];
  553. $this->getModel()->getFunctionalCurrencyRate($this->getFunctionalCurrency());
  554. $analysis['trades'] = $this->getModel()->tradeValuation($trades, (float)$rate, $budgetRates, $cashflows);
  555. $forwardPoints = [0 => ['bid' => $rate]];
  556. $forwardPointsByPair = $this->getForwardPoints($portfolio->getCurrencyPair()->getName());
  557. if (!empty($forwardPointsByPair)) {
  558. $forwardPoints += $forwardPointsByPair;
  559. }
  560. $analysis['totalValuation'] = 0;
  561. $periods = [];
  562. if (!empty($analysis['trades']['diffByMonth'])) {
  563. $analysis['totalValuation'] = round(array_sum($analysis['trades']['diffByMonth']));
  564. $periods = array_keys($analysis['trades']['diffByMonth']);
  565. }
  566. //vykdomas vertinimas trade ir cashflows
  567. $analysis['cashflows']['diffByMonth'] = [];
  568. $portfolioDirection = $portfolio->getDirection();
  569. try {
  570. $analysis['cashflows'] = $this->getModel()->cashflowValuation($cashflows, $budgetRates, $portfolioDirection, (float)$rate, $analysis['trades'] ?? []);
  571. } catch (\Exception $e) {
  572. return new JsonResponse('error on cashflows valuation calcs', 200);
  573. }
  574. if ($method == 'budget') {
  575. if (!empty($analysis['cashflows']['diffByMonth'])) {
  576. $analysis['totalValuation'] += round(array_sum($analysis['cashflows']['diffByMonth']));
  577. }
  578. }
  579. // $spot = $this->getRate($portfolio->getCurrencyPair());
  580. foreach ($forwardPoints as $days => $points) {
  581. $period = date_create(' + '.$days.' days')->format('Y-m');
  582. // $pointDiff = (float)$points['bid'] - $spot;
  583. // $pointDiff = 1 + $pointDiff / $spot;
  584. // $analysis['forwards'][$period] = round((float)$rate * $pointDiff, 5);
  585. if (empty($days)) {
  586. $analysis['forwards'][$period] = (float)$rate;
  587. }
  588. if (!empty($analysis['cashflows']) && empty($analysis['cashflows']['diffByMonth'][$period])) {
  589. $analysis['cashflows']['diffByMonth'][$period] = 0;
  590. ksort($analysis['cashflows']['diffByMonth']);
  591. }
  592. if (!empty($analysis['trades']) && empty($analysis['trades']['diffByMonth'][$period])) {
  593. $analysis['trades']['diffByMonth'][$period] = 0;
  594. ksort($analysis['trades']['diffByMonth']);
  595. }
  596. }
  597. foreach ($periods as $period) {
  598. if (empty($analysis['forwards'][$period])) {
  599. $analysis['forwards'][$period] = 'null';
  600. }
  601. }
  602. $analysis['minValue'][1] = $this->getModel()->getMin($analysis['trades']['hedgeRates'] ?? [], $analysis['cashflows']['budgetRateByMonth'] ?? []);
  603. $analysis['maxValue'][1] = $this->getModel()->getMax($analysis['trades']['hedgeRates'] ?? [], $analysis['cashflows']['budgetRateByMonth'] ?? []);
  604. return new JsonResponse($analysis, 200);
  605. }
  606. /**
  607. * @return Report
  608. */
  609. protected function getReportModel($renew = false) {
  610. if (empty($this->reportModel) || $renew) {
  611. $excel = $this->get('Excel');
  612. $this->reportModel = new Report($this->em, $excel);
  613. }
  614. return $this->reportModel;
  615. }
  616. protected function getEventAlertStatuses() {
  617. $this->marginCalls = []; //pagal gauta final kursa, patikrinama, ar spotas nevirsyja budget Rate
  618. $statuses = [];
  619. $actionModel = new Actions($this->connection, $this->data['cashFlows']);
  620. foreach ($this->data['cashFlows'] as $item) {
  621. $flowId = $item->getId();
  622. $finalRate = $actionModel->getFinalPointsCashflow($item);
  623. $this->marginCalls[$flowId] = $finalRate != '-' ? round($finalRate, 5) : $finalRate;
  624. $statuses[$flowId] = $actionModel::ALERT_STATUS_DISABLED;
  625. if (!empty($finalRate) && $finalRate !== '-') {
  626. $statuses[$flowId] = $actionModel::ALERT_STATUS_READY;
  627. if ($this->hasAlert($flowId)) {
  628. $statuses[$flowId] = $actionModel::ALERT_STATUS_ACTIVE;
  629. }
  630. }
  631. }
  632. return $statuses;
  633. }
  634. protected function hasAlert($flowId) {
  635. if (empty($this->eventAlerts)) {
  636. $eventAlerts = $this->em->getRepository(Event::class)->findBy([
  637. 'User' => $this->getUser(),
  638. 'objectType' => 'flow',
  639. 'active' => true,
  640. ]);
  641. foreach ($eventAlerts as $item) {
  642. $this->eventAlerts[$item->getFlowId()] = $item;
  643. }
  644. }
  645. return !empty($this->eventAlerts[$flowId]);
  646. }
  647. protected function notificationsAsSeen() {
  648. $user = $this->getUser();
  649. $portfolioId = $this->portfolioId;
  650. $seenMessages = $this->em->getRepository(Notification::class)->unseenMessagesBy($user, $portfolioId);
  651. if (empty($seenMessages)) {
  652. return true;
  653. }
  654. foreach ($seenMessages as $item) {
  655. $item->setSeen(true);
  656. $this->em->persist($item);
  657. $this->em->flush();
  658. }
  659. return true;
  660. }
  661. protected function getHedgePolicy(HedgingPolitics $currentPolicy = null, $years, $week) {
  662. return 1;
  663. }
  664. /**
  665. * @param $flowId
  666. * @param $type
  667. * @return \Symfony\Component\HttpFoundation\Response
  668. */
  669. protected function callAlert($flowId, $type) {
  670. return $this->forward('Bundles\Messenger\Controller\EventController::index', [
  671. 'flowId' => $flowId,
  672. 'type' => $type,
  673. ]);
  674. }
  675. public function getPortfoliosByCompany($companyId, PortfolioRepository $portfolioRepository): JsonResponse {
  676. if (empty($companyId)) {
  677. return new JsonResponse(['error' => 'Company ID is required'], 400);
  678. }
  679. $portfolios = $portfolioRepository->findBy(['status' => 1, 'customer' => $companyId]);
  680. $data = array_map(function ($p) {
  681. return ['id' => $p->getId(), 'name' => $p->getName()];
  682. }, $portfolios);
  683. return new JsonResponse($data);
  684. }
  685. }