MangroveAI Python SDK
Python SDK for the MangroveAI trading strategy platform.
Install
pip install mangroveai
The package imports as
mangrove_ai(e.g.from mangrove_ai import MangroveAI). The legacyimport mangroveaipath also still works for backwards compatibility.
Setup
- Create an account at mangrovedeveloper.ai
- Navigate to Settings > API Keys
- Generate a new API key
- Set it as an environment variable:
macOS / Linux (bash/zsh):
export MANGROVE_API_KEY=prod_your_key_here
Windows (PowerShell):
$env:MANGROVE_API_KEY = "prod_your_key_here"
Windows (cmd.exe):
set MANGROVE_API_KEY=prod_your_key_here
Private configuration
For an agent or local script, keep the key in private configuration outside your
repository. examples/first_call.py reads a JSON file
containing api_key and optional base_url, disables dotenv loading, and makes
one usage request. Restrict the file's permissions and do not commit it.
Existing environment variable and optional mangroveai[dotenv] loading remain
supported for compatibility; use load_dotenv=False to disable file discovery.
First API call
from mangrove_ai import MangroveAI
# api_key comes from private configuration; do not paste it into source.
with MangroveAI(api_key=api_key, load_dotenv=False, timeout=15, auto_retry=False) as client:
metrics = client.users.get_my_metrics()
print(metrics.messages_count)
This calls GET /api/v1/users/me/metrics with Bearer authentication. It does not
create a strategy or launch a backtest. The SDK surfaces 401, 402, 403, 429 and
503 as typed errors, including available retry and correlation fields. It does
not purchase access or pay automatically.
The following sections also describe features already present in source but not yet published in the current PyPI 2.0.0 wheel. See the release handoff for release prerequisites.
Quickstart
from mangrove_ai import MangroveAI
client = MangroveAI() # reads MANGROVE_API_KEY from environment
# List trading signals
signals = client.signals.list(limit=10)
for s in signals.items:
print(f"{s.name} ({s.category}, {s.signal_type})")
# Get market data
btc = client.crypto_assets.get_market_data("BTC")
print(f"BTC: ${btc.data['current_price']:,.2f}")
# Create a strategy
from mangrove_ai.models import CreateStrategyRequest
strategy = client.strategies.create(CreateStrategyRequest(
name="RSI Momentum",
asset="BTC",
entry=[{"name": "rsi_oversold", "signal_type": "TRIGGER",
"timeframe": "1d", "params": {"window": 14, "threshold": 30}}],
))
# Run a backtest (blocks until done; async-backed since v1.14, so long
# lookbacks work -- it submits to the async surface and polls internally)
import json
from mangrove_ai.models import BacktestRequest
result = client.backtesting.run(BacktestRequest(
asset="BTC",
interval="1d",
strategy_json=json.dumps({"name": "test", "asset": "BTC",
"entry": [{"name": "rsi_oversold", "signal_type": "TRIGGER",
"timeframe": "1d", "params": {"window": 14, "threshold": 30}}],
"exit": []}),
lookback_months=3,
initial_balance=10000,
min_balance_threshold=0.1, min_trade_amount=25,
max_open_positions=3, max_trades_per_day=10,
max_risk_per_trade=0.02, max_units_per_trade=1000000,
max_trade_amount=10000000, volatility_window=24,
target_volatility=0.1,
# Partial trades-model overrides retain the other server defaults.
# Loss pauses default to 36 bars, clamped to 24 hours through 14 days.
execution_config={
"circuit_breaker": {
"consecutive_loss_limit": 4,
"pause_bars": 36,
}
},
))
print(f"Trades: {result.trade_count}, Sharpe: {result.metrics.get('sharpe_ratio')}")
Compare circuit-breaker settings by submitting a complete new backtest for each setting over the same original history and seed. Reusing or filtering a prior trade list cannot measure the changed signals, sizing, fees, exits, and account balances produced by a full run.
The regular and Oracle single/bulk backtest request models accept cooldown_model,
max_drawdown_limit, and max_drawdown_halt_limit. The SDK places explicitly
supplied values in execution_config, where the engine reads them. Omitting a
threshold accepts the server default; explicitly passing None disables that
guard. Conflicting values in both locations raise ValueError before sending.
Disabling drawdown guards alone does not disable loss pauses: a fully unprotected
control also needs the explicit bars model and zero short_cooldown_bars and
long_cooldown_bars for every active timeframe. Keep valid positive loss limits
and window sizes. Legacy cooldown_config supplied on an SDK request is also
placed inside execution_config. Align these settings with any configuration
embedded in strategy_json or individual bulk strategy configs: Oracle's
per-strategy configuration precedence still applies. Preserve all other strategy
inputs, fees, and sizing between full runs.
Session authentication
API keys use Bearer authentication and do not refresh. client.auth.login()
accepts a Firebase ID token and installs the returned access token for core v1
and v2 requests. Keep the same client open: refresh uses its scoped mg_refresh
and mg_csrf cookies plus X-CSRF-Token, with no JSON body. Current login
responses omit the body refresh token; older missing or null fields are accepted.
auto_auth=True refreshes an expired JWT once, then retries once. Use
auto_auth=False to handle expiration yourself. Supplying a body refresh token
raises a local configuration error because the current API does not accept it.
Unsupported compatibility methods
execution.update_account, execution.get_position, crypto_assets.risk_analysis
and the three retired WhaleAlert methods raise locally before HTTP because the
current API does not expose those operations. Use the documented resources.
execution.list_positions(open_only=False) includes both states. Legacy
status="closed" filters the returned server page locally; offsets still refer
to all server rows, so a filtered page can contain fewer than limit rows.
Caller-owned execution and confirmed fills
Mangrove owns enforcement of configured user limits and approval policy. The SDK submits documented requests and preserves Mangrove decisions and audit results; it does not implement a separate trading safety policy.
For an external executor, evaluate_by_object(..., persist=False) proposes
orders from the caller's actual ledger. Even an order marked filled in an
evaluation is an estimate until that executor confirms it. The result separates:
open_positions: the authoritative input book, unchanged by proposed fills.proposed_positions: entry templates with stable position IDs and brackets.proposed_closes: estimated net exit results.closed_trades: confirmed engine-owned simulated fills only.
Both proposal fields carry execution_confirmed=False. Book an entry or close
only from the executor's actual fill, including its actual fees and net P&L.
Retain returned execution_state risk counters and candle cursors, then overlay
the actual account balances and position book before evaluating again. Preserve
gross cumulative deposits and the net funding reference separately.
Confirmed close delivery belongs inside the inline strategy:
# strategy contains rules/config plus persisted risk state overlaid with the
# actual ledger. ledger_* values below come from your executor's durable store.
payload = {
**strategy,
"closed_positions": ledger_closed_positions,
"closed_positions_bootstrap_complete": ledger_history_is_complete,
}
result = client.execution.evaluate_by_object(
payload, open_positions=ledger_open_positions, persist=False,
)
Each close has a stable position_id, exit_timestamp, actual realized_pnl,
and the ledger's booked_at timestamp. Deliver the complete initial confirmed
history before marking bootstrap complete; an empty complete history is valid
for a new account. In the trades model, new entries are held until that initial
history is acknowledged. Exits continue, and the returned risk state reports
entry_block_reason="close_history_bootstrap_required". This also applies to
inline drafts and requests that omit open_positions; omission does not attest
an empty history. Keep delivering new closes until the returned risk state
acknowledges their position IDs. Do not clear risk state, reset halts, or turn an
unfilled proposal into history when retrying. When flat, send explicit
open_positions=[].
For evaluate_bulk, put these same three fields, including open_positions,
inside each strategy config. Check each result's success independently.
persist=True with a saved strategy ID instead uses the engine's simulated fill
ledger; it does not confirm execution at an external venue. persist=True
without a strategy ID does not confirm fills.
Services
Layer 1: MangroveAI Core API
| Service | Access | Methods | Description |
|---|---|---|---|
client.auth |
auth.* |
5 | Login, refresh, API key management |
client.strategies |
strategies.* |
9 | Strategy CRUD, status, execution state, verify_strategy (pre-backtest conformance check) |
client.backtesting |
backtesting.* |
8 | Sync/async/bulk backtesting, get_benchmark (buy-and-hold over a window) |
client.market_data |
market_data.* |
2 | get_market_regime (direction over 90/180/365d + volatility + the lookback in sweep-catalog labels, for crypto and listed stocks, ETFs, commodities and bonds such as SPY, GOLD and CRUDE), classify_market_segment (one named stretch in sweep-catalog labels) |
client.config |
config.* |
3 | Trading/execution defaults, get_execution_config_schema (each parameter's default, effect, bounds, status) |
client.oracle |
oracle.* |
28 | SIEVE scoring, parameter sweeps/experiments, corpus data queries, backtests, simulation, leaderboard |
client.signals |
signals.* |
8 | Signal discovery, evaluation, validation, query_signal_behavior (measured selectivity / firing rate by parameter setting) |
client.crypto_assets |
crypto_assets.* |
10 | Assets, approved universe, per-asset exchanges, OHLCV, market data |
client.execution |
execution.* |
8 | Accounts, positions, trades, evaluation |
client.on_chain |
on_chain.* |
11 | Smart-money flows, DEX/perp trades and token holders (Nansen); retired WhaleAlert compatibility methods fail locally |
client.defi |
defi.* |
8 | TVL/chain/stablecoins (any plan); token unlocks, perp funding, treasuries, ETF flows, lending rates (DeFiLlama Pro -- requires Pro/Startup/Enterprise, capped at 1,000 calls/account/month) |
client.social |
social.* |
3 | Topic sentiment, mentions, user influence (X / Twitter) |
client.docs |
docs.* |
2 | Documentation listing and content |
client.config.get_execution_config_schema() makes an explicit server request
and exposes the canonical normalized authoring schema as result.json_schema,
alongside defaults and the parameter glossary. Older servers may omit this
additive field, in which case it is None. Nested glossary metadata includes
cooldown fields and the backend's required-field declarations. The SDK does not
fetch this metadata automatically or use it to impose trading policy. Authoring
guidance is separate from accepted legacy transport shapes; the HTTP boundary
does not enforce the entire nested authoring schema.
Layer 2: Knowledge Base API
| Service | Access | Methods | Description |
|---|---|---|---|
client.kb.documents |
kb.documents.* |
3 | Document listing, content, sections |
client.kb.search |
kb.search.* |
1 | Full-text search with BM25 ranking |
client.kb.tags |
kb.tags.* |
2 | Tag listing and filtering |
client.kb.glossary |
kb.glossary.* |
3 | Glossary terms and backlinks |
client.kb.signals |
kb.signals.* |
2 | Signal metadata from KB |
client.kb.indicators |
kb.indicators.* |
2 | Indicator metadata from KB |
client.kb.compute |
kb.compute.* |
2 | x402 paid signal/indicator computation |
On-chain capability surface
client.on_chain covers Mangrove's supported Nansen Pro operations. Three WhaleAlert methods remain as compatibility stubs and raise locally without making a request:
| Method | Source | What it returns |
|---|---|---|
get_onchain_series(symbol, metrics, date_from, date_to, interval, provider) |
Nansen | Per-bar metric time series (SmartMoneyNetflow, SmartMoneyHoldings, ExchangeNetflow, WhaleNetInflow, HolderConcentration) — one column per metric |
get_smart_money_sentiment(symbol) |
Nansen | Single-token accumulation/distribution score |
screen_smart_money(chains, timeframe) |
Nansen | Tokens with high smart-money activity |
get_smart_money_historical_holdings(chains, date_range, filters, order_by) |
Nansen | Date-stamped holdings snapshots |
get_smart_money_dex_trades(chains, filters, order_by) |
Nansen | Live DEX trades by smart-money wallets |
get_smart_money_perp_trades(filters, order_by) |
Nansen (Hyperliquid) | Perpetual-futures trades by smart-money wallets |
get_token_holders(symbol) |
Nansen | Holder distribution + concentration |
get_token_dex_trades(symbol, chain, date_range, filters, order_by) |
Nansen | Single-token DEX trades across all participants |
get_token_flows(symbol, chain, date_range, label, filters, order_by) |
Nansen | Per-wallet-category hourly flow rows (label scopes to smart_money/exchange/whale/…; excludes stablecoins) |
On-chain time series → signals. get_onchain_series returns a per-bar series for any window — the
same call serves a live trailing window (e.g. last 10 days, ending now) or a long historical range.
Build a DataFrame with pd.DataFrame(resp.series).set_index("timestamp") and feed it to a
mangrove-kb on-chain signal. End-to-end walkthrough:
examples/onchain_signals_demo.py.
filters and order_by pass through directly to the upstream Nansen API — restrict by
include_smart_money_labels, set value_usd min/max bounds, sort by any field. See
examples/on_chain_nansen.py for raw-method snippets.
Oracle — SIEVE + sweep
client.oracle.* is Mangrove's strategy-research engine. Two headline tools:
- SIEVE scores up to 99 strategy ideas in one call and returns binary go/no-go
probabilities (
p_tradesvsp_no_trades) for whether a strategy will trade. The retiredfour_classoutcome head isNoneon current responses; do not use it to predict performance or rank strategies. - Sweep fans a parameter search into one managed experiment of many backtests and ranks the results.
Score ideas with SIEVE
from mangrove_ai import MangroveAI
from mangrove_ai.models.oracle import SieveScoreRequest, StrategyInput, SignalSpec
client = MangroveAI() # reads MANGROVE_API_KEY
resp = client.oracle.sieve_score(SieveScoreRequest(strategies=[
StrategyInput(
asset="BTC",
entry=[SignalSpec(name="ema_crossover", signal_type="TRIGGER", timeframe="1h")],
exit=[SignalSpec(name="rsi", signal_type="FILTER", timeframe="1h")],
),
# ...up to 99 strategies per request
]))
print(f"scored {resp.count} | model {resp.model_version}")
for p in resp.predictions:
print(f" go/no-go: {p.binary}") # {'p_no_trades': .., 'p_trades': ..}
# SIEVE is a go/no-go gate (will it trade?), not a performance prediction:
# backtest the strategies it expects to trade. `four_class` is retired (None).
Run a parameter sweep (experiment lifecycle)
A sweep is a draft experiment you validate, then launch; it fans out into backtests
asynchronously. Poll get_experiment / list_results to track it.
# Pick a dataset + execution defaults from the API, then build a config
ds = client.oracle.list_datasets()[0]
exec_config = client.oracle.exec_config_defaults()
config = {
"name": "ema-sweep-demo",
"kind": "single",
"search_mode": "random",
"seed": 42,
"n_random": 50, # number of random strategies to draw
"datasets": [ds],
"random_signals": {
"n_entry_triggers": 1, "min_entry_filters": 0, "max_entry_filters": 2,
"min_exit_triggers": 0, "max_exit_triggers": 1,
"min_exit_filters": 0, "max_exit_filters": 1,
"n_param_draws": 3, "allowed_categories": None,
},
"execution_config": exec_config,
}
created = client.oracle.create_experiment(config) # -> status "draft"
val = client.oracle.validate_experiment(created.experiment_id)
print(f"valid={val.valid} total_runs={val.total_runs} errors={val.errors}")
if val.valid: # must pass before launch
client.oracle.launch_experiment(created.experiment_id) # fans out asynchronously
status = client.oracle.get_experiment(created.experiment_id)
print(f"status={status['status']} completed={status.get('completed_runs')}")
# client.oracle.pause_experiment(id) / delete_experiment(id) to stop or clean up
results = client.oracle.list_results(experiment_id=created.experiment_id, limit=20)
Canonical Sieve and sweep workflows
For the same Sieve and sweep operations advertised by MangroveAI MCP, use
client.strategy_workflow.screen_candidates, list_sweep_markets, size_sweep,
get_sweep_limits, create_sweep, launch_sweep, get_sweep, list_sweeps,
find_sweep_runs, list_run_filters, get_sweep_run, and pause_sweep.
These methods call /strategy-workflow/<name> and leave composition, sizing,
ranking, ownership and pricing to the server. Load sweeps or sweep-results
with client.strategy_workflow.load_skill for the current workflow. Creating
a sweep does not launch it; launch only after agreeing to the server's run count.
The SDK uses its existing authenticated HTTP transport; it does not add a wallet
or sign x402 payments. The agent's existing MCP payer handles wallet access.
The lower-level client.oracle.* methods retain their existing raw-engine contracts.
Other client.oracle.* methods
- Backtests:
backtest,backtest_async+backtest_poll,backtest_bulk - Corpus query:
data_query(curated BigQuery proxy over the results corpus) - Simulation:
simulate_run,simulate_generate,simulate_presets,simulate_history - Results & catalogs:
list_results,list_datasets,list_signals,list_templates,exec_config_defaults - Leaderboard & live:
leaderboard,list_deployed_strategies,get_deployed_strategy_state,get_deployed_strategy_events
Full reference: SIEVE pre-filter guide, SIEVE end-to-end, and the Experiments API reference.
Environment Detection
The SDK auto-detects the environment from your API key prefix:
| Prefix | Environment | API Base URL |
|---|---|---|
prod_ |
Production | https://api.mangrovedeveloper.ai/api/v1 |
dev_ |
Development | Explicit base_url or MANGROVE_BASE_URL required |
Development never falls back to production. A test_ key also requires an
explicit base URL or local environment. Base URL overrides apply to v1 and v2;
pass a base ending in /api/v1. The Knowledge Base has its own kb_base_url.
Override with explicit parameters:
client = MangroveAI(api_key="...", base_url="http://localhost:5001/api/v1")
Error Handling
from mangrove_ai import MangroveAI, NotFoundError, RateLimitError, APIError
client = MangroveAI()
try:
strategy = client.strategies.get("nonexistent-id")
except NotFoundError as e:
print(f"Not found: {e.message} (correlation_id={e.correlation_id})")
except RateLimitError as e:
print(f"Rate limited, retry after {e.retry_after}s")
except APIError as e:
print(f"[{e.status_code}] {e.code}: {e.message}")
Pagination
Paginated endpoints return PaginatedResponse[T]:
# Single page
page = client.strategies.list(skip=0, limit=10)
print(f"Showing {len(page.items)} of {page.total}")
# Auto-paginate all items
for strategy in client.strategies.list_iter():
print(strategy.name)
Examples
See the examples/ directory for working scripts.
Development
git clone https://github.com/MangroveTechnologies/MangroveAI-SDK.git
cd MangroveAI-SDK
pip install -e ".[dev]"
pytest tests/ --ignore=tests/integration # unit tests
MANGROVE_API_KEY=... pytest tests/integration/ -m integration # live tests
Metadata
Release files for mangroveai 2.1.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 |
|---|---|---|---|---|
| mangroveai-2.1.0.tar.gz | 412.3 kB | Without provenance | Details for mangroveai-2.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| mangroveai-2.1.0-py3-none-any.whl | Python 3 | none | any | Details for mangroveai-2.1.0-py3-none-any.whl |
Total release size: 516.5 kB
Release files / mangroveai-2.1.0.tar.gz
| Download URL | mangroveai-2.1.0.tar.gz |
|---|---|
| Size | 412.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b120e9cd0dfa7252a9e2f926b3aefde181e5e032c043dac165635c2422059aab
|
|
BLAKE2b-256 checksum How to use checksums |
ee66aa7c9923d01faeff13b27dfc93c5512d31a6f59d67b4734784da32bb60a1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Release files / mangroveai-2.1.0-py3-none-any.whl
| Download URL | mangroveai-2.1.0-py3-none-any.whl |
|---|---|
| Size | 104.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
66c63d7eab97ec0aac913dbc5df4ffd00067d3f3db92fd1fac221aeeb58a01eb
|
|
BLAKE2b-256 checksum How to use checksums |
5a66ad6d4b95a0acd5014f292faf48e2798149e4bb11e004d7d1dc874dc60174
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Security for mangroveai 2.1.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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A 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.