sample code

This commit is contained in:
2022-10-13 11:55:19 -04:00
commit bde8aa5e5d
67 changed files with 6421 additions and 0 deletions
@@ -0,0 +1,9 @@
name: Dafter Quote
description: Provides quotes for a security, ETF or an option.
core: 8.x
type: module
package: Daftar
core_version_requirement: ^8 || ^9
version: 1.0.0
dependencies:
- daftar_stock_api:daftar_stock_api
@@ -0,0 +1,35 @@
daftar_quote.options:
path: '/quote/options'
defaults:
_title: 'Options Quote'
_controller: '\Drupal\daftar_quote\Controller\OptionsController::quote'
requirements:
_access: 'TRUE'
daftar_quote.v2.options.contracts:
path: '/v2/option/contracts/{symbol}/{type}/{expiration_date}'
defaults:
_title: 'Options Quote'
_controller: '\Drupal\daftar_quote\Controller\OptionsController::v2_contracts'
requirements:
_access: 'TRUE'
symbol: '[A-Z]{1,5}'
type: 'calls|puts'
expiration_date: '\d{4}-\d{2}-\d{2}'
daftar_quote.v2.options.contract:
path: '/v2/option/contracts/{contract_symbol}'
defaults:
_title: 'Option Contract'
_controller: '\Drupal\daftar_quote\Controller\OptionsController::v2_contract'
requirements:
_access: 'TRUE'
#contract_symbol: '[A-Z]{1,5}\d{6}(P|C)\d{8}'
daftar_quote.v2.options.parse_contract_symbol:
path: '/v2/option/contracts/parse'
defaults:
_title: 'Option Contract Parse'
_controller: '\Drupal\daftar_quote\Controller\OptionsController::v2_parse_contract_symbol'
requirements:
_access: 'TRUE'
@@ -0,0 +1,292 @@
<?php
namespace Drupal\daftar_quote\Controller;
use DateTime;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Site\Settings;
use Drupal\daftar_stock_api\Exception\StockApiException;
use Drupal\daftar_stock_api\Service\YahooApiService;
use Drupal\daftar_stock_api\Service\YahooParserService;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* Provides callback methods used for launching modals with content form.
* @package Drupal\daftar_content\Controller
*/
class OptionsController extends ControllerBase {
const SETTINGS_KEY_OPTIONS_TOKEN = 'daftar_quote_options_token';
const CONTRACT_SYMBOL_PATTERN = '/^([A-Z]{1,5})(\d{6})([PC])(\d{8})$/';
const CONTRACT_SYMBOL_FD_PATTERN = '/^-?([A-Z]{1,5})(\d{6})([PC])(\d{1,4}([.]\d{1,2})?)$/';
// Tran Date Contact Symbol QT PPS Total
const CONTRACT_SYMBOL_FD_PATTERN2 = '/^(\d{2}\/\d{2}\/\d{4})[ ]+YOU (SOLD|BOUGHT) (OPENING|CLOSING) TRANSACTION[ ]+-?([A-Z]{1,5})(\d{6})([PC])(\d{1,4}[\.]?\d{0,2})[ ]+(-?\d{1,4})[ ]+\$(\d{1,4}[\.]?\d{0,4})[ ]+\$(-?\d+[,]?\d{0,3}[\.]?\d{0,2})[ ]+\$(\d{1,3}[\.]?\d{0,2})[ ]+\$(\d{1,3}[\.]?\d{0,2}).*$/';
// Tran Date Contact Symbol QT PPS Total
const CONTRACT_SYMBOL_FD_PATTERN2B = '/^(\d{2}\/\d{2}\/\d{4})YOU (SOLD|BOUGHT) (OPENING|CLOSING) TRANSACTION-?([A-Z]{1,5})(\d{6})([PC])(\d{1,4}[\.]?\d{0,2})(-?\d{1,4})\$(\d{1,4}[\.]?\d{0,4})\$(-?\d+[,]?\d{0,3}[\.]?\d{0,2})\$(\d{1,3}[\.]?\d{0,2})\$(\d{1,3}[\.]?\d{0,2}).*$/';
const CONTRACT_SYMBOL_TDA_PATTERN = '/^(Sold|Bought) (\d+) ([A-Z]{1,5}) ([A-Z][a-z]{2} \d{1,2} \d{4}) (\d{1,4}([.]\d{1,2})?) (Call|Put) @ (\d{1,4}([.]\d{1,2})?)$/';
/**
* @var \Drupal\daftar_stock_api\Service\YahooParserService
*/
protected YahooParserService $yahooParser;
/**
* @var \Drupal\daftar_stock_api\Service\YahooApiService
*/
protected YahooApiService $yahooApi;
/**
* OptionsController constructor.
*
* @param YahooParserService $yahoo_parser_service
* @param \Drupal\daftar_stock_api\Service\YahooApiService $yahoo_api_service
*/
public function __construct(YahooParserService $yahoo_parser_service, YahooApiService $yahoo_api_service) {
$this->yahooParser = $yahoo_parser_service;
$this->yahooApi = $yahoo_api_service;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container): OptionsController {
return new static(
$container->get('daftar.stock_api.yahoo_parser'),
$container->get('daftar.stock_api.yahoo_api')
);
}
/**
* @param string $symbol
* @param string $type
* @param string $expiration_date
* @param Request $request
*
* @return JsonResponse
*/
public function v2_contracts(string $symbol, string $type, string $expiration_date, Request $request): JsonResponse {
$tokenCheck = $this->checkToken($request);
if ($tokenCheck) {
return $tokenCheck;
}
$strike = $request->get('strike');
return $this->v2_fetchContracts($symbol, $type, $expiration_date, $strike);
}
/**
* @param string $contract_symbol
* @param Request $request
*
* @return JsonResponse
*/
public function v2_contract(string $contract_symbol, Request $request): JsonResponse {
$tokenCheck = $this->checkToken($request);
if ($tokenCheck) {
return $tokenCheck;
}
if (preg_match(self::CONTRACT_SYMBOL_PATTERN, $contract_symbol, $matches)) {
$date = DateTime::createFromFormat('ymd', $matches[2]);
$type = ($matches[3] === 'P') ? 'puts' : 'calls';
$strike = intval($matches[4])/1000;
return $this->v2_fetchContracts($matches[1], $type, $date->format('Y-m-d'), $strike);
}
return new JsonResponse([
'error' => TRUE,
'message' => $this->t('Invalid contract symbol.'),
], Response::HTTP_BAD_REQUEST);
}
/**
* @param string $contract_symbol
* @param Request $request
*
* @return JsonResponse
*/
public function v2_parse_contract_symbol(Request $request): JsonResponse {
$tokenCheck = $this->checkToken($request);
if ($tokenCheck) {
return $tokenCheck;
}
$parsed = [];
$contractSymbol = $request->request->get('contract_symbol');
//$contractSymbol = $request->get('contract_symbol');
if (preg_match(self::CONTRACT_SYMBOL_PATTERN, $contractSymbol, $matches)) {
$date = DateTime::createFromFormat('ymd', $matches[2]);
$parsed = [
'symbol' => $matches[1],
'expiration_date' => $date->format('m/d/Y'),
'type' => ($matches[3] === 'P') ? 'PUT' : 'CALL',
'strike' => intval($matches[4])/1000,
'account' => '',
];
}
elseif (preg_match(self::CONTRACT_SYMBOL_FD_PATTERN, $contractSymbol, $matches)) {
$date = DateTime::createFromFormat('ymd', $matches[2]);
$parsed = [
'symbol' => $matches[1],
'expiration_date' => $date->format('m/d/Y'),
'type' => ($matches[3] === 'P') ? 'PUT' : 'CALL',
'strike' => $matches[4],
'account' => 'FD',
];
}
elseif (preg_match(self::CONTRACT_SYMBOL_FD_PATTERN2, $contractSymbol, $matches)
|| preg_match(self::CONTRACT_SYMBOL_FD_PATTERN2B, $contractSymbol, $matches)) {
$date = DateTime::createFromFormat('ymd', $matches[5]);
$action = substr($matches[2], 0, 1) . '2';
$action .= ($matches[3] === 'CLOSING') ? 'X' : 'O';
$parsed = [
'tran_date' => $matches[1],
'action' => $action,
'symbol' => $matches[4],
'expiration_date' => $date->format('m/d/Y'),
'type' => ($matches[6] === 'P') ? 'PUT' : 'CALL',
'strike' => $matches[7],
'quantity' => $matches[8],
'account' => 'FD2',
'price' => $matches[9],
'comm' => $matches[11],
'fees' => $matches[12]
];
}
elseif (preg_match(self::CONTRACT_SYMBOL_TDA_PATTERN, $contractSymbol, $matches)) {
$date = DateTime::createFromFormat('M d Y', $matches[4]);
$parsed = [
'symbol' => $matches[3],
'expiration_date' => $date->format('m/d/Y'),
'type' => ($matches[7] === 'Put') ? 'PUT' : 'CALL',
'strike' => $matches[5],
'account' => 'TDA',
'action' => ($matches[1] === 'Sold') ? 'S2O' : 'B2O',
'quantity' => $matches[2],
'price' => $matches[8],
];
}
if ($parsed) {
$parsed['contract'] = NULL;
try {
$type = ($parsed['type'] == 'CALL') ? 'calls' : 'puts';
$contractData = $this->yahooApi->getOptionContract(
$parsed['symbol'], $parsed['expiration_date'], $type, $parsed['strike']
);
if (!$contractData['error']) {
$parsed['contract'] = $contractData['contents'];
}
} catch (StockApiException $e) {
// do nothing
}
return new JsonResponse([
'error' => FALSE,
'message' => '',
'contents' => $parsed,
], Response::HTTP_OK);
}
else {
return new JsonResponse([
'error' => TRUE,
'message' => $this->t('Invalid or missing contract symbol.'),
'contract_symbol' => $contractSymbol,
], Response::HTTP_BAD_REQUEST);
}
}
/**
* @param Request $request
* @return JsonResponse
*/
public function quote(Request $request): JsonResponse {
$tokenCheck = $this->checkToken($request);
if ($tokenCheck) {
return $tokenCheck;
}
$symbol = $request->get('symbol');
if (!$symbol) {
return new JsonResponse([
'error' => TRUE,
'message' => $this->t('Missing symbol.'),
], Response::HTTP_BAD_REQUEST);
}
$price = $this->yahooParser->getPrice($symbol);
if ($price) {
return new JsonResponse([
'error' => FALSE,
'message' => 'success',
'price' => $price,
], Response::HTTP_OK);
}
return new JsonResponse([
'error' => TRUE,
'message' => $this->t('Unable to parse price.'),
], Response::HTTP_INTERNAL_SERVER_ERROR);
}
/**
* @param string $symbol
* @param string $type
* @param string $expiration_date
* @param $strike
*
* @return \Symfony\Component\HttpFoundation\JsonResponse
*/
private function v2_fetchContracts(string $symbol, string $type, string $expiration_date, $strike = ""): JsonResponse {
if ($type == 'CALL') {
$type == 'calls';
}
if ($type == 'PUT') {
$type == 'puts';
}
try {
if ($strike) {
$data = $this->yahooApi->getOptionContract($symbol, $expiration_date, $type, $strike);
}
else {
$data = $this->yahooApi->getOptionContracts($symbol, $expiration_date, $type);
}
return new JsonResponse($data, Response::HTTP_OK);
}
catch (StockApiException $e) {
//do nothing
}
return new JsonResponse([
'error' => TRUE,
'message' => $this->t('Error occurred while finding contract.'),
], Response::HTTP_INTERNAL_SERVER_ERROR);
}
private function checkToken(Request $request): ?JsonResponse {
$optionsToken = Settings::get(static::SETTINGS_KEY_OPTIONS_TOKEN, '');
if (!$optionsToken) {
return new JsonResponse([
'error' => TRUE,
'message' => $this->t('Token setup has not been completed.'),
], Response::HTTP_INTERNAL_SERVER_ERROR);
}
$receivedToken = $request->get('token');
if ($receivedToken !== $optionsToken) {
return new JsonResponse([
'error' => TRUE,
'message' => $this->t('Invalid token.'),
], Response::HTTP_FORBIDDEN);
}
return NULL;
}
}