NexusTrade guide

Build and backtest an algorithmic long put

A bearish trend rule buys an ATM put and closes when the trend reverses or time runs short.

As of 2026-10-10

Follow the actual example

Explore the completed backtest, check its saved rules and follow a recorded position. Open the same rules in Aurora when you want a preview.

Long put: completed 2024 backtest12.99K6.81K2024-01-022024-12-31
Long putFull-capital SPY buy and holdHistorical simulation
Completed 2024 historical simulation. The plotted observations and comparison series are preserved.
Step 1 of 4

See what the 2024 test returned

Compare the two recorded curves and the result below. SPY invests the full starting balance; this options candidate uses its saved allocation. Their fees and invested capital differ.

Candidate vs full-capital SPY
-26.04% vs 25.30%
What you should see

The requested period was 2024-01-01 through 2024-12-31. The candidate’s largest reported decline was 41.94%, with $11.05 in recorded commissions. Quote coverage is not certified for strategy comparison; inspect the retained quote checks before comparing these strategies.

Step 2 of 4

Check the saved entry, legs and exit

Start with the actual rules used in this example. Days to expiration means the days remaining until the option expires. The complete editor inputs and SDK functions are available below.

Entry
Open when SPY’s price is below its 50-day simple moving average and no matching option position is open.
Position sizing
One strategy unit; leg ratios apply. Buying power, collateral and liquidity can restrict orders.
Signal exit
Close all matching option legs when SPY rises to its 50-day average or above, or when 14 or fewer days remain until expiration.
Starting capital
$10,000.00
Leg 1
Buy 1 put contract per unit, targeting a strike nearest SPY’s price. Choose the nearest available expiry 30 to 45 days away.
What you should see

The entry decides when to open. The leg selectors choose contracts. The allocation sets size, and the exit decides when to close. Keep these parts together in your draft.

Step 3 of 4

Follow one recorded position

Buying to open purchases an option; selling to open writes it. Closing reverses that position. Multiply each premium below by 100 shares per contract and its quantity, then subtract the recorded fees to follow the cash flow.

Put · $586 strike
Dec 18, 2024 21:00 UTC: buy to open 1 at $11.0660 per share; $0.65 fee. Dec 24, 2024 18:00 UTC: sell to close 1 at $2.5195 per share; $0.65 fee.
What you should see

These Filled orders show one captured position from the run. Its recorded opening and closing cash flows sum to $-855.95 after commissions. Inspect every leg and ratio; the engine's closed-trade count can count option legs rather than whole spread round trips.

Step 4 of 4

Preview your rules with Aurora

Open the prepared request. Aurora receives the exact rules shown here and checks them without saving a portfolio. Review its preview before choosing whether to save or test your own version.

What you should see

A preview of the rules and their validation result. Any invalid inputs are identified before you decide what to save.

Inspect a captured opening and close

Inspect inspect a captured opening and close

These Filled orders show one captured position from the run. Its recorded opening and closing cash flows sum to $-855.95 after commissions. Inspect every leg and ratio; the engine's closed-trade count can count option legs rather than whole spread round trips.

Follow this position's Filled events by contract, side and time. Compare opening and closing premiums, quantities and commissions to understand its P&L. This example comes from the captured excerpt of the most recent 50 Filled orders.

Scroll sideways to compare all columns. Select a heading to sort.

2024-12-18 21:00:00SPY250117P00586000Buy to open1$11.0660$0.65
2024-12-24 18:00:00SPY250117P00586000Sell to close1$2.5195$0.65

The algorithm and its purpose

Inspect the algorithm and its purpose

The rules below trade SPY options. The completed 2024 backtest includes the portfolio curve, contract-resolution records and any recorded fills.

Scroll sideways to compare all columns. Select a heading to sort.

EntryPrice(SPY) < SMA(SPY,50,Day) AND zero matching option positions
Position sizingOne strategy unit; leg ratios apply. Buying power, collateral and liquidity can restrict orders.
Signal exitPrice(SPY) >= SMA(SPY,50,Day); close whole matching spread at 14 or fewer DTE
Starting capital$10000.00
Spread typecustom
ExecutionMarket; $0.65 commission per option contract, default 0.5 half-spread fraction plus applicable liquidity impact.

Contract selectors and ratios

Inspect contract selectors and ratios

Strike distances are percentages of the underlying price, not fixed market strikes or delta targets. Positive distance means out of the money: a higher strike for calls and a lower strike for puts. Negative distance means in the money; zero targets at the money (ATM). Days to expiration (DTE) measures the time remaining until a contract expires. Nearest expiration must lie within each leg's DTE range. Inspect the resolved contracts rather than assuming the authored offsets produce exact strike widths.

