# Range Bar in kdb

**URL:** <https://forum.kx.com/t/range-bar-in-kdb/14005>\
**Category:** Community Support\
**Tags:** imported, kdb-and-q\
**Created:** [July 23, 2024, 2:36pm UTC](https://forum.kx.com/t/range-bar-in-kdb/14005 "2024-07-23T14:36:34Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![rgiu70](https://sea2.discourse-cdn.com/flex002/user_avatar/forum.kx.com/rgiu70/32/128_2.png) [@rgiu70](https://forum.kx.com/u/rgiu70)\
**Post date:** [July 23, 2024, 2:36pm UTC](https://forum.kx.com/t/range-bar-in-kdb/14005/1 "2024-07-23T14:36:34Z")

</div>

[https://learninghub.kx.com/forums/topic/range-bar-in-kdb](https://learninghub.kx.com/forums/topic/range-bar-in-kdb)

Hi all,

I am a new kdb user, i'm trying step by step to transform backtest logic written in Python into q language. Specifically, I am encountering problems replicating logic for constant range bars (https://help.cqg.com/cqgic/19/default.htm#!Documents/rangebarrb.htm).  
  
If I am not mistaken, conceptually, the scan logic should be used to replace a traditional for loop, but unfortunately, I cannot achieve the desired result.

Below, I share very rudimentary code that runs correctly but is actually very slow. Any suggestions to adapt this logic efficiently?

Thanks

// Constant range bars, the bars update each time the "high - low" target is reached

rangeBars:{[lastPrice; rangeTarget]

candle: enlist 1;

cumulativeRange: 0;

candleIdx: 1;

high: first lastPrice;

low: first lastPrice;

loopFn: {[params]

lastPrice: params 0;

rangeTarget: params 1;

cumulativeRange: params 2;

candleIdx: params 3;

high: params 4;

low: params 5;

candle: params 6;

index: params 7;

/ Update high & low

if[lastPrice[index] \> high; cumulativeRange+: abs lastPrice[index] - high; high: lastPrice[index]];

if[lastPrice[index] \< low; cumulativeRange+: abs lastPrice[index] - low; low: lastPrice[index]];

/ Trigger cond

triggerCond: cumulativeRange \> rangeTarget;

/ Update Variables if trigger

if[triggerCond;

candleIdx+: 1;

cumulativeRange: 0;

high: lastPrice[index];

low: lastPrice[index];

];

candle,: candleIdx;

(lastPrice; rangeTarget; cumulativeRange; candleIdx; high; low; candle; index + 1)

};

/ Loop

params: (lastPrice; rangeTarget; cumulativeRange; candleIdx; high; low; candle; 0);

do[count lastPrice - 1; params: loopFn params];

:params 6;

};

lastPrice: 1.0500 1.0501 1.0502 1.0503 1.0504 1.0505 1.0506 1.0507 1.0508 1.0509 1.0510 1.0511 1.0512;

rangeTarget: 0.0003; / (3 pips)

rangeBars[lastPrice; rangeTarget]

---

<div class="post-metadata">

**Author:** ![unterrainer\_ale](https://sea2.discourse-cdn.com/flex002/user_avatar/forum.kx.com/unterrainer_ale/32/108_2.png) [@unterrainer\_ale](https://forum.kx.com/u/unterrainer_ale)\
**Post date:** [July 23, 2024, 3:52pm UTC](https://forum.kx.com/t/range-bar-in-kdb/14005/2 "2024-07-23T15:52:12Z")

</div>

Hi rgiu70,

not sure if this is what you are looking for, but if you have a table and just want to get the open, high, low, close by sym you can run the following query

````

`q)t:([] sym:20?`AAPL`MSFT`C; price:20?100.0)

q)t

sym price

-------------

MSFT 39.27524

MSFT 51.70911

C 51.59796

AAPL 40.66642

MSFT 17.80839

C 30.17723

MSFT 78.5033

C 53.47096

MSFT 71.11716

AAPL 41.1597

AAPL 49.31835

MSFT 57.85203

C 8.388858

MSFT 19.59907

C 37.5638

AAPL 61.37452

AAPL 52.94808

C 69.16099

AAPL 22.96615

MSFT 69.19531

q)exec `o`h`l`c!(first;max;min;last)@\:price by sym from t

| o h l c

----| -----------------------------------

AAPL| 40.66642 61.37452 22.96615 22.96615

C | 51.59796 69.16099 8.388858 69.16099

MSFT| 39.27524 78.5033 17.80839 69.19531

q)

`

---

<div class="post-metadata">

**Author:** ![unterrainer\_ale](https://sea2.discourse-cdn.com/flex002/user_avatar/forum.kx.com/unterrainer_ale/32/108_2.png) [@unterrainer\_ale](https://forum.kx.com/u/unterrainer_ale)\
**Post date:** [July 23, 2024, 3:53pm UTC](https://forum.kx.com/t/range-bar-in-kdb/14005/3 "2024-07-23T15:53:07Z")

</div>

@laura would you mind formatting my answer? thanks

---

<div class="post-metadata">

**Author:** ![rgiu70](https://sea2.discourse-cdn.com/flex002/user_avatar/forum.kx.com/rgiu70/32/128_2.png) [@rgiu70](https://forum.kx.com/u/rgiu70)\
**Post date:** [July 23, 2024, 4:13pm UTC](https://forum.kx.com/t/range-bar-in-kdb/14005/4 "2024-07-23T16:13:35Z")

</div>

Thank you for the response.

Specifically, I would like to create bars from a tick-by-tick dataset, where each bar has a specific range (for example, 10 pips). So, every time the max-min range is equal to 10, the "counter" should reset and start counting max-min again. Every time the range is greater than 10, a new OHLC bar is generated.

---

<div class="post-metadata">

**Author:** ![rgiu70](https://sea2.discourse-cdn.com/flex002/user_avatar/forum.kx.com/rgiu70/32/128_2.png) [@rgiu70](https://forum.kx.com/u/rgiu70)\
**Post date:** [July 23, 2024, 4:17pm UTC](https://forum.kx.com/t/range-bar-in-kdb/14005/5 "2024-07-23T16:17:39Z")

</div>

This is a specific example for this type of bars (range bar chart):

---

<div class="post-metadata">

**Author:** ![rgiu70](https://sea2.discourse-cdn.com/flex002/user_avatar/forum.kx.com/rgiu70/32/128_2.png) [@rgiu70](https://forum.kx.com/u/rgiu70)\
**Post date:** [July 23, 2024, 4:24pm UTC](https://forum.kx.com/t/range-bar-in-kdb/14005/6 "2024-07-23T16:24:12Z")

</div>

So it is necessary to:

1. Map the cumulative (high - low)
2. If the cumulative (high - low) is greater than the set range, then a new bar is generated, independent of time
3. When a new bar is generated, the high - low "counter" must reset and start from 0

Unfortunately, I can't recreate this logic with scan. In the example I shared, it is very rudimentary but it works. That is, every time the range exceeds the set value, an index number is incremented by 1 (new bar index).

---

<div class="post-metadata">

**Author:** ![sujoy13](https://avatars.discourse-cdn.com/v4/letter/s/90db22/32.png) [@sujoy13](https://forum.kx.com/u/sujoy13)\
**Post date:** [July 27, 2024, 6:25am UTC](https://forum.kx.com/t/range-bar-in-kdb/14005/7 "2024-07-27T06:25:31Z")

</div>

It seems to me, you need to break when the range is met.

```
f:{1+1\_first -2#{x,last where z=abs y[last x] - y}[;x;y]\ [{0\<=last deltas -2#x};0]}

where (1+til count c)!c:deltas f[lastPrice;rangeTarget]

1 1 1 1 2 2 2 3 3 3 4 4 4
```
