src/Bundles/Portfolios/Controller/PortfolioCashflowForwardController.php line 1458

Open in your IDE?
  1. <?php
  2. namespace Bundles\Portfolios\Controller;
  3. use App\Entity\CurrencyHistory;
  4. use App\Models\systems\broker\Corpay;
  5. use Bundles\AiStudio\forms\PolicyType1;
  6. use Bundles\AiStudio\forms\PolicyType2;
  7. use Bundles\Instruments\CashflowForward\Entity\ResidualCash;
  8. use Bundles\RemoteSystems\Controller\CorpayController;
  9. use Symfony\Component\HttpFoundation\Request;
  10. use Psr\Log\LoggerInterface;
  11. use Bundles\Portfolios\Form\PortfolioType;
  12. use Bundles\Instruments\CashflowForward\Form\HedgingPoliticsType;
  13. use Bundles\Portfolios\Entity\Portfolio;
  14. use Bundles\Instruments\CashflowForward\Entity\HedgingPolitics;
  15. use Bundles\Instruments\CashflowForward\Entity\BudgetRates;
  16. use Bundles\Instruments\Base\Entity\CashFlows;
  17. use Bundles\Instruments\Base\Entity\Forward;
  18. use Bundles\Instruments\Base\Entity\ForwardOperations;
  19. use Bundles\Instruments\CashflowForward\Entity\PolicyGenerator;
  20. use Bundles\Instruments\CashflowForward\Form\BudgetRateType;
  21. use Bundles\Instruments\CashflowForward\Form\CashFlowType;
  22. use Bundles\Instruments\Base\Form\ForwardType;
  23. use Bundles\Instruments\CashflowForward\Model\HedgingPolicy;
  24. use Bundles\Instruments\CashflowForward\Form\PolicyGeneratorType;
  25. use App\Entity\CurrencyPair;
  26. use Bundles\Instruments\CashflowForward\Form\TradesOperationsType;
  27. use App\Models\ForwardPoints;
  28. use Symfony\Component\HttpFoundation\JsonResponse;
  29. use Bundles\Instruments\Base\Model\CalcsVaR;
  30. use App\Entity\Counterparty;
  31. use Bundles\Instruments\Base\Entity\ReportFilter;
  32. use Bundles\Instruments\Base\Form\ReportFilterType;
  33. use Bundles\Instruments\CashflowForward\Model\ImportValidator;
  34. use Doctrine\DBAL\Connection;
  35. use Psr\Cache\CacheItemPoolInterface;
  36. class PortfolioCashflowForwardController extends PortfolioController {
  37. protected $export = false;
  38. protected $url = '/portfolios/cashflow-forward?menuIndex=1';
  39. protected $importDataOverwrite = false;
  40. protected $importDataAsSpotTrades = false;
  41. protected $importCorpay = false;
  42. public function index(LoggerInterface $analyticsLogger, CacheItemPoolInterface $cache, Request $request, Connection $connection) {
  43. $this->init($analyticsLogger, $cache, $request, $connection);
  44. if (!empty($this->portfolioId)) {
  45. $this->session->set('portfolioId', $this->portfolioId);
  46. $userPortfolios = $this->getUser()->getUserPortfolios();
  47. if ($this->getUser()->isUser() && !$userPortfolios->isEmpty()) {
  48. $portfolio = $userPortfolios->filter(function ($portfolio) {
  49. return $portfolio->getId() == $this->portfolioId;
  50. });
  51. $portfolio = $portfolio[0] ?? null;
  52. } else {
  53. $portfolio = $this->em->getRepository(Portfolio::class)->findOneBy([
  54. 'id' => $this->portfolioId,
  55. 'status' => 1,
  56. 'customer' => $this->getCustomer()->getId(),
  57. ]);
  58. }
  59. if (empty($portfolio) || $portfolio->getType() !== 'cashflow_forward') {
  60. $this->addFlash('warning', 'portfolio id '.$this->portfolioId.' not found or not allowed to access');
  61. return $this->redirect($this->url.'&step='.self::STEP_PORTFOLIO);
  62. }
  63. $this->data['portfolio'] = $portfolio;
  64. }
  65. $this->data['sumGroup'] = $this->sumGroup ?? 'month';
  66. $this->data['tradeFilter'] = $this->tradeFilter ?? '';
  67. $this->getPortfolios($request);
  68. if ($this->step > self::STEP_PORTFOLIO) {
  69. $this->getPolicies($request);
  70. if ($this->redirect) {
  71. return $this->redirect($this->url.'&step='.$this->step);
  72. }
  73. $this->getBudgetRates($request);
  74. $this->getCashFlows($request);
  75. $this->getTrades($request);
  76. $this->getTradesOperations($request);
  77. // $this->getResidualCash($request);
  78. $this->getAnalysis();
  79. $this->getReport($request);
  80. $this->notificationsAsSeen();
  81. }
  82. if (!empty($this->export)) {
  83. return $this->export;
  84. }
  85. if ($this->redirect) {
  86. return $this->redirect($this->url.'&step='.$this->step);
  87. }
  88. $this->data['summaryTotals'] = $this->getModel()->getSummaryTotals();
  89. $this->data['step'] = $this->step;
  90. $this->data['title'] = 'Portfolios';
  91. $this->data['action'] = 'portfolio';
  92. $this->data['edit'] = $this->edit;
  93. $this->data['url'] = $this->url;
  94. $this->data['show'] = $this->show;
  95. $this->data['operation'] = $this->tradeOperation ?? null;
  96. $this->data['tradeTypes'] = array_flip($this->tradeTypes);
  97. $instrument = '@cashflowForward';
  98. // dump($this->data);
  99. if (!empty($this->getCustomer()->isUseRemoteApi()) && !empty($this->getCustomer()->getRemoteUserCode()) && $this->getUser()->isAccessRemoteSystems()) {
  100. $this->data['remoteSystem'] = $this->getCustomer()->getRemoteSystem();
  101. }
  102. return $this->render($instrument.'/index.html.twig', $this->data);
  103. }
  104. protected function getPolicies($request) {
  105. if (empty($this->portfolioId) || $this->redirect) {
  106. return false;
  107. }
  108. $step = self::STEP_POLICY;
  109. $formGenerator = $this->getForm('policyGenerator');
  110. $formGenerator->handleRequest($request);
  111. if ($formGenerator->isSubmitted() && $formGenerator->isValid()) {
  112. $currencyPairSelected = $this->data['portfolio']->getCurrencyPair();
  113. $existsCurrency = $this->em->getRepository(HedgingPolitics::class)->findOneBy([
  114. 'Portfolio' => $this->portfolioId,
  115. 'CurrencyPair' => $currencyPairSelected,
  116. ]);
  117. if (!empty($existsCurrency) && $existsCurrency->getModel() != 'static_policy') {
  118. $this->addFlash('error', 'Selected currency pair '.$currencyPairSelected->getName().' is already in policy. Delete existing or add policy manually');
  119. } else {
  120. $policyGenerator = $formGenerator->getData();
  121. $policyGenerator->setCurrencyPair($currencyPairSelected);
  122. $policyGenerator->setExposureDirection($this->data['portfolio']->getDirection() == 'pay' ? 'Down' : 'Up');
  123. $policyRanges = $this->getModel()->generatePolicies($policyGenerator);
  124. if (!empty($policyRanges)) {
  125. if (!empty($existsCurrency)) {
  126. $this->actionsPolicies($policyGenerator, [], 'deleteAll'); //trinam sena
  127. }
  128. $this->actionsPolicies($policyGenerator, $policyRanges);
  129. }
  130. }
  131. }
  132. $this->data['formGeneratorPolicy'] = $formGenerator->createView();
  133. $form = $this->getForm($step);
  134. $form->handleRequest($request);
  135. if ($form->isSubmitted() && $form->isValid()) {
  136. $this->actionsPolicies($form);
  137. }
  138. if (!empty($this->policyId)) {
  139. $this->data['policy'] = $this->em->getRepository(HedgingPolitics::class)->find($this->policyId);
  140. }
  141. $policies = $this->em->getRepository(HedgingPolitics::class)->findBy(['Portfolio' => $this->portfolioId], ['boundaryLow' => 'ASC']);
  142. if (!empty($policies) && $policies[0]->getModel() == 'dynamic_policy') {
  143. $currentRate = $this->getModel()->getRate($this->data['portfolio']->getCurrencyPair());
  144. $needRegenerate = true;
  145. foreach ($policies as &$policy) {
  146. if ($currentRate >= (float)$policy->getBoundaryLow() && $currentRate < (float)$policy->getBoundaryUpper()) {
  147. $policy->setCurrentRate($currentRate);
  148. $this->currrentHedgingPolicy = $policy;
  149. $needRegenerate = false;
  150. break;
  151. }
  152. }
  153. if (!empty($request->get('regeneratePolicy'))) {
  154. $needRegenerate = true;// inicijuota naudotojo, forcinam perkurima
  155. }
  156. if ($needRegenerate) {
  157. $formGenerator = new PolicyGenerator();
  158. $formGenerator->setCurrencyPair($this->data['portfolio']->getCurrencyPair());
  159. $formGenerator->setExposureDirection($this->data['portfolio']->getDirection() == 'pay' ? 'Down' : 'Up');
  160. $formGenerator->setStepRange(1.5);
  161. $formGenerator->setNumberRanges(9);
  162. $policyRanges = $this->getModel()->generatePolicies($formGenerator);
  163. if (!empty($policyRanges)) {
  164. $this->actionsPolicies($formGenerator, [], 'deleteAll'); //trinam sena
  165. $this->actionsPolicies($formGenerator, $policyRanges); //kuriam nauja
  166. }
  167. }
  168. }
  169. // static policy turi kas menesi atsinaujinti schedule
  170. elseif (!empty($policies) && $policies[0]->getModel() == 'static_policy') {
  171. $policy = $policies[0];
  172. $schedule = $policy->getSchedule();
  173. if ($schedule[1]['executionDate'] < date_create()->format('Y-m-d')) {
  174. $formGenerator = $this->getForm('policyGenerator');
  175. $formGenerator->getData()->setMonths(count($policy->getSchedule()));
  176. $formGenerator->getData()->setModel($policy->getModel());
  177. $policy->setSchedule($this->getModel()->generatePolicies($formGenerator->getData()));
  178. $this->em->persist($policy);
  179. $this->em->flush();
  180. $this->addFlash('success', 'Static policy schedule is updated');
  181. }
  182. $policies = [$policy];
  183. }
  184. $this->data['policies'] = $policies;
  185. $this->data['policyModel'] = count($policies) == 1 ? 'static_policy' : 'dynamic_policy';
  186. $this->data['form'.$step] = $this->getForm($step, ($this->data['policy'] ?? null))->createView();
  187. $options = [
  188. 'customerPairs' => [$this->data['portfolio']->getCurrencyPair()->getId() ?? []],
  189. ];
  190. $this->data['policyForm2'] = $this->createForm(PolicyType2::class, null, $options)->createView();
  191. $this->data['symbol'] = $this->data['portfolio']->getCurrencyPair()->getCurrencyNameByNumber(2);
  192. }
  193. protected function getBudgetRates($request) {
  194. if (empty($this->portfolioId) || $this->redirect) {
  195. return false;
  196. }
  197. $step = self::STEP_BUDGET_RATES;
  198. $form = $this->getForm($step, null, ['portfolioId' => $this->portfolioId]);
  199. $form->handleRequest($request);
  200. if ($form->isSubmitted() && $form->isValid()) {
  201. $this->actionsBudgetRates($form);
  202. }
  203. if (!empty($this->budgetRateId)) {
  204. $this->data['budgetRate'] = $this->em->getRepository(BudgetRates::class)->find($this->budgetRateId);
  205. }
  206. $this->data['budgetRates'] = $this->em->getRepository(BudgetRates::class)->findByDates([
  207. 'Portfolio' => $this->portfolioId,
  208. // 'show' => $this->show,
  209. ]);
  210. $this->data['form'.$step] = $this->getForm($step, ($this->data['budgetRate'] ?? null))->createView();
  211. }
  212. protected function getCashFlowByTrade($trade) {
  213. return $this->em->getRepository(CashFlows::class)->findByFixedDates([
  214. 'Portfolio' => $trade->getPortfolio()->getId(),
  215. 'dateFrom' => date_create($trade->getDateDelivery())->format('Y-m-01'),
  216. 'dateTo' => date_create($trade->getDateDelivery())->format('Y-m-t'),
  217. ]);
  218. }
  219. protected function getCashFlows($request) {
  220. if (empty($this->portfolioId) || empty($this->data['portfolio']) || $this->redirect) {
  221. return false;
  222. }
  223. $step = self::STEP_CASH_FLOWS;
  224. $form = $this->getForm($step, null, ['portfolioId' => $this->portfolioId]);
  225. $form->handleRequest($request);
  226. $importDataCashFlow = $request->request->get('importDataCashFlow');
  227. $this->importDataOverwrite = $request->request->get('dataOverwriteCashFlow');
  228. if (($form->isSubmitted() && $form->isValid()) || !empty($importDataCashFlow)) {
  229. $this->actionsCashFlows($form, $importDataCashFlow);
  230. }
  231. if (!empty($this->cashFlowId)) {
  232. $cashFlow = $this->em->getRepository(CashFlows::class)->find($this->cashFlowId);
  233. $cashFlow->setRate($this->getRate($cashFlow->getCurrencyPair()));
  234. $this->data['cashFlow'] = $cashFlow;
  235. //dump($cashFlow);
  236. }
  237. $cashFlows = $this->em->getRepository(CashFlows::class)->findByDates([
  238. 'Portfolio' => $this->portfolioId,
  239. 'show' => $this->show,
  240. ]);
  241. $this->data['cashFlowSumAmount'] = $this->getModel()->getCashFlowSums($cashFlows, 'minor');
  242. $this->data['cashFlowSumAmountMajor'] = $this->getModel()->getCashFlowSums($cashFlows);
  243. $this->data['cashFlows'] = $this->getModel()->setCashFlowBudgetRates($cashFlows, $this->data['budgetRates']);
  244. $this->data['eventAlerts'] = $this->getEventAlertStatuses();
  245. $this->data['marginCalls'] = $this->marginCalls;
  246. $this->data['spot'] = $this->getRate($this->data['portfolio']->getCurrencyPair());
  247. $this->data['form'.$step] = $this->getForm($step, ($this->data['cashFlow'] ?? null))->createView();
  248. if ($form->isSubmitted()) {
  249. return $this->redirect($this->url.'&step='.$step);
  250. }
  251. }
  252. protected function getTrades($request) {
  253. if (empty($this->portfolioId) || empty($this->data['portfolio']) || $this->redirect) {
  254. return false;
  255. }
  256. $step = self::STEP_TRADES;
  257. $options = ['portfolioId' => $this->portfolioId, 'customer' => $this->getCustomer()->getId()];
  258. $form = $this->getForm($step, null, $options);
  259. $form->handleRequest($request);
  260. $importDataTrades = $request->request->get('importDataTrades');
  261. $this->importCorpay = $request->request->get('importCorpay');
  262. $this->importDataOverwrite = $request->request->get('dataOverwriteTrades');
  263. $this->importDataAsSpotTrades = $request->request->get('importSpotTrades') ?? false;
  264. if (($form->isSubmitted() && $form->isValid()) || !empty($importDataTrades)) {
  265. $this->actionsTrades($form, $importDataTrades);
  266. }
  267. if (!empty($this->tradeId)) {
  268. $this->data['trade'] = $this->em->getRepository(Forward::class)->find($this->tradeId);
  269. }
  270. $tradesOrigin = $this->em->getRepository(Forward::class)->findByDates([
  271. 'Portfolio' => $this->portfolioId,
  272. 'show' => $this->show,
  273. 'tradeFilter' => $this->tradeFilter,
  274. 'type' => 'forward',
  275. ]);
  276. $spotTrades = $this->em->getRepository(Forward::class)->findByDates([
  277. 'Portfolio' => $this->portfolioId,
  278. 'show' => $this->show,
  279. 'tradeFilter' => $this->tradeFilter,
  280. 'type' => 'spot',
  281. ]);
  282. $cashFlows = $this->em->getRepository(CashFlows::class)->findByDates([
  283. 'Portfolio' => $this->portfolioId,
  284. 'show' => $this->show,
  285. ]);
  286. /// rikia pasiaiskinti kas kviecia. Nezinau ar dar reikalingas!
  287. if (!empty($request->get('updatePolicy'))) {
  288. //pasiimam tradus
  289. $allTrades = $this->em->getRepository(Forward::class)->findBy([
  290. 'status' => true,
  291. 'show' => 'current',
  292. 'type' => 'forward',
  293. ]);
  294. $policies = $this->em->getRepository(HedgingPolitics::class)->findBy(['Portfolio' => $this->portfolioId], ['boundaryLow' => 'ASC']);
  295. if (empty($policies)) {
  296. return true;
  297. }
  298. foreach ($allTrades as $item) {
  299. $currentRate = $item->getRate();
  300. foreach ($policies as &$policy) {
  301. if ($currentRate >= (float)$policy->getBoundaryLow() && $currentRate < (float)$policy->getBoundaryUpper()) {
  302. $this->currrentHedgingPolicy = $policy;
  303. break;
  304. }
  305. }
  306. $dateExpire = date_create($item->getDateDelivery());
  307. $hedgePolicyRatio = $this->getHedgePolicy($this->currrentHedgingPolicy, $dateExpire->format('Y'), $dateExpire->format('W'));
  308. $item->setPolicyRatio($hedgePolicyRatio);
  309. $this->em->persist($item);
  310. }
  311. $this->em->flush();
  312. }
  313. $this->data['trades'] = $tradesOrigin;
  314. $this->data['spotTrades'] = $spotTrades;
  315. $this->data['tradeFilter'] = $this->tradeFilter;
  316. foreach ($tradesOrigin as $item) {
  317. $this->data['tradeList'][$item->getId()] = $item;
  318. }
  319. $this->data['portfolioOperations'] = $this->em->getRepository(ForwardOperations::class)->findAllByPortfolio($this->portfolioId, 'DESC');
  320. $this->data['valuation'] = $this->getModel()->tradeValuation($this->data['trades'], 0, [], $cashFlows, $this->sumGroup);
  321. $this->data['form'.$step] = $this->getForm($step, ($this->data['trade'] ?? null), $options)->createView();
  322. if ($form->isSubmitted()) {
  323. return $this->redirect($this->url.'&step='.$step);
  324. }
  325. }
  326. /**
  327. * @param PolicyGenerator $policyGenerator
  328. * @param $policyRanges
  329. * @return false|void
  330. * @throws \Doctrine\ORM\ORMException
  331. * @throws \Doctrine\ORM\OptimisticLockException
  332. */
  333. protected function actionsPolicies($form, $policyRanges = [], $action = '') {
  334. $formData = $form;
  335. if (empty($action) && !empty($form) && method_exists($form, 'getData')) {
  336. $formData = $form->getData();
  337. $action = $form->get('delete')->isClicked() ? 'delete' : 'add';
  338. if ($form->get('deleteAll')->isClicked()) {
  339. $action = 'deleteAll';
  340. }
  341. $policyId = $formData->getId();
  342. if (!empty($policyId)) {
  343. $policyRecord = $this->em->getRepository(HedgingPolitics::class)->find($policyId);
  344. }
  345. } elseif (!empty($policyRanges)) {
  346. $action = 'autoPolicy';
  347. }
  348. if ($action == 'delete') {
  349. if (empty($policyId)) {
  350. $this->addFlash('warning', 'No policys ID submited');
  351. return false;
  352. }
  353. if (empty($policyRecord)) {
  354. $this->addFlash('warning', 'No policy record was found');
  355. return false;
  356. }
  357. //todo: padaryti isjungima per status=0
  358. $this->em->remove($policyRecord);
  359. $this->em->flush();
  360. $this->addFlash('success', 'Hedging policy was deleted successfully');
  361. } elseif ($action == 'deleteAll') {
  362. $records = $this->em->getRepository(HedgingPolitics::class)->findBy([
  363. 'Portfolio' => $this->portfolioId,
  364. ]);
  365. foreach ($records as $record) {
  366. $this->em->remove($record);
  367. }
  368. $this->em->flush();
  369. // $this->addFlash('success', 'Hedging policies were deleted successfully for the currency pair '.$policyRecord->getCurrencyPair()->getName());
  370. } elseif ($action == 'autoPolicy' && !empty($formData->getModel())) { //generated policy
  371. if ($formData->getModel() == 'static_policy') {
  372. $policyRecord = new HedgingPolitics();
  373. $policyRecord->setCustomer($this->getCustomer());
  374. $policyRecord->setPortfolio($this->data['portfolio']);
  375. $policyRecord->setCurrencyPair($formData->getCurrencyPair());
  376. $policyRecord->setModel($formData->getModel());
  377. $policyRecord->setSchedule($policyRanges);
  378. $this->em->persist($policyRecord);
  379. } else {
  380. foreach ($policyRanges as $policy) {
  381. $policyRecord = new HedgingPolitics();
  382. $policyRecord->setCustomer($this->getCustomer());
  383. $policyRecord->setPortfolio($this->data['portfolio']);
  384. $policyRecord->setCurrencyPair($formData->getCurrencyPair());
  385. $policyRecord->setBoundaryLow($policy['low']);
  386. $policyRecord->setBoundaryUpper($policy['high']);
  387. $policyRecord->setd30($policy['30']);
  388. $policyRecord->setd60($policy['60']);
  389. $policyRecord->setd90($policy['90']);
  390. $policyRecord->setd120($policy['120']);
  391. $policyRecord->setd150($policy['150']);
  392. $policyRecord->setd180($policy['180']);
  393. $policyRecord->setModel($formData->getModel());
  394. $this->em->persist($policyRecord);
  395. }
  396. }
  397. $this->em->flush();
  398. $this->addFlash('success', 'Policy was created successfully');
  399. $this->redirect = true;
  400. } elseif ($formData->getModel() != 'static_policy') { //add/edit
  401. if (empty($this->portfolioId)) {
  402. $this->addFlash('warning', 'No Policy found');
  403. return false;
  404. }
  405. if (empty($policyRecord)) {
  406. $policyRecord = new HedgingPolitics();
  407. $policyRecord->setCustomer($this->getCustomer());
  408. $policyRecord->setPortfolio($this->data['portfolio']);
  409. }
  410. $policyRecord->setCurrencyPair($formData->getCurrencyPair());
  411. $policyRecord->setBoundaryLow($formData->getBoundaryLow());
  412. $policyRecord->setBoundaryUpper($formData->getBoundaryUpper());
  413. $policyRecord->setd30($formData->getd30());
  414. $policyRecord->setd60($formData->getd60());
  415. $policyRecord->setd90($formData->getd90());
  416. $policyRecord->setd120($formData->getd120());
  417. $policyRecord->setd150($formData->getd150());
  418. $policyRecord->setd180($formData->getd180());
  419. $this->em->persist($policyRecord);
  420. $this->em->flush();
  421. $this->addFlash('success', 'Policy was '.($policyId > 0 ? 'updated' : 'created').' successfully');
  422. $this->redirect = true;
  423. }
  424. }
  425. /**
  426. * @param $form
  427. * @return bool|void
  428. */
  429. protected function actionsBudgetRates($form) {
  430. $action = $form->get('delete')->isClicked() ? 'delete' : 'add';
  431. $formData = $form->getData();
  432. $budgetRateId = $formData->getId();
  433. if (!empty($budgetRateId)) {
  434. $budgetRateRecord = $this->em->getRepository(BudgetRates::class)->find($budgetRateId);
  435. }
  436. if ($action == 'delete') {
  437. if (empty($budgetRateId)) {
  438. $this->addFlash('warning', 'No budget rate ID submited');
  439. return false;
  440. }
  441. if (empty($budgetRateRecord)) {
  442. $this->addFlash('warning', 'No budget rate record was found');
  443. return false;
  444. }
  445. //todo: padaryti isjungima per status=0
  446. $this->em->remove($budgetRateRecord);
  447. $this->em->flush();
  448. $this->addFlash('success', 'Budget rate was deleted successfully');
  449. $this->redirect = true;
  450. } else { //add/edit
  451. if (empty($this->portfolioId)) {
  452. $this->addFlash('warning', 'No portfolio found');
  453. return false;
  454. }
  455. if (empty($budgetRateId)) {
  456. $budgetRateRecord = new BudgetRates();
  457. $budgetRateRecord->setPortfolio($this->data['portfolio']);
  458. }
  459. $budgetRateRecord->setCurrencyPair($formData->getCurrencyPair());
  460. $budgetRateRecord->setDateStart($formData->getDateStart());
  461. $budgetRateRecord->setDateEnd($formData->getDateEnd());
  462. $budgetRateRecord->setRate($formData->getRate());
  463. $budgetRateRecord->setStatus(true);
  464. $this->em->persist($budgetRateRecord);
  465. $this->em->flush();
  466. $this->addFlash('success', 'budget rate was '.($budgetRateId > 0 ? 'updated' : 'created').' successfully');
  467. $this->redirect = true;
  468. }
  469. }
  470. /**
  471. * @param $form
  472. * @return bool|void
  473. */
  474. protected function actionsCashFlows($form, $importData = '') {
  475. $this->portfolioId = $this->session->get('portfolioId');
  476. $action = $form->get('delete')->isClicked() ? 'delete' : 'add';
  477. $formData = $form->getData();
  478. $cashFlowId = $formData->getId();
  479. if (!empty($cashFlowId)) {
  480. $cashFlowRecord = $this->em->getRepository(CashFlows::class)->find($cashFlowId);
  481. }
  482. if ($action == 'delete') {
  483. if (empty($cashFlowId)) {
  484. $this->addFlash('warning', 'No cash flow ID submited');
  485. return false;
  486. }
  487. if (empty($cashFlowRecord)) {
  488. $this->addFlash('warning', 'No cash flow record was found');
  489. return false;
  490. }
  491. //todo: padaryti isjungima per status=0
  492. $this->em->remove($cashFlowRecord);
  493. $this->em->flush();
  494. $this->addFlash('success', 'Cash flow record was deleted successfully');
  495. $this->redirect = true;
  496. } elseif (empty($importData)) { //add/edit
  497. if (empty($this->portfolioId)) {
  498. $this->addFlash('warning', 'No portfolio found');
  499. return false;
  500. }
  501. if (empty($cashFlowId)) {
  502. $cashFlowRecord = new CashFlows();
  503. $cashFlowRecord->setPortfolio($this->data['portfolio']);
  504. }
  505. $cashFlowRecord->setCurrencyPair($formData->getCurrencyPair());
  506. $cashFlowRecord->setFlowDate($formData->getFlowDate());
  507. $cashFlowRecord->setCashAmountMinor($formData->getCashAmountMinor());
  508. $cashFlowRecord->setComment($formData->getComment());
  509. $cashFlowRecord->setStatus(true);
  510. $this->em->persist($cashFlowRecord);
  511. $this->em->flush();
  512. $this->addFlash('success', 'Cash flow was '.($cashFlowId > 0 ? 'updated' : 'created').' successfully');
  513. $this->redirect = true;
  514. } elseif (!empty($importData)) {
  515. try {
  516. $importDataList = json_decode($importData, true, 512, JSON_THROW_ON_ERROR);
  517. if (empty($importDataList)) {
  518. return false;
  519. }
  520. $recordNr = 0;
  521. $successSaved = [];
  522. $allErrors = [];
  523. $portfolioCurrency = $this->data['portfolio']->getCurrencyPair();
  524. foreach ($importDataList as $item) {
  525. $recordNr++;
  526. $errors = $this->getImportValidatorModel()->validImportDataCashFlow($item, $portfolioCurrency);
  527. if (!empty($errors)) {
  528. $allErrors[$recordNr] = $errors;
  529. continue;
  530. }
  531. $date = date_create($item[0])->format('Y-m-d');
  532. $cashFlowRecord = $this->em->getRepository(CashFlows::class)->findOneBy([
  533. 'flowDate' => $date,
  534. 'Portfolio' => $this->data['portfolio']->getId(),
  535. ]);
  536. if (empty($cashFlowRecord) || empty($this->importDataOverwrite)) {
  537. $cashFlowRecord = new CashFlows();
  538. }
  539. $cashFlowRecord->setPortfolio($this->data['portfolio']);
  540. $cashFlowRecord->setCurrencyPair($portfolioCurrency);
  541. $cashFlowRecord->setFlowDate($date);
  542. $amountMinor = (int)$item[2];
  543. if ($amountMinor < 0 && $cashFlowRecord->getCashAmountMinor() + $amountMinor < 0) {
  544. $allErrors[$recordNr][] = 'Incorrect amount value';
  545. continue;
  546. }
  547. if ($amountMinor < 0 && $cashFlowRecord->getCashAmountMinor() >= $amountMinor) { //jei ivesta neigiama, isminusuojam is esamo
  548. $amountMinor = (int)$cashFlowRecord->getCashAmountMinor() + $amountMinor;
  549. }
  550. $cashFlowRecord->setCashAmountMinor($amountMinor);
  551. $cashFlowRecord->setRate(1);
  552. $cashFlowRecord->setComment(trim(($item[4] ?? ''), '"'));
  553. $cashFlowRecord->setStatus(true);
  554. $this->em->persist($cashFlowRecord);
  555. $successSaved[] = $recordNr;
  556. }
  557. $this->em->flush();
  558. } catch (\Exception $e) {
  559. $this->addFlash('warning', 'Internal error, import data process stopped.'.$e->getMessage());
  560. }
  561. if (!empty($successSaved)) {
  562. $this->addFlash('success', 'Import data created successfully for '.count($successSaved).' records from '.count($importDataList));
  563. $this->redirect = true;
  564. }
  565. if (!empty($allErrors)) {
  566. $errorString = 'Errors found on these records:<br>';
  567. foreach ($allErrors as $id => $errors) {
  568. $errorString .= 'Record nr. '.$id.'<br>';
  569. foreach ($errors as $key => $error) {
  570. $errorString .= 'Column '.$key.': '.$error.'<br>';
  571. }
  572. $errorString .= '<hr>';
  573. }
  574. $this->addFlash('warning', $errorString);
  575. }
  576. }
  577. }
  578. /**
  579. * @param $form
  580. * @return bool|void
  581. */
  582. protected function actionsTrades($form, $importData = []) {
  583. $action = $form->get('delete')->isClicked() ? 'delete' : 'add';
  584. $formData = $form->getData();
  585. $tradeId = $formData->getId();
  586. if (!empty($tradeId)) {
  587. $tradeRecord = $this->em->getRepository(Forward::class)->find($tradeId);
  588. }
  589. if ($action == 'delete') {
  590. if (empty($tradeId)) {
  591. $this->addFlash('warning', 'No trade ID submited');
  592. return false;
  593. }
  594. if (empty($tradeRecord)) {
  595. $this->addFlash('warning', 'No trade record was found');
  596. return false;
  597. }
  598. //todo: padaryti isjungima per status=0
  599. $this->em->remove($tradeRecord);
  600. $this->em->flush();
  601. $this->addFlash('success', 'Trade record was deleted successfully');
  602. } elseif (empty($importData)) { //add/edit
  603. if (empty($this->portfolioId)) {
  604. $this->addFlash('warning', 'No portfolio found');
  605. return false;
  606. }
  607. if (empty($tradeId)) {
  608. $tradeRecord = $formData;
  609. $tradeRecord->setPortfolio($this->data['portfolio']);
  610. }
  611. $tradeRecord->setCurrencyPair($formData->getCurrencyPair());
  612. $tradeRecord->setAmount($formData->getAmount());
  613. $tradeRecord->setAmountMinor($formData->getAmountMinor());
  614. $tradeRecord->setDateExpire(date('Y-m-d'));
  615. $tradeRecord->setDateDelivery($formData->getDateDelivery());
  616. $tradeRecord->setExposureDirection($formData->getExposureDirection());
  617. $tradeRecord->setStatus(true);
  618. $tradeRecord->setStrike($formData->getStrike());
  619. $tradeRecord->setCounterparty($formData->getCounterparty());
  620. $tradeRecord->setComment($formData->getComment());
  621. $tradeRecord->setTradeNumber($formData->getTradeNumber());
  622. $tradeRecord->setType($formData->getType());
  623. $tradeRecord->setRate($this->getRate($tradeRecord->getCurrencyPair()));
  624. $hedgePolicyRatio = $this->getModel()->getHedgeRequired($tradeRecord->getDateDelivery(), $tradeRecord->getCurrencyPair(), $tradeRecord->getPortfolio());
  625. $tradeRecord->setPolicyRatio($hedgePolicyRatio / 100);
  626. $this->em->persist($tradeRecord);
  627. $this->em->flush();
  628. $this->addFlash('success', 'Trade was '.($tradeId > 0 ? 'updated' : 'created').' successfully');
  629. $this->redirect = true;
  630. } elseif (!empty($importData)) {
  631. $importData = str_replace('\r', '', $importData);
  632. try {
  633. $importDataList = json_decode($importData, true, 512, JSON_THROW_ON_ERROR);
  634. if (empty($importDataList)) {
  635. return false;
  636. }
  637. $recordNr = 0;
  638. $successSaved = [];
  639. $allErrors = [];
  640. $portfolioCurrency = $this->data['portfolio']->getCurrencyPair();
  641. $counterparties = $this->em->getRepository(Counterparty::class)->findBy(['Customer' => $this->getCustomer()]);
  642. $brokerForwardsData = $this->cache->getItem('broker_forwards_'.$this->getUser()->getId())->get() ?? [];
  643. foreach ($importDataList as $item) {
  644. $recordNr++;
  645. $errors = $this->getImportValidatorModel()->validImportDataTrades($item, $portfolioCurrency, $counterparties);
  646. if (!empty($errors)) {
  647. $allErrors[$recordNr] = $errors;
  648. // dump($counterparties);
  649. continue;
  650. }
  651. $date = date_create($item[0]);
  652. if (!is_object($date)) {
  653. $date = date_create(str_replace('/', '-', $item[0]));
  654. }
  655. $date = $date->format('Y-m-d');
  656. $tradeRecord = $this->em->getRepository(Forward::class)->findOneBy([
  657. 'tradeNumber' => $item[1],
  658. 'Portfolio' => $this->data['portfolio']->getId(),
  659. ]);
  660. if (empty($tradeRecord) || empty($this->importDataOverwrite)) {
  661. $tradeRecord = new Forward();
  662. }
  663. $tradeRecord->setPortfolio($this->data['portfolio']);
  664. $tradeRecord->setCurrencyPair($portfolioCurrency);
  665. $amountMinor = (int)$item[3];
  666. if ($amountMinor < 0 && $tradeRecord->getAmountMinor() + $amountMinor < 0) {
  667. $allErrors[$recordNr][] = 'Incorrect amount value';
  668. continue;
  669. }
  670. if ($amountMinor < 0 && $tradeRecord->getAmountMinor() >= $amountMinor) { //jei ivesta neigiama, isminusuojam is esamo
  671. $amountMinor = (int)$tradeRecord->getAmountMinor() + $amountMinor;
  672. }
  673. $hedgeRate = (float)str_replace(',', '.', $item[4]);
  674. $tradeRecord->setAmount($amountMinor / $hedgeRate);
  675. $tradeRecord->setAmountMinor($amountMinor);
  676. $tradeRecord->setDateExpire(date('Y-m-d'));
  677. $tradeRecord->setDateDelivery($date);
  678. $tradeRecord->setExposureDirection($this->data['portfolio']->getDirection());
  679. $tradeRecord->setStatus(true);
  680. $tradeRecord->setStrike($hedgeRate);
  681. $tradeRecord->setTradeNumber($item[1]);
  682. if (!empty($this->importCorpay)) {
  683. $tradeRecord->setType('spot');
  684. if (strpos($item[1], 'FD') !== false) { //OFD, EFD - forwardai
  685. $tradeRecord->setType('forward');
  686. }
  687. $tradeRecord->setUsedRemoteSystem(true);
  688. if (!empty($brokerForwardsData)) {
  689. foreach ($brokerForwardsData as $forward) {
  690. if ($forward['ordNum'] == $tradeRecord->getTradeNumber()) {
  691. $tradeRecord->setBrokerTradeData(json_encode($forward));
  692. break;
  693. }
  694. }
  695. }
  696. } else {
  697. $tradeRecord->setType($this->importDataAsSpotTrades ? 'spot' : 'forward');
  698. }
  699. $dateExpire = date_create($tradeRecord->getDateExpire());
  700. $tradeRecord->setRate($this->getRate($tradeRecord->getCurrencyPair(), $dateExpire->format('Y-m-d')));
  701. $hedgePolicyRatio = $this->getHedgePolicy($this->currrentHedgingPolicy, $dateExpire->format('Y'), $dateExpire->format('W'));
  702. $tradeRecord->setPolicyRatio($hedgePolicyRatio);
  703. $counterpartyName = trim($item[5] ?? 0, '"');
  704. $counterParty = $this->getModel()->findCounterParty($counterparties, $counterpartyName);
  705. if (!empty($counterParty)) {
  706. $tradeRecord->setCounterparty($counterParty);
  707. }
  708. $tradeRecord->setComment($item[6] ?? '');
  709. $this->em->persist($tradeRecord);
  710. $this->em->flush();
  711. $successSaved[] = $recordNr;
  712. }
  713. } catch (\Exception $e) {
  714. $this->addFlash('warning', 'Internal error, process stopped.'.$e->getMessage());
  715. }
  716. if (!empty($successSaved)) {
  717. $this->addFlash('success', 'Import data created successfully for '.count($successSaved).' records from '.count($importDataList));
  718. }
  719. if (!empty($allErrors)) {
  720. if (empty($successSaved)) {
  721. $errorString = '<p>Global errors are:</p>';
  722. // $this->addFlash('warning', $errorString);
  723. $globalErrors = '';
  724. $errorList = $allErrors[1] ?? [];
  725. if (!empty($errorList['counterparty'])) {
  726. $globalErrors .= '<li>'.$errorList['counterparty'].'</li>';
  727. }
  728. if (!empty($errorList['currencyPair'])) {
  729. $globalErrors .= '<li>'.$errorList['currencyPair'].'</li>';
  730. }
  731. if (!empty($globalErrors)) {
  732. $errorString .= $globalErrors;
  733. $this->addFlash('warning', $errorString);
  734. }
  735. } else {
  736. $errorString = 'Errors found on these records:<br>';
  737. $count = 0;
  738. foreach ($allErrors as $id => $errors) {
  739. $count++;
  740. $errorString .= 'Record nr. '.$id.'<br>';
  741. foreach ($errors as $key => $error) {
  742. $errorString .= 'Column '.$key.': '.$error.'<br>';
  743. }
  744. $errorString .= '<hr>';
  745. if ($count > 10) {
  746. $errorString .= '...<br>Too many errors, only first 10 shown';
  747. break;
  748. }
  749. }
  750. $this->addFlash('warning', $errorString);
  751. // dump($errorString);
  752. }
  753. } else {
  754. $this->redirect = true;
  755. }
  756. }
  757. }
  758. protected function getForm($step, $object = null, $options = []) {
  759. switch ($step) {
  760. case self::STEP_PORTFOLIO:
  761. if (empty($object)) {
  762. $object = new Portfolio();
  763. }
  764. $objetType = PortfolioType::class;
  765. break;
  766. case self::STEP_POLICY:
  767. if (empty($object)) {
  768. $object = new HedgingPolitics();
  769. }
  770. $objetType = HedgingPoliticsType::class;
  771. break;
  772. case self::STEP_BUDGET_RATES:
  773. if (empty($object)) {
  774. $object = new BudgetRates();
  775. }
  776. $objetType = BudgetRateType::class;
  777. break;
  778. case self::STEP_CASH_FLOWS:
  779. if (empty($object)) {
  780. $object = new CashFlows();
  781. }
  782. $objetType = CashFlowType::class;
  783. break;
  784. case self::STEP_TRADES:
  785. if (empty($object)) {
  786. $object = new Forward();
  787. }
  788. $objetType = ForwardType::class;
  789. break;
  790. case 'reportFilter':
  791. if (empty($object)) {
  792. $object = new ReportFilter();
  793. }
  794. $objetType = ReportFilterType::class;
  795. $step = self::STEP_REPORT;
  796. break;
  797. case 'policyGenerator':
  798. if (empty($object)) {
  799. $object = new PolicyGenerator();
  800. }
  801. $objetType = PolicyGeneratorType::class;
  802. $step = self::STEP_POLICY;
  803. break;
  804. case 'tradesOperations':
  805. if (empty($object)) {
  806. $object = new ForwardOperations();
  807. }
  808. $objetType = TradesOperationsType::class;
  809. $step = self::STEP_TRADES.'&tradeId='.$this->tradeId.'&operation='.$this->tradeOperation;
  810. break;
  811. }
  812. $options['portfolioId'] = $this->portfolioId;
  813. $options['url'] = $this->url.'&step='.$step;
  814. return $this->createForm($objetType, $object, $options);
  815. }
  816. protected function getResidualCash($request) {
  817. if (empty($this->portfolioId) || $this->redirect) {
  818. return false;
  819. }
  820. if (!empty($request->query->get('deleteYears'))) {
  821. $this->deleteYears = $request->query->get('deleteYears');
  822. }
  823. if (!empty($request->request->get('years')) || !empty($this->deleteYears)) {
  824. $this->actionsResidualCash($request->request);
  825. }
  826. $filter = [
  827. 'Portfolio' => $this->portfolioId,
  828. 'status' => true,
  829. ];
  830. $residualCashRecords = $this->em->getRepository(ResidualCash::class)->findBy($filter);
  831. $residualCash = [];
  832. if (!empty($residualCashRecords)) {
  833. /** @var ResidualCash $item */
  834. foreach ($residualCashRecords as $item) {
  835. $residualCash[$item->getYears()][$item->getWeek()] = [
  836. 'funcAmount' => round($item->getFuncAmount(), 3),
  837. 'nonFuncAmount' => round($item->getNonFuncAmount(), 3),
  838. 'amountsInMajorCurr' => $item->isAmountsInMajorCurr(),
  839. 'amountsInKilos' => $item->isAmountsInKilos(),
  840. 'total' => round((float)$item->getNonFuncAmount() + (float)$item->getFuncAmount(), 3),
  841. ];
  842. }
  843. }
  844. ksort($residualCash);
  845. if (!empty($request->query->get('years'))) {
  846. $this->data['years'] = $request->query->get('years');
  847. }
  848. $residualCash = $this->addHedgeInfo($residualCash);
  849. $chartData = [];
  850. foreach ($residualCash as $years => $weeks) {
  851. foreach ($weeks as $week => $item) {
  852. if (empty($item['funcAmount']) && empty($item['nonFuncAmount'])) {
  853. continue;
  854. }
  855. $chartData[$years]['funcAmount'][$week] = $item['funcAmountM'];
  856. $chartData[$years]['nonFuncAmount'][$week] = $item['nonFuncAmountM'];
  857. $chartData[$years]['amountToHedge'][$week] = $item['amountToHedgeM'];
  858. $chartData[$years]['hedgedAmount'][$week] = $item['hedgedAmount'];
  859. $chartData[$years]['funcAmountAfterHedge'][$week] = $item['funcAmountAfterHedgeM'];
  860. $chartData[$years]['nonFuncAmountAfterHedge'][$week] = $item['nonFuncAmountAfterHedgeM'];
  861. $chartData[$years]['totalAfterHedge'][$week] = $item['totalAfterHedgeM'];
  862. }
  863. }
  864. //isvesti grafikus pries trade ir po
  865. // dump($residualCash);
  866. // dump($chartData);
  867. if (!empty($this->data['years'])) {
  868. $years = (int)$this->data['years'];
  869. } else {
  870. $years = date('Y');
  871. }
  872. $this->data['middleYearWeek'] = 27;
  873. $this->data['afterMiddleYearWeek'] = 28;
  874. $this->data['lastYearWeek'] = 54;
  875. if (!empty($years)) {
  876. $this->data['middleYearWeek'] = (int)date_create($years.'-06-31')->format('W');
  877. $this->data['afterMiddleYearWeek'] = $this->data['middleYearWeek'] + 1;
  878. $this->data['lastYearWeek'] = (int)date_create($years.'-12-25')->format('W');
  879. }
  880. $this->data['residualCash'] = $residualCash;
  881. $this->data['charts'] = $chartData;
  882. $this->data['residualFunctionalCurrency'] = $this->data['majorCurrency'];
  883. $this->data['residualNonFunctionalCurrency'] = $this->data['minorCurrency'];
  884. $customerFunctionalCurrency = $this->getCustomer()->getFunctionalCurrency();
  885. if ($customerFunctionalCurrency != $this->data['majorCurrency']) {
  886. $this->data['residualFunctionalCurrency'] = $this->data['minorCurrency'];
  887. $this->data['residualNonFunctionalCurrency'] = $this->data['majorCurrency'];
  888. }
  889. }
  890. protected function actionsResidualCash($formData) {
  891. $years = (int)$formData->get('years');
  892. $funCurr = $formData->get('funCurr'); //minor valiuta
  893. $nonFunCurr = $formData->get('nonFunCurr');
  894. $amountsInMajorCurr = !empty($formData->get('amountsInMajorCurr'));
  895. $amountsInKilos = !empty($formData->get('amountsInKilos'));
  896. //trynimas
  897. if (!empty($this->deleteYears)) {
  898. $residualCash = $this->em->getRepository(ResidualCash::class)->findBy([
  899. 'status' => true,
  900. 'Portfolio' => $this->data['portfolio']->getId(),
  901. 'years' => $this->deleteYears,
  902. ]);
  903. foreach ($residualCash as $item) {
  904. $item->setStatus(false);
  905. $this->em->persist($item);
  906. }
  907. $this->em->flush();
  908. // trinam visus susijusius cashFlows tiems metams tik tam portfeliui
  909. $repository = $this->em->getRepository(CashFlows::class);
  910. $query = $repository->createQueryBuilder('a')->where('a.status = 1')->andWhere('a.flowDate LIKE :years')->setParameter('years', '%'.$this->deleteYears.'%')->andWhere('a.Portfolio ='.$this->data['portfolio']->getId())->getQuery();
  911. $results = $query->getResult();
  912. foreach ($results as $item) {
  913. $item->setStatus(false);
  914. $this->em->persist($item);
  915. }
  916. $this->em->flush();
  917. $this->addFlash('success', 'Residual cash data for '.$this->deleteYears.' years was deleted successfully');
  918. $this->redirect = true; //reikia perkrauti, kad cashflowsai atsinaujintu
  919. return true;
  920. }
  921. if (empty($years)) {
  922. return false;
  923. }
  924. $this->cashFlowSorted = [];
  925. $cashFlows = $this->em->getRepository(CashFlows::class)->findBy([
  926. 'Portfolio' => $this->data['portfolio'],
  927. ]);
  928. if (!empty($cashFlows)) {
  929. foreach ($cashFlows as $item) {
  930. $date = date_create($item->getFlowDate());
  931. $yy = $date->format('Y');
  932. $ww = (int)$date->format('W');
  933. $this->cashFlowSorted[$yy][$ww][] = $item;
  934. }
  935. }
  936. $amounts = $funCurr;
  937. //Jeigu poreikis yra Major valiutos, portfelis turi buti "receive" tipo.
  938. //Amountai imami is Major valiutos eilutes
  939. if ($this->data['portfolio']->getDirection() == 'receive') {
  940. $amounts = $nonFunCurr;
  941. }
  942. //kurimas, atnaujinimas
  943. foreach ($amounts as $week => $amount) {
  944. $residualCash = $this->em->getRepository(ResidualCash::class)->findOneBy([
  945. 'years' => $years,
  946. 'week' => $week,
  947. 'Portfolio' => $this->data['portfolio']->getId(),
  948. ]);
  949. if (empty($residualCash)) {
  950. $residualCash = new ResidualCash();
  951. $residualCash->setPortfolio($this->data['portfolio']);
  952. $residualCash->setYears($years);
  953. $residualCash->setWeek($week);
  954. }
  955. $amount = (float)$amount;
  956. $residualCash->setStatus(true);
  957. $residualCash->setFuncAmount((float)$funCurr[$week]);
  958. $residualCash->setAmountsInMajorCurr($amountsInMajorCurr);
  959. $residualCash->setAmountsInKilos($amountsInKilos);
  960. $residualCash->setNonFuncAmount((float)$nonFunCurr[$week]);
  961. $this->em->persist($residualCash);
  962. if ($amount > 0) {
  963. //jeigu reiksme > 0, reiskias CF kurti nereikia, bet reikia patikrinti, ar nebuvo seno,
  964. // kuri reikia nutrinti, todel ji keiciam i 0 ir tkrinam, radus - trinam
  965. $amount = 0;
  966. }
  967. if (!empty($amount) && $amountsInKilos) {
  968. $amount = $amount * 1000;
  969. }
  970. $this->updateCashFlowByResidualCash($years, $week, abs($amount), $amountsInMajorCurr);
  971. }
  972. $this->data['editedYears'] = $years;
  973. $this->addFlash('success', 'Residual cash data for '.$years.' years was updated successfully');
  974. $this->em->flush();
  975. $this->redirect = true; //reikia perkrauti, kad cashflowsai atsinaujintu
  976. }
  977. protected function updateCashFlowByResidualCash(int $years, int $week, int $amount, $amountsInMajorCurr = false) {
  978. //surandam kiek procentu policy siulo hedginti
  979. // $hedgePolicy = $this->getHedgePolicy($this->currrentHedgingPolicy, $years, $week);
  980. // if (!empty($hedgePolicy)) {
  981. // $amount = $amount * $hedgePolicy;
  982. // }
  983. // kuriam nauja cash flow, tik jeigu amountas > 0
  984. if (empty($this->cashFlowSorted[$years][$week])) {
  985. $date = $this->getDateFromWeekNumber($years, $week);
  986. $yearByWeek = date_create($date)->format('Y');
  987. // jei nera amounto, ar savaite jau lipa i kitus metus, skipinam
  988. if (empty($amount) || $yearByWeek != $years) {
  989. return false;
  990. }
  991. $cashFlowRecord = new CashFlows();
  992. $cashFlowRecord->setPortfolio($this->data['portfolio']);
  993. $cashFlowRecord->setCurrencyPair($this->data['portfolio']->getCurrencyPair());
  994. $cashFlowRecord->setFlowDate($date);
  995. $direction = $this->data['portfolio']->getDirection();
  996. if ($amountsInMajorCurr) {
  997. //jeigu amountas ivestas major valiuta ir portfolio direction == pay, reikia ji pakonvertuoti pagal kursa, nes sistemoje visada saugojame minor valiuta
  998. if ($direction == 'pay') {
  999. $rate = $this->getRate($this->data['portfolio']->getCurrencyPair());
  1000. $amount = (int)($amount * $rate);
  1001. }
  1002. //jeigu amountas yra minor valiuta ir portfolio direction == receive, konvertuoti nereikia, nes sistemoje visada saugojame minor valiuta
  1003. } else {
  1004. //jeigu amountas yra major valiuta ir portfolio direction == receive, konvertuoti reikia, nes sistemoje visada saugojame minor valiuta
  1005. if ($direction == 'receive') {
  1006. $rate = $this->getRate($this->data['portfolio']->getCurrencyPair());
  1007. $amount = (int)($amount * $rate);
  1008. }
  1009. }
  1010. $cashFlowRecord->setCashAmountMinor($amount);
  1011. $cashFlowRecord->setRate(1);
  1012. $cashFlowRecord->setComment('autogenerated from residual cash insert/update');
  1013. $cashFlowRecord->setStatus(true);
  1014. $this->em->persist($cashFlowRecord);
  1015. $this->em->flush();
  1016. return true;
  1017. } else {
  1018. // atnaujinam esancius. visus isjungiam, koreguojam tik pirmaji
  1019. $updateFirstFlag = false;
  1020. /** @var CashFlows $item */
  1021. foreach ($this->cashFlowSorted[$years][$week] as $item) {
  1022. if ($updateFirstFlag || $amount == 0) {
  1023. $item->setStatus(false);
  1024. } else {
  1025. $updateFirstFlag = true;
  1026. $direction = $this->data['portfolio']->getDirection();
  1027. if ($amountsInMajorCurr) {
  1028. //jeigu amountas ivestas major valiuta ir portfolio direction == pay, reikia ji pakonvertuoti pagal kursa, nes sistemoje visada saugojame minor valiuta
  1029. if ($direction == 'pay') {
  1030. $rate = $this->getRate($this->data['portfolio']->getCurrencyPair());
  1031. $amount = (int)($amount * $rate);
  1032. }
  1033. //jeigu amountas yra minor valiuta ir portfolio direction == receive, konvertuoti nereikia, nes sistemoje visada saugojame minor valiuta
  1034. } else {
  1035. //jeigu amountas yra major valiuta ir portfolio direction == receive, konvertuoti reikia, nes sistemoje visada saugojame minor valiuta
  1036. if ($direction == 'receive') {
  1037. $rate = $this->getRate($this->data['portfolio']->getCurrencyPair());
  1038. $amount = (int)($amount * $rate);
  1039. }
  1040. }
  1041. $item->setCashAmountMinor($amount);
  1042. $item->setStatus(true);
  1043. }
  1044. $this->em->persist($item);
  1045. }
  1046. $this->em->flush();
  1047. $this->addFlash('success', 'Cash flow info updated successfully');
  1048. }
  1049. }
  1050. protected function addHedgeInfo($residualCash) {
  1051. /** @var Forward[] $trades */
  1052. $trades = $this->em->getRepository(Forward::class)->findByDates([
  1053. 'Portfolio' => $this->data['portfolio'],
  1054. ]);
  1055. $tradesList = [];
  1056. if (!empty($trades)) {
  1057. /** @var Forward $item */
  1058. foreach ($trades as $item) {
  1059. $year = date_create($item->getDateDelivery())->format('Y');
  1060. $week = (int)date_create($item->getDateDelivery())->format('W');
  1061. $rate = 1;
  1062. // hedge amounta reikia konvertuoti atgal i mayor valiuta, jei residual yra nurodyta, kad poreikiai mayor valiuta
  1063. if (!empty($residualCash[$year][$week]['amountsInMajorCurr'])) {
  1064. $rate = $item->getStrike();
  1065. }
  1066. $amount = (int)($item->getAmountMinor() / $rate);
  1067. if (!empty($tradesList[$year][$week])) {
  1068. $tradesList[$year][$week] += $amount;
  1069. } else {
  1070. $tradesList[$year][$week] = $amount;
  1071. }
  1072. }
  1073. }
  1074. foreach ($residualCash as $years => $weeks) {
  1075. $totalHedged = 0;
  1076. foreach ($weeks as $week => $item) {
  1077. $item['funcAmountM'] = $item['funcAmount'];
  1078. $item['nonFuncAmountM'] = $item['nonFuncAmount'];
  1079. if ($item['amountsInKilos']) {
  1080. $multiple = 1000;
  1081. $item['funcAmountM'] = $item['funcAmount'] * $multiple;
  1082. $item['nonFuncAmountM'] = $item['nonFuncAmount'] * $multiple;
  1083. }
  1084. //naudojame aktualia, gal veliau reiks pakeisti i trado turima policy, nes reali galejo buti pasikeitus nuo tradinimo dienos
  1085. $item['hedgePolicy'] = $this->getHedgePolicy($this->currrentHedgingPolicy, $years, $week);
  1086. $item['amountToHedge'] = $item['funcAmount'] < 0 ? abs($item['funcAmount']) * $item['hedgePolicy'] : 0;
  1087. $item['amountToHedgeM'] = $item['funcAmountM'] < 0 ? abs($item['funcAmountM']) * $item['hedgePolicy'] : 0;
  1088. $portfolioDirection = $this->data['portfolio']->getDirection();
  1089. if ($portfolioDirection == 'receive') {
  1090. $item['amountToHedge'] = $item['nonFuncAmount'] < 0 ? abs($item['nonFuncAmount']) * $item['hedgePolicy'] : 0;
  1091. $item['amountToHedgeM'] = $item['nonFuncAmountM'] < 0 ? abs($item['nonFuncAmountM']) * $item['hedgePolicy'] : 0;
  1092. }
  1093. $item['hedgedAmount'] = $tradesList[$years][$week] ?? 0;
  1094. $totalHedged += $item['hedgedAmount'];
  1095. if ($item['funcAmount'] + $item['nonFuncAmount'] == 0) {
  1096. $totalHedged = 0;
  1097. }
  1098. $item['totalHedged'] = $totalHedged;
  1099. $item['funcAmountAfterHedge'] = $item['funcAmount'] + $item['hedgedAmount'];
  1100. $item['funcAmountAfterHedgeM'] = $item['funcAmountM'] + $item['hedgedAmount'];
  1101. $item['nonFuncAmountAfterHedge'] = $item['nonFuncAmount'] - $item['hedgedAmount'];
  1102. $item['nonFuncAmountAfterHedgeM'] = $item['nonFuncAmountM'] - $item['hedgedAmount'];
  1103. if ($portfolioDirection == 'receive') {
  1104. $item['funcAmountAfterHedge'] = $item['funcAmount'] - $item['hedgedAmount'];
  1105. $item['funcAmountAfterHedgeM'] = $item['funcAmountM'] - $item['hedgedAmount'];
  1106. $item['nonFuncAmountAfterHedge'] = $item['nonFuncAmount'] + $item['hedgedAmount'];
  1107. $item['nonFuncAmountAfterHedgeM'] = $item['nonFuncAmountM'] + $item['hedgedAmount'];
  1108. }
  1109. $item['totalAfterHedge'] = $item['funcAmountAfterHedge'] + $item['nonFuncAmountAfterHedge'];
  1110. $item['totalAfterHedgeM'] = $item['funcAmountAfterHedgeM'] + $item['nonFuncAmountAfterHedgeM'];
  1111. $residualCash[$years][$week] = $item;
  1112. }
  1113. }
  1114. return $residualCash;
  1115. }
  1116. protected function getHedgePolicy(HedgingPolitics $currentPolicy = null, $years, $week) {
  1117. if (empty($currentPolicy)) {
  1118. return 0;
  1119. }
  1120. $period = 30;
  1121. $diff = date_diff(date_create(), date_create($this->getDateFromWeekNumber($years, $week)))->days;
  1122. if ($diff >= 180) {
  1123. $period = 180;
  1124. } elseif ($diff >= 150) {
  1125. $period = 150;
  1126. } elseif ($diff >= 120) {
  1127. $period = 120;
  1128. } elseif ($diff >= 90) {
  1129. $period = 90;
  1130. } elseif ($diff >= 60) {
  1131. $period = 60;
  1132. }
  1133. $method = 'getD'.$period;
  1134. return $currentPolicy->$method() / 100;
  1135. }
  1136. protected function getAnalysis() {
  1137. if (empty($this->portfolioId) || $this->redirect) {
  1138. return false;
  1139. }
  1140. //pasiimam is naujo portfolio tradus, nes gali buti pakeisti valuationai
  1141. $this->data['trades'] = $this->em->getRepository(Forward::class)->findByDates([
  1142. 'Portfolio' => $this->portfolioId,
  1143. 'show' => $this->show,
  1144. 'tradeFilter' => $this->tradeFilter,
  1145. 'type' => 'forward',
  1146. ]);
  1147. $this->data['summary'] = $this->getModel()->totalPortfolioSummary($this->data, $this->sumGroup);
  1148. $this->data['summaryMonthly'] = $this->data['summary'];
  1149. if ($this->sumGroup != 'month') {
  1150. $this->getModel()->emptyTotals();
  1151. $this->data['summaryMonthly'] = $this->getModel()->totalPortfolioSummary($this->data, 'month');
  1152. }
  1153. $analysis = [];
  1154. if (empty($this->data['summary'])) {
  1155. return false;
  1156. }
  1157. /** @var CurrencyPair $portfolioCurrencyPair */
  1158. $portfolioCurrencyPair = $this->data['portfolio']->getCurrencyPair();
  1159. $analysis['majorCurrency'] = $portfolioCurrencyPair->getCurrencyNameByNumber(1);
  1160. $analysis['minorCurrency'] = $portfolioCurrencyPair->getCurrencyNameByNumber(2);
  1161. //todo: perdaryti ant cachiniu
  1162. $spot['bid'] = $this->getRate($this->data['portfolio']->getCurrencyPair());
  1163. $forwardPoints = [0 => $spot];
  1164. $forwardPointList = (new ForwardPoints($this->logger))->get($this->data['portfolio']->getCurrencyPair());
  1165. if (!empty($forwardPointList)) {
  1166. $forwardPoints += $forwardPointList;
  1167. } else {
  1168. //reikia siusti pranesima adminams - negavom kursu
  1169. }
  1170. $this->data['currentMarketRate'] = $spot['bid'] ?? 1; //nusistatom current spot rate
  1171. foreach ($forwardPoints as $days => $points) {
  1172. $period = date_create(' + '.$days.' days')->format('Y-m');
  1173. $analysis['forwards'][$period] = $points['bid'];
  1174. if (empty($analysis['budgetRates'][$period])) {
  1175. $analysis['budgetRates'][$period] = 'null';
  1176. $analysis['averageHedgedRate'][$period] = 'null';
  1177. }
  1178. $analysis['ratePeriods'][] = $period;
  1179. }
  1180. foreach ($this->data['summaryMonthly'] as $period => $item) {
  1181. if ($period < date('Y-m')) {
  1182. continue;
  1183. }
  1184. $analysis['periods'][] = $period;
  1185. $analysis['cashFlows'][] = isset($item['cashFlows']) ? number_format(($item['cashFlows']), 0, '', '') : 0;
  1186. $analysis['hedgeRequired'][] = !empty($item['hedgeRequiredAmount']) ? number_format($item['hedgeRequiredAmount'], 0, '', '') : 'null';
  1187. $analysis['trades'][] = isset($item['trades']) ? number_format($item['trades'], 0, '', '') : 0;
  1188. $analysis['averageHedgedRate'][$period] = $item['averageHedgedRate'] ?? 'null';
  1189. $analysis['budgetRates'][$period] = $item['budgetRate'] ?? 'null';
  1190. if (empty($this->data['currentBudgetRate']) && $analysis['budgetRates'][$period] != 'null') {
  1191. $this->data['currentBudgetRate'] = $analysis['budgetRates'][$period]; //nusistatom current budget rate
  1192. }
  1193. //jeigu reiksmiu daugiau nei metiniu forwardu, sulyginame su flowsu/tradu periodais, kad grafikas negriutu.
  1194. // Privaloma, kad butu tiek periodu, kiek yra flowsu/treidu periodu suvesta i ateiti
  1195. if (empty($analysis['forwards'][$period])) {
  1196. $analysis['forwards'][$period] = 'null';
  1197. $analysis['ratePeriods'][] = $period;
  1198. }
  1199. }
  1200. ksort($analysis['budgetRates']);
  1201. ksort($analysis['averageHedgedRate']);
  1202. $analysis['scaleMax'] = $this->getModel()->getMax($analysis['forwards'], $analysis['budgetRates'], $analysis['averageHedgedRate']);
  1203. $analysis['scaleMin'] = $this->getModel()->getMin($analysis['forwards'], $analysis['budgetRates'], $analysis['averageHedgedRate']);
  1204. $analysis['analysisMethod'] = $this->analysisMethod;
  1205. $analysis['cvarMethod'] = $this->cvarMethod;
  1206. if (!empty($this->sensitivity)) {
  1207. $this->data['sensitivity'] = $this->sensitivity;
  1208. $analysis['analysisTable'] = $this->getModel()->getAnalysisTable($spot, $this->data, $this->analysisMethod, $this->data['portfolio']->getDirection());
  1209. }
  1210. $calcVar = new CalcsVaR($this->em, $this->session);
  1211. $calcVar->calc($this->data['portfolio']->getCurrencyPair()->getId());
  1212. // $trades = $this->cvarMethod == 'full' ? [] : $analysis['trades'];
  1213. // $analysis['varChartData'] = $calcVar->calcLossAmounts($this->cvarMethod, $analysis['cashFlows'], $trades);
  1214. // $analysis['varChartDataMonthly'] = $calcVar->calcLossMonthly($analysis['periods'], $this->cvarMethod, $analysis['cashFlows'], $trades);
  1215. // if (empty($analysis['varChartDataMonthly'])) {
  1216. // $this->addFlash('No monthly currency data for this currencypair '.$this->data['portfolio']->getCurrencyPair()->getName(), 'warning');
  1217. // }
  1218. $this->data['analysis'] = $analysis;
  1219. }
  1220. protected function getReport(Request $request) {
  1221. if (empty($this->portfolioId) || $this->redirect) {
  1222. return false;
  1223. }
  1224. $reportFilter = $this->getForm('reportFilter');
  1225. $reportFilter->handleRequest($request);
  1226. if ($reportFilter->isSubmitted() && $reportFilter->isValid()) {
  1227. $reportData = $reportFilter->getData();
  1228. $this->export = $this->getReportModel()->getReport($reportData, $this->portfolioId);
  1229. if (empty($this->export)) {
  1230. $this->addFlash('warning', 'No data found. Try to change report type or period of data');
  1231. }
  1232. }
  1233. $this->data['reportForm'] = $reportFilter->createView();
  1234. }
  1235. public function tradeOperationAjax(?int $tradeId, $tradeOperation, Request $request) {
  1236. $this->initialize($request);
  1237. $trade = $this->em->getRepository(Forward::class)->find($tradeId);
  1238. if (empty($trade)) {
  1239. return new JsonResponse('trade not found', 400);
  1240. }
  1241. $portfolio = $this->em->getRepository(Portfolio::class)->findOneBy([
  1242. 'id' => $trade->getPortfolio(),
  1243. 'customer' => $this->getCustomer()->getId(),
  1244. ]);
  1245. if (empty($portfolio)) {
  1246. return new JsonResponse('unauthorized access', 400);
  1247. }
  1248. $this->tradeId = $tradeId;
  1249. $this->tradeOperation = $tradeOperation;
  1250. $this->portfolioId = $portfolio->getId();
  1251. $object = new ForwardOperations();
  1252. $object->setRate($trade->getStrike());
  1253. $object->setAmount($trade->getAmountMinor());
  1254. $object->setTradeNumber($trade->getTradeNumber());
  1255. $object->setRate($trade->getStrike());
  1256. $form = $this->getForm('tradesOperations', $object);
  1257. $operationFormData = [
  1258. 'trade' => $trade,
  1259. 'operation' => $this->tradeOperation,
  1260. 'form' => $form->createView(),
  1261. ];
  1262. $operatinForm = $this->render('@instrumentsBase/operation_form.html.twig', $operationFormData)->getContent();
  1263. return new JsonResponse($operatinForm, 200);
  1264. }
  1265. public function cashflowAjax(?int $cashflowId, LoggerInterface $analyticsLogger, CacheItemPoolInterface $cache, Request $request, Connection $connection) {
  1266. $this->init($analyticsLogger, $cache, $request, $connection);
  1267. $cashflow = $this->em->getRepository(CashFlows::class)->find($cashflowId);
  1268. if (empty($cashflow)) {
  1269. return new JsonResponse('cashflow not found', 400);
  1270. }
  1271. $portfolio = $this->em->getRepository(Portfolio::class)->findOneBy([
  1272. 'id' => $cashflow->getPortfolio(),
  1273. 'customer' => $this->getCustomer()->getId(),
  1274. ]);
  1275. if (empty($portfolio)) {
  1276. return new JsonResponse('unauthorized access', 400);
  1277. }
  1278. // $this->portfolioId = $portfolio->getId();
  1279. $pairId = $portfolio->getCurrencyPair()->getId();
  1280. //pasiimam jau paskaiciuota var data is sesijos
  1281. $varData = $this->session->get('var_result_pairId_'.$pairId);
  1282. $dailyVolatility = abs($varData['daily']['VaR'][95]);
  1283. $calcAll = $request->get('all') ?? 0; //jei yra get parametras, tai skaiciuojam visus aktyvius flowsus siam portfeliui
  1284. if (!empty($calcAll)) {
  1285. $qb = $this->em->createQueryBuilder();
  1286. $qb->select('c')->from(CashFlows::class, 'c')->where('c.Portfolio = :portfolio')->andWhere('c.flowDate >= :flowDate')->andWhere('c.status = 1')->setParameter('portfolio', $portfolio)->setParameter('flowDate', date_create()->format('Y-m-d'));
  1287. $cashflows = $qb->getQuery()->getResult();
  1288. } else {
  1289. $cashflows = [$cashflow];
  1290. }
  1291. // reikia surasti paskutine expire date.
  1292. $dateExpireNewest = $this->getModel()->getDateBy('new', $cashflows);
  1293. // $leftDays = $this->getModel()->getLeftDays($cashflows);
  1294. $leftDays = $request->get('spotHistory') ?? 60;
  1295. $now = date_create()->format('Y-m-d');
  1296. if ($dateExpireNewest < $now) {
  1297. $forecastDataPoints = 0;
  1298. $spot = $this->getRate($portfolio->getCurrencyPair(), $dateExpireNewest);
  1299. } else {
  1300. $forecastDataPoints = 2; //kiek dienu i ateiti prognozuosime
  1301. $spot = $this->getRate($portfolio->getCurrencyPair());
  1302. }
  1303. $result = $this->em->getRepository(CurrencyHistory::class)->getSpotsBy($pairId, $dateExpireNewest, $leftDays);
  1304. $spots = array_column($result, 'rate');
  1305. $dates = array_column($result, 'date');
  1306. if ($dateExpireNewest > $now) { //prediction idedam tik tada, jei dar nepasibaiges flowsas
  1307. $dates[] = date_create(' + 1 days')->format('Y-m-d');
  1308. $dates[] = date_create(' + 2 days')->format('Y-m-d');
  1309. }
  1310. $portfolioDirection = $portfolio->getDirection();
  1311. // skaiciuojam riskus
  1312. $riskData = $this->getModel()->getRiskCalcs($cashflows, $dailyVolatility, $portfolioDirection, $spot, $dates);
  1313. $averageHedgeRate = $riskData['averageHedgeRate'];
  1314. $serieData = [];
  1315. foreach ($dates as $key => $date) {
  1316. if (!isset($serie0)) {
  1317. $serie0 = [];
  1318. $serie1 = [];
  1319. $serie2 = [];
  1320. $serie3 = [];
  1321. }
  1322. // skaiciuojame spot rate range lyginant su praejusios dienos spot rate
  1323. $temKey = $key > 0 ? $key - 1 : 0;
  1324. $temSpot = $spots[$temKey] ?? $spot;
  1325. $spotMin = round($temSpot - ($temSpot * $dailyVolatility), 4);
  1326. $spotMax = round($temSpot + ($temSpot * $dailyVolatility), 4);
  1327. $serie0[] = ['x' => $date, 'y' => [$spotMin, $spotMax]];
  1328. $serie1[] = ['x' => $date, 'y' => $spots[$key] ?? $temSpot];
  1329. $serie2[] = ['x' => $date, 'y' => $riskData['budgetRates'][$date] ?? null];
  1330. if (!empty($averageHedgeRate)) {
  1331. $serie3[] = ['x' => $date, 'y' => $riskData['hedgeRates'][$date] ?? null];
  1332. }
  1333. if (!empty($spotMin) && $spotMin < $riskData['minRate']) {
  1334. $riskData['minRate'] = $spotMin;
  1335. }
  1336. if ($spotMax > $riskData['maxRate']) {
  1337. $riskData['maxRate'] = $spotMax;
  1338. }
  1339. }
  1340. $serieData[] = ['type' => 'rangeArea', 'name' => 'Spot range', 'data' => $serie0];
  1341. $serieData[] = ['type' => 'line', 'name' => 'Spot', 'data' => $serie1];
  1342. $serieData[] = ['type' => 'line', 'name' => 'Budget Rate', 'data' => $serie2];
  1343. if (!empty($averageHedgeRate)) {
  1344. $serieData[] = ['type' => 'line', 'name' => 'Hedge Rate', 'data' => $serie3];
  1345. }
  1346. if (empty($spots)) {
  1347. $spots = $serie1;
  1348. }
  1349. $calcs = [
  1350. 'dates' => $dates,
  1351. 'spotSeries' => $serieData,
  1352. 'forecastDataPoints' => $forecastDataPoints,
  1353. ];
  1354. $calcs = array_merge_recursive($calcs, $riskData);
  1355. $params = [
  1356. 'calcs' => $calcs ?? null,
  1357. 'minorCurrency' => $portfolio->getCurrencyPair()->getCurrencyNameByNumber(2),
  1358. 'spots' => $spots,
  1359. ];
  1360. $content = $this->render('@instrumentsBase/cashflow_details.html.twig', $params)->getContent();
  1361. $response = [
  1362. 'content' => $content,
  1363. 'calcs' => $calcs ?? [],
  1364. ];
  1365. return new JsonResponse($response, 200);
  1366. }
  1367. protected function getBudgetRateList($data = []) {
  1368. return $data['budgetRates'];
  1369. }
  1370. /**
  1371. * @return HedgingPolicy
  1372. */
  1373. protected function getModel($renew = false) {
  1374. if (empty($this->model) || $renew) {
  1375. $this->model = new HedgingPolicy($this->em, $this->logger);
  1376. }
  1377. return $this->model;
  1378. }
  1379. /**
  1380. * @param $renew
  1381. * @return ImportValidator
  1382. */
  1383. protected function getImportValidatorModel($renew = false) {
  1384. if (empty($this->importValidatorModel) || $renew) {
  1385. $this->importValidatorModel = new ImportValidator();
  1386. }
  1387. return $this->importValidatorModel;
  1388. }
  1389. protected function getDateFromWeekNumber($year, $weekNumber, $dayOfWeek = 1) {
  1390. $dayOfWeek = $dayOfWeek >= 1 && $dayOfWeek <= 7 ? $dayOfWeek : 1;
  1391. $date = date_create();
  1392. $date->setISODate($year, $weekNumber, $dayOfWeek);
  1393. if ($date->format('Y') != $year && $weekNumber == 1) {
  1394. $date = date_create($year.'-01-01');
  1395. }
  1396. return $date->format('Y-m-d');
  1397. }
  1398. protected function updateCashflows($oldTrade, $newTrade = null, $action = 'extension') {
  1399. //reikia pasitikrinti ar budget rate turime tam periodui, jei ne - reik sukurti
  1400. if ($action != 'drawdown') {
  1401. $this->checkBudgetRate($oldTrade, $newTrade);
  1402. }
  1403. //kazkada reiks padaryti ir rollbacka budget rate atstatyti
  1404. return;
  1405. }
  1406. /**
  1407. * @param Forward $forwardNew
  1408. * @param Forward $forwardOld
  1409. * @return void
  1410. */
  1411. protected function checkBudgetRate($forwardOld, $forwardNew = null) {
  1412. $budgetRateOld = $this->getModel()->getBudgetRateByDate($forwardOld->getPortfolio()->getId(), $forwardOld->getDateDelivery());
  1413. $budgetRate = $this->getModel()->getBudgetRateByDate($forwardNew->getPortfolio()->getId(), $forwardNew->getDateDelivery());
  1414. if (empty($budgetRate)) {
  1415. $bugdetRate = new BudgetRates();
  1416. $bugdetRate->setPortfolio($forwardNew->getPortfolio());
  1417. $bugdetRate->setRate($budgetRateOld ?? $forwardOld->getStrike());
  1418. $bugdetRate->setCurrencyPair($forwardOld->getCurrencyPair());
  1419. $bugdetRate->setDateStart($forwardNew->getDateDelivery());
  1420. $bugdetRate->setDateEnd(date_create($forwardNew->getDateDelivery())->modify('+1 day')->format('Y-m-d'));
  1421. $bugdetRate->setStatus(true);
  1422. $this->em->persist($bugdetRate);
  1423. $this->em->flush();
  1424. }
  1425. }
  1426. }