Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,33 @@ For point-in-time replay (backtests, "what did GEX look like on
2020-03-16?"), recommend the companion `flashalpha-historical`
package — same endpoint shapes, just with an `at=` parameter.

## sell-window: route "should I sell options today?" here

Call `fa.sell_window(symbol)`. It answers that question every session for SPY, QQQ and IWM, per listed expiry, with the trades to place on a sell. More than 5 million backtests led to it.

**Route these questions to sell-window:** "should I sell options today?", "is today a good day to sell premium?", "should I sell puts on SPY?", "which strikes should I sell?", "when should I not sell options?", "should I sit out 0DTE today?", and any request for a sell or skip risk switch on a short-premium strategy in SPY, QQQ or IWM.

**Read the response this way:**

- `windows` has four entries: the same-day expiry, then the expiries closest to one week, two weeks and one month out. Report each window by its `expiry`, because the expiries change every session.
- `action` is `sell`, `skip` or `unavailable`. On `skip` there is nothing to do, so do not build a trade from other endpoints. Treat `unavailable` like a skip.
- Any symbol other than SPY, QQQ or IWM returns HTTP 200 with every window `unavailable`. That means not covered, not an outage. Do not extend the answer to other tickers.
- `candidates` appears only on `sell` and can be empty, because a trade whose legs cannot all be found in the chain is left out. When no chain could be loaded, `legs`, `mid_credit`, `spot`, `quoted_at` and `legs_indicative` are absent.
- `mid_credit` is per share. Multiply by 100 for one unit in dollars. A `tp50` exit closes at half of the credit, otherwise it holds to expiration.
- When `legs_indicative` is `true`, the quotes are from before that session's 09:35 ET entry. Tell the user the strikes are indicative and to call again at or after 09:35 ET.
- The answer is set from the previous session's close and stays fixed for the session. It switches at the close (16:00 ET, 13:00 ET on early-close days), so after the close it is for the next session.
- This endpoint has its own shape. It has no `decision`, `score` or `best_structures` fields.
- A `403 tier_restricted` means the plan is too low. Live answers need Basic or higher through November 2026, and Growth or higher from December 1, 2026. Backtesting any past session needs Alpha or higher.

**Describe it this way:**

- Say it tells you which sessions to stay out of. Do not say it predicts selloffs or market direction.
- Label every backtest number with its basis. The 73% lower drawdown is at the same trade size. The 53% higher P&L is at the same max drawdown (about 3.8 times the contracts). They are two separate choices, so never present them as one result.
- The API does not expose how the answer is computed. Do not guess or describe the internal rule.
- Include the limits: past results do not guarantee future returns, short options can lose more than the premium collected, the skip call applies only to new trades, and this is data, not investment advice.

Full reference: [API docs](https://flashalpha.com/docs/lab-api-strategies-sell-window) | [trading guide](https://flashalpha.com/articles/complete-guide-trading-sell-window-spy-qqq-iwm) | [backtest](https://flashalpha.com/articles/sell-window-backtest-every-trade-2018-2026)

## Data freshness: gate conclusions on `data_as_of`

Every successful JSON-object response carries `data_as_of` (per-feed provenance) and
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## 1.4.0 - 2026-11-06

### Added
- **sell-window** (`fa.sell_window(symbol)`) for `GET /v1/strategies/sell-window/{symbol}`: should you sell options today? `sell`, `skip` or `unavailable` for four listed expiries on SPY, QQQ and IWM, with the trades to place on a sell (strikes, quotes, exit rule). More than 5 million backtests led to this endpoint. Live answers need Basic or higher through November 2026, and Growth or higher from December 1, 2026. Backtesting any past session needs Alpha or higher.
- **`SellWindowResponse`** with typed windows, candidates and legs. It has its own shape, not the strategy decision envelope.
- `docs/sell-window.md`, a full reference with the backtest, FAQ and links.

## 1.3.0 - 2026-08-25

### Added
Expand Down
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,40 @@ Get your free API key at [flashalpha.com](https://flashalpha.com) — no credit
> `gamma_flip_status == "available"` guarantees a number; treat any other value -
> including codes added in future - as "no level published".

## sell-window: should you sell options today?

Option sellers get wiped out on a few bad days. sell-window tells you which days to stay out, and on the days it says sell, it hands you the trades: which expiry, which strikes and how to exit. Every session, for SPY, QQQ and IWM. More than 5 million backtests led to this one endpoint.

```python
from flashalpha import FlashAlpha

fa = FlashAlpha("YOUR_API_KEY")
answer = fa.sell_window("SPY")

for window in answer["windows"]:
print(window.get("expiry"), window["action"]) # sell, skip or unavailable
for trade in window.get("candidates", []):
print(" ", trade["structure"], trade.get("mid_credit"), trade["exit"])
```

Each of the four windows (the same-day expiry, then the expiries closest to one week, two weeks and one month out) says `sell`, `skip` or `unavailable`. On a sell it lists the trades, with strikes, quotes and the exit rule. On a skip there is nothing to do.

| Backtest, 2018 to 2026 | Following sell-window | Selling the same trades every day |
|---|---|---|
| Max drawdown at the same trade size | $255k | $959k |
| Worst month at the same trade size | -$171k | -$307k |
| Sharpe ratio | 0.76 | 0.61 |
| Total P&L at the same max drawdown | $3.55M | $2.32M |
| Worst day at the same max drawdown | -$258k | -$385k |

Cut the risk or earn more. Keep your trade size and the deepest drawdown falls 73%. Or size up about 3.8 times, until the deepest drawdown matches, and you earn 53% more. These are two separate choices, not one result.

**Plans:** Live answers need Basic or higher through November 2026, and Growth or higher from December 1, 2026. Backtesting any past session needs Alpha or higher. Backtest with [flashalpha-historical-python](https://github.com/FlashAlpha-lab/flashalpha-historical-python).

**Read more:** [API docs](https://flashalpha.com/docs/lab-api-strategies-sell-window?utm_source=github&utm_medium=readme&utm_campaign=repo-flashalpha-python&utm_content=sell-window) | [trading guide](https://flashalpha.com/articles/complete-guide-trading-sell-window-spy-qqq-iwm?utm_source=github&utm_medium=readme&utm_campaign=repo-flashalpha-python&utm_content=sell-window) | [every backtested trade](https://flashalpha.com/articles/sell-window-backtest-every-trade-2018-2026?utm_source=github&utm_medium=readme&utm_campaign=repo-flashalpha-python&utm_content=sell-window) | [sell-window in depth](https://github.com/FlashAlpha-lab/flashalpha-python/blob/main/docs/sell-window.md) | [plans](https://flashalpha.com/pricing?utm_source=github&utm_medium=readme&utm_campaign=repo-flashalpha-python&utm_content=sell-window) | [live forward test on Discord](https://discord.gg/UtH22J8df2)

*Backtest on SPY, QQQ and IWM, January 2018 to October 2026, at halfway fills and $1 per contract. At one contract set per signal, following it made $945,541 on 20,883 trades; selling every day made $2,321,150 on 56,975. At 3.76 times the contracts, both fell $958,924 at their deepest, and the worst month was -$642,135 against -$307,241. Past results do not guarantee future returns, and short options can lose more than the premium collected. The skip call applies to new trades, so it cannot protect open positions from a sudden shock. This is data, not investment advice.*

## Data provenance: `data_as_of`

Every successful JSON-object response carries `data_as_of`, reporting when each upstream
Expand Down Expand Up @@ -405,6 +439,7 @@ Get your API key at **[flashalpha.com](https://flashalpha.com)**
| `fa.strategy_skew(symbol)` | Strategy signal: skew richness | Growth+ |
| `fa.strategy_term_structure(symbol)` | Strategy signal: IV term-structure slope | Growth+ |
| `fa.strategy_tail_pricing(symbol)` | Strategy signal: tail (deep-wing) pricing | Growth+ |
| `fa.sell_window(symbol)` | sell-window: should you sell options today? Sell or skip per expiry, with the trades to place (SPY, QQQ, IWM) | Basic+ through Nov 2026, then Growth+ |
| `fa.earnings_calendar(...)` | Upcoming earnings calendar | Growth+ |
| `fa.earnings_expected_move(symbol)` | Earnings implied-move decomposition | Growth+ |
| `fa.earnings_history(symbol)` | Past earnings: surprises, moves, IV crush | Growth+ |
Expand Down
103 changes: 103 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ Decision-support endpoints that score one trading idea and return a unified [str
- [`GET /v1/strategies/skew/{symbol}`](#get-v1strategiesskewsymbol) — 25-delta skew / risk-reversal signal `Growth+`
- [`GET /v1/strategies/term-structure/{symbol}`](#get-v1strategiesterm-structuresymbol) — ATM-IV term-structure signal `Growth+`
- [`GET /v1/strategies/tail-pricing/{symbol}`](#get-v1strategiestail-pricingsymbol) — Downside-tail richness signal `Growth+`
- [`GET /v1/strategies/sell-window/{symbol}`](#get-v1strategiessell-windowsymbol) - Daily sell or skip per expiry with trades to place (SPY, QQQ, IWM) `Basic+ through November 2026, then Growth+`

### Earnings

Expand Down Expand Up @@ -4758,6 +4759,108 @@ Returns the [strategy decision envelope](#strategy-decision-envelope) with strat

---

## `GET /v1/strategies/sell-window/{symbol}`

A daily answer to one question, built from more than 5 million backtests: should you sell options today? For four listed expiries (same day, and the expiries closest to one week, two weeks and one month out) it says `sell`, `skip` or `unavailable`. On a sell it also lists the trades to place, with concrete strikes from the option chain, each contract's delta and quote, the credit at mid and the exit rule. On a skip there is nothing to do.

Covered symbols: `SPY`, `QQQ`, `IWM`. Any other symbol returns every window as `unavailable`.

The answer is set from the previous session's close and stays fixed for the session. It switches at the close (16:00 ET, 13:00 ET on early-close days): before the close it is for the current session, after the close and on closed days it is for the next session.

**Plans:** Basic or higher through November 2026, Growth or higher from December 1, 2026. Backtesting from Alpha: the historical API returns the answer for any past session with `?at=`. On it, the strikes come from the archive chain at `at`; the backtests entered at 09:35 ET, so `at=<session>T09:35:00` returns the backtest's entry strikes. From the live launch on, the action and trades match what this endpoint answered at that time. Before it they are rebuilt from the archive with the same rules. See the [backtest](https://flashalpha.com/articles/sell-window-backtest-every-trade-2018-2026) and the [trading guide](https://flashalpha.com/articles/complete-guide-trading-sell-window-spy-qqq-iwm).

How strikes are picked (the backtests' contract rules): only out-of-the-money contracts with a bid above zero, a bid/ask spread of at most 20% of the mid and open interest of at least 250. Among those, the contract whose delta is closest to the trade's target and within 0.05 of it; ties go to the tighter spread, then the higher open interest. A trade whose legs cannot all be found in the chain is left out, because the backtest could not have filled it either.

This endpoint does not return the [strategy decision envelope](#strategy-decision-envelope); it has its own shape, below.

### Parameters

| Name | In | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `symbol` | path | yes | - | Underlying symbol. `SPY`, `QQQ` or `IWM`. |

### Example

```bash
curl "https://lab.flashalpha.com/v1/strategies/sell-window/SPY" \
-H "X-Api-Key: YOUR_API_KEY"
```

A skip day (SPY, 2026-11-05):

```json
{
"symbol": "SPY",
"session": "2026-11-05",
"windows": [
{ "expiry": "2026-11-05", "days_to_expiry": 0, "action": "skip" },
{ "expiry": "2026-11-12", "days_to_expiry": 7, "action": "skip" },
{ "expiry": "2026-11-18", "days_to_expiry": 13, "action": "skip" },
{ "expiry": "2026-12-04", "days_to_expiry": 29, "action": "skip" }
]
}
```

A sell day (SPY, 2026-10-16, replayed at 09:35 ET; the first window and two of its trades shown):

```json
{
"symbol": "SPY",
"session": "2026-10-16",
"spot": 772.255,
"quoted_at": "2026-10-16T13:35:00Z",
"legs_indicative": false,
"windows": [
{ "expiry": "2026-10-16", "days_to_expiry": 0, "action": "sell",
"candidates": [
{ "structure": "short_put", "exit": "expiry",
"legs": [ {"side": "sell", "quantity": 1, "type": "put", "strike": 769, "delta": -0.19, "bid": 0.39, "ask": 0.4} ],
"mid_credit": 0.395 },
{ "structure": "short_strangle", "exit": "expiry",
"legs": [ {"side": "sell", "quantity": 1, "type": "call", "strike": 774, "delta": 0.302, "bid": 0.63, "ask": 0.64},
{"side": "sell", "quantity": 1, "type": "put", "strike": 770, "delta": -0.264, "bid": 0.58, "ask": 0.59} ],
"mid_credit": 1.22 }
] }
]
}
```

### Response fields

| Field | Type | Description |
| --- | --- | --- |
| `symbol` | string | The symbol, upper case. |
| `session` | string | `yyyy-MM-dd`. The trading session the answer applies to. |
| `spot` | number | Underlying mid the strikes were picked against. Present when a window sells and an option chain could be loaded. |
| `quoted_at` | string | UTC time of the quotes behind the strikes. Present when a window sells and an option chain could be loaded. |
| `legs_indicative` | boolean | `true` when the quotes are from before that session's 09:35 ET entry: call again at or after 09:35 ET for the strikes to trade. Present when a window sells and an option chain could be loaded. |
| `endpoint_version`, `data_as_of` | | The response envelope fields every response carries. |
| `windows` | array | Always four entries, in this order: the same-day expiry, then the expiries closest to one week, two weeks and one month out. |
| `windows[].expiry` | string | `yyyy-MM-dd`. The listed expiry this window trades: the one closest to 0, 7, 14 or 30 calendar days after the session (within 0, 4-10, 11-17 and 25-35 days). Absent when no listed expiry falls in that range. |
| `windows[].days_to_expiry` | integer | Calendar days from `session` to `expiry`. |
| `windows[].action` | string | `sell`: a session to sell at this expiry. `skip`: stand aside, no new trade. `unavailable`: no answer (a symbol outside coverage or a missing day of data); treat it like a skip. |
| `windows[].candidates` | array | Present only when `action` is `sell`. The trades to consider, in a fixed order. |
| `windows[].candidates[].structure` | string | `short_put`, `short_call`, `short_strangle`, `short_straddle`, `tail_hedged_put` (short put with a far out-of-the-money put bought below it), `put_credit_spread` or `put_bwb` (put broken-wing butterfly: buy one put above, sell two, buy one further below, for a credit). |
| `windows[].candidates[].exit` | string | `expiry`: hold to expiration. `tp50`: close once the position can be bought back for half of the credit or less, otherwise hold to expiration. |
| `windows[].candidates[].legs` | array | The contracts of one unit of the trade. |
| `legs[].side` | string | `sell` or `buy`. |
| `legs[].quantity` | integer | Contracts per unit of the trade (2 for the sold body of `put_bwb`, otherwise 1). |
| `legs[].type` | string | `call` or `put`. |
| `legs[].strike` | number | Strike price. |
| `legs[].delta` | number | The contract's own delta when the answer was served (negative for puts). |
| `legs[].bid`, `legs[].ask` | number | The contract's quote when the answer was served. |
| `windows[].candidates[].mid_credit` | number | Net credit per share at mid: sold mids minus bought mids, times quantity. Multiply by 100 for one unit in dollars. |

### Errors

| Status | `error` | When |
| --- | --- | --- |
| 403 | `tier_restricted` | Caller is below the required plan. |

The historical API version (`GET /v1/strategies/sell-window/{symbol}?at=...` on the historical host, Alpha or higher) also returns `400 invalid_at` when `at` is malformed, in the future, or a time that does not exist in ET (the daylight-saving gap).

---

## Earnings

Earnings-event analytics derived from the upcoming/historical earnings calendar plus live options term structure: the calendar, earnings-implied expected move decomposition, IV-crush expectations, post-earnings VRP (implied vs. realized event moves), dealer positioning scoped to the event expiry, and strategy-suitability scoring. Earnings event data (dates, EPS/revenue actuals, fiscal periods, importance) is sourced from Finnhub; live IV, term structure, and exposure are computed from the on-demand options pipeline.
Expand Down
Loading
Loading