Skip to content

Latest commit

 

History

History
125 lines (101 loc) · 5.12 KB

strategy.md

File metadata and controls

125 lines (101 loc) · 5.12 KB

Strategy contract

Pass a module/object to gpu_backtest.run, or an installed dotted module name such as my_strategies.example. The separate example module is gpu_backtest_examples.rsi.strategy. The old rsi_meanrev shorthand is translated only at the public API/CLI edge; the core imports no example strategy. A local engine run imports your installed plugin without copying it. The optional RunPod helper uploads selected strategy source and input data to the rented GPU.

Required declarations

NAME = "example"
ENTRY_DIMS = [("entry_period", 2, 4, 2, False)]
EXIT_DIMS = [("exit_period", 2, 4, 2, False)]
TABLES = [("rsi", "close", ["entry_period", "exit_period"])]

Each dimension is (name, low, high, step, is_float). Endpoints are inclusive; high must lie on the grid. Names must be unique across both sides. Each side has one to four dimensions. Overrides may change ranges, but must preserve the declared names and order. Table windows must be positive integer dimensions. A single combination on either side is supported, including raw file output.

The last dimension varies fastest. ep and xp are four-element float64 arrays; only the slots corresponding to declared dimensions are defined. Integer parameters arrive as float64 values and must be converted before indexing.

Indicator tables

TABLES contains up to four (kind, source, [dimension_names]) entries. For each entry, the union of the specified window values determines table rows.

Kind Definition
roll_max, roll_min, roll_mean pandas rolling window, including the current bar
roll_std pandas rolling sample standard deviation (ddof=1)
ema ewm(span=window, adjust=False), seeded with the first value
rsi EWM gains/losses with alpha=1/window, adjust=False; seeded with the first delta
atr EWM true range with alpha=1/window, seeded with the first high-minus-low

Sources are close, open, high, low, and volume; ATR uses OHLC and ignores the source. Tables are float32; the row maps are int64. rm[window] gives the row for a configured window. Rolling warmup values and the first RSI value are NaN. RSI uses 100 when smoothed loss is zero, including a flat-price sequence; this is an explicit example convention, not every RSI library's initialization. Unused table slots are 1×1 dummy arrays and must not be indexed as full tables.

GPU callback

def make_device_fn(cuda):
    @cuda.jit(device=True, inline=True)
    def algo(
        close,
        open_,
        high,
        low,
        vol,
        t0,
        t1,
        t2,
        t3,
        rm0,
        rm1,
        rm2,
        rm3,
        ep,
        xp,
        num_bars,
        buy,
        sell,
    ):
        # Implement your complete trading loop with CUDA-compatible operations.
        return 0.0  # Replace with total return in percent.

    return algo

Market arrays are float32. Fees are fractions (0.0015 means 0.15%). The device function must return a finite number and must give the same result in both reduction passes. Do not use randomness, shared mutable state, Python callbacks, or future market values. The callback must compile under Numba CUDA.

Trading semantics are owned by the plugin; the harness does not enforce signals, broker accounting, or position sizing. To compare a plugin with a CPU reference, provide:

def reference(
    close, open_, high, low, vol, tabs, rms, ep, xp, buy, sell
): ...  # Independent CPU implementation returning percent return.

gpu_backtest_tools.checks.reference.reference_group_sums uses this reference on small grids. Supply a hand-calculated case as well: matching two implementations alone does not establish that the intended rules are correct.

Example

See the separate RSI example for a complete plugin, runnable config/data, and its execution/fee conventions.

Raw results

run() returns entry_sum, entry_sumsq, exit_sum, exit_sumsq, each float64. With an output prefix it saves the same arrays in <prefix>_results.npz, plus a schema-2 <prefix>_manifest.json. Entry arrays have entry_count elements; exit arrays have exit_count elements. Each side aggregates across all combinations of the opposite side; these are grouped results, not a full pair-return matrix. Each per-pair return and square rounds to float32 before float64 accumulation.

Array order follows declared dimension order; the last dimension varies fastest. The manifest stores dimensions as [name, low, high, step, is_float], fees, counts, reduction checks, array dtypes/shapes and the NPZ SHA-256. Decode index i with gpu_backtest.core.grid.decode_values(dims, i) using the corresponding dimensions.

Output contains no scoring, ranking, probabilities or intervals. top_n and date split settings are no longer accepted. Read NPZ with numpy.load(..., allow_pickle=False) and apply your own downstream analysis.

The manifest marks completed output. A failed publication invalidates that marker; ignore an NPZ without its matching manifest. Check the recorded SHA-256 before consuming a persisted result pair.