Why a green chart can still hide a losing exit
You bought 100 units for a total cost of $100. A chart now displays $1.20 per unit. Multiplying those numbers suggests a $20 gain. That calculation leaves out a question the chart cannot answer: how much can buyers actually pay for all 100 units at the moment you sell?
A bid is an offer to buy. Depth is the quantity available at successive prices. A small sale can fit into the highest bid; a larger sale may consume lower bids. The resulting average price can be much worse than the displayed last trade. Fees reduce the cash that remains. An incomplete sale leaves inventory, which cannot be treated as settled cash.
Our research question is: Can a position show a positive chart gain while its full-size, cost-adjusted exit estimate is negative or unavailable? You will build a calculator that answers this question on invented order-book snapshots and explains why an estimate was refused.
Edition 14 set an evidence gate for evaluating rules. We now investigate the exit evidence that such a gate needs. The earlier evaluation extension remains a research question; this issue takes up the owner's recurring concern about profit giveback and apparently missed runners.
What we inspected on October 11, 2026
The current trading branch is main in Primus19/memecoine-mcp-server. Its inspected head was 4f0df427273bdb21d0a56f653141eb170cfb9d0a. We read services/solana-executor/index.mjs, operations.mjs and runner-research.test.mjs at the preceding implementation commit d721619dcb99e9cccdef16fd9960930b26ccb148.
The inspected scheduler invokes funded continuation research separately from paper followups. Disabling paper mode therefore no longer disables that quote research when its other prerequisites hold. It requests exact raw position quantities in quote-only mode. The source also rejects a missed initial observation window or an excessive gap after a valid observation. Invalid quotes do not refresh the valid-observation clock. Those checks matter because a favorable later quote cannot reconstruct an unobserved exit path.
Railway confirmed the Solana executor deployed that implementation successfully on October 11 at 04:01:39 UTC. A bounded runtime read covering 09:54 through 09:59 UTC contained supervision and cycle events. It did not provide completed continuation comparisons or settlement receipts. We verified deployment identity and inspected behavior in source; we did not independently verify a new funded result or successful research observation. No model-accuracy or profitability conclusion follows.
This is a practical response to two distinct signals: the owner's October 8 requests to explain missed gains and profit capture, and the October 11 inspected repair to missing exit-research collection. Neither establishes general search volume. The lab below is original, explicitly synthetic educational code, not a reproduction of the executor, a Solana pool, or an exchange matching engine.
Prerequisites and scope
Use Python 3.12 or later, a terminal and an empty folder. Allow about 30 minutes. There are no packages, API keys, wallet addresses, broker accounts or network requests. All quantities, prices, fees and timestamps are invented.
We model a long position sold into a fixed order book. An automated market maker requires its own size-specific route calculation; its liquidity cannot be substituted into this ladder. Venue protection bands, queue changes, token transfer costs, taxes and transaction failure are outside this model. The completed artifact is a diagnostic calculator, not an order-placement tool.
1. Declare the evidence contract before calculating
The position and snapshot must identify the same pair. The requested quantity must be positive. Bids must have finite positive prices and quantities, ordered from highest to lowest without duplicate price levels. The snapshot must be neither future-dated nor older than five seconds at the frozen decision time. Five seconds is a lab policy, not a recommended production threshold.
We require a known sell-fee rate and a known fixed exit cost. Zero is an explicit known value; missing is unknown. The entry cost includes the position's purchase and entry costs. It is allocated proportionally in this fixture, so 10 units cost $10 and 100 units cost $100. Real accounting needs the correct remaining cost basis after earlier sales.
Our acceptance criteria are declared now: the small position has a positive estimate, the larger fully covered position has a negative estimate despite the green mark, a partial sale has no full-position P&L, and defective evidence is rejected. We will not change these criteria after seeing the numbers.
2. Walk bids and keep unsold inventory visible
The synthetic bids offer $1.18 for 20 units, $0.88 for 80 more and $0.80 for another 10. A 100-unit sale therefore models $23.60 plus $70.40, or $94 gross. A 120-unit request has only 110 units of displayed buying capacity. The last 10 units remain unpriced by this snapshot.
The algorithm consumes each price level once and stops when the requested size is covered. It reports proceeds only for the covered quantity. It produces a full-position estimate only when nothing remains. Displayed capacity is still conditional: buyers can cancel before a real order arrives.
3. Run the complete synthetic lab
Save the following as exit_lab.py inside your empty folder. Run python -I exit_lab.py or python3 -I exit_lab.py. Isolation mode avoids local Python startup customizations; the program itself reads no files or environment variables. Decimal values are constructed from strings so decimal money arithmetic does not begin with binary floating-point approximations.
from decimal import Decimal, InvalidOperation
D = Decimal
def number(value):
if not isinstance(value, str):
raise ValueError("DECIMAL_STRING_REQUIRED")
try:
result = D(value)
except InvalidOperation:
raise ValueError("INVALID_NUMBER") from None
if not result.is_finite():
raise ValueError("INVALID_NUMBER")
return result
def estimate(position, book, now):
if position["pair"] != book["pair"]:
raise ValueError("PAIR_MISMATCH")
if type(now) is not int or type(book["at"]) is not int:
raise ValueError("INVALID_CLOCK")
age = now - book["at"]
if age < 0 or age > 5:
raise ValueError("STALE_OR_FUTURE")
if book["fee_rate"] is None or book["fixed_cost"] is None:
raise ValueError("UNKNOWN_COST")
quantity = number(position["quantity"])
cost = number(position["entry_cost"])
mark = number(position["mark"])
rate = number(book["fee_rate"])
fixed = number(book["fixed_cost"])
if quantity <= 0 or cost < 0 or mark <= 0:
raise ValueError("INVALID_POSITION")
if not (D("0") <= rate < D("1")) or fixed < 0:
raise ValueError("INVALID_COST")
levels = [(number(p), number(q)) for p, q in book["bids"]]
for index, (price, size) in enumerate(levels):
if price <= 0 or size <= 0:
raise ValueError("INVALID_DEPTH")
if index and price >= levels[index - 1][0]:
raise ValueError("UNSORTED_OR_DUPLICATE")
remaining, gross = quantity, D("0")
for price, size in levels:
used = min(remaining, size)
gross += used * price
remaining -= used
if remaining == 0:
break
filled = quantity - remaining
cash = gross * (D("1") - rate) - fixed if filled else D("0")
complete = remaining == 0
return {"filled": filled, "remaining": remaining,
"cash": cash, "pnl": cash - cost if complete else None,
"chart_gain": quantity * mark - cost,
"status": "ESTIMATE" if complete else "INCOMPLETE"}
book = {"pair": "LAB-USD", "at": 100,
"fee_rate": "0.01", "fixed_cost": "0.01",
"bids": [("1.18", "20"), ("0.88", "80"), ("0.80", "10")]}
def position(quantity):
return {"pair": "LAB-USD", "quantity": quantity,
"entry_cost": quantity, "mark": "1.20"}
for quantity in ("10", "100", "120"):
result = estimate(position(quantity), book, 102)
pnl = "unavailable" if result["pnl"] is None else f'{result["pnl"]:.2f}'
print(f'q={quantity} status={result["status"]} '
f'chart_gain={result["chart_gain"]:.2f} '
f'filled={result["filled"]} remaining={result["remaining"]} '
f'cash_estimate={result["cash"]:.2f} pnl_estimate={pnl}')
bad_cases = {
"stale": {**book, "at": 90},
"future": {**book, "at": 103},
"wrong_pair": {**book, "pair": "OTHER-USD"},
"missing_fee": {**book, "fee_rate": None},
"unsorted": {**book, "bids": list(reversed(book["bids"]))},
"nonfinite": {**book, "bids": [("NaN", "20")]},
}
for name, snapshot in bad_cases.items():
try:
estimate(position("100"), snapshot, 102)
except ValueError as error:
print(f"{name}: rejected={error}")
else:
raise RuntimeError(f"defective case accepted: {name}")
small = estimate(position("10"), book, 102)
large = estimate(position("100"), book, 102)
partial = estimate(position("120"), book, 102)
empty = estimate(position("100"), {**book, "bids": []}, 102)
assert small["pnl"] == D("1.6720")
assert large["pnl"] == D("-6.9500")
assert partial["pnl"] is None and partial["remaining"] == D("10")
assert empty["filled"] == 0 and empty["cash"] == 0 and empty["pnl"] is None
print("checks=10 passed; confirmed_fills=0")
Exact verified stdout, including a final newline:
q=10 status=ESTIMATE chart_gain=2.00 filled=10 remaining=0 cash_estimate=11.67 pnl_estimate=1.67
q=100 status=ESTIMATE chart_gain=20.00 filled=100 remaining=0 cash_estimate=93.05 pnl_estimate=-6.95
q=120 status=INCOMPLETE chart_gain=24.00 filled=110 remaining=10 cash_estimate=100.97 pnl_estimate=unavailable
stale: rejected=STALE_OR_FUTURE
future: rejected=STALE_OR_FUTURE
wrong_pair: rejected=PAIR_MISMATCH
missing_fee: rejected=UNKNOWN_COST
unsorted: rejected=UNSORTED_OR_DUPLICATE
nonfinite: rejected=INVALID_NUMBER
checks=10 passed; confirmed_fills=0
4. Explain the result before adapting it
The small sale stays inside the $1.18 bid. Its unrounded estimated cash is $11.672. The 100-unit sale reaches the $0.88 bid; after the hypothetical 1% fee and $0.01 fixed cost, estimated cash is $93.05. Its average gross sale price is $0.94, even though the mark is $1.20. This difference comes from the stated ladder, not from an extra slippage percentage imposed afterward.
For 120 units, $100.97 is estimated cash from the covered 110 units, not a $100.97 sale of the whole position. The remaining inventory is 10. Full-position P&L remains unavailable. A real partial fill could support realized accounting for its sold portion after reconciliation, but this snapshot has no fill at all.
Printed cents round for readability. Calculations retain their decimal precision. The program's filled field means hypothetically covered by displayed bids; it is not evidence of an executed trade. Every result ends with zero confirmed fills.
5. Define the real integration boundary
For an order-book venue, preserve pair, timestamp, requested quantity, available bid levels and the relevant fee evidence together. Coinbase's order-type documentation explains that market orders can fill partially at several prices. Its preview endpoint exposes an estimated fill price and commission information. A preview belongs in an estimate ledger; later fills belong in settlement accounting.
For a routed swap, request the exact remaining raw token quantity. Jupiter's Metis guide documents input amounts, quoted output, route details and the slippage threshold. Check the selected API version's fee semantics before subtracting costs; deducting an already included fee twice produces a different error. No universal fee rate is assumed here.
Keep absent quotes, wrong-size responses and observation gaps as explicit unavailable evidence. Do not bridge a monitoring outage with a profitable later quote. A production adapter must also enforce units, decimals, limits, clock provenance, schema validation and venue-specific execution rules. This lab deliberately stops at the read-only calculation boundary.
Troubleshooting and completion check
If your output differs, retain decimal strings and the original descending bid order. Run without -O, which removes assertions. UNKNOWN_COST requires a documented cost value; do not replace it with zero. INCOMPLETE calls for more contemporaneous depth or an explicit unavailable result, not extension of the last price across missing units.
You are finished when complete stdout matches, you can recompute $94 gross by hand, and you can explain why $100.97 is not full liquidation. As an independent exercise, replace the second bid with $1.10. Predict the 100-unit estimate first, run the modified fixture, and record why the sign changes. Then remove that level and confirm that the missing capacity does not become zero P&L.
The calculator does not model order-book cancellations, concurrent trades, hidden liquidity, failed transactions or real execution latency. It verifies arithmetic and evidence refusal on six defective snapshots and four outcome checks. It proves no strategy advantage, funded return, successful exit or customer outcome.
The next investigation should record a chronological series of exact-size quotes with deliberate outages. We will ask whether an apparent exit-rule advantage survives missing observations, instead of selecting the best later price retrospectively. Preserve this lab's inputs, source identities and output as the baseline for that experiment.