@signals-protocol/v1-sdk 1.4.0 → 1.5.0
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/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/share/avatar.d.ts +9 -0
- package/dist/share/avatar.js +167 -0
- package/dist/share/index.d.ts +2 -0
- package/dist/share/index.js +4 -1
- package/dist/share/position-image.js +1 -1
- package/dist/share/profile-image.js +495 -149
- package/dist/share/types.d.ts +15 -0
- package/package.json +3 -4
package/dist/index.d.ts
CHANGED
|
@@ -10,4 +10,4 @@ export * as MathUtils from "./utils/math";
|
|
|
10
10
|
export { toWAD, toMicroUSDC } from "./clmsr-sdk";
|
|
11
11
|
export { createCLMSRSDK, createSignalsSDK } from "./clmsr-sdk";
|
|
12
12
|
export * from "./share";
|
|
13
|
-
export declare const VERSION = "1.
|
|
13
|
+
export declare const VERSION = "1.5.0";
|
package/dist/index.js
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type AvatarTheme = "light" | "dark";
|
|
2
|
+
export interface AvatarData {
|
|
3
|
+
pattern: boolean[][];
|
|
4
|
+
primary: string;
|
|
5
|
+
secondary: string;
|
|
6
|
+
background: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const AVATAR_COLOR_PALETTE: readonly ["#1444c2", "#2d88ff", "#fdd979", "#8bcaff", "#00BF40", "#F7931A", "#FF4343", "#6366f1", "#8b5cf6", "#ec4899", "#f59e0b", "#10b981", "#fb7185", "#fc97f3", "#5eead4", "#c4b5fd"];
|
|
9
|
+
export declare function getAvatarData(address: string, theme: AvatarTheme): AvatarData;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AVATAR_COLOR_PALETTE = void 0;
|
|
4
|
+
exports.getAvatarData = getAvatarData;
|
|
5
|
+
exports.AVATAR_COLOR_PALETTE = [
|
|
6
|
+
"#1444c2",
|
|
7
|
+
"#2d88ff",
|
|
8
|
+
"#fdd979",
|
|
9
|
+
"#8bcaff",
|
|
10
|
+
"#00BF40",
|
|
11
|
+
"#F7931A",
|
|
12
|
+
"#FF4343",
|
|
13
|
+
"#6366f1",
|
|
14
|
+
"#8b5cf6",
|
|
15
|
+
"#ec4899",
|
|
16
|
+
"#f59e0b",
|
|
17
|
+
"#10b981",
|
|
18
|
+
"#fb7185",
|
|
19
|
+
"#fc97f3",
|
|
20
|
+
"#5eead4",
|
|
21
|
+
"#c4b5fd",
|
|
22
|
+
];
|
|
23
|
+
const AVATAR_BACKGROUNDS = {
|
|
24
|
+
light: "#f8fafc",
|
|
25
|
+
dark: "#1e1e22",
|
|
26
|
+
};
|
|
27
|
+
function simpleHash(str) {
|
|
28
|
+
let hash = 5381;
|
|
29
|
+
for (let i = 0; i < str.length; i += 1) {
|
|
30
|
+
const char = str.charCodeAt(i);
|
|
31
|
+
hash = (hash << 5) + hash + char;
|
|
32
|
+
}
|
|
33
|
+
return Math.abs(hash);
|
|
34
|
+
}
|
|
35
|
+
function getSecondaryHash(address) {
|
|
36
|
+
let hash = 0;
|
|
37
|
+
const suffix = address.slice(-8);
|
|
38
|
+
for (let i = 0; i < suffix.length; i += 1) {
|
|
39
|
+
hash = ((hash << 3) + hash + suffix.charCodeAt(i)) & 0xffffffff;
|
|
40
|
+
}
|
|
41
|
+
return Math.abs(hash);
|
|
42
|
+
}
|
|
43
|
+
function generateSeedValues(address) {
|
|
44
|
+
const hash1 = simpleHash(address);
|
|
45
|
+
const hash2 = getSecondaryHash(address);
|
|
46
|
+
const values = [];
|
|
47
|
+
let seed = hash1;
|
|
48
|
+
for (let i = 0; i < 10; i += 1) {
|
|
49
|
+
seed = (seed * 1103015245 + 12345 + hash2) & 0x7fffffff;
|
|
50
|
+
values.push(seed);
|
|
51
|
+
}
|
|
52
|
+
return values;
|
|
53
|
+
}
|
|
54
|
+
function generatePixelPattern(address) {
|
|
55
|
+
const seeds = generateSeedValues(address);
|
|
56
|
+
const pattern = Array(5)
|
|
57
|
+
.fill(null)
|
|
58
|
+
.map(() => Array(5).fill(false));
|
|
59
|
+
for (let y = 0; y < 5; y += 1) {
|
|
60
|
+
for (let x = 0; x < 2; x += 1) {
|
|
61
|
+
const index = y * 2 + x;
|
|
62
|
+
const seedIndex = index % seeds.length;
|
|
63
|
+
const shouldFill = seeds[seedIndex] % 100 > 40;
|
|
64
|
+
pattern[y][x] = shouldFill;
|
|
65
|
+
pattern[y][4 - x] = shouldFill;
|
|
66
|
+
}
|
|
67
|
+
const centerIndex = y + 10;
|
|
68
|
+
const centerSeedIndex = centerIndex % seeds.length;
|
|
69
|
+
pattern[y][2] = seeds[centerSeedIndex] % 100 > 40;
|
|
70
|
+
}
|
|
71
|
+
const countFilled = () => pattern.flat().filter(Boolean).length;
|
|
72
|
+
const ensureMinPixels = (minPixels) => {
|
|
73
|
+
let filledCount = countFilled();
|
|
74
|
+
if (filledCount >= minPixels)
|
|
75
|
+
return;
|
|
76
|
+
const candidates = [];
|
|
77
|
+
for (let y = 0; y < 5; y += 1) {
|
|
78
|
+
for (let x = 0; x < 3; x += 1) {
|
|
79
|
+
if (!pattern[y][x]) {
|
|
80
|
+
candidates.push([y, x]);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
let i = 0;
|
|
85
|
+
while (filledCount < minPixels && candidates.length > 0) {
|
|
86
|
+
const idx = (seeds[i % seeds.length] + i) % candidates.length;
|
|
87
|
+
const [y, x] = candidates.splice(idx, 1)[0];
|
|
88
|
+
if (!pattern[y][x]) {
|
|
89
|
+
pattern[y][x] = true;
|
|
90
|
+
if (x !== 2) {
|
|
91
|
+
if (!pattern[y][4 - x]) {
|
|
92
|
+
pattern[y][4 - x] = true;
|
|
93
|
+
filledCount += 2;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
filledCount += 1;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
filledCount += 1;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
i += 1;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
ensureMinPixels(12);
|
|
107
|
+
const parityCounts = { even: 0, odd: 0 };
|
|
108
|
+
for (let y = 0; y < 5; y += 1) {
|
|
109
|
+
for (let x = 0; x < 5; x += 1) {
|
|
110
|
+
if (pattern[y][x]) {
|
|
111
|
+
if ((y + x) % 2 === 0) {
|
|
112
|
+
parityCounts.even += 1;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
parityCounts.odd += 1;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (parityCounts.even === 0 || parityCounts.odd === 0) {
|
|
121
|
+
const missingParity = parityCounts.even === 0 ? 0 : 1;
|
|
122
|
+
const candidates = [];
|
|
123
|
+
for (let y = 0; y < 5; y += 1) {
|
|
124
|
+
for (let x = 0; x < 3; x += 1) {
|
|
125
|
+
if (!pattern[y][x] && (y + x) % 2 === missingParity) {
|
|
126
|
+
candidates.push([y, x]);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (candidates.length > 0) {
|
|
131
|
+
const idx = (seeds[(seeds.length - 1 + missingParity) % seeds.length] +
|
|
132
|
+
parityCounts.even +
|
|
133
|
+
parityCounts.odd) %
|
|
134
|
+
candidates.length;
|
|
135
|
+
const [y, x] = candidates[idx];
|
|
136
|
+
pattern[y][x] = true;
|
|
137
|
+
if (x !== 2) {
|
|
138
|
+
pattern[y][4 - x] = true;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return pattern;
|
|
143
|
+
}
|
|
144
|
+
function generateColors(address) {
|
|
145
|
+
const seeds = generateSeedValues(address);
|
|
146
|
+
const hash1 = simpleHash(address);
|
|
147
|
+
const hash2 = getSecondaryHash(address);
|
|
148
|
+
const primaryColorIndex = Math.abs(hash1 ^ seeds[3]) % exports.AVATAR_COLOR_PALETTE.length;
|
|
149
|
+
let secondaryColorIndex = Math.abs(hash2 ^ seeds[7]) % exports.AVATAR_COLOR_PALETTE.length;
|
|
150
|
+
if (secondaryColorIndex === primaryColorIndex) {
|
|
151
|
+
secondaryColorIndex =
|
|
152
|
+
(secondaryColorIndex + 1) % exports.AVATAR_COLOR_PALETTE.length;
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
primary: exports.AVATAR_COLOR_PALETTE[primaryColorIndex],
|
|
156
|
+
secondary: exports.AVATAR_COLOR_PALETTE[secondaryColorIndex],
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function getAvatarData(address, theme) {
|
|
160
|
+
const colors = generateColors(address);
|
|
161
|
+
return {
|
|
162
|
+
pattern: generatePixelPattern(address),
|
|
163
|
+
primary: colors.primary,
|
|
164
|
+
secondary: colors.secondary,
|
|
165
|
+
background: AVATAR_BACKGROUNDS[theme],
|
|
166
|
+
};
|
|
167
|
+
}
|
package/dist/share/index.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export { buildPositionVNode } from "./position-image";
|
|
|
3
3
|
export { buildProfileVNode } from "./profile-image";
|
|
4
4
|
export { buildDistributionVNode } from "./distribution-image";
|
|
5
5
|
export { buildBrandVNode } from "./brand-image";
|
|
6
|
+
export { AVATAR_COLOR_PALETTE, getAvatarData } from "./avatar";
|
|
7
|
+
export type { AvatarData, AvatarTheme } from "./avatar";
|
|
6
8
|
export { h } from "./h";
|
|
7
9
|
export { SIGNALS_LOGO_URI, buildLogoStrokeSVG } from "./assets";
|
|
8
10
|
export { formatUSDC, formatPrice, formatPriceRange, percForm, formatUsdPnl, getPnlColor, } from "./format";
|
package/dist/share/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.formatDistributionPriceRange = exports.getPeakRange = exports.formatXTick = exports.generateXAxisTicks = exports.getSqrtHeight = exports.getBarCategory = exports.getPnlColor = exports.formatUsdPnl = exports.percForm = exports.formatPriceRange = exports.formatPrice = exports.formatUSDC = exports.buildLogoStrokeSVG = exports.SIGNALS_LOGO_URI = exports.h = exports.buildBrandVNode = exports.buildDistributionVNode = exports.buildProfileVNode = exports.buildPositionVNode = void 0;
|
|
3
|
+
exports.formatDistributionPriceRange = exports.getPeakRange = exports.formatXTick = exports.generateXAxisTicks = exports.getSqrtHeight = exports.getBarCategory = exports.getPnlColor = exports.formatUsdPnl = exports.percForm = exports.formatPriceRange = exports.formatPrice = exports.formatUSDC = exports.buildLogoStrokeSVG = exports.SIGNALS_LOGO_URI = exports.h = exports.getAvatarData = exports.AVATAR_COLOR_PALETTE = exports.buildBrandVNode = exports.buildDistributionVNode = exports.buildProfileVNode = exports.buildPositionVNode = void 0;
|
|
4
4
|
// VNode builders
|
|
5
5
|
var position_image_1 = require("./position-image");
|
|
6
6
|
Object.defineProperty(exports, "buildPositionVNode", { enumerable: true, get: function () { return position_image_1.buildPositionVNode; } });
|
|
@@ -10,6 +10,9 @@ var distribution_image_1 = require("./distribution-image");
|
|
|
10
10
|
Object.defineProperty(exports, "buildDistributionVNode", { enumerable: true, get: function () { return distribution_image_1.buildDistributionVNode; } });
|
|
11
11
|
var brand_image_1 = require("./brand-image");
|
|
12
12
|
Object.defineProperty(exports, "buildBrandVNode", { enumerable: true, get: function () { return brand_image_1.buildBrandVNode; } });
|
|
13
|
+
var avatar_1 = require("./avatar");
|
|
14
|
+
Object.defineProperty(exports, "AVATAR_COLOR_PALETTE", { enumerable: true, get: function () { return avatar_1.AVATAR_COLOR_PALETTE; } });
|
|
15
|
+
Object.defineProperty(exports, "getAvatarData", { enumerable: true, get: function () { return avatar_1.getAvatarData; } });
|
|
13
16
|
// Helpers (used by v1-server for its own distribution rendering)
|
|
14
17
|
var h_1 = require("./h");
|
|
15
18
|
Object.defineProperty(exports, "h", { enumerable: true, get: function () { return h_1.h; } });
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.buildProfileVNode = buildProfileVNode;
|
|
4
4
|
const h_1 = require("./h");
|
|
5
5
|
const assets_1 = require("./assets");
|
|
6
|
+
const avatar_1 = require("./avatar");
|
|
6
7
|
const CARD_WIDTH = 1200;
|
|
7
8
|
const CARD_HEIGHT = 630;
|
|
8
9
|
const OUTER_PADDING = 24;
|
|
@@ -15,6 +16,9 @@ const PLOT_RIGHT = 18;
|
|
|
15
16
|
const PLOT_TOP = 14;
|
|
16
17
|
const PLOT_BOTTOM = 24;
|
|
17
18
|
const BAND_WIDTH = 18;
|
|
19
|
+
const DAY_MS = 86400000;
|
|
20
|
+
const AVATAR_SIZE = 96;
|
|
21
|
+
const MAX_RENDERED_POSITIONS = 1000;
|
|
18
22
|
const OG_THEMES = {
|
|
19
23
|
light: {
|
|
20
24
|
pageBg: "#eef7ff", // bg-surface-soft
|
|
@@ -41,13 +45,83 @@ const OG_THEMES = {
|
|
|
41
45
|
neutralBg: "#27272A", // surface-muted
|
|
42
46
|
},
|
|
43
47
|
};
|
|
44
|
-
const
|
|
45
|
-
BRONZE:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
const TIER_CHIP_THEMES = {
|
|
49
|
+
BRONZE: {
|
|
50
|
+
light: {
|
|
51
|
+
from: "#FFE6CC",
|
|
52
|
+
to: "#FFC999",
|
|
53
|
+
border: "#C97E3D",
|
|
54
|
+
icon: "#8B4513",
|
|
55
|
+
},
|
|
56
|
+
dark: {
|
|
57
|
+
from: "#3D2B1A",
|
|
58
|
+
to: "#5C3A1E",
|
|
59
|
+
border: "#8B5E2B",
|
|
60
|
+
icon: "#F5E5D4",
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
SILVER: {
|
|
64
|
+
light: {
|
|
65
|
+
from: "#F2F5F8",
|
|
66
|
+
to: "#E2E7EE",
|
|
67
|
+
border: "#BFC6CE",
|
|
68
|
+
icon: "#6B7280",
|
|
69
|
+
},
|
|
70
|
+
dark: {
|
|
71
|
+
from: "#2A2D31",
|
|
72
|
+
to: "#363A40",
|
|
73
|
+
border: "#6B7280",
|
|
74
|
+
icon: "#F3F4F6",
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
GOLD: {
|
|
78
|
+
light: {
|
|
79
|
+
from: "#FFF0B8",
|
|
80
|
+
to: "#FFD875",
|
|
81
|
+
border: "#D9A441",
|
|
82
|
+
icon: "#DA9100",
|
|
83
|
+
},
|
|
84
|
+
dark: {
|
|
85
|
+
from: "#3D3520",
|
|
86
|
+
to: "#4A3D1A",
|
|
87
|
+
border: "#B8892E",
|
|
88
|
+
icon: "#FFE9B3",
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
PLATINUM: {
|
|
92
|
+
light: {
|
|
93
|
+
from: "#E4FFF7",
|
|
94
|
+
to: "#CDEFE5",
|
|
95
|
+
border: "#8FD7C6",
|
|
96
|
+
icon: "#059669",
|
|
97
|
+
},
|
|
98
|
+
dark: {
|
|
99
|
+
from: "#1A3D33",
|
|
100
|
+
to: "#1F3B31",
|
|
101
|
+
border: "#4DAE96",
|
|
102
|
+
icon: "#BAEBD9",
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
DIAMOND: {
|
|
106
|
+
light: {
|
|
107
|
+
from: "#E2F4FF",
|
|
108
|
+
to: "#C9E9FF",
|
|
109
|
+
border: "#89D0FF",
|
|
110
|
+
icon: "#0284C7",
|
|
111
|
+
},
|
|
112
|
+
dark: {
|
|
113
|
+
from: "#1A2F3D",
|
|
114
|
+
to: "#1E3545",
|
|
115
|
+
border: "#5AAFE0",
|
|
116
|
+
icon: "#5EC0F2",
|
|
117
|
+
},
|
|
118
|
+
},
|
|
50
119
|
};
|
|
120
|
+
const PRICE_TICK_STEPS = [
|
|
121
|
+
10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000, 25000, 50000, 100000,
|
|
122
|
+
250000, 500000, 1000000,
|
|
123
|
+
];
|
|
124
|
+
const MAX_MATERIALIZED_PRICE_TICKS = 20;
|
|
51
125
|
function shortenAddress(address) {
|
|
52
126
|
return `${address.slice(0, 6)}…${address.slice(-4)}`;
|
|
53
127
|
}
|
|
@@ -99,65 +173,130 @@ function normalizePositions(positions) {
|
|
|
99
173
|
});
|
|
100
174
|
return normalized;
|
|
101
175
|
}
|
|
176
|
+
function capRenderablePositions(positions) {
|
|
177
|
+
if (positions.length <= MAX_RENDERED_POSITIONS) {
|
|
178
|
+
return positions;
|
|
179
|
+
}
|
|
180
|
+
return positions
|
|
181
|
+
.map((position, index) => ({ position, index }))
|
|
182
|
+
.sort((a, b) => b.position.marketEndTimestampMs - a.position.marketEndTimestampMs ||
|
|
183
|
+
a.index - b.index)
|
|
184
|
+
.slice(0, MAX_RENDERED_POSITIONS)
|
|
185
|
+
.map(({ position }) => position);
|
|
186
|
+
}
|
|
102
187
|
function buildMainText(input) {
|
|
188
|
+
const handle = resolveDisplayName(input.realHandle ?? null);
|
|
189
|
+
if (handle)
|
|
190
|
+
return handle;
|
|
103
191
|
if (input.hideWallet)
|
|
104
192
|
return "A Signals trader";
|
|
105
193
|
return resolveDisplayName(input.displayName) ?? shortenAddress(input.address);
|
|
106
194
|
}
|
|
195
|
+
function formatCallCopy(totalCalls) {
|
|
196
|
+
return `${totalCalls} BTC range ${totalCalls === 1 ? "call" : "calls"}`;
|
|
197
|
+
}
|
|
107
198
|
function buildSubline(input) {
|
|
108
|
-
const callCopy =
|
|
199
|
+
const callCopy = formatCallCopy(input.totalCalls);
|
|
109
200
|
if (input.hideWallet)
|
|
110
|
-
return callCopy
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
parts.push(shortenAddress(input.address));
|
|
201
|
+
return `${callCopy} on Signals`;
|
|
202
|
+
if (resolveDisplayName(input.realHandle ?? null) !== null) {
|
|
203
|
+
return `${shortenAddress(input.address)} with ${callCopy}`;
|
|
114
204
|
}
|
|
115
205
|
if (input.twitter) {
|
|
116
|
-
|
|
206
|
+
return `@${input.twitter} with ${callCopy}`;
|
|
207
|
+
}
|
|
208
|
+
return `${callCopy} on Signals`;
|
|
209
|
+
}
|
|
210
|
+
function buildPriceTickCandidate(min, max, step) {
|
|
211
|
+
const alignedMin = Math.floor(min / step) * step;
|
|
212
|
+
const alignedMax = Math.ceil(max / step) * step;
|
|
213
|
+
const count = Math.floor((alignedMax - alignedMin) / step) + 1;
|
|
214
|
+
const ticks = [];
|
|
215
|
+
if (count > MAX_MATERIALIZED_PRICE_TICKS) {
|
|
216
|
+
return {
|
|
217
|
+
min: alignedMin,
|
|
218
|
+
max: alignedMax,
|
|
219
|
+
step,
|
|
220
|
+
count,
|
|
221
|
+
ticks,
|
|
222
|
+
};
|
|
117
223
|
}
|
|
118
|
-
|
|
119
|
-
|
|
224
|
+
for (let tick = alignedMin; tick <= alignedMax + step / 2; tick += step) {
|
|
225
|
+
ticks.push(tick);
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
min: alignedMin,
|
|
229
|
+
max: alignedMax,
|
|
230
|
+
step,
|
|
231
|
+
count,
|
|
232
|
+
ticks,
|
|
233
|
+
};
|
|
120
234
|
}
|
|
121
235
|
function getPriceTickStep(span) {
|
|
122
236
|
const target = span / 4;
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
1000,
|
|
126
|
-
2500,
|
|
127
|
-
5000,
|
|
128
|
-
10000,
|
|
129
|
-
25000,
|
|
130
|
-
50000,
|
|
131
|
-
100000,
|
|
132
|
-
250000,
|
|
133
|
-
500000,
|
|
134
|
-
1000000,
|
|
135
|
-
];
|
|
136
|
-
return candidates.find((step) => step >= target) ?? 1000000;
|
|
237
|
+
return (PRICE_TICK_STEPS.find((step) => step >= target) ??
|
|
238
|
+
PRICE_TICK_STEPS[PRICE_TICK_STEPS.length - 1]);
|
|
137
239
|
}
|
|
138
240
|
function getExpandedPriceBounds(min, max) {
|
|
139
241
|
const span = max - min;
|
|
140
|
-
const
|
|
242
|
+
const desiredStep = getPriceTickStep(span);
|
|
243
|
+
const candidates = PRICE_TICK_STEPS.map((step) => buildPriceTickCandidate(min, max, step));
|
|
244
|
+
const validCandidates = candidates.filter((candidate) => candidate.ticks.length > 0 &&
|
|
245
|
+
candidate.count >= 4 &&
|
|
246
|
+
candidate.count <= 5);
|
|
247
|
+
const fallbackCandidates = candidates.filter((candidate) => candidate.ticks.length > 0);
|
|
248
|
+
const [selected] = (validCandidates.length > 0
|
|
249
|
+
? validCandidates
|
|
250
|
+
: fallbackCandidates.length > 0
|
|
251
|
+
? fallbackCandidates
|
|
252
|
+
: candidates)
|
|
253
|
+
.slice()
|
|
254
|
+
.sort((a, b) => {
|
|
255
|
+
const aCountPenalty = a.count >= 4 && a.count <= 5
|
|
256
|
+
? 0
|
|
257
|
+
: Math.abs(a.count - 5) * 1000000;
|
|
258
|
+
const bCountPenalty = b.count >= 4 && b.count <= 5
|
|
259
|
+
? 0
|
|
260
|
+
: Math.abs(b.count - 5) * 1000000;
|
|
261
|
+
return (aCountPenalty +
|
|
262
|
+
Math.abs(a.step - desiredStep) -
|
|
263
|
+
(bCountPenalty + Math.abs(b.step - desiredStep)));
|
|
264
|
+
});
|
|
141
265
|
return {
|
|
142
|
-
min:
|
|
143
|
-
max:
|
|
144
|
-
step,
|
|
266
|
+
min: selected.min,
|
|
267
|
+
max: selected.max,
|
|
268
|
+
step: selected.step,
|
|
145
269
|
};
|
|
146
270
|
}
|
|
147
271
|
function getPriceTicks(min, max, step) {
|
|
272
|
+
const count = Math.floor((max - min) / step) + 1;
|
|
148
273
|
const ticks = [];
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
274
|
+
if (count <= MAX_MATERIALIZED_PRICE_TICKS) {
|
|
275
|
+
for (let tick = min; tick <= max + step / 2; tick += step) {
|
|
276
|
+
ticks.push(tick);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (count >= 4 && count <= 5 && ticks.length > 0) {
|
|
280
|
+
return { ticks, step };
|
|
153
281
|
}
|
|
154
|
-
|
|
282
|
+
const fallbackCount = count < 4 ? 4 : 5;
|
|
283
|
+
const fallbackStep = (max - min) / (fallbackCount - 1);
|
|
284
|
+
return {
|
|
285
|
+
ticks: Array.from({ length: fallbackCount }, (_, index) => Math.round(min + fallbackStep * index)),
|
|
286
|
+
step: fallbackStep,
|
|
287
|
+
};
|
|
155
288
|
}
|
|
156
|
-
function formatAxisLabel(value) {
|
|
289
|
+
function formatAxisLabel(value, step) {
|
|
290
|
+
if (step < 100) {
|
|
291
|
+
return `$${Math.round(value).toLocaleString("en-US")}`;
|
|
292
|
+
}
|
|
157
293
|
if (Math.abs(value) >= 1000) {
|
|
294
|
+
if (step >= 100 && step < 1000) {
|
|
295
|
+
return `$${(value / 1000).toFixed(1)}k`;
|
|
296
|
+
}
|
|
158
297
|
return `$${Math.round(value / 1000)}k`;
|
|
159
298
|
}
|
|
160
|
-
return `$${Math.round(value)}`;
|
|
299
|
+
return `$${Math.round(value).toLocaleString("en-US")}`;
|
|
161
300
|
}
|
|
162
301
|
function computePriceBounds(btcHistory, positions) {
|
|
163
302
|
let rawMin = Infinity;
|
|
@@ -229,9 +368,239 @@ function buildHistoryPath(btcHistory, scaleX, scaleY) {
|
|
|
229
368
|
})
|
|
230
369
|
.join(" ");
|
|
231
370
|
}
|
|
371
|
+
function clamp(value, min, max) {
|
|
372
|
+
return Math.min(Math.max(value, min), max);
|
|
373
|
+
}
|
|
374
|
+
function formatTierName(tier) {
|
|
375
|
+
return `${tier[0]}${tier.slice(1).toLowerCase()}`;
|
|
376
|
+
}
|
|
377
|
+
function buildTierChip(tier, themeName, theme) {
|
|
378
|
+
const tierTheme = TIER_CHIP_THEMES[tier][themeName];
|
|
379
|
+
return (0, h_1.h)("div", {
|
|
380
|
+
style: {
|
|
381
|
+
display: "flex",
|
|
382
|
+
alignItems: "center",
|
|
383
|
+
gap: 8,
|
|
384
|
+
padding: "8px 14px",
|
|
385
|
+
borderRadius: 999,
|
|
386
|
+
border: `1px solid ${tierTheme.border}`,
|
|
387
|
+
background: `linear-gradient(135deg, ${tierTheme.from}, ${tierTheme.to})`,
|
|
388
|
+
color: theme.ink,
|
|
389
|
+
fontSize: 18,
|
|
390
|
+
fontWeight: 700,
|
|
391
|
+
lineHeight: 1,
|
|
392
|
+
flexShrink: 0,
|
|
393
|
+
},
|
|
394
|
+
}, (0, h_1.h)("svg", {
|
|
395
|
+
width: 18,
|
|
396
|
+
height: 18,
|
|
397
|
+
viewBox: "0 0 16 16",
|
|
398
|
+
style: { display: "flex", flexShrink: 0 },
|
|
399
|
+
}, (0, h_1.h)("polygon", {
|
|
400
|
+
points: "8 1 15 8 8 15 1 8",
|
|
401
|
+
fill: tierTheme.icon,
|
|
402
|
+
})), (0, h_1.h)("div", { style: { display: "flex" } }, formatTierName(tier)));
|
|
403
|
+
}
|
|
404
|
+
function buildAddressAvatar(address, themeName, theme) {
|
|
405
|
+
const avatar = (0, avatar_1.getAvatarData)(address, themeName);
|
|
406
|
+
return (0, h_1.h)("div", {
|
|
407
|
+
style: {
|
|
408
|
+
width: AVATAR_SIZE,
|
|
409
|
+
height: AVATAR_SIZE,
|
|
410
|
+
display: "flex",
|
|
411
|
+
flexDirection: "column",
|
|
412
|
+
overflow: "hidden",
|
|
413
|
+
borderRadius: 22,
|
|
414
|
+
border: `1px solid ${theme.border}`,
|
|
415
|
+
background: avatar.background,
|
|
416
|
+
flexShrink: 0,
|
|
417
|
+
},
|
|
418
|
+
}, ...avatar.pattern.map((row, y) => (0, h_1.h)("div", {
|
|
419
|
+
style: {
|
|
420
|
+
display: "flex",
|
|
421
|
+
width: "100%",
|
|
422
|
+
height: "20%",
|
|
423
|
+
},
|
|
424
|
+
}, ...row.map((isFilled, x) => (0, h_1.h)("div", {
|
|
425
|
+
style: {
|
|
426
|
+
width: "20%",
|
|
427
|
+
height: "100%",
|
|
428
|
+
backgroundColor: isFilled
|
|
429
|
+
? (y + x) % 2 === 0
|
|
430
|
+
? avatar.primary
|
|
431
|
+
: avatar.secondary
|
|
432
|
+
: "transparent",
|
|
433
|
+
},
|
|
434
|
+
})))));
|
|
435
|
+
}
|
|
436
|
+
function buildGenericAvatar(theme) {
|
|
437
|
+
return (0, h_1.h)("div", {
|
|
438
|
+
style: {
|
|
439
|
+
width: AVATAR_SIZE,
|
|
440
|
+
height: AVATAR_SIZE,
|
|
441
|
+
display: "flex",
|
|
442
|
+
alignItems: "center",
|
|
443
|
+
justifyContent: "center",
|
|
444
|
+
overflow: "hidden",
|
|
445
|
+
borderRadius: 22,
|
|
446
|
+
border: `1px solid ${theme.border}`,
|
|
447
|
+
background: theme.primary,
|
|
448
|
+
flexShrink: 0,
|
|
449
|
+
},
|
|
450
|
+
}, (0, h_1.h)("div", {
|
|
451
|
+
style: {
|
|
452
|
+
width: 58,
|
|
453
|
+
height: 58,
|
|
454
|
+
display: "flex",
|
|
455
|
+
alignItems: "center",
|
|
456
|
+
justifyContent: "center",
|
|
457
|
+
borderRadius: 999,
|
|
458
|
+
background: theme.card,
|
|
459
|
+
},
|
|
460
|
+
}, (0, h_1.h)("img", {
|
|
461
|
+
src: assets_1.SIGNALS_LOGO_URI,
|
|
462
|
+
width: 34,
|
|
463
|
+
height: 32,
|
|
464
|
+
style: { display: "flex" },
|
|
465
|
+
})));
|
|
466
|
+
}
|
|
467
|
+
function buildAvatar(input, theme) {
|
|
468
|
+
if (input.hideWallet) {
|
|
469
|
+
return buildGenericAvatar(theme);
|
|
470
|
+
}
|
|
471
|
+
return buildAddressAvatar(input.address, input.theme, theme);
|
|
472
|
+
}
|
|
473
|
+
function buildBrandWordmark(theme) {
|
|
474
|
+
return (0, h_1.h)("div", {
|
|
475
|
+
style: {
|
|
476
|
+
display: "flex",
|
|
477
|
+
alignItems: "center",
|
|
478
|
+
gap: 10,
|
|
479
|
+
flexShrink: 0,
|
|
480
|
+
},
|
|
481
|
+
}, (0, h_1.h)("img", {
|
|
482
|
+
src: assets_1.SIGNALS_LOGO_URI,
|
|
483
|
+
width: 34,
|
|
484
|
+
height: 32,
|
|
485
|
+
style: { display: "flex" },
|
|
486
|
+
}), (0, h_1.h)("div", {
|
|
487
|
+
style: {
|
|
488
|
+
display: "flex",
|
|
489
|
+
fontSize: 28,
|
|
490
|
+
fontWeight: 700,
|
|
491
|
+
color: theme.primary,
|
|
492
|
+
},
|
|
493
|
+
}, "Signals"));
|
|
494
|
+
}
|
|
495
|
+
function buildHeader(input, theme, mainText, subline) {
|
|
496
|
+
return (0, h_1.h)("div", {
|
|
497
|
+
style: {
|
|
498
|
+
display: "flex",
|
|
499
|
+
alignItems: "center",
|
|
500
|
+
justifyContent: "space-between",
|
|
501
|
+
gap: 24,
|
|
502
|
+
minHeight: AVATAR_SIZE,
|
|
503
|
+
},
|
|
504
|
+
}, (0, h_1.h)("div", {
|
|
505
|
+
style: {
|
|
506
|
+
flex: 1,
|
|
507
|
+
minWidth: 0,
|
|
508
|
+
display: "flex",
|
|
509
|
+
alignItems: "center",
|
|
510
|
+
gap: 18,
|
|
511
|
+
},
|
|
512
|
+
}, buildAvatar(input, theme), (0, h_1.h)("div", {
|
|
513
|
+
style: {
|
|
514
|
+
flex: 1,
|
|
515
|
+
minWidth: 0,
|
|
516
|
+
display: "flex",
|
|
517
|
+
flexDirection: "column",
|
|
518
|
+
},
|
|
519
|
+
}, (0, h_1.h)("div", {
|
|
520
|
+
style: {
|
|
521
|
+
display: "flex",
|
|
522
|
+
alignItems: "center",
|
|
523
|
+
gap: 14,
|
|
524
|
+
minWidth: 0,
|
|
525
|
+
},
|
|
526
|
+
}, (0, h_1.h)("div", {
|
|
527
|
+
style: {
|
|
528
|
+
display: "flex",
|
|
529
|
+
fontSize: 52,
|
|
530
|
+
lineHeight: 1.05,
|
|
531
|
+
fontWeight: 700,
|
|
532
|
+
color: theme.ink,
|
|
533
|
+
maxWidth: 620,
|
|
534
|
+
overflow: "hidden",
|
|
535
|
+
textOverflow: "ellipsis",
|
|
536
|
+
whiteSpace: "nowrap",
|
|
537
|
+
},
|
|
538
|
+
}, mainText), buildTierChip(input.tier, input.theme, theme)), (0, h_1.h)("div", {
|
|
539
|
+
style: {
|
|
540
|
+
display: "flex",
|
|
541
|
+
fontSize: 20,
|
|
542
|
+
fontWeight: 500,
|
|
543
|
+
color: theme.inkMuted,
|
|
544
|
+
marginTop: 8,
|
|
545
|
+
overflow: "hidden",
|
|
546
|
+
textOverflow: "ellipsis",
|
|
547
|
+
whiteSpace: "nowrap",
|
|
548
|
+
},
|
|
549
|
+
}, subline))), buildBrandWordmark(theme));
|
|
550
|
+
}
|
|
551
|
+
function buildStatItem(label, value, valueColor, theme) {
|
|
552
|
+
return (0, h_1.h)("div", {
|
|
553
|
+
style: {
|
|
554
|
+
flex: 1,
|
|
555
|
+
minWidth: 0,
|
|
556
|
+
display: "flex",
|
|
557
|
+
flexDirection: "column",
|
|
558
|
+
},
|
|
559
|
+
}, (0, h_1.h)("div", {
|
|
560
|
+
style: {
|
|
561
|
+
display: "flex",
|
|
562
|
+
fontSize: 14,
|
|
563
|
+
fontWeight: 500,
|
|
564
|
+
color: theme.inkMuted,
|
|
565
|
+
marginBottom: 8,
|
|
566
|
+
},
|
|
567
|
+
}, label), (0, h_1.h)("div", {
|
|
568
|
+
style: {
|
|
569
|
+
display: "flex",
|
|
570
|
+
fontSize: 30,
|
|
571
|
+
lineHeight: 1.1,
|
|
572
|
+
fontWeight: 700,
|
|
573
|
+
color: valueColor,
|
|
574
|
+
},
|
|
575
|
+
}, value));
|
|
576
|
+
}
|
|
577
|
+
function buildStatSeparator(theme) {
|
|
578
|
+
return (0, h_1.h)("div", {
|
|
579
|
+
style: {
|
|
580
|
+
width: 1,
|
|
581
|
+
height: 28,
|
|
582
|
+
display: "flex",
|
|
583
|
+
background: theme.border,
|
|
584
|
+
margin: "0 18px",
|
|
585
|
+
flexShrink: 0,
|
|
586
|
+
},
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
function buildStatRow(input, theme, pnlValue, roiValue, bestMultValue) {
|
|
590
|
+
return (0, h_1.h)("div", {
|
|
591
|
+
style: {
|
|
592
|
+
display: "flex",
|
|
593
|
+
alignItems: "center",
|
|
594
|
+
marginTop: 20,
|
|
595
|
+
padding: "0 4px",
|
|
596
|
+
},
|
|
597
|
+
}, buildStatItem("P&L", pnlValue, input.hideAmounts
|
|
598
|
+
? theme.inkMuted
|
|
599
|
+
: getSignedValueColor(input.totalPnl, theme), theme), buildStatSeparator(theme), buildStatItem("ROI", roiValue, getSignedValueColor(input.roi, theme), theme), buildStatSeparator(theme), buildStatItem("Best call", bestMultValue, theme.primary, theme));
|
|
600
|
+
}
|
|
232
601
|
function buildChartArea(theme, btcHistory, positions) {
|
|
233
602
|
const normalizedPositions = normalizePositions(positions);
|
|
234
|
-
const renderablePositions = normalizedPositions
|
|
603
|
+
const renderablePositions = capRenderablePositions(normalizedPositions);
|
|
235
604
|
const sortedHistory = [...btcHistory].sort((a, b) => a.t - b.t);
|
|
236
605
|
if (btcHistory.length === 0 && renderablePositions.length === 0) {
|
|
237
606
|
return (0, h_1.h)("div", {
|
|
@@ -281,12 +650,40 @@ function buildChartArea(theme, btcHistory, positions) {
|
|
|
281
650
|
return PLOT_TOP + plotHeight / 2;
|
|
282
651
|
}
|
|
283
652
|
return (PLOT_TOP +
|
|
284
|
-
(1 -
|
|
285
|
-
(value - priceBounds.min) / (priceBounds.max - priceBounds.min)) *
|
|
653
|
+
(1 - (value - priceBounds.min) / (priceBounds.max - priceBounds.min)) *
|
|
286
654
|
plotHeight);
|
|
287
655
|
};
|
|
288
656
|
const priceTicks = getPriceTicks(priceBounds.min, priceBounds.max, priceBounds.step);
|
|
289
657
|
const hasOpenPositions = renderablePositions.some((position) => position.status === "OPEN");
|
|
658
|
+
const latestSettlement = renderablePositions
|
|
659
|
+
.filter((position) => (position.status === "WIN" || position.status === "LOSS") &&
|
|
660
|
+
position.settlementPrice != null)
|
|
661
|
+
.sort((a, b) => b.marketEndTimestampMs - a.marketEndTimestampMs)[0];
|
|
662
|
+
const groupSizes = new Map();
|
|
663
|
+
const groupOrdinals = new Map();
|
|
664
|
+
renderablePositions.forEach((position) => {
|
|
665
|
+
const timestamp = position.marketEndTimestampMs;
|
|
666
|
+
const ordinal = groupSizes.get(timestamp) ?? 0;
|
|
667
|
+
groupOrdinals.set(position, ordinal);
|
|
668
|
+
groupSizes.set(timestamp, ordinal + 1);
|
|
669
|
+
});
|
|
670
|
+
const dayWidth = timeMin === timeMax ? 0 : (DAY_MS / (timeMax - timeMin)) * plotWidth;
|
|
671
|
+
const getBandX = (position) => {
|
|
672
|
+
const groupSize = groupSizes.get(position.marketEndTimestampMs) ?? 1;
|
|
673
|
+
const ordinal = groupOrdinals.get(position) ?? 0;
|
|
674
|
+
const center = scaleX(position.marketEndTimestampMs);
|
|
675
|
+
const spreadWidth = groupSize <= 1
|
|
676
|
+
? 0
|
|
677
|
+
: timeMin === timeMax
|
|
678
|
+
? plotWidth / 4
|
|
679
|
+
: Math.max(dayWidth, BAND_WIDTH * (groupSize + 1));
|
|
680
|
+
const offset = groupSize <= 1
|
|
681
|
+
? 0
|
|
682
|
+
: (ordinal - (groupSize - 1) / 2) * (spreadWidth / groupSize);
|
|
683
|
+
const rawX = center + offset - BAND_WIDTH / 2;
|
|
684
|
+
return clamp(rawX, PLOT_LEFT, PLOT_LEFT + plotWidth - BAND_WIDTH);
|
|
685
|
+
};
|
|
686
|
+
const getBandCenterX = (position) => getBandX(position) + BAND_WIDTH / 2;
|
|
290
687
|
const svgChildren = [];
|
|
291
688
|
if (hasOpenPositions) {
|
|
292
689
|
svgChildren.push((0, h_1.h)("defs", null, (0, h_1.h)("pattern", {
|
|
@@ -300,7 +697,7 @@ function buildChartArea(theme, btcHistory, positions) {
|
|
|
300
697
|
strokeWidth: 2,
|
|
301
698
|
}))));
|
|
302
699
|
}
|
|
303
|
-
priceTicks.forEach((tick) => {
|
|
700
|
+
priceTicks.ticks.forEach((tick) => {
|
|
304
701
|
const y = scaleY(tick);
|
|
305
702
|
svgChildren.push((0, h_1.h)("line", {
|
|
306
703
|
x1: PLOT_LEFT,
|
|
@@ -312,10 +709,23 @@ function buildChartArea(theme, btcHistory, positions) {
|
|
|
312
709
|
strokeWidth: 1,
|
|
313
710
|
}));
|
|
314
711
|
});
|
|
712
|
+
if (latestSettlement) {
|
|
713
|
+
const y = scaleY(latestSettlement.settlementPrice);
|
|
714
|
+
svgChildren.push((0, h_1.h)("line", {
|
|
715
|
+
x1: PLOT_LEFT,
|
|
716
|
+
x2: PLOT_LEFT + plotWidth,
|
|
717
|
+
y1: y,
|
|
718
|
+
y2: y,
|
|
719
|
+
stroke: theme.bitcoin,
|
|
720
|
+
strokeDasharray: "4 5",
|
|
721
|
+
strokeWidth: 1.5,
|
|
722
|
+
strokeOpacity: 0.75,
|
|
723
|
+
}));
|
|
724
|
+
}
|
|
315
725
|
renderablePositions.forEach((position) => {
|
|
316
726
|
const topPrice = Math.max(position.lowerPrice, position.upperPrice);
|
|
317
727
|
const bottomPrice = Math.min(position.lowerPrice, position.upperPrice);
|
|
318
|
-
const x =
|
|
728
|
+
const x = getBandX(position);
|
|
319
729
|
const top = scaleY(topPrice);
|
|
320
730
|
const bottom = scaleY(bottomPrice);
|
|
321
731
|
const height = Math.max(bottom - top, 6);
|
|
@@ -327,13 +737,22 @@ function buildChartArea(theme, btcHistory, positions) {
|
|
|
327
737
|
height,
|
|
328
738
|
rx: 6,
|
|
329
739
|
fill: "url(#profile-open-band)",
|
|
740
|
+
opacity: 0.4,
|
|
330
741
|
stroke: theme.bitcoin,
|
|
331
742
|
strokeWidth: 2,
|
|
332
743
|
}));
|
|
333
744
|
return;
|
|
334
745
|
}
|
|
335
|
-
const fill = position.status === "WIN"
|
|
336
|
-
|
|
746
|
+
const fill = position.status === "WIN"
|
|
747
|
+
? theme.primary
|
|
748
|
+
: position.status === "LOSS"
|
|
749
|
+
? theme.negative
|
|
750
|
+
: theme.inkMuted;
|
|
751
|
+
const fillOpacity = position.status === "WIN"
|
|
752
|
+
? 0.45
|
|
753
|
+
: position.status === "LOSS"
|
|
754
|
+
? 0.28
|
|
755
|
+
: 0.3;
|
|
337
756
|
svgChildren.push((0, h_1.h)("rect", {
|
|
338
757
|
x,
|
|
339
758
|
y: top,
|
|
@@ -343,7 +762,7 @@ function buildChartArea(theme, btcHistory, positions) {
|
|
|
343
762
|
fill,
|
|
344
763
|
fillOpacity,
|
|
345
764
|
stroke: fill,
|
|
346
|
-
strokeOpacity: 0.9,
|
|
765
|
+
strokeOpacity: position.status === "CLOSED" ? 0.55 : 0.9,
|
|
347
766
|
}));
|
|
348
767
|
});
|
|
349
768
|
if (sortedHistory.length === 1) {
|
|
@@ -368,14 +787,14 @@ function buildChartArea(theme, btcHistory, positions) {
|
|
|
368
787
|
if ((position.status === "WIN" || position.status === "LOSS") &&
|
|
369
788
|
position.settlementPrice != null) {
|
|
370
789
|
svgChildren.push((0, h_1.h)("circle", {
|
|
371
|
-
cx:
|
|
790
|
+
cx: getBandCenterX(position),
|
|
372
791
|
cy: scaleY(position.settlementPrice),
|
|
373
792
|
r: 4.5,
|
|
374
793
|
fill: position.status === "WIN" ? theme.primary : theme.negative,
|
|
375
794
|
}));
|
|
376
795
|
}
|
|
377
796
|
});
|
|
378
|
-
const labelNodes = priceTicks.map((tick) => (0, h_1.h)("div", {
|
|
797
|
+
const labelNodes = priceTicks.ticks.map((tick) => (0, h_1.h)("div", {
|
|
379
798
|
style: {
|
|
380
799
|
position: "absolute",
|
|
381
800
|
left: 12,
|
|
@@ -387,7 +806,26 @@ function buildChartArea(theme, btcHistory, positions) {
|
|
|
387
806
|
background: theme.card,
|
|
388
807
|
padding: "0 4px",
|
|
389
808
|
},
|
|
390
|
-
}, formatAxisLabel(tick)));
|
|
809
|
+
}, formatAxisLabel(tick, priceTicks.step)));
|
|
810
|
+
const settlementLabel = latestSettlement == null
|
|
811
|
+
? []
|
|
812
|
+
: [
|
|
813
|
+
(0, h_1.h)("div", {
|
|
814
|
+
style: {
|
|
815
|
+
position: "absolute",
|
|
816
|
+
left: PLOT_LEFT + plotWidth - 64,
|
|
817
|
+
top: scaleY(latestSettlement.settlementPrice) - 14,
|
|
818
|
+
display: "flex",
|
|
819
|
+
fontSize: 14,
|
|
820
|
+
fontWeight: 700,
|
|
821
|
+
color: theme.bitcoin,
|
|
822
|
+
background: theme.card,
|
|
823
|
+
border: `1px solid ${theme.bitcoin}`,
|
|
824
|
+
borderRadius: 999,
|
|
825
|
+
padding: "4px 10px",
|
|
826
|
+
},
|
|
827
|
+
}, formatAxisLabel(latestSettlement.settlementPrice, priceTicks.step)),
|
|
828
|
+
];
|
|
391
829
|
return (0, h_1.h)("div", {
|
|
392
830
|
style: {
|
|
393
831
|
width: CHART_WIDTH,
|
|
@@ -395,9 +833,6 @@ function buildChartArea(theme, btcHistory, positions) {
|
|
|
395
833
|
display: "flex",
|
|
396
834
|
position: "relative",
|
|
397
835
|
overflow: "hidden",
|
|
398
|
-
background: theme.neutralBg,
|
|
399
|
-
border: `1px solid ${theme.border}`,
|
|
400
|
-
borderRadius: 20,
|
|
401
836
|
},
|
|
402
837
|
}, (0, h_1.h)("svg", {
|
|
403
838
|
width: CHART_WIDTH,
|
|
@@ -409,43 +844,15 @@ function buildChartArea(theme, btcHistory, positions) {
|
|
|
409
844
|
display: "flex",
|
|
410
845
|
pointerEvents: "none",
|
|
411
846
|
},
|
|
412
|
-
}, ...svgChildren), ...labelNodes);
|
|
413
|
-
}
|
|
414
|
-
function buildStatCard(label, value, valueColor, theme) {
|
|
415
|
-
return (0, h_1.h)("div", {
|
|
416
|
-
style: {
|
|
417
|
-
flex: 1,
|
|
418
|
-
display: "flex",
|
|
419
|
-
flexDirection: "column",
|
|
420
|
-
padding: "16px 18px",
|
|
421
|
-
borderRadius: 18,
|
|
422
|
-
border: `1px solid ${theme.border}`,
|
|
423
|
-
background: theme.neutralBg,
|
|
424
|
-
},
|
|
425
|
-
}, (0, h_1.h)("div", {
|
|
426
|
-
style: {
|
|
427
|
-
display: "flex",
|
|
428
|
-
fontSize: 14,
|
|
429
|
-
fontWeight: 500,
|
|
430
|
-
color: theme.inkMuted,
|
|
431
|
-
marginBottom: 10,
|
|
432
|
-
},
|
|
433
|
-
}, label), (0, h_1.h)("div", {
|
|
434
|
-
style: {
|
|
435
|
-
display: "flex",
|
|
436
|
-
fontSize: 28,
|
|
437
|
-
lineHeight: 1.1,
|
|
438
|
-
fontWeight: 700,
|
|
439
|
-
color: valueColor,
|
|
440
|
-
},
|
|
441
|
-
}, value));
|
|
847
|
+
}, ...svgChildren), ...labelNodes, ...settlementLabel);
|
|
442
848
|
}
|
|
443
849
|
function buildProfileVNode(input) {
|
|
444
850
|
const theme = OG_THEMES[input.theme];
|
|
445
|
-
const accent = TIER_ACCENT_COLORS[input.tier];
|
|
446
851
|
const mainText = buildMainText(input);
|
|
447
852
|
const subline = buildSubline(input);
|
|
448
|
-
const pnlValue = input.hideAmounts
|
|
853
|
+
const pnlValue = input.hideAmounts
|
|
854
|
+
? "••••"
|
|
855
|
+
: formatProfilePnl(input.totalPnl);
|
|
449
856
|
const roiValue = formatProfileRoi(input.roi);
|
|
450
857
|
const bestMultValue = formatBestMult(input.bestMult);
|
|
451
858
|
return (0, h_1.h)("div", {
|
|
@@ -467,79 +874,18 @@ function buildProfileVNode(input) {
|
|
|
467
874
|
border: `1px solid ${theme.border}`,
|
|
468
875
|
borderRadius: 24,
|
|
469
876
|
},
|
|
470
|
-
}, (0, h_1.h)("div", {
|
|
471
|
-
style: {
|
|
472
|
-
display: "flex",
|
|
473
|
-
alignItems: "center",
|
|
474
|
-
justifyContent: "space-between",
|
|
475
|
-
},
|
|
476
|
-
}, (0, h_1.h)("div", {
|
|
477
|
-
style: {
|
|
478
|
-
display: "flex",
|
|
479
|
-
alignItems: "center",
|
|
480
|
-
gap: 10,
|
|
481
|
-
},
|
|
482
|
-
}, (0, h_1.h)("img", {
|
|
483
|
-
src: assets_1.SIGNALS_LOGO_URI,
|
|
484
|
-
width: 34,
|
|
485
|
-
height: 32,
|
|
486
|
-
style: { display: "flex" },
|
|
487
|
-
}), (0, h_1.h)("div", {
|
|
488
|
-
style: {
|
|
489
|
-
display: "flex",
|
|
490
|
-
fontSize: 28,
|
|
491
|
-
fontWeight: 700,
|
|
492
|
-
color: theme.primary,
|
|
493
|
-
},
|
|
494
|
-
}, "Signals")), (0, h_1.h)("div", {
|
|
495
|
-
style: {
|
|
496
|
-
display: "flex",
|
|
497
|
-
fontSize: 18,
|
|
498
|
-
fontWeight: 700,
|
|
499
|
-
letterSpacing: "0.04em",
|
|
500
|
-
textTransform: "uppercase",
|
|
501
|
-
color: accent,
|
|
502
|
-
},
|
|
503
|
-
}, input.tier)), (0, h_1.h)("div", {
|
|
877
|
+
}, buildHeader(input, theme, mainText, subline), (0, h_1.h)("div", {
|
|
504
878
|
style: {
|
|
505
879
|
flex: 1,
|
|
506
880
|
display: "flex",
|
|
507
881
|
flexDirection: "column",
|
|
508
|
-
marginTop:
|
|
509
|
-
},
|
|
510
|
-
}, (0, h_1.h)("div", {
|
|
511
|
-
style: {
|
|
512
|
-
display: "flex",
|
|
513
|
-
flexDirection: "column",
|
|
882
|
+
marginTop: 22,
|
|
514
883
|
},
|
|
515
884
|
}, (0, h_1.h)("div", {
|
|
516
885
|
style: {
|
|
517
886
|
display: "flex",
|
|
518
|
-
fontSize: 52,
|
|
519
|
-
lineHeight: 1.05,
|
|
520
|
-
fontWeight: 700,
|
|
521
|
-
color: theme.ink,
|
|
522
|
-
marginBottom: 10,
|
|
523
|
-
},
|
|
524
|
-
}, mainText), (0, h_1.h)("div", {
|
|
525
|
-
style: {
|
|
526
|
-
display: "flex",
|
|
527
|
-
fontSize: 20,
|
|
528
|
-
fontWeight: 500,
|
|
529
|
-
color: theme.inkMuted,
|
|
530
|
-
},
|
|
531
|
-
}, subline)), (0, h_1.h)("div", {
|
|
532
|
-
style: {
|
|
533
|
-
display: "flex",
|
|
534
|
-
marginTop: 24,
|
|
535
|
-
},
|
|
536
|
-
}, buildChartArea(theme, input.btcHistory, input.positions)), (0, h_1.h)("div", {
|
|
537
|
-
style: {
|
|
538
|
-
display: "flex",
|
|
539
|
-
gap: 16,
|
|
540
|
-
marginTop: 20,
|
|
541
887
|
},
|
|
542
|
-
},
|
|
888
|
+
}, buildChartArea(theme, input.btcHistory, input.positions)), buildStatRow(input, theme, pnlValue, roiValue, bestMultValue), (0, h_1.h)("div", {
|
|
543
889
|
style: {
|
|
544
890
|
display: "flex",
|
|
545
891
|
justifyContent: "flex-end",
|
package/dist/share/types.d.ts
CHANGED
|
@@ -30,7 +30,22 @@ export interface ProfileChartPosition {
|
|
|
30
30
|
marketEndTimestampMs: number;
|
|
31
31
|
}
|
|
32
32
|
export interface ProfileImageInput {
|
|
33
|
+
/**
|
|
34
|
+
* User-facing display fallback used when `realHandle` is empty and
|
|
35
|
+
* `hideWallet` is false. This may include an address fallback supplied by
|
|
36
|
+
* legacy callers; it is never shown while `hideWallet` is true.
|
|
37
|
+
*/
|
|
33
38
|
displayName: string | null;
|
|
39
|
+
/**
|
|
40
|
+
* Real, human-friendly handle (e.g. Namoshi name) that is safe to show
|
|
41
|
+
* even when `hideWallet` is true.
|
|
42
|
+
*
|
|
43
|
+
* Pass null when no real handle exists. Do not pass a truncated address
|
|
44
|
+
* fallback here; that belongs in `displayName`. Optional for backward
|
|
45
|
+
* compatibility, so callers that omit it get the existing hideWallet
|
|
46
|
+
* fallback semantics.
|
|
47
|
+
*/
|
|
48
|
+
realHandle?: string | null;
|
|
34
49
|
address: string;
|
|
35
50
|
twitter: string | null;
|
|
36
51
|
tier: "BRONZE" | "SILVER" | "GOLD" | "PLATINUM" | "DIAMOND";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@signals-protocol/v1-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Signals v1 SDK for CLMSR market calculations and utilities",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -11,8 +11,6 @@
|
|
|
11
11
|
"test": "jest",
|
|
12
12
|
"test:cov": "jest --coverage",
|
|
13
13
|
"test:watch": "jest --watch",
|
|
14
|
-
"npm:auth": "node scripts/write-npmrc.js",
|
|
15
|
-
"publish:package": "NPM_CONFIG_USERCONFIG=.npmrc.auth npm publish",
|
|
16
14
|
"prepack": "yarn rebuild",
|
|
17
15
|
"lint": "eslint src/**/*.ts",
|
|
18
16
|
"format": "prettier --write src/**/*.ts",
|
|
@@ -38,6 +36,7 @@
|
|
|
38
36
|
"eslint": "^8.0.0",
|
|
39
37
|
"jest": "^29.5.0",
|
|
40
38
|
"prettier": "^3.0.0",
|
|
39
|
+
"semver": "^7.7.3",
|
|
41
40
|
"ts-jest": "^29.1.0",
|
|
42
41
|
"typedoc": "^0.28.10",
|
|
43
42
|
"typedoc-docusaurus-theme": "^1.4.2",
|
|
@@ -52,7 +51,7 @@
|
|
|
52
51
|
],
|
|
53
52
|
"repository": {
|
|
54
53
|
"type": "git",
|
|
55
|
-
"url": "git+https://github.com/signals-protocol/
|
|
54
|
+
"url": "git+https://github.com/signals-protocol/v1-sdk.git"
|
|
56
55
|
},
|
|
57
56
|
"exports": {
|
|
58
57
|
".": {
|