@forgecharts/sdk 1.4.3 → 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/forgecharts.css
CHANGED
|
@@ -4083,6 +4083,10 @@ select {
|
|
|
4083
4083
|
background: rgba(88, 166, 255, 0.18);
|
|
4084
4084
|
color: var(--accent, #58a6ff);
|
|
4085
4085
|
}
|
|
4086
|
+
.broker-logo--topstep {
|
|
4087
|
+
background: rgba(74, 222, 128, 0.18);
|
|
4088
|
+
color: var(--color-success, #4ade80);
|
|
4089
|
+
}
|
|
4086
4090
|
|
|
4087
4091
|
/* ── Rithmic / broker connect form ────────────────────────────────────────── */
|
|
4088
4092
|
|
package/dist/react/internal.js
CHANGED
|
@@ -33507,6 +33507,7 @@ var BROKERS = [
|
|
|
33507
33507
|
{ id: "ninjabrokerage", name: "NinjaTrader Brokerage", description: "Futures & Forex", status: "coming-soon" },
|
|
33508
33508
|
{ id: "rithmic", name: "Rithmic", description: "Futures & Forex", status: "available" },
|
|
33509
33509
|
{ id: "tastytrade", name: "tastytrade", description: "Options & Futures", status: "coming-soon" },
|
|
33510
|
+
{ id: "topstep", name: "TopStep", description: "Futures (TopstepX)", status: "available" },
|
|
33510
33511
|
{ id: "tradestation", name: "TradeStation", description: "Stocks, Futures & Options", status: "coming-soon" },
|
|
33511
33512
|
{ id: "tradier", name: "Tradier", description: "Stocks & Options", status: "coming-soon" },
|
|
33512
33513
|
{ id: "tradovate", name: "Tradovate", description: "Futures & Options", status: "coming-soon" }
|
|
@@ -33867,6 +33868,255 @@ function RithmicConnectDrawer({ onClose, onConnected, getAuthToken, savedConnect
|
|
|
33867
33868
|
] })
|
|
33868
33869
|
] });
|
|
33869
33870
|
}
|
|
33871
|
+
function validateTopStepCreds(f) {
|
|
33872
|
+
const errs = {};
|
|
33873
|
+
if (!f.username.trim()) errs.username = "Username is required";
|
|
33874
|
+
if (!f.apiKey.trim()) errs.apiKey = "API key is required";
|
|
33875
|
+
return errs;
|
|
33876
|
+
}
|
|
33877
|
+
function TopStepConnectDrawer({ onClose, onConnected, getAuthToken, savedConnection }) {
|
|
33878
|
+
const [overrideCredentials, setOverrideCredentials] = useState(false);
|
|
33879
|
+
const [step, setStep] = useState("credentials");
|
|
33880
|
+
const [fields, setFields] = useState({
|
|
33881
|
+
username: savedConnection?.username ?? "",
|
|
33882
|
+
apiKey: ""
|
|
33883
|
+
});
|
|
33884
|
+
const [errors, setErrors] = useState({});
|
|
33885
|
+
const [loading, setLoading] = useState(false);
|
|
33886
|
+
const [accounts, setAccounts] = useState([]);
|
|
33887
|
+
const [apiError, setApiError] = useState("");
|
|
33888
|
+
const shouldAutoConnect = !!savedConnection && !savedConnection.isExpired && !overrideCredentials;
|
|
33889
|
+
const [autoConnecting, setAutoConnecting] = useState(shouldAutoConnect);
|
|
33890
|
+
function handleUseNewCredentials() {
|
|
33891
|
+
setFields({ username: "", apiKey: "" });
|
|
33892
|
+
setErrors({});
|
|
33893
|
+
setApiError("");
|
|
33894
|
+
setOverrideCredentials(true);
|
|
33895
|
+
setStep("credentials");
|
|
33896
|
+
}
|
|
33897
|
+
React11.useEffect(() => {
|
|
33898
|
+
if (!shouldAutoConnect) return;
|
|
33899
|
+
let cancelled = false;
|
|
33900
|
+
(async () => {
|
|
33901
|
+
const h = await authHeaders();
|
|
33902
|
+
try {
|
|
33903
|
+
const res = await fetch("/api/broker/user-sessions", {
|
|
33904
|
+
method: "POST",
|
|
33905
|
+
headers: h,
|
|
33906
|
+
body: JSON.stringify({ connectionId: savedConnection.id })
|
|
33907
|
+
});
|
|
33908
|
+
if (cancelled) return;
|
|
33909
|
+
const body = await res.json().catch(() => ({}));
|
|
33910
|
+
if (res.ok && body.sessionId) {
|
|
33911
|
+
onConnected(savedConnection.id);
|
|
33912
|
+
setStep("done");
|
|
33913
|
+
} else {
|
|
33914
|
+
setApiError(body.error ?? "Auto-connect failed \u2014 please re-enter your API key");
|
|
33915
|
+
}
|
|
33916
|
+
} catch {
|
|
33917
|
+
if (!cancelled) setApiError("Network error during auto-connect \u2014 please try again");
|
|
33918
|
+
} finally {
|
|
33919
|
+
if (!cancelled) setAutoConnecting(false);
|
|
33920
|
+
}
|
|
33921
|
+
})();
|
|
33922
|
+
return () => {
|
|
33923
|
+
cancelled = true;
|
|
33924
|
+
};
|
|
33925
|
+
}, [shouldAutoConnect]);
|
|
33926
|
+
function set(key, value) {
|
|
33927
|
+
setFields((prev) => ({ ...prev, [key]: value }));
|
|
33928
|
+
if (errors[key]) setErrors((prev) => ({ ...prev, [key]: void 0 }));
|
|
33929
|
+
setApiError("");
|
|
33930
|
+
}
|
|
33931
|
+
async function authHeaders() {
|
|
33932
|
+
const h = { "Content-Type": "application/json" };
|
|
33933
|
+
if (getAuthToken) {
|
|
33934
|
+
try {
|
|
33935
|
+
h["Authorization"] = `Bearer ${await getAuthToken()}`;
|
|
33936
|
+
} catch {
|
|
33937
|
+
}
|
|
33938
|
+
}
|
|
33939
|
+
return h;
|
|
33940
|
+
}
|
|
33941
|
+
async function handleConnect(e) {
|
|
33942
|
+
e.preventDefault();
|
|
33943
|
+
const errs = validateTopStepCreds(fields);
|
|
33944
|
+
if (Object.keys(errs).length > 0) {
|
|
33945
|
+
setErrors(errs);
|
|
33946
|
+
return;
|
|
33947
|
+
}
|
|
33948
|
+
setLoading(true);
|
|
33949
|
+
setApiError("");
|
|
33950
|
+
try {
|
|
33951
|
+
const res = await fetch("/api/broker/topstep/verify", {
|
|
33952
|
+
method: "POST",
|
|
33953
|
+
headers: await authHeaders(),
|
|
33954
|
+
body: JSON.stringify({
|
|
33955
|
+
username: fields.username,
|
|
33956
|
+
apiKey: fields.apiKey
|
|
33957
|
+
})
|
|
33958
|
+
});
|
|
33959
|
+
const body = await res.json().catch(() => ({}));
|
|
33960
|
+
if (!res.ok) {
|
|
33961
|
+
setApiError(body.error ?? `Error ${res.status} \u2014 check your credentials and try again`);
|
|
33962
|
+
return;
|
|
33963
|
+
}
|
|
33964
|
+
setAccounts(body.accounts ?? []);
|
|
33965
|
+
setStep("accounts");
|
|
33966
|
+
} catch {
|
|
33967
|
+
setApiError("Network error \u2014 please try again");
|
|
33968
|
+
} finally {
|
|
33969
|
+
setLoading(false);
|
|
33970
|
+
}
|
|
33971
|
+
}
|
|
33972
|
+
async function handleSelectAccount(account) {
|
|
33973
|
+
setLoading(true);
|
|
33974
|
+
setApiError("");
|
|
33975
|
+
try {
|
|
33976
|
+
const res = await fetch("/api/broker/credentials", {
|
|
33977
|
+
method: "POST",
|
|
33978
|
+
headers: await authHeaders(),
|
|
33979
|
+
body: JSON.stringify({
|
|
33980
|
+
broker: "topstep",
|
|
33981
|
+
propFirm: "topstep",
|
|
33982
|
+
username: fields.username,
|
|
33983
|
+
password: fields.apiKey,
|
|
33984
|
+
gatewayId: "topstepx",
|
|
33985
|
+
accountId: account.id,
|
|
33986
|
+
accountName: account.name
|
|
33987
|
+
})
|
|
33988
|
+
});
|
|
33989
|
+
if (!res.ok) {
|
|
33990
|
+
const body2 = await res.json().catch(() => ({}));
|
|
33991
|
+
setApiError(body2.error ?? `Error ${res.status}`);
|
|
33992
|
+
return;
|
|
33993
|
+
}
|
|
33994
|
+
const body = await res.json().catch(() => ({}));
|
|
33995
|
+
onConnected(body.connectionId ?? "");
|
|
33996
|
+
setStep("done");
|
|
33997
|
+
} catch {
|
|
33998
|
+
setApiError("Network error \u2014 please try again");
|
|
33999
|
+
} finally {
|
|
34000
|
+
setLoading(false);
|
|
34001
|
+
}
|
|
34002
|
+
}
|
|
34003
|
+
return /* @__PURE__ */ jsxs("div", { className: "trade-drawer trade-drawer--connect", children: [
|
|
34004
|
+
/* @__PURE__ */ jsxs("div", { className: "trade-drawer-header", children: [
|
|
34005
|
+
/* @__PURE__ */ jsxs("span", { className: "trade-drawer-title", children: [
|
|
34006
|
+
/* @__PURE__ */ jsx("span", { className: "broker-logo broker-logo--topstep", children: "T" }),
|
|
34007
|
+
"TopStep \u2014 Connect"
|
|
34008
|
+
] }),
|
|
34009
|
+
/* @__PURE__ */ jsxs("button", { className: "trade-drawer-close", onClick: onClose, title: "Close drawer", children: [
|
|
34010
|
+
/* @__PURE__ */ jsx(BackIcon, {}),
|
|
34011
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: 11, marginLeft: 3 }, children: "Close drawer" })
|
|
34012
|
+
] })
|
|
34013
|
+
] }),
|
|
34014
|
+
/* @__PURE__ */ jsxs("div", { className: "trade-drawer-body trade-drawer-body--form", children: [
|
|
34015
|
+
autoConnecting && /* @__PURE__ */ jsxs("div", { className: "broker-connect-form", children: [
|
|
34016
|
+
/* @__PURE__ */ jsx("div", { className: "bcf-section-label", children: "Reconnecting\u2026" }),
|
|
34017
|
+
/* @__PURE__ */ jsxs("p", { style: { fontSize: 12, color: "var(--text-muted)", marginTop: 8 }, children: [
|
|
34018
|
+
"Connecting with saved credentials for ",
|
|
34019
|
+
/* @__PURE__ */ jsx("strong", { children: savedConnection?.username }),
|
|
34020
|
+
"."
|
|
34021
|
+
] })
|
|
34022
|
+
] }),
|
|
34023
|
+
!autoConnecting && step === "credentials" && /* @__PURE__ */ jsxs("form", { className: "broker-connect-form", onSubmit: handleConnect, noValidate: true, children: [
|
|
34024
|
+
savedConnection && !overrideCredentials && /* @__PURE__ */ jsxs("div", { className: "bcf-saved-notice", children: [
|
|
34025
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 14 14", width: "11", height: "11", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", style: { flexShrink: 0, opacity: 0.7 }, children: [
|
|
34026
|
+
/* @__PURE__ */ jsx("circle", { cx: "7", cy: "5", r: "2.5" }),
|
|
34027
|
+
/* @__PURE__ */ jsx("path", { d: "M1.5 13c0-3.04 2.46-5.5 5.5-5.5s5.5 2.46 5.5 5.5" })
|
|
34028
|
+
] }),
|
|
34029
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
34030
|
+
"Saved as ",
|
|
34031
|
+
/* @__PURE__ */ jsx("strong", { children: savedConnection.username }),
|
|
34032
|
+
savedConnection.isExpired && /* @__PURE__ */ jsx("span", { style: { color: "var(--color-warning, #f59e0b)", marginLeft: 4 }, children: "\xB7 session expired" })
|
|
34033
|
+
] }),
|
|
34034
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "bcf-link-btn", onClick: handleUseNewCredentials, children: "Use different credentials" })
|
|
34035
|
+
] }),
|
|
34036
|
+
/* @__PURE__ */ jsx("div", { className: "bcf-section-label", children: "TopstepX credentials" }),
|
|
34037
|
+
/* @__PURE__ */ jsxs("div", { className: "bcf-row", children: [
|
|
34038
|
+
/* @__PURE__ */ jsxs("label", { className: "bcf-label", children: [
|
|
34039
|
+
"Username ",
|
|
34040
|
+
/* @__PURE__ */ jsx("span", { className: "bcf-req", children: "*" })
|
|
34041
|
+
] }),
|
|
34042
|
+
/* @__PURE__ */ jsx(
|
|
34043
|
+
"input",
|
|
34044
|
+
{
|
|
34045
|
+
className: `bcf-input${errors.username ? " bcf-input--error" : ""}`,
|
|
34046
|
+
type: "text",
|
|
34047
|
+
value: fields.username,
|
|
34048
|
+
autoComplete: "username",
|
|
34049
|
+
onChange: (e) => set("username", e.target.value)
|
|
34050
|
+
}
|
|
34051
|
+
),
|
|
34052
|
+
errors.username && /* @__PURE__ */ jsx("span", { className: "bcf-error", children: errors.username })
|
|
34053
|
+
] }),
|
|
34054
|
+
/* @__PURE__ */ jsxs("div", { className: "bcf-row", children: [
|
|
34055
|
+
/* @__PURE__ */ jsxs("label", { className: "bcf-label", children: [
|
|
34056
|
+
"API key ",
|
|
34057
|
+
/* @__PURE__ */ jsx("span", { className: "bcf-req", children: "*" })
|
|
34058
|
+
] }),
|
|
34059
|
+
/* @__PURE__ */ jsx(
|
|
34060
|
+
"input",
|
|
34061
|
+
{
|
|
34062
|
+
className: `bcf-input${errors.apiKey ? " bcf-input--error" : ""}`,
|
|
34063
|
+
type: "password",
|
|
34064
|
+
value: fields.apiKey,
|
|
34065
|
+
autoComplete: "off",
|
|
34066
|
+
onChange: (e) => set("apiKey", e.target.value)
|
|
34067
|
+
}
|
|
34068
|
+
),
|
|
34069
|
+
errors.apiKey && /* @__PURE__ */ jsx("span", { className: "bcf-error", children: errors.apiKey }),
|
|
34070
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: 11, color: "var(--text-muted)", marginTop: 4, display: "block" }, children: "Generate an API key in TopstepX under Settings \u2192 API Access." })
|
|
34071
|
+
] }),
|
|
34072
|
+
apiError && /* @__PURE__ */ jsx("div", { className: "bcf-api-error", children: apiError }),
|
|
34073
|
+
/* @__PURE__ */ jsx("div", { className: "bcf-actions", children: /* @__PURE__ */ jsx("button", { type: "submit", className: "bcf-btn-save", disabled: loading, children: loading ? "Connecting\u2026" : "Connect & Fetch Accounts" }) })
|
|
34074
|
+
] }),
|
|
34075
|
+
!autoConnecting && step === "accounts" && /* @__PURE__ */ jsxs("div", { className: "broker-connect-form", children: [
|
|
34076
|
+
/* @__PURE__ */ jsx("div", { className: "bcf-section-label", children: "Select your trading account" }),
|
|
34077
|
+
accounts.length === 0 ? /* @__PURE__ */ jsx("p", { className: "bcf-empty", children: "No accounts found for these credentials." }) : /* @__PURE__ */ jsx("div", { className: "bcf-account-list", children: accounts.map((acct) => /* @__PURE__ */ jsxs("div", { className: "bcf-account-row", children: [
|
|
34078
|
+
/* @__PURE__ */ jsxs("div", { className: "bcf-account-info", children: [
|
|
34079
|
+
/* @__PURE__ */ jsx("span", { className: "bcf-account-name", children: acct.name }),
|
|
34080
|
+
acct.type && /* @__PURE__ */ jsx("span", { className: "bcf-account-type", children: acct.type })
|
|
34081
|
+
] }),
|
|
34082
|
+
/* @__PURE__ */ jsx(
|
|
34083
|
+
"button",
|
|
34084
|
+
{
|
|
34085
|
+
className: "bcf-btn-select",
|
|
34086
|
+
disabled: loading,
|
|
34087
|
+
onClick: () => handleSelectAccount(acct),
|
|
34088
|
+
children: loading ? "\u2026" : "Select"
|
|
34089
|
+
}
|
|
34090
|
+
)
|
|
34091
|
+
] }, acct.id)) }),
|
|
34092
|
+
apiError && /* @__PURE__ */ jsx("div", { className: "bcf-api-error", children: apiError }),
|
|
34093
|
+
/* @__PURE__ */ jsx("div", { className: "bcf-actions", children: /* @__PURE__ */ jsx(
|
|
34094
|
+
"button",
|
|
34095
|
+
{
|
|
34096
|
+
type: "button",
|
|
34097
|
+
className: "bcf-btn-back",
|
|
34098
|
+
onClick: () => {
|
|
34099
|
+
setStep("credentials");
|
|
34100
|
+
setApiError("");
|
|
34101
|
+
},
|
|
34102
|
+
children: "\u2190 Back"
|
|
34103
|
+
}
|
|
34104
|
+
) })
|
|
34105
|
+
] }),
|
|
34106
|
+
!autoConnecting && step === "done" && /* @__PURE__ */ jsxs("div", { className: "broker-connect-form", children: [
|
|
34107
|
+
/* @__PURE__ */ jsxs("div", { className: "bcf-success", children: [
|
|
34108
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 20 20", width: "28", height: "28", fill: "none", stroke: "#22c55e", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
34109
|
+
/* @__PURE__ */ jsx("circle", { cx: "10", cy: "10", r: "8" }),
|
|
34110
|
+
/* @__PURE__ */ jsx("polyline", { points: "6,10 9,13 14,7" })
|
|
34111
|
+
] }),
|
|
34112
|
+
/* @__PURE__ */ jsx("span", { children: "TopStep account connected successfully." })
|
|
34113
|
+
] }),
|
|
34114
|
+
/* @__PURE__ */ jsx("div", { className: "bcf-actions", children: /* @__PURE__ */ jsx("button", { type: "button", className: "bcf-btn-save", onClick: onClose, children: "Done" }) }),
|
|
34115
|
+
/* @__PURE__ */ jsx("div", { style: { textAlign: "center", marginTop: 12 }, children: /* @__PURE__ */ jsx("button", { type: "button", className: "bcf-link-btn", onClick: handleUseNewCredentials, children: "Use different credentials" }) })
|
|
34116
|
+
] })
|
|
34117
|
+
] })
|
|
34118
|
+
] });
|
|
34119
|
+
}
|
|
33870
34120
|
function BrokerCard({ broker, savedConnection, justConnected, onConnect, onStartTrading, onDisconnect, onUseNewCredentials, activeConnectionId }) {
|
|
33871
34121
|
const isConnected = !!(justConnected || savedConnection && savedConnection.id === activeConnectionId);
|
|
33872
34122
|
const isExpired2 = !isConnected && !!savedConnection?.isExpired;
|
|
@@ -33990,6 +34240,21 @@ function TradeDrawer({ onClose, savedConnections = [], onBrokerConnected, onDisc
|
|
|
33990
34240
|
);
|
|
33991
34241
|
}) }) })
|
|
33992
34242
|
] }),
|
|
34243
|
+
connectingBroker === "topstep" && (() => {
|
|
34244
|
+
const topstepSaved = newCredsBroker !== "topstep" ? savedConnections.find((c) => c.broker === "topstep") : void 0;
|
|
34245
|
+
return /* @__PURE__ */ jsx(
|
|
34246
|
+
TopStepConnectDrawer,
|
|
34247
|
+
{
|
|
34248
|
+
onClose: () => {
|
|
34249
|
+
setConnectingBroker(null);
|
|
34250
|
+
setNewCredsBroker(null);
|
|
34251
|
+
},
|
|
34252
|
+
onConnected: (connId) => markConnected("topstep", connId),
|
|
34253
|
+
...getAuthToken ? { getAuthToken } : {},
|
|
34254
|
+
...topstepSaved ? { savedConnection: topstepSaved } : {}
|
|
34255
|
+
}
|
|
34256
|
+
);
|
|
34257
|
+
})(),
|
|
33993
34258
|
connectingBroker === "rithmic" && (() => {
|
|
33994
34259
|
const rithmicSaved = newCredsBroker !== "rithmic" ? savedConnections.find((c) => c.broker === "rithmic") : void 0;
|
|
33995
34260
|
return /* @__PURE__ */ jsx(
|