├── .gitignore
├── cover.png
├── package.json
├── api
├── client.js
├── indices.js
├── sectors.js
└── index.js
├── README.md
├── yarn.lock
└── index.html
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .vercel
3 | node_modules
--------------------------------------------------------------------------------
/cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/risan/indonesia-stock-exchange/HEAD/cover.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "axios": "^0.21.1",
4 | "yup": "^0.32.8"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/api/client.js:
--------------------------------------------------------------------------------
1 | const axios = require('axios');
2 |
3 | module.exports = axios.create({
4 | baseURL: 'https://pasardana.id/api/',
5 | headers: {
6 | Accept: 'application/json',
7 | 'Accept-Encoding': 'gzip',
8 | Host: 'pasardana.id',
9 | Pragma: 'no-cache',
10 | Referer: 'https://pasardana.id/stock/search',
11 | 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0',
12 | },
13 | });
--------------------------------------------------------------------------------
/api/indices.js:
--------------------------------------------------------------------------------
1 | const client = require('./client');
2 |
3 | module.exports = async (req, res) => {
4 | try {
5 | const response = await client.get('/Stock/GetIndexFilters');
6 |
7 | const data = response.data.map((i, idx) => ({
8 | id: i.Id,
9 | name: i.Name,
10 | stocks_url: `https://indonesia-stock-exchange.vercel.app/api?index_id=${i.Id}`,
11 | }));
12 |
13 | return res.json({ data });
14 | } catch (error) {
15 | return res.status(500).json({ error: error.message });
16 | }
17 | };
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Indonesia Stock Exchange API
2 |
3 | API to get Indonesia stock exchange daily data from [Pasardana](https://pasardana.id/) website.
4 |
5 | ## Installation
6 |
7 | You'll need a [Vercel](https://vercel.com/home) account and [Vercel CLI](https://vercel.com/download) to run this API.
8 |
9 | ```bash
10 | # Clone the repository
11 | $ git clone git@github.com:risan/indonesia-stock-exchange.git
12 |
13 | # Install dependencies
14 | $ npm install
15 |
16 | # Spin up vercel development
17 | $ vercel dev
18 | ```
19 |
20 | Checkout [indonesia-stock-exchange.vercel.app](https://indonesia-stock-exchange.vercel.app/) for API documentation.
--------------------------------------------------------------------------------
/api/sectors.js:
--------------------------------------------------------------------------------
1 | const client = require('./client');
2 |
3 | const getSubsectors = async sectorId => {
4 | const response = await client.get('/StockSubSector/GetSubSectorFromSector', {
5 | params: { sectorId },
6 | });
7 |
8 | return response.data;
9 | };
10 |
11 | module.exports = async (req, res) => {
12 | try {
13 | const response = await client.get('/StockSector/GetAll');
14 |
15 | const subSectors = await Promise.all(response.data.map(i => getSubsectors(i.Id)));
16 |
17 | const data = response.data.map((sector, idx) => ({
18 | id: sector.Id,
19 | name: sector.Name,
20 | stocks_url: `https://indonesia-stock-exchange.vercel.app/api?sector_id=${sector.Id}`,
21 | subsectors: subSectors[idx].map(subsector => ({
22 | id: subsector.Id,
23 | name: subsector.Name,
24 | stocks_url: `https://indonesia-stock-exchange.vercel.app/api?subsector_id=${subsector.Id}`,
25 | })),
26 | }));
27 |
28 | return res.json({ data });
29 | } catch (error) {
30 | return res.status(500).json({ error: error.message });
31 | }
32 | };
--------------------------------------------------------------------------------
/api/index.js:
--------------------------------------------------------------------------------
1 | const yup = require('yup');
2 | const client = require('./client');
3 |
4 | const SORTABLE_FIELDS = [
5 | 'Code',
6 | 'Name',
7 | 'SectorName',
8 | 'SubSectorName',
9 | 'Last',
10 | 'AdjustedClosingPrice',
11 | 'PrevClosingPrice',
12 | 'AdjustedOpenPrice',
13 | 'AdjustedHighPrice',
14 | 'AdjustedLowPrice',
15 | 'Per',
16 | 'Pbr',
17 | 'Volume',
18 | 'Value',
19 | 'Frequency',
20 | 'OneDay',
21 | 'OneWeek',
22 | 'OneMonth',
23 | 'ThreeMonth',
24 | 'SixMonth',
25 | 'OneYear',
26 | 'ThreeYear',
27 | 'FiveYear',
28 | 'TenYear',
29 | 'Mtd',
30 | 'Ytd',
31 | 'Capitalization',
32 | 'BetaOneYear',
33 | 'StdevOneYear',
34 | 'LastDate',
35 | ];
36 |
37 | const validate = async (params) => {
38 | const schema = yup.object().shape({
39 | search: yup.string().trim(),
40 | page: yup.number().positive().integer().default(1),
41 | per_page: yup.number().positive().integer().default(25),
42 | sort_by: yup.string().trim().oneOf(SORTABLE_FIELDS).default('Code'),
43 | sort_direction: yup.string().trim().lowercase().oneOf(['asc', 'desc']).default('asc'),
44 | index_id: yup.number().positive().integer(),
45 | sector_id: yup.number().positive().integer(),
46 | subsector_id: yup.number().positive().integer(),
47 | });
48 |
49 | await schema.validate(params);
50 |
51 | const data = schema.cast(params);
52 |
53 | return {
54 | Keywords: data.search,
55 | pageBegin: data.page,
56 | pageLength: data.per_page,
57 | sortField: data.sort_by,
58 | sortOrder: data.sort_direction.toUpperCase(),
59 | Index: data.index_id,
60 | StockSector: data.sector_id,
61 | StockSubSector: data.subsector_id,
62 | };
63 | };
64 |
65 | const getData = async (params = {}) => {
66 | const parsedParams = await validate(params);
67 |
68 | try {
69 | const response = await client.get('/StockSearchResult/GetAll', {
70 | params: parsedParams,
71 | });
72 |
73 | return response.data;
74 | } catch (error) {
75 | const message = error.response ? error.response.data.message : error.message;
76 |
77 | throw new Error(message);
78 | }
79 | };
80 |
81 | module.exports = async (req, res) => {
82 | try {
83 | const data = await getData(req.query);
84 |
85 | return res.json({ data });
86 | } catch (error) {
87 | const status = error.name === 'ValidationError' ? 422 : 500;
88 |
89 | return res.status(status).json({ error: error.message });
90 | }
91 | };
--------------------------------------------------------------------------------
/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@babel/runtime@^7.10.5":
6 | version "7.12.5"
7 | resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e"
8 | integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==
9 | dependencies:
10 | regenerator-runtime "^0.13.4"
11 |
12 | "@types/lodash@^4.14.165":
13 | version "4.14.167"
14 | resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.167.tgz#ce7d78553e3c886d4ea643c37ec7edc20f16765e"
15 | integrity sha512-w7tQPjARrvdeBkX/Rwg95S592JwxqOjmms3zWQ0XZgSyxSLdzWaYH3vErBhdVS/lRBX7F8aBYcYJYTr5TMGOzw==
16 |
17 | axios@^0.21.1:
18 | version "0.21.1"
19 | resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
20 | integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
21 | dependencies:
22 | follow-redirects "^1.10.0"
23 |
24 | follow-redirects@^1.10.0:
25 | version "1.13.1"
26 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7"
27 | integrity sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==
28 |
29 | lodash-es@^4.17.11:
30 | version "4.17.20"
31 | resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.20.tgz#29f6332eefc60e849f869c264bc71126ad61e8f7"
32 | integrity sha512-JD1COMZsq8maT6mnuz1UMV0jvYD0E0aUsSOdrr1/nAG3dhqQXwRRgeW0cSqH1U43INKcqxaiVIQNOUDld7gRDA==
33 |
34 | lodash@^4.17.20:
35 | version "4.17.20"
36 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
37 | integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
38 |
39 | nanoclone@^0.2.1:
40 | version "0.2.1"
41 | resolved "https://registry.yarnpkg.com/nanoclone/-/nanoclone-0.2.1.tgz#dd4090f8f1a110d26bb32c49ed2f5b9235209ed4"
42 | integrity sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==
43 |
44 | property-expr@^2.0.4:
45 | version "2.0.4"
46 | resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.4.tgz#37b925478e58965031bb612ec5b3260f8241e910"
47 | integrity sha512-sFPkHQjVKheDNnPvotjQmm3KD3uk1fWKUN7CrpdbwmUx3CrG3QiM8QpTSimvig5vTXmTvjz7+TDvXOI9+4rkcg==
48 |
49 | regenerator-runtime@^0.13.4:
50 | version "0.13.7"
51 | resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
52 | integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
53 |
54 | toposort@^2.0.2:
55 | version "2.0.2"
56 | resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330"
57 | integrity sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=
58 |
59 | yup@^0.32.8:
60 | version "0.32.8"
61 | resolved "https://registry.yarnpkg.com/yup/-/yup-0.32.8.tgz#16e4a949a86a69505abf99fd0941305ac9adfc39"
62 | integrity sha512-SZulv5FIZ9d5H99EN5tRCRPXL0eyoYxWIP1AacCrjC9d4DfP13J1dROdKGfpfRHT3eQB6/ikBl5jG21smAfCkA==
63 | dependencies:
64 | "@babel/runtime" "^7.10.5"
65 | "@types/lodash" "^4.14.165"
66 | lodash "^4.17.20"
67 | lodash-es "^4.17.11"
68 | nanoclone "^0.2.1"
69 | property-expr "^2.0.4"
70 | toposort "^2.0.2"
71 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Indonesia Stock Exchange API
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
33 |
34 |
35 |
36 |
37 | 📈
38 | Indonesia Stock Exchange API
39 |
40 |
41 | API to get Indonesia stock exchange daily data from Pasardana website.
42 |
43 |
44 | 1. List of Stocks
45 | https://indonesia-stock-exchange.vercel.app/api
46 |
47 | Query Parameter
48 |
49 |
50 |
51 |
52 | | Parameter |
53 | Description |
54 |
55 |
56 |
57 |
58 | search |
59 |
60 | Type: string
61 | Search query
62 | |
63 |
64 |
65 | page |
66 |
67 | Type: int | Default: 1
68 | Page to retrieve
69 | |
70 |
71 |
72 | per_page |
73 |
74 | Type: int | Default: 25
75 | Number of records per page
76 | |
77 |
78 |
79 | sort_by |
80 |
81 | Type: string | Default: Code
82 | Sort the results by the given field. Possible values:
83 | Code,
84 | Name,
85 | SectorName,
86 | SubSectorName,
87 | Last,
88 | AdjustedClosingPrice,
89 | PrevClosingPrice,
90 | AdjustedOpenPrice,
91 | AdjustedHighPrice,
92 | AdjustedLowPrice,
93 | Per,
94 | Pbr,
95 | Volume,
96 | Value,
97 | Frequency,
98 | OneDay,
99 | OneWeek,
100 | OneMonth,
101 | ThreeMonth,
102 | SixMonth,
103 | OneYear,
104 | ThreeYear,
105 | FiveYear,
106 | TenYear,
107 | Mtd,
108 | Ytd,
109 | Capitalization,
110 | BetaOneYear,
111 | StdevOneYear,
112 | LastDate,
113 | |
114 |
115 |
116 | sort_direction |
117 |
118 | Type: string | Default: asc
119 | Sort direction: asc or desc.
120 | |
121 |
122 |
123 | index_id |
124 |
125 | Type: int | Default:
126 | Filter stocks by the given index id.
127 | Get all possible index ids from the List Indices endpoint.
128 | |
129 |
130 |
131 | sector_id |
132 |
133 | Type: int | Default:
134 | Filter stocks by the given sector id.
135 | Get all possible sector ids from the List Sectors endpoint.
136 | |
137 |
138 |
139 | subsector_id |
140 |
141 | Type: int | Default:
142 | Filter stocks by the given subsector id.
143 | Get all possible subsector ids from the List Sectors endpoint.
144 | |
145 |
146 |
147 |
148 |
149 | 2. List Indices
150 |
151 | List all stock market indices.
152 |
153 | https://indonesia-stock-exchange.vercel.app/api/indices
154 |
155 | 3. List Sectors
156 |
157 | List all stocks' sectors and subsectors.
158 |
159 | https://indonesia-stock-exchange.vercel.app/api/sectors
160 |
161 |
162 | By Risan Bagja | Source Code
163 |
164 |
165 |
166 |
--------------------------------------------------------------------------------