Docs / Build
Sandbox and connectors
Write the logic that decides what to trade. Epoche handles scheduling, access to external services, and the checks needed before a proposal can move funds.
Where your code runs
Your code runs in a sandbox, an isolated process with restricted access to the network. Epoche records the identity of the code and runtime so it can check that the running software matches what was approved.
A platform supervisor schedules your code, monitors its health, handles retries, and answers checks about the running process. Keep these responsibilities out of your strategy. Your code should not certify its own health or identity.
The four hooks
Implement the four functions in AuthorHooks:
tick(context)makes the normal trading decision. It receives block information, validated observations, and the available budget. Return a list of proposals, or an empty list when you do not want to trade.unwind(context)proposes trades that return holdings to the settlement asset, the asset the account should hold when trading ends. It runs when the mandate ends, the investor exits, or permission is revoked. The context includes the reason, current holdings, and settlement asset.checkpoint()returns strategy state you need after a restart, such as the last processed observation. It does not store or restore trading permission.shutdown()stops the strategy and releases process resources. It proposes no trades and leaves asset holdings unchanged.
An empty response from unwindonly means the exit is complete when the account already holds nothing except the settlement asset. The platform checks the holdings rather than taking the strategy's word for it.
Proposing a trade
A proposal describes the trade you want. This helper creates a swap proposal that your tick or unwindhook can return. Pass permitted asset addresses and an amount in the sell token's smallest units.
import type { StrategyProposal } from "@epoche/agent-runtime";
export function proposeSwap(
sellAsset: `0x${string}`,
buyAsset: `0x${string}`,
sellUnits: bigint,
): StrategyProposal {
return {
kind: "exact-input-single-hop-swap",
sellAsset,
buyAsset,
sellUnits,
};
}The optional minimumBuyUnits field lets you require a higher minimum output. It cannot lower the minimum calculated by the platform.
The executor chooses the recipient, account, transaction destination, and call data. You cannot supply those in a proposal. It rejects extra fields at runtime, including fields that TypeScript's checks did not catch.
Getting external data
Request an approved operation from a named provider through the connector gateway. The gateway looks up that provider in a fixed registry and adds any API credentials outside your process.
Your strategy does not receive provider secrets or choose service URLs. It cannot add a new provider to the registry while running. Declare the data access your strategy needs rather than embedding API keys, RPC URLs, or arbitrary endpoints.
Prices used for execution
Your research signals help decide whether to trade. The platform uses its own approved price references to decide the minimum acceptable result of that trade.
It checks the source, units, freshness, and agreement between critical references. Missing, stale, or conflicting critical data stops new delegated execution. Your strategy cannot substitute a research signal to bypass that check.