Momentum Trading on Prediction Markets
Momentum is a hypothesis that a specified recent price move helps predict a later move. A rising chart is not enough to establish it. The useful research question is whether a rule defined before testing remains informative on later data after execution costs and uncertainty are considered.
For an ordinary binary contract, settlement can be $0 or $1, but a move toward either bound is not automatically exploitable drift. New information, thin quoting, a stale last trade, or a different resolution interpretation can each explain the same visible move. Read the actual contract rules; not every event product uses an ordinary binary payout.
Separate the hypotheses
News-response momentum asks whether a defined information release precedes a further price move. A closing-window hypothesis asks whether a specified pre-resolution observation predicts a later price or outcome. A cross-market hypothesis asks whether information in one precisely identified contract helps another. These require different datasets and clocks.
Do not describe slow participant updating, a particular holding window, or a category's superior liquidity as established facts without evidence for that sample. Choose one hypothesis, preserve its lookback and evaluation horizon, and record how many alternatives you tried. Closing prices alone cannot reconstruct every earlier executable opportunity.
Read a movement as a data record
Kalshi's order-book documentation distinguishes resting bids, asks and quantities. Keep those observations separate from a last-trade price or midpoint. A change smaller than plausible spread and fees may be interesting as a forecast feature while being unusable as a transaction.
- Save the exact market, contract side, timestamp and price units.
- Record both the observation interval and the later evaluation interval.
- Keep missing, repeated and out-of-order observations visible.
- Evaluate costs and executable size using contemporaneous evidence.
- Preserve unchanged prices, reversals and inconclusive tests.
Volume or open interest can be an additional feature, but neither proves informed flow or future direction. Do not identify a trader's motive from a few public prints. An expanding sample is useful only when its collection rule stays clear.
Measure first with the real Banger callback
This no-order observer uses the public Banger 0.1.7 SDK's on_market_tick(market, tick, ctx) callback and structured ctx.log. It logs changes between successive eligible observations, not a forecast or a complete momentum strategy. Replace the placeholder with an exact currently open Kalshi ticker; an old example ticker is not evidence of a working feed.
from banger import Strategy, signals
from banger_adapter_kalshi import Kalshi
from decimal import Decimal
class MomentumObserver(Strategy):
name = "momentum-observer"
venues = [Kalshi()]
universe = signals.kalshi_markets(
status="open", tickers=["REPLACE_WITH_CURRENT_OPEN_TICKER"]
)
def on_start(self, ctx):
self._previous = {}
self._logged = 0
def on_market_tick(self, market, tick, ctx):
if self._logged >= 20: # Illustrative logging bound, not a trade rule.
return
p = tick.yes_price
if p is None or not p.is_finite() or not Decimal("0") <= p <= Decimal("1"):
return
if any(v is None or not v.is_finite() or not Decimal("0") <= v <= Decimal("1")
for v in (tick.bid, tick.ask)):
return
if tick.bid > tick.ask or tick.market_id != market.market_id:
return
if tick.timestamp.tzinfo is None:
return
previous = self._previous.get(market.market_id)
if previous and tick.timestamp <= previous[0]:
return
self._previous[market.market_id] = (tick.timestamp, p)
if previous:
ctx.log(
"price_change_observation",
market_id=market.market_id,
change_usd=str(p - previous[1]),
elapsed_seconds=(tick.timestamp - previous[0]).total_seconds(),
spread_usd=str(tick.ask - tick.bid),
)
self._logged += 1
# No order, close, or cancel method is called.The logging bound limits output, not worker lifetime. For a local check, first complete the account setup in the CLI documentation, save the code as strategy.py, and use banger run strategy.py --paper. Stop the supervised process with Ctrl+C after a deliberately short observation period and check its retained session history. This article's callback was exercised offline with fixture ticks; no production strategy was launched for validation.
Add a signal only with explicit assumptions
Before adding order logic, define how a lookback is formed, what counts as fresh data, which observations are rejected, and when the hypothesis expires. Use the same rule on every comparison window. The difference between successive ticks above is a diagnostic input, not proof that a particular threshold predicts anything.
Banger's public beta is paper-only. Its forward-paper context in the public 0.1.7 package explicitly assumes a fill at a requested non-crossing limit in one path. Such a simulated fill does not demonstrate that a passive real order would execute. Check the specific model, fees and logs before interpreting P&L.
What a useful result establishes
A successful observer establishes that the tested callback handles its supplied inputs and logs a price change. A successful market run adds evidence about that particular feed and window. Neither establishes a profitable momentum effect, continuous uptime, real fills, or permission for live trading. Keep the hypothesis, evidence and remaining uncertainty together instead of turning a rising price series into a trading conclusion.