@kkkermit/trading212.js 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,159 @@
1
+ import { FetchLike, TradableInstrument, HistoryDividendItem, HistoryTransactionItem, Exchange, Order, Environment, HistoricalOrder, ReportResponse, PieRequest, EndpointId, Position, Trading212Options, Trading212 } from './index.cjs';
2
+
3
+ /** A request seen by `scriptedFetch` or the fake server. The Authorization header is never recorded. */
4
+ interface RecordedRequest {
5
+ readonly method: string;
6
+ readonly url: string;
7
+ readonly path: string;
8
+ readonly query: Readonly<Record<string, string>>;
9
+ readonly headers: Readonly<Record<string, string>>;
10
+ /** True when an Authorization header was sent. Its value is never kept. */
11
+ readonly authorized: boolean;
12
+ /** The body, parsed when it was JSON. */
13
+ readonly body: unknown;
14
+ }
15
+ interface ScriptedResponse {
16
+ /** Default 200. */
17
+ status?: number;
18
+ /** An object or array is sent as JSON; a string as it is; `undefined` as an empty body. */
19
+ body?: unknown;
20
+ headers?: Record<string, string>;
21
+ }
22
+ /** A response, an error to throw as the network failure, or a function that decides from the request. */
23
+ type ScriptStep = ScriptedResponse | Error | ((request: RecordedRequest) => ScriptedResponse | Error | Promise<ScriptedResponse | Error>);
24
+ interface ScriptedFetch {
25
+ readonly fetch: FetchLike;
26
+ /** Every request made so far. */
27
+ readonly requests: readonly RecordedRequest[];
28
+ /** How many scripted steps have not been used. */
29
+ readonly pending: () => number;
30
+ }
31
+ /**
32
+ * A `fetch` that answers from a script, in order, and records what it was asked. Use it to test code at the HTTP
33
+ * level; for whole flows, `createFakeTrading212()` is usually easier.
34
+ *
35
+ * @example
36
+ * const { fetch, requests } = scriptedFetch([{ body: { id: 1, currency: "GBP" } }]);
37
+ * const t212 = new Trading212({ environment: "demo", apiKey: "k", apiSecret: "s", fetch, rateLimit: "off" });
38
+ */
39
+ declare function scriptedFetch(steps: readonly ScriptStep[]): ScriptedFetch;
40
+ /** A network failure shaped like Node's, with the `code` this library reads to tell "never sent" from "lost". */
41
+ declare function networkError(code?: string): TypeError;
42
+
43
+ declare const FAKE_API_KEY = "fake-trading212-key";
44
+ declare const FAKE_API_SECRET = "fake-trading212-secret";
45
+ interface FakeHolding {
46
+ ticker: string;
47
+ quantity: number;
48
+ /** Average price paid per share. Defaults to the current price. */
49
+ averagePrice?: number;
50
+ /** Shares held in pies, which cannot be sold directly. */
51
+ inPies?: number;
52
+ }
53
+ /** The account the fake starts with. Everything is optional. */
54
+ interface FakeAccountState {
55
+ accountId?: number;
56
+ /** Default `GBP`. Prices are treated as already in this currency; the fake does no FX. */
57
+ currency?: string;
58
+ /** Free cash. Default 10,000. */
59
+ cash?: number;
60
+ /** Default: Apple, Microsoft and Tesla on US markets, and Vanguard's S&P 500 ETF in London. */
61
+ instruments?: TradableInstrument[];
62
+ /** Price per ticker. Unlisted tickers default to 100. */
63
+ prices?: Record<string, number>;
64
+ positions?: FakeHolding[];
65
+ dividends?: HistoryDividendItem[];
66
+ transactions?: HistoryTransactionItem[];
67
+ exchanges?: Exchange[];
68
+ }
69
+ /** A failure to inject: an HTTP response, or a network failure with Node's error code. */
70
+ type FakeFailure = ScriptedResponse | {
71
+ network: string;
72
+ };
73
+ declare const DEFAULT_INSTRUMENTS: readonly TradableInstrument[];
74
+ /** A week of weekday sessions from the start of `from`'s day, in UTC. */
75
+ declare function weekOfSessions(from: Date, openUtc: string, closeUtc: string): {
76
+ date: string;
77
+ type: string;
78
+ }[];
79
+ /** A holding as the fake keeps it. */
80
+ interface FakeHoldingState {
81
+ ticker: string;
82
+ quantity: number;
83
+ averagePrice: number;
84
+ inPies: number;
85
+ createdAt: string;
86
+ }
87
+ /** A pending order and what it has reserved. */
88
+ interface FakePendingOrder {
89
+ order: Order;
90
+ reservedCash: number;
91
+ reservedShares: number;
92
+ }
93
+ /**
94
+ * An in-memory Trading 212 that the real client talks to through `fetch`, so your code runs exactly as it would
95
+ * against the API: validation, errors, pagination and all. Market orders fill at once at the current price; limit
96
+ * and stop orders wait until you call `fill()`.
97
+ */
98
+ declare class FakeTrading212Server {
99
+ #private;
100
+ readonly environment: Environment;
101
+ /** Every request the client made, without credentials. */
102
+ readonly requests: RecordedRequest[];
103
+ /** When false, every order is refused with `market-closed`. */
104
+ marketOpen: boolean;
105
+ /** Trading 212 refuses CSV exports on demo; set this to let the fake offer them anyway. */
106
+ exportsOnDemo: boolean;
107
+ cash: number;
108
+ readonly currency: string;
109
+ readonly accountId: number;
110
+ readonly instruments: TradableInstrument[];
111
+ readonly prices: Map<string, number>;
112
+ readonly holdings: Map<string, FakeHoldingState>;
113
+ readonly pending: Map<number, FakePendingOrder>;
114
+ readonly orderHistory: HistoricalOrder[];
115
+ readonly dividends: HistoryDividendItem[];
116
+ readonly transactions: HistoryTransactionItem[];
117
+ readonly exchanges: Exchange[];
118
+ readonly reports: ReportResponse[];
119
+ readonly pies: Map<number, {
120
+ settings: PieRequest;
121
+ cash: number;
122
+ }>;
123
+ realisedProfitLoss: number;
124
+ constructor(state?: FakeAccountState, environment?: Environment);
125
+ /** Pass this as the client's `fetch`. */
126
+ readonly fetch: FetchLike;
127
+ /** Makes the next call to an endpoint fail. Queue several to fail several calls. */
128
+ failNext(endpoint: EndpointId, failure: FakeFailure): this;
129
+ setPrice(ticker: string, price: number): this;
130
+ priceOf(ticker: string): number;
131
+ /** Fills a pending limit or stop order, at its limit price, or `price` if given. */
132
+ fill(orderId: number, price?: number): Order;
133
+ position(ticker: string): Position | undefined;
134
+ }
135
+
136
+ interface CreateFakeOptions {
137
+ /** The account the fake starts with. */
138
+ state?: FakeAccountState;
139
+ /** Default `demo`. On `live`, trading is enabled for you unless `client.trading` says otherwise. */
140
+ environment?: Environment;
141
+ /** Any other client option, such as a logger. */
142
+ client?: Partial<Omit<Trading212Options, "environment" | "apiKey" | "apiSecret" | "fetch">>;
143
+ }
144
+ /**
145
+ * A real `Trading212` client wired to an in-memory Trading 212, for testing your own code with no network and no
146
+ * key. Rate limiting is off so tests run instantly.
147
+ *
148
+ * @example
149
+ * const { client, server } = createFakeTrading212({ state: { cash: 1000 } });
150
+ * await client.orders.buy({ ticker: "AAPL_US_EQ", quantity: 2 });
151
+ * expect(server.position("AAPL_US_EQ")?.quantity).toBe(2);
152
+ */
153
+ declare function createFakeTrading212(options?: CreateFakeOptions): {
154
+ client: Trading212;
155
+ server: FakeTrading212Server;
156
+ };
157
+
158
+ export { DEFAULT_INSTRUMENTS, FAKE_API_KEY, FAKE_API_SECRET, FakeTrading212Server, createFakeTrading212, networkError, scriptedFetch, weekOfSessions };
159
+ export type { CreateFakeOptions, FakeAccountState, FakeFailure, FakeHolding, FakeHoldingState, FakePendingOrder, RecordedRequest, ScriptStep, ScriptedFetch, ScriptedResponse };
@@ -0,0 +1,159 @@
1
+ import { FetchLike, TradableInstrument, HistoryDividendItem, HistoryTransactionItem, Exchange, Order, Environment, HistoricalOrder, ReportResponse, PieRequest, EndpointId, Position, Trading212Options, Trading212 } from './index.js';
2
+
3
+ /** A request seen by `scriptedFetch` or the fake server. The Authorization header is never recorded. */
4
+ interface RecordedRequest {
5
+ readonly method: string;
6
+ readonly url: string;
7
+ readonly path: string;
8
+ readonly query: Readonly<Record<string, string>>;
9
+ readonly headers: Readonly<Record<string, string>>;
10
+ /** True when an Authorization header was sent. Its value is never kept. */
11
+ readonly authorized: boolean;
12
+ /** The body, parsed when it was JSON. */
13
+ readonly body: unknown;
14
+ }
15
+ interface ScriptedResponse {
16
+ /** Default 200. */
17
+ status?: number;
18
+ /** An object or array is sent as JSON; a string as it is; `undefined` as an empty body. */
19
+ body?: unknown;
20
+ headers?: Record<string, string>;
21
+ }
22
+ /** A response, an error to throw as the network failure, or a function that decides from the request. */
23
+ type ScriptStep = ScriptedResponse | Error | ((request: RecordedRequest) => ScriptedResponse | Error | Promise<ScriptedResponse | Error>);
24
+ interface ScriptedFetch {
25
+ readonly fetch: FetchLike;
26
+ /** Every request made so far. */
27
+ readonly requests: readonly RecordedRequest[];
28
+ /** How many scripted steps have not been used. */
29
+ readonly pending: () => number;
30
+ }
31
+ /**
32
+ * A `fetch` that answers from a script, in order, and records what it was asked. Use it to test code at the HTTP
33
+ * level; for whole flows, `createFakeTrading212()` is usually easier.
34
+ *
35
+ * @example
36
+ * const { fetch, requests } = scriptedFetch([{ body: { id: 1, currency: "GBP" } }]);
37
+ * const t212 = new Trading212({ environment: "demo", apiKey: "k", apiSecret: "s", fetch, rateLimit: "off" });
38
+ */
39
+ declare function scriptedFetch(steps: readonly ScriptStep[]): ScriptedFetch;
40
+ /** A network failure shaped like Node's, with the `code` this library reads to tell "never sent" from "lost". */
41
+ declare function networkError(code?: string): TypeError;
42
+
43
+ declare const FAKE_API_KEY = "fake-trading212-key";
44
+ declare const FAKE_API_SECRET = "fake-trading212-secret";
45
+ interface FakeHolding {
46
+ ticker: string;
47
+ quantity: number;
48
+ /** Average price paid per share. Defaults to the current price. */
49
+ averagePrice?: number;
50
+ /** Shares held in pies, which cannot be sold directly. */
51
+ inPies?: number;
52
+ }
53
+ /** The account the fake starts with. Everything is optional. */
54
+ interface FakeAccountState {
55
+ accountId?: number;
56
+ /** Default `GBP`. Prices are treated as already in this currency; the fake does no FX. */
57
+ currency?: string;
58
+ /** Free cash. Default 10,000. */
59
+ cash?: number;
60
+ /** Default: Apple, Microsoft and Tesla on US markets, and Vanguard's S&P 500 ETF in London. */
61
+ instruments?: TradableInstrument[];
62
+ /** Price per ticker. Unlisted tickers default to 100. */
63
+ prices?: Record<string, number>;
64
+ positions?: FakeHolding[];
65
+ dividends?: HistoryDividendItem[];
66
+ transactions?: HistoryTransactionItem[];
67
+ exchanges?: Exchange[];
68
+ }
69
+ /** A failure to inject: an HTTP response, or a network failure with Node's error code. */
70
+ type FakeFailure = ScriptedResponse | {
71
+ network: string;
72
+ };
73
+ declare const DEFAULT_INSTRUMENTS: readonly TradableInstrument[];
74
+ /** A week of weekday sessions from the start of `from`'s day, in UTC. */
75
+ declare function weekOfSessions(from: Date, openUtc: string, closeUtc: string): {
76
+ date: string;
77
+ type: string;
78
+ }[];
79
+ /** A holding as the fake keeps it. */
80
+ interface FakeHoldingState {
81
+ ticker: string;
82
+ quantity: number;
83
+ averagePrice: number;
84
+ inPies: number;
85
+ createdAt: string;
86
+ }
87
+ /** A pending order and what it has reserved. */
88
+ interface FakePendingOrder {
89
+ order: Order;
90
+ reservedCash: number;
91
+ reservedShares: number;
92
+ }
93
+ /**
94
+ * An in-memory Trading 212 that the real client talks to through `fetch`, so your code runs exactly as it would
95
+ * against the API: validation, errors, pagination and all. Market orders fill at once at the current price; limit
96
+ * and stop orders wait until you call `fill()`.
97
+ */
98
+ declare class FakeTrading212Server {
99
+ #private;
100
+ readonly environment: Environment;
101
+ /** Every request the client made, without credentials. */
102
+ readonly requests: RecordedRequest[];
103
+ /** When false, every order is refused with `market-closed`. */
104
+ marketOpen: boolean;
105
+ /** Trading 212 refuses CSV exports on demo; set this to let the fake offer them anyway. */
106
+ exportsOnDemo: boolean;
107
+ cash: number;
108
+ readonly currency: string;
109
+ readonly accountId: number;
110
+ readonly instruments: TradableInstrument[];
111
+ readonly prices: Map<string, number>;
112
+ readonly holdings: Map<string, FakeHoldingState>;
113
+ readonly pending: Map<number, FakePendingOrder>;
114
+ readonly orderHistory: HistoricalOrder[];
115
+ readonly dividends: HistoryDividendItem[];
116
+ readonly transactions: HistoryTransactionItem[];
117
+ readonly exchanges: Exchange[];
118
+ readonly reports: ReportResponse[];
119
+ readonly pies: Map<number, {
120
+ settings: PieRequest;
121
+ cash: number;
122
+ }>;
123
+ realisedProfitLoss: number;
124
+ constructor(state?: FakeAccountState, environment?: Environment);
125
+ /** Pass this as the client's `fetch`. */
126
+ readonly fetch: FetchLike;
127
+ /** Makes the next call to an endpoint fail. Queue several to fail several calls. */
128
+ failNext(endpoint: EndpointId, failure: FakeFailure): this;
129
+ setPrice(ticker: string, price: number): this;
130
+ priceOf(ticker: string): number;
131
+ /** Fills a pending limit or stop order, at its limit price, or `price` if given. */
132
+ fill(orderId: number, price?: number): Order;
133
+ position(ticker: string): Position | undefined;
134
+ }
135
+
136
+ interface CreateFakeOptions {
137
+ /** The account the fake starts with. */
138
+ state?: FakeAccountState;
139
+ /** Default `demo`. On `live`, trading is enabled for you unless `client.trading` says otherwise. */
140
+ environment?: Environment;
141
+ /** Any other client option, such as a logger. */
142
+ client?: Partial<Omit<Trading212Options, "environment" | "apiKey" | "apiSecret" | "fetch">>;
143
+ }
144
+ /**
145
+ * A real `Trading212` client wired to an in-memory Trading 212, for testing your own code with no network and no
146
+ * key. Rate limiting is off so tests run instantly.
147
+ *
148
+ * @example
149
+ * const { client, server } = createFakeTrading212({ state: { cash: 1000 } });
150
+ * await client.orders.buy({ ticker: "AAPL_US_EQ", quantity: 2 });
151
+ * expect(server.position("AAPL_US_EQ")?.quantity).toBe(2);
152
+ */
153
+ declare function createFakeTrading212(options?: CreateFakeOptions): {
154
+ client: Trading212;
155
+ server: FakeTrading212Server;
156
+ };
157
+
158
+ export { DEFAULT_INSTRUMENTS, FAKE_API_KEY, FAKE_API_SECRET, FakeTrading212Server, createFakeTrading212, networkError, scriptedFetch, weekOfSessions };
159
+ export type { CreateFakeOptions, FakeAccountState, FakeFailure, FakeHolding, FakeHoldingState, FakePendingOrder, RecordedRequest, ScriptStep, ScriptedFetch, ScriptedResponse };