graph_data_request
Description
Subscribe to a continuously updating chart bar data. Chart bar data refers to chart base data and subgraph data
~11 Min Overview Video
Request Lifecycle
The general approach to the lifecycle is that when the client first subscribes, some historical data is initially sent. This initial historical data would normally be used to warm up indicators. Once that is done the server sends continuous updates. The user can control how many bars are sent in each phase.
In more detail, the client sends a request to subscribe with X historical_bars and Y realtime_update_bars
The server then goes through to 2 phases:
Init phase - the server responds by sending back Y bars. This is a one time event, allowing the client to initialize itself with enough historical data.
Realtime update phase - the server sends Y bars on each update. So more below on the update frequency.
In other words, the server first sends back X bars, this is a one time thing. After that the server continuously sends Y bars on each update.
X is dependent on the historical_bars parameter sent in the request.
Y is dependent on the realtime_update_bars send in the request.
If Y is zero, phase2, realtime updates, will not happen. The server will simply send the historical bars and end there
When done the client sends an unsubscribe message.
Request Parameters
Param | Description | Type | Valid Values | Example |
---|---|---|---|---|
Header params |
|
|
|
|
base_data | List of base data elements. Base data refers to open, high, low, last, volume, etc. The values in the list correspond with the Sierra Chart base data numbering | string | List of base data Elements are separated by semicolon
| “1;3;4;12;13” refers to open, low, last, up vol and down vol |
sg_data | Defines the list of subgraphs to retrieve | string | Follows the SG Data format | ID1.[SG1-SG3] subscribes to updates on study ID1, subgraphs 1-3 |
include_timestamp |
| boolean | 1 or 0 |
|
historical_bars |
| int | integer | 500 - 500 historical bars are sent during the init subcription phase |
realtime_update_bars |
| int | integer | 5 - 5 bars are sent on each real time update |
on_bar_close | True - real time updates are sent only when a bar closes False - real time updates are sent continuously based on the update_frequency_ms | boolean | True or False |
|
update_frequency_ms |
| int | integer | 5000, updates are sent every 5000 milliseconds |
include_bar_ closed_status |
| boolean | True or False |
|
include_bar_index |
| boolean | True or False |
|
include_vbp |
| boolean | True or False |
|
vbp_include_empty_price_levels |
| boolean | True or False |
|
block | blocking call (should the call be async or sync, see more here) | boolean |
|
|
sg_data=”ID2.[SG1-SG3]”
The SG Data parameter is used to specify a list of subgraphs to subscribe to. The parameter syntax generally looks like this: study_id.[subgraphs]
A simple example of this is something like "ID0.[SG4]” which translates to study 0 (the main price graph) and subgraph 4 (which happens to be the last price in the main price graph)
In more detail
study_id - formatted like it appears in SC. ID1, ID2, ID3 etc. ID0 is the main price graph.
subgraphs - subgraphs are denoted similar to how they appear in SC. SG1, SG2. SG3 etc. The field supports single values, range and list
Single - [SG3]
Range - [SG3-SG5]
List - [SG3, SG4, SG5]
Mixed list and range [SG3-SG5, SG8, SG9]
Subscribing to multiple studies at the same time
The description above demonstrates subscribing to data from a single study.
It’s possible to subscribe to multiple studies in one subscription. To that simply separate each study description by a semicolon
For example, assume you have a chart with a simple moving average on it at ID1.
To receive both the last price and the ma, the syntax would look like this: "ID0.[SG4];ID1.[SG1]”
update_frequency
The client uses on_bar_close and update_frequency to specify how often it wishes to receive updates from the server.
If on_bar_close is 1, the server will send updates only when a bar is closed - no updates will be sent while the bar is still forming.
If on_bar_close is 0, the server will use update_frequency to determine how often it sends messages to clients. The update_frequency param is tied with Sierra Chart own chart update frequency settings.
If the update_fequency is used when it is greater than the chart update frequency.
For example, if the chart update frequency is 800ms and the client sent update_frequency 5000, the server will update the client every 5000ms (5 seconds).
If the update_frequency is set to 0, the server will default to the chart update frequency set in Sierra Chart
If the update_fequency is less than the chart update frequency, again, the server will fall back to the chart update frequency.
historical_init=x
Sends x number of bars when a request is first made.
Use cases:
Reducing real time resource usage
One time data retrievals (while in a research workflow or live)
This setting is impacted by on_bar_close:
if on_bar_close is set to False, the last (live) bar on the chart will not be sent
if on_bar_close is set to True, the last (live) bar on the chart, will be sent
2 Approaches for retrieving data for realtime processing
The method graph_data_request has 2 settings that control how many bars are sent
historical_init_bars - when you send a request, the first response is that many bars. it will send that many bars once at the start
realtime_update_bars - on each update this many bars will be sent
The python library does not cache or store any history. If you set realtime_update_bars=10, effectively what that means is that 10 bars are sent from SC to your code on every update. In your example, where you need 10 bars of history to do your calculations, you could achieve that as you did in your code which would work fine.
But there are other strategies to do that. For example, you could get hisotrical_init_bars of 10 which you would use to initialize your raw DataFrame with 10 bars. You could then set the realtime_update_bars to 1. Then, on each update you would receive a new bar of data. You could then take the raw dataframe and append the new bar you just received and drop the oldest one.
Why do we need these two approaches? In many cases, you only need 10-100 bars of data which is not a lot. So the approach of setting realtime_update_bars to a value in that range is simple and easy. You always get the number of bars you need to calculate from SC and you don't have to worry about it. In some cases you need a lot more history and it becomes resource intensive and laggy to send too many bars. In that case the 2nd approach would work well.
block - blocking vs. non-blocking
By default graph_data_request is asynchronous and non-blocking.
In the async mode, the usage looks like this:
Make a call to grapg_data_request, receive the request_id
Get a hold of the response queue
Using the response queue, get the response
Code snipper looks like this:
from trade29.sc.bridge import SCBridge
bridge = SCBridge()
bridge.graph_data_request(key='xx',
base_data='4',
historical_init_bars = 1000,
realtime_update_bars=0,
block=False)
response_q = bridge.get_response_queue()
while True:
msg = response_q.get()
print(msg.df)
graph_data_request supports blocking mode. In blocking mode, which is sync and not async, the usage would look like this:
Make a call to graph_data_request and receive the response
All in one step, no need to use the queue as a middle man to retrieve the result.
This type of call is similar to the standard way of calling functions where you make the function call and the function returns the desired result directly.
Code snippet looks like this:
from src.trade29.sc.bridge import SCBridge
from src.trade29.sc.constants import *
bridge = SCBridge()
response = bridge.graph_data_request(key='xx',
base_data='4',
historical_init_bars=1000,
realtime_update_bars=0,
block=True)
print(response.df)
The two snippets are similar but for two differences:
The 1st snippet has block=False, the 2nd snippet has blocl=True
The 1st snippet uses the queue to receive the response. The 2nd snippet receive the data directly from graph_data_request
block=True only makes sense when realtime_update_bars=0. In other words, it only makes sense when retrieving historical data. When using subscribing to realtime updates, block should be False
Return Message Format
Graph data is returned in a pandas DataFrame.
The order of the fields
bar timestamp - single column
bar closed flag - single column
bar index - single column
base data - multiple columns
subgraph data - multiple columns
Whether a field appears or not depends on what was requested. For example, if include_bar_closed_status was set to False, the returned DataFrame will not include this field.
Graph data DataFrame field descriptions and examples
| Description | Example |
---|---|---|
bar_timestamp | Bar timestamp This field will appear only if the user has set include_bar_timestamp to 1 | 2023-04-27 22:25:00 |
bar_is_closed_flag | A flag signaling if this bar is closed This field will appear only if the user has set include_bar_closed_status to 1 |
|
bar_index | The index of the bar on the chart. This field will appear only if the user has set include_bar_closed_status to 1 | 374 would be bar number 375 on the chart (the count starts from 0) |
base_data | A list of values. | User has set the base_data list to SC_OPEN, SC_LAST 4034.25, 4044.50 would mean that the open of the bar is 4034.25 and the last price is 4044.50
|
bar_sg_data | A list of values. | User has set sg_data to ID1.[SG1-SG3], ID1 is the BB on the chart The record will include 3 fields for SG1, SG2, SG3 In other words, we would have the 3 BB values separated by commas
|
vbp_data | Volume by Price (footprint) data List of tuples, each tuple includes price, bid volume, ask volume,number of trades |
|