@n1xyz/nord-ts 0.1.1 → 0.1.3
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.
- package/dist/gen/nord_pb.d.ts +196 -105
- package/dist/gen/nord_pb.js +117 -89
- package/dist/gen/openapi.d.ts +137 -7
- package/dist/nord/api/actions.d.ts +22 -1
- package/dist/nord/api/actions.js +61 -2
- package/dist/nord/api/triggers.d.ts +7 -0
- package/dist/nord/api/triggers.js +38 -0
- package/dist/nord/client/Nord.d.ts +10 -1
- package/dist/nord/client/Nord.js +21 -0
- package/dist/nord/client/NordUser.d.ts +54 -1
- package/dist/nord/client/NordUser.js +97 -0
- package/dist/types.d.ts +16 -3
- package/dist/types.js +30 -4
- package/package.json +2 -2
- package/src/gen/nord_pb.ts +268 -173
- package/src/gen/openapi.ts +137 -7
- package/src/nord/api/actions.ts +100 -2
- package/src/nord/api/triggers.ts +57 -0
- package/src/nord/client/Nord.ts +27 -0
- package/src/nord/client/NordUser.ts +160 -1
- package/src/types.ts +34 -3
package/src/gen/openapi.ts
CHANGED
|
@@ -84,8 +84,12 @@ export interface paths {
|
|
|
84
84
|
get: {
|
|
85
85
|
parameters: {
|
|
86
86
|
query: {
|
|
87
|
+
/** @description Start action ID (exclusive) */
|
|
87
88
|
from: number;
|
|
89
|
+
/** @description End action ID (inclusive) */
|
|
88
90
|
to: number;
|
|
91
|
+
/** @description Optionally provided binary version of client, so nord server can ensure all actions provided are executable by client. */
|
|
92
|
+
clientVersion?: components["schemas"]["ExecutableVersion"] | null;
|
|
89
93
|
};
|
|
90
94
|
header?: never;
|
|
91
95
|
path?: never;
|
|
@@ -109,6 +113,14 @@ export interface paths {
|
|
|
109
113
|
"application/json": components["schemas"]["RangeTooLarge"];
|
|
110
114
|
};
|
|
111
115
|
};
|
|
116
|
+
501: {
|
|
117
|
+
headers: {
|
|
118
|
+
[name: string]: unknown;
|
|
119
|
+
};
|
|
120
|
+
content: {
|
|
121
|
+
"application/json": components["schemas"]["NotImplemented"];
|
|
122
|
+
};
|
|
123
|
+
};
|
|
112
124
|
};
|
|
113
125
|
};
|
|
114
126
|
put?: never;
|
|
@@ -787,7 +799,7 @@ export interface paths {
|
|
|
787
799
|
[name: string]: unknown;
|
|
788
800
|
};
|
|
789
801
|
content: {
|
|
790
|
-
"application/json": components["schemas"]["
|
|
802
|
+
"application/json": components["schemas"]["AccountTriggerInfo"][] | null;
|
|
791
803
|
};
|
|
792
804
|
};
|
|
793
805
|
};
|
|
@@ -984,6 +996,79 @@ export interface paths {
|
|
|
984
996
|
patch?: never;
|
|
985
997
|
trace?: never;
|
|
986
998
|
};
|
|
999
|
+
"/state/download": {
|
|
1000
|
+
parameters: {
|
|
1001
|
+
query?: never;
|
|
1002
|
+
header?: never;
|
|
1003
|
+
path?: never;
|
|
1004
|
+
cookie?: never;
|
|
1005
|
+
};
|
|
1006
|
+
/** @description Returns state download link and metadata, as defined by nearest(up to equality) snapshot `time` or `action_id`` or `timestamp`. If not specified, returns latest snapshot. Example: /state/download?actionId=1 /state/download?time&2025-09-22T12:34:56Z /state/download?timestamp&1695380000 - Does not implemented yet. To get the latest snap use: /state/download - w/o any params.
|
|
1007
|
+
*
|
|
1008
|
+
* The link from responce can be inserted to search bar in browser to download. */
|
|
1009
|
+
get: {
|
|
1010
|
+
parameters: {
|
|
1011
|
+
query?: never;
|
|
1012
|
+
header?: never;
|
|
1013
|
+
path?: never;
|
|
1014
|
+
cookie?: never;
|
|
1015
|
+
};
|
|
1016
|
+
requestBody?: never;
|
|
1017
|
+
responses: {
|
|
1018
|
+
200: {
|
|
1019
|
+
headers: {
|
|
1020
|
+
[name: string]: unknown;
|
|
1021
|
+
};
|
|
1022
|
+
content: {
|
|
1023
|
+
"application/json": components["schemas"]["StateDownloadMeta"];
|
|
1024
|
+
};
|
|
1025
|
+
};
|
|
1026
|
+
501: {
|
|
1027
|
+
headers: {
|
|
1028
|
+
[name: string]: unknown;
|
|
1029
|
+
};
|
|
1030
|
+
content: {
|
|
1031
|
+
"application/json": components["schemas"]["NotImplemented"];
|
|
1032
|
+
};
|
|
1033
|
+
};
|
|
1034
|
+
};
|
|
1035
|
+
};
|
|
1036
|
+
put?: never;
|
|
1037
|
+
post?: never;
|
|
1038
|
+
delete?: never;
|
|
1039
|
+
options?: never;
|
|
1040
|
+
head?: never;
|
|
1041
|
+
patch?: never;
|
|
1042
|
+
trace?: never;
|
|
1043
|
+
};
|
|
1044
|
+
"/state/download/{snapshot_id}": {
|
|
1045
|
+
parameters: {
|
|
1046
|
+
query?: never;
|
|
1047
|
+
header?: never;
|
|
1048
|
+
path?: never;
|
|
1049
|
+
cookie?: never;
|
|
1050
|
+
};
|
|
1051
|
+
/** @description Typical HTTP file download endpoint. Actually same as provided by S3 storages. Means supports Range header, Content-Length, Content-Type, ETag and so on, and relevant headers. */
|
|
1052
|
+
get: {
|
|
1053
|
+
parameters: {
|
|
1054
|
+
query?: never;
|
|
1055
|
+
header?: never;
|
|
1056
|
+
path: {
|
|
1057
|
+
snapshot_id: string;
|
|
1058
|
+
};
|
|
1059
|
+
cookie?: never;
|
|
1060
|
+
};
|
|
1061
|
+
requestBody?: never;
|
|
1062
|
+
responses: never;
|
|
1063
|
+
};
|
|
1064
|
+
put?: never;
|
|
1065
|
+
post?: never;
|
|
1066
|
+
delete?: never;
|
|
1067
|
+
options?: never;
|
|
1068
|
+
head?: never;
|
|
1069
|
+
patch?: never;
|
|
1070
|
+
trace?: never;
|
|
1071
|
+
};
|
|
987
1072
|
"/tv": {
|
|
988
1073
|
parameters: {
|
|
989
1074
|
query?: never;
|
|
@@ -1924,11 +2009,20 @@ export interface components {
|
|
|
1924
2009
|
limit: number;
|
|
1925
2010
|
};
|
|
1926
2011
|
ActionsQuery: {
|
|
1927
|
-
/**
|
|
2012
|
+
/**
|
|
2013
|
+
* Format: uint64
|
|
2014
|
+
* @description Start action ID (exclusive)
|
|
2015
|
+
*/
|
|
1928
2016
|
from: number;
|
|
1929
|
-
/**
|
|
2017
|
+
/**
|
|
2018
|
+
* Format: uint64
|
|
2019
|
+
* @description End action ID (inclusive)
|
|
2020
|
+
*/
|
|
1930
2021
|
to: number;
|
|
2022
|
+
/** @description Optionally provided binary version of client, so nord server can ensure all actions provided are executable by client. */
|
|
2023
|
+
clientVersion?: components["schemas"]["ExecutableVersion"] | null;
|
|
1931
2024
|
};
|
|
2025
|
+
ExecutableVersion: string;
|
|
1932
2026
|
ActionsItem: {
|
|
1933
2027
|
/** Format: uint64 */
|
|
1934
2028
|
actionId: number;
|
|
@@ -1941,6 +2035,10 @@ export interface components {
|
|
|
1941
2035
|
/** Format: uint16 */
|
|
1942
2036
|
maximal: number;
|
|
1943
2037
|
};
|
|
2038
|
+
NotImplemented: {
|
|
2039
|
+
/** @description Human readable message describing what to expect next. */
|
|
2040
|
+
message: string;
|
|
2041
|
+
};
|
|
1944
2042
|
ActionNotFound: null;
|
|
1945
2043
|
/** @description Returns fee parts per market per balance change per action per account. Fee is taken from user without return. Please note that some operations need some deposit which will be returned - these are not part of fees. */
|
|
1946
2044
|
FillRole: "maker" | "taker";
|
|
@@ -2271,14 +2369,23 @@ export interface components {
|
|
|
2271
2369
|
/** @enum {string} */
|
|
2272
2370
|
TriggerKind: "stopLoss" | "takeProfit";
|
|
2273
2371
|
/** @enum {string} */
|
|
2274
|
-
TriggerStatus: "
|
|
2275
|
-
|
|
2372
|
+
TriggerStatus: "Active" | "Success" | "Removed" | "Canceled";
|
|
2373
|
+
/** @description Trigger into per account. */
|
|
2374
|
+
AccountTriggerInfo: {
|
|
2276
2375
|
/** Format: uint32 */
|
|
2277
2376
|
marketId: number;
|
|
2377
|
+
key: components["schemas"]["TriggerKey"];
|
|
2378
|
+
triggerPrices: components["schemas"]["TriggerPrice"];
|
|
2379
|
+
/** Format: uint64 */
|
|
2380
|
+
actionId: number;
|
|
2381
|
+
};
|
|
2382
|
+
TriggerKey: {
|
|
2278
2383
|
side: components["schemas"]["Side"];
|
|
2279
2384
|
kind: components["schemas"]["TriggerKind"];
|
|
2280
|
-
|
|
2281
|
-
|
|
2385
|
+
};
|
|
2386
|
+
TriggerPrice: {
|
|
2387
|
+
trigger: components["schemas"]["PositivePriceMantissa"];
|
|
2388
|
+
settlement?: components["schemas"]["PositivePriceMantissa"] | null;
|
|
2282
2389
|
};
|
|
2283
2390
|
/**
|
|
2284
2391
|
* Format: uint64
|
|
@@ -2346,6 +2453,29 @@ export interface components {
|
|
|
2346
2453
|
*/
|
|
2347
2454
|
pageSize: number | null;
|
|
2348
2455
|
};
|
|
2456
|
+
DownloadFilter: components["schemas"]["Op_for_DataDateTime"] | components["schemas"]["Op_for_uint64"] | components["schemas"]["Op_for_uint64"] | null;
|
|
2457
|
+
/** @description Parses tag (anycase), and value in round braces using `from_str`. */
|
|
2458
|
+
Op_for_DataDateTime: {
|
|
2459
|
+
le: components["schemas"]["DataDateTime"];
|
|
2460
|
+
};
|
|
2461
|
+
DataDateTime: string;
|
|
2462
|
+
/** @description Parses tag (anycase), and value in round braces using `from_str`. */
|
|
2463
|
+
Op_for_uint64: {
|
|
2464
|
+
/** Format: uint64 */
|
|
2465
|
+
le: number;
|
|
2466
|
+
};
|
|
2467
|
+
StateDownloadMeta: {
|
|
2468
|
+
/** @description Hash of the state file. Used as ETag. */
|
|
2469
|
+
hash: number[];
|
|
2470
|
+
/**
|
|
2471
|
+
* Format: uri
|
|
2472
|
+
* @description Link to download the state file.
|
|
2473
|
+
*/
|
|
2474
|
+
link: string;
|
|
2475
|
+
/** @description Version of binary which produced this state snapshot. So can decide whether it is compatible with client. todo(repl): After upgrade release make it non-optional. */
|
|
2476
|
+
version?: components["schemas"]["BinaryId"] | null;
|
|
2477
|
+
};
|
|
2478
|
+
BinaryId: string;
|
|
2349
2479
|
/** @description TV config query response https://www.tradingview.com/charting-library-docs/latest/connecting_data/UDF/#data-feed-configuration-data */
|
|
2350
2480
|
TvConfigResponse: {
|
|
2351
2481
|
supported_resolutions: components["schemas"]["Resolution"][];
|
package/src/nord/api/actions.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
KeyType,
|
|
11
11
|
Side,
|
|
12
12
|
QuoteSize,
|
|
13
|
+
TriggerKind,
|
|
13
14
|
} from "../../types";
|
|
14
15
|
import {
|
|
15
16
|
assert,
|
|
@@ -265,7 +266,7 @@ export async function placeOrder(
|
|
|
265
266
|
const size = toScaledU64(params.size ?? 0, params.sizeDecimals);
|
|
266
267
|
|
|
267
268
|
const scaledQuote = params.quoteSize
|
|
268
|
-
? params.quoteSize.
|
|
269
|
+
? params.quoteSize.toWire(params.priceDecimals, params.sizeDecimals)
|
|
269
270
|
: undefined;
|
|
270
271
|
|
|
271
272
|
assert(
|
|
@@ -411,6 +412,103 @@ export async function transfer(
|
|
|
411
412
|
}
|
|
412
413
|
}
|
|
413
414
|
|
|
415
|
+
export async function addTrigger(
|
|
416
|
+
serverUrl: string,
|
|
417
|
+
signFn: (message: Uint8Array) => Promise<Uint8Array>,
|
|
418
|
+
currentTimestamp: bigint,
|
|
419
|
+
nonce: number,
|
|
420
|
+
params: {
|
|
421
|
+
sessionId: BigIntValue;
|
|
422
|
+
marketId: number;
|
|
423
|
+
side: Side;
|
|
424
|
+
kind: TriggerKind;
|
|
425
|
+
priceDecimals: number;
|
|
426
|
+
triggerPrice: Decimal.Value;
|
|
427
|
+
limitPrice?: Decimal.Value;
|
|
428
|
+
accountId?: number;
|
|
429
|
+
},
|
|
430
|
+
): Promise<{ actionId: bigint }> {
|
|
431
|
+
const triggerPrice = toScaledU64(params.triggerPrice, params.priceDecimals);
|
|
432
|
+
assert(triggerPrice > 0n, "Trigger price must be positive");
|
|
433
|
+
const limitPrice =
|
|
434
|
+
params.limitPrice === undefined
|
|
435
|
+
? undefined
|
|
436
|
+
: toScaledU64(params.limitPrice, params.priceDecimals);
|
|
437
|
+
if (limitPrice !== undefined) {
|
|
438
|
+
assert(limitPrice > 0n, "Limit price must be positive");
|
|
439
|
+
}
|
|
440
|
+
const key = create(proto.TriggerKeySchema, {
|
|
441
|
+
kind:
|
|
442
|
+
params.kind === TriggerKind.StopLoss
|
|
443
|
+
? proto.TriggerKind.STOP_LOSS
|
|
444
|
+
: proto.TriggerKind.TAKE_PROFIT,
|
|
445
|
+
side: params.side === Side.Bid ? proto.Side.BID : proto.Side.ASK,
|
|
446
|
+
});
|
|
447
|
+
const prices = create(proto.Action_TriggerPricesSchema, {
|
|
448
|
+
triggerPrice,
|
|
449
|
+
limitPrice,
|
|
450
|
+
});
|
|
451
|
+
const action = createAction(currentTimestamp, nonce, {
|
|
452
|
+
case: "addTrigger",
|
|
453
|
+
value: create(proto.Action_AddTriggerSchema, {
|
|
454
|
+
sessionId: BigInt(params.sessionId),
|
|
455
|
+
marketId: params.marketId,
|
|
456
|
+
key,
|
|
457
|
+
prices,
|
|
458
|
+
accountId: params.accountId,
|
|
459
|
+
}),
|
|
460
|
+
});
|
|
461
|
+
const resp = await sendAction(
|
|
462
|
+
serverUrl,
|
|
463
|
+
(m) => sessionSign(signFn, m),
|
|
464
|
+
action,
|
|
465
|
+
);
|
|
466
|
+
if (resp.kind?.case === "triggerAdded") {
|
|
467
|
+
return { actionId: resp.actionId };
|
|
468
|
+
}
|
|
469
|
+
throw new Error(`Unexpected receipt kind ${resp.kind?.case}`);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
export async function removeTrigger(
|
|
473
|
+
serverUrl: string,
|
|
474
|
+
signFn: (message: Uint8Array) => Promise<Uint8Array>,
|
|
475
|
+
currentTimestamp: bigint,
|
|
476
|
+
nonce: number,
|
|
477
|
+
params: {
|
|
478
|
+
sessionId: BigIntValue;
|
|
479
|
+
marketId: number;
|
|
480
|
+
side: Side;
|
|
481
|
+
kind: TriggerKind;
|
|
482
|
+
accountId?: number;
|
|
483
|
+
},
|
|
484
|
+
): Promise<{ actionId: bigint }> {
|
|
485
|
+
const key = create(proto.TriggerKeySchema, {
|
|
486
|
+
kind:
|
|
487
|
+
params.kind === TriggerKind.StopLoss
|
|
488
|
+
? proto.TriggerKind.STOP_LOSS
|
|
489
|
+
: proto.TriggerKind.TAKE_PROFIT,
|
|
490
|
+
side: params.side === Side.Bid ? proto.Side.BID : proto.Side.ASK,
|
|
491
|
+
});
|
|
492
|
+
const action = createAction(currentTimestamp, nonce, {
|
|
493
|
+
case: "removeTrigger",
|
|
494
|
+
value: create(proto.Action_RemoveTriggerSchema, {
|
|
495
|
+
sessionId: BigInt(params.sessionId),
|
|
496
|
+
marketId: params.marketId,
|
|
497
|
+
key,
|
|
498
|
+
accountId: params.accountId,
|
|
499
|
+
}),
|
|
500
|
+
});
|
|
501
|
+
const resp = await sendAction(
|
|
502
|
+
serverUrl,
|
|
503
|
+
(m) => sessionSign(signFn, m),
|
|
504
|
+
action,
|
|
505
|
+
);
|
|
506
|
+
if (resp.kind?.case === "triggerRemoved") {
|
|
507
|
+
return { actionId: resp.actionId };
|
|
508
|
+
}
|
|
509
|
+
throw new Error(`Unexpected receipt kind ${resp.kind?.case}`);
|
|
510
|
+
}
|
|
511
|
+
|
|
414
512
|
export type AtomicSubaction =
|
|
415
513
|
| {
|
|
416
514
|
kind: "place";
|
|
@@ -457,7 +555,7 @@ export async function atomic(
|
|
|
457
555
|
const price = toScaledU64(a.price ?? 0, a.priceDecimals);
|
|
458
556
|
const size = toScaledU64(a.size ?? 0, a.sizeDecimals);
|
|
459
557
|
const scaledQuote = a.quoteSize
|
|
460
|
-
? a.quoteSize.
|
|
558
|
+
? a.quoteSize.toWire(a.priceDecimals, a.sizeDecimals)
|
|
461
559
|
: undefined;
|
|
462
560
|
|
|
463
561
|
// Require at least one limit to be set (non-zero size, non-zero price, or quoteSize)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import createClient from "openapi-fetch";
|
|
2
|
+
|
|
3
|
+
import { paths, components } from "../../gen/openapi";
|
|
4
|
+
|
|
5
|
+
type AccountTriggerInfo = components["schemas"]["AccountTriggerInfo"];
|
|
6
|
+
type TriggerHistoryPage =
|
|
7
|
+
components["schemas"]["PageResult_for_uint64_and_HistoryTriggerInfo"];
|
|
8
|
+
type HistoryTriggerQuery = components["schemas"]["AccountTriggersQuery"];
|
|
9
|
+
|
|
10
|
+
export type { AccountTriggerInfo, TriggerHistoryPage, HistoryTriggerQuery };
|
|
11
|
+
|
|
12
|
+
export async function getAccountTriggers(
|
|
13
|
+
serverUrl: string,
|
|
14
|
+
accountId: number,
|
|
15
|
+
): Promise<AccountTriggerInfo[]> {
|
|
16
|
+
const client = createClient<paths>({ baseUrl: serverUrl });
|
|
17
|
+
const response = await client.GET("/account/{account_id}/triggers", {
|
|
18
|
+
params: {
|
|
19
|
+
path: { account_id: accountId },
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
if (response.data === undefined) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
`Failed to fetch triggers for account ${accountId}: HTTP ${response.response.status}`,
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return response.data ?? [];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function getAccountTriggerHistory(
|
|
33
|
+
serverUrl: string,
|
|
34
|
+
accountId: number,
|
|
35
|
+
options: HistoryTriggerQuery,
|
|
36
|
+
): Promise<TriggerHistoryPage> {
|
|
37
|
+
const client = createClient<paths>({ baseUrl: serverUrl });
|
|
38
|
+
const response = await client.GET("/account/{account_id}/triggers/history", {
|
|
39
|
+
params: {
|
|
40
|
+
path: { account_id: accountId },
|
|
41
|
+
query: {
|
|
42
|
+
since: options.since,
|
|
43
|
+
until: options.until,
|
|
44
|
+
pageSize: options.pageSize,
|
|
45
|
+
startInclusive: options.startInclusive,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (!response.data) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`Failed to fetch trigger history for account ${accountId}: HTTP ${response.response.status}`,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return response.data;
|
|
57
|
+
}
|
package/src/nord/client/Nord.ts
CHANGED
|
@@ -6,6 +6,8 @@ import * as proto from "../../gen/nord_pb";
|
|
|
6
6
|
import type { paths } from "../../gen/openapi.ts";
|
|
7
7
|
import {
|
|
8
8
|
Account,
|
|
9
|
+
AccountPnlPage,
|
|
10
|
+
AccountPnlQuery,
|
|
9
11
|
ActionResponse,
|
|
10
12
|
AggregateMetrics,
|
|
11
13
|
Info,
|
|
@@ -636,6 +638,31 @@ export class Nord {
|
|
|
636
638
|
});
|
|
637
639
|
}
|
|
638
640
|
|
|
641
|
+
/**
|
|
642
|
+
* Get profit and loss history for an account
|
|
643
|
+
*
|
|
644
|
+
* @param accountId - Account ID to query
|
|
645
|
+
* @param query - Optional time and pagination filters
|
|
646
|
+
* @returns Page of PnL entries ordered from latest to oldest
|
|
647
|
+
* @throws {NordError} If the request fails
|
|
648
|
+
*/
|
|
649
|
+
public async getAccountPnl(
|
|
650
|
+
accountId: number,
|
|
651
|
+
query?: Partial<AccountPnlQuery>,
|
|
652
|
+
): Promise<AccountPnlPage> {
|
|
653
|
+
return await this.GET("/account/{account_id}/pnl", {
|
|
654
|
+
params: {
|
|
655
|
+
path: { account_id: accountId },
|
|
656
|
+
query: {
|
|
657
|
+
since: query?.since,
|
|
658
|
+
until: query?.until,
|
|
659
|
+
startInclusive: query?.startInclusive,
|
|
660
|
+
pageSize: query?.pageSize,
|
|
661
|
+
},
|
|
662
|
+
},
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
|
|
639
666
|
/**
|
|
640
667
|
* Get market statistics (alias for marketsStats for backward compatibility)
|
|
641
668
|
*
|
|
@@ -15,7 +15,13 @@ import * as ed from "@noble/ed25519";
|
|
|
15
15
|
import { sha512 } from "@noble/hashes/sha512";
|
|
16
16
|
ed.etc.sha512Sync = sha512;
|
|
17
17
|
import { floatToScaledBigIntLossy } from "@n1xyz/proton";
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
FillMode,
|
|
20
|
+
Side,
|
|
21
|
+
SPLTokenInfo,
|
|
22
|
+
QuoteSize,
|
|
23
|
+
TriggerKind,
|
|
24
|
+
} from "../../types";
|
|
19
25
|
import * as proto from "../../gen/nord_pb";
|
|
20
26
|
import {
|
|
21
27
|
BigIntValue,
|
|
@@ -34,7 +40,18 @@ import {
|
|
|
34
40
|
withdraw,
|
|
35
41
|
atomic as atomicAction,
|
|
36
42
|
AtomicSubaction as ApiAtomicSubaction,
|
|
43
|
+
addTrigger as addTriggerAction,
|
|
44
|
+
removeTrigger as removeTriggerAction,
|
|
37
45
|
} from "../api/actions";
|
|
46
|
+
import type {
|
|
47
|
+
AccountTriggerInfo,
|
|
48
|
+
TriggerHistoryPage,
|
|
49
|
+
HistoryTriggerQuery,
|
|
50
|
+
} from "../api/triggers";
|
|
51
|
+
import {
|
|
52
|
+
getAccountTriggers as fetchAccountTriggers,
|
|
53
|
+
getAccountTriggerHistory as fetchAccountTriggerHistory,
|
|
54
|
+
} from "../api/triggers";
|
|
38
55
|
import { NordError } from "../utils/NordError";
|
|
39
56
|
import { Nord } from "./Nord";
|
|
40
57
|
|
|
@@ -99,6 +116,22 @@ export interface PlaceOrderParams {
|
|
|
99
116
|
accountId?: number;
|
|
100
117
|
}
|
|
101
118
|
|
|
119
|
+
export interface AddTriggerParams {
|
|
120
|
+
marketId: number;
|
|
121
|
+
side: Side;
|
|
122
|
+
kind: TriggerKind;
|
|
123
|
+
triggerPrice: Decimal.Value;
|
|
124
|
+
limitPrice?: Decimal.Value;
|
|
125
|
+
accountId?: number;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface RemoveTriggerParams {
|
|
129
|
+
marketId: number;
|
|
130
|
+
side: Side;
|
|
131
|
+
kind: TriggerKind;
|
|
132
|
+
accountId?: number;
|
|
133
|
+
}
|
|
134
|
+
|
|
102
135
|
/**
|
|
103
136
|
* Parameters for transferring tokens between accounts
|
|
104
137
|
*/
|
|
@@ -834,6 +867,132 @@ export class NordUser {
|
|
|
834
867
|
}
|
|
835
868
|
}
|
|
836
869
|
|
|
870
|
+
/**
|
|
871
|
+
* Add a trigger for the current session
|
|
872
|
+
*
|
|
873
|
+
* @param params - Trigger parameters including market, side, and prices
|
|
874
|
+
* @returns Object containing the actionId of the submitted trigger
|
|
875
|
+
* @throws {NordError} If the operation fails
|
|
876
|
+
*/
|
|
877
|
+
async addTrigger(params: AddTriggerParams): Promise<{ actionId: bigint }> {
|
|
878
|
+
try {
|
|
879
|
+
this.checkSessionValidity();
|
|
880
|
+
const market = findMarket(this.nord.markets, params.marketId);
|
|
881
|
+
if (!market) {
|
|
882
|
+
throw new NordError(`Market with ID ${params.marketId} not found`);
|
|
883
|
+
}
|
|
884
|
+
const result = await addTriggerAction(
|
|
885
|
+
this.nord.webServerUrl,
|
|
886
|
+
this.sessionSignFn,
|
|
887
|
+
await this.nord.getTimestamp(),
|
|
888
|
+
this.getNonce(),
|
|
889
|
+
{
|
|
890
|
+
sessionId: optExpect(this.sessionId, "No session"),
|
|
891
|
+
marketId: params.marketId,
|
|
892
|
+
side: params.side,
|
|
893
|
+
kind: params.kind,
|
|
894
|
+
priceDecimals: market.priceDecimals,
|
|
895
|
+
triggerPrice: params.triggerPrice,
|
|
896
|
+
limitPrice: params.limitPrice,
|
|
897
|
+
accountId: params.accountId,
|
|
898
|
+
},
|
|
899
|
+
);
|
|
900
|
+
return result;
|
|
901
|
+
} catch (error) {
|
|
902
|
+
throw new NordError("Failed to add trigger", { cause: error });
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
/**
|
|
907
|
+
* Remove a trigger for the current session
|
|
908
|
+
*
|
|
909
|
+
* @param params - Trigger parameters identifying the trigger to remove
|
|
910
|
+
* @returns Object containing the actionId of the removal action
|
|
911
|
+
* @throws {NordError} If the operation fails
|
|
912
|
+
*/
|
|
913
|
+
async removeTrigger(
|
|
914
|
+
params: RemoveTriggerParams,
|
|
915
|
+
): Promise<{ actionId: bigint }> {
|
|
916
|
+
try {
|
|
917
|
+
this.checkSessionValidity();
|
|
918
|
+
const market = findMarket(this.nord.markets, params.marketId);
|
|
919
|
+
if (!market) {
|
|
920
|
+
throw new NordError(`Market with ID ${params.marketId} not found`);
|
|
921
|
+
}
|
|
922
|
+
const result = await removeTriggerAction(
|
|
923
|
+
this.nord.webServerUrl,
|
|
924
|
+
this.sessionSignFn,
|
|
925
|
+
await this.nord.getTimestamp(),
|
|
926
|
+
this.getNonce(),
|
|
927
|
+
{
|
|
928
|
+
sessionId: optExpect(this.sessionId, "No session"),
|
|
929
|
+
marketId: params.marketId,
|
|
930
|
+
side: params.side,
|
|
931
|
+
kind: params.kind,
|
|
932
|
+
accountId: params.accountId,
|
|
933
|
+
},
|
|
934
|
+
);
|
|
935
|
+
return result;
|
|
936
|
+
} catch (error) {
|
|
937
|
+
throw new NordError("Failed to remove trigger", { cause: error });
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
/**
|
|
942
|
+
* Fetch active triggers for an account.
|
|
943
|
+
*
|
|
944
|
+
* @param params Optional parameters containing an explicit account id.
|
|
945
|
+
* @throws {NordError} If no account can be resolved or the request fails.
|
|
946
|
+
*/
|
|
947
|
+
async getAccountTriggers(params?: {
|
|
948
|
+
accountId?: number;
|
|
949
|
+
}): Promise<AccountTriggerInfo[]> {
|
|
950
|
+
const accountId = params?.accountId ?? this.accountIds?.[0];
|
|
951
|
+
|
|
952
|
+
if (accountId == null) {
|
|
953
|
+
throw new NordError(
|
|
954
|
+
"Account ID is undefined. Make sure to call updateAccountId() before requesting triggers.",
|
|
955
|
+
);
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
try {
|
|
959
|
+
return await fetchAccountTriggers(this.nord.webServerUrl, accountId);
|
|
960
|
+
} catch (error) {
|
|
961
|
+
throw new NordError("Failed to fetch account triggers", { cause: error });
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
/**
|
|
966
|
+
* Fetch trigger history for an account.
|
|
967
|
+
*
|
|
968
|
+
* @param params Optional parameters with account id and history query filters.
|
|
969
|
+
* @throws {NordError} If no account can be resolved or the request fails.
|
|
970
|
+
*/
|
|
971
|
+
async getAccountTriggerHistory(
|
|
972
|
+
params: HistoryTriggerQuery & { accountId?: number },
|
|
973
|
+
): Promise<TriggerHistoryPage> {
|
|
974
|
+
const { accountId: providedAccountId, ...query } = params;
|
|
975
|
+
const accountId = providedAccountId ?? this.accountIds?.[0];
|
|
976
|
+
|
|
977
|
+
if (accountId == null) {
|
|
978
|
+
throw new NordError(
|
|
979
|
+
"Account ID is undefined. Make sure to call updateAccountId() before requesting trigger history.",
|
|
980
|
+
);
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
try {
|
|
984
|
+
return await fetchAccountTriggerHistory(
|
|
985
|
+
this.nord.webServerUrl,
|
|
986
|
+
accountId,
|
|
987
|
+
query,
|
|
988
|
+
);
|
|
989
|
+
} catch (error) {
|
|
990
|
+
throw new NordError("Failed to fetch account trigger history", {
|
|
991
|
+
cause: error,
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
|
|
837
996
|
/**
|
|
838
997
|
* Transfer tokens to another account
|
|
839
998
|
*
|
package/src/types.ts
CHANGED
|
@@ -88,6 +88,10 @@ export type SideFromApi = components["schemas"]["Side"];
|
|
|
88
88
|
export type FillModeFromApi = components["schemas"]["FillMode"];
|
|
89
89
|
export type PlacementOrigin = components["schemas"]["PlacementOrigin"];
|
|
90
90
|
export type FinalizationReason = components["schemas"]["FinalizationReason"];
|
|
91
|
+
export type AccountPnlQuery = components["schemas"]["AccountPnlQuery"];
|
|
92
|
+
export type AccountPnl = components["schemas"]["AccountPnl"];
|
|
93
|
+
export type AccountPnlPage =
|
|
94
|
+
components["schemas"]["PageResult_for_uint64_and_AccountPnl"];
|
|
91
95
|
|
|
92
96
|
/**
|
|
93
97
|
* Configuration options for the Nord client
|
|
@@ -114,8 +118,8 @@ export enum KeyType {
|
|
|
114
118
|
}
|
|
115
119
|
|
|
116
120
|
export enum Side {
|
|
117
|
-
Ask,
|
|
118
|
-
Bid,
|
|
121
|
+
Ask = "ask",
|
|
122
|
+
Bid = "bid",
|
|
119
123
|
}
|
|
120
124
|
|
|
121
125
|
export enum FillMode {
|
|
@@ -125,6 +129,18 @@ export enum FillMode {
|
|
|
125
129
|
FillOrKill,
|
|
126
130
|
}
|
|
127
131
|
|
|
132
|
+
export enum TriggerKind {
|
|
133
|
+
StopLoss = 0,
|
|
134
|
+
TakeProfit = 1,
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export enum TriggerStatus {
|
|
138
|
+
Active = 0,
|
|
139
|
+
Success = 1,
|
|
140
|
+
Cancel = 2,
|
|
141
|
+
Remove = 4,
|
|
142
|
+
}
|
|
143
|
+
|
|
128
144
|
export interface SubscriberConfig {
|
|
129
145
|
streamURL: string;
|
|
130
146
|
maxBufferLen?: number;
|
|
@@ -304,9 +320,22 @@ export interface SPLTokenInfo {
|
|
|
304
320
|
}
|
|
305
321
|
|
|
306
322
|
// Positive decimal price and size.
|
|
323
|
+
// Example:
|
|
324
|
+
// ```
|
|
325
|
+
// const limit = new QuoteSize(new Decimal(114000), new Decimal(0.00035)),
|
|
326
|
+
//```
|
|
327
|
+
// Gives 40$ USD limit.
|
|
328
|
+
//
|
|
329
|
+
// Given price is same(or very close) to the market price,
|
|
330
|
+
// limit gives size tick as close as possible to settlemnt size tick.
|
|
331
|
+
|
|
332
|
+
// If you want to get smaller tick on client (on server it will not change),
|
|
333
|
+
// do `new QuoteSize(new Decimal(114000/2), new Decimal(0.00070))`.
|
|
334
|
+
// It will be 40$ limit, but may help if BTC suddently moves fast.
|
|
307
335
|
export class QuoteSize {
|
|
308
336
|
price: Decimal;
|
|
309
337
|
size: Decimal;
|
|
338
|
+
/// Input can be only positive values.
|
|
310
339
|
constructor(quotePrice: Decimal.Value, quoteSize: Decimal.Value) {
|
|
311
340
|
const p = new Decimal(quotePrice);
|
|
312
341
|
const s = new Decimal(quoteSize);
|
|
@@ -317,11 +346,13 @@ export class QuoteSize {
|
|
|
317
346
|
this.size = s;
|
|
318
347
|
}
|
|
319
348
|
|
|
349
|
+
// USD value of limit, use for debug
|
|
320
350
|
value(): Decimal {
|
|
321
351
|
return this.price.mul(this.size);
|
|
322
352
|
}
|
|
323
353
|
|
|
324
|
-
|
|
354
|
+
// Converts to wire format to be send to server, scaling price and size according to market decimals.
|
|
355
|
+
toWire(
|
|
325
356
|
marketPriceDecimals: number,
|
|
326
357
|
marketSizeDecimals: number,
|
|
327
358
|
): { price: bigint; size: bigint } {
|