FloatPilot logo

FloatPilot

YouTube X

Sandbox Config Documentation

Write a JavaScript function that estimates a fair value for the current item using past sales. Keep it pure and fast—no network calls or DOM access.

What is a Config?

A config is a JavaScript function that analyzes a list of past sales (listings[]) and returns a single number—your suggested price/value. You also receive the current item object for comparison.

Inputs

  • listings[]: past sales with fields like sale_price, float_value, fade_percent, playside_blue, sale_date, stickers, …
  • item: current item’s float_value, fade_percent, playside_blue, stickers, etc.

Return Value

Return a number (the price). If your config can’t estimate, return null.

Writing Your First Config

Simplest possible: average of all sale prices.

function run(listings, item) {
  let total = 0;
  for (const l of listings) total += l.sale_price;
  return listings.length ? total / listings.length : null;
}

Default Config Example

Buckets float, filters fade/blue, trims outliers, time‑decays, and boosts for stickers.

function run(listings, item) {
  function getFloatBucket(f) {
    const r = [[0.75,1],[0.5,0.75],[0.45,0.5],[0.4,0.45],[0.38,0.4],[0.3,0.38],[0.24,0.3],[0.21,0.24],[0.18,0.21],[0.165,0.18],[0.15,0.165],[0.12,0.15],[0.09,0.12],[0.07,0.09],[0.06,0.07],[0.01,0.06],[0.001,0.01],[0,0.001]];
    for (const [min, max] of r) if (f <= max && f > min) return [min, max];
    return null;
  }
  function within(a, b, t) { return typeof a==='number' && typeof b==='number' && Math.abs(a-b) <= t; }

  const b = getFloatBucket(item.float_value);
  if (!b) return null;

  const TOL_FADE = 3, TOL_BLUE = 3, now = Date.now(), decayDays = 1, trimPercent = 0.15;

  const baseOnly = listings.filter(s =>
    s.float_value > b[0] && s.float_value <= b[1] &&
    (item.fade_percent == null || within(s.fade_percent, item.fade_percent, TOL_FADE)) &&
    (item.playside_blue == null || within(s.playside_blue, item.playside_blue, TOL_BLUE)) &&
    s.stickers === '[]'
  );

  if (baseOnly.length < 3) return null;

  const sorted = baseOnly.slice().sort((a,b) => a.sale_price - b.sale_price);
  const cut = Math.floor(sorted.length * trimPercent);
  const trimmed = sorted.slice(cut, sorted.length - cut);

  const weighted = trimmed.map(s => {
    const age = (now - new Date(s.sale_date).getTime()) / (1000*60*60*24);
    const w = Math.exp(-age / decayDays);
    return { p: s.sale_price, w };
  });

  const sumW = weighted.reduce((a,x) => a + x.w, 0);
  const base = sumW ? weighted.reduce((a,x) => a + x.p * x.w, 0) / sumW : null;
  if (!base) return null;

  let stickerPrice = 0, boost = 0;
  try {
    const stickers = JSON.parse(item.stickers || '[]');
    stickerPrice = stickers.reduce((sum,s) => sum + (s.reference?.price || 0), 0);
    if (stickerPrice < 100) boost = 0.1;
    else if (stickerPrice < 1000) boost = 0.05;
    else boost = 0.02;
  } catch {}

  return Math.round(base + (stickerPrice * boost));
}

Security & Restrictions

  • No access to window, document, fetch, or network.
  • No DOM or browser APIs.
  • Keep the function pure; only compute from inputs and return a number or null.

Tips

  • Wrap logic in try{…}catch{} to avoid crashes.
  • Return null when you can’t estimate confidently.
  • Test with the sandbox using real listings arrays.
  • Favor clarity and small, fast helpers.

This documentation is for FloatPilot config creators only.