Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ This [Trading 212](https://www.trading212.com/) API can be used with TypeScript
## Implemented Endpoints

- [x] [Instruments Metadata](https://t212public-api-docs.redoc.ly/#tag/Instruments-Metadata)
- [ ] Pies
- [x] [Pies](https://t212public-api-docs.redoc.ly/#tag/Pies)
- [x] [Equity Orders](https://t212public-api-docs.redoc.ly/#tag/Equity-Orders)
- [x] [Account Data](https://t212public-api-docs.redoc.ly/#tag/Account-Data)
- [x] [Personal Portfolio](https://t212public-api-docs.redoc.ly/#tag/Personal-Portfolio)
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"demo:history": "cross-env TSIMP_DIAG=error node --import=tsimp/import ./src/demo/history.ts",
"demo:metadata": "cross-env TSIMP_DIAG=error node --import=tsimp/import ./src/demo/metadata.ts",
"demo:order": "cross-env TSIMP_DIAG=error node --import=tsimp/import ./src/demo/order.ts",
"demo:pie": "cross-env TSIMP_DIAG=error node --import=tsimp/import ./src/demo/pie.ts",
"demo:portfolio": "cross-env TSIMP_DIAG=error node --import=tsimp/import ./src/demo/portfolio.ts",
"deploy": "exit 0",
"dev": "cross-env TSIMP_DIAG=error node --inspect --import=tsimp/import ./src/start.ts",
Expand Down
17 changes: 14 additions & 3 deletions src/RESTClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {MetadataAPI} from './api/metadata/MetadataAPI.js';
import {PortfolioAPI} from './api/portfolio/PortfolioAPI.js';
import {HistoryAPI} from './api/history/HistoryAPI.js';
import {OrderAPI} from './api/order/OrderAPI.js';
import {PieAPI} from './api/pie/PieAPI.js';

/**
* This class configures the HTTP Library (axios) so it uses the proper URL and reconnection states. It also exposes all available endpoints.
Expand All @@ -34,6 +35,7 @@ export class RESTClient {
readonly history: HistoryAPI;
readonly metadata: MetadataAPI;
readonly order: OrderAPI;
readonly pie: PieAPI;
readonly portfolio: PortfolioAPI;

private readonly httpClient: AxiosInstance;
Expand Down Expand Up @@ -76,9 +78,16 @@ export class RESTClient {
const url = error.config?.url;
const method = error.config?.method;

// If a particular order is fetched we can use a lower timeout
if (method === 'get' && url?.includes(OrderAPI.URL.ORDERS + '/')) {
return 1_000;
if (method === 'get') {
// If a particular order ID is fetched we can use a lower timeout
if (url?.includes(OrderAPI.URL.ORDERS + '/')) {
return 1_000;
}

// If a particular pie ID is fetched we can use a lower timeout
if (url?.includes(PieAPI.URL.PIES + '/')) {
return 5_000;
}
}

switch (url) {
Expand All @@ -89,6 +98,7 @@ export class RESTClient {
return 5_000;
case AccountAPI.URL.INFO:
case MetadataAPI.URL.EXCHANGES:
case PieAPI.URL.PIES:
return 30_000;
case MetadataAPI.URL.INSTRUMENTS:
return 50_000;
Expand All @@ -111,6 +121,7 @@ export class RESTClient {
this.history = new HistoryAPI(this.httpClient);
this.metadata = new MetadataAPI(this.httpClient);
this.order = new OrderAPI(this.httpClient);
this.pie = new PieAPI(this.httpClient);
this.portfolio = new PortfolioAPI(this.httpClient);
}
}
78 changes: 78 additions & 0 deletions src/api/pie/PieAPI.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import type {AxiosInstance} from 'axios';
import {z} from 'zod';
import {DIVIDEND_CASH_ACTION, PIE_ICON, PIE_STATUS_GOAL} from '../union.js';

const PieSchema = z.object({
cash: z.number(),
dividendDetails: z.object({
gained: z.number(),
inCash: z.number(),
reinvested: z.number(),
}),
id: z.number(),
progress: z.number(),
result: z.object({
investedValue: z.number(),
result: z.number(),
resultCoef: z.number(),
value: z.number(),
}),
status: PIE_STATUS_GOAL,
});

const DetailedPieSchema = z.object({
instruments: z.array(
z.object({
currentShare: z.number(),
expectedShare: z.number(),
issues: z.array(z.object({name: z.string(), severity: z.string()})),
ownedQuantity: z.number(),
result: z.object({
investedValue: z.number(),
result: z.number(),
resultCoef: z.number(),
value: z.number(),
}),
ticker: z.string(),
})
),
settings: z.object({
creationDate: z.string().datetime({offset: true}),
dividendCashAction: DIVIDEND_CASH_ACTION,
endDate: z.string().datetime({offset: true}),
goal: z.number(),
icon: PIE_ICON,
id: z.number(),
instrumentShares: z.union([z.null(), z.record(z.number())]),
initialInvestment: z.number(),
name: z.string(),
publicUrl: z.union([z.string(), z.null()]),
}),
});

export type Pie = z.infer<typeof PieSchema>;

export type DetailedPie = z.infer<typeof DetailedPieSchema>;

/**
* @see https://t212public-api-docs.redoc.ly/#tag/Pies
*/
export class PieAPI {
static readonly URL = {
PIES: '/api/v0/equity/pies',
};

constructor(private readonly apiClient: AxiosInstance) {}

/**
* @see https://t212public-api-docs.redoc.ly/#operation/getAll
* @see https://t212public-api-docs.redoc.ly/#operation/getDetailed
*/
async getPie(): Promise<Pie[]>;
async getPie(id: number): Promise<DetailedPie>;
async getPie(id?: number) {
const resource = id ? `${PieAPI.URL.PIES}/${id}` : PieAPI.URL.PIES;
const response = await this.apiClient.get(resource);
return id ? DetailedPieSchema.parse(response.data) : z.array(PieSchema).parse(response.data);
}
}
44 changes: 44 additions & 0 deletions src/api/union.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export const DEVICE = z.union([
z.literal('WEB'),
]);

export const DIVIDEND_CASH_ACTION = z.union([z.literal('REINVEST'), z.literal('TO_ACCOUNT_CASH')]);

export const DIVIDEND_TYPE = z.union([
z.literal('BONUS_MANUFACTURED_PAYMENT'),
z.literal('BONUS'),
Expand Down Expand Up @@ -108,6 +110,48 @@ export const ORDER_STATUS = z.union([

export const ORDER_STRATEGY = z.union([z.literal('QUANTITY'), z.literal('VALUE')]);

export const PIE_ICON = z.union([
z.literal('Airplane'),
z.literal('Apartments'),
z.literal('Bills'),
z.literal('BillsAndCoins'),
z.literal('Briefcase'),
z.literal('Burger'),
z.literal('Bus'),
z.literal('Cabin'),
z.literal('Car'),
z.literal('Child'),
z.literal('Coins'),
z.literal('Convertable'),
z.literal('Education'),
z.literal('Energy'),
z.literal('Factory'),
z.literal('Family'),
z.literal('Global'),
z.literal('Home'),
z.literal('Iceberg'),
z.literal('Landscape'),
z.literal('Leaf'),
z.literal('Materials'),
z.literal('Medical'),
z.literal('PiggyBank'),
z.literal('Pill'),
z.literal('Ring'),
z.literal('RV'),
z.literal('Shipping'),
z.literal('Storefront'),
z.literal('Tech'),
z.literal('Travel'),
z.literal('Umbrella'),
z.literal('Unicorn'),
z.literal('Vault'),
z.literal('Water'),
z.literal('Whale'),
z.literal('Wind'),
]);

export const PIE_STATUS_GOAL = z.union([z.literal('AHEAD'), z.literal('ON_TRACK'), z.literal('BEHIND')]);

export const TAX_NAME = z.union([
z.literal('COMMISSION_TURNOVER'),
z.literal('CURRENCY_CONVERSION_FEE'),
Expand Down
6 changes: 6 additions & 0 deletions src/demo/pie.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import {initClient} from './initClient.js';

const client = await initClient();

const pies = await client.rest.pie.getPie();
console.info(new Date().toISOString(), pies);
29 changes: 29 additions & 0 deletions src/fixtures/api/v0/equity/pies/pie.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"instruments": [
{
"currentShare": 1,
"expectedShare": 1,
"issues": [],
"ownedQuantity": 8.3444584,
"result": {
"investedValue": 1000,
"result": 24.37,
"resultCoef": 0.0244,
"value": 1024.37
},
"ticker": "VWCEd_EQ"
}
],
"settings": {
"creationDate": "2023-12-01T23:59:59.999+03:00",
"dividendCashAction": "REINVEST",
"endDate": "2024-12-01T23:59:59.999+03:00",
"goal": 13159,
"icon": "Coins",
"id": 1337,
"initialInvestment": 1000,
"instrumentShares": null,
"name": "World ETF",
"publicUrl": null
}
}