All option legs use the same requested expiry window. Verify actual shared expiration and the resulting quantities in the contract-resolution and fill records.

Scroll sideways to compare all columns. Select a heading to sort.

1longput0%130 to 45 DTE

Create the exact inactive draft

Inspect create the exact inactive draft

Ask Aurora to build the rules above, review the preview, then save the draft while keeping automated trading off. A chat draft is inspected in Portfolio > Drafts or Aurora; the native Strategies editor belongs to a paper or live portfolio.

In an inactive native portfolio, use Open Options Position, choose the matching structure preset and verify each leg above. Under Allocation, set Type Contracts and Amount 1. Add the entry conditions, including zero matching option positions. Add both Close Options Position rules separately, matching SPY and the spread type with close scope spread and quantity all. A preset adds contract legs; it does not supply the complete entry or exit algorithm.

Plan a separate variant and later test

Review the optional next experiment

After reviewing the recorded example, use the prepared request above for the original rules. This optional plan covers a separate variant and a later test.

  1. Create one variant, then test unseen dates

    Ask: "Change only the timed close trigger from 14 DTE to 10 DTE. Show the rule diff and compare both candidates on the 2024 development period with every other setting fixed." After choosing, freeze the rules and select another test period you have not inspected. The displayed 2024 results already make 2024 a viewed development period. Keep any later revisions in a new research cycle.

Create and backtest with Python

Use the Python SDK instead of Aurora

Python SDK 1.42.0. Save the runner below as run_long_put.py.

Install and download

Shell
python3 -m pip install nexustrade==1.42.0
export NEXUSTRADE_API_KEY="YOUR_API_KEY"
curl --fail --output long_put.py https://nexustrade.io/seo/strategy-examples/long-put.py

Create, test and inspect

Python
from long_put import create_long_put, backtest_long_put

book = create_long_put()
completed = backtest_long_put()
print(completed["result"])

Run

Shell
python3 run_long_put.py

Python strategy and backtest functions

View complete Python SDK functions

The backtest function submits the 2024 test with SPY as its benchmark, records events and waits for the result. Use a new request key when changing rules, dates or fees.

Python
import nexustrade as nt


def create_long_put():
    asset = nt.stock_asset("SPY")
    return nt.portfolio(
        "Public SEO example - Long put",
        [
            nt.strategy(
                "Open Long put",
                ((nt.Price(asset) < nt.SMA(asset, 50, "Day")) & (nt.OptionPositionCount("SPY", "put", "long", "custom") == nt.Value(0))),
                nt.open_option(
                    builder=nt.options_builder(
                        legs=[nt.leg(option_type="put", direction="long", min_days_to_expiration=30, max_days_to_expiration=45, preference="nearest", distance=0, ratio=1)],
                        underlying_symbol="SPY",
                        spread_type="custom"
                    ),
                    allocation={'type': 'contracts', 'amount': 1}
                )
            ),
            nt.strategy(
                "Close when signal invalidates",
                (nt.Price(asset) >= nt.SMA(asset, 50, "Day")),
                nt.close_option(
                    underlyings=['SPY'],
                    spread_type="custom",
                    close_scope="spread",
                    quantity={'type': 'all'}
                )
            ),
            nt.strategy(
                "Close at 14 DTE",
                (nt.Value(1) > nt.Value(0)),
                nt.close_option(
                    underlyings=['SPY'],
                    spread_type="custom",
                    close_scope="spread",
                    quantity={'type': 'all'},
                    triggers=[nt.dte_trigger(max_dte=14)]
                )
            ),
        ],
        initial_value=10000,
        supports_fractional_shares=False,
        alerts_enabled=False,
        dividend_policy='cash',
    )


def backtest_long_put(client=None):
    client = client or nt.NexusTradeClient.from_environment()
    operation = client.create_backtest(
        nt.backtest(
            create_long_put(),
            start_date="2024-01-01", end_date="2024-12-31",
            interval="Day", baseline_symbol="SPY",
            initial_value=10000, generate_events=True,
            dividend_policy="cash",
            fee_config={"Stock": {"type": "percent", "amount": 0.1},
                        "Cryptocurrency": {"type": "percent", "amount": 0.7},
                        "Option": {"type": "dollars", "amount": 0.65}},
        ),
        idempotency_key="explore-long-put-2024-spy-v1",
    )
    return client.wait_for_backtest(operation["id"])


