AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Basis Arb

skill-superior-trade-superior-skills-basis-arb · by Superior-Trade

Use when the user asks for spot-perp basis trade, basis arbitrage, cash-and-carry, perp discount, or any setup that reads the spot–perp basis as a positioning signal. Long-perp leg only — pure two-leg basis arb requires a paired spot short (or long) which Freqtrade can't run cleanly. The strategy below captures the directional read, not the hedged carry.

No reviews yet
0 installs
4 views
0.0% view→install

Install

$ agentstack add skill-superior-trade-superior-skills-basis-arb

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-superior-trade-superior-skills-basis-arb)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
1mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Basis Arb? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Strategy: Basis Flipping — Directional (Long-Perp Leg)

Honest framing — read this first

True basis arbitrage is a two-leg trade:

  • Long perp + short spot (when perp trades at a discount to spot, basis 0)

You earn the basis as the legs converge. Freqtrade is a single-leg engine — it can't run paired hedged trades on the same ticker. The strategy below captures the directional signal that "basis flipping negative + funding negative = bullish positioning shift" and goes long the perp accordingly. It's a momentum read on positioning, not a hedged arb.

If you want the actual hedged version, run an external system (or a custom Hyperliquid-only multi-leg runtime). Don't deploy this template thinking it's market-neutral.

Backtest reference

| Window | BTC/USDC:USDC 1h, 2026-01-01 → 2026-05-01 | |---|---| | Trades | 0 (no spot leg available in the engine) | | Backtest ID | 01kr42hegps9w20njsty2cqb41 |

The Superior Trade backtest engine doesn't currently expose Hyperliquid spot OHLCV alongside perp pairs via Freqtrade's informative_pairs mechanism, so the spot_close column is never populated and the entry filter never fires. The strategy code is structurally sound — it runs cleanly to completion with 0 trades — but this template can't be validated end-to-end on the current backtest engine. Two paths:

  1. Validate live, paper-traded — run as a dry-run deployment for a week and compare the entries against an external basis tracker (CoinGlass basisHistory or a simple notebook).
  2. Substitute external basis feed — replace the informative_pairs spot fetch with a CoinGlass /api/futures/basis/history call from a side-channel cache. Out of scope for this template; would be a Phase 2 backend change.

The strategy is shipped as a directional blueprint, not a live-validated runtime. Treat it as a teachable template for how to wire spot-perp basis into a Freqtrade strategy, rather than an off-the-shelf deployment.

When to use

A user asks for:

  • "Basis trade", "basis arb", "perp discount", "cash and carry"
  • A directional follow-on to spot-side accumulation ("spot is buying, perp is short, going long the perp")
  • Anything where the basis flipping negative is the trigger

This template assumes Hyperliquid has both the spot and perp pair for the asset (BTC, ETH, SOL — the few HL has spot books for). For perp-only assets, this strategy can't compute basis and won't fire.

The Freqtrade primitive that makes this work

Two dp.get_pair_dataframe calls:

  1. The current perp's funding rate (same pattern as strategy-funding-rate-arbitrage)
  2. The corresponding spot pair's OHLCV via informative_pairs() so we can compute basis

Basis = (perp_mark - spot_mid) / spot_mid. Annualised by funding period.

Reference implementation

from freqtrade.strategy import IStrategy, informative
from datetime import datetime
import pandas as pd
import talib.abstract as ta

def perp_to_spot(pair: str) -> str:
    """`BTC/USDC:USDC` → `BTC/USDC`. HL spot lives at the un-suffixed pair."""
    return pair.split(":")[0] if ":" in pair else pair

