
QuaScript Documentation
Language guide, engine API, 601 builtins and the preset library · v3.0.0-phase1
Libraries
bots/execution
library · presets/library/bots/execution.qs
Execution management for bots, driven by the broker's confirmations (order.status): retries after a rejection, cancelling orders that rest too long, re-entry cool-downs and loss-streak pauses. In backtests and paper bots the same helpers read the simulated broker.
library "bots/execution"
/// Execution management for bots, driven by the broker's confirmations (order.status): retries
/// after a rejection, cancelling orders that rest too long, re-entry cool-downs and loss-streak
/// pauses. In backtests and paper bots the same helpers read the simulated broker.
/// True on the bar an order id gets its final answer from the broker (filled, rejected or
/// cancelled), once per order.
pub fn settled(id: text) -> bool {
st = order.status(id)
done = st == "filled" or st == "rejected" or st == "cancelled"
persist was = false
fired = done and not was
was := done
return fired
}
/// True when order id was rejected and may be sent again: at most `max_attempts` retries per
/// order id, at least `cooldown` bars apart. Re-place the order when it returns true.
pub fn retry_after_reject(id: text, max_attempts: int = 3, cooldown: int = 1) -> bool {
persist attempts = 0
persist last_bar = -1000000
persist seen = false
rejected = order.is_rejected(id)
// a fill or a fresh pending order resets the retry budget
if order.is_filled(id) {
attempts := 0
}
go = rejected and not seen and attempts < max_attempts and bar.index - last_bar >= cooldown
if go {
attempts := attempts + 1
last_bar := bar.index
}
seen := rejected and not go
return go
}
/// True when order id has been waiting longer than `bars` bars (cancel or re-price it).
pub fn stale(id: text, bars: int = 3) -> bool {
waiting = order.is_open(id)
persist since = -1
if waiting and since < 0 {
since := bar.index
}
if not waiting {
since := -1
}
return waiting and since >= 0 and bar.index - since >= bars
}
/// Cancels order id when it has rested more than `bars` bars; returns true on that bar.
pub fn cancel_if_stale(id: text, bars: int = 3) -> bool {
s = stale(id, bars)
if s {
order.cancel(id)
}
return s
}
/// True when a new entry is allowed: flat, at least `cooldown` bars since the position was last
/// closed, and fewer than `max_entries` entries in the current session day.
pub fn reentry_ready(cooldown: int = 5, max_entries: int = 3) -> bool {
flat = account.position == 0
persist was_flat = true
persist flat_since = 0
persist entries = 0
if interval.changed("1D") {
entries := 0
}
if flat and not was_flat {
flat_since := bar.index
}
if not flat and was_flat {
entries := entries + 1
}
was_flat := flat
return flat and bar.index - flat_since >= cooldown and entries < max_entries
}
/// False after `max_losses` losing trades in a row until `pause` bars have passed.
pub fn loss_streak_ok(max_losses: int = 3, pause: int = 20) -> bool {
n = portfolio.trade_count
persist seen = 0
persist streak = 0
persist paused_at = -1000000
persist last_realized = 0.0
r = portfolio.realized_pnl
if n > seen {
streak := r < last_realized ? streak + 1 : 0
seen := n
last_realized := r
if streak >= max_losses {
paused_at := bar.index
streak := 0
}
}
return bar.index - paused_at >= pause
}
/// Quantity that risks `risk_pct` of the account between entry and stop, rounded down to `lot`.
pub fn risk_qty(entry: float, stop: float, risk_pct: float = 1, lot: float = 1) -> float {
eq = account.equity
d = math.abs(entry - stop)
raw = d > 0 and eq > 0 ? eq * risk_pct / 100 / d : 0
return math.floor(raw / lot) * lot
}broker
library · presets/library/broker.qs
Any broker connected in Manage Broker, through bot-api's normalized order API: AngelOne, Upstox, Dhan, 5Paisa, Fyers, Flattrade, Kotak, ICICI Direct, HDFC, Motilal Oswal, IIFL, Paytm Money, Axis Direct, Alice Blue, Bajaj, TradeSmart, Groww, Binance, plus Zerodha and Delta (which also have their own libraries with every order option). Broker-specific fields go in options ("k=v;k=v") and are passed through unchanged. import broker broker.login("angelone", "main") if signal { broker.market("L", side.long, 10, "NSE:SBIN-EQ", product = "INTRADAY") }
library "broker"
/// Any broker connected in Manage Broker, through bot-api's normalized order API: AngelOne, Upstox,
/// Dhan, 5Paisa, Fyers, Flattrade, Kotak, ICICI Direct, HDFC, Motilal Oswal, IIFL, Paytm Money,
/// Axis Direct, Alice Blue, Bajaj, TradeSmart, Groww, Binance, plus Zerodha and Delta (which also
/// have their own libraries with every order option). Broker-specific fields go in `options`
/// ("k=v;k=v") and are passed through unchanged.
///
/// import broker
/// broker.login("angelone", "main")
/// if signal { broker.market("L", side.long, 10, "NSE:SBIN-EQ", product = "INTRADAY") }
/// Sends every order of this bot to broker `id` (angelone, upstox, dhan, ...), account `alias`.
pub fn login(id: text, alias: text = "") {
order.route("", broker = id, account = alias)
}
fn route(id: text, symbol: text, product: text, options: text) {
o = product == "" ? options : options == "" ? "product=" + product : "product=" + product + ";" + options
order.route(id, symbol = symbol, options = o)
}
pub fn market(id: text, dir: int, qty: float, symbol: text, product: text = "", options: text = "") {
route(id, symbol, product, options)
order.market(id, dir, qty)
}
pub fn limit(id: text, dir: int, qty: float, price: float, symbol: text, product: text = "", options: text = "") {
route(id, symbol, product, options)
order.limit(id, dir, price, qty)
}
pub fn stop_market(id: text, dir: int, qty: float, trigger: float, symbol: text, product: text = "", options: text = "") {
route(id, symbol, product, options)
order.stop(id, dir, trigger, qty)
}
pub fn stop_limit(id: text, dir: int, qty: float, trigger: float, price: float, symbol: text, product: text = "",
options: text = "") {
route(id, symbol, product, options)
order.stop_limit(id, dir, trigger, price, qty)
}
pub fn cancel(id: text) {
order.cancel(id)
}
pub fn cancel_all() {
order.cancel_all()
}
pub fn close_position(id: text, qty_pct: float = 100) {
exit.close(id, qty_pct = qty_pct)
}
pub fn close_all_positions() {
exit.close_all()
}delta
library · presets/library/delta.qs
Delta Exchange (India, Global, testnet) orders for bots. login picks the Delta account connected in Manage Broker by alias; bot-api holds the API key, signs every request, places the orders and reports fills back (order.status, order.avgfill, account.position). import delta delta.login("main", venue = delta.Venue.india) delta.leverage("BTCUSD", 10) if ta.crossup(fast, slow) { delta.limit("L", side.long, 5, close, "BTCUSD", postonly = true) } if order.isfilled("L") { delta.bracket("L tp/sl", "L", stop = close - 2 * atr, target = close + 3 * atr) } Sizes are contracts. Backtests and paper bots fill the same calls on the simulated broker.
library "delta"
/// Delta Exchange (India, Global, testnet) orders for bots. `login` picks the Delta account
/// connected in Manage Broker by alias; bot-api holds the API key, signs every request, places the
/// orders and reports fills back (order.status, order.avg_fill, account.position).
///
/// import delta
/// delta.login("main", venue = delta.Venue.india)
/// delta.leverage("BTCUSD", 10)
/// if ta.cross_up(fast, slow) { delta.limit("L", side.long, 5, close, "BTCUSD", post_only = true) }
/// if order.is_filled("L") { delta.bracket("L tp/sl", "L", stop = close - 2 * atr, target = close + 3 * atr) }
///
/// Sizes are contracts. Backtests and paper bots fill the same calls on the simulated broker.
pub enum Venue {
india(title = "india")
global(title = "global")
testnet(title = "testnet")
}
pub enum TimeInForce {
gtc(title = "gtc")
ioc(title = "ioc")
fok(title = "fok")
}
pub enum MarginMode {
isolated(title = "isolated")
cross(title = "cross")
}
/// Sends every order of this bot to the Delta account `alias` on `venue` (testnet for dry runs
/// against the exchange). Call it at the top level, on every bar.
pub fn login(alias: text = "", venue: Venue = Venue.india) {
order.route("", broker = "delta", account = alias, options = "venue=" + venue.title)
}
/// Leverage for the product (bot-api sets it once per change, before the next order).
pub fn leverage(symbol: text, x: float, mode: MarginMode = MarginMode.isolated) {
order.route("", symbol = symbol, options = "leverage=" + str.of(x) + ";margin_mode=" + mode.title)
}
fn flags(post_only: bool, reduce_only: bool, tif: TimeInForce, extra: text) -> text {
o = "time_in_force=" + tif.title
o2 = post_only ? o + ";post_only=1" : o
o3 = reduce_only ? o2 + ";reduce_only=1" : o2
return extra == "" ? o3 : o3 + ";" + extra
}
pub fn market(id: text, dir: int, size: float, symbol: text, reduce_only: bool = false, options: text = "") {
order.route(id, symbol = symbol, options = flags(false, reduce_only, TimeInForce.ioc, options))
order.market(id, dir, size)
}
/// Limit order; post_only orders that would take liquidity are rejected by the exchange.
/// Placing the same id again while it is open edits it.
pub fn limit(id: text, dir: int, size: float, price: float, symbol: text, post_only: bool = false,
reduce_only: bool = false, tif: TimeInForce = TimeInForce.gtc, options: text = "") {
order.route(id, symbol = symbol, options = flags(post_only, reduce_only, tif, options))
order.limit(id, dir, price, size)
}
/// Stop-market (stop_loss_order) once `stop_price` trades.
pub fn stop_market(id: text, dir: int, size: float, stop_price: float, symbol: text, reduce_only: bool = true,
options: text = "") {
order.route(id, symbol = symbol, options = flags(false, reduce_only, TimeInForce.gtc, options))
order.stop(id, dir, stop_price, size)
}
/// Stop-limit: triggers at `stop_price`, then rests at `limit_price`.
pub fn stop_limit(id: text, dir: int, size: float, stop_price: float, limit_price: float, symbol: text,
reduce_only: bool = true, options: text = "") {
order.route(id, symbol = symbol, options = flags(false, reduce_only, TimeInForce.gtc, options))
order.stop_limit(id, dir, stop_price, limit_price, size)
}
/// Take-profit order (take_profit_order) at `price`.
pub fn take_profit(id: text, dir: int, size: float, price: float, symbol: text) {
order.route(id, symbol = symbol, options = flags(false, true, TimeInForce.gtc, "stop_order_type=take_profit_order"))
order.limit(id, dir, price, size)
}
/// Exchange-held bracket (stop loss, take profit, optional trailing amount) on the position of
/// `entry`; bot-api sends it as a Delta bracket order.
pub fn bracket(id: text, entry: text, stop: float = na, target: float = na, trail: float = na) {
exit.bracket(id, entry = entry, stop = stop, target = target, trail_offset = trail)
}
pub fn cancel(id: text) {
order.cancel(id)
}
pub fn cancel_all() {
order.cancel_all()
}
/// Closes the lots of entry `id` with a reduce-only market order.
pub fn close_position(id: text, qty_pct: float = 100) {
exit.close(id, qty_pct = qty_pct)
}
pub fn close_all_positions() {
exit.close_all()
}risk/position_sizing
library · presets/library/risk/position_sizing.qs
Risk-based position sizing and stop placement.
library "risk/position_sizing"
/// Risk-based position sizing and stop placement.
/// Quantity that loses `risk_pct` of equity if the stop is hit.
pub fn risk_qty(entry: float, stop: float, risk_pct: float = 1.0) -> float {
dist = math.abs(entry - stop)
return dist > 0 ? portfolio.equity * risk_pct / 100 / dist : 0
}
/// ATR stop and target for a long (side = 1) or short (side = -1) entry: (stop, target).
pub fn atr_bracket(entry: float, side: int, atr_len: int = 14, stop_mult: float = 2.0, reward: float = 2.0) -> (float, float) {
a = ta.atr(atr_len)
stop = entry - side * a * stop_mult
return (stop, entry + side * a * stop_mult * reward)
}
/// Fractional Kelly fraction from the running statistics, capped at `cap`.
pub fn kelly_fraction(fraction: float = 0.5, cap: float = 0.25) -> float {
w = portfolio.win_rate / 100
pf = portfolio.profit_factor
payoff = w > 0 and w < 1 and not math.is_na(pf) ? pf * (1 - w) / w : na
k = math.is_na(payoff) ? 0 : risk.kelly(w, payoff)
return math.clamp(k * fraction, 0, cap)
}smc/structure
library · presets/library/smc/structure.qs
Swing points and market-structure breaks.
library "smc/structure"
/// Swing points and market-structure breaks.
/// A confirmed swing point.
pub struct Swing {
price: float = na
at_bar: int = 0
is_high: bool = true
fn age(self) -> int { return bar.index - self.at_bar }
}
/// Confirmed swing highs and lows (`legs` bars each side), reported on the bar they confirm.
pub fn swings(legs: int = 5) -> (float, float) {
return (ta.pivot_high(legs, legs), ta.pivot_low(legs, legs))
}
/// Structure direction: +1 after a close above the last swing high, -1 after a close below the
/// last swing low. Returns (direction, broke_up, broke_down, last_high, last_low).
pub fn structure(legs: int = 5) -> (float, bool, bool, float, float) {
(ph, pl) = swings(legs)
persist last_high = na
persist last_low = na
persist dir = 0
if not math.is_na(ph) { last_high := ph }
if not math.is_na(pl) { last_low := pl }
up = not math.is_na(last_high) and ta.cross_up(close, last_high)
down = not math.is_na(last_low) and ta.cross_down(close, last_low)
if up { dir := 1 }
if down { dir := -1 }
return (dir, up, down, last_high, last_low)
}smc/zones
library · presets/library/smc/zones.qs · uses smc/structure
Price zones (order blocks, fair-value gaps) with mitigation tracking.
library "smc/zones"
/// Price zones (order blocks, fair-value gaps) with mitigation tracking.
use "smc/structure" as st
pub DEFAULT_ATR = 14
pub struct Zone {
top: float = na
bottom: float = na
born: int = 0
bullish: bool = true
hits: int = 0
fn width(self) -> float { return self.top - self.bottom }
fn mid(self) -> float { return (self.top + self.bottom) / 2 }
fn contains(self, price: float) -> bool { return price <= self.top and price >= self.bottom }
/// A bullish zone is mitigated once price closes below it, a bearish one above it.
fn mitigated(self) -> bool { return self.bullish ? close < self.bottom : close > self.top }
fn touch(mut self) { self.hits := self.hits + 1 }
}
/// Three-bar fair-value gap ending on this bar: (found, zone).
pub fn fvg(min_atr: float = 0.1) -> (bool, Zone) {
a = ta.atr(DEFAULT_ATR)
bull = low > high[2] and low - high[2] >= a * min_atr
bear = high < low[2] and low[2] - high >= a * min_atr
z = Zone(top = bull ? low : low[2], bottom = bull ? high[2] : high, born = bar.index - 1, bullish = bull)
return (bull or bear, z)
}
/// The last opposite candle before a structure break, as a zone: (found, zone).
pub fn order_block(legs: int = 5) -> (bool, Zone) {
(dir, up, down, hh, ll) = st.structure(legs)
// the last down candle before an up break (and vice versa) within 10 bars
k_bull = ta.bars_since(close < open)
k_bear = ta.bars_since(close > open)
z = Zone()
if up and k_bull <= 10 {
z := Zone(top = high[k_bull], bottom = low[k_bull], born = bar.index - k_bull, bullish = true)
}
if down and k_bear <= 10 {
z := Zone(top = high[k_bear], bottom = low[k_bear], born = bar.index - k_bear, bullish = false)
}
return ((up and k_bull <= 10) or (down and k_bear <= 10), z)
}stats/filters
library · presets/library/stats/filters.qs
Smoothing, normalisation and regime helpers.
library "stats/filters"
/// Smoothing, normalisation and regime helpers.
/// Average selector used by several analyses.
pub enum Average {
sma(title = "Simple")
ema(title = "Exponential")
hull(title = "Hull")
kaufman(title = "Kaufman adaptive")
jurik(title = "Jurik")
}
/// The selected average of `src` (the selector is a param, so only one runs).
pub fn average(src: series, len: int, kind: Average) -> series {
return match kind {
case Average.sma { ta.sma(src, len) }
case Average.ema { ta.ema(src, len) }
case Average.hull { ta.hull(src, len) }
case Average.kaufman { ta.kaufman(src, len) }
case Average.jurik { ta.jurik(src, len) }
}
}
/// Rolling z-score clipped to +-cap.
pub fn zscore(src: series, len: int = 50, cap: float = 4.0) -> series {
z = stat.zscore(src, len)
return math.clamp(z, -cap, cap)
}
/// Min-max scale of `src` over `len` bars into 0..100.
pub fn rescale(src: series, len: int) -> series {
lo = ta.lowest(src, len)
hi = ta.highest(src, len)
return hi > lo ? (src - lo) / (hi - lo) * 100 : 50
}
/// Market regime from the Hurst exponent and the efficiency of the move.
pub enum Regime(weight: float = 1.0) {
trend(title = "Trending", weight = 1.0)
range(title = "Ranging", weight = 0.5)
noise(title = "Noisy", weight = 0.0)
fn tradeable(self) -> bool { return self != Regime.noise }
}
pub fn regime(len: int = 100) -> Regime {
h = stat.hurst(close, len)
er = math.abs(ta.change(close, 20)) / math.max(stat.sum(math.abs(ta.change(close)), 20), 1e-12)
return h > 0.55 and er > 0.3 ? Regime.trend : h < 0.45 or er < 0.1 ? Regime.noise : Regime.range
}viz/palette
library · presets/library/viz/palette.qs
Shared colors and color helpers for the bundled analyses.
library "viz/palette"
/// Shared colors and color helpers for the bundled analyses.
pub BULL = color.emerald.500
pub BEAR = color.rose.500
pub NEUTRAL = color.slate.500
pub ACCENT = color.amber.400
/// Green above zero, red below, fading towards zero over `scale`.
pub fn signed_heat(x: float, scale: float) -> color {
t = math.clamp(math.abs(x) / math.max(scale, 1e-12), 0, 1)
return x >= 0 ? color.gradient(t, 0, 1, color.emerald.900, color.emerald.400) : color.gradient(t, 0, 1, color.rose.900, color.rose.400)
}
/// Cold-to-hot ramp of a 0..100 oscillator.
pub fn osc_heat(x: float) -> color {
return x >= 50 ? color.gradient(x, 50, 100, color.slate.400, color.rose.500) : color.gradient(x, 0, 50, color.sky.500, color.slate.400)
}
/// Direction color with a neutral state.
pub fn trend_color(dir: float) -> color {
return dir > 0 ? BULL : dir < 0 ? BEAR : NEUTRAL
}viz/premium
library · presets/library/viz/premium.qs
Shared look of the premium analyses: the dashboard heading, key / value rows and the colour helpers used by the heatmaps (dark centre, saturated tails), so every script reads the same.
library "viz/premium"
/// Shared look of the premium analyses: the dashboard heading, key / value rows and the colour
/// helpers used by the heatmaps (dark centre, saturated tails), so every script reads the same.
pub INK = color.slate.950
pub HEAD_BG = color.indigo.950
pub LABEL = color.slate.400
pub BULL = color.emerald.400
pub BEAR = color.rose.400
pub WARN = color.amber.300
/// Diverging colour: dark near zero, `pos` for positive and `neg` for negative values, saturating at |x| = scale.
pub fn diverging(x: float, scale: float, pos: color, neg: color) -> color {
t = math.clamp(math.abs(x) / math.max(scale, 1e-12), 0, 1)
return math.is_na(x) ? na : x >= 0 ? color.gradient(t, 0, 1, color.slate.900, pos) : color.gradient(t, 0, 1, color.slate.900, neg)
}
/// Heading of a dashboard table: the title on the first row across every column, the subtitle below it.
pub fn heading(t: id, cols: int, title: text, subtitle: text) {
draw.table.cell(t, 0, 0, title, text_color = color.white, bg_color = HEAD_BG, size = 1.05, align = "left")
draw.table.merge(t, 0, 0, 0, cols - 1)
draw.table.cell(t, 1, 0, subtitle, text_color = color.indigo.300, bg_color = HEAD_BG, size = 0.8, align = "left")
draw.table.merge(t, 1, 0, 1, cols - 1)
}
/// One key / value row of a two-column dashboard.
pub fn row(t: id, r: int, key: text, value: text, tone: color) {
draw.table.cell(t, r, 0, key, text_color = LABEL, align = "left")
draw.table.cell(t, r, 1, value, text_color = tone, align = "right")
}zerodha
library · presets/library/zerodha.qs
Zerodha Kite orders for bots. The script never sees a password or token: login picks the Zerodha account connected in Manage Broker (by alias), and bot-api signs in, places every order and reports back. Read the confirmations with order.status / order.isfilled / order.avgfill. import zerodha zerodha.login("main") if ta.crossup(fast, slow) { zerodha.market("L", side.long, 25, "INFY") } if order.isfilled("L") { zerodha.protect("L exit", "L", stop = low, target = close * 1.02) } Backtests and paper bots fill the same calls on the simulated broker.
library "zerodha"
/// Zerodha Kite orders for bots. The script never sees a password or token: `login` picks the
/// Zerodha account connected in Manage Broker (by alias), and bot-api signs in, places every order
/// and reports back. Read the confirmations with order.status / order.is_filled / order.avg_fill.
///
/// import zerodha
/// zerodha.login("main")
/// if ta.cross_up(fast, slow) { zerodha.market("L", side.long, 25, "INFY") }
/// if order.is_filled("L") { zerodha.protect("L exit", "L", stop = low, target = close * 1.02) }
///
/// Backtests and paper bots fill the same calls on the simulated broker.
pub enum Exchange {
nse(title = "NSE")
bse(title = "BSE")
nfo(title = "NFO")
bfo(title = "BFO")
mcx(title = "MCX")
cds(title = "CDS")
bcd(title = "BCD")
}
/// MIS intraday (auto square-off), CNC delivery, NRML overnight derivatives, MTF margin funding.
pub enum Product {
mis(title = "MIS")
cnc(title = "CNC")
nrml(title = "NRML")
mtf(title = "MTF")
}
pub enum Variety {
regular(title = "regular")
amo(title = "amo")
co(title = "co")
iceberg(title = "iceberg")
auction(title = "auction")
}
pub enum Validity {
day(title = "DAY")
ioc(title = "IOC")
ttl(title = "TTL")
}
/// Kite order types: MARKET, LIMIT, SL (stop-loss limit), SL-M (stop-loss market).
pub enum OrderType {
market(title = "MARKET")
limit(title = "LIMIT")
sl(title = "SL")
sl_m(title = "SL-M")
}
/// Sends every order of this bot to the Zerodha account `alias` (its name in Manage Broker;
/// "" = the default Zerodha account). Call it at the top level, on every bar.
pub fn login(alias: text = "") {
order.route("", broker = "zerodha", account = alias)
}
/// Kite instrument key: "NSE:INFY", "NFO:NIFTY24DECFUT".
pub fn instrument(symbol: text, exchange: Exchange = Exchange.nse) -> text {
return exchange.title + ":" + symbol
}
fn route(id: text, symbol: text, exchange: Exchange, product: Product, variety: Variety, validity: Validity,
disclosed_qty: float, validity_ttl: int, iceberg_legs: int, iceberg_qty: float, extra: text) {
o = "product=" + product.title + ";variety=" + variety.title + ";validity=" + validity.title
o2 = disclosed_qty > 0 ? o + ";disclosed_quantity=" + str.of(disclosed_qty) : o
o3 = validity_ttl > 0 ? o2 + ";validity_ttl=" + str.of(validity_ttl) : o2
o4 = iceberg_legs > 0 ? o3 + ";iceberg_legs=" + str.of(iceberg_legs) + ";iceberg_quantity=" + str.of(iceberg_qty) : o3
order.route(id, symbol = instrument(symbol, exchange), options = extra == "" ? o4 : o4 + ";" + extra)
}
/// Any Kite order: `order_type` picks MARKET / LIMIT / SL / SL-M; `price` is the limit price
/// (LIMIT, SL) and `trigger_price` the trigger (SL, SL-M). Placing the same id again while it is
/// open modifies it (bot-api sends a modify instead of a new order).
pub fn place_order(id: text, dir: int, qty: float, symbol: text, order_type: OrderType = OrderType.market,
price: float = na, trigger_price: float = na, exchange: Exchange = Exchange.nse,
product: Product = Product.mis, variety: Variety = Variety.regular,
validity: Validity = Validity.day, disclosed_qty: float = 0, validity_ttl: int = 0,
iceberg_legs: int = 0, iceberg_qty: float = 0, options: text = "") {
route(id, symbol, exchange, product, variety, validity, disclosed_qty, validity_ttl, iceberg_legs, iceberg_qty, options)
if order_type == OrderType.market {
order.market(id, dir, qty)
} else if order_type == OrderType.limit {
order.limit(id, dir, price, qty)
} else if order_type == OrderType.sl {
order.stop_limit(id, dir, trigger_price, price, qty)
} else {
order.stop(id, dir, trigger_price, qty)
}
}
pub fn market(id: text, dir: int, qty: float, symbol: text, exchange: Exchange = Exchange.nse,
product: Product = Product.mis, variety: Variety = Variety.regular, options: text = "") {
place_order(id, dir, qty, symbol, OrderType.market, na, na, exchange, product, variety, Validity.day, 0, 0, 0, 0, options)
}
pub fn limit(id: text, dir: int, qty: float, price: float, symbol: text, exchange: Exchange = Exchange.nse,
product: Product = Product.mis, variety: Variety = Variety.regular,
validity: Validity = Validity.day, options: text = "") {
place_order(id, dir, qty, symbol, OrderType.limit, price, na, exchange, product, variety, validity, 0, 0, 0, 0, options)
}
/// Stop-loss / target exits for the position opened by `entry`: bot-api places an SL-M at `stop`
/// and a LIMIT at `target` and cancels the other when one fills (one-cancels-other); `trail`
/// moves the stop behind the best price. Use this, not stop_loss / stop_market, to protect a
/// position: those two are entries in the opposite direction and would reverse it.
pub fn protect(id: text, entry: text, stop: float = na, target: float = na, trail: float = na) {
exit.bracket(id, entry = entry, stop = stop, target = target, trail_offset = trail)
}
/// SL order: triggers at `trigger`, then rests as a limit at `price` (an entry: it opens or
/// reverses a position; protect an open position with `protect`).
pub fn stop_loss(id: text, dir: int, qty: float, trigger: float, price: float, symbol: text,
exchange: Exchange = Exchange.nse, product: Product = Product.mis, options: text = "") {
place_order(id, dir, qty, symbol, OrderType.sl, price, trigger, exchange, product, Variety.regular, Validity.day, 0, 0, 0, 0, options)
}
/// SL-M order: a market order once `trigger` trades (an entry, like stop_loss).
pub fn stop_market(id: text, dir: int, qty: float, trigger: float, symbol: text,
exchange: Exchange = Exchange.nse, product: Product = Product.mis, options: text = "") {
place_order(id, dir, qty, symbol, OrderType.sl_m, na, trigger, exchange, product, Variety.regular, Validity.day, 0, 0, 0, 0, options)
}
/// Cover order: a market entry with a compulsory SL-M stop at `trigger`.
pub fn cover(id: text, dir: int, qty: float, trigger: float, symbol: text, exchange: Exchange = Exchange.nse) {
place_order(id, dir, qty, symbol, OrderType.market, na, na, exchange, Product.mis, Variety.co, Validity.day, 0, 0, 0, 0,
"co_trigger=" + str.of(trigger))
exit.bracket(id + " co", entry = id, stop = trigger)
}
/// GTT OCO (stop-loss and target held by Zerodha until one triggers) on the position of `entry`.
pub fn gtt_oco(id: text, entry: text, stop: float, target: float, symbol: text, exchange: Exchange = Exchange.nse,
product: Product = Product.cnc) {
order.route(id, symbol = instrument(symbol, exchange), options = "gtt=oco;product=" + product.title)
exit.bracket(id, entry = entry, stop = stop, target = target)
}
/// Cancels order `id` (open orders only).
pub fn cancel(id: text) {
order.cancel(id)
}
pub fn cancel_all() {
order.cancel_all()
}
/// Squares off the lots of entry `id` with a market order.
pub fn square_off(id: text, qty_pct: float = 100) {
exit.close(id, qty_pct = qty_pct)
}
pub fn square_off_all() {
exit.close_all()
}