# Build locally: book = create_long_put()
# Run and inspect: completed = backtest_long_put(); print(completed["result"])

Create and backtest with TypeScript

Use the TypeScript SDK instead of Aurora

TypeScript SDK 1.42.0. Save the runner below as run-long-put.ts.

Install and download

Shell
npm install nexustrade@1.42.0
npm install --save-dev tsx
export NEXUSTRADE_API_KEY="YOUR_API_KEY"
curl --fail --output long-put.ts https://nexustrade.io/seo/strategy-examples/long-put.ts

Create, test and inspect

TypeScript
import { createLongPut, backtestLongPut } from "./long-put";

async function main() {
  const book = createLongPut();
  const completed = await backtestLongPut();
  console.log(completed.result);
}

main().catch((error: unknown) => {
  console.error(error);
  process.exitCode = 1;
});

Run

Shell
npx tsx run-long-put.ts

TypeScript strategy and backtest functions

View complete TypeScript SDK functions

The same rules, dates and fees are used in both SDKs. The backtest function keeps its request key across retries.

TypeScript
import * as nt from "nexustrade";

export function createLongPut() {
  const asset = nt.stockAsset("SPY");
  return nt.portfolio(
    "Public SEO example - Long put",
    [
      nt.strategy(
          "Open Long put",
          nt.and(
              nt.lt(nt.Price(asset), nt.SMA(asset, 50, "Day")),
              nt.eq(nt.OptionPositionCount("SPY", "put", "long", "custom"), nt.Value(0))
          ),
          nt.openOption({ builder: nt.optionsBuilder({ legs: [nt.leg({ optionType: "put", direction: "long", minDaysToExpiration: 30, maxDaysToExpiration: 45, preference: "nearest", distance: 0, ratio: 1 })], underlyingSymbol: "SPY", spreadType: "custom" }), allocation: {"type": "contracts", "amount": 1} })
      ),
      nt.strategy(
          "Close when signal invalidates",
          nt.gte(nt.Price(asset), nt.SMA(asset, 50, "Day")),
          nt.closeOption({ underlyings: ["SPY"], spreadType: "custom", closeScope: "spread", quantity: {"type": "all"} })
      ),
      nt.strategy(
          "Close at 14 DTE",
          nt.gt(nt.Value(1), nt.Value(0)),
          nt.closeOption({ underlyings: ["SPY"], spreadType: "custom", closeScope: "spread", quantity: {"type": "all"}, triggers: [nt.dteTrigger({ maxDte: 14 })] })
      ),
    ],
    { initialValue: 10000, supportsFractionalShares: false, alertsEnabled: false, dividendPolicy: "cash" }
  );
}

export async function backtestLongPut(client = new nt.NexusTradeClient()) {
  const operation = await client.createBacktest(
    nt.backtest(createLongPut(), {
      startDate: "2024-01-01", endDate: "2024-12-31",
      interval: "Day", baselineSymbol: "SPY",
      initialValue: 10000, generateEvents: true,
      dividendPolicy: "cash",
      feeConfig: {
        Stock: { type: "percent", amount: 0.1 },
        Cryptocurrency: { type: "percent", amount: 0.7 },
        Option: { type: "dollars", amount: 0.65 },
      },
    }),
    { idempotencyKey: "explore-long-put-2024-spy-v1" }
  );
  if (typeof operation.id !== "string") throw new Error("Missing backtest ID");
  return client.waitForBacktest(operation.id);
}

// Build locally: const book = createLongPut();
// Run and inspect: const completed = await backtestLongPut(); console.log(completed.result);

Completed historical backtest

Inspect completed historical backtest

This exact saved candidate completed its fixed 2024 Day simulation on 2026-10-10. Starting capital was $10000.00. The request enabled detailed events and cash dividends. The curve below contains all 519 exported history points.

The SPY comparison invests the full starting balance, reinvests dividends and applies no strategy commission. This strategy requests one spread unit with its declared fees. Use the cash, position value and collateral history to explore how each deploys capital.

Scroll sideways to compare all columns. Select a heading to sort.

StatusCOMPLETE
Requested period2024-01-01 through 2024-12-31
Starting capital$10000.00
Modeled return-26.04%
Reported maximum drawdown41.94%
Full-capital SPY comparator return25.30%
Recorded commissions$11.05
Engine closed-trade count8
Peak reserved collateral$0.00
Median reserved collateral while holding positions$0.00
Pricing contract version8
Observed / synthetic half-spread calls21 / 26
Unknown-timing quote accesses1262
Missing-current-quote checks0
Rejected fill groups / marks0 / 0
Stale basket / single-option marks0 / 0
Long put: completed 2024 backtest12.99K6.81K2024-01-022024-12-31
Long putFull-capital SPY buy and holdHistorical simulation

