mirror of
https://github.com/discountry/ritmex-bot.git
synced 2026-09-09 16:28:06 +00:00
Adding a strategy meant editing six places that had to agree: the StrategyId union and a parallel Set in args.ts, STRATEGY_LABELS and a nine-branch STRATEGY_FACTORIES in strategy-runner.ts, plus an inline id union and BASE_STRATEGIES in App.tsx. runEngine's type parameter was additionally bounded by a union of all nine snapshot types. They had already drifted: the CLI gated basis on isBasisSupportedExchangeId while the menu checked only isBasisStrategyEnabled, so the menu offered basis on exchanges where startStrategy would throw. - strategy-ids.ts: the id list and alias parsing, dependency-free so CLI arg parsing does not pull in every engine. - registry.ts: one definition per strategy (labels, symbol, engine factory, and a single unavailableReason both the menu and the runner consult), keyed by a total Record so a new id will not compile until it is defined. - StrategyEngine/StrategySnapshot interfaces replace the snapshot union; runEngine now depends only on the contract. All nine engines already satisfied it — no engine changed. - App.tsx keeps only the id -> Ink view map, also a total Record. Menu order preserved. 8 new tests, one pinning menu/CLI availability agreement. 234 pass; tsc clean. -242 lines.
70 lines
2.8 KiB
TypeScript
70 lines
2.8 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
STRATEGY_DEFINITIONS,
|
|
availableStrategies,
|
|
getStrategyDefinition,
|
|
strategyUnavailableReason,
|
|
} from "../src/strategy/registry";
|
|
import { STRATEGY_IDS, isStrategyId, parseStrategyId } from "../src/strategy/strategy-ids";
|
|
|
|
describe("strategy ids", () => {
|
|
it("resolves canonical ids and documented aliases", () => {
|
|
expect(parseStrategyId("trend")).toBe("trend");
|
|
expect(parseStrategyId(" GRID ")).toBe("grid");
|
|
expect(parseStrategyId("offset")).toBe("offset-maker");
|
|
expect(parseStrategyId("offsetmaker")).toBe("offset-maker");
|
|
expect(parseStrategyId("makerpoints")).toBe("maker-points");
|
|
expect(parseStrategyId("maker_points")).toBe("maker-points");
|
|
expect(parseStrategyId("liquidity")).toBe("liquidity-maker");
|
|
expect(parseStrategyId("liquidity_maker")).toBe("liquidity-maker");
|
|
});
|
|
|
|
it("rejects unknown names", () => {
|
|
expect(parseStrategyId("nope")).toBeNull();
|
|
expect(parseStrategyId("")).toBeNull();
|
|
expect(isStrategyId("nope")).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("strategy registry", () => {
|
|
it("defines every id exactly once, in menu order", () => {
|
|
expect(STRATEGY_DEFINITIONS.map((d) => d.id)).toEqual([...STRATEGY_IDS]);
|
|
expect(new Set(STRATEGY_DEFINITIONS.map((d) => d.id)).size).toBe(STRATEGY_IDS.length);
|
|
});
|
|
|
|
it("gives every strategy a console label and i18n keys", () => {
|
|
for (const definition of STRATEGY_DEFINITIONS) {
|
|
expect(definition.consoleLabel).toBeTruthy();
|
|
expect(definition.labelKey).toMatch(/^app\.strategy\./);
|
|
expect(definition.descriptionKey).toMatch(/^app\.strategy\./);
|
|
expect(typeof definition.symbol()).toBe("string");
|
|
}
|
|
});
|
|
|
|
it("gates maker-points to StandX", () => {
|
|
expect(strategyUnavailableReason("maker-points", "standx")).toBeNull();
|
|
expect(strategyUnavailableReason("maker-points", "aster")).toContain("StandX");
|
|
});
|
|
|
|
it("keeps the menu and the CLI on one availability rule", () => {
|
|
// The menu shows exactly what startStrategy would accept — the two used to
|
|
// disagree, so basis appeared on exchanges where the runner then threw.
|
|
for (const exchangeId of ["aster", "standx", "backpack"] as const) {
|
|
const shown = availableStrategies(exchangeId).map((d) => d.id);
|
|
const runnable = STRATEGY_IDS.filter((id) => strategyUnavailableReason(id, exchangeId) == null);
|
|
expect(shown).toEqual(runnable);
|
|
}
|
|
});
|
|
|
|
it("hides strategies whose environment gate is closed", () => {
|
|
const shown = availableStrategies("backpack").map((d) => d.id);
|
|
expect(shown).not.toContain("maker-points");
|
|
});
|
|
|
|
it("exposes an engine factory per strategy", () => {
|
|
for (const id of STRATEGY_IDS) {
|
|
expect(typeof getStrategyDefinition(id).createEngine).toBe("function");
|
|
}
|
|
});
|
|
});
|