-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from 0xTxbi/stock-info-service
stock info service
- Loading branch information
Showing
10 changed files
with
473 additions
and
119 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { MigrationInterface, QueryRunner } from "typeorm" | ||
|
||
export class StockInfo1701883838427 implements MigrationInterface { | ||
|
||
public async up(queryRunner: QueryRunner): Promise<void> { | ||
} | ||
|
||
public async down(queryRunner: QueryRunner): Promise<void> { | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { MigrationInterface, QueryRunner } from "typeorm"; | ||
|
||
export class StockInfo1701883841242 implements MigrationInterface { | ||
name = 'StockInfo1701883841242' | ||
|
||
public async up(queryRunner: QueryRunner): Promise<void> { | ||
await queryRunner.query(`CREATE TABLE "stock_info" ("symbol" character varying NOT NULL, "companyName" character varying NOT NULL, "currentPrice" integer NOT NULL, "industry" character varying NOT NULL, "marketCap" character varying NOT NULL, "changePercent" integer, "volume" integer, "peRatio" integer, CONSTRAINT "PK_e070ed33681787dcb207b8a0b22" PRIMARY KEY ("symbol"))`); | ||
} | ||
|
||
public async down(queryRunner: QueryRunner): Promise<void> { | ||
await queryRunner.query(`DROP TABLE "stock_info"`); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import { Entity, PrimaryColumn, Column } from "typeorm"; | ||
|
||
@Entity() | ||
export class StockInfo { | ||
@PrimaryColumn() | ||
symbol: string; | ||
|
||
@Column() | ||
companyName: string; | ||
|
||
@Column() | ||
currentPrice: number; | ||
|
||
@Column() | ||
industry: string; | ||
|
||
@Column() | ||
marketCap: number; | ||
|
||
@Column() | ||
market: string; | ||
|
||
@Column() | ||
locale: string; | ||
|
||
@Column() | ||
primaryExchange: string; | ||
|
||
@Column() | ||
type: string; | ||
|
||
@Column() | ||
active: boolean; | ||
|
||
@Column() | ||
currencyName: string; | ||
|
||
@Column() | ||
cik: string; | ||
|
||
@Column() | ||
compositeFigi: string; | ||
|
||
@Column() | ||
shareClassFigi: string; | ||
|
||
@Column() | ||
phoneNumber: string; | ||
|
||
@Column() | ||
address: string; | ||
|
||
@Column() | ||
description: string; | ||
|
||
@Column() | ||
sicCode: string; | ||
|
||
@Column() | ||
sicDescription: string; | ||
|
||
@Column() | ||
tickerRoot: string; | ||
|
||
@Column() | ||
homepageUrl: string; | ||
|
||
@Column() | ||
totalEmployees: number; | ||
|
||
@Column() | ||
listDate: string; | ||
|
||
@Column() | ||
logoUrl: string; | ||
|
||
@Column() | ||
iconUrl: string; | ||
|
||
@Column() | ||
shareClassSharesOutstanding: number; | ||
|
||
@Column() | ||
weightedSharesOutstanding: number; | ||
|
||
@Column() | ||
roundLot: number; | ||
} |
122 changes: 122 additions & 0 deletions
122
stock-info-service/src/controllers/StockInfoController.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import { JsonController, Get, Param, QueryParams } from "routing-controllers"; | ||
import { StockInfo } from "../../entities/StockInfo"; | ||
import axios from "axios"; | ||
import * as dotenv from "dotenv"; | ||
import { FilteredStock } from "../types"; | ||
|
||
dotenv.config(); | ||
|
||
class ApiError extends Error { | ||
constructor(message: string) { | ||
super(message); | ||
this.name = "ApiError"; | ||
} | ||
} | ||
|
||
@JsonController("/stock") | ||
export class StockInfoController { | ||
// retrieve stock info | ||
@Get("/:stockSymbol") | ||
async getStockInfo( | ||
@Param("stockSymbol") stockSymbol: string | ||
): Promise<StockInfo> { | ||
try { | ||
// retrieve stock information from the financial API | ||
const apiUrl = `${ | ||
process.env.POL_FIN_API_URL | ||
}/tickers/${stockSymbol.toUpperCase()}?apiKey=${ | ||
process.env.POL_FIN_API_KEY | ||
}`; | ||
const response = await axios.get(apiUrl); | ||
|
||
// map the response data to the StockInfo object | ||
const stockInfo: StockInfo = { | ||
symbol: response.data.results.ticker, | ||
companyName: response.data.results.name, | ||
currentPrice: response.data.results.market_cap, | ||
industry: response.data.results.sic_description, | ||
marketCap: response.data.results.market_cap, | ||
market: response.data.results.market, | ||
locale: response.data.results.locale, | ||
primaryExchange: | ||
response.data.results.primary_exchange, | ||
type: response.data.results.type, | ||
active: response.data.results.active, | ||
currencyName: | ||
response.data.results.currency_name, | ||
cik: response.data.results.cik, | ||
compositeFigi: | ||
response.data.results.composite_figi, | ||
shareClassFigi: | ||
response.data.results.share_class_figi, | ||
phoneNumber: response.data.results.phone_number, | ||
address: response.data.results.address.address1, | ||
description: response.data.results.description, | ||
sicCode: response.data.results.sic_code, | ||
sicDescription: | ||
response.data.results.sic_description, | ||
tickerRoot: response.data.results.ticker_root, | ||
homepageUrl: response.data.results.homepage_url, | ||
totalEmployees: | ||
response.data.results.total_employees, | ||
listDate: response.data.results.list_date, | ||
logoUrl: response.data.results.branding | ||
.logo_url, | ||
iconUrl: response.data.results.branding | ||
.icon_url, | ||
shareClassSharesOutstanding: | ||
response.data.results | ||
.share_class_shares_outstanding, | ||
weightedSharesOutstanding: | ||
response.data.results | ||
.weighted_shares_outstanding, | ||
roundLot: response.data.results.round_lot, | ||
}; | ||
|
||
return stockInfo; | ||
} catch (error) { | ||
if (axios.isAxiosError(error)) { | ||
throw new ApiError( | ||
`Failed to retrieve stock information: ${error.message}` | ||
); | ||
} | ||
throw error; | ||
} | ||
} | ||
|
||
// search for stock | ||
@Get("/search/:query") | ||
async searchStocks( | ||
@Param("query") query: string | ||
): Promise<{ stocks: FilteredStock[] }> { | ||
try { | ||
// retrieve stock symbols by keyword | ||
const searchApiUrl = `${process.env.TD_FIN_API_URL}/symbol_search?symbol=${query}&outputsize=5&apikey=${process.env.TD_FIN_API_KEY}`; | ||
const response = await axios.get(searchApiUrl); | ||
|
||
// filter out stocks whose exchange country isn't the United States | ||
const filteredStocks = response.data.data.filter( | ||
(stock: any) => | ||
stock.country === "United States" | ||
); | ||
|
||
// map the filtered stocks to an array of simplified stock objects | ||
const stocks: FilteredStock[] = filteredStocks.map( | ||
(stock: any) => ({ | ||
symbol: stock.symbol, | ||
exchange: stock.exchange, | ||
instrumentName: stock.instrument_name, | ||
}) | ||
); | ||
|
||
return { stocks }; | ||
} catch (error) { | ||
if (axios.isAxiosError(error)) { | ||
throw new ApiError( | ||
`Failed to perform stock search: ${error.message}` | ||
); | ||
} | ||
throw error; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import "reflect-metadata"; | ||
import { createExpressServer } from "routing-controllers"; | ||
import { StockInfoController } from "./controllers/StockInfoController"; | ||
|
||
const app = createExpressServer({ | ||
controllers: [StockInfoController], | ||
}); | ||
|
||
app.listen(3001, () => { | ||
console.log("the Stock Info Service is running on port 3001"); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export type FilteredStock = { | ||
symbol: string; | ||
exchange: string; | ||
instrumentName: string; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,12 @@ | ||
{ | ||
"compilerOptions": { | ||
"lib": ["es5", "es6"], | ||
"target": "es5", | ||
"target": "es6", | ||
"module": "commonjs", | ||
"moduleResolution": "node", | ||
"outDir": "./build", | ||
"emitDecoratorMetadata": true, | ||
"experimentalDecorators": true, | ||
"sourceMap": true | ||
} | ||
}, | ||
"exclude": ["node_modules"] | ||
} |
Oops, something went wrong.