Inspect the candidate book

Inspect inspect the candidate book

This is the indicative selected-candidate snapshot from the opening size decision. Compare the selected symbols and ratios with the actual Filled orders. Use the candidate bid, ask and mid to follow the sizing calculation, then compare the recorded execution premium.

Scroll sideways to compare all columns. Select a heading to sort.

SPY250117P00586000$586.002025-01-1730$11.02$11.08$11.051949; sessionVolume
JSON
{
  "modelVersion": 2,
  "enforced": true,
  "chosenIndex": 0,
  "path": "targetKept",
  "reason": "target size limit 237 covers 1",
  "requestedQuantity": 1,
  "quantity": 1,
  "sizingChain": [
    {
      "kind": "allocationImplied",
      "bound": 1,
      "quantity": 1,
      "binding": true
    },
    {
      "kind": "buyingPowerCap",
      "bound": 6,
      "quantity": 1,
      "binding": false
    },
    {
      "kind": "volumeLimit",
      "bound": 237,
      "quantity": 1,
      "binding": false
    },
    {
      "kind": "finalPriceRecap",
      "bound": 1,
      "quantity": 1,
      "binding": false
    }
  ],
  "pricing": {
    "mid": 11.05,
    "halfSpread": 0.03000000000000025,
    "participation": 0.001053740779768177,
    "impactCost": 0.09738412097418017
  }
}

Inspect a contract-resolution audit

Inspect inspect a contract-resolution audit

This is the latest retained resolution attempt, which can belong to a different position than the matched trade above. Its outcome, capital and risk codes explain what the resolver accepted or rejected at that decision. Follow the resulting Order events to see its fill status.

JSON
{
  "time": "2024-12-27T14:31:00.000Z",
  "status": "success",
  "reason": null,
  "riskAudit": {
    "rulesetVersion": 1,
    "outcome": "allowed",
    "longSharesAvailable": 0,
    "unhedgedShortCallContractsPerSpread": 0,
    "buyingPowerEffective": 6814.047752189774,
    "allocationRequested": 1,
    "allocation": {
      "type": "contracts",
      "amount": 1
    },
    "portfolioValue": 6814.047752189774,
    "underlyingPrice": 597.5399780273438,
    "maxLossPerUnit": 852.9999999999999,
    "finalContractQuantity": 1,
    "collateralReserved": 852.9999999999999,
    "committedCostBefore": 0
  }
}

Inspect the largest decline in the exported history

Inspect inspect the largest decline in the exported history

The exported grid's largest peak-to-trough decline was 39.07%, from $11183.36 at 2024-08-05T20:00:00.000Z to $6814.05 at 2024-12-24T18:00:00.000Z. The engine reported maximum drawdown of 41.94%. Compare the history snapshots with the reported statistic and inspect the events around this period.

Scroll sideways to compare all columns. Select a heading to sort.

2024-08-05T20:00:00.000Z$8264.86$2918.50$0.00
2024-12-24T18:00:00.000Z$6814.05$0.00$0.00

Explain this options decision with Aurora

Inspect explain this options decision with aurora

Ask Aurora to connect the recorded signal, contract choice, allocation and fills. It receives this page's captured decision and order fields, so you can follow its explanation back to each value.

Download the result and explore the events

Inspect download the result and explore the events

Download the exact strategy inputs, completed result, full history, pricing counters and captured events. You can use these records to follow the strategy from its signal to its closing fill. Detailed events remain available in the product for 3 days; this saved example stays readable.

Explore this strategy further

Inspect explore this strategy further

Inspect matched quantities, actual strike widths, expirations and all close orders. For sold options, review collateral and exercise or assignment exposure separately from premium income. A condition firing can emit zero close orders; look at its evaluation and the Filled-order records.

Try another date range or change one rule, then compare the new backtest with this saved 2024 result. Inspect which trades and periods explain the difference.

Keep the recorded example

Download the captured history, orders and receipt to inspect the same simulation. Private account identifiers are omitted.

Continue exploring

Discussion

Sign in or create a free account to join the discussion.

No comments yet.

Aurora · AI research assistant

Try this in Aurora

Edit the request, then open it in Aurora. You choose when to send it.