In the following, we assume that the reader has already some experience with WealthScript and is already familiar with how to access the Knowledge Base for further information.
Pro Tip to begin with: WealthScript is a case-insensitive language. This means that it doesn't matter if you prefer to type letters in lower-case, upper-case, or a mixture of the two. All of the examples provided in this section use the _camelCase_ convention, but you can write your own code following the convention that you prefer!
WealthScript is a strongly typed language where type is usually inferred from usage. Basic types are:
- integer
- number
- string
- bool
Warning: Numeric values are always represented as a double even when the `integer` type is used. The `integer` keyword allows WealthCharts to better perform tasks like the verification of input parameters, but it doesn't affect how the code is executed.
See the operands section for additional information.
Numbers are double-precision (64-bit) floating-point values and can be expressed both in decimal form or through scientific notations. Examples include:
- 1
- 1.25
- 1e2
- 1.25e3
The underscore `_` may be used as a digit separator. The benefit of this is to improve the code readability when dealing with large numbers. For example:
- 1_000_000
Strings are a sequence of characters delimited by double quotes `"`.
Certain characters need to be escaped before the can be included in a string.
These escape sequences are:
String literals can be concatenated together just by juxtaposing them and in this case a single space will be automatically added.
For example, writing "hello" "world` is exactly the same as writing "hello world" but it makes it easier to wrap long strings.
The + operand must always be used when concatenating one or more string variables.
See the operands section for additional information.
Booleans
Boolean values can either be `true` or `false`.
Colors are useful values to distinguish and give meaning to the visuals of your script.
The WealthScript Language offers a predefined set of color constants for easy use:
If these predefined values don't do it for you, colors can also be created on the go with the RGB(<red>, <green>, <blue>)
. The function lets you set the values of `red`, `green` and `blue` from `0` to `255` to find the perfect mix for the color you need.
A WealthScript is composed of a series of _statements_.
Each statement is preferably put on its own separate line in the code editor and, in this case, using the semi-colon `;` at the end of each line is optional.
Common examples of statements are:
- [declarations and assignments]
- [indicator definitions]
- [trading statements]
- [plotting statements]
- [conditional checks]
Comments are lines of code that do not get executed and can contain anything
Pro Tip: Use comments for useful notes to explain your process so that you don't get lost
if you need to edit your formula in the future
Line comments start with //.
Block comments start with {` and are closed by `}.
Unary operators
| Operand | Effect |
| :-----: | ---------------- |
| `-` | Negates a number |
| Operand | Effect |
| :-----: | -------------------------------------------------------------------- |
| `+` | Sums two numbers or concatenates two strings |
| `-` | Subtracts one number from another |
| `*` | Multiplies two numbers together |
| `/` | Divides two floating-point numbers |
| `div` | Divides two integer numbers |
| `mod` | Calculates the remainder of the integer division between two numbers |
| Operand | Effect |
| :-----: | --------------------------------------------- |
| `not` | Negates a boolean value (unary) |
| `and` | Computes the logical AND between two booleans |
| `or` | Computes the logical OR between two booleans |
Logical expressions are always short-circuited. Short-circuiting is a feature in many programming languages where the evaluation of these expressions stops as soon as the final outcome is determined. In other words, if the result can be decided before evaluating all the conditions, the remaining conditions are not checked.
In a logical AND expression, if any of the conditions is `false`, the entire expression is guaranteed to be `false`. Therefore, the evaluation of the expression stops as soon as a `false` condition is encountered.
In a similar way OR expressions only get evaluated until a `true` condition is found.
These operators _(also known as comparison operators)_, compare their operands from left to right.
| Operand | Effect |
| :-----: | --------------------------------------------- |
| `>` | Checks if the first value is greater than the second |
| `<` | Checks if the first value is less than the second |
| `==` | Checks if the first value and the second are equal |
| `!=` | Checks if the first value and the second are different |
| `>=` | Checks if the first value is greater than the second or if they are equal |
| `<=` | Checks if the first value is less than the second or if they are equal |
| `cross over` | Checks if a value goes from being less than the other to being greater than it |
| `cross under` | Checks if a value goes from being greater than the other to being less than it |
Relational operators return a `boolean` value reflecting the outcome of the comparison between the two operands
The standard C precedence rules apply.
In the WealthScript Language, operator precedence determines the order in which different operators are evaluated in an expression. When you have an expression that contains multiple operators, the operators with higher precedence are evaluated before operators with lower precedence. If two operators have the same precedence, the order of evaluation is determined by their associativity, which can be _left-to-right_ or _right-to-left_.
Here are the standard operator precedence rules, with operators listed from highest to lowest precedence:
1. Postfix operators: These operators follow the operand. Examples include the function call `()` and array subscript `[]`.
2. Unary operators: These operators act on a single operand. Examples include the unary minus `-`, unary plus `+`, logical NOT `not`.
3. Multiplicative operators: These operators perform multiplication, division, and modulus operations. Examples include `*` (multiplication), `/` (division), and `%` (modulus).
4. Additive operators: These operators perform addition and subtraction operations. Examples include `+` (addition) and `-` (subtraction).
5. Relational operators: These operators compare values. Examples include `<` (less than), `>` (greater than), `<=` (less than or equal to), and `>=` (greater than or equal to).
6. Equality operators: These operators compare for equality and inequality. Examples include `==` (equal) and `!=` (not equal).
7. Logical AND: The `and` operator performs a logical AND operation.
8. Logical OR: The `or` operator performs a logical OR operation.
9. Conditional operator: The `? :` operator is the conditional operator, used for conditional expressions.
10. Assignment operators: The `=` operator is used to assign values to variables.
It's important to note that you can use parentheses `()` to override the default precedence and enforce a specific order of evaluation in your expressions. Expressions enclosed in parentheses are always evaluated first.
WealthScript has different ways to assign names to values and, even if not all of them are technically variables, they will all be discussed in this sub-section.
Except for **Aliases** the order of declaration **does not** apply for these statements so its good practice to put them at the beginning of the script and outside any conditional checks to avoid confusion.
The simplest way to use the result of an expression multiple times without computing it every single time, is to assign a name to it by using the keyword `let`.
Once an alias has been assigned to an expression, the same alias cannot be assigned to a different expression. This essentially make aliases read-only variables.
Warning: You can only define up to a certain number of aliases (currently around 200) in your script. You will get a compilation error if you attempt to define more than that.
Aliases can also be defined inside closed scopes like [conditional checks]
If you're already familiar with computer languages in general then you should already have an idea of what a variable is but, as a quick definition, imagine variables as storage boxes that you can use to hold some values.
You can either get stuff from the box (i.e. _read the variable_) or put new stuff into it (i.e. _write the variable_) and you can do that however many times you need.
Variables are declared using the `var` keyword in a dedicated statement. Their initial value can be specified by wrapping it in parenthesis just after the variable name.
The type of the variable will be inferred from the type of the initial value.
See the following examples:
Pro Tip: You can declare multiple variables on a single line or have multiple `var` statements. If the initial value is optional and if left empty the variable will be initialized with the number **zero**.
WealthScript variables have a _super-power_ where values for previous candles are automatically preserved and you can access those previous values with squared bracket expressions. For example:
Warning: You cannot set the value of previous bars for variables
Constants are values that cannot be changed after they're initially defined.
Their value is simply used as-is every time the constant is referenced, making them faster and more efficient than aliases, but they can only hold simple values and not expressions.
Reminder: Memory is never allocated for constants.
Inputs are a special type of constants whose value can be changed by the user before the script is executed.
Indicator parameters are a primary example of inputs.
Pro Tip: You can force a numeric input to be treated as an integer by using the following syntax:
Doing this allows the chart's UI to validate the input thus preventing the user from passing decimal values.
The **WealthScript Language** offers a list of predefined keywords to access useful values for your script.
All of these predefined keywords are **readonly** and you can access their past values using square bracket indexes same as [variables](./#{#declared-identifiers-variables}).
| Name | Description |
| :-----: | ------------------------------------- |
| `close` | The last price _(C in short)_|
| `open` | The opening price _(O in short)_|
| `low` | The lowest price _(L in short)_|
| `high` | The highest price _(H in short)_|
| `volume` | The volume value _(V in short)_|
| `range` | The range of the bar |
| `truehigh` | The true high of the bar |
| `truelow` | The true low of the bar |
| `truerange` | The true range of the bar |
Some of those keywords refer to commonly used equations in the trading world.
| Name | Description |
| :-----: | ------------------------------------- |
| `hl2` | $(H + L) \by 2$ |
| `hlc3` | $(H + L + C) \by 3$ |
| `ohlc4` | $(O + H + L + C) \by 4$ |
Conditional checks are a powerful way to add complexity to your code.
The code inside the body of these statements will only be executed based on whether one or more conditions are met or not.
These blocks of code always start with the `if` keyword followed by a `<boolean condition>` which can be pre evaluated in a [declaration] or evaluated inline without allocating any memory.
After the `<boolean condition>` use `then` to start listing the code to execute when the condition is met.
You can use the [relational operators] in the conditions and concatenate them using the [logical operators](./#logical-operators)
Remember: Standard [precedence] rules apply
You can also add a list of code statements as a fallback when the condition is not met using the `else` keyword.
One of the most powerful tools of the WealthScript Language is the option to reuse logic from other scripts you wrote and tap into any of the Indicators you have access to on your account to enhance your own scripts.
To use any Indicator you must first import it using the button on the left called Indicators. Once we click it, a window with all of the indicators you've already imported will pop up on the screen _(should be empty when starting from scratch)_. From here you can add as many of the indicators that you have access to by clicking the button on the bottom left of the window + Add Indicator.
Once you add any indicator you'll see it in the window.
After you successfully import an Indicator you are going to need to define it by assigning it to an [alias] and then access its values from the alias you've defined.
Warning: Do not define indicators inside conditional statements as they need to be calculated bar by bar, and some need to access their previous values.
Pro Tip:
if you don't know the correct way to define every indicator or the parameters to use no worries - we have a tip for you!
After you import an indicator you can access a list of ready to use snippets by clicking the import or by opening the Additional Options menu and clicking on Code Snippets
The info panel contains the definition of the indicator and the list of values you can access from it.
Importing the "Moving Average Convergence Divergence (MACD)" indicator will present you with these snippets
Definition:
Outputs:
The values of the Indicators correspond directly to the ones that get plotted on the chart when they are applied and can be accessed by following your alias with a `.` and the name of the value you need (`myIndicator.<name>`).
You can wrap the value you want to access in double quotes like this `myIndicator."<name>"` if it contains spaces or reserved/special characters
Some outputs of the "ADX / DMI" indicator contain a reserved character (`+` and `-`) so they must be wrapped in double quotes
In your Indicator definitions we can distinguish between `sources` and `parameters`.
A `parameter` is a value taking one of the [standard supported types].
You can specify any parameter using the following syntax in the Indicator definition: `let myIndicator = Indicator(<param1>: <value1>, <param2>: <value2>, ...)`
Every `parameter` is optional and the Indicator will use its default when it gets defined, this way you only need to write the parameters you want to change and leave the rest of them alone.
Warning (Important): Indicators only get defined with the first parameters they are given so changing them in future bars will not have any effect. If you need two different versions of the same indicator on different occasions, define it twice and use the values accordingly:
Pro Tip: You can always skip any parameter that is only tied to visual changes _(like offsets, colors, visibility toggles, ...)_ since the indicators you import do not get plotted when you define them.
Reminder: Just like accessing an Indicator's value you may need to wrap parameters' names in double quotes if needed.
A `source` is an array of values taken from the [predefined title series] a [variable]or another indicators value (`myIndicator.<name>`). You can specify any source using the following syntax in the Indicator definition: `let myIndicator = Indicator(<source1>: <var1>, <source2>: <var2>, ...)`.
If an indicator needs sources they must be explicit. For example a Simple Moving Average has one source called input so it is required.
Warning (Important): The WealthScript engine will start calculations right away with the first bar available, but will wait for every indicator to be defined to be ready to draw on the chart.
So if for example you have two Moving Averages, one of 5 periods and one of 30, the plots will show up once the 30-bar MA is ready _(after 30 bars)_.
Keep this in mind when applying your scripts and using them.
Trading is a fundamental part of any strategy with the following syntax: `<action> <quantity> <time> <price>`.
The action sets the trade direction.
Some actions need a position already opened or otherwise they do nothing.
| Keyword | Effect |
| :-----: | --------------------------------------- |
| `buy` | Opens a Long position |
| `sell` | Closes an opened Long position |
| `sellshort` | Opens a Short position |
| `buy to cover` | Closes an opened Short position |
Pro Tip: You can use just the opening and closing actions on the current bar like so:
But it's best to specify the rest of the parameters as well!
This parameter specifies how many shares to trade.
If you want to specify the number of shares to trade you can use the `shares` _(or `share`)_ keyword prefixed by a numeric value
This parameter specifies the bar to trigger the trade.
You can choose between two values `this bar` which triggers the trade at `close` of the current bar
and `next bar` which triggers the trade in the next bar
Pro Tip: Using next bar lets you further customize the [price] option
This parameter specifies the price of the trade using the following keywords:
| Keyword | Effect |
| :-----: | --------------------------------------- |
| `at close` | The price the bar closes with |
| `at market` | The market price **(only available for [next bar](./#trading-time) trades)** |
| `at open` | The opening price of the bar **(only available for [next bar](./#trading-time) trades)** |
| `at <value> limit` | The price as soon it goes **over** the `<value>` specified |
| `at <value> stop` | The price as soon it goes **under** the `<value>` specified |
**limit** and **stop** orders only get triggered if the bar price crosses the specified value, otherwise nothing happens.
In the prices that specify a **value** you can use any numeric value _(like for [specific shares].
Plotting refers to drawings that reoccur every bar.
They are part of almost any indicator you've ever applied to your chart and are extremely useful for visual cues and controls.
Every plot follows this syntax `Plot<N>(<source>, <...optional parameters...>)`
- N: is the integer number acting as a unique identifier for each plot
- source: is the numeric value that is drawn on the chart
The optional parameters are not required but much recommended for more complete control over your script's visuals
- name: a string that identifies the value being plotted
- color: the color used for the drawing
Warning: You can't define two plots with the same identifier and different name
-----
The default way of plotting is to represent the values as a line on the main chart, but if you need more customization
for your script, check out the following attributes:
Attributes (advanced):
In this section we'll look at the PlotStyle and PlotSubchart attributes to further customize your plots.
For both of the attributes the first parameter is the unique number that identifies the plot.
PlotStyle follows this syntax `[PlotStyle(<N>, <style>, <size>)]`
- N: is the integer number acting as a unique identifier for each plot.
- style: is the shape to give the values you want to draw with `line` being the default. These are the possible values for the plot style:
- `line`
- `histogram`
- `hbands`
- `dots`
- `dashes`
- `crosses`
- `squares`
- `triangleUp`
- `triangleDown`
- `levels`
- `columns`
- `diamonds`
- size: is the numeric value from 1 to 7 representing the size of your drawing where `1` is the minimum and the default value.
PlotSubchart follows this syntax `[PlotSubchart(<N>, <subchart>)]`
- N: is the integer number acting as a unique identifier for each plot.
- subchart: is a positive number identifying the section of the chart where the drawing will be located. The default value is `0` which stands for the main chart window. Any number above 0 will create a new section under the main one and draw there.
Warning (Important): The WealthScript engine will start calculations right away with the first bar available but will wait for every indicator defined to be ready to draw on the chart.
So if for example you have two Moving Averages: one of 5 periods and one of 30, the drawings will show up once the 30-bar MA is ready _(after 30 bars)_.
Keep this in mind when applying your scripts and using them.
Here are some things to keep in mind to make your creations stable, and easy to use:
- Use aliases instead of variables if you need to store a value with no need to access its past values
- Use constants if you need a value that stays the same during the whole script
- It's best to never leave the body of conditional statements empty
// Compiles but not great code
---
// Compiles and much better code
- It's best to concatenate nested conditional statements
// Works but not great code
---
// Works and much better code
Here is a good script structure for statements to avoid any inconsistency and keep your formula solid:
<code>
// 1: Any optional attributes
[PlotStyle(1, Dashes, 5)]
[PlotStyle(2, Dashes, 5)]
// 2: Input definitions
input integer longTermLen(200)
// 3: Constants definitions
const shortTermLen(50)
// 4: Variables definitions
var tradesCounter(0)
// 5: Indicator definitions
let longTermSMA = SMA(input: close, Len: longTermLen)
let shortTermSMA = SMA(input: close, Len: shortTermLen)
// 6: All of your script logic, operations, conditions and endpoint checks
if (shortTermSMA.value crosses over longTermSMA.value) then
buy next bar at market
tradesCounter[0] = tradesCounter[0] + 1
end
if (shortTermSMA.value crosses under longTermSMA.value) then
sell next bar at market
tradesCounter[0] = tradesCounter[0] + 1
end
// 7: Any endpoint reoccurring every bar
plot1(longTermSMA.value, "Long Term SMA", red)
plot2(shortTermSMA.value, "Short Term SMA", green)
{
Here is a basic template to help you get started with your indicator.
Any code between curly brackets is "block commented" which means the script
does not read this when compiling or running.
}
{
Plot Styles
Remember, these are formatted by (signal number, signal visual, signal size)
[PlotStyle(1, Dots, 1)]
}
[PlotStyle(1, Dots, 10)]
[PlotStyle(2, Dots, 10)]
{
Setting Variables and conditionals
Defining the rules
}
var buyDot(false)
var sellDot(false)
let newBuy = (close > close[1]) and (close[1] > close[2]) and (close[2] > open[3])
let newSell = (close < close[1]) and (close[1] < close[2]) and (close[2] < open[3])
{
Creating some conditional checks/statements
}
if newBuy and not buyDot then
buyDot = true
sellDot = false
plot1(low, "New Buy Entry", RGB(0,255,0))
elseif newBuy and buyDot then
plot1(low, "New Buy Entry", RGB(0,255,0), 0, 5) // This resizes the dot to a size of 5
end
if newSell and not sellDot then
sellDot = true
buyDot = false
plot2(high, "New Sell Entry", RGB(255,0,0))
elseif newSell and sellDot then
plot2(high, "New Sell Entry", RGB(255,0,0), 0, 5) // This resizes the dot to a size of 5
end
Check out related articles for further information!