Skip to content

Commit

Permalink
Added methods for getting, transforming stock options. Test to validate
Browse files Browse the repository at this point in the history
  • Loading branch information
CompoTypo committed Mar 16, 2024
1 parent 6ec102e commit 729e02a
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 0 deletions.
19 changes: 19 additions & 0 deletions src/ApiClient.php
Expand Up @@ -285,6 +285,25 @@ public function stockSummary(string $symbol): array
$responseBody = (string) $this->client->request('GET', $url, ['cookies' => $cookieJar, 'headers' => $this->getHeaders()])->getBody();

return $this->resultDecoder->transformQuotesSummary($responseBody);
}

public function getStockOptions(string $symbol, ?\DateTimeInterface $expiryDate = null): array
{
$qs = $this->getRandomQueryServer();

// Initialize session cookies
$cookieJar = $this->getCookies();

// Get crumb value
$crumb = $this->getCrumb($qs, $cookieJar);

// Fetch options
$url = 'https://query'.$qs.'.finance.yahoo.com/v7/finance/options/'.$symbol.'?crumb='.$crumb;
if ($expiryDate) {
$url .= '&date='.(string) $expiryDate->getTimestamp();
}
$responseBody = (string) $this->client->request('GET', $url, ['cookies' => $cookieJar, 'headers' => $this->getHeaders()])->getBody();

return $this->resultDecoder->transformOptions($responseBody);
}
}
49 changes: 49 additions & 0 deletions src/ResultDecoder.php
Expand Up @@ -8,6 +8,7 @@
use Scheb\YahooFinanceApi\Exception\InvalidValueException;
use Scheb\YahooFinanceApi\Results\DividendData;
use Scheb\YahooFinanceApi\Results\HistoricalData;
use Scheb\YahooFinanceApi\Results\Option;
use Scheb\YahooFinanceApi\Results\Quote;
use Scheb\YahooFinanceApi\Results\SearchResult;
use Scheb\YahooFinanceApi\Results\SplitData;
Expand All @@ -18,6 +19,23 @@ class ResultDecoder
public const DIVIDEND_DATA_HEADER_LINE = ['Date', 'Dividends'];
public const SPLIT_DATA_HEADER_LINE = ['Date', 'Stock Splits'];
public const SEARCH_RESULT_FIELDS = ['symbol', 'name', 'exch', 'type', 'exchDisp', 'typeDisp'];
public const OPTION_FIELDS_MAP = [
'contractSymbol' => ValueMapperInterface::TYPE_STRING,
'strike' => ValueMapperInterface::TYPE_FLOAT,
'currency' => ValueMapperInterface::TYPE_STRING,
'lastPrice' => ValueMapperInterface::TYPE_FLOAT,
'change' => ValueMapperInterface::TYPE_FLOAT,
'percentChange' => ValueMapperInterface::TYPE_FLOAT,
'volume' => ValueMapperInterface::TYPE_INT,
'openInterest' => ValueMapperInterface::TYPE_INT,
'bid' => ValueMapperInterface::TYPE_FLOAT,
'ask' => ValueMapperInterface::TYPE_FLOAT,
'contractSize' => ValueMapperInterface::TYPE_STRING,
'expiration' => ValueMapperInterface::TYPE_DATE,
'lastTradeDate' => ValueMapperInterface::TYPE_DATE,
'impliedVolatility' => ValueMapperInterface::TYPE_FLOAT,
'inTheMoney' => ValueMapperInterface::TYPE_BOOL,
];
public const QUOTE_FIELDS_MAP = [
'ask' => ValueMapperInterface::TYPE_FLOAT,
'askSize' => ValueMapperInterface::TYPE_INT,
Expand Down Expand Up @@ -283,4 +301,35 @@ public function transformQuotesSummary(string $responseBody): array
return $decoded['quoteSummary']['result'];
}

