edenalpha
Write trading strategies in Python. Run them against the EdenAlpha engine — the same engine, fills, charges, and risk controls behind every EdenAlpha backtest, paper deployment, and live deployment. Two ways to backtest: hosted, where your file runs on EdenAlpha compute, and local sessions, where your code stays on your machine and drives the engine bar by bar. Deployment happens in the web app.
# my_strategy.py
from edenalpha import strategy, Feature
@strategy(features=[Feature(name="rsi", period=14)])
def decide(ctx):
if ctx.position.is_open and ctx.features["rsi_14"] > 55:
return "EXIT"
if not ctx.position.is_open and ctx.features["rsi_14"] < 30:
return "BUY"
return "HOLD"
pip install edenalpha
edenalpha login # paste an API key from Settings -> API keys
edenalpha backtest my_strategy.py \
--symbol RELIANCE --timeframe 15m \
--start 2026-06-01 --end 2026-07-01
Beta. The SDK is in open beta and enabled per account: sign up at edenalpha.in, then request SDK access to have API-key creation turned on in Settings. Hosted backtests and local single/portfolio sessions work today; paper deployment from the SDK is not exposed yet (deploy from the web app). Python backtests inside the web app need no grant. The
decidecontract is versioned through the backward-compatible/api/sdk/v1contract.
How it works
Your function is the agent; EdenAlpha is the world. On every bar
close the engine hands you a Ctx — the bar, your declared indicators
(computed server-side, identical to the web rule builder), your position,
your cash, and the available bar history — and you answer BUY, SELL
(opens a short), EXIT, or HOLD. Hosted backtests expose the full run
through the current bar; paper deployments expose a rolling window from
deployment time. Fills, charges, sizing, stop-losses, and
square-off stay in the engine, so a backtest here is directly comparable to
every other EdenAlpha run. The same strategy contract powers paper
deployments in the EdenAlpha web app.
The contract does not change with where your code runs: one decide
function works hosted, driven locally through run_local, or stepped by hand
in your own loop.
- Sizing is server-owned. You return direction; quantity comes from the run's sizing configuration. A strategy that can't over-size in a backtest can't over-size live.
- Broker-agnostic. Nothing in this contract names a broker; execution routing happens server-side behind your deployment settings.
- Typed everywhere.
Ctx,Bar,Position,Decisionare Pydantic models with full annotations (py.typedshipped) — your IDE's autocomplete is the API reference. - Unit-testable.
@strategyreturns a callable: build a fakeCtxand assert on the returnedDecisionin plain pytest, no network involved.
Shared-pool portfolio strategies
The web app also supports hosted Python portfolio backtests and paper deployments over up to 50 symbols. One agent sees the latest state of the whole universe while EdenAlpha keeps ownership of slots, quantities, fills, charges, risk, and square-off:
from edenalpha import Feature, Order, portfolio_strategy
@portfolio_strategy(
features=[Feature(name="rsi", period=14, alias="rsi_14")],
lookback_bars=50,
)
def decide(pf):
orders = []
for view in pf.holdings():
if view.actionable_this_step and view.features["rsi_14"] > 55:
orders.append(Order(symbol=view.symbol, action="EXIT"))
for view in pf.symbols.values():
rsi = view.features.get("rsi_14")
if view.actionable_this_step and not view.position.is_open and rsi is not None and rsi < 30:
orders.append(Order(
symbol=view.symbol,
action="BUY",
score=30 - rsi,
reason="oversold",
))
return orders
Higher score proposals are admitted first. With unequal configured slot
weights, higher scores also receive the larger available slots. The same file
can run locally while EdenAlpha keeps the shared-pool book server-side:
outcome = client.run_local_portfolio(
"portfolio_strategy.py",
symbols=["RELIANCE", "HDFCBANK", "INFY", "TCS"],
timeframe="15m", start="2026-06-01", end="2026-07-01",
pool_capital=100_000,
slot_weights=[25, 25, 25, 25],
)
print(outcome.stats.net_return_pct)
client.portfolio_session(...) exposes the lower-level step loop for custom
optimizers and RL policies. It uses the existing portfolio engine; the local
policy only supplies ranked proposals.
Python API
import edenalpha
client = edenalpha.Client() # auth: EDENALPHA_API_KEY or `edenalpha login`
outcome = client.backtest(
"my_strategy.py",
symbol="RELIANCE", timeframe="15m",
start="2026-06-01", end="2026-07-01",
)
print(outcome.summary.net_return_pct)
for trade in outcome.trades:
print(trade["entry_time"], trade["net_pnl"])
Errors are typed (AuthenticationError, ScopeError,
InsufficientCreditsError, RateLimitError, StrategyError,
TransportError, EngineError) and retriable statuses (429/5xx) are retried
with backoff automatically.
Hosted execution
client.backtest(...) runs your file on EdenAlpha compute next to the data
(requires the backtest:hosted scope). Hosted strategies are single
self-contained files with an import allowlist (numpy, pandas,
scikit-learn, joblib, onnxruntime, torch (CPU), and the
computation-flavored stdlib). Inside hosted compute, auth is ambient — the
runner injects the session; your code never handles keys.
Trained models
Train anywhere, upload once, and load the model inside a hosted strategy
(requires the models:write / models:read scopes):
edenalpha models upload policy.onnx --name ppo-v3 # .onnx, .pt/.pth, .pkl, .joblib
edenalpha models list
import onnxruntime as ort
from edenalpha import model_path, strategy
session = ort.InferenceSession(model_path()) # loaded once, not per bar
@strategy()
def decide(ctx):
...
Attach the model to the strategy (web editor, or client.backtest(..., model=id)).
Hosted, model_path() is your model file mounted read-only; locally it reads
EDENALPHA_MODEL_PATH or model_path("policy.onnx"), so the same file runs in
both. Models are private to your account and immutable. ONNX is recommended:
pickle/joblib/torch files load reliably only on the library version they were
saved with — models upload records your versions and EdenAlpha flags a
mismatch with the runner (it blocks paper deploys for those formats).
Local sessions — your code stays on your machine
A session inverts the loop: instead of uploading your file, you drive the
engine one bar at a time over HTTPS. The engine still owns fills, charges,
sizing, and risk — you only answer with a direction. Requires the
session:backtest scope.
The simplest form takes the same @strategy file as client.backtest:
outcome = client.run_local(
"my_strategy.py",
symbol="RELIANCE", timeframe="15m",
start="2026-06-01", end="2026-07-01",
)
print(outcome.summary.net_pnl)
decide() runs on your computer, so local imports, private models, and
anything else you would rather not upload stay put. No import allowlist
applies to code that never leaves your machine.
The convenience methods derive features from the decorated file, and
run_local_portfolio() also derives lookback_bars. You may pass label= and
normal run settings, but strategy-owned declarations cannot be overridden at
the call site.
For research loops — walk-forward, notebooks, policy inference — drive the session yourself:
from edenalpha import Feature
with client.session(symbol="RELIANCE", timeframe="15m",
start="2026-06-01", end="2026-07-01",
features=[Feature(name="rsi", period=14)]) as sess:
t = sess.start()
while not t.done:
rsi = t.ctx.features["rsi_14"]
t = sess.step("BUY" if rsi is not None and rsi < 30 else "HOLD")
print(sess.result().summary.net_pnl)
Each step() returns a Transition:
t.ctx— the next decidable bar.ctx.historyis rebuilt locally from incremental updates, so a long run never retransmits its past.t.ack— what happened to the action you just submitted. Exactly one action is accepted per bar sequence number, so a runaway loop cannot fire twice for the same bar.t.events— what the engine did, as typed events:FilledEvent,OrderPendingEvent,EntrySkippedEvent,ActionIgnoredEvent. "Accepted" is never dressed up as "filled" — with the defaultnext_openfill policy the fill arrives on the following transition.t.done— the run is over andsess.result()holds the full outcome.
A session costs the same one backtest credit as any other run, and completed
sessions are saved to your Runs page alongside web and hosted backtests.
Use the context manager (or call sess.close()) so an abandoned session
releases its server-side slot promptly.
The portfolio equivalent returns a PortfolioCtx and accepts a list of
Order proposals:
from edenalpha import Order
with client.portfolio_session(
symbols=["RELIANCE", "HDFCBANK"], timeframe="15m",
start="2026-06-01", end="2026-07-01",
lookback_bars=32,
) as sess:
t = sess.start()
while not t.done:
orders = []
for symbol, view in t.ctx.symbols.items():
if view.actionable_this_step and not view.position.is_open:
momentum = view.bar.close / view.history[0].close - 1
if momentum > 0:
orders.append(Order(symbol=symbol, action="BUY", score=momentum))
t = sess.step(orders)
print(sess.result().stats.net_pnl)
Declared features
Any indicator from the EdenAlpha catalog (the same one behind the web rule builder — Strategies → Reference lists all ~57):
Feature(name="rsi", period=14) # ctx.features["rsi_14"]
Feature(name="vwap") # ctx.features["vwap"]
Feature(name="sma", period=50, alias="slow_ma") # ctx.features["slow_ma"]
Feature(name="macd", params={"fast": 12, "slow": 26, "signal": 9},
outputs={"line": "macd", "signal": "macd_sig", "histogram": "macd_hist"})
Raw OHLCV (open, high, low, close, volume) is always present in
ctx.features. Prefer declared features over hand-rolled ones — they're
computed by the exact code that will feed your strategy in paper/live, so
train/serve skew can't happen.
Choosing a feature path
- Declared
Featureis best when parity matters. EdenAlpha computes it consistently for backtest and hosted paper execution. - Custom finite-window feature uses
lookback_barson a@portfolio_strategyand derives a value from eachview.history. Hosted imports remain restricted to the supported runtime libraries. - Local SDK / RL observation derives any fixed-size observation from
ctx.historyorview.historywith your own local libraries. Your model and feature code never leave your machine.
Gymnasium and RL
Session and PortfolioSession provide the environment primitive, not a
ready-made gymnasium.Env: start() supplies the first observation,
step(action) advances the certified engine, and result() supplies the
terminal run. A Gymnasium adapter should convert the typed context into a
fixed-size NumPy observation, define a fixed action space, and derive reward
from equity or engine events. That adapter can then be used by libraries such
as Stable-Baselines3.
Remote sessions use HTTPS and consume backtest credits. They are suited to engine-faithful evaluation, parity checks, and selected policy rollouts. For millions of training steps, train in a local in-memory environment first, then evaluate the resulting policy through EdenAlpha before paper deployment.
Requirements & license
Python 3.10+. MIT licensed — the SDK is open; the EdenAlpha engine and platform are a separate, server-side service.
Metadata
Release files for edenalpha 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| edenalpha-0.4.0.tar.gz | 27.7 kB | With provenance | Details for edenalpha-0.4.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| edenalpha-0.4.0-py3-none-any.whl | Python 3 | none | any | Details for edenalpha-0.4.0-py3-none-any.whl |
Total release size: 61.5 kB
Release files / edenalpha-0.4.0.tar.gz
| Download URL | edenalpha-0.4.0.tar.gz |
|---|---|
| Size | 27.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9caf9c9464f3e088b053fd322e95346fb78d3627c6ead468fcfe7023cc29eb54
|
|
BLAKE2b-256 checksum How to use checksums |
c1690848ce8eb0244f20bfc329b71ce36d1e043ed78a4f0a66885bbd25eb02e4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.
Transparency logRelease files / edenalpha-0.4.0-py3-none-any.whl
| Download URL | edenalpha-0.4.0-py3-none-any.whl |
|---|---|
| Size | 33.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
90d7a0dc208e3c53d27135cf25945296f6a341824e89850725f5a1c4c565153e
|
|
BLAKE2b-256 checksum How to use checksums |
689819b966501713232b3de6a909a954ab8e42a5a1ae6f4f5f1fc5de1f53a4de
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.
Transparency logSecurity for edenalpha 0.4.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for edenalpha-0.4.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 10, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.