class BasisFlippingStrategy(IStrategy):
    minimal_roi = {"0": 100.0}
    stoploss = -0.04
    trailing_stop = False
    timeframe = "1h"
    process_only_new_candles = True
    startup_candle_count = 30
    can_short = False

    def informative_pairs(self):
        # Tell Freqtrade we need spot-side OHLCV for every perp in the
        # whitelist. The base pair list is set by the user; we mirror
        # each entry to its spot equivalent.
        pairs = self.dp.current_whitelist()
        return [(perp_to_spot(p), self.timeframe) for p in pairs if ":" in p]

    def populate_indicators(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        perp_pair = metadata["pair"]
        spot_pair = perp_to_spot(perp_pair)

        # Funding rate via the dedicated candle type.
        try:
            funding = self.dp.get_pair_dataframe(
                pair=perp_pair,
                timeframe="1h",
                candle_type="funding_rate",
            )
        except Exception:
            funding = pd.DataFrame()
        if not funding.empty and "open" in funding.columns:
            f = funding[["date", "open"]].rename(columns={"open": "funding_rate"}).copy()
            dataframe = dataframe.merge(f, on="date", how="left")
            dataframe["funding_rate"] = dataframe["funding_rate"].ffill().fillna(0.0)
            dataframe["funding_apr"] = dataframe["funding_rate"] * 24 * 365
        else:
            dataframe["funding_rate"] = 0.0
            dataframe["funding_apr"] = 0.0

        # Spot OHLCV — declared via `informative_pairs`.
        try:
            spot = self.dp.get_pair_dataframe(pair=spot_pair, timeframe=self.timeframe)
        except Exception:
            spot = pd.DataFrame()
        # Initialise required columns up front so the entry guard can
        # read them even when the spot leg isn't available on this
        # dataset. Hyperliquid's backtest cache doesn't always expose
        # the matching spot pair via informative_pairs — without these
        # default columns the strategy crashes with KeyError on the
        # first candle.
        dataframe["spot_close"] = float("nan")
        dataframe["basis"] = 0.0
        dataframe["basis_apr"] = 0.0
        if not spot.empty and "close" in spot.columns:
            s = spot[["date", "close"]].rename(columns={"close": "spot_close"}).copy()
            dataframe = dataframe.drop(columns=["spot_close"])
            dataframe = dataframe.merge(s, on="date", how="left")
            dataframe["spot_close"] = dataframe["spot_close"].ffill()
            # Instantaneous basis (perp - spot) / spot. Annualise as
            # basis_apr ≈ basis * (8h_settlement_periods/year) ≈ basis * 1095
            # (3 settlements/day × 365). HL's actual basis convergence
            # path is messier but this is the standard back-of-envelope.
            dataframe["basis"] = (
                dataframe["close"] - dataframe["spot_close"]
            ) / dataframe["spot_close"]
            dataframe["basis_apr"] = dataframe["basis"] * 1095

        dataframe["atr_24"] = ta.ATR(dataframe, timeperiod=24)
        return dataframe

    def populate_entry_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        # Long perp when:
        #   1. Basis flipped negative (perp at discount to spot)
        #   2. Funding is also negative (shorts paying — same crowd)
        #   3. Spot isn't crashing (close > 24h SMA proxy)
        # Only fire when the spot leg is actually available — without
        # spot data the basis is meaningless and we'd be entering on a
        # zero-filled signal.
        sma_spot = dataframe["spot_close"].rolling(24).mean()
        spot_available = dataframe["spot_close"].notna()
        dataframe.loc[
            spot_available
            & (dataframe["basis_apr"]  sma_spot)
            & (dataframe["volume"] > 0),
            "enter_long",
        ] = 1
        return dataframe

    def populate_exit_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
        # Exit when basis converges back to neutral (≥ 0) — the structural
        # pressure has been worked off.
        dataframe.loc[(dataframe["basis_apr"] >= 0.0), "exit_long"] = 1
        return dataframe

    def custom_exit(self, pair: str, trade, current_time: datetime,
                    current_rate: float, current_profit: float, **kwargs):
        # Basis convergence is slow but persistent. 48h is the patience floor.
        elapsed_h = (current_time - trade.open_date_utc).total_seconds() / 3600.0
        if elapsed_h >= 48:
            return "timeout_48h"
        return None

Config requirements

{
  "exchange": { "name": "hyperliquid", "pair_whitelist": ["BTC/USDC:USDC"] },
  "stake_currency": "USDC",
  "stake_amount": 100,
  "timeframe": "1h",
  "max_open_trades": 1,
  "stoploss": -0.04,
  "minimal_roi": { "0": 100.0 },
  "trading_mode": "futures",
  "margin_mode": "cross",
  "entry_pricing": { "price_side": "same" },
  "exit_pricing": { "price_side": "same" },
  "pairlists": [{ "method": "StaticPairList" }]
}

Pair must have a corresponding HL spot pair. Today that's effectively BTC, ETH, SOL, HYPE — the few assets with both perp and spot books on Hyperliquid. Other perps will return zero basis and never fire.

Tunable parameters

| Knob | Effect | |---|---| | basis_apr sma_24 | The "spot isn't crashing" filter. Without it, the strategy buys into spot drawdowns where the basis is negative because everything's down. | | timeout_48h | Basis trades take days, not hours. Don't tighten below 24h. |

Variants

  • Hedged via external runtime: pair this with a spot-short (e.g. through Aerodrome or a CEX) for true delta-neutral. Out of scope for Freqtrade.
  • Reverse premium (variant): when basis > +0.10 APR with positive funding, short the perp. Mirror logic, requires can_short = True and an isolated-margin perp config.
  • Term-structure variant: skip same-pair basis and use the 1d-MA basis vs 1h basis as the signal. Less noisy, more reliable for swing horizon.

Common pitfalls

  1. Treating this as market-neutral. It isn't. The price exposure is full perp delta. The basis convergence is an additional edge on top of that exposure, not a substitute for it.
  2. Pairs without spot. Perp-only HL pairs (most alts) won't have basis data — the strategy will never fire and the user gets confused why "no trades". Always confirm informative_pairs declared the spot leg.
  3. Hourly basis noise. Basis at the 1h scale flickers around zero. Without the funding-confirming filter, you get whipsawed by every tick of perp-spot divergence.
  4. Tight stops. Convergence trades take days, not hours. -0.04 is the floor; tighter and ATR noise stops you out before the trade works.

Sources

  • Internal plan: docs/alpha-scan-improvement-plan.md — basis-flipping bucket
  • Freqtrade informative_pairs — https://www.freqtrade.io/en/stable/strategy-customization/#additional-data-informative_pairs
  • Sibling: the funding-rate-arbitrage skill

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.