diff --git a/src/ApiClient.php b/src/ApiClient.php index da7b89e..d1e9712 100644 --- a/src/ApiClient.php +++ b/src/ApiClient.php @@ -102,7 +102,7 @@ public function getHistoricalQuoteData(string $symbol, string $interval, \DateTi $this->validateIntervals($interval); $this->validateDates($startDate, $endDate); - $responseBody = $this->getHistoricalDataResponseBody($symbol, $interval, $startDate, $endDate, self::FILTER_HISTORICAL); + $responseBody = $this->getHistoricalDataResponseBodyJson($symbol, $interval, $startDate, $endDate, self::FILTER_HISTORICAL); return $this->resultDecoder->transformHistoricalDataResult($responseBody); } @@ -118,7 +118,7 @@ public function getHistoricalDividendData(string $symbol, \DateTimeInterface $st { $this->validateDates($startDate, $endDate); - $responseBody = $this->getHistoricalDataResponseBody($symbol, self::INTERVAL_1_MONTH, $startDate, $endDate, self::FILTER_DIVIDENDS); + $responseBody = $this->getHistoricalDataResponseBodyJson($symbol, self::INTERVAL_1_MONTH, $startDate, $endDate, self::FILTER_DIVIDENDS); $historicData = $this->resultDecoder->transformDividendDataResult($responseBody); usort($historicData, function (DividendData $a, DividendData $b): int { @@ -140,7 +140,7 @@ public function getHistoricalSplitData(string $symbol, \DateTimeInterface $start { $this->validateDates($startDate, $endDate); - $responseBody = $this->getHistoricalDataResponseBody($symbol, self::INTERVAL_1_MONTH, $startDate, $endDate, self::FILTER_SPLITS); + $responseBody = $this->getHistoricalDataResponseBodyJson($symbol, self::INTERVAL_1_MONTH, $startDate, $endDate, self::FILTER_SPLITS); $historicData = $this->resultDecoder->transformSplitDataResult($responseBody); usort($historicData, function (SplitData $a, SplitData $b): int { @@ -241,10 +241,10 @@ private function fetchQuotes(array $symbols) return $this->resultDecoder->transformQuotes($responseBody); } - private function getHistoricalDataResponseBody(string $symbol, string $interval, \DateTimeInterface $startDate, \DateTimeInterface $endDate, string $filter): string + private function getHistoricalDataResponseBodyJson(string $symbol, string $interval, \DateTimeInterface $startDate, \DateTimeInterface $endDate, string $filter): string { $qs = $this->getRandomQueryServer(); - $dataUrl = 'https://query'.$qs.'.finance.yahoo.com/v7/finance/download/'.urlencode($symbol).'?period1='.$startDate->getTimestamp().'&period2='.$endDate->getTimestamp().'&interval='.$interval.'&events='.$filter; + $dataUrl = 'https://query'.$qs.'.finance.yahoo.com/v8/finance/chart/'.urlencode($symbol).'?period1='.$startDate->getTimestamp().'&period2='.$endDate->getTimestamp().'&interval='.$interval.'&events='.$filter; return (string) $this->client->request('GET', $dataUrl, ['headers' => $this->getHeaders()])->getBody(); } diff --git a/src/ResultDecoder.php b/src/ResultDecoder.php index 35374b1..9a5a4bc 100644 --- a/src/ResultDecoder.php +++ b/src/ResultDecoder.php @@ -195,81 +195,101 @@ private function validateDate(string $value): \DateTime public function transformHistoricalDataResult(string $responseBody): array { - $lines = $this->validateHeaderLines($responseBody, self::HISTORICAL_DATA_HEADER_LINE); + $decoded = json_decode($responseBody, true); + + if ((!\is_array($decoded)) || (isset($decoded['chart']['error']))) { + throw new ApiException('Response is not a valid JSON', ApiException::INVALID_RESPONSE); + } + + $result = $decoded['chart']['result'][0]; + $entryCount = \count($result['timestamp']); + $returnArray = []; + for ($i = 0; $i < $entryCount; ++$i) { + $returnArray[] = $this->createHistoricalData($result, $i); + } - return array_map(function ($line) { - return $this->createHistoricalData(explode(',', $line)); - }, $lines); + return $returnArray; } - private function createHistoricalData(array $columns): HistoricalData + private function createHistoricalData(array $json, int $index): HistoricalData { - if (7 !== \count($columns)) { - throw new ApiException('CSV did not contain correct number of columns', ApiException::INVALID_RESPONSE); + $dateStr = date('Y-m-d', $json['timestamp'][$index]); + if ($dateStr) { + $date = $this->validateDate($dateStr); + } else { + throw new ApiException(\sprintf('Not a date in column "Date":%s', $json['timestamp'][$index]), ApiException::INVALID_VALUE); } - $date = $this->validateDate($columns[0]); - - for ($i = 1; $i <= 6; ++$i) { - if (!is_numeric($columns[$i]) && 'null' !== $columns[$i]) { - throw new ApiException(\sprintf('Not a number in column "%s": %s', self::HISTORICAL_DATA_HEADER_LINE[$i], $columns[$i]), ApiException::INVALID_VALUE); + foreach (['open', 'high', 'low', 'close', 'volume'] as $column) { + $columnValue = $json['indicators']['quote'][0][$column][$index]; + if (!is_numeric($columnValue) && 'null' !== $columnValue) { + throw new ApiException(\sprintf('Not a number in column "%s": %s', $column, $column), ApiException::INVALID_VALUE); } } - $open = (float) $columns[1]; - $high = (float) $columns[2]; - $low = (float) $columns[3]; - $close = (float) $columns[4]; - $adjClose = (float) $columns[5]; - $volume = (int) $columns[6]; + $columnValue = $json['indicators']['adjclose'][0]['adjclose'][$index]; + if (!is_numeric($columnValue) && 'null' !== $columnValue) { + throw new ApiException(\sprintf('Not a number in column "%s": %s', 'adjclose', 'adjclose'), ApiException::INVALID_VALUE); + } + + $open = (float) $json['indicators']['quote'][0]['open'][$index]; + $high = (float) $json['indicators']['quote'][0]['high'][$index]; + $low = (float) $json['indicators']['quote'][0]['low'][$index]; + $close = (float) $json['indicators']['quote'][0]['close'][$index]; + $volume = (int) $json['indicators']['quote'][0]['volume'][$index]; + $adjClose = (float) $json['indicators']['adjclose'][0]['adjclose'][$index]; return new HistoricalData($date, $open, $high, $low, $close, $adjClose, $volume); } public function transformDividendDataResult(string $responseBody): array { - $lines = $this->validateHeaderLines($responseBody, self::DIVIDEND_DATA_HEADER_LINE); + $decoded = json_decode($responseBody, true); + if ((!\is_array($decoded)) || (isset($decoded['chart']['error']))) { + throw new ApiException('Response is not a valid JSON', ApiException::INVALID_RESPONSE); + } - return array_map(function ($line) { - return $this->createDividendData(explode(',', $line)); - }, $lines); + return array_map(function (array $item) { + return $this->createDividendData($item); + }, $decoded['chart']['result'][0]['events']['dividends']); } - private function createDividendData(array $columns): DividendData + private function createDividendData(array $json): DividendData { - if (2 !== \count($columns)) { - throw new ApiException('CSV did not contain correct number of columns', ApiException::INVALID_RESPONSE); - } - - $date = $this->validateDate($columns[0]); - - if (!is_numeric($columns[1]) && 'null' !== $columns[1]) { - throw new ApiException(\sprintf('Not a number in column Dividends: %s', $columns[1]), ApiException::INVALID_VALUE); + $dateStr = date('Y-m-d', $json['date']); + if ($dateStr) { + $date = $this->validateDate($dateStr); + } else { + throw new ApiException(\sprintf('Not a date in column "Date":%s', $json['date']), ApiException::INVALID_VALUE); } - $dividends = (float) $columns[1]; + $dividends = (float) $json['amount']; return new DividendData($date, $dividends); } public function transformSplitDataResult(string $responseBody): array { - $lines = $this->validateHeaderLines($responseBody, self::SPLIT_DATA_HEADER_LINE); + $decoded = json_decode($responseBody, true); + if ((!\is_array($decoded)) || (isset($decoded['chart']['error']))) { + throw new ApiException('Response is not a valid JSON', ApiException::INVALID_RESPONSE); + } - return array_map(function ($line) { - return $this->createSplitData(explode(',', $line)); - }, $lines); + return array_map(function (array $item) { + return $this->createSplitData($item); + }, $decoded['chart']['result'][0]['events']['splits']); } - private function createSplitData(array $columns): SplitData + private function createSplitData(array $json): SplitData { - if (2 !== \count($columns)) { - throw new ApiException('CSV did not contain correct number of columns', ApiException::INVALID_RESPONSE); + $dateStr = date('Y-m-d', $json['date']); + if ($dateStr) { + $date = $this->validateDate($dateStr); + } else { + throw new ApiException(\sprintf('Not a date in column "Date":%s', $json['date']), ApiException::INVALID_VALUE); } - $date = $this->validateDate($columns[0]); - - $stockSplits = (string) $columns[1]; + $stockSplits = (string) $json['splitRatio']; return new SplitData($date, $stockSplits); } diff --git a/tests/ResultDecoderTest.php b/tests/ResultDecoderTest.php index 70accc4..0fcedce 100644 --- a/tests/ResultDecoderTest.php +++ b/tests/ResultDecoderTest.php @@ -106,19 +106,19 @@ public function extractCrumb_invalidStringGiven_throwApiException(): void */ public function transformHistoricalDataResult_csvGiven_returnArrayOfHistoricalData(): void { - $returnedResult = $this->resultDecoder->transformHistoricalDataResult(file_get_contents(__DIR__.'/fixtures/historicalData.csv')); + $returnedResult = $this->resultDecoder->transformHistoricalDataResult(file_get_contents(__DIR__.'/fixtures/historicalData.json')); $this->assertIsArray($returnedResult); $this->assertContainsOnlyInstancesOf(HistoricalData::class, $returnedResult); $expectedExchangeRate = new HistoricalData( - new \DateTime('2017-07-11'), - 144.729996, - 145.850006, - 144.380005, - 145.529999, - 145.529999, - 19781800 + new \DateTime('2024-09-30', new \DateTimeZone('UTC')), + 230.0399932861328, + 233.0, + 229.64999389648438, + 233.0, + 233.0, + 54541900 ); $this->assertEquals($expectedExchangeRate, $returnedResult[0]); } @@ -129,7 +129,7 @@ public function transformHistoricalDataResult_csvGiven_returnArrayOfHistoricalDa public function transformHistoricalDataResult_invalidColumnsCsvGiven_throwApiException(): void { $this->expectException(ApiException::class); - $this->expectExceptionMessage('CSV did not contain correct number of columns'); + $this->expectExceptionMessage('Response is not a valid JSON'); $this->resultDecoder->transformHistoricalDataResult(file_get_contents(__DIR__.'/fixtures/invalidColumnsHistoricalData.csv')); } @@ -140,7 +140,7 @@ public function transformHistoricalDataResult_invalidColumnsCsvGiven_throwApiExc public function transformHistoricalDataResult_unexpectedHeaderLineCsvGiven_throwApiException(): void { $this->expectException(ApiException::class); - $this->expectExceptionMessage('CSV header line did not match expected header line, given: 12345 1234567, expected: Date,Open,High,Low,Close,Adj Close,Volume'); + $this->expectExceptionMessage('Response is not a valid JSON'); $invalidCsvString = "12345\t1234567\t"; $this->resultDecoder->transformHistoricalDataResult($invalidCsvString); @@ -152,7 +152,7 @@ public function transformHistoricalDataResult_unexpectedHeaderLineCsvGiven_throw public function transformHistoricalDataResult_invalidDateTimeFormatCsvGiven_throwApiException(): void { $this->expectException(ApiException::class); - $this->expectExceptionMessage('Not a date in column "Date":2017-07'); + $this->expectExceptionMessage('Response is not a valid JSON'); $this->resultDecoder->transformHistoricalDataResult(file_get_contents(__DIR__.'/fixtures/invalidDateTimeFormatHistoricalData.csv')); } @@ -163,7 +163,7 @@ public function transformHistoricalDataResult_invalidDateTimeFormatCsvGiven_thro public function transformHistoricalDataResult_invalidNumericStringCsvGiven_throwApiException(): void { $this->expectException(ApiException::class); - $this->expectExceptionMessage('Not a number in column "High": this_is_not_numeric_string'); + $this->expectExceptionMessage('Response is not a valid JSON'); $this->resultDecoder->transformHistoricalDataResult(file_get_contents(__DIR__.'/fixtures/invalidNumericStringHistoricalData.csv')); } @@ -173,16 +173,17 @@ public function transformHistoricalDataResult_invalidNumericStringCsvGiven_throw */ public function transformDividendDataResult_csvGiven_returnArrayOfDividendData(): void { - $returnedResult = $this->resultDecoder->transformDividendDataResult(file_get_contents(__DIR__.'/fixtures/dividendData.csv')); + $returnedResult = $this->resultDecoder->transformDividendDataResult(file_get_contents(__DIR__.'/fixtures/dividendData.json')); $this->assertIsArray($returnedResult); $this->assertContainsOnlyInstancesOf(DividendData::class, $returnedResult); + $firstResult = array_shift($returnedResult); $expectedExchangeRate = new DividendData( - new \DateTime('2017-07-11'), - 0.205 + new \DateTime('2019-11-07', new \DateTimeZone('UTC')), + 0.1925 ); - $this->assertEquals($expectedExchangeRate, $returnedResult[0]); + $this->assertEquals($expectedExchangeRate, $firstResult); } /** @@ -191,7 +192,7 @@ public function transformDividendDataResult_csvGiven_returnArrayOfDividendData() public function transformDividendDataResult_invalidColumnsCsvGiven_throwApiException(): void { $this->expectException(ApiException::class); - $this->expectExceptionMessage('CSV did not contain correct number of columns'); + $this->expectExceptionMessage('Response is not a valid JSON'); $this->resultDecoder->transformDividendDataResult(file_get_contents(__DIR__.'/fixtures/invalidColumnsDividendData.csv')); } @@ -202,7 +203,7 @@ public function transformDividendDataResult_invalidColumnsCsvGiven_throwApiExcep public function transformDividendDataResult_unexpectedHeaderLineCsvGiven_throwApiException(): void { $this->expectException(ApiException::class); - $this->expectExceptionMessage('CSV header line did not match expected header line, given: 12345 1234567, expected: Date,Dividends'); + $this->expectExceptionMessage('Response is not a valid JSON'); $invalidCsvString = "12345\t1234567\t"; $this->resultDecoder->transformDividendDataResult($invalidCsvString); @@ -214,7 +215,7 @@ public function transformDividendDataResult_unexpectedHeaderLineCsvGiven_throwAp public function transformDividendDataResult_invalidDateTimeFormatCsvGiven_throwApiException(): void { $this->expectException(ApiException::class); - $this->expectExceptionMessage('Not a date in column "Date":2017-07'); + $this->expectExceptionMessage('Response is not a valid JSON'); $this->resultDecoder->transformDividendDataResult(file_get_contents(__DIR__.'/fixtures/invalidDateTimeFormatDividendData.csv')); } @@ -225,7 +226,7 @@ public function transformDividendDataResult_invalidDateTimeFormatCsvGiven_throwA public function transformDividendDataResult_invalidNumericStringCsvGiven_throwApiException(): void { $this->expectException(ApiException::class); - $this->expectExceptionMessage('Not a number in column Dividends: this_is_not_numeric_string'); + $this->expectExceptionMessage('Response is not a valid JSON'); $this->resultDecoder->transformDividendDataResult(file_get_contents(__DIR__.'/fixtures/invalidNumericStringDividendData.csv')); } @@ -235,16 +236,17 @@ public function transformDividendDataResult_invalidNumericStringCsvGiven_throwAp */ public function transformSplitDataResult_csvGiven_returnArrayOfSplitData(): void { - $returnedResult = $this->resultDecoder->transformSplitDataResult(file_get_contents(__DIR__.'/fixtures/splitData.csv')); + $returnedResult = $this->resultDecoder->transformSplitDataResult(file_get_contents(__DIR__.'/fixtures/splitData.json')); $this->assertIsArray($returnedResult); $this->assertContainsOnlyInstancesOf(SplitData::class, $returnedResult); + $firstResult = array_shift($returnedResult); $expectedExchangeRate = new SplitData( - new \DateTime('2017-07-11'), + new \DateTime('2020-08-31', new \DateTimeZone('UTC')), '4:1' ); - $this->assertEquals($expectedExchangeRate, $returnedResult[0]); + $this->assertEquals($expectedExchangeRate, $firstResult); } /** @@ -253,7 +255,7 @@ public function transformSplitDataResult_csvGiven_returnArrayOfSplitData(): void public function transformSplitDataResult_invalidColumnsCsvGiven_throwApiException(): void { $this->expectException(ApiException::class); - $this->expectExceptionMessage('CSV did not contain correct number of columns'); + $this->expectExceptionMessage('Response is not a valid JSON'); $this->resultDecoder->transformSplitDataResult(file_get_contents(__DIR__.'/fixtures/invalidColumnsSplitData.csv')); } @@ -264,7 +266,7 @@ public function transformSplitDataResult_invalidColumnsCsvGiven_throwApiExceptio public function transformSplitDataResult_unexpectedHeaderLineCsvGiven_throwApiException(): void { $this->expectException(ApiException::class); - $this->expectExceptionMessage('CSV header line did not match expected header line, given: 12345 1234567, expected: Date,Stock Splits'); + $this->expectExceptionMessage('Response is not a valid JSON'); $invalidCsvString = "12345\t1234567\t"; $this->resultDecoder->transformSplitDataResult($invalidCsvString); @@ -276,7 +278,7 @@ public function transformSplitDataResult_unexpectedHeaderLineCsvGiven_throwApiEx public function transformSplitDataResult_invalidDateTimeFormatCsvGiven_throwApiException(): void { $this->expectException(ApiException::class); - $this->expectExceptionMessage('Not a date in column "Date":2017-07'); + $this->expectExceptionMessage('Response is not a valid JSON'); $this->resultDecoder->transformSplitDataResult(file_get_contents(__DIR__.'/fixtures/invalidDateTimeFormatSplitData.csv')); } diff --git a/tests/fixtures/dividendData.csv b/tests/fixtures/dividendData.csv deleted file mode 100644 index 9fc9be3..0000000 --- a/tests/fixtures/dividendData.csv +++ /dev/null @@ -1,11 +0,0 @@ -Date,Dividends -2017-07-11,0.205 -2017-07-12,0.206 -2017-07-13,0.207 -2017-07-14,0.208 -2017-07-17,0.209 -2017-07-18,0.200 -2017-07-19,0.201 -2017-07-20,0.202 -2017-07-21,0.203 -2017-07-24,0.204 diff --git a/tests/fixtures/dividendData.json b/tests/fixtures/dividendData.json new file mode 100644 index 0000000..2e529a4 --- /dev/null +++ b/tests/fixtures/dividendData.json @@ -0,0 +1 @@ +{"chart":{"result":[{"meta":{"currency":"USD","symbol":"AAPL","exchangeName":"NMS","fullExchangeName":"NasdaqGS","instrumentType":"EQUITY","firstTradeDate":345479400,"regularMarketTime":1728676801,"hasPrePostMarketData":true,"gmtoffset":-14400,"timezone":"EDT","exchangeTimezoneName":"America/New_York","regularMarketPrice":227.55,"fiftyTwoWeekHigh":229.41,"fiftyTwoWeekLow":227.34,"regularMarketDayHigh":229.41,"regularMarketDayLow":227.34,"regularMarketVolume":31759188,"longName":"Apple Inc.","shortName":"Apple Inc.","chartPreviousClose":59.053,"priceHint":2,"currentTradingPeriod":{"pre":{"timezone":"EDT","start":1728892800,"end":1728912600,"gmtoffset":-14400},"regular":{"timezone":"EDT","start":1728912600,"end":1728936000,"gmtoffset":-14400},"post":{"timezone":"EDT","start":1728936000,"end":1728950400,"gmtoffset":-14400}},"dataGranularity":"1mo","range":"","validRanges":["1d","5d","1mo","3mo","6mo","1y","2y","5y","10y","ytd","max"]},"timestamp":[1572580800,1575176400,1577854800,1580533200,1583038800,1585713600,1588305600,1590984000,1593576000,1596254400,1598932800,1601524800,1604203200,1606798800,1609477200,1612155600,1614574800,1617249600,1619841600,1622520000,1625112000,1627790400,1630468800,1633060800,1635739200,1638334800,1641013200,1643691600,1646110800,1648785600,1651377600,1654056000,1656648000,1659326400,1662004800,1664596800,1667275200,1669870800,1672549200,1675227600,1677646800,1680321600,1682913600,1685592000,1688184000,1690862400,1693540800,1696132800,1698811200,1701406800,1704085200,1706763600,1709269200,1711944000,1714536000,1717214400,1719806400,1722484800,1725163200,1727755200,1728676801],"events":{"dividends":{"1572580800":{"amount":0.1925,"date":1573137000},"1580533200":{"amount":0.1925,"date":1581085800},"1588305600":{"amount":0.205,"date":1588944600},"1596254400":{"amount":0.205,"date":1596807000},"1604203200":{"amount":0.205,"date":1604673000},"1612155600":{"amount":0.205,"date":1612535400},"1619841600":{"amount":0.22,"date":1620394200},"1627790400":{"amount":0.22,"date":1628256600},"1635739200":{"amount":0.22,"date":1636119000},"1643691600":{"amount":0.22,"date":1643985000},"1651377600":{"amount":0.23,"date":1651843800},"1659326400":{"amount":0.23,"date":1659706200},"1667275200":{"amount":0.23,"date":1667568600},"1675227600":{"amount":0.23,"date":1676039400},"1682913600":{"amount":0.24,"date":1683898200},"1690862400":{"amount":0.24,"date":1691760600},"1698811200":{"amount":0.24,"date":1699626600},"1706763600":{"amount":0.24,"date":1707489000},"1714536000":{"amount":0.25,"date":1715347800},"1722484800":{"amount":0.25,"date":1723469400}}},"indicators":{"quote":[{"close":[66.8125,73.4124984741211,77.37750244140625,68.33999633789062,63.5724983215332,73.44999694824219,79.48500061035156,91.19999694824219,106.26000213623047,129.0399932861328,115.80999755859375,108.86000061035156,119.05000305175781,132.69000244140625,131.9600067138672,121.26000213623047,122.1500015258789,131.4600067138672,124.61000061035156,136.9600067138672,145.86000061035156,151.8300018310547,141.5,149.8000030517578,165.3000030517578,177.57000732421875,174.77999877929688,165.1199951171875,174.61000061035156,157.64999389648438,148.83999633789062,136.72000122070312,162.50999450683594,157.22000122070312,138.1999969482422,153.33999633789062,148.02999877929688,129.92999267578125,144.2899932861328,147.41000366210938,164.89999389648438,169.67999267578125,177.25,193.97000122070312,196.4499969482422,187.8699951171875,171.2100067138672,170.77000427246094,189.9499969482422,192.52999877929688,184.39999389648438,180.75,171.47999572753906,170.3300018310547,192.25,210.6199951171875,222.0800018310547,229.0,233.0,227.5500030517578,227.5500030517578],"open":[62.3849983215332,66.81749725341797,74.05999755859375,76.07499694824219,70.56999969482422,61.625,71.5625,79.4375,91.27999877929688,108.19999694824219,132.75999450683594,117.63999938964844,109.11000061035156,121.01000213623047,133.52000427246094,133.75,123.75,123.66000366210938,132.0399932861328,125.08000183105469,136.60000610351562,146.36000061035156,152.8300018310547,141.89999389648438,148.99000549316406,167.47999572753906,177.8300018310547,174.00999450683594,164.6999969482422,174.02999877929688,156.7100067138672,149.89999389648438,136.0399932861328,161.00999450683594,156.63999938964844,138.2100067138672,155.0800018310547,148.2100067138672,130.27999877929688,143.97000122070312,146.8300018310547,164.27000427246094,169.27999877929688,177.6999969482422,193.77999877929688,196.24000549316406,189.49000549316406,171.22000122070312,171.0,190.3300018310547,187.14999389648438,183.99000549316406,179.5500030517578,171.19000244140625,169.5800018310547,192.89999389648438,212.08999633789062,224.3699951171875,228.5500030517578,229.52000427246094,229.32000732421875],"low":[62.290000915527344,64.07250213623047,73.1875,64.09249877929688,53.15250015258789,59.224998474121094,71.4625015258789,79.30249786376953,89.1449966430664,107.89250183105469,103.0999984741211,107.72000122070312,107.31999969482422,120.01000213623047,126.37999725341797,118.38999938964844,116.20999908447266,122.48999786376953,122.25,123.12999725341797,135.75999450683594,144.5,141.27000427246094,138.27000427246094,147.47999572753906,157.8000030517578,154.6999969482422,152.0,150.10000610351562,155.3800048828125,132.61000061035156,129.0399932861328,135.66000366210938,157.13999938964844,138.0,134.3699951171875,134.3800048828125,125.87000274658203,124.16999816894531,141.32000732421875,143.89999389648438,159.77999877929688,164.30999755859375,176.92999267578125,186.60000610351562,171.9600067138672,167.6199951171875,165.6699981689453,170.1199951171875,187.4499969482422,180.1699981689453,179.25,168.49000549316406,164.0800018310547,169.11000061035156,192.14999389648438,211.9199981689453,196.0,213.9199981689453,221.3300018310547,227.33999633789062],"high":[67.0,73.49250030517578,81.9625015258789,81.80500030517578,76.0,73.63249969482422,81.05999755859375,93.09500122070312,106.41500091552734,131.0,137.97999572753906,125.38999938964844,121.98999786376953,138.7899932861328,145.08999633789062,137.8800048828125,128.72000122070312,137.07000732421875,134.07000732421875,137.41000366210938,150.0,153.49000549316406,157.25999450683594,153.1699981689453,165.6999969482422,182.1300048828125,182.94000244140625,176.64999389648438,179.61000061035156,178.49000549316406,166.47999572753906,151.74000549316406,163.6300048828125,176.14999389648438,164.25999450683594,157.5,155.4499969482422,150.9199981689453,147.22999572753906,157.3800048828125,165.0,169.85000610351562,179.35000610351562,194.47999572753906,198.22999572753906,196.72999572753906,189.97999572753906,182.33999633789062,192.92999267578125,199.6199951171875,196.3800048828125,191.0500030517578,180.52999877929688,178.36000061035156,193.0,220.1999969482422,237.22999572753906,232.9199981689453,233.08999633789062,229.75,229.41000366210938],"volume":[1793326000,2388794800,2934370400,3019279200,6280072400,3265299200,2805936000,3243375600,3020283200,4070061100,3885245100,2894666500,2123077300,2322189600,2240262000,1833855600,2650418200,1889857500,1711934900,1606590000,1919035100,1461542800,1797835100,1565079200,1691029000,2444766700,2108446000,1627516300,2180800100,1687795600,2401040300,1749099800,1447125400,1510239600,2084722800,1868139700,1724847700,1675731200,1443652500,1307198900,1520266600,969709700,1275155500,1297101100,996066400,1322439400,1337586600,1172719600,1099586100,1062774800,1187219300,1161627000,1432782800,1245717000,1336537700,1723984500,1153099800,1122667000,1232140300,332258600,31759188]}],"adjclose":[{"adjclose":[64.65072631835938,71.25043487548828,75.09867858886719,66.32732391357422,61.8466682434082,71.45600891113281,77.32717895507812,88.96431732177734,103.6551513671875,125.876708984375,113.17472076416016,106.38287353515625,116.34099578857422,129.89434814453125,129.1797332763672,118.70514678955078,119.75508117675781,128.88253784179688,122.16685485839844,134.50277709960938,143.2431182861328,149.1060028076172,139.16954040527344,147.33282470703125,162.57757568359375,174.90037536621094,172.15231323242188,162.6375274658203,172.2039794921875,155.47764587402344,146.78907775878906,135.03416442871094,160.50619506835938,155.2814178466797,136.68553161621094,151.65963745117188,146.4078369140625,128.71932983398438,142.9455108642578,146.03646850585938,163.61293029785156,168.35562133789062,175.8665313720703,192.72225952148438,195.18629455566406,186.66146850585938,170.33837890625,169.90060424804688,188.98294067382812,191.80218505859375,183.70291137695312,180.06671142578125,171.0497283935547,169.90261840820312,191.7676239013672,210.37648010253906,221.8232421875,228.73524475097656,233.0,227.5500030517578,227.5500030517578]}]}}],"error":null}} \ No newline at end of file diff --git a/tests/fixtures/historicalData.csv b/tests/fixtures/historicalData.csv deleted file mode 100644 index 678f5cd..0000000 --- a/tests/fixtures/historicalData.csv +++ /dev/null @@ -1,25 +0,0 @@ -Date,Open,High,Low,Close,Adj Close,Volume -2017-07-11,144.729996,145.850006,144.380005,145.529999,145.529999,19781800 -2017-07-12,145.869995,146.179993,144.820007,145.740005,145.740005,24884500 -2017-07-13,145.500000,148.490005,145.440002,147.770004,147.770004,25199400 -2017-07-14,147.970001,149.330002,147.330002,149.039993,149.039993,20132100 -2017-07-17,148.820007,150.899994,148.570007,149.559998,149.559998,23793500 -2017-07-18,149.199997,150.130005,148.669998,150.080002,150.080002,17868800 -2017-07-19,150.479996,151.419998,149.949997,151.020004,151.020004,20923000 -2017-07-20,151.500000,151.740005,150.190002,150.339996,150.339996,17243700 -2017-07-21,149.990005,150.440002,148.880005,150.270004,150.270004,26252600 -2017-07-24,150.580002,152.440002,149.899994,152.089996,152.089996,21493200 -2017-07-25,151.800003,153.839996,151.800003,152.740005,152.740005,18853900 -2017-07-26,153.350006,153.929993,153.059998,153.460007,153.460007,15781000 -2017-07-27,153.750000,153.990005,147.300003,150.559998,150.559998,32476300 -2017-07-28,149.889999,150.229996,149.190002,149.500000,149.500000,17213700 -2017-07-31,149.899994,150.330002,148.130005,148.729996,148.729996,19845900 -2017-08-01,149.100006,150.220001,148.410004,150.050003,150.050003,35368600 -2017-08-02,159.279999,159.750000,156.160004,157.139999,157.139999,69936800 -2017-08-03,157.050003,157.210007,155.020004,155.570007,155.570007,27097300 -2017-08-04,156.070007,157.399994,155.690002,156.389999,156.389999,20559900 -2017-08-07,157.059998,158.919998,156.669998,158.809998,158.809998,21870300 -2017-08-08,null,null,null,null,null,null -2017-08-09,158.600006,161.830002,158.270004,160.080002,160.080002,36205900 -2017-08-10,159.259995,161.270004,159.110001,161.059998,161.059998,25845200 -2017-08-11,159.899994,160.000000,155.180099,155.190002,155.190002,34240467 diff --git a/tests/fixtures/historicalData.json b/tests/fixtures/historicalData.json new file mode 100644 index 0000000..abf355a --- /dev/null +++ b/tests/fixtures/historicalData.json @@ -0,0 +1 @@ +{"chart":{"result":[{"meta":{"currency":"USD","symbol":"AAPL","exchangeName":"NMS","fullExchangeName":"NasdaqGS","instrumentType":"EQUITY","firstTradeDate":345479400,"regularMarketTime":1728676801,"hasPrePostMarketData":true,"gmtoffset":-14400,"timezone":"EDT","exchangeTimezoneName":"America/New_York","regularMarketPrice":227.55,"fiftyTwoWeekHigh":229.41,"fiftyTwoWeekLow":227.34,"regularMarketDayHigh":229.41,"regularMarketDayLow":227.34,"regularMarketVolume":31759188,"longName":"Apple Inc.","shortName":"Apple Inc.","chartPreviousClose":227.79,"priceHint":2,"currentTradingPeriod":{"pre":{"timezone":"EDT","start":1728892800,"end":1728912600,"gmtoffset":-14400},"regular":{"timezone":"EDT","start":1728912600,"end":1728936000,"gmtoffset":-14400},"post":{"timezone":"EDT","start":1728936000,"end":1728950400,"gmtoffset":-14400}},"dataGranularity":"1d","range":"","validRanges":["1d","5d","1mo","3mo","6mo","1y","2y","5y","10y","ytd","max"]},"timestamp":[1727703000,1727789400,1727875800,1727962200,1728048600,1728307800,1728394200,1728480600,1728567000,1728653400],"indicators":{"quote":[{"open":[230.0399932861328,229.52000427246094,225.88999938964844,225.13999938964844,227.89999389648438,224.5,224.3000030517578,225.22999572753906,227.77999877929688,229.3000030517578],"low":[229.64999389648438,223.74000549316406,223.02000427246094,223.32000732421875,224.1300048828125,221.3300018310547,223.25,224.8300018310547,227.1699981689453,227.33999633789062],"close":[233.0,226.2100067138672,226.77999877929688,225.6699981689453,226.8000030517578,221.69000244140625,225.77000427246094,229.5399932861328,229.0399932861328,227.5500030517578],"volume":[54541900,63285000,32880600,34044200,37245100,39505400,31855700,33591100,28183500,31668000],"high":[233.0,229.64999389648438,227.3699951171875,226.80999755859375,228.0,225.69000244140625,225.97999572753906,229.75,229.5,229.41000366210938]}],"adjclose":[{"adjclose":[233.0,226.2100067138672,226.77999877929688,225.6699981689453,226.8000030517578,221.69000244140625,225.77000427246094,229.5399932861328,229.0399932861328,227.5500030517578]}]}}],"error":null}} \ No newline at end of file diff --git a/tests/fixtures/splitData.csv b/tests/fixtures/splitData.csv deleted file mode 100644 index 321e8cb..0000000 --- a/tests/fixtures/splitData.csv +++ /dev/null @@ -1,5 +0,0 @@ -Date,Stock Splits -2017-07-11,4:1 -2017-07-12,3:1 -2017-07-13,2:1 -2017-07-14,1:1 diff --git a/tests/fixtures/splitData.json b/tests/fixtures/splitData.json new file mode 100644 index 0000000..13c1fce --- /dev/null +++ b/tests/fixtures/splitData.json @@ -0,0 +1 @@ +{"chart":{"result":[{"meta":{"currency":"USD","symbol":"AAPL","exchangeName":"NMS","fullExchangeName":"NasdaqGS","instrumentType":"EQUITY","firstTradeDate":345479400,"regularMarketTime":1728676801,"hasPrePostMarketData":true,"gmtoffset":-14400,"timezone":"EDT","exchangeTimezoneName":"America/New_York","regularMarketPrice":227.55,"fiftyTwoWeekHigh":229.41,"fiftyTwoWeekLow":227.34,"regularMarketDayHigh":229.41,"regularMarketDayLow":227.34,"regularMarketVolume":31759188,"longName":"Apple Inc.","shortName":"Apple Inc.","chartPreviousClose":59.053,"priceHint":2,"currentTradingPeriod":{"pre":{"timezone":"EDT","start":1728892800,"end":1728912600,"gmtoffset":-14400},"regular":{"timezone":"EDT","start":1728912600,"end":1728936000,"gmtoffset":-14400},"post":{"timezone":"EDT","start":1728936000,"end":1728950400,"gmtoffset":-14400}},"dataGranularity":"1mo","range":"","validRanges":["1d","5d","1mo","3mo","6mo","1y","2y","5y","10y","ytd","max"]},"timestamp":[1572580800,1575176400,1577854800,1580533200,1583038800,1585713600,1588305600,1590984000,1593576000,1596254400,1598932800,1601524800,1604203200,1606798800,1609477200,1612155600,1614574800,1617249600,1619841600,1622520000,1625112000,1627790400,1630468800,1633060800,1635739200,1638334800,1641013200,1643691600,1646110800,1648785600,1651377600,1654056000,1656648000,1659326400,1662004800,1664596800,1667275200,1669870800,1672549200,1675227600,1677646800,1680321600,1682913600,1685592000,1688184000,1690862400,1693540800,1696132800,1698811200,1701406800,1704085200,1706763600,1709269200,1711944000,1714536000,1717214400,1719806400,1722484800,1725163200,1727755200,1728676801],"events":{"splits":{"1596254400":{"date":1598880600,"numerator":4.0,"denominator":1.0,"splitRatio":"4:1"}}},"indicators":{"quote":[{"volume":[1793326000,2388794800,2934370400,3019279200,6280072400,3265299200,2805936000,3243375600,3020283200,4070061100,3885245100,2894666500,2123077300,2322189600,2240262000,1833855600,2650418200,1889857500,1711934900,1606590000,1919035100,1461542800,1797835100,1565079200,1691029000,2444766700,2108446000,1627516300,2180800100,1687795600,2401040300,1749099800,1447125400,1510239600,2084722800,1868139700,1724847700,1675731200,1443652500,1307198900,1520266600,969709700,1275155500,1297101100,996066400,1322439400,1337586600,1172719600,1099586100,1062774800,1187219300,1161627000,1432782800,1245717000,1336537700,1723984500,1153099800,1122667000,1232140300,332258600,31759188],"high":[67.0,73.49250030517578,81.9625015258789,81.80500030517578,76.0,73.63249969482422,81.05999755859375,93.09500122070312,106.41500091552734,131.0,137.97999572753906,125.38999938964844,121.98999786376953,138.7899932861328,145.08999633789062,137.8800048828125,128.72000122070312,137.07000732421875,134.07000732421875,137.41000366210938,150.0,153.49000549316406,157.25999450683594,153.1699981689453,165.6999969482422,182.1300048828125,182.94000244140625,176.64999389648438,179.61000061035156,178.49000549316406,166.47999572753906,151.74000549316406,163.6300048828125,176.14999389648438,164.25999450683594,157.5,155.4499969482422,150.9199981689453,147.22999572753906,157.3800048828125,165.0,169.85000610351562,179.35000610351562,194.47999572753906,198.22999572753906,196.72999572753906,189.97999572753906,182.33999633789062,192.92999267578125,199.6199951171875,196.3800048828125,191.0500030517578,180.52999877929688,178.36000061035156,193.0,220.1999969482422,237.22999572753906,232.9199981689453,233.08999633789062,229.75,229.41000366210938],"close":[66.8125,73.4124984741211,77.37750244140625,68.33999633789062,63.5724983215332,73.44999694824219,79.48500061035156,91.19999694824219,106.26000213623047,129.0399932861328,115.80999755859375,108.86000061035156,119.05000305175781,132.69000244140625,131.9600067138672,121.26000213623047,122.1500015258789,131.4600067138672,124.61000061035156,136.9600067138672,145.86000061035156,151.8300018310547,141.5,149.8000030517578,165.3000030517578,177.57000732421875,174.77999877929688,165.1199951171875,174.61000061035156,157.64999389648438,148.83999633789062,136.72000122070312,162.50999450683594,157.22000122070312,138.1999969482422,153.33999633789062,148.02999877929688,129.92999267578125,144.2899932861328,147.41000366210938,164.89999389648438,169.67999267578125,177.25,193.97000122070312,196.4499969482422,187.8699951171875,171.2100067138672,170.77000427246094,189.9499969482422,192.52999877929688,184.39999389648438,180.75,171.47999572753906,170.3300018310547,192.25,210.6199951171875,222.0800018310547,229.0,233.0,227.5500030517578,227.5500030517578],"open":[62.3849983215332,66.81749725341797,74.05999755859375,76.07499694824219,70.56999969482422,61.625,71.5625,79.4375,91.27999877929688,108.19999694824219,132.75999450683594,117.63999938964844,109.11000061035156,121.01000213623047,133.52000427246094,133.75,123.75,123.66000366210938,132.0399932861328,125.08000183105469,136.60000610351562,146.36000061035156,152.8300018310547,141.89999389648438,148.99000549316406,167.47999572753906,177.8300018310547,174.00999450683594,164.6999969482422,174.02999877929688,156.7100067138672,149.89999389648438,136.0399932861328,161.00999450683594,156.63999938964844,138.2100067138672,155.0800018310547,148.2100067138672,130.27999877929688,143.97000122070312,146.8300018310547,164.27000427246094,169.27999877929688,177.6999969482422,193.77999877929688,196.24000549316406,189.49000549316406,171.22000122070312,171.0,190.3300018310547,187.14999389648438,183.99000549316406,179.5500030517578,171.19000244140625,169.5800018310547,192.89999389648438,212.08999633789062,224.3699951171875,228.5500030517578,229.52000427246094,229.32000732421875],"low":[62.290000915527344,64.07250213623047,73.1875,64.09249877929688,53.15250015258789,59.224998474121094,71.4625015258789,79.30249786376953,89.1449966430664,107.89250183105469,103.0999984741211,107.72000122070312,107.31999969482422,120.01000213623047,126.37999725341797,118.38999938964844,116.20999908447266,122.48999786376953,122.25,123.12999725341797,135.75999450683594,144.5,141.27000427246094,138.27000427246094,147.47999572753906,157.8000030517578,154.6999969482422,152.0,150.10000610351562,155.3800048828125,132.61000061035156,129.0399932861328,135.66000366210938,157.13999938964844,138.0,134.3699951171875,134.3800048828125,125.87000274658203,124.16999816894531,141.32000732421875,143.89999389648438,159.77999877929688,164.30999755859375,176.92999267578125,186.60000610351562,171.9600067138672,167.6199951171875,165.6699981689453,170.1199951171875,187.4499969482422,180.1699981689453,179.25,168.49000549316406,164.0800018310547,169.11000061035156,192.14999389648438,211.9199981689453,196.0,213.9199981689453,221.3300018310547,227.33999633789062]}],"adjclose":[{"adjclose":[64.65070343017578,71.25043487548828,75.09866333007812,66.32731628417969,61.8466682434082,71.45601654052734,77.32716369628906,88.9643325805664,103.6551513671875,125.87670135498047,113.17472839355469,106.38286590576172,116.34101104736328,129.8943328857422,129.17970275878906,118.70515441894531,119.75508117675781,128.88255310058594,122.16686248779297,134.50279235839844,143.2430877685547,149.1060028076172,139.16954040527344,147.33282470703125,162.57757568359375,174.90037536621094,172.15231323242188,162.6375274658203,172.2039794921875,155.4776611328125,146.78907775878906,135.03421020507812,160.50619506835938,155.2814178466797,136.68553161621094,151.65963745117188,146.40782165527344,128.71934509277344,142.94552612304688,146.03646850585938,163.6129150390625,168.35562133789062,175.86654663085938,192.7222442626953,195.18629455566406,186.66146850585938,170.33836364746094,169.90060424804688,188.98294067382812,191.80218505859375,183.70291137695312,180.06671142578125,171.0497283935547,169.90261840820312,191.7676239013672,210.37648010253906,221.8232421875,228.73524475097656,233.0,227.5500030517578,227.5500030517578]}]}}],"error":null}} \ No newline at end of file