Alpha Trading Labs DSL

If you have not already you should first take a look at Getting Started

The Alpha Trading Labs Domain Specific Language is built to help you create a valuation function easily and effectively. The language is based on nodes where each event or variable is a unique instance of a node with special properties (You do not have to worry about their structure or what is going on behind the scenes). Each of these ‘nodes’ is built with valuation functions in mind.

Use this page as a reference about what features of the DSL you have available and what they do.

NOTE:

  • You will write your valuation functions in all sense and purposes as python code. In reality however you are using our DSL, which when creating the model will interpret and restructure your code centered around Events . To understand more about what that implies, go through the Getting Started page, specifically the Valuation Functions
  • Since the DSL is built on the idea of nodes this documentation may refer to values as nodes such as a number as a constant node, or a Boolean as a conditional node.
  • You will note that the functions are shown as ‘atomlib.function’, the application implements the DSL such that you will not need to call any of the functions as ‘.function’ but simply call them as they are, examples found in the sample code for some of the functions.

Event Combining Functions

Event Filtering Functions

With Initial

Logical Operators

All Functions

And, Not, Or, Xor

( Logical Operators )

The DSL implements its own logical operators to work with the Node based structure. It does not change much except that instead of using the python diction for and, not, and or (no python diction for xor) you use And(args), Not(x), Or(args), Xor(args).

So for example:

trade = Trade(symbol)
same_price = (trade.price == Last(trade.price))
same_qty = (trade.qty == Last(trade.qty))

myAnd = And(same_price, same_qty)
myNot = Not(same_price)
myOr = Or(same_price, same_qty)
myXor = Xor(same_price, same_qty)
  • myAnd will now be a true condition if, for the latest Trade ‘trade’ and the trade before it (Last trade), the price and quantity of both trades was the same
  • myNot will be a true condition if the piece of the two trades were different
  • myOr will be a true condition if either the price or quantity of the trades were the same
  • myXor will be a true condition if only the price or only the quantity was the same in both trades

You can now pass these as the ‘condition’ for the functions that require one ( Filter and Select )

Any

( Event Combining Functions )

atomlib.Any(*args)

Takes at least two ‘args’ which have to be event nodes (Clock, Trade, Quote)

returns the event which happened most recently

Any requires at least two Events as arguments, and will return the latest event to have triggered.

For example:

c = Clock(100)
q = Quote(symbol)
trade = Trade(symbol)
event = Any(c, q, trade)

Event is now set up such that it is reading from c, q, and trade ( Clock , Quote or Trade events). Specifically when the valuation function is being executed in the model, event will contain the event which happened most recently at that given time. Each iteration of the valuation function can contain either the same event or a different one as it is dependent on the live changes of the given Quote and Trade passed as its arguments.

‘event’ most recent first index
‘trade’ 1
‘q’ 2
‘q’ 3
‘c’ 4
‘q’ 5
‘trade’ 6
‘c’ 7

This means if there is something you would want to do every iteration of the valuation function, regardless of the event, you wont have to write the same line of code two or three times. For example, it allows you to do things like using Timestamp to figure out the time between events regardless of the type of event that happened before:

last_event_timestamp = Last(Timestamp(event))
delta_time = Timestamp(event) - last_event_timestamp

Similar to Events an Any node will have a record of its changes which can be accessed using Last or Lag

It can also be used as an argument with already initialized events to check if they are the most recent active event during an iteration of the valuation function. For example using Filter:

c = Clock(100)
q = Quote(symbol)
trade = Trade(symbol)
event = Any(c, q, trade)
noTrade = Filter(Any(c, q), 0)

This would assign 0 to ‘noTrade’ if c or q were the most recent event.

Clock

( Events )

atomlib.Clock(period)

period is an int representing microsecoinds

will keep track of the time passed, event is registered when the time ‘period’ is reached

Clock will keep track of the time passed, where an event is registered every time the period is reached.

It’s use is to have a way to know that there were no changes to the events in the valuation function between iterations which can be used to skip to a specific part of the function or go to the next iteration.

c = Clock(100)
q = Quote(symbol)
trade = Trade(symbol)
event = Any(c, q, trade)

Here Any would contain c if the period of 100 microseconds passed and none of the other arguments have triggered (in this case, for the ‘symbol’ a new Quote was not registered in the book and no Trade was executed)