public function transformOptions(string $responseBody): array
{
$decoded = json_decode($responseBody, true);
if (!isset($decoded['optionChain']['result']) || !\is_array($decoded['optionChain']['result'])) {
throw new ApiException('Yahoo Search API returned an invalid result.', ApiException::INVALID_RESPONSE);
}

$results = $decoded['optionChain']['result'];

// Single element is returned directly in "quote"
return array_map(function (array $item) {
return $this->createOption($item);
}, $results);
}

private function createOption(array $json): Option
{
$mappedValues = [];
foreach ($json as $field => $value) {
if (\array_key_exists($field, self::QUOTE_FIELDS_MAP)) {
$type = self::QUOTE_FIELDS_MAP[$field];
try {
$mappedValues[$field] = $this->valueMapper->mapValue($value, $type);
} catch (InvalidValueException $e) {
throw new ApiException(sprintf('Not a %s in field "%s": %s', $type, $field, $value), ApiException::INVALID_VALUE, $e);
}
}
}

return new Option($mappedValues);
}
}
111 changes: 111 additions & 0 deletions src/Results/Option.php
@@ -0,0 +1,111 @@
<?php

declare(strict_types=1);

namespace Scheb\YahooFinanceApi\Results;

class Option implements \JsonSerializable
{
private $contractSymbol;
private $strike;
private $currency;
private $lastPrice;
private $change;
private $percentChange;
private $volume;
private $openInterest;
private $bid;
private $ask;
private $contractSize;
private $expiration;
private $lastTradeDate;
private $impliedVolatility;
private $inTheMoney;

public function __construct(array $values)
{
foreach ($values as $property => $value) {
$this->{$property} = $value;
}
}

public function jsonSerialize(): array
{
return get_object_vars($this);
}

public function getContractSymbol(): string
{
return $this->contractSymbol;
}

public function getStrike(): float
{
return $this->strike;
}

public function getCurrency(): string
{
return $this->currency;
}

public function getLastPrice(): float
{
return $this->lastPrice;
}

public function getChange(): float
{
return $this->change;
}

public function getPercentChange(): float
{
return $this->percentChange;
}

public function getVolume(): int
{
return $this->volume;
}

public function getOpenInterest(): int
{
return $this->openInterest;
}

public function getBid(): float
{
return $this->bid;
}

public function getAsk(): float
{
return $this->ask;
}

public function getContractSize(): string
{
return $this->contractSize;
}

public function getExpiration(): \DateTimeInterface
{
return $this->expiration;
}

public function getLastTradeDate(): \DateTimeInterface
{
return $this->lastTradeDate;
}

public function getImpliedVolatility(): float
{
return $this->impliedVolatility;
}

public function getInTheMoney(): bool
{
return $this->inTheMoney;
}
}
9 changes: 9 additions & 0 deletions tests/ApiClientIntegrationTest.php
Expand Up @@ -10,6 +10,7 @@
use Scheb\YahooFinanceApi\ApiClientFactory;
use Scheb\YahooFinanceApi\Results\DividendData;
use Scheb\YahooFinanceApi\Results\HistoricalData;
use Scheb\YahooFinanceApi\Results\Option;
use Scheb\YahooFinanceApi\Results\Quote;
use Scheb\YahooFinanceApi\Results\SearchResult;
use Scheb\YahooFinanceApi\Results\SplitData;
Expand Down Expand Up @@ -317,4 +318,12 @@ public function testStockSummary(): void
$this->assertEquals(self::APPLE_SYMBOL, $returnValue[0]['quoteType']['symbol']);
}

public function testGetStockOptions(): void
{
$returnValue = $this->client->getStockOptions(self::APPLE_SYMBOL);

$this->assertIsArray($returnValue);
$this->assertGreaterThan(0, \count($returnValue));
$this->assertContainsOnlyInstancesOf(Option::class, $returnValue);
}
}

0 comments on commit 729e02a

Please sign in to comment.