Skip to content

Commit 5804414

Browse files
authored
Merge pull request #1 from MarketDataApp/add-indices
Add Indices > Candles
2 parents 349aa02 + 6453d59 commit 5804414

5 files changed

Lines changed: 140 additions & 3 deletions

File tree

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[![Codecov](https://codecov.io/gh/MarketDataApp/sdk-php/graph/badge.svg?token=5W2IB9F6RU)](https://codecov.io/github/MarketDataApp/sdk-php)
66
[![Total Downloads](https://img.shields.io/packagist/dt/MarketDataApp/sdk-php.svg?style=flat-square)](https://packagist.org/packages/MarketDataApp/sdk-php)
77

8-
This is an official PHP SDK for Market Data. It provides developers with a powerful, easy-to-use interface to obtain
8+
This is the official PHP SDK for [Market Data](https://marketdata.app). It provides developers with a powerful, easy-to-use interface to obtain
99
real-time and historical financial data. Ideal for building financial applications, trading bots, and investment
1010
strategies.
1111

@@ -21,7 +21,13 @@ composer require MarketDataApp/sdk-php
2121

2222
```php
2323
$client = new MarketDataApp\Client();
24-
$quote = $client->indices->quote('AAPL');
24+
$quote = $client->indices->quote('DJI');
25+
$candles = $this->client->indices->candles(
26+
symbol: "DJI",
27+
from: Carbon::parse('2022-09-01'),
28+
to: Carbon::parse('2022-09-05'),
29+
resolution: 'D'
30+
);
2531
```
2632

2733
## Testing

src/Endpoints/Indices.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
namespace MarketDataApp\Endpoints;
44

5+
use Carbon\Carbon;
56
use GuzzleHttp\Exception\GuzzleException;
67
use MarketDataApp\Client;
8+
use MarketDataApp\Endpoints\Responses\IndicesCandles;
79
use MarketDataApp\Endpoints\Responses\IndicesQuote;
810

911
class Indices
@@ -26,4 +28,34 @@ public function quote(string $symbol): IndicesQuote
2628
{
2729
return new IndicesQuote($this->client->execute(self::BASE_URL . "quotes/{$symbol}"));
2830
}
31+
32+
/**
33+
* @param string $symbol The index symbol, without any leading or trailing index identifiers. For example, use DJI
34+
* do not use $DJI, ^DJI, .DJI, DJI.X, etc.
35+
* @param Carbon $from The leftmost candle on a chart (inclusive). If you use countback, to is not required.
36+
* Accepted timestamp inputs: ISO 8601, unix, spreadsheet.
37+
* @param Carbon|null $to The rightmost candle on a chart (inclusive). Accepted timestamp inputs: ISO 8601, unix,
38+
* spreadsheet.
39+
* @param string $resolution The duration of each candle.
40+
* Minutely Resolutions: (minutely, 1, 3, 5, 15, 30, 45, ...) Hourly Resolutions: (hourly, H, 1H, 2H, ...)
41+
* Daily Resolutions: (daily, D, 1D, 2D, ...)
42+
* Weekly Resolutions: (weekly, W, 1W, 2W, ...)
43+
* Monthly Resolutions: (monthly, M, 1M, 2M, ...)
44+
* Yearly Resolutions:(yearly, Y, 1Y, 2Y, ...)
45+
* @param int|null $countback Will fetch a number of candles before (to the left of) to. If you use from, countback
46+
* is not required.
47+
* @return IndicesCandles
48+
* @throws GuzzleException
49+
*/
50+
public function candles(
51+
string $symbol,
52+
Carbon $from,
53+
Carbon $to = null,
54+
string $resolution = 'D',
55+
int $countback = null
56+
): IndicesCandles {
57+
return new IndicesCandles($this->client->execute(self::BASE_URL . "candles/{$resolution}/{$symbol}/",
58+
compact('from', 'to', 'countback')
59+
));
60+
}
2961
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace MarketDataApp\Endpoints\Responses;
4+
5+
use Carbon\Carbon;
6+
7+
class IndicesCandle
8+
{
9+
10+
// Open price.
11+
public string $open;
12+
13+
// High price.
14+
public float $high;
15+
16+
// Low price.
17+
public float $low;
18+
19+
// Close price.
20+
public float $close;
21+
22+
// Candle time (Unix timestamp, UTC). Daily, weekly, monthly, yearly candles are returned without times.
23+
public Carbon $timestamp;
24+
25+
public function __construct(float $open, float $high, float $low, float $close, Carbon $timestamp)
26+
{
27+
$this->open = $open;
28+
$this->high = $high;
29+
$this->low = $low;
30+
$this->close = $close;
31+
$this->timestamp = $timestamp;
32+
}
33+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace MarketDataApp\Endpoints\Responses;
4+
5+
use Carbon\Carbon;
6+
7+
class IndicesCandles
8+
{
9+
// Will always be ok when there is data for the candles requested.
10+
public string $status;
11+
12+
/** @var IndicesCandle[] $candles */
13+
public array $candles;
14+
15+
public function __construct(object $response)
16+
{
17+
// Convert the response to this object.
18+
$this->status = $response->s;
19+
20+
for($i = 0; $i < count($response->o); $i++) {
21+
$this->candles[] = new IndicesCandle(
22+
$response->o[$i],
23+
$response->h[$i],
24+
$response->l[$i],
25+
$response->c[$i],
26+
Carbon::parse($response->t[$i]),
27+
);
28+
}
29+
}
30+
}

tests/IndicesTest.php

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
use GuzzleHttp\Psr7\Request;
1111
use GuzzleHttp\Psr7\Response;
1212
use MarketDataApp\Client;
13+
use MarketDataApp\Endpoints\Responses\IndicesCandle;
14+
use MarketDataApp\Endpoints\Responses\IndicesCandles;
1315
use MarketDataApp\Endpoints\Responses\IndicesQuote;
1416
use PHPUnit\Framework\TestCase;
1517

@@ -39,7 +41,7 @@ public function testIndicesQuote_success()
3941
];
4042
$this->setMockResponses([new Response(200, [], json_encode($mocked_response))]);
4143

42-
$response = $this->client->indices->quote("AAPL");
44+
$response = $this->client->indices->quote("DJI");
4345
$this->assertInstanceOf(IndicesQuote::class, $response);
4446
$this->assertEquals($mocked_response['s'], $response->status);
4547
$this->assertEquals($mocked_response['symbol'][0], $response->symbol);
@@ -51,6 +53,40 @@ public function testIndicesQuote_success()
5153
$this->assertEquals(Carbon::parse($mocked_response['updated']), $response->updated);
5254
}
5355

56+
public function testIndicesCandles_fromTo_success()
57+
{
58+
$mocked_response = [
59+
's' => 'ok',
60+
'c' => [22.84, 23.93, 21.95, 21.44, 21.15],
61+
'h' => [23.27, 24.68, 23.92, 22.66, 22.58],
62+
'l' => [22.26, 22.67, 21.68, 21.44, 20.76],
63+
'o' => [22.41, 24.08, 23.86, 22.06, 21.5],
64+
't' => [1659326400, 1659412800, 1659499200, 1659585600, 1659672000]
65+
];
66+
$this->setMockResponses([new Response(200, [], json_encode($mocked_response))]);
67+
68+
$response = $this->client->indices->candles(
69+
symbol: "DJI",
70+
from: Carbon::parse('2022-09-01'),
71+
to: Carbon::parse('2022-09-05'),
72+
resolution: 'D'
73+
);
74+
75+
// Verify that the response is an object of the correct type.
76+
$this->assertInstanceOf(IndicesCandles::class, $response);
77+
$this->assertCount(5, $response->candles);
78+
79+
// Verify each item in the response is an object of the correct type and has the correct values.
80+
for($i = 0; $i < count($response->candles); $i++) {
81+
$this->assertInstanceOf(IndicesCandle::class, $response->candles[$i]);
82+
$this->assertEquals($mocked_response['c'][$i], $response->candles[$i]->close);
83+
$this->assertEquals($mocked_response['h'][$i], $response->candles[$i]->high);
84+
$this->assertEquals($mocked_response['l'][$i], $response->candles[$i]->low);
85+
$this->assertEquals($mocked_response['o'][$i], $response->candles[$i]->open);
86+
$this->assertEquals(Carbon::parse($mocked_response['t'][$i]), $response->candles[$i]->timestamp);
87+
}
88+
}
89+
5490
public function testExceptionHandling_throwsGuzzleException()
5591
{
5692
$this->setMockResponses([

0 commit comments

Comments
 (0)