Signal dataset
Every signal we sent from June 2021 to February 2024, as one file.
33,694 signals across 33 months — entry, close, timestamps and the per-trade result, including the ones that went nowhere. Download it, run your own numbers, argue with ours.
Download CSV (3.5 MB) Download JSON (8.6 MB)
What is in it
One row per signal the bot broadcast, flattened from the 33 monthly order spreadsheets. Those spreadsheets remain the source; this is the same data in a shape you can load in one line. First entry 2021-06-08 00:15:55, last close 2024-03-10 07:41:30 (UTC).
| Column | Meaning |
|---|---|
month | Which monthly report the row came from, YYYY-MM. |
symbol | Binance USDT-M perpetual pair. |
side | long or short. |
status | closed, or open for a signal still running when that month's sheet was cut. For open rows close_price and close_time are empty and pnl_pct is a mark-to-market snapshot at the cut, not a result. |
strength | The signal's strength rating, where the month recorded it. Empty in the earliest months, which predate the rating. |
entry_price, close_price | Reference price when the signal fired, and the price at which the sheet recorded it closed. |
entry_time, close_time | UTC, YYYY-MM-DD HH:MM:SS. Converted from the spreadsheets' Excel serial dates. |
pnl_pct | Unleveraged move from entry to close, in percent, signed for the side. A short that fell 9.45% reads 9.4500. |
pnl_max_pct, pnl_min_pct | The best and worst unleveraged excursion the sheet recorded while the signal was open — useful for anyone studying where a tighter target or stop would have landed. |
What is not in it, and why
The spreadsheets carry a column naming what fired each signal. It is not here. What the model looks at is the product, and the public description stops at "240+ indicators, formulas and components"; a dataset that let a reader reconstruct the trigger rules would give that away row by row. Everything about the outcome of each signal is included. Nothing about the cause is.
How to read it honestly
- Filter on
statusbefore you compute anything. 613 rows are signals that were still open when the month closed. The largest losses in the file are those snapshots, not closed trades; a reader who averages the whole column without noticing is measuring the cut-off date, not the signals. - "Closed" means the target was reached — and only that. Every one of the 33,081 closed rows is positive, because these sheets recorded a close when a signal hit its target and nothing else: a signal that had not got there by the month's cut is the
openrow, mark-to-market. So the archive's accuracy is target-hits over everything, with the misses sitting instatus = open. That is a weaker accounting than the live page uses now, where a stop or an expiry is scored as such; read the two with that difference in mind, and the monthly figures on the reports page as reported in those sheets. - The same symbol and entry time can appear more than once. When more than one signal fired on a pair at the same moment, the sheet kept a row for each and closed each on its own — 3,004 rows in 1,376 such groups, always within one month. Count rows, not entry moments, if you want the figure the reports use.
- Returns are per signal, unleveraged, and unsized. A row is a price move, not an account return. Position sizing, fees, funding and slippage all sit between this file and any real P&L.
- Frequency is not constant. Signal counts swing with the market; a month with more rows is a more volatile month, not a better one.
- This is the archive, not the present. Signals since June 2026 are on the live performance page, rebuilt hourly from the trade database, where expired signals count against the hit rate and stop tracking began in late August 2026.
Use it
Free to download and analyse. If you publish something built on it, a link back to this page is appreciated — and if you find something in it we should know about, tell us: @hafizebot.
import pandas as pd
df = pd.read_csv("https://www.hafizebot.com/downloads/signals-2021-2024.csv")
closed = df[df["status"] == "closed"]
closed.groupby("month")["pnl_pct"].agg(["count", "mean", "median"])
Past performance does not guarantee future results; nothing here is investment advice. See the risk disclosure below.