@riocrypto/common-server 1.0.2782 → 1.0.2783
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/build/clients/cluster-client.d.ts +1 -1
- package/build/helpers/get-fx-trading-policy.d.ts +61 -0
- package/build/helpers/get-fx-trading-policy.js +287 -0
- package/build/index.d.ts +1 -0
- package/build/index.js +1 -0
- package/build/models/rio-settings.d.ts +3 -15
- package/build/models/rio-settings.js +2 -8
- package/package.json +2 -2
|
@@ -186,7 +186,7 @@ declare class ClusterClient {
|
|
|
186
186
|
requestedOriginAmount?: number;
|
|
187
187
|
requestedDestinationAmount?: number;
|
|
188
188
|
limitPrice?: number;
|
|
189
|
-
twoWaySettlementDateOffset
|
|
189
|
+
twoWaySettlementDateOffset?: number;
|
|
190
190
|
arbitrageSessionId?: string;
|
|
191
191
|
fxTradeId?: string;
|
|
192
192
|
timeInForce?: "day" | "gtc" | "ioc" | "fok";
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Fiat, FXTradingSidePolicy, RioSettings, ResolvedFXPlacement, Side } from "@riocrypto/common";
|
|
2
|
+
/**
|
|
3
|
+
* Minimum trade size (in MXN) below which the Transnetwork bot will reject
|
|
4
|
+
* an order. Kept here because it's a hard operational constraint of the
|
|
5
|
+
* provider, not a per-policy admin setting.
|
|
6
|
+
*/
|
|
7
|
+
export declare const TRANSNETWORK_MIN_MXN_AMOUNT = 1000000;
|
|
8
|
+
type RioSettingsForPolicy = Pick<RioSettings, "fxTradingPolicies">;
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the active FX trading side-policy for a given (fiat, side, offset).
|
|
11
|
+
*
|
|
12
|
+
* Resolution order:
|
|
13
|
+
* 1. `fxTradingPolicies[fiat].byOffset[offset][side]` if defined
|
|
14
|
+
* 2. `fxTradingPolicies[fiat].default[side]` if defined
|
|
15
|
+
* 3. An empty policy that routes every trade to manual (Other) with no
|
|
16
|
+
* execution threshold. This is the safe default when no policy is
|
|
17
|
+
* configured for a (fiat, side) - the operator must explicitly opt in
|
|
18
|
+
* to any automated path.
|
|
19
|
+
*
|
|
20
|
+
* Per-side overrides are all-or-nothing: a configured override fully
|
|
21
|
+
* replaces the default; no field-level merging happens. Offsets without
|
|
22
|
+
* an explicit override always fall back to the default for that
|
|
23
|
+
* (fiat, side), regardless of how distant they are from any configured
|
|
24
|
+
* override.
|
|
25
|
+
*
|
|
26
|
+
* A caller that passes `undefined` (e.g. an order whose request body
|
|
27
|
+
* omitted twoWaySettlementType, which means "default / spot") is
|
|
28
|
+
* treated as T+0 so the same byOffset[0] override and provider
|
|
29
|
+
* eligibility apply as an explicit T+0 trade.
|
|
30
|
+
*/
|
|
31
|
+
export declare const getFXTradingPolicy: (rioSettings: RioSettingsForPolicy, fiat: Fiat, side: Side, twoWaySettlementDateOffset: number | undefined) => FXTradingSidePolicy;
|
|
32
|
+
/**
|
|
33
|
+
* Resolve the runtime placement for an FX trade given a side-policy and
|
|
34
|
+
* the trade's amount. Returns `{ provider, automatic, executionThresholdMs }`:
|
|
35
|
+
* - First the time range covering "now" in the currency's country-local
|
|
36
|
+
* timezone is selected. If no range covers the current minute,
|
|
37
|
+
* the trade falls back to manual (Other/automatic, threshold=0).
|
|
38
|
+
* - The matched time window's `executionThresholdMs` is always
|
|
39
|
+
* surfaced even when the inner amount-range lookup falls through to
|
|
40
|
+
* Other - aggregation behavior is a property of the time window, not
|
|
41
|
+
* of the band the trade landed in.
|
|
42
|
+
* - Within that time range, the first amount range matching the trade
|
|
43
|
+
* amount wins.
|
|
44
|
+
* - If the matched amount range's provider fails operational eligibility
|
|
45
|
+
* (e.g. Transnetwork below its 1M minimum, StoneX at T+0), falls back
|
|
46
|
+
* to manual (Other/automatic) while preserving the window's threshold.
|
|
47
|
+
* - If no amount range matches, same fallback (preserves threshold).
|
|
48
|
+
* - For providers that don't support cosigner approval (Matching, Other)
|
|
49
|
+
* the `automatic` flag is forced to true regardless of what is stored
|
|
50
|
+
* on the range - the dashboard renders the checkbox forced-on for
|
|
51
|
+
* these providers, but defense-in-depth on the runtime is cheap.
|
|
52
|
+
*
|
|
53
|
+
* `now` defaults to `new Date()` and is exposed so the cron sweep can
|
|
54
|
+
* pin a single timestamp across the whole batch.
|
|
55
|
+
*
|
|
56
|
+
* `Other` returning `automatic: true` is intentional: there is no
|
|
57
|
+
* cosigner gate for manual trades, so they immediately settle without
|
|
58
|
+
* waiting for approval.
|
|
59
|
+
*/
|
|
60
|
+
export declare const resolveFXProviderForAmount: (policy: FXTradingSidePolicy, fiat: Fiat, amountFiat: number, offset: number | undefined, now?: Date) => ResolvedFXPlacement;
|
|
61
|
+
export {};
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveFXProviderForAmount = exports.getFXTradingPolicy = exports.TRANSNETWORK_MIN_MXN_AMOUNT = void 0;
|
|
4
|
+
const common_1 = require("@riocrypto/common");
|
|
5
|
+
/**
|
|
6
|
+
* Minimum trade size (in MXN) below which the Transnetwork bot will reject
|
|
7
|
+
* an order. Kept here because it's a hard operational constraint of the
|
|
8
|
+
* provider, not a per-policy admin setting.
|
|
9
|
+
*/
|
|
10
|
+
exports.TRANSNETWORK_MIN_MXN_AMOUNT = 1000000;
|
|
11
|
+
/**
|
|
12
|
+
* Settlement-offset constraints baked into provider eligibility. The
|
|
13
|
+
* Emarkets and Transnetwork bots only quote spot / very-short-dated MXN
|
|
14
|
+
* (T+0..T+2), and StoneX is RFQ/RFS for forward MXN (T+1+) and refuses
|
|
15
|
+
* same-day (T+0). Keep these in sync with
|
|
16
|
+
* `isProviderSelectableForFiatAndOffset` in the dashboard's
|
|
17
|
+
* fx-trading-policy-card so saved policies match what the runtime will
|
|
18
|
+
* actually pick.
|
|
19
|
+
*/
|
|
20
|
+
const SHORT_DATED_BOT_MAX_OFFSET = 2;
|
|
21
|
+
const STONEX_MIN_OFFSET = 1;
|
|
22
|
+
/**
|
|
23
|
+
* Providers that do not support a cosigner approval workflow. For these
|
|
24
|
+
* providers the `automatic` flag is meaningless and the runtime always
|
|
25
|
+
* treats the band as automatic, regardless of what is persisted on the
|
|
26
|
+
* policy. The dashboard mirrors this by rendering the checkbox forced-on
|
|
27
|
+
* and disabled for these providers.
|
|
28
|
+
*
|
|
29
|
+
* StoneX is intentionally NOT in this set: even though placement is RFQ
|
|
30
|
+
* (not fill-or-kill), internal-fx supports gating a StoneX placement
|
|
31
|
+
* behind the same cosigner webhook the bots use, so operators can
|
|
32
|
+
* require approval before an RFQ stream is opened.
|
|
33
|
+
*/
|
|
34
|
+
const NON_COSIGNER_PROVIDERS = new Set([
|
|
35
|
+
common_1.FXProvider.Matching,
|
|
36
|
+
common_1.FXProvider.Other,
|
|
37
|
+
]);
|
|
38
|
+
const FALLBACK_RESOLUTION = {
|
|
39
|
+
provider: common_1.FXProvider.Other,
|
|
40
|
+
automatic: true,
|
|
41
|
+
// 0ms threshold = place immediately. The Other path completes
|
|
42
|
+
// manually with no aggregation; surfacing a longer threshold here
|
|
43
|
+
// would just pin manual trades in PendingExecutionThreshold for no
|
|
44
|
+
// reason.
|
|
45
|
+
executionThresholdMs: 0,
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Defensive shape-check. Legacy persisted policies use a different shape
|
|
49
|
+
* (`providers[]` / `automaticTradeLimit`, or the pre-per-window-threshold
|
|
50
|
+
* shape that put `executionThresholdMs` on the side policy) - left in
|
|
51
|
+
* place by an out-of-date deploy, by an admin who hasn't saved the new
|
|
52
|
+
* editor yet, or by a typo'd manual DB edit. The resolver MUST NOT
|
|
53
|
+
* crash on that data; instead it treats those slots as "not configured"
|
|
54
|
+
* so the safe Other/automatic fallback applies and trades route to
|
|
55
|
+
* manual until the admin saves the new policy shape.
|
|
56
|
+
*/
|
|
57
|
+
const isWellFormedSidePolicy = (policy) => {
|
|
58
|
+
if (!policy || typeof policy !== "object")
|
|
59
|
+
return false;
|
|
60
|
+
const candidate = policy;
|
|
61
|
+
return Array.isArray(candidate.byTimeRange);
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Defensive shape-check for a single time range. The resolver tolerates
|
|
65
|
+
* a malformed entry by skipping it (so a malformed window doesn't take
|
|
66
|
+
* down the whole side). Note: we do NOT require contiguous coverage at
|
|
67
|
+
* the resolver layer - the rio-settings save route enforces that. Stale
|
|
68
|
+
* data with a gap still routes to manual via fallback.
|
|
69
|
+
*
|
|
70
|
+
* `executionThresholdMs` may be absent on legacy rows that were saved
|
|
71
|
+
* before the threshold moved per-window; we treat that as "place
|
|
72
|
+
* immediately" rather than rejecting the whole window, which matches
|
|
73
|
+
* the safer default and keeps existing routing rules intact for
|
|
74
|
+
* operators who haven't re-saved yet.
|
|
75
|
+
*/
|
|
76
|
+
const isWellFormedTimeRange = (range) => {
|
|
77
|
+
if (!range || typeof range !== "object")
|
|
78
|
+
return false;
|
|
79
|
+
const candidate = range;
|
|
80
|
+
return (typeof candidate.startMinute === "number" &&
|
|
81
|
+
typeof candidate.endMinute === "number" &&
|
|
82
|
+
Array.isArray(candidate.byAmountRange));
|
|
83
|
+
};
|
|
84
|
+
const safeExecutionThresholdMs = (range) => {
|
|
85
|
+
return typeof range.executionThresholdMs === "number" &&
|
|
86
|
+
Number.isFinite(range.executionThresholdMs) &&
|
|
87
|
+
range.executionThresholdMs >= 0
|
|
88
|
+
? range.executionThresholdMs
|
|
89
|
+
: 0;
|
|
90
|
+
};
|
|
91
|
+
const pickPolicyBySide = (policyBySide, side) => {
|
|
92
|
+
if (!policyBySide) {
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
const candidate = policyBySide[side];
|
|
96
|
+
if (!isWellFormedSidePolicy(candidate)) {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
return candidate;
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Resolve the active FX trading side-policy for a given (fiat, side, offset).
|
|
103
|
+
*
|
|
104
|
+
* Resolution order:
|
|
105
|
+
* 1. `fxTradingPolicies[fiat].byOffset[offset][side]` if defined
|
|
106
|
+
* 2. `fxTradingPolicies[fiat].default[side]` if defined
|
|
107
|
+
* 3. An empty policy that routes every trade to manual (Other) with no
|
|
108
|
+
* execution threshold. This is the safe default when no policy is
|
|
109
|
+
* configured for a (fiat, side) - the operator must explicitly opt in
|
|
110
|
+
* to any automated path.
|
|
111
|
+
*
|
|
112
|
+
* Per-side overrides are all-or-nothing: a configured override fully
|
|
113
|
+
* replaces the default; no field-level merging happens. Offsets without
|
|
114
|
+
* an explicit override always fall back to the default for that
|
|
115
|
+
* (fiat, side), regardless of how distant they are from any configured
|
|
116
|
+
* override.
|
|
117
|
+
*
|
|
118
|
+
* A caller that passes `undefined` (e.g. an order whose request body
|
|
119
|
+
* omitted twoWaySettlementType, which means "default / spot") is
|
|
120
|
+
* treated as T+0 so the same byOffset[0] override and provider
|
|
121
|
+
* eligibility apply as an explicit T+0 trade.
|
|
122
|
+
*/
|
|
123
|
+
const getFXTradingPolicy = (rioSettings, fiat, side, twoWaySettlementDateOffset) => {
|
|
124
|
+
var _a;
|
|
125
|
+
const effectiveOffset = twoWaySettlementDateOffset !== null && twoWaySettlementDateOffset !== void 0 ? twoWaySettlementDateOffset : 0;
|
|
126
|
+
const fiatPolicies = (_a = rioSettings.fxTradingPolicies) === null || _a === void 0 ? void 0 : _a[fiat];
|
|
127
|
+
if (fiatPolicies) {
|
|
128
|
+
if (fiatPolicies.byOffset) {
|
|
129
|
+
const offsetPolicy = pickPolicyBySide(fiatPolicies.byOffset[effectiveOffset], side);
|
|
130
|
+
if (offsetPolicy) {
|
|
131
|
+
return offsetPolicy;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const defaultPolicy = pickPolicyBySide(fiatPolicies.default, side);
|
|
135
|
+
if (defaultPolicy) {
|
|
136
|
+
return defaultPolicy;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return { byTimeRange: [] };
|
|
140
|
+
};
|
|
141
|
+
exports.getFXTradingPolicy = getFXTradingPolicy;
|
|
142
|
+
/**
|
|
143
|
+
* Returns true when the given provider can operationally handle the trade.
|
|
144
|
+
* These are hard provider-side constraints, not admin-configurable.
|
|
145
|
+
*
|
|
146
|
+
* `offset === undefined` is interpreted as T+0 (the system convention:
|
|
147
|
+
* "no settlement type specified" == "default" == spot), matching
|
|
148
|
+
* `getFXTradingPolicy`. As a result Emarkets / Transnetwork stay eligible
|
|
149
|
+
* for undefined-offset callers but StoneX (which refuses T+0) does not.
|
|
150
|
+
*/
|
|
151
|
+
const isProviderEligible = (provider, fiat, amountFiat, offset) => {
|
|
152
|
+
if (provider === common_1.FXProvider.StoneX ||
|
|
153
|
+
provider === common_1.FXProvider.Emarkets ||
|
|
154
|
+
provider === common_1.FXProvider.Transnetwork) {
|
|
155
|
+
if (fiat !== common_1.Fiat.MXN)
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
if (provider === common_1.FXProvider.Transnetwork) {
|
|
159
|
+
if (amountFiat === undefined || amountFiat < exports.TRANSNETWORK_MIN_MXN_AMOUNT) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const effectiveOffset = offset !== null && offset !== void 0 ? offset : 0;
|
|
164
|
+
if ((provider === common_1.FXProvider.Emarkets ||
|
|
165
|
+
provider === common_1.FXProvider.Transnetwork) &&
|
|
166
|
+
effectiveOffset > SHORT_DATED_BOT_MAX_OFFSET) {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
if (provider === common_1.FXProvider.StoneX && effectiveOffset < STONEX_MIN_OFFSET) {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
return true;
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* Returns true if `amount` falls inside `[range.minAmount, range.maxAmount)`.
|
|
176
|
+
* Both bounds are required by the type, but the resolver defensively
|
|
177
|
+
* treats a missing/non-finite `maxAmount` (legacy data, manual db edit)
|
|
178
|
+
* as a non-match so the trade falls through to the safe Other/manual
|
|
179
|
+
* fallback rather than silently inheriting a previous "open-ended"
|
|
180
|
+
* interpretation.
|
|
181
|
+
*/
|
|
182
|
+
const rangeMatchesAmount = (range, amount) => {
|
|
183
|
+
const min = Math.max(range.minAmount, 0);
|
|
184
|
+
if (amount < min)
|
|
185
|
+
return false;
|
|
186
|
+
if (typeof range.maxAmount !== "number" || !Number.isFinite(range.maxAmount)) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
return amount < range.maxAmount;
|
|
190
|
+
};
|
|
191
|
+
/**
|
|
192
|
+
* Returns true if `minuteOfDay` (0..1439, in the time range's reference
|
|
193
|
+
* timezone) falls inside `[range.startMinute, range.endMinute)`.
|
|
194
|
+
*/
|
|
195
|
+
const rangeMatchesMinute = (range, minuteOfDay) => {
|
|
196
|
+
if (minuteOfDay < range.startMinute)
|
|
197
|
+
return false;
|
|
198
|
+
return minuteOfDay < range.endMinute;
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* Returns the current minute-of-day in the given IANA timezone, in the
|
|
202
|
+
* range [0, 1440). Uses `Intl.DateTimeFormat` so DST transitions are
|
|
203
|
+
* applied automatically by the platform - no manual offset bookkeeping.
|
|
204
|
+
*
|
|
205
|
+
* `now` is injectable for testability and so a single cron tick can
|
|
206
|
+
* snapshot the time once and reuse it across all FX trades being swept.
|
|
207
|
+
*/
|
|
208
|
+
const getMinuteOfDayInTimezone = (now, timezone) => {
|
|
209
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
210
|
+
timeZone: timezone,
|
|
211
|
+
hour12: false,
|
|
212
|
+
hour: "2-digit",
|
|
213
|
+
minute: "2-digit",
|
|
214
|
+
}).formatToParts(now);
|
|
215
|
+
let hour = 0;
|
|
216
|
+
let minute = 0;
|
|
217
|
+
for (const part of parts) {
|
|
218
|
+
if (part.type === "hour")
|
|
219
|
+
hour = parseInt(part.value, 10);
|
|
220
|
+
if (part.type === "minute")
|
|
221
|
+
minute = parseInt(part.value, 10);
|
|
222
|
+
}
|
|
223
|
+
// Intl formats midnight as "24" in some locales/Node versions; clamp
|
|
224
|
+
// defensively so we never return 1440 (which would miss the [0, 1440)
|
|
225
|
+
// last range and fall through to manual).
|
|
226
|
+
if (hour === 24)
|
|
227
|
+
hour = 0;
|
|
228
|
+
return hour * 60 + minute;
|
|
229
|
+
};
|
|
230
|
+
/**
|
|
231
|
+
* Resolve the runtime placement for an FX trade given a side-policy and
|
|
232
|
+
* the trade's amount. Returns `{ provider, automatic, executionThresholdMs }`:
|
|
233
|
+
* - First the time range covering "now" in the currency's country-local
|
|
234
|
+
* timezone is selected. If no range covers the current minute,
|
|
235
|
+
* the trade falls back to manual (Other/automatic, threshold=0).
|
|
236
|
+
* - The matched time window's `executionThresholdMs` is always
|
|
237
|
+
* surfaced even when the inner amount-range lookup falls through to
|
|
238
|
+
* Other - aggregation behavior is a property of the time window, not
|
|
239
|
+
* of the band the trade landed in.
|
|
240
|
+
* - Within that time range, the first amount range matching the trade
|
|
241
|
+
* amount wins.
|
|
242
|
+
* - If the matched amount range's provider fails operational eligibility
|
|
243
|
+
* (e.g. Transnetwork below its 1M minimum, StoneX at T+0), falls back
|
|
244
|
+
* to manual (Other/automatic) while preserving the window's threshold.
|
|
245
|
+
* - If no amount range matches, same fallback (preserves threshold).
|
|
246
|
+
* - For providers that don't support cosigner approval (Matching, Other)
|
|
247
|
+
* the `automatic` flag is forced to true regardless of what is stored
|
|
248
|
+
* on the range - the dashboard renders the checkbox forced-on for
|
|
249
|
+
* these providers, but defense-in-depth on the runtime is cheap.
|
|
250
|
+
*
|
|
251
|
+
* `now` defaults to `new Date()` and is exposed so the cron sweep can
|
|
252
|
+
* pin a single timestamp across the whole batch.
|
|
253
|
+
*
|
|
254
|
+
* `Other` returning `automatic: true` is intentional: there is no
|
|
255
|
+
* cosigner gate for manual trades, so they immediately settle without
|
|
256
|
+
* waiting for approval.
|
|
257
|
+
*/
|
|
258
|
+
const resolveFXProviderForAmount = (policy, fiat, amountFiat, offset, now = new Date()) => {
|
|
259
|
+
const country = (0, common_1.getCountryForFiat)(fiat);
|
|
260
|
+
const timezone = (0, common_1.getCountryTimezone)(country);
|
|
261
|
+
const minuteOfDay = getMinuteOfDayInTimezone(now, timezone);
|
|
262
|
+
const matchedTimeRange = policy.byTimeRange.find((range) => isWellFormedTimeRange(range) && rangeMatchesMinute(range, minuteOfDay));
|
|
263
|
+
if (!matchedTimeRange) {
|
|
264
|
+
return FALLBACK_RESOLUTION;
|
|
265
|
+
}
|
|
266
|
+
// Threshold for the active time window. Always returned (even when
|
|
267
|
+
// we fall through to Other below) so callers can decide aggregation
|
|
268
|
+
// based on the window the trade is currently in, not the window when
|
|
269
|
+
// it was created.
|
|
270
|
+
const executionThresholdMs = safeExecutionThresholdMs(matchedTimeRange);
|
|
271
|
+
const matchedAmountRange = matchedTimeRange.byAmountRange.find((range) => rangeMatchesAmount(range, amountFiat));
|
|
272
|
+
if (!matchedAmountRange) {
|
|
273
|
+
return Object.assign(Object.assign({}, FALLBACK_RESOLUTION), { executionThresholdMs });
|
|
274
|
+
}
|
|
275
|
+
if (!isProviderEligible(matchedAmountRange.provider, fiat, amountFiat, offset)) {
|
|
276
|
+
return Object.assign(Object.assign({}, FALLBACK_RESOLUTION), { executionThresholdMs });
|
|
277
|
+
}
|
|
278
|
+
const automatic = NON_COSIGNER_PROVIDERS.has(matchedAmountRange.provider)
|
|
279
|
+
? true
|
|
280
|
+
: matchedAmountRange.automatic;
|
|
281
|
+
return {
|
|
282
|
+
provider: matchedAmountRange.provider,
|
|
283
|
+
automatic,
|
|
284
|
+
executionThresholdMs,
|
|
285
|
+
};
|
|
286
|
+
};
|
|
287
|
+
exports.resolveFXProviderForAmount = resolveFXProviderForAmount;
|
package/build/index.d.ts
CHANGED
|
@@ -171,3 +171,4 @@ export * from "./helpers/interpolate-forward-curve-rate";
|
|
|
171
171
|
export * from "./helpers/find-user-tier-forward-curve-rate";
|
|
172
172
|
export * from "./helpers/resolve-forward-curve-rate";
|
|
173
173
|
export * from "./helpers/get-resolved-forward-curve-tiers";
|
|
174
|
+
export * from "./helpers/get-fx-trading-policy";
|
package/build/index.js
CHANGED
|
@@ -187,3 +187,4 @@ __exportStar(require("./helpers/interpolate-forward-curve-rate"), exports);
|
|
|
187
187
|
__exportStar(require("./helpers/find-user-tier-forward-curve-rate"), exports);
|
|
188
188
|
__exportStar(require("./helpers/resolve-forward-curve-rate"), exports);
|
|
189
189
|
__exportStar(require("./helpers/get-resolved-forward-curve-tiers"), exports);
|
|
190
|
+
__exportStar(require("./helpers/get-fx-trading-policy"), exports);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Country, DeferredPaymentType, EmarketsSettlementType, Fiat, FXProvider, Processor, Side, TransnetworkSettlementType } from "@riocrypto/common";
|
|
1
|
+
import { Country, DeferredPaymentType, EmarketsSettlementType, Fiat, FXProvider, FXTradingPolicies, Processor, Side, TransnetworkSettlementType } from "@riocrypto/common";
|
|
2
2
|
import { TVFXDataProvider } from "@riocrypto/common";
|
|
3
3
|
import mongoose, { HydratedDocument } from "mongoose";
|
|
4
4
|
interface RioSettingsAttrs {
|
|
@@ -28,12 +28,6 @@ interface RioSettingsAttrs {
|
|
|
28
28
|
sellOrderGeneralExchangeRateMarkup: {
|
|
29
29
|
[key in Fiat]: number;
|
|
30
30
|
};
|
|
31
|
-
buyOrderAutomaticFXTradeLimit: {
|
|
32
|
-
[key in Fiat]: number;
|
|
33
|
-
};
|
|
34
|
-
sellOrderAutomaticFXTradeLimit: {
|
|
35
|
-
[key in Fiat]: number;
|
|
36
|
-
};
|
|
37
31
|
defaultServiceFee: {
|
|
38
32
|
[key in Country]?: {
|
|
39
33
|
[key in Side]?: number;
|
|
@@ -51,7 +45,7 @@ interface RioSettingsAttrs {
|
|
|
51
45
|
};
|
|
52
46
|
};
|
|
53
47
|
};
|
|
54
|
-
|
|
48
|
+
fxTradingPolicies?: FXTradingPolicies;
|
|
55
49
|
activeFiatPaymentProcessors: {
|
|
56
50
|
[Side.Buy]: Processor[];
|
|
57
51
|
[Side.Sell]: Processor[];
|
|
@@ -117,12 +111,6 @@ interface RioSettingsDoc extends mongoose.Document {
|
|
|
117
111
|
sellOrderAfterHoursExchangeRateMarkup: {
|
|
118
112
|
[key in Fiat]: number;
|
|
119
113
|
};
|
|
120
|
-
buyOrderAutomaticFXTradeLimit: {
|
|
121
|
-
[key in Fiat]: number;
|
|
122
|
-
};
|
|
123
|
-
sellOrderAutomaticFXTradeLimit: {
|
|
124
|
-
[key in Fiat]: number;
|
|
125
|
-
};
|
|
126
114
|
additionalBuyOrderLiquidityUSD?: number;
|
|
127
115
|
additionalSellOrderLiquidityUSD?: number;
|
|
128
116
|
previousDayUSDMXNConversionRate?: number;
|
|
@@ -140,7 +128,7 @@ interface RioSettingsDoc extends mongoose.Document {
|
|
|
140
128
|
};
|
|
141
129
|
};
|
|
142
130
|
};
|
|
143
|
-
|
|
131
|
+
fxTradingPolicies?: FXTradingPolicies;
|
|
144
132
|
activeFiatPaymentProcessors: {
|
|
145
133
|
[Side.Buy]: Processor[];
|
|
146
134
|
[Side.Sell]: Processor[];
|
|
@@ -15,12 +15,6 @@ const buildRioSettings = (mongoose) => {
|
|
|
15
15
|
defaultDeferredPaymentFee: {
|
|
16
16
|
type: Object,
|
|
17
17
|
},
|
|
18
|
-
buyOrderAutomaticFXTradeLimit: {
|
|
19
|
-
type: Object,
|
|
20
|
-
},
|
|
21
|
-
sellOrderAutomaticFXTradeLimit: {
|
|
22
|
-
type: Object,
|
|
23
|
-
},
|
|
24
18
|
isAfterHours: {
|
|
25
19
|
type: Object,
|
|
26
20
|
},
|
|
@@ -48,8 +42,8 @@ const buildRioSettings = (mongoose) => {
|
|
|
48
42
|
fxProviders: {
|
|
49
43
|
type: Object,
|
|
50
44
|
},
|
|
51
|
-
|
|
52
|
-
type:
|
|
45
|
+
fxTradingPolicies: {
|
|
46
|
+
type: Object,
|
|
53
47
|
},
|
|
54
48
|
defaultServiceFee: {
|
|
55
49
|
type: Object,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riocrypto/common-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2783",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./build/index.js",
|
|
6
6
|
"types": "./build/index.d.ts",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"@google-cloud/secret-manager": "^5.6.0",
|
|
25
25
|
"@google-cloud/storage": "^7.19.0",
|
|
26
26
|
"@hyperdx/node-opentelemetry": "^0.10.3",
|
|
27
|
-
"@riocrypto/common": "1.0.
|
|
27
|
+
"@riocrypto/common": "1.0.2587",
|
|
28
28
|
"@slack/web-api": "^7.15.0",
|
|
29
29
|
"@types/express": "^4.17.25",
|
|
30
30
|
"axios": "1.13.6",
|