3-ways-to-simplify-position-lifecycle
· 2 min read
Top 3 ways to simplify the position lifecycle
- Collapse patterns into a single declarative state machine
- What: Replace
BaseTradingContext+Template Method+JobFlowOrchestratorwith one finite-state machine (e.g., XState or a tiny hand-rolled map) that defines states, transitions, and actions. - How:
- Define transitions for
CALCULATED → OPEN → PROMOTED → CLOSED | ERRORin one place. - Attach actions for each transition (create position, create orders, sync).
- Drive next-step scheduling directly from the machine (no separate orchestrator).
- Define transitions for
- Impact:
- One source of truth for lifecycle logic.
- Fewer files: deprecate
base-trading-context.ts, shrinkjob-flow-orchestrator.service.ts. - Easier testing: pure transitions + effect handlers.
- Unify contexts via composition instead of inheritance/strategy
- What: Merge
LiveTradingContextandShadowTradingContextinto a singleTradingContextthat delegates IO to anExchangeAdapter(e.g.,BybitAdaptervsShadowAdapter). - How:
- Create
ExchangeAdapterinterface:submitOrder,getPositionInfo,getActiveOrders,setTradingStop, etc. - Inject adapter based on
PositionType(or account), dropTradingContextFactoryandBaseTradingContext. - Keep one code path; swap only the adapter.
- Create
- Impact:
- Removes
Strategy + Template Method. - Cuts duplicate logic; the business rules live in one place.
- Mocks become trivial (fake adapter).
- Removes
- Flatten queues and in-process orchestration
-
What: Use a single queue (
KairosQueueName.POSITION) and handle next-step decisions inside the consumer. Keep retries/backoff but drop cross-queue orchestration. -
How:
- Consolidate
DYNAMIC_TPSL,POSITION_EXECUTION,POSITION_SCHEDULERinto one queue with namespaced jobs. - In
position-sync.consumer.ts, return a “next step” descriptor and enqueue it immediately (or call synchronously when cheap). - Keep cron-only scheduling separate if needed.
- Consolidate
-
Impact:
- Fewer moving parts; simpler mental model.
- Less cross-queue glue and fewer BullMQ handles.
- Easier observability around a single pipeline.
-
Bonus quick wins (low risk):
- Always create TP/SL as separate orders (already aligned with current code/tests).
- Merge error statuses into a single
ERRORwithlastErrormetadata. - Remove
canHandlePosition()and factory indirection once adapter-based composition is in.
Summary
- Consolidate lifecycle into one declarative machine.
- Replace contexts with an adapter-based single implementation.
- Collapse queues and inline orchestration in one consumer.