Filter

( Event Filtering Functions )

atomlib.Filter(cond, x)

Checks if the condition ‘cond’ is true, where ‘cond’ must be a conditional Node.

returns ‘x’ if condition is true. x is a an event or value node

Filter allows you to assign values to a variable only if a specific condition you choose is met.

For example if you want a variable ‘z’ assigned to 0 only if there was no change in the price of a Trade you could write:

trade = Tade('symbol')
last_trade = Last(trade)
samePrice = (trade.price == last_trade.price)
z = Filter(samePrice, 0)

Now if the trade price in a given iteration has not changed z will contain a constant node of value 0

It is important to note that the DSL works through nodes and implements its own relational, arithmetic, and logical relationships, so even though it looks similar a python Boolean cannot be passed, i.e this would not work:

py_bool = True
z = Filter(py_bool, 0)

Hold

( Event Functions )

atomlib.Hold(x, initial_value=None)

evaluates ‘x’ and stores it (if x is an attribute which has not been triggered yet, will store ‘None’)

will check if there is a change in ‘x’ and store the change

Hold can store any value which you need to use in future iterations of your valuation function.

For example lets say you need to do a calculation with the latest trade price, but a given iteration of the function there might not be any new trades so you would have to use the last registered price, you could do so by assigning a variable Hold with the trade price. Like this:

trade = Trade(symbol)
trade_price = Hold(trade.price)

Now every time the valuation function runs, it will have a value with the latest trade price. So for example if the model is running and for the first four iterations, on the first iteration there is no trade, then trade_price would have ‘None’. On the second, if there was a trade then it would hold that price. On the third iteration, if there is no trade the value being held would stay the same. If on the fourth there is a new trade then the new price will now be held.

iteration 1 2 3 4
trade_price value None price same price new price

So no matter whether or not there was a trade for any given execution of the valuation function you are always able to make a calculation using trade_price

For a detailed look on hold go to Why Use Hold

Join

( Event Combining Functions )

atomlib.Join(*args)

‘args’ must be nodes of the same type, and there must be at least 2

returns most recent active node

Similar to Any it can read from multiple nodes, but whereas Any holds only Events , Join can read from other node types, but all the nodes that are passed into a join they must be of the same type.

Join will contain the node which was ‘activated’ most recently. For example passing two Filter nodes

trade = Trade(symbol)
same_price = (trade.price == Last(trade.price))
same_qty = (trade.qty == Last(trade.qty))

bothOrOne = Join(Filter(And(same_price, same_qty), 5), Filter(Xor(same_price, same_qty), 10))

bothOrOne will have a Join which contains two Filters, so for a given iteration of the valuation function if either of these filters in the join activates, then join will contain that filter. The first filter checks if, for a the two latest trades, the price and quantity stayed the same, and returns 5, meaning bothOrOne will have the value 5. The second filter checks if only the price or only the quantity stayed the same, and returns 10 if so, thus bothOrOne would have the value 10.

‘bothOrOne’ most recent first index
5 1
10 2
5 3
5 4

Note that for any give iteration of the valuation function there may not be a Trade that executed therefore bothOrOne will not be executed, this is fine where the register will only contain the values for when it was executed. For example:

iteration 1 2 3 4
bothOrOne value
5
10

Lag

( Event Functions )

atomlib.Lag(x, n)

‘x’ is an event, ‘n’ is an index in the form of an integer > 0

will return the n’th record of event x

When an event is assigned to a variable, whenever there is a change and an event is registered, the event variable is updated, but the past version of the event variable is stored in an array in memory. Lag allows you to access this array, where n is the index.

trade = Trade(symbol)
fiveBack = Lag(trade, 5)

Trade(symbol) creates a node where its attributes and changes are stored in an array:

Trades of ‘symbol’ index fiveBack
Info on most recent tarde 1  
Info on Last trade 2  
Info on 3rd most recent trade 3  
Info on 4th most recent trade 4  
Info on 5th most recent trade 5 <—
 

fiveBack would point to index 5, and as more trades are registered the array will change but fiveBack will still point to index 5.

( Last is the equivalent of Lag(x, 1) )

Last

( Event Functions )

atomlib.Last(x)

‘x’ is an event

return the last event triggered by the event x

When an event is assigned to a variable, whenever there is a change and an event is registered, the event variable is updated, but the past version of the event variable is stored. Last allows you to access the most recent registered event prior to the current one.

Example of it being used to save the latest Timestamp of an event

trade = Trade(symbol)
    quote = Quote(symbol)
    event = Any(trade, quote)
last_event_timestamp = Last(Timestamp(event))
Timstamps of ‘event’ index last_event_timestamp
most recent timestamp 1  
Last timestamp 2 <—
3rd most recent timestamp 3  
 

here since we are using an event with Any there is a register of the timestamps when any of the events are registered regardless of the type of event. last_event_timestamp will have the time at which the event directly before the most recent event happened.

OnChange

( Event Functions )

atomlib.OnChange(x)

takes an event ‘x’

if event has changed returns updated event x

Call OnChange with an Events to check when the event has been updated

i.e for Trade when a trade has been executed, for Quote when there is a change in the book, for Clock when the period has been reached.

trade = Trade(symbol)
mostRecentTrade = OnChange(trade)

mostRecentTrade will always contain the latest trade to have actually happened.

Quote

( Events )

atomlib.Quote(symbol)

symbol: security symbol in the form of a string

Returns a QuoteNode

Calling Quote with a security symbol will return a Quote Node which will have the following attributes:

  • Quote.bid_qty[ i ] bid qyuantity at i’th position in the book
  • Quote.ask_qty[ i ] ask quantity at i’th position in the book
  • Quote.bid_price[ i ] bid price at i’th position in the book
  • Quote.ask_price[ i ] ask price at i’th position in the book

This will allow you to check the bid price/quantity and ask bid/quanitity for the first 5 levels in the book of the given security symbol (the inside quote is at i = 0)

Quote:

.bid_qty .ask_qty .bid_price .ask_price level
bid_qty[ 0 ] ask_qty[ 0 ] bid_price[ 0 ] ask_price[ 0 ] Inside (1)
bid_qty[ 1 ] ask_qty[ 1 ] bid_price[ 1 ] ask_price[ 1 ] 2
bid_qty[ 2 ] ask_qty[ 2 ] bid_price[ 2 ] ask_price[ 2 ] 3
bid_qty[ 3 ] ask_qty[ 3 ] bid_price[ 3 ] ask_price[ 3 ] 4
bid_qty[ 4 ] ask_qty[ 4 ] bid_price[ 4 ] ask_price[ 4 ] 5

Here is a useful example of using quote to assign an inside bid price using Select based on whether the inside bid quantity is within the size threshold (a value you can determine)

q = Quote(symbol)
inside_bid_price = Select(q.bid_qty[0] <= size_threshold, q.bid_price[1], q.bid_price[0])

If the bid quantity of the inside bid is less than or equal to your determined threshold, then assign the Last bid price q.bid_price[1] as your desired inside bid price, otherwise use the actual inside bid price ‘q.bid_price[0]’

Select

( Event Filtering Functions )

atomlib.Select(cond, a, b)

Checks if the condition ‘cond’ is true, where ‘cond’ must be a conditional Node.

returns ‘a’ if condition is true and ‘b’ otherwise. ‘a’ and ‘b’ are event or value nodes

Select is essentially the DSL’s implementation of ‘If Else’

You can use this for a variety of calculations where you want to assign values based on whether a condition is met like on the example used in Quote assigning a desired inside bid price only when the current inside bid quantity is within a desired threshold:

q = Quote(symbol)
inside_bid_price = Select(q.bid_qty[0] <= size_threshold, q.bid_price[1], q.bid_price[0])

If the bid quantity of the inside bid is less than or equal to your determined threshold, then assign the Last bid price (q.bid_price[1]) as your desired inside bid price, otherwise assign it to the actual inside bid price (q.bid_price[0]).

iteration 1 2 3 4
inside_bid_price value
q.bid_price[1] q.bid_price[0] q.bid_price[0]

Note that for any give iteration of the valuation function there may not be a Quote that executed therefore inside_bid_price will not be executed, in this example the first iteration, this is fine.

Snapshot

( Event Functions )

atomlib.Snapshot(x, y)

x is is an int > 0, and y is an event node

returns y event at x level

