Paid for the way back.
Unwick is a ticket on one hour of SOL. It names a stop: a distance below the hour's open for a long, above it for a short. If the price touches the stop and the hour closes back past it, the ticket pays 10 USDC for every 1% of the close past the stop, up to 40.
A stop-loss order is a promise to leave at the worst price of the hour. Most of the time that is the right call. Some hours the wick takes every stop in its reach and the candle closes as if nothing happened. A ticket is paid on exactly those hours, and only by how far the close came back.
Every ticket holds its 40 USDC before the hour opens: the seller's deposit plus the buyer's premium. Settlement only splits it. The whole rule is one function in unwick.js, which you can import straight from this site.
Quick start
The module has no dependencies. Import it from this site in a browser or in Node 19 and newer. The replay of 36 hours of 2026 sits next to it, in cents.
import { loadReplay, settle } from "https://unwick.vercel.app/lib/unwick.js";
// 36 hours of 2026, Binance SOL/USDT one-minute candles
const { hours } = await loadReplay();
const feb20 = hours.find((h) => h.t === 1771606800); // Fri Feb 20, 17:00 UTC
const r = settle(feb20.m, { side: "long", stop: 1, premium: 3 });
console.log(r.armedBy, r.payout, r.buyer); // "read" 9.88 6.88# the same hour, straight from Binance
curl "https://api.binance.com/api/v3/klines?symbol=SOLUSDT&interval=1m&startTime=1771606800000&limit=60"
# the replay file the site reads
curl -O "https://unwick.vercel.app/assets/replay.json"<script type="module">
import { fetchHour, mark, nextHour } from "https://unwick.vercel.app/lib/unwick.js";
// this hour, as if it closed now
const start = nextHour() - 3600000;
const h = await fetchHour(start);
document.body.textContent = JSON.stringify(mark(h.m, { side: "long", stop: 1, premium: 3 }));
</script>unwick.mjs or import it by URL with a loader. Node 19+ has fetch built in, which fetchHour() needs.Try it
Pick an hour and terms. The output is what settle() returns, from the same module the main page uses.
// pick an hour and press Settle
The rule
A ticket pays only when two things happened in its hour: the price reached the stop, and the close is back on the ticket's side of it.
payout = armed ? min(40, 10 × max(close past the stop, 0)) : 0
close past the stop = (close − stop) / open × 100 for a long, (stop − close) / open × 100 for a short
The payout is measured from the stop, not from the low. The part of the wick below a long's stop is never paid: the ticket pays for the way back, not the way down. At 4% past the stop the payout reaches 40 and stops there, because 40 is all a ticket holds.
export function payout(open, close, stopPx, side, armed) {
if (!armed) return 0;
const past = side === "long" ? (close - stopPx) / open * 100 : (stopPx - close) / open * 100;
return cents(Math.min(RULE.vault, RULE.perPct * Math.max(past, 0)));
}Three hours of 2026
| Hour · UTC | Ticket | Low | Close | Pays |
|---|---|---|---|---|
| Fri Feb 20 · 17:00 | long, 1.00% | −1.99% | −0.01% | 9.88 |
| Thu Jun 04 · 02:00 | long, 2.00% | −2.67% | +2.10% | 40.00 |
| Sun Mar 29 · 22:00 | long, 1.00% | −3.88% | −1.47% | 0.00 |
The last one took the stop and never came back. The ticket was armed and pays nothing, and the seller keeps the 40. A stop that was right is not a wick.
The stop
A seller opens a ticket with a side and a stop distance from 0.25% to 5%, in steps of 0.25%. The distance is fixed when the ticket opens. The stop price is fixed at the hour's first read, so nobody knows the exact number until the hour starts.
- Long: stop below the open. Pays when SOL wicks down through it and closes back above it.
- Short: stop above the open. Pays when SOL wicks up through it and closes back below it.
A tight stop is taken often and pays small. A deep stop is taken rarely, and when it is, the wick that reached it was usually fast. That is where the witness earns its burn.
Reads
Every hour is read thirteen times: at the open, then every 300 seconds, and the last read is the close. Anyone can send a read. A read takes the first price published at or after its boundary.
Reads decide two things: the open, which fixes the stop price, and the close, which fixes the payout. They can also arm a ticket, if one of them lands beyond the stop. Five-minute reads miss most of what happens between them, and the wick that takes a stop often lasts less than a minute.
In the replay
Read j for j = 0 … 11 is the open of minute 5j. Read 12 is the close of minute 59.
export function reads(m) {
shape(m);
const out = [];
for (let j = 0; j < 12; j++) out.push(m[5 * j][0]);
out.push(m[59][3]);
return out;
}The witness
A buyer can load $UNWICK into a ticket. The ticket then has a witness: a recorder that watches every published price and records the first one that reaches the stop. A witness record can only arm a ticket. It never changes the open or the close, so it never changes how much an armed ticket pays.
- A record that arms the ticket burns 2,500 $UNWICK.
- A record the reads already covered, or a price that never reached the stop, is free.
- Unburned $UNWICK goes back to the buyer at settlement.
| Long stop | Paid, reads only | Paid, with witness |
|---|---|---|
| 1.00% | 22 of 36 | 24 of 36 |
| 1.50% | 23 of 36 | 25 of 36 |
| 2.00% | 9 of 36 | 17 of 36 |
| 2.50% | 5 of 36 | 10 of 36 |
| 3.00% | 2 of 36 | 8 of 36 |
Payout and settlement
The seller deposits 40 USDC less the premium when opening a ticket. The buyer pays the premium. From the sale on, the full 40 sits in the ticket's vault. 41 seconds after the hour closes, anyone can settle: the buyer receives the payout, the seller receives the rest of the 40.
seller = premium − payout
break-even = the close is premium / 10 percent past the stop
Neither side can lose more than it put in. A buyer's worst case is the premium. A seller's worst case is the deposit, on an hour that took the stop and closed 4% or more past it.
When money waits
- Nobody buys. The ticket stays on sale until 5 seconds before the hour opens. Until it sells, the seller can withdraw the deposit.
- The open read is missed. There is no stop price, so the hour is void. Both sides get back what they put in.
- A later read is missed. It arms nothing. A ticket with a witness still arms on the first price past the stop.
- The close read is missed. Settlement uses the last read that landed. Missing data favours the seller.
- The witness runs short. A ticket holding less than one burn is not witnessed. Reads still apply.
- Nobody settles. The 40 waits in the vault. Settlement has no deadline and anyone can send it.
settle()
settle(m, ticket) applies the rule to one hour. m is 60 one-minute candles [open, high, low, close]. ticket is { side, stop, premium, witness }; witness defaults to true.
| Field | Meaning |
|---|---|
| open, close | Read 0 and read 12. |
| stopPx | The stop price, fixed at the open. |
| lowPct, highPct, closePct | Percent from the open. |
| touchMinute | First minute whose extreme reached the stop, or null. |
| readHit | Index of the first read beyond the stop, or null. |
| armedBy | "read", "witness" or null. |
| armedAt | Minute the ticket was armed. |
| payout | USDC to the buyer, 0 to 40. |
| buyer, seller | Each side's result after the premium. |
| burn | $UNWICK burned: 2,500 if the witness armed it, else 0. |
| breakEvenPct | How far past the stop the close must be for the buyer to break even. |
book()
book(hours, ticket) settles the same terms on every hour and adds them up. It is what the Book on the main page draws.
import { loadReplay, book } from "https://unwick.vercel.app/lib/unwick.js";
const { hours } = await loadReplay();
const b = book(hours, { side: "long", stop: 2, premium: 3, witness: true });
console.log(b.paid, b.average, b.buyer, b.seller); // 17 6.8 136.95 -136.95fetchHour() and mark()
fetchHour(start) loads one UTC hour of SOL/USDT one-minute candles from Binance. start is milliseconds on the hour. While the hour is running it returns the minutes so far, with complete: false.
mark(partial, ticket) settles a running hour as if it closed at its last price. The main page uses it for "This hour, live".
import { fetchHour, mark, nextHour } from "https://unwick.vercel.app/lib/unwick.js";
const h = await fetchHour(nextHour() - 3600000);
const now = mark(h.m, { side: "short", stop: 0.5, premium: 2 });
console.log(h.m.length, now.armedBy, now.payout);mark() pays on the price right now; the ticket pays on the close.reads(), payout(), stopPrice()
The parts settle() is built from, exported on their own: reads(m) returns the thirteen read prices, stopPrice(open, side, stop) the stop, payout(open, close, stopPx, side, armed) the USDC. check(ticket) validates terms and throws an UnwickError. nextHour(now) returns the next hour's start, and RULE holds the values below.
Values
| Value | Now | What it does |
|---|---|---|
| vault | 40 USDC | What every ticket holds and the most it pays. |
| perPct | 10 USDC | Paid for each 1% the close is past the stop. |
| stop | 0.25–5%, step 0.25 | Set by the seller on each ticket. |
| premium | 0.10–39 USDC | Set by the seller on each ticket. |
| reads | 13, every 300 s | The open, eleven in between, the close. |
| buyCutoff | 5 s | Buying closes this long before the hour opens. |
| settleAfter | 41 s | Settlement opens this long after the close. |
| burn | 2,500 $UNWICK | Burned when a witness record arms a ticket. |
Ticket lifecycle
| State | Who acts | What happens |
|---|---|---|
| open | seller | Deposits 40 − premium, sets side, stop and premium. |
| withdraw | seller | Takes back an unsold ticket at any time. |
| buy | buyer | Pays the premium, until 5 s before the hour. |
| load | buyer | Loads $UNWICK and names the witness. |
| read | anyone | Records one of the thirteen reads. |
| witness | the named witness | Records the first price past the stop and burns 2,500 $UNWICK. |
| settle | anyone | 41 s after the close: payout to the buyer, the rest to the seller. |
Errors
| Code | When |
|---|---|
| E_SIDE | side is not "long" or "short". |
| E_STOP_RANGE | stop is outside 0.25% to 5%. |
| E_STOP_STEP | stop is not a multiple of 0.25%. |
| E_PREMIUM_RANGE | premium is outside 0.10 to 39 USDC. |
| E_HOUR_SHAPE | the hour is not 60 candles of [o, h, l, c]. |
| E_HOUR_START | fetchHour got a start that is not on the hour. |
| E_FEED | Binance answered with an error. |
Data on this site
The Book replays 36 hours of 2026: the hours with the deepest wick that closed at least 60% of the way back, one per day, from Binance SOL/USDT. Each hour is 60 one-minute candles in cents, in assets/replay.json (sha256 0f94624e…82958). "This hour, live" and the rail price read Binance directly from your browser.
Inside a minute, the drawing goes open, low, high, close for a minute that closed up and open, high, low, close for one that closed down. That order is a drawing. Every number comes from the candles alone.
Limits
- The rule pays on one price feed. Whatever the feed says the close was, that is the close.
- The replay is one-minute resolution. Sub-minute wicks that never showed in a minute's high or low are not in it.
- The 36 hours were picked because they wicked and came back. They are a book of shakeouts, not an average hour. An average over them is not a price for the next one.
- A seller who sells tight stops cheap pays for it on the hours this page is made of.
$UNWICK
$UNWICK is what a witness burns. The more tickets with deep stops, the more fast wicks get recorded, and the more of it goes.
Contract: soon
FAQ
Is this a stop-loss?
No. A stop-loss sells at the stop. A ticket sells nothing and is paid at the close, if the close came back past the stop.
Why not pay on the whole wick?
The part of the wick past the stop is what the stop was there for. Paying it would pay every crash. Unwick pays only the part that came back.
Which hour can I buy?
Any UTC hour, until 5 seconds before it opens. The main page prices the next one.
Do I need $UNWICK to buy a ticket?
No. Without it the ticket is armed by reads only. With it the first price past the stop counts.