# End-to-end example

_Source: https://docs.neurotrading.io/en/api/example_

This script puts the pieces together in **Paper Trading**, so it never touches real funds:

1. Checks the session token.
2. Turns Paper Trading on and finds a paper Solana wallet.
3. Streams a token's price.
4. Buys once, confirms the buy, then sells everything once the price is 20% up.

It needs Node.js 18 or newer (for the built-in `fetch`) and the `ws` package.

```bash
npm install ws
node bot.mjs
```

> **Warning**
>
> This is a teaching example, not a strategy. It has no stop-loss and makes no attempt to handle partial fills or a restart. Do not point it at production with real funds without making it yours first.

```js
// bot.mjs

const { NEURO_API, NEURO_WS, NEURO_TOKEN, TOKEN_ADDRESS } = process.env;
const BUY_SOL = 0.1;
const TAKE_PROFIT = 1.2; // sell everything at +20%

// ── REST helper: unwraps { data } and throws the API's own error message ──────────────
async function api(method, path, body) {
  const res = await fetch(`${NEURO_API}${path}`, {
    method,
    headers: { Authorization: `Bearer ${NEURO_TOKEN}`, "Content-Type": "application/json" },
    body: body === undefined ? undefined : JSON.stringify(body),
  });
  const json = await res.json().catch(() => ({}));
  if (!res.ok) throw new Error(`${res.status} ${json.error?.message ?? json.message ?? res.statusText}`);
  return json.data;
}

const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

// Solana trades return at submission — poll until the network has a verdict.
async function confirm(txId) {
  for (let i = 0; i < 60; i++) {
    const { status, onChainError } = await api("GET", `/tradings/${txId}/status`);
    if (status === "success") return;
    if (status === "failed") throw new Error(`transaction failed: ${JSON.stringify(onChainError)}`);
    await sleep(2000);
  }
  throw new Error("transaction not confirmed after 2 minutes");
}

// ── 1. Who am I? ───────────────────────────────────────────────────────────────────────
const user = await api("GET", "/user");
console.log("signed in as", user.username ?? user._id);

// ── 2. Paper Trading on, and a paper wallet to trade from ─────────────────────────────
await api("POST", "/demo/mode", { mode: "demo" });
const status = await api("GET", "/demo/status");
if (!status.enabled) throw new Error("Paper Trading did not turn on — refusing to trade for real");

const wallets = await api("GET", "/wallets?chainId=101&includeBalance=true");
const wallet = wallets[0];
if (!wallet) throw new Error("no Solana paper wallet found");
console.log("trading from paper wallet", wallet.publicKey);

// ── 3 & 4. Stream the price, buy once, sell at the target ─────────────────────────────
let entryPrice = null;
let state = "idle"; // idle → buying → holding → selling → done

const ws = new WebSocket(NEURO_WS);
ws.on("open", () => ws.send(JSON.stringify({ type: "subscribe", contractAddresses: [TOKEN_ADDRESS] })));

ws.on("message", async (raw) => {
  const msg = JSON.parse(raw.toString());
  if (msg.type !== "price_update" || msg.data.metricsUpdatedAt !== undefined) return;

  const price = Number(msg.data.priceNative); // SOL per token
  console.log(`${msg.data.symbol}: ${price} SOL`);

  try {
    if (state === "idle") {
      state = "buying";
      const buy = await api("POST", "/tradings/buy", {
        walletAddress: wallet.publicKey,
        tokenAddress: TOKEN_ADDRESS,
        amount: BUY_SOL,
        slippage: 15,
      });
      await confirm(buy.txId);
      entryPrice = price;
      state = "holding";
      console.log(`bought at ~${entryPrice} SOL (${buy.txId})`);
    } else if (state === "holding" && price >= entryPrice * TAKE_PROFIT) {
      state = "selling";
      const sell = await api("POST", "/tradings/sell", {
        walletAddress: wallet.publicKey,
        tokenAddress: TOKEN_ADDRESS,
        amount: 100,
        slippage: 15,
      });
      await confirm(sell.txId);
      state = "done";
      console.log(`sold at ~${price} SOL (${sell.txId})`);
      ws.close();
    }
  } catch (err) {
    console.error(err.message);
    // Go back one step so the next tick can try again.
    state = state === "buying" ? "idle" : state === "selling" ? "holding" : state;
  }
});

ws.on("close", () => console.log("stream closed"));
```

## Going live

When the script behaves in Paper Trading:

1. Switch the account back with `POST /demo/mode` and `{"mode": "real"}`.
2. Remove the Paper Trading step, and replace it with a check that `GET /demo/status` reports `enabled: false`, so the script knows it is trading for real.
3. Use a wallet that holds only what you are prepared to lose to a bug.