Think of Snapshot as a mix of Hold and Lag, where you pass an event node ‘y’ and a level ‘x’ where this will hold the x’th index of that event. For example:

trade = Trade(symbol)
    snapTrade = Snapshot(3, trade)
Trades of ‘symbol’ index ‘x’ fiveBack
Info on most recent tarde 1  
Info on Last trade 2  
Info on 3rd most recent trade 3  
Info on 4th most recent trade 4  
Info on 5th most recent trade 5  
 

snapTrade will hold a the value at the third index stored and can be used at any iteration of the valuation function regardless of whether there was a Trade event that iteration or not.

Timestamp

( Event Functions )

atomlib.Timestamp(x)

x must be an event node

returns the time at which an event happened

Here is an example of it being used to get the change in time between the latest event and the most recent using Any with Clock , Quote and Trade

c = Clock(100)
q = Quote(symbol)
trade = Trade(symbol)
event = Any(c, q, trade)
last_event_timestamp = Last(Timestamp(event))
delta_time = Timestamp(event) - last_event_timestamp

Timestamp also keeps a register of its changes like events do, so delta_time will always have the time difference between the last two events regardless of the type of event they were.

Timstamps of ‘event’ event index delta_time = (a - b)
most recent timestamp ‘trade’ 1 <— a (Timestamp(event))
Last timestamp ‘q’ 2 <— b (Last(Timstamp(event)))
3rd most recent timestamp ‘c’ 3  
 

Trade

( Events )

atomlib.Trade(symbol)

Using the given security symbol will return a Trade node

The Trade Node will keep track of the latest trades in the market for the given security symbol. This will allow you to check if and what trade happened last.

Calling Trade with a security symbol will return a Trade Node which will have the following attributes:

  • Trade.price returns the price of the last trade
  • Trade.qty returns the quantity of the last trade
  • Trade.side returns which side of the book the trade happened in

Allowing you to access the price, quantity and side of the most recent trade to have actually executed. Combine Trade with other functions such as (Last and Lag) to get the attributes for earlier trades to have happened.

Trade:

.price .qty .side
most recent trade price most recent trade quantity side of most recent trade
last trade price last trade quantity side of last trade
3rd most recent trade price 3rd most recent trade quantity side of 3rd most recent trade

Here is an example of using Last to check if the trade price has changed or not

trade = Trade(symbol)
last_trade = Last(trade)
same_price = (trade.price == last_trade.price)

trade:

Trades of ‘symbol’ index same_price = (a == b)
Info on most recent trade 1 <— a (trade.price)
Info on Last trade 2 <— b (last_trade.price)
Info on 3rd most recent trade 3  
 

WithInitial

( With Initial )

atomlib.WithInitial(value)

with an initial value ‘value’ pass an anonymous function (lambda expression) which will execute with the initial value as a parameter

the value returned by the anonymous function is saved to be used as the value the next time it is run.

returns the value returned by the anonymous function

WithInitial is useful so you can do a certain calculation based on a given condition as well as using a running value that changes each time the calculation is done.

For example let’s say that if the price has not changed between the most recent trade and the one before, then you want to decay the value of that price, and at every iteration of the valuation function if the price still has not changed then decay the value even further. You can do this as such:

trade = Trade(symbol)
same_price = (trade.price == Last(trade.price))
decayedPrice = WithInitial(0.0)(lambda decayed: Select(same_price, (decayed * decay) + trade.price, trade.price))
  • In this example decayedPrice is initialized with withInitial where the variable ‘decayed’ is assigned an initial value of 0.0.
  • It then runs the anonymous (lambda) function that in this case uses Select to help
  • it checks if the price in the last two trades has not changed (same_price) and if that is the case:
    • decayed is assigned the calculation of its own value multiplied by a pre-determined ‘decay’ to which the trade price is then added. (decayed * decay) + trade.price
    • It is evident that the first time this happens since the initial value of decayed will be 0.0 then decayed will return the trade price.
    • Further, since decayed will save the value it has, and at every iteration runs the calculation with its updated value, then the decay keeps changing each time the price does not change.
  • If same_price is not true and the price has changed:
    • decayed is assigned the trade price effectively re-starting the process.
  • each time this process executes ‘decayedPrice’ is assigned the running value of ‘decayed’