@luxfi/ui 6.1.2 → 7.0.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/accordion.cjs +213 -0
- package/dist/accordion.d.cts +74 -0
- package/dist/accordion.d.ts +74 -0
- package/dist/accordion.js +186 -0
- package/dist/alert.cjs +553 -0
- package/dist/alert.d.cts +30 -0
- package/dist/alert.d.ts +30 -0
- package/dist/alert.js +531 -0
- package/dist/avatar.cjs +149 -0
- package/dist/avatar.d.cts +25 -0
- package/dist/avatar.d.ts +25 -0
- package/dist/avatar.js +125 -0
- package/dist/badge.cjs +611 -0
- package/dist/badge.d.cts +23 -0
- package/dist/badge.d.ts +23 -0
- package/dist/badge.js +589 -0
- package/dist/bank.cjs +272 -0
- package/dist/bank.d.cts +76 -0
- package/dist/bank.d.ts +76 -0
- package/dist/bank.js +258 -0
- package/dist/button.cjs +689 -0
- package/dist/button.d.cts +52 -0
- package/dist/button.d.ts +52 -0
- package/dist/button.js +664 -0
- package/dist/checkbox.cjs +265 -0
- package/dist/checkbox.d.cts +31 -0
- package/dist/checkbox.d.ts +31 -0
- package/dist/checkbox.js +241 -0
- package/dist/close-button.cjs +73 -0
- package/dist/close-button.d.cts +9 -0
- package/dist/close-button.d.ts +9 -0
- package/dist/close-button.js +51 -0
- package/dist/collapsible.cjs +702 -0
- package/dist/collapsible.d.cts +23 -0
- package/dist/collapsible.d.ts +23 -0
- package/dist/collapsible.js +679 -0
- package/dist/color-mode.cjs +96 -0
- package/dist/color-mode.d.cts +25 -0
- package/dist/color-mode.d.ts +25 -0
- package/dist/color-mode.js +72 -0
- package/dist/dialog.cjs +279 -0
- package/dist/dialog.d.cts +66 -0
- package/dist/dialog.d.ts +66 -0
- package/dist/dialog.js +246 -0
- package/dist/drawer.cjs +207 -0
- package/dist/drawer.d.cts +36 -0
- package/dist/drawer.d.ts +36 -0
- package/dist/drawer.js +175 -0
- package/dist/empty-state.cjs +93 -0
- package/dist/empty-state.d.cts +13 -0
- package/dist/empty-state.d.ts +13 -0
- package/dist/empty-state.js +71 -0
- package/dist/expiration-selector.cjs +138 -0
- package/dist/expiration-selector.d.cts +16 -0
- package/dist/expiration-selector.d.ts +16 -0
- package/dist/expiration-selector.js +116 -0
- package/dist/field.cjs +183 -0
- package/dist/field.d.cts +24 -0
- package/dist/field.d.ts +24 -0
- package/dist/field.js +160 -0
- package/dist/greeks-display.cjs +114 -0
- package/dist/greeks-display.d.cts +14 -0
- package/dist/greeks-display.d.ts +14 -0
- package/dist/greeks-display.js +92 -0
- package/dist/heading.cjs +46 -0
- package/dist/heading.d.cts +10 -0
- package/dist/heading.d.ts +10 -0
- package/dist/heading.js +40 -0
- package/dist/icon-button.cjs +491 -0
- package/dist/icon-button.d.cts +33 -0
- package/dist/icon-button.d.ts +33 -0
- package/dist/icon-button.js +470 -0
- package/dist/image.cjs +572 -0
- package/dist/image.d.cts +57 -0
- package/dist/image.d.ts +57 -0
- package/dist/image.js +551 -0
- package/dist/index.cjs +7316 -0
- package/dist/index.d.cts +58 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.js +7137 -0
- package/dist/input-group.cjs +155 -0
- package/dist/input-group.d.cts +17 -0
- package/dist/input-group.d.ts +17 -0
- package/dist/input-group.js +133 -0
- package/dist/input.cjs +65 -0
- package/dist/input.d.cts +11 -0
- package/dist/input.d.ts +11 -0
- package/dist/input.js +59 -0
- package/dist/link.cjs +630 -0
- package/dist/link.d.cts +37 -0
- package/dist/link.d.ts +37 -0
- package/dist/link.js +606 -0
- package/dist/menu.cjs +305 -0
- package/dist/menu.d.cts +84 -0
- package/dist/menu.d.ts +84 -0
- package/dist/menu.js +269 -0
- package/dist/option-chain.cjs +221 -0
- package/dist/option-chain.d.cts +37 -0
- package/dist/option-chain.d.ts +37 -0
- package/dist/option-chain.js +199 -0
- package/dist/option-position.cjs +164 -0
- package/dist/option-position.d.cts +29 -0
- package/dist/option-position.d.ts +29 -0
- package/dist/option-position.js +142 -0
- package/dist/pin-input.cjs +182 -0
- package/dist/pin-input.d.cts +25 -0
- package/dist/pin-input.d.ts +25 -0
- package/dist/pin-input.js +160 -0
- package/dist/pnl-diagram.cjs +453 -0
- package/dist/pnl-diagram.d.cts +11 -0
- package/dist/pnl-diagram.d.ts +11 -0
- package/dist/pnl-diagram.js +431 -0
- package/dist/popover.cjs +327 -0
- package/dist/popover.d.cts +73 -0
- package/dist/popover.d.ts +73 -0
- package/dist/popover.js +294 -0
- package/dist/progress-circle.cjs +152 -0
- package/dist/progress-circle.d.cts +47 -0
- package/dist/progress-circle.d.ts +47 -0
- package/dist/progress-circle.js +128 -0
- package/dist/progress.cjs +117 -0
- package/dist/progress.d.cts +28 -0
- package/dist/progress.d.ts +28 -0
- package/dist/progress.js +94 -0
- package/dist/provider.cjs +62 -0
- package/dist/provider.d.cts +7 -0
- package/dist/provider.d.ts +7 -0
- package/dist/provider.js +40 -0
- package/dist/radio.cjs +177 -0
- package/dist/radio.d.cts +53 -0
- package/dist/radio.d.ts +53 -0
- package/dist/radio.js +153 -0
- package/dist/rating.cjs +80 -0
- package/dist/rating.d.cts +14 -0
- package/dist/rating.d.ts +14 -0
- package/dist/rating.js +58 -0
- package/dist/select.cjs +791 -0
- package/dist/select.d.cts +130 -0
- package/dist/select.d.ts +130 -0
- package/dist/select.js +757 -0
- package/dist/separator.cjs +57 -0
- package/dist/separator.d.cts +13 -0
- package/dist/separator.d.ts +13 -0
- package/dist/separator.js +51 -0
- package/dist/skeleton.cjs +370 -0
- package/dist/skeleton.d.cts +60 -0
- package/dist/skeleton.d.ts +60 -0
- package/dist/skeleton.js +346 -0
- package/dist/slider.cjs +138 -0
- package/dist/slider.d.cts +24 -0
- package/dist/slider.d.ts +24 -0
- package/dist/slider.js +115 -0
- package/dist/strategy-builder.cjs +373 -0
- package/dist/strategy-builder.d.cts +26 -0
- package/dist/strategy-builder.d.ts +26 -0
- package/dist/strategy-builder.js +351 -0
- package/dist/switch.cjs +163 -0
- package/dist/switch.d.cts +27 -0
- package/dist/switch.d.ts +27 -0
- package/dist/switch.js +140 -0
- package/dist/table.cjs +1044 -0
- package/dist/table.d.cts +107 -0
- package/dist/table.d.ts +107 -0
- package/dist/table.js +1013 -0
- package/dist/tabs.cjs +240 -0
- package/dist/tabs.d.cts +78 -0
- package/dist/tabs.d.ts +78 -0
- package/dist/tabs.js +213 -0
- package/dist/tag.cjs +651 -0
- package/dist/tag.d.cts +21 -0
- package/dist/tag.d.ts +21 -0
- package/dist/tag.js +628 -0
- package/dist/textarea.cjs +65 -0
- package/dist/textarea.d.cts +11 -0
- package/dist/textarea.d.ts +11 -0
- package/dist/textarea.js +59 -0
- package/dist/toaster.cjs +99 -0
- package/dist/toaster.d.cts +29 -0
- package/dist/toaster.d.ts +29 -0
- package/dist/toaster.js +96 -0
- package/dist/tooltip.cjs +171 -0
- package/dist/tooltip.d.cts +39 -0
- package/dist/tooltip.d.ts +39 -0
- package/dist/tooltip.js +148 -0
- package/dist/utils.cjs +11 -0
- package/dist/utils.d.cts +5 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +9 -0
- package/package.json +284 -85
- package/src/accordion.tsx +285 -0
- package/src/alert.tsx +221 -0
- package/src/avatar.tsx +174 -0
- package/src/badge.tsx +158 -0
- package/src/bank.tsx +325 -0
- package/src/button.tsx +411 -0
- package/src/checkbox.tsx +307 -0
- package/src/close-button.tsx +51 -0
- package/src/collapsible.tsx +126 -0
- package/src/color-mode.tsx +125 -0
- package/src/dialog.tsx +356 -0
- package/src/drawer.tsx +186 -0
- package/src/empty-state.tsx +97 -0
- package/src/expiration-selector.tsx +163 -0
- package/src/field.tsx +202 -0
- package/src/greeks-display.tsx +138 -0
- package/src/heading.tsx +55 -0
- package/src/icon-button.tsx +192 -0
- package/src/image.tsx +280 -0
- package/src/index.ts +268 -0
- package/src/input-group.tsx +159 -0
- package/src/input.tsx +60 -0
- package/src/link.tsx +326 -0
- package/src/menu.tsx +471 -0
- package/src/option-chain.tsx +261 -0
- package/src/option-position.tsx +200 -0
- package/src/pin-input.tsx +187 -0
- package/src/pnl-diagram.tsx +458 -0
- package/src/popover.tsx +400 -0
- package/src/progress-circle.tsx +180 -0
- package/src/progress.tsx +109 -0
- package/src/provider.tsx +12 -0
- package/src/radio.tsx +175 -0
- package/src/rating.tsx +79 -0
- package/src/select.tsx +696 -0
- package/src/separator.tsx +59 -0
- package/src/skeleton.tsx +302 -0
- package/src/slider.tsx +152 -0
- package/src/strategy-builder.tsx +466 -0
- package/src/switch.tsx +158 -0
- package/src/table.tsx +621 -0
- package/src/tabs.tsx +354 -0
- package/src/tag.tsx +159 -0
- package/src/textarea.tsx +60 -0
- package/src/toaster.tsx +117 -0
- package/src/tokens.css +438 -0
- package/src/tooltip.tsx +184 -0
- package/src/utils/cn.ts +7 -0
- package/src/utils.ts +6 -0
- package/tokens.css +438 -0
- package/README.md +0 -109
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var React = require('react');
|
|
5
|
+
var clsx = require('clsx');
|
|
6
|
+
var tailwindMerge = require('tailwind-merge');
|
|
7
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
8
|
+
|
|
9
|
+
function _interopNamespace(e) {
|
|
10
|
+
if (e && e.__esModule) return e;
|
|
11
|
+
var n = Object.create(null);
|
|
12
|
+
if (e) {
|
|
13
|
+
Object.keys(e).forEach(function (k) {
|
|
14
|
+
if (k !== 'default') {
|
|
15
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
16
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
get: function () { return e[k]; }
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
n.default = e;
|
|
24
|
+
return Object.freeze(n);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
28
|
+
|
|
29
|
+
function cn(...inputs) {
|
|
30
|
+
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
31
|
+
}
|
|
32
|
+
function findATM(strikes, spot) {
|
|
33
|
+
if (strikes.length === 0) return spot;
|
|
34
|
+
return strikes.reduce(
|
|
35
|
+
(prev, curr) => Math.abs(curr - spot) < Math.abs(prev - spot) ? curr : prev
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
function findOTM(strikes, spot, type, offset) {
|
|
39
|
+
const sorted = [...strikes].sort((a, b) => a - b);
|
|
40
|
+
const atm = findATM(strikes, spot);
|
|
41
|
+
const atIdx = sorted.indexOf(atm);
|
|
42
|
+
if (type === "call") {
|
|
43
|
+
return sorted[Math.min(atIdx + offset, sorted.length - 1)] ?? atm;
|
|
44
|
+
}
|
|
45
|
+
return sorted[Math.max(atIdx - offset, 0)] ?? atm;
|
|
46
|
+
}
|
|
47
|
+
var STRATEGY_PRESETS = [
|
|
48
|
+
{
|
|
49
|
+
label: "Bull Call Spread",
|
|
50
|
+
value: "bull_call_spread",
|
|
51
|
+
description: "Buy lower strike call, sell higher strike call",
|
|
52
|
+
buildLegs: (strikes, spot, exp) => {
|
|
53
|
+
const lower = findATM(strikes, spot);
|
|
54
|
+
const upper = findOTM(strikes, spot, "call", 2);
|
|
55
|
+
return [
|
|
56
|
+
{ action: "buy", type: "call", strike: lower, expiration: exp, quantity: 1 },
|
|
57
|
+
{ action: "sell", type: "call", strike: upper, expiration: exp, quantity: 1 }
|
|
58
|
+
];
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
label: "Bear Put Spread",
|
|
63
|
+
value: "bear_put_spread",
|
|
64
|
+
description: "Buy higher strike put, sell lower strike put",
|
|
65
|
+
buildLegs: (strikes, spot, exp) => {
|
|
66
|
+
const upper = findATM(strikes, spot);
|
|
67
|
+
const lower = findOTM(strikes, spot, "put", 2);
|
|
68
|
+
return [
|
|
69
|
+
{ action: "buy", type: "put", strike: upper, expiration: exp, quantity: 1 },
|
|
70
|
+
{ action: "sell", type: "put", strike: lower, expiration: exp, quantity: 1 }
|
|
71
|
+
];
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
label: "Iron Condor",
|
|
76
|
+
value: "iron_condor",
|
|
77
|
+
description: "Sell OTM put + call, buy further OTM put + call",
|
|
78
|
+
buildLegs: (strikes, spot, exp) => {
|
|
79
|
+
const sellPut = findOTM(strikes, spot, "put", 1);
|
|
80
|
+
const buyPut = findOTM(strikes, spot, "put", 3);
|
|
81
|
+
const sellCall = findOTM(strikes, spot, "call", 1);
|
|
82
|
+
const buyCall = findOTM(strikes, spot, "call", 3);
|
|
83
|
+
return [
|
|
84
|
+
{ action: "buy", type: "put", strike: buyPut, expiration: exp, quantity: 1 },
|
|
85
|
+
{ action: "sell", type: "put", strike: sellPut, expiration: exp, quantity: 1 },
|
|
86
|
+
{ action: "sell", type: "call", strike: sellCall, expiration: exp, quantity: 1 },
|
|
87
|
+
{ action: "buy", type: "call", strike: buyCall, expiration: exp, quantity: 1 }
|
|
88
|
+
];
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
label: "Butterfly",
|
|
93
|
+
value: "butterfly",
|
|
94
|
+
description: "Buy 1 low, sell 2 middle, buy 1 high",
|
|
95
|
+
buildLegs: (strikes, spot, exp) => {
|
|
96
|
+
const mid = findATM(strikes, spot);
|
|
97
|
+
const low = findOTM(strikes, spot, "put", 2);
|
|
98
|
+
const high = findOTM(strikes, spot, "call", 2);
|
|
99
|
+
return [
|
|
100
|
+
{ action: "buy", type: "call", strike: low, expiration: exp, quantity: 1 },
|
|
101
|
+
{ action: "sell", type: "call", strike: mid, expiration: exp, quantity: 2 },
|
|
102
|
+
{ action: "buy", type: "call", strike: high, expiration: exp, quantity: 1 }
|
|
103
|
+
];
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
label: "Straddle",
|
|
108
|
+
value: "straddle",
|
|
109
|
+
description: "Buy call + put at same strike",
|
|
110
|
+
buildLegs: (strikes, spot, exp) => {
|
|
111
|
+
const atm = findATM(strikes, spot);
|
|
112
|
+
return [
|
|
113
|
+
{ action: "buy", type: "call", strike: atm, expiration: exp, quantity: 1 },
|
|
114
|
+
{ action: "buy", type: "put", strike: atm, expiration: exp, quantity: 1 }
|
|
115
|
+
];
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
label: "Strangle",
|
|
120
|
+
value: "strangle",
|
|
121
|
+
description: "Buy OTM call + OTM put",
|
|
122
|
+
buildLegs: (strikes, spot, exp) => {
|
|
123
|
+
const otmCall = findOTM(strikes, spot, "call", 2);
|
|
124
|
+
const otmPut = findOTM(strikes, spot, "put", 2);
|
|
125
|
+
return [
|
|
126
|
+
{ action: "buy", type: "put", strike: otmPut, expiration: exp, quantity: 1 },
|
|
127
|
+
{ action: "buy", type: "call", strike: otmCall, expiration: exp, quantity: 1 }
|
|
128
|
+
];
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
label: "Custom",
|
|
133
|
+
value: "custom",
|
|
134
|
+
description: "Build your own multi-leg strategy",
|
|
135
|
+
buildLegs: (_strikes, spot, exp) => [
|
|
136
|
+
{ action: "buy", type: "call", strike: spot, expiration: exp, quantity: 1 }
|
|
137
|
+
]
|
|
138
|
+
}
|
|
139
|
+
];
|
|
140
|
+
function LegRow({ leg, index, strikes, expirations, removable, onChange, onRemove }) {
|
|
141
|
+
const update = (patch) => {
|
|
142
|
+
onChange(index, { ...leg, ...patch });
|
|
143
|
+
};
|
|
144
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 rounded-md bg-zinc-800/50 px-2 py-1.5", children: [
|
|
145
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-mono text-zinc-500 w-4 shrink-0", children: index + 1 }),
|
|
146
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
147
|
+
"button",
|
|
148
|
+
{
|
|
149
|
+
type: "button",
|
|
150
|
+
className: cn(
|
|
151
|
+
"px-2 py-0.5 rounded text-xs font-semibold transition-colors",
|
|
152
|
+
leg.action === "buy" ? "bg-emerald-900/60 text-emerald-400" : "bg-red-900/60 text-red-400"
|
|
153
|
+
),
|
|
154
|
+
onClick: () => update({ action: leg.action === "buy" ? "sell" : "buy" }),
|
|
155
|
+
children: leg.action === "buy" ? "BUY" : "SELL"
|
|
156
|
+
}
|
|
157
|
+
),
|
|
158
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
159
|
+
"button",
|
|
160
|
+
{
|
|
161
|
+
type: "button",
|
|
162
|
+
className: cn(
|
|
163
|
+
"px-2 py-0.5 rounded text-xs font-medium transition-colors",
|
|
164
|
+
leg.type === "call" ? "bg-blue-900/40 text-blue-400" : "bg-orange-900/40 text-orange-400"
|
|
165
|
+
),
|
|
166
|
+
onClick: () => update({ type: leg.type === "call" ? "put" : "call" }),
|
|
167
|
+
children: leg.type === "call" ? "CALL" : "PUT"
|
|
168
|
+
}
|
|
169
|
+
),
|
|
170
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
171
|
+
"select",
|
|
172
|
+
{
|
|
173
|
+
value: leg.strike,
|
|
174
|
+
onChange: (e) => update({ strike: Number(e.target.value) }),
|
|
175
|
+
className: "bg-zinc-900 border border-zinc-700 rounded px-1.5 py-0.5 text-xs font-mono text-zinc-200 outline-none focus:border-zinc-500 min-w-[72px]",
|
|
176
|
+
children: strikes.map((s) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: s, children: s.toFixed(2) }, s))
|
|
177
|
+
}
|
|
178
|
+
),
|
|
179
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
180
|
+
"select",
|
|
181
|
+
{
|
|
182
|
+
value: leg.expiration,
|
|
183
|
+
onChange: (e) => update({ expiration: e.target.value }),
|
|
184
|
+
className: "bg-zinc-900 border border-zinc-700 rounded px-1.5 py-0.5 text-xs text-zinc-200 outline-none focus:border-zinc-500 min-w-[80px]",
|
|
185
|
+
children: expirations.map((exp) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: exp, children: exp }, exp))
|
|
186
|
+
}
|
|
187
|
+
),
|
|
188
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
189
|
+
"input",
|
|
190
|
+
{
|
|
191
|
+
type: "number",
|
|
192
|
+
min: 1,
|
|
193
|
+
max: 999,
|
|
194
|
+
value: leg.quantity,
|
|
195
|
+
onChange: (e) => update({ quantity: Math.max(1, parseInt(e.target.value, 10) || 1) }),
|
|
196
|
+
className: "bg-zinc-900 border border-zinc-700 rounded px-1.5 py-0.5 text-xs font-mono text-zinc-200 outline-none focus:border-zinc-500 w-[48px] text-center"
|
|
197
|
+
}
|
|
198
|
+
),
|
|
199
|
+
leg.premium !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn(
|
|
200
|
+
"font-mono tabular-nums text-xs ml-auto",
|
|
201
|
+
leg.premium >= 0 ? "text-emerald-400" : "text-red-400"
|
|
202
|
+
), children: [
|
|
203
|
+
leg.premium >= 0 ? "+" : "",
|
|
204
|
+
leg.premium.toFixed(2)
|
|
205
|
+
] }),
|
|
206
|
+
removable && /* @__PURE__ */ jsxRuntime.jsx(
|
|
207
|
+
"button",
|
|
208
|
+
{
|
|
209
|
+
type: "button",
|
|
210
|
+
onClick: () => onRemove(index),
|
|
211
|
+
className: "ml-auto p-0.5 text-zinc-500 hover:text-red-400 transition-colors",
|
|
212
|
+
"aria-label": "Remove leg",
|
|
213
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "h-3.5 w-3.5", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6L6 18M6 6l12 12", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) })
|
|
214
|
+
}
|
|
215
|
+
)
|
|
216
|
+
] });
|
|
217
|
+
}
|
|
218
|
+
var StrategyBuilder = React__namespace.forwardRef(
|
|
219
|
+
function StrategyBuilder2(props, ref) {
|
|
220
|
+
const {
|
|
221
|
+
underlying,
|
|
222
|
+
expirations,
|
|
223
|
+
strikes,
|
|
224
|
+
spotPrice,
|
|
225
|
+
onSubmit,
|
|
226
|
+
className,
|
|
227
|
+
...rest
|
|
228
|
+
} = props;
|
|
229
|
+
const defaultExp = expirations[0] ?? "";
|
|
230
|
+
const [strategyType, setStrategyType] = React__namespace.useState("bull_call_spread");
|
|
231
|
+
const [legs, setLegs] = React__namespace.useState(() => {
|
|
232
|
+
const preset = STRATEGY_PRESETS.find((p) => p.value === "bull_call_spread");
|
|
233
|
+
return preset ? preset.buildLegs(strikes, spotPrice, defaultExp) : [];
|
|
234
|
+
});
|
|
235
|
+
const handleStrategyChange = React__namespace.useCallback(
|
|
236
|
+
(e) => {
|
|
237
|
+
const type = e.target.value;
|
|
238
|
+
setStrategyType(type);
|
|
239
|
+
const preset = STRATEGY_PRESETS.find((p) => p.value === type);
|
|
240
|
+
if (preset) {
|
|
241
|
+
setLegs(preset.buildLegs(strikes, spotPrice, defaultExp));
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
[strikes, spotPrice, defaultExp]
|
|
245
|
+
);
|
|
246
|
+
const handleLegChange = React__namespace.useCallback(
|
|
247
|
+
(index, leg) => {
|
|
248
|
+
setLegs((prev) => {
|
|
249
|
+
const next = [...prev];
|
|
250
|
+
next[index] = leg;
|
|
251
|
+
return next;
|
|
252
|
+
});
|
|
253
|
+
},
|
|
254
|
+
[]
|
|
255
|
+
);
|
|
256
|
+
const handleLegRemove = React__namespace.useCallback(
|
|
257
|
+
(index) => {
|
|
258
|
+
setLegs((prev) => prev.filter((_, i) => i !== index));
|
|
259
|
+
},
|
|
260
|
+
[]
|
|
261
|
+
);
|
|
262
|
+
const handleAddLeg = React__namespace.useCallback(() => {
|
|
263
|
+
if (legs.length >= 4) return;
|
|
264
|
+
const atm = findATM(strikes, spotPrice);
|
|
265
|
+
setLegs((prev) => [
|
|
266
|
+
...prev,
|
|
267
|
+
{ action: "buy", type: "call", strike: atm, expiration: defaultExp, quantity: 1 }
|
|
268
|
+
]);
|
|
269
|
+
}, [legs.length, strikes, spotPrice, defaultExp]);
|
|
270
|
+
const netPremium = React__namespace.useMemo(() => {
|
|
271
|
+
return legs.reduce((sum, leg) => {
|
|
272
|
+
if (leg.premium === void 0) return sum;
|
|
273
|
+
const sign = leg.action === "buy" ? -1 : 1;
|
|
274
|
+
return sum + sign * leg.premium * leg.quantity;
|
|
275
|
+
}, 0);
|
|
276
|
+
}, [legs]);
|
|
277
|
+
const handleSubmit = React__namespace.useCallback(() => {
|
|
278
|
+
if (!onSubmit || legs.length === 0) return;
|
|
279
|
+
onSubmit({
|
|
280
|
+
type: strategyType,
|
|
281
|
+
legs,
|
|
282
|
+
netPremium
|
|
283
|
+
});
|
|
284
|
+
}, [onSubmit, strategyType, legs, netPremium]);
|
|
285
|
+
const currentPreset = STRATEGY_PRESETS.find((p) => p.value === strategyType);
|
|
286
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
287
|
+
"div",
|
|
288
|
+
{
|
|
289
|
+
ref,
|
|
290
|
+
className: cn(
|
|
291
|
+
"flex flex-col gap-3 rounded-lg border border-zinc-800 bg-zinc-900/50 p-3",
|
|
292
|
+
className
|
|
293
|
+
),
|
|
294
|
+
...rest,
|
|
295
|
+
children: [
|
|
296
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3", children: [
|
|
297
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
298
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold uppercase tracking-wider text-zinc-500", children: "Strategy" }),
|
|
299
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-zinc-200", children: underlying })
|
|
300
|
+
] }),
|
|
301
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[10px] text-zinc-500", children: [
|
|
302
|
+
"Spot: ",
|
|
303
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono tabular-nums text-zinc-300", children: spotPrice.toFixed(2) })
|
|
304
|
+
] })
|
|
305
|
+
] }),
|
|
306
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
307
|
+
"select",
|
|
308
|
+
{
|
|
309
|
+
value: strategyType,
|
|
310
|
+
onChange: handleStrategyChange,
|
|
311
|
+
className: "flex-1 bg-zinc-900 border border-zinc-700 rounded-md px-2.5 py-1.5 text-sm text-zinc-200 outline-none focus:border-zinc-500",
|
|
312
|
+
children: STRATEGY_PRESETS.map((preset) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: preset.value, children: preset.label }, preset.value))
|
|
313
|
+
}
|
|
314
|
+
) }),
|
|
315
|
+
currentPreset && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] text-zinc-500 -mt-1", children: currentPreset.description }),
|
|
316
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-1.5", children: legs.map((leg, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
317
|
+
LegRow,
|
|
318
|
+
{
|
|
319
|
+
leg,
|
|
320
|
+
index: i,
|
|
321
|
+
strikes,
|
|
322
|
+
expirations,
|
|
323
|
+
removable: strategyType === "custom" && legs.length > 1,
|
|
324
|
+
onChange: handleLegChange,
|
|
325
|
+
onRemove: handleLegRemove
|
|
326
|
+
},
|
|
327
|
+
i
|
|
328
|
+
)) }),
|
|
329
|
+
strategyType === "custom" && legs.length < 4 && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
330
|
+
"button",
|
|
331
|
+
{
|
|
332
|
+
type: "button",
|
|
333
|
+
onClick: handleAddLeg,
|
|
334
|
+
className: "flex items-center justify-center gap-1 rounded-md border border-dashed border-zinc-700 py-1 text-xs text-zinc-500 hover:text-zinc-300 hover:border-zinc-500 transition-colors",
|
|
335
|
+
children: [
|
|
336
|
+
/* @__PURE__ */ jsxRuntime.jsx("svg", { className: "h-3 w-3", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 5v14M5 12h14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }),
|
|
337
|
+
"Add Leg"
|
|
338
|
+
]
|
|
339
|
+
}
|
|
340
|
+
),
|
|
341
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between pt-1 border-t border-zinc-800", children: [
|
|
342
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
343
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-zinc-500", children: "Net:" }),
|
|
344
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn(
|
|
345
|
+
"font-mono tabular-nums text-sm font-semibold",
|
|
346
|
+
netPremium > 0 ? "text-emerald-400" : netPremium < 0 ? "text-red-400" : "text-zinc-400"
|
|
347
|
+
), children: [
|
|
348
|
+
netPremium > 0 ? "Credit " : netPremium < 0 ? "Debit " : "",
|
|
349
|
+
netPremium !== 0 ? `$${Math.abs(netPremium).toFixed(2)}` : "-"
|
|
350
|
+
] })
|
|
351
|
+
] }),
|
|
352
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
353
|
+
"button",
|
|
354
|
+
{
|
|
355
|
+
type: "button",
|
|
356
|
+
onClick: handleSubmit,
|
|
357
|
+
disabled: legs.length === 0,
|
|
358
|
+
className: cn(
|
|
359
|
+
"px-4 py-1.5 rounded-md text-xs font-semibold transition-colors",
|
|
360
|
+
"bg-blue-600 text-white hover:bg-blue-500",
|
|
361
|
+
"disabled:opacity-40 disabled:cursor-not-allowed"
|
|
362
|
+
),
|
|
363
|
+
children: "Review Order"
|
|
364
|
+
}
|
|
365
|
+
)
|
|
366
|
+
] })
|
|
367
|
+
]
|
|
368
|
+
}
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
exports.StrategyBuilder = StrategyBuilder;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
interface StrategyLeg {
|
|
4
|
+
action: 'buy' | 'sell';
|
|
5
|
+
type: 'call' | 'put';
|
|
6
|
+
strike: number;
|
|
7
|
+
expiration: string;
|
|
8
|
+
quantity: number;
|
|
9
|
+
premium?: number;
|
|
10
|
+
}
|
|
11
|
+
type StrategyType = 'bull_call_spread' | 'bear_put_spread' | 'iron_condor' | 'butterfly' | 'straddle' | 'strangle' | 'collar' | 'custom';
|
|
12
|
+
interface StrategySubmission {
|
|
13
|
+
type: string;
|
|
14
|
+
legs: StrategyLeg[];
|
|
15
|
+
netPremium: number;
|
|
16
|
+
}
|
|
17
|
+
interface StrategyBuilderProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onSubmit'> {
|
|
18
|
+
readonly underlying: string;
|
|
19
|
+
readonly expirations: string[];
|
|
20
|
+
readonly strikes: number[];
|
|
21
|
+
readonly spotPrice: number;
|
|
22
|
+
readonly onSubmit?: (strategy: StrategySubmission) => void;
|
|
23
|
+
}
|
|
24
|
+
declare const StrategyBuilder: React.ForwardRefExoticComponent<StrategyBuilderProps & React.RefAttributes<HTMLDivElement>>;
|
|
25
|
+
|
|
26
|
+
export { StrategyBuilder, type StrategyBuilderProps, type StrategyLeg, type StrategySubmission, type StrategyType };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
interface StrategyLeg {
|
|
4
|
+
action: 'buy' | 'sell';
|
|
5
|
+
type: 'call' | 'put';
|
|
6
|
+
strike: number;
|
|
7
|
+
expiration: string;
|
|
8
|
+
quantity: number;
|
|
9
|
+
premium?: number;
|
|
10
|
+
}
|
|
11
|
+
type StrategyType = 'bull_call_spread' | 'bear_put_spread' | 'iron_condor' | 'butterfly' | 'straddle' | 'strangle' | 'collar' | 'custom';
|
|
12
|
+
interface StrategySubmission {
|
|
13
|
+
type: string;
|
|
14
|
+
legs: StrategyLeg[];
|
|
15
|
+
netPremium: number;
|
|
16
|
+
}
|
|
17
|
+
interface StrategyBuilderProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onSubmit'> {
|
|
18
|
+
readonly underlying: string;
|
|
19
|
+
readonly expirations: string[];
|
|
20
|
+
readonly strikes: number[];
|
|
21
|
+
readonly spotPrice: number;
|
|
22
|
+
readonly onSubmit?: (strategy: StrategySubmission) => void;
|
|
23
|
+
}
|
|
24
|
+
declare const StrategyBuilder: React.ForwardRefExoticComponent<StrategyBuilderProps & React.RefAttributes<HTMLDivElement>>;
|
|
25
|
+
|
|
26
|
+
export { StrategyBuilder, type StrategyBuilderProps, type StrategyLeg, type StrategySubmission, type StrategyType };
|