Files
ritmex-bot/src/strategy/strategy-ids.ts
T
discountry 9538ecf265 refactor(strategy): collapse six strategy lists into one registry
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.
2026-07-29 20:36:55 +08:00

45 lines
1.3 KiB
TypeScript

/**
* The canonical list of strategies. Dependency-free on purpose: CLI argument
* parsing imports this without dragging in every engine and its config.
*
* Adding a strategy starts here; `strategy/registry.ts` then fails to compile
* until the new id has a definition.
*/
/** Order is the interactive menu's order. */
export const STRATEGY_IDS = [
"trend",
"swing",
"guardian",
"maker",
"maker-points",
"grid",
"offset-maker",
"liquidity-maker",
"basis",
] as const;
export type StrategyId = (typeof STRATEGY_IDS)[number];
/** Spellings accepted on the command line beyond the canonical ids. */
const STRATEGY_ALIASES: Record<string, StrategyId> = {
offset: "offset-maker",
offsetmaker: "offset-maker",
makerpoints: "maker-points",
maker_points: "maker-points",
liquidity: "liquidity-maker",
liquiditymaker: "liquidity-maker",
liquidity_maker: "liquidity-maker",
};
export function isStrategyId(value: string): value is StrategyId {
return (STRATEGY_IDS as readonly string[]).includes(value);
}
/** @returns the strategy the input names, or null when it names none. */
export function parseStrategyId(raw: string): StrategyId | null {
const normalized = raw.trim().toLowerCase();
if (!normalized) return null;
if (isStrategyId(normalized)) return normalized;
return STRATEGY_ALIASES[normalized] ?